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:
authorPeter Klimenko <peterklimk@outlook.com>2020-07-31 11:45:15 +0300
committerPeter Klimenko <peterklimk@outlook.com>2020-07-31 11:45:15 +0300
commit97a4a8d0fb7fd9ac34f9f5d4d5a0689c01235e14 (patch)
treefc9746d2210eda08be9d44ae67d5e58d64b48b40
parent4a7c203e9ecc7c5b0370afc0fdd6bcc183dc00df (diff)
parentf3e8326453ae856d7914e45e832a2ed80aa9a9b9 (diff)
merge
-rw-r--r--.clang-tidy45
-rw-r--r--CMakeLists.txt40
-rw-r--r--GNUmakefile10
-rw-r--r--build_files/build_environment/CMakeLists.txt22
-rw-r--r--build_files/build_environment/cmake/alembic.cmake12
-rw-r--r--build_files/build_environment/cmake/boost.cmake2
-rw-r--r--build_files/build_environment/cmake/check_software.cmake9
-rw-r--r--build_files/build_environment/cmake/clang.cmake5
-rw-r--r--build_files/build_environment/cmake/embree.cmake1
-rw-r--r--build_files/build_environment/cmake/ffmpeg.cmake9
-rw-r--r--build_files/build_environment/cmake/freetype.cmake3
-rw-r--r--build_files/build_environment/cmake/harvest.cmake23
-rw-r--r--build_files/build_environment/cmake/hdf5.cmake42
-rw-r--r--build_files/build_environment/cmake/ispc.cmake76
-rw-r--r--build_files/build_environment/cmake/llvm.cmake8
-rw-r--r--build_files/build_environment/cmake/nasm.cmake (renamed from build_files/build_environment/cmake/hidapi.cmake)18
-rw-r--r--build_files/build_environment/cmake/numpy.cmake1
-rw-r--r--build_files/build_environment/cmake/ogg.cmake1
-rw-r--r--build_files/build_environment/cmake/opencolorio.cmake7
-rw-r--r--build_files/build_environment/cmake/openimagedenoise.cmake25
-rw-r--r--build_files/build_environment/cmake/openmp.cmake1
-rw-r--r--build_files/build_environment/cmake/options.cmake54
-rw-r--r--build_files/build_environment/cmake/osl.cmake1
-rw-r--r--build_files/build_environment/cmake/png.cmake4
-rw-r--r--build_files/build_environment/cmake/python.cmake20
-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/sqlite.cmake2
-rw-r--r--build_files/build_environment/cmake/ssl.cmake2
-rw-r--r--build_files/build_environment/cmake/ssl.conf7
-rw-r--r--build_files/build_environment/cmake/tbb.cmake7
-rw-r--r--build_files/build_environment/cmake/theora.cmake1
-rw-r--r--build_files/build_environment/cmake/tiff.cmake8
-rw-r--r--build_files/build_environment/cmake/usd.cmake7
-rw-r--r--build_files/build_environment/cmake/versions.cmake75
-rw-r--r--build_files/build_environment/cmake/vpx.cmake6
-rw-r--r--build_files/build_environment/cmake/x264.cmake23
-rwxr-xr-xbuild_files/build_environment/install_deps.sh11
-rw-r--r--build_files/build_environment/patches/blosc.diff38
-rw-r--r--build_files/build_environment/patches/cmakelists_hidapi.txt20
-rw-r--r--build_files/build_environment/patches/embree.diff14
-rw-r--r--build_files/build_environment/patches/ffmpeg.diff59
-rw-r--r--build_files/build_environment/patches/hidapi.diff15
-rw-r--r--build_files/build_environment/patches/ispc.diff85
-rw-r--r--build_files/build_environment/patches/nasm.diff129
-rw-r--r--build_files/build_environment/patches/numpy.diff27
-rw-r--r--build_files/build_environment/patches/ogg.diff12
-rw-r--r--build_files/build_environment/patches/opencollada.diff44
-rw-r--r--build_files/build_environment/patches/openimagedenoise.diff120
-rw-r--r--build_files/build_environment/patches/openmp.diff23
-rw-r--r--build_files/build_environment/patches/python_macos.diff289
-rw-r--r--build_files/build_environment/patches/sqlite.diff14
-rw-r--r--build_files/build_environment/patches/theora.diff18
-rw-r--r--build_files/build_environment/patches/usd.diff109
-rw-r--r--build_files/buildbot/README.md2
-rw-r--r--build_files/buildbot/buildbot_utils.py6
-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/FindClangTidy.cmake104
-rw-r--r--build_files/cmake/Modules/FindEmbree.cmake2
-rw-r--r--build_files/cmake/Modules/FindHDF5.cmake65
-rw-r--r--build_files/cmake/Modules/FindOpenImageDenoise.cmake7
-rw-r--r--build_files/cmake/Modules/GTestTesting.cmake5
-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.cmake3
-rw-r--r--build_files/cmake/macros.cmake58
-rw-r--r--build_files/cmake/platform/platform_apple.cmake16
-rw-r--r--build_files/cmake/platform/platform_apple_xcode.cmake33
-rw-r--r--build_files/cmake/platform/platform_unix.cmake13
-rw-r--r--build_files/cmake/platform/platform_win32.cmake8
-rwxr-xr-xbuild_files/utils/make_test.py3
-rwxr-xr-xbuild_files/utils/make_update.py3
-rwxr-xr-xbuild_files/utils/make_utils.py17
-rw-r--r--cmake-build-debug/CMakeCache.txt823
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CMakeCCompiler.cmake76
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CMakeCXXCompiler.cmake88
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_C.binbin0 -> 36352 bytes
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_CXX.binbin0 -> 36352 bytes
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CMakeRCCompiler.cmake6
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CMakeSystem.cmake15
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.c671
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.exebin0 -> 79872 bytes
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.objbin0 -> 1412 bytes
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.cpp660
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.exebin0 -> 79872 bytes
-rw-r--r--cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.objbin0 -> 1423 bytes
-rw-r--r--cmake-build-debug/CMakeFiles/CMakeOutput.log137
-rw-r--r--cmake-build-debug/CMakeFiles/clion-environment.txt4
-rw-r--r--cmake-build-debug/CMakeFiles/clion-log.txt32
-rw-r--r--cmake-build-debug/CMakeFiles/cmake.check_cache1
-rw-r--r--cmake-build-debug/CMakeFiles/crt_0b14e42ed68c80dc9fdae2cd4c7e8bbd617f30950
-rw-r--r--cmake-build-debug/blender.crt.manifest54
-rw-r--r--cmake-build-debug/blender.exe.manifest37
-rw-r--r--cmake-build-debug/source/blender/blenkernel/BKE_blender_version.h.done58
-rw-r--r--cmake-build-debug/tests.exe.manifest37
-rw-r--r--doc/doxygen/Doxyfile2
-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.rst120
-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.py94
-rwxr-xr-xdoc/python_api/sphinx_doc_update.py27
-rw-r--r--extern/audaspace/plugins/sdl/SDLDevice.cpp40
-rw-r--r--extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch41
-rw-r--r--extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp2
-rw-r--r--extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp16
-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/cuew/src/cuew.c2
-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.txt72
-rw-r--r--extern/mantaflow/UPDATE.sh23
-rw-r--r--extern/mantaflow/dependencies/cnpy/LICENSE21
-rw-r--r--extern/mantaflow/dependencies/cnpy/cnpy.cpp385
-rw-r--r--extern/mantaflow/dependencies/cnpy/cnpy.h310
-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/helper/util/vectorbase.h42
-rw-r--r--extern/mantaflow/preprocessed/fileio/iogrids.cpp600
-rw-r--r--extern/mantaflow/preprocessed/fileio/iomeshes.cpp91
-rw-r--r--extern/mantaflow/preprocessed/fileio/ioparticles.cpp50
-rw-r--r--extern/mantaflow/preprocessed/fileio/ioutil.cpp73
-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/mesh.cpp38
-rw-r--r--extern/mantaflow/preprocessed/mesh.h117
-rw-r--r--extern/mantaflow/preprocessed/mesh.h.reg.cpp16
-rw-r--r--extern/mantaflow/preprocessed/particle.cpp56
-rw-r--r--extern/mantaflow/preprocessed/particle.h141
-rw-r--r--extern/mantaflow/preprocessed/particle.h.reg.cpp227
-rw-r--r--extern/mantaflow/preprocessed/plugin/initplugins.cpp144
-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.cpp8
-rw-r--r--intern/CMakeLists.txt1
-rw-r--r--intern/clog/clog.c29
-rw-r--r--intern/cycles/CMakeLists.txt1
-rw-r--r--intern/cycles/app/CMakeLists.txt2
-rw-r--r--intern/cycles/blender/CMakeLists.txt11
-rw-r--r--intern/cycles/blender/addon/engine.py30
-rw-r--r--intern/cycles/blender/addon/properties.py157
-rw-r--r--intern/cycles/blender/addon/ui.py145
-rw-r--r--intern/cycles/blender/blender_camera.cpp26
-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_id_map.h2
-rw-r--r--intern/cycles/blender/blender_mesh.cpp76
-rw-r--r--intern/cycles/blender/blender_object.cpp8
-rw-r--r--intern/cycles/blender/blender_python.cpp12
-rw-r--r--intern/cycles/blender/blender_session.cpp83
-rw-r--r--intern/cycles/blender/blender_shader.cpp9
-rw-r--r--intern/cycles/blender/blender_sync.cpp210
-rw-r--r--intern/cycles/blender/blender_sync.h35
-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/cmake/external_libs.cmake4
-rw-r--r--intern/cycles/device/CMakeLists.txt12
-rw-r--r--intern/cycles/device/cuda/device_cuda.h39
-rw-r--r--intern/cycles/device/cuda/device_cuda_impl.cpp220
-rw-r--r--intern/cycles/device/device.cpp59
-rw-r--r--intern/cycles/device/device.h15
-rw-r--r--intern/cycles/device/device_cpu.cpp350
-rw-r--r--intern/cycles/device/device_cuda.cpp1
-rw-r--r--intern/cycles/device/device_denoising.cpp60
-rw-r--r--intern/cycles/device/device_denoising.h10
-rw-r--r--intern/cycles/device/device_multi.cpp51
-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.cpp464
-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.h73
-rw-r--r--intern/cycles/device/opencl/device_opencl.h18
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp105
-rw-r--r--intern/cycles/device/opencl/memory_manager.cpp5
-rw-r--r--intern/cycles/kernel/CMakeLists.txt18
-rw-r--r--intern/cycles/kernel/bvh/bvh.h121
-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.h1357
-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_camera.h2
-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_path.h14
-rw-r--r--intern/cycles/kernel/kernel_shader.h72
-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/opencl/kernel_opencl_image.h2
-rw-r--r--intern/cycles/kernel/kernels/optix/kernel_optix.cu72
-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.osl153
-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.h304
-rw-r--r--intern/cycles/kernel/svm/svm_types.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt4
-rw-r--r--intern/cycles/render/buffers.h46
-rw-r--r--intern/cycles/render/camera.cpp40
-rw-r--r--intern/cycles/render/colorspace.cpp4
-rw-r--r--intern/cycles/render/curves.cpp110
-rw-r--r--intern/cycles/render/curves.h62
-rw-r--r--intern/cycles/render/denoising.cpp70
-rw-r--r--intern/cycles/render/denoising.h4
-rw-r--r--intern/cycles/render/geometry.cpp41
-rw-r--r--intern/cycles/render/graph.cpp8
-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.cpp94
-rw-r--r--intern/cycles/render/image_sky.h49
-rw-r--r--intern/cycles/render/integrator.cpp1
-rw-r--r--intern/cycles/render/light.cpp146
-rw-r--r--intern/cycles/render/nodes.cpp229
-rw-r--r--intern/cycles/render/nodes.h10
-rw-r--r--intern/cycles/render/object.cpp88
-rw-r--r--intern/cycles/render/scene.cpp13
-rw-r--r--intern/cycles/render/scene.h12
-rw-r--r--intern/cycles/render/session.cpp121
-rw-r--r--intern/cycles/render/session.h18
-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.txt8
-rw-r--r--intern/cycles/util/util_debug.cpp16
-rw-r--r--intern/cycles/util/util_debug.h9
-rw-r--r--intern/cycles/util/util_math.h10
-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_task.cpp430
-rw-r--r--intern/cycles/util/util_task.h102
-rw-r--r--intern/cycles/util/util_tbb.h44
-rw-r--r--intern/cycles/util/util_thread.h77
-rw-r--r--intern/cycles/util/util_version.h2
-rw-r--r--intern/ghost/CMakeLists.txt1
-rw-r--r--intern/ghost/GHOST_C-api.h4
-rw-r--r--intern/ghost/GHOST_IWindow.h4
-rw-r--r--intern/ghost/intern/GHOST_IXrGraphicsBinding.h3
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp31
-rw-r--r--intern/ghost/intern/GHOST_Window.h6
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp21
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h4
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp1
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h1
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp35
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp10
-rw-r--r--intern/ghost/intern/GHOST_XrSwapchain.cpp7
-rw-r--r--intern/ghost/intern/GHOST_XrSwapchain.h3
-rw-r--r--intern/guardedalloc/CMakeLists.txt1
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h16
-rw-r--r--intern/guardedalloc/intern/leak_detector.cc61
-rw-r--r--intern/guardedalloc/intern/mallocn_guarded_impl.c165
-rw-r--r--intern/guardedalloc/intern/mallocn_inline.h8
-rw-r--r--intern/guardedalloc/intern/mallocn_intern.h11
-rw-r--r--intern/guardedalloc/intern/mallocn_lockfree_impl.c4
-rw-r--r--intern/guardedalloc/test/simpletest/memtest.c2
-rw-r--r--intern/libc_compat/libc_compat.c12
-rw-r--r--intern/libmv/CMakeLists.txt2
-rw-r--r--intern/libmv/libmv/multiview/projection_test.cc8
-rw-r--r--intern/libmv/libmv/simple_pipeline/bundle.cc17
-rw-r--r--intern/libmv/libmv/simple_pipeline/pipeline.cc4
-rw-r--r--intern/mantaflow/extern/manta_fluid_API.h150
-rw-r--r--intern/mantaflow/intern/MANTA_main.cpp2272
-rw-r--r--intern/mantaflow/intern/MANTA_main.h133
-rw-r--r--intern/mantaflow/intern/manta_fluid_API.cpp345
-rw-r--r--intern/mantaflow/intern/strings/fluid_script.h177
-rw-r--r--intern/mantaflow/intern/strings/liquid_script.h115
-rw-r--r--intern/mantaflow/intern/strings/smoke_script.h53
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc139
-rw-r--r--intern/quadriflow/quadriflow_capi.cpp2
-rw-r--r--intern/rigidbody/RBI_api.h10
-rw-r--r--intern/rigidbody/rb_bullet_api.cpp69
-rw-r--r--intern/sky/CMakeLists.txt (renamed from tests/gtests/functions/CMakeLists.txt)28
-rw-r--r--intern/sky/include/sky_model.h (renamed from intern/cycles/util/util_sky_model.h)94
-rw-r--r--intern/sky/source/sky_float3.h157
-rw-r--r--intern/sky/source/sky_model.cpp (renamed from intern/cycles/util/util_sky_model.cpp)30
-rw-r--r--intern/sky/source/sky_model_data.h (renamed from intern/cycles/util/util_sky_model_data.h)4
-rw-r--r--intern/sky/source/sky_nishita.cpp376
-rw-r--r--release/darwin/background.tifbin33768 -> 143624 bytes
-rw-r--r--release/darwin/buildbot/background.tifbin20286 -> 220950 bytes
-rwxr-xr-xrelease/datafiles/blender_icons_geom_update.py4
-rw-r--r--release/datafiles/icons/ops.mesh.extrude_manifold.datbin0 -> 494 bytes
-rw-r--r--release/datafiles/preview_grease_pencil.blendbin947876 -> 989580 bytes
-rw-r--r--release/datafiles/userdef/userdef_default.c4
-rw-r--r--release/scripts/modules/addon_utils.py23
-rw-r--r--release/scripts/modules/bl_i18n_utils/settings.py3
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_spell_check.py6
-rw-r--r--release/scripts/modules/bl_previews_utils/bl_previews_render.py8
-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/__init__.py12
-rw-r--r--release/scripts/modules/bpy/utils/previews.py11
-rw-r--r--release/scripts/modules/rna_keymap_ui.py3
-rw-r--r--release/scripts/modules/rna_manual_reference.py135
-rw-r--r--release/scripts/modules/rna_prop_ui.py2
-rw-r--r--release/scripts/presets/keyconfig/blender.py15
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py133
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py4
-rw-r--r--release/scripts/startup/bl_operators/__init__.py2
-rw-r--r--release/scripts/startup/bl_operators/gpencil_mesh_bake.py157
-rw-r--r--release/scripts/startup/bl_operators/node.py9
-rw-r--r--release/scripts/startup/bl_operators/object.py7
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py59
-rw-r--r--release/scripts/startup/bl_operators/presets.py8
-rw-r--r--release/scripts/startup/bl_operators/screen_play_rendered_anim.py40
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py12
-rw-r--r--release/scripts/startup/bl_operators/userpref.py5
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_follow_active.py7
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_smart_project.py1053
-rw-r--r--release/scripts/startup/bl_operators/vertexpaint_dirt.py30
-rw-r--r--release/scripts/startup/bl_operators/view3d.py30
-rw-r--r--release/scripts/startup/bl_operators/wm.py39
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py2075
-rw-r--r--release/scripts/startup/bl_ui/properties_data_bone.py2
-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.py41
-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_freestyle.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py27
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_output.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py88
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_cloth.py5
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py86
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_rigidbody.py46
-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_clip.py8
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py26
-rw-r--r--release/scripts/startup/bl_ui/space_image.py65
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py32
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py38
-rw-r--r--release/scripts/startup/bl_ui/space_statusbar.py19
-rw-r--r--release/scripts/startup/bl_ui/space_text.py7
-rw-r--r--release/scripts/startup/bl_ui/space_time.py4
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_common.py38
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py159
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py67
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py54
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py25
-rw-r--r--release/scripts/startup/keyingsets_builtins.py28
-rw-r--r--release/scripts/startup/nodeitems_builtins.py34
-rw-r--r--source/CMakeLists.txt10
-rw-r--r--source/blender/CMakeLists.txt7
-rw-r--r--source/blender/blenfont/intern/blf_font.c4
-rw-r--r--source/blender/blenkernel/BKE_action.h7
-rw-r--r--source/blender/blenkernel/BKE_anim_data.h6
-rw-r--r--source/blender/blenkernel/BKE_anim_path.h14
-rw-r--r--source/blender/blenkernel/BKE_animsys.h34
-rw-r--r--source/blender/blenkernel/BKE_armature.h42
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_blendfile.h1
-rw-r--r--source/blender/blenkernel/BKE_cloth.h7
-rw-r--r--source/blender/blenkernel/BKE_collection.h18
-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_context.h2
-rw-r--r--source/blender/blenkernel/BKE_curve.h48
-rw-r--r--source/blender/blenkernel/BKE_curveprofile.h22
-rw-r--r--source/blender/blenkernel/BKE_deform.h14
-rw-r--r--source/blender/blenkernel/BKE_duplilist.h2
-rw-r--r--source/blender/blenkernel/BKE_effect.h1
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h9
-rw-r--r--source/blender/blenkernel/BKE_fcurve_driver.h3
-rw-r--r--source/blender/blenkernel/BKE_fluid.h30
-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.h30
-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_idtype.h42
-rw-r--r--source/blender/blenkernel/BKE_image.h29
-rw-r--r--source/blender/blenkernel/BKE_key.h32
-rw-r--r--source/blender/blenkernel/BKE_lattice.h80
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h17
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h21
-rw-r--r--source/blender/blenkernel/BKE_main.h1
-rw-r--r--source/blender/blenkernel/BKE_material.h3
-rw-r--r--source/blender/blenkernel/BKE_mesh.h5
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h2
-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_mesh_wrapper.h2
-rw-r--r--source/blender/blenkernel/BKE_modifier.h109
-rw-r--r--source/blender/blenkernel/BKE_movieclip.h5
-rw-r--r--source/blender/blenkernel/BKE_node.h35
-rw-r--r--source/blender/blenkernel/BKE_object.h6
-rw-r--r--source/blender/blenkernel/BKE_object_deform.h2
-rw-r--r--source/blender/blenkernel/BKE_paint.h70
-rw-r--r--source/blender/blenkernel/BKE_particle.h5
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h18
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh132
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h11
-rw-r--r--source/blender/blenkernel/BKE_screen.h10
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h10
-rw-r--r--source/blender/blenkernel/BKE_shader_fx.h7
-rw-r--r--source/blender/blenkernel/BKE_simulation.h24
-rw-r--r--source/blender/blenkernel/BKE_subdiv_ccg.h31
-rw-r--r--source/blender/blenkernel/BKE_subsurf.h1
-rw-r--r--source/blender/blenkernel/BKE_undo_system.h4
-rw-r--r--source/blender/blenkernel/CMakeLists.txt32
-rw-r--r--source/blender/blenkernel/intern/CCGSubSurf.c181
-rw-r--r--source/blender/blenkernel/intern/CCGSubSurf_legacy.c17
-rw-r--r--source/blender/blenkernel/intern/CCGSubSurf_util.c2
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c29
-rw-r--r--source/blender/blenkernel/intern/action.c23
-rw-r--r--source/blender/blenkernel/intern/anim_data.c44
-rw-r--r--source/blender/blenkernel/intern/anim_path.c96
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c196
-rw-r--r--source/blender/blenkernel/intern/anim_visualization.c6
-rw-r--r--source/blender/blenkernel/intern/appdir.c68
-rw-r--r--source/blender/blenkernel/intern/armature.c653
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c685
-rw-r--r--source/blender/blenkernel/intern/armature_test.cc (renamed from tests/gtests/blenkernel/BKE_armature_test.cc)4
-rw-r--r--source/blender/blenkernel/intern/armature_update.c7
-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.c164
-rw-r--r--source/blender/blenkernel/intern/bpath.c120
-rw-r--r--source/blender/blenkernel/intern/brush.c69
-rw-r--r--source/blender/blenkernel/intern/bvhutils.c22
-rw-r--r--source/blender/blenkernel/intern/camera.c42
-rw-r--r--source/blender/blenkernel/intern/cloth.c85
-rw-r--r--source/blender/blenkernel/intern/collection.c389
-rw-r--r--source/blender/blenkernel/intern/collision.c14
-rw-r--r--source/blender/blenkernel/intern/colortools.c28
-rw-r--r--source/blender/blenkernel/intern/constraint.c63
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenkernel/intern/crazyspace.c8
-rw-r--r--source/blender/blenkernel/intern/curve.c332
-rw-r--r--source/blender/blenkernel/intern/curve_bevel.c272
-rw-r--r--source/blender/blenkernel/intern/curve_deform.c504
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c295
-rw-r--r--source/blender/blenkernel/intern/customdata.c199
-rw-r--r--source/blender/blenkernel/intern/data_transfer.c2
-rw-r--r--source/blender/blenkernel/intern/deform.c14
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c34
-rw-r--r--source/blender/blenkernel/intern/editmesh.c11
-rw-r--r--source/blender/blenkernel/intern/effect.c46
-rw-r--r--source/blender/blenkernel/intern/fcurve.c21
-rw-r--r--source/blender/blenkernel/intern/fcurve_driver.c392
-rw-r--r--source/blender/blenkernel/intern/fcurve_test.cc (renamed from tests/gtests/blenkernel/BKE_fcurve_test.cc)6
-rw-r--r--source/blender/blenkernel/intern/fluid.c2791
-rw-r--r--source/blender/blenkernel/intern/font.c18
-rw-r--r--source/blender/blenkernel/intern/gpencil.c434
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c3
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c1050
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c522
-rw-r--r--source/blender/blenkernel/intern/idprop.c4
-rw-r--r--source/blender/blenkernel/intern/idtype.c50
-rw-r--r--source/blender/blenkernel/intern/image.c135
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c777
-rw-r--r--source/blender/blenkernel/intern/image_save.c6
-rw-r--r--source/blender/blenkernel/intern/key.c169
-rw-r--r--source/blender/blenkernel/intern/lattice.c648
-rw-r--r--source/blender/blenkernel/intern/lattice_deform.c470
-rw-r--r--source/blender/blenkernel/intern/layer.c32
-rw-r--r--source/blender/blenkernel/intern/lib_id.c112
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c8
-rw-r--r--source/blender/blenkernel/intern/lib_override.c675
-rw-r--r--source/blender/blenkernel/intern/lib_query.c3
-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/mask.c2
-rw-r--r--source/blender/blenkernel/intern/mask_rasterize.c8
-rw-r--r--source/blender/blenkernel/intern/material.c18
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c14
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c84
-rw-r--r--source/blender/blenkernel/intern/mesh_mapping.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.c31
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.c49
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.c12
-rw-r--r--source/blender/blenkernel/intern/modifier.c7
-rw-r--r--source/blender/blenkernel/intern/movieclip.c125
-rw-r--r--source/blender/blenkernel/intern/multires.c2
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c2
-rw-r--r--source/blender/blenkernel/intern/multires_unsubdivide.c2
-rw-r--r--source/blender/blenkernel/intern/node.c55
-rw-r--r--source/blender/blenkernel/intern/object.c474
-rw-r--r--source/blender/blenkernel/intern/object_deform.c2
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c6
-rw-r--r--source/blender/blenkernel/intern/object_update.c4
-rw-r--r--source/blender/blenkernel/intern/ocean.c12
-rw-r--r--source/blender/blenkernel/intern/packedFile.c30
-rw-r--r--source/blender/blenkernel/intern/paint.c47
-rw-r--r--source/blender/blenkernel/intern/particle.c84
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c2
-rw-r--r--source/blender/blenkernel/intern/particle_system.c104
-rw-r--r--source/blender/blenkernel/intern/pbvh.c42
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h3
-rw-r--r--source/blender/blenkernel/intern/pointcache.c658
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c130
-rw-r--r--source/blender/blenkernel/intern/scene.c93
-rw-r--r--source/blender/blenkernel/intern/screen.c26
-rw-r--r--source/blender/blenkernel/intern/seqeffects.c14
-rw-r--r--source/blender/blenkernel/intern/seqmodifier.c70
-rw-r--r--source/blender/blenkernel/intern/seqprefetch.c7
-rw-r--r--source/blender/blenkernel/intern/sequencer.c907
-rw-r--r--source/blender/blenkernel/intern/shader_fx.c20
-rw-r--r--source/blender/blenkernel/intern/shrinkwrap.c12
-rw-r--r--source/blender/blenkernel/intern/simulation.cc308
-rw-r--r--source/blender/blenkernel/intern/softbody.c24
-rw-r--r--source/blender/blenkernel/intern/sound.c23
-rw-r--r--source/blender/blenkernel/intern/studiolight.c7
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c93
-rw-r--r--source/blender/blenkernel/intern/text.c257
-rw-r--r--source/blender/blenkernel/intern/tracking.c2
-rw-r--r--source/blender/blenkernel/intern/undo_system.c4
-rw-r--r--source/blender/blenkernel/intern/volume.cc17
-rw-r--r--source/blender/blenkernel/nla_private.h10
-rw-r--r--source/blender/blenlib/BLI_allocator.hh4
-rw-r--r--source/blender/blenlib/BLI_array.hh193
-rw-r--r--source/blender/blenlib/BLI_blenlib.h2
-rw-r--r--source/blender/blenlib/BLI_color.hh39
-rw-r--r--source/blender/blenlib/BLI_disjoint_set.hh106
-rw-r--r--source/blender/blenlib/BLI_dot_export.hh89
-rw-r--r--source/blender/blenlib/BLI_dot_export_attribute_enums.hh6
-rw-r--r--source/blender/blenlib/BLI_float2.hh38
-rw-r--r--source/blender/blenlib/BLI_float3.hh129
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh56
-rw-r--r--source/blender/blenlib/BLI_ghash.h14
-rw-r--r--source/blender/blenlib/BLI_hash.hh101
-rw-r--r--source/blender/blenlib/BLI_hash_tables.hh175
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh56
-rw-r--r--source/blender/blenlib/BLI_index_range.hh100
-rw-r--r--source/blender/blenlib/BLI_kdopbvh.h2
-rw-r--r--source/blender/blenlib/BLI_linear_allocator.hh85
-rw-r--r--source/blender/blenlib/BLI_linklist.h1
-rw-r--r--source/blender/blenlib/BLI_listbase_wrapper.hh32
-rw-r--r--source/blender/blenlib/BLI_map.hh441
-rw-r--r--source/blender/blenlib/BLI_map_slots.hh144
-rw-r--r--source/blender/blenlib/BLI_math_base_safe.h50
-rw-r--r--source/blender/blenlib/BLI_math_color.h8
-rw-r--r--source/blender/blenlib/BLI_math_geom.h21
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h3
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh283
-rw-r--r--source/blender/blenlib/BLI_multi_value_map.hh134
-rw-r--r--source/blender/blenlib/BLI_optional.hh189
-rw-r--r--source/blender/blenlib/BLI_probing_strategies.hh86
-rw-r--r--source/blender/blenlib/BLI_rand.h6
-rw-r--r--source/blender/blenlib/BLI_rand.hh147
-rw-r--r--source/blender/blenlib/BLI_rect.h1
-rw-r--r--source/blender/blenlib/BLI_resource_collector.hh151
-rw-r--r--source/blender/blenlib/BLI_set.hh346
-rw-r--r--source/blender/blenlib/BLI_set_slots.hh160
-rw-r--r--source/blender/blenlib/BLI_span.hh230
-rw-r--r--source/blender/blenlib/BLI_stack.hh198
-rw-r--r--source/blender/blenlib/BLI_string_ref.hh115
-rw-r--r--source/blender/blenlib/BLI_threads.h18
-rw-r--r--source/blender/blenlib/BLI_timeit.hh22
-rw-r--r--source/blender/blenlib/BLI_utildefines.h47
-rw-r--r--source/blender/blenlib/BLI_vector.hh451
-rw-r--r--source/blender/blenlib/BLI_vector_adaptor.hh105
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh367
-rw-r--r--source/blender/blenlib/BLI_vector_set_slots.hh36
-rw-r--r--source/blender/blenlib/BLI_voxel.h11
-rw-r--r--source/blender/blenlib/CMakeLists.txt39
-rw-r--r--source/blender/blenlib/intern/BLI_ghash_utils.c19
-rw-r--r--source/blender/blenlib/intern/BLI_index_range.cc22
-rw-r--r--source/blender/blenlib/intern/BLI_kdopbvh.c89
-rw-r--r--source/blender/blenlib/intern/BLI_linklist.c10
-rw-r--r--source/blender/blenlib/intern/bitmap.c2
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.c45
-rw-r--r--source/blender/blenlib/intern/dot_export.cc106
-rw-r--r--source/blender/blenlib/intern/edgehash.c2
-rw-r--r--source/blender/blenlib/intern/expr_pylike_eval.c92
-rw-r--r--source/blender/blenlib/intern/fileops.c8
-rw-r--r--source/blender/blenlib/intern/math_base_safe_inline.c79
-rw-r--r--source/blender/blenlib/intern/math_bits_inline.c2
-rw-r--r--source/blender/blenlib/intern/math_color.c8
-rw-r--r--source/blender/blenlib/intern/math_geom.c118
-rw-r--r--source/blender/blenlib/intern/math_matrix.c18
-rw-r--r--source/blender/blenlib/intern/math_rotation.c6
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c16
-rw-r--r--source/blender/blenlib/intern/noise.c4
-rw-r--r--source/blender/blenlib/intern/path_util.c2
-rw-r--r--source/blender/blenlib/intern/rand.cc (renamed from source/blender/blenlib/intern/rand.c)235
-rw-r--r--source/blender/blenlib/intern/rct.c8
-rw-r--r--source/blender/blenlib/intern/storage.c19
-rw-r--r--source/blender/blenlib/intern/string.c34
-rw-r--r--source/blender/blenlib/intern/system.c6
-rw-r--r--source/blender/blenlib/intern/task_range.cc2
-rw-r--r--source/blender/blenlib/intern/threads.cc (renamed from source/blender/blenlib/intern/threads.c)157
-rw-r--r--source/blender/blenlib/intern/timecode.c4
-rw-r--r--source/blender/blenlib/intern/timeit.cc6
-rw-r--r--source/blender/blenlib/intern/voronoi_2d.c2
-rw-r--r--source/blender/blenlib/intern/voxel.c13
-rw-r--r--source/blender/blenlib/tests/BLI_array_test.cc (renamed from tests/gtests/blenlib/BLI_array_test.cc)56
-rw-r--r--source/blender/blenlib/tests/BLI_disjoint_set_test.cc36
-rw-r--r--source/blender/blenlib/tests/BLI_edgehash_test.cc (renamed from tests/gtests/blenlib/BLI_edgehash_test.cc)11
-rw-r--r--source/blender/blenlib/tests/BLI_index_mask_test.cc (renamed from tests/gtests/blenlib/BLI_index_mask_test.cc)8
-rw-r--r--source/blender/blenlib/tests/BLI_index_range_test.cc (renamed from tests/gtests/blenlib/BLI_index_range_test.cc)22
-rw-r--r--source/blender/blenlib/tests/BLI_linear_allocator_test.cc (renamed from tests/gtests/blenlib/BLI_linear_allocator_test.cc)6
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc (renamed from tests/gtests/blenlib/BLI_map_test.cc)81
-rw-r--r--source/blender/blenlib/tests/BLI_math_base_safe_test.cc37
-rw-r--r--source/blender/blenlib/tests/BLI_memory_utils_test.cc159
-rw-r--r--source/blender/blenlib/tests/BLI_multi_value_map_test.cc109
-rw-r--r--source/blender/blenlib/tests/BLI_set_test.cc (renamed from tests/gtests/blenlib/BLI_set_test.cc)86
-rw-r--r--source/blender/blenlib/tests/BLI_span_test.cc (renamed from tests/gtests/blenlib/BLI_span_test.cc)31
-rw-r--r--source/blender/blenlib/tests/BLI_stack_cxx_test.cc (renamed from tests/gtests/blenlib/BLI_stack_cxx_test.cc)18
-rw-r--r--source/blender/blenlib/tests/BLI_string_ref_test.cc (renamed from tests/gtests/blenlib/BLI_string_ref_test.cc)8
-rw-r--r--source/blender/blenlib/tests/BLI_vector_set_test.cc (renamed from tests/gtests/blenlib/BLI_vector_set_test.cc)10
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc (renamed from tests/gtests/blenlib/BLI_vector_test.cc)93
-rw-r--r--source/blender/blenloader/BLO_read_write.h2
-rw-r--r--source/blender/blenloader/BLO_readfile.h26
-rw-r--r--source/blender/blenloader/BLO_writefile.h40
-rw-r--r--source/blender/blenloader/CMakeLists.txt2
-rw-r--r--source/blender/blenloader/intern/blend_validate.c14
-rw-r--r--source/blender/blenloader/intern/readblenentry.c37
-rw-r--r--source/blender/blenloader/intern/readfile.c2579
-rw-r--r--source/blender/blenloader/intern/readfile.h21
-rw-r--r--source/blender/blenloader/intern/versioning_250.c26
-rw-r--r--source/blender/blenloader/intern/versioning_260.c72
-rw-r--r--source/blender/blenloader/intern/versioning_270.c18
-rw-r--r--source/blender/blenloader/intern/versioning_280.c37
-rw-r--r--source/blender/blenloader/intern/versioning_290.c160
-rw-r--r--source/blender/blenloader/intern/versioning_cycles.c78
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c18
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c12
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c6
-rw-r--r--source/blender/blenloader/intern/writefile.c378
-rw-r--r--source/blender/bmesh/CMakeLists.txt6
-rw-r--r--source/blender/bmesh/bmesh.h1
-rw-r--r--source/blender/bmesh/bmesh_tools.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c197
-rw-r--r--source/blender/bmesh/intern/bmesh_edgeloop.c8
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c29
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.c14
-rw-r--r--source/blender/bmesh/intern/bmesh_log.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_marking.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c41
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.c10
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c28
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c24
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.h15
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c12
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c11
-rw-r--r--source/blender/bmesh/intern/bmesh_query.c157
-rw-r--r--source/blender/bmesh/intern/bmesh_query.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_query_uv.c169
-rw-r--r--source/blender/bmesh/intern/bmesh_query_uv.h58
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_impl.c49
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c8
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c49
-rw-r--r--source/blender/bmesh/operators/bmo_connect_concave.c25
-rw-r--r--source/blender/bmesh/operators/bmo_connect_pair.c20
-rw-r--r--source/blender/bmesh/operators/bmo_create.c2
-rw-r--r--source/blender/bmesh/operators/bmo_edgenet.c6
-rw-r--r--source/blender/bmesh/operators/bmo_extrude.c9
-rw-r--r--source/blender/bmesh/operators/bmo_inset.c12
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.c2
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide_edgering.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c721
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.h4
-rw-r--r--source/blender/bmesh/tools/bmesh_bisect_plane.c12
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_collapse.c23
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_edgenet.c81
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect.c8
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect_edges.c38
-rw-r--r--source/blender/bmesh/tools/bmesh_path.c126
-rw-r--r--source/blender/bmesh/tools/bmesh_path_region.c6
-rw-r--r--source/blender/bmesh/tools/bmesh_path_region_uv.c502
-rw-r--r--source/blender/bmesh/tools/bmesh_path_region_uv.h48
-rw-r--r--source/blender/bmesh/tools/bmesh_path_uv.c433
-rw-r--r--source/blender/bmesh/tools/bmesh_path_uv.h47
-rw-r--r--source/blender/bmesh/tools/bmesh_region_match.c28
-rw-r--r--source/blender/bmesh/tools/bmesh_wireframe.c14
-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/intern/COM_WorkScheduler.cpp8
-rw-r--r--source/blender/compositor/nodes/COM_FlipNode.cpp4
-rw-r--r--source/blender/compositor/operations/COM_ColorCorrectionOperation.cpp15
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp28
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp2
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.cpp18
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_NormalizeOperation.cpp4
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.cpp21
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cpp4
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.cpp3
-rw-r--r--source/blender/depsgraph/CMakeLists.txt13
-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.cc25
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.h22
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cycle.cc14
-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.cc53
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h13
-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.cc287
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h12
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.cc258
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h76
-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.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc41
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.h19
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna_test.cc60
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_transitive.cc8
-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.cc21
-rw-r--r--source/blender/depsgraph/intern/debug/deg_time_average.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc49
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h10
-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.cc34
-rw-r--r--source/blender/depsgraph/intern/depsgraph_registry.cc22
-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.h10
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc55
-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.cc32
-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.cc56
-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.cc8
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.h10
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc16
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h17
-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.h8
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc10
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h8
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc12
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h9
-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.txt28
-rw-r--r--source/blender/draw/DRW_engine.h4
-rw-r--r--source/blender/draw/DRW_select_buffer.h14
-rw-r--r--source/blender/draw/engines/basic/basic_engine.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c145
-rw-r--r--source/blender/draw/engines/eevee/eevee_depth_of_field.c36
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c31
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c141
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c10
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c47
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c165
-rw-r--r--source/blender/draw/engines/eevee/eevee_lut_gen.c31
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c157
-rw-r--r--source/blender/draw/engines/eevee/eevee_mist.c18
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c594
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c21
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h145
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c82
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c47
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c26
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c465
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c33
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c63
-rw-r--r--source/blender/draw/engines/eevee/eevee_temporal_sampling.c49
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c141
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl14
-rw-r--r--source/blender/draw/engines/eevee/shaders/background_vert.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl867
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl5
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl5
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_lib.glsl181
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl (renamed from source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl)42
-rw-r--r--source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl65
-rw-r--r--source/blender/draw/engines/eevee/shaders/concentric_samples_lib.glsl265
-rw-r--r--source/blender/draw/engines/eevee/shaders/default_frag.glsl51
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl38
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl5
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl244
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl9
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl29
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl25
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl13
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl22
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl151
-rw-r--r--source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl80
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl31
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl20
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/lights_lib.glsl95
-rw-r--r--source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/default_world_frag.glsl)43
-rw-r--r--source/blender/draw/engines/eevee/shaders/ltc_lib.glsl6
-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.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/prepass_frag.glsl14
-rw-r--r--source/blender/draw/engines/eevee/shaders/prepass_vert.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl43
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl6
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_vert.glsl36
-rw-r--r--source/blender/draw/engines/eevee/shaders/ssr_lib.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_frag.glsl89
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_geom.glsl46
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_lib.glsl43
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_vert.glsl (renamed from source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl)33
-rw-r--r--source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl5
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl22
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl8
-rw-r--r--source/blender/draw/engines/external/external_engine.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c12
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c27
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_render.c1
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_fx.c8
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl32
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c18
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_curve.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c7
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_text.c29
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c10
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c83
-rw-r--r--source/blender/draw/engines/overlay/overlay_facing.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_grid.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_image.c16
-rw-r--r--source/blender/draw/engines/overlay/overlay_metaball.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_outline.c46
-rw-r--r--source/blender/draw/engines/overlay/overlay_paint.c17
-rw-r--r--source/blender/draw/engines/overlay/overlay_pointcloud.c72
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h18
-rw-r--r--source/blender/draw/engines/overlay/overlay_sculpt.c3
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader.c88
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.c23
-rw-r--r--source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl2
-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/armature_sphere_solid_frag.glsl13
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/grid_frag.glsl9
-rw-r--r--source/blender/draw/engines/overlay/shaders/grid_vert.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/image_frag.glsl3
-rw-r--r--source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl6
-rw-r--r--source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl16
-rw-r--r--source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl27
-rw-r--r--source/blender/draw/engines/select/select_engine.c2
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl7
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl32
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl2
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl1
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl10
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl38
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl15
-rw-r--r--source/blender/draw/engines/workbench/workbench_data.c40
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c72
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c26
-rw-r--r--source/blender/draw/engines/workbench/workbench_opaque.c24
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h38
-rw-r--r--source/blender/draw/engines/workbench/workbench_render.c1
-rw-r--r--source/blender/draw/engines/workbench/workbench_shader.c54
-rw-r--r--source/blender/draw/engines/workbench/workbench_transparent.c30
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c69
-rw-r--r--source/blender/draw/intern/DRW_render.h38
-rw-r--r--source/blender/draw/intern/draw_cache.c48
-rw-r--r--source/blender/draw/intern/draw_cache.h4
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h10
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c3877
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h30
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.c17
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c22
-rw-r--r--source/blender/draw/intern/draw_cache_impl_lattice.c2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c289
-rw-r--r--source/blender/draw/intern/draw_cache_impl_metaball.c12
-rw-r--r--source/blender/draw/intern/draw_cache_impl_pointcloud.c164
-rw-r--r--source/blender/draw/intern/draw_cache_impl_volume.c2
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h24
-rw-r--r--source/blender/draw/intern/draw_common.c7
-rw-r--r--source/blender/draw/intern/draw_common.h25
-rw-r--r--source/blender/draw/intern/draw_fluid.c (renamed from source/blender/gpu/intern/gpu_draw_smoke.c)229
-rw-r--r--source/blender/draw/intern/draw_hair.c159
-rw-r--r--source/blender/draw/intern/draw_manager.c183
-rw-r--r--source/blender/draw/intern/draw_manager.h6
-rw-r--r--source/blender/draw/intern/draw_manager_data.c107
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c29
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c81
-rw-r--r--source/blender/draw/intern/draw_manager_text.c6
-rw-r--r--source/blender/draw/intern/draw_select_buffer.c26
-rw-r--r--source/blender/draw/intern/draw_view.c11
-rw-r--r--source/blender/draw/intern/shaders/common_globals_lib.glsl14
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl22
-rw-r--r--source/blender/draw/intern/shaders/common_math_geom_lib.glsl119
-rw-r--r--source/blender/draw/intern/shaders/common_math_lib.glsl130
-rw-r--r--source/blender/draw/intern/shaders/common_pointcloud_lib.glsl39
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl112
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c104
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c24
-rw-r--r--source/blender/editors/animation/anim_draw.c4
-rw-r--r--source/blender/editors/animation/anim_filter.c27
-rw-r--r--source/blender/editors/animation/anim_ipo_utils.c3
-rw-r--r--source/blender/editors/animation/anim_markers.c19
-rw-r--r--source/blender/editors/animation/drivers.c30
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c3
-rw-r--r--source/blender/editors/animation/keyframes_draw.c37
-rw-r--r--source/blender/editors/animation/keyframes_edit.c16
-rw-r--r--source/blender/editors/animation/keyframes_general.c2
-rw-r--r--source/blender/editors/animation/keyframing.c201
-rw-r--r--source/blender/editors/animation/keyingsets.c45
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c49
-rw-r--r--source/blender/editors/armature/armature_add.c17
-rw-r--r--source/blender/editors/armature/armature_edit.c26
-rw-r--r--source/blender/editors/armature/armature_relations.c36
-rw-r--r--source/blender/editors/armature/armature_select.c19
-rw-r--r--source/blender/editors/armature/armature_skinning.c4
-rw-r--r--source/blender/editors/armature/armature_utils.c6
-rw-r--r--source/blender/editors/armature/meshlaplacian.c15
-rw-r--r--source/blender/editors/armature/meshlaplacian.h2
-rw-r--r--source/blender/editors/armature/pose_group.c17
-rw-r--r--source/blender/editors/armature/pose_lib.c56
-rw-r--r--source/blender/editors/armature/pose_select.c6
-rw-r--r--source/blender/editors/armature/pose_slide.c52
-rw-r--r--source/blender/editors/armature/pose_transform.c15
-rw-r--r--source/blender/editors/curve/editcurve.c43
-rw-r--r--source/blender/editors/curve/editcurve_query.c6
-rw-r--r--source/blender/editors/curve/editcurve_select.c29
-rw-r--r--source/blender/editors/curve/editfont.c68
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt1
-rw-r--r--source/blender/editors/gizmo_library/gizmo_draw_utils.c5
-rw-r--r--source/blender/editors/gizmo_library/gizmo_library_utils.c30
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c3
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c6
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c4
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c64
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c577
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c61
-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.c513
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c374
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c408
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c161
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h105
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c80
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c23
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c409
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c151
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c459
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c153
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c376
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c252
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c362
-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.c143
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c112
-rw-r--r--source/blender/editors/include/BIF_glutil.h37
-rw-r--r--source/blender/editors/include/ED_anim_api.h29
-rw-r--r--source/blender/editors/include/ED_armature.h3
-rw-r--r--source/blender/editors/include/ED_buttons.h4
-rw-r--r--source/blender/editors/include/ED_curve.h2
-rw-r--r--source/blender/editors/include/ED_fileselect.h2
-rw-r--r--source/blender/editors/include/ED_gpencil.h90
-rw-r--r--source/blender/editors/include/ED_image.h4
-rw-r--r--source/blender/editors/include/ED_info.h6
-rw-r--r--source/blender/editors/include/ED_keyframing.h7
-rw-r--r--source/blender/editors/include/ED_mball.h8
-rw-r--r--source/blender/editors/include/ED_mesh.h16
-rw-r--r--source/blender/editors/include/ED_node.h4
-rw-r--r--source/blender/editors/include/ED_numinput.h8
-rw-r--r--source/blender/editors/include/ED_object.h25
-rw-r--r--source/blender/editors/include/ED_render.h2
-rw-r--r--source/blender/editors/include/ED_screen.h1
-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/ED_uvedit.h77
-rw-r--r--source/blender/editors/include/ED_view3d.h7
-rw-r--r--source/blender/editors/include/UI_interface.h50
-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.c371
-rw-r--r--source/blender/editors/interface/interface_align.c65
-rw-r--r--source/blender/editors/interface/interface_anim.c8
-rw-r--r--source/blender/editors/interface/interface_context_menu.c18
-rw-r--r--source/blender/editors/interface/interface_draw.c303
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c4
-rw-r--r--source/blender/editors/interface/interface_eyedropper_color.c10
-rw-r--r--source/blender/editors/interface/interface_eyedropper_colorband.c8
-rw-r--r--source/blender/editors/interface/interface_eyedropper_datablock.c14
-rw-r--r--source/blender/editors/interface/interface_eyedropper_depth.c8
-rw-r--r--source/blender/editors/interface/interface_eyedropper_driver.c86
-rw-r--r--source/blender/editors/interface/interface_eyedropper_gpencil_color.c8
-rw-r--r--source/blender/editors/interface/interface_handlers.c298
-rw-r--r--source/blender/editors/interface/interface_icons.c177
-rw-r--r--source/blender/editors/interface/interface_intern.h26
-rw-r--r--source/blender/editors/interface/interface_layout.c176
-rw-r--r--source/blender/editors/interface/interface_ops.c78
-rw-r--r--source/blender/editors/interface/interface_panel.c523
-rw-r--r--source/blender/editors/interface/interface_query.c10
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.c50
-rw-r--r--source/blender/editors/interface/interface_region_search.c67
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c24
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c32
-rw-r--r--source/blender/editors/interface/interface_template_search_operator.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c907
-rw-r--r--source/blender/editors/interface/interface_undo.c4
-rw-r--r--source/blender/editors/interface/interface_utils.c74
-rw-r--r--source/blender/editors/interface/interface_widgets.c293
-rw-r--r--source/blender/editors/interface/resources.c4
-rw-r--r--source/blender/editors/interface/view2d.c115
-rw-r--r--source/blender/editors/interface/view2d_draw.c63
-rw-r--r--source/blender/editors/interface/view2d_ops.c157
-rw-r--r--source/blender/editors/io/CMakeLists.txt4
-rw-r--r--source/blender/editors/io/io_alembic.c159
-rw-r--r--source/blender/editors/io/io_collada.c268
-rw-r--r--source/blender/editors/io/io_usd.c12
-rw-r--r--source/blender/editors/lattice/editlattice_select.c13
-rw-r--r--source/blender/editors/mask/mask_add.c10
-rw-r--r--source/blender/editors/mask/mask_draw.c3
-rw-r--r--source/blender/editors/mask/mask_ops.c18
-rw-r--r--source/blender/editors/mask/mask_select.c67
-rw-r--r--source/blender/editors/mask/mask_shapekey.c16
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c228
-rw-r--r--source/blender/editors/mesh/editmesh_extrude.c7
-rw-r--r--source/blender/editors/mesh/editmesh_inset.c314
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c22
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c552
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c11
-rw-r--r--source/blender/editors/mesh/editmesh_loopcut.c9
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c4
-rw-r--r--source/blender/editors/mesh/editmesh_path.c4
-rw-r--r--source/blender/editors/mesh/editmesh_polybuild.c18
-rw-r--r--source/blender/editors/mesh/editmesh_rip.c137
-rw-r--r--source/blender/editors/mesh/editmesh_select.c323
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c64
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c101
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c2
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c55
-rw-r--r--source/blender/editors/mesh/mesh_data.c200
-rw-r--r--source/blender/editors/mesh/mesh_intern.h3
-rw-r--r--source/blender/editors/mesh/mesh_mirror.c14
-rw-r--r--source/blender/editors/mesh/mesh_ops.c6
-rw-r--r--source/blender/editors/mesh/meshtools.c81
-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.c285
-rw-r--r--source/blender/editors/object/object_bake.c4
-rw-r--r--source/blender/editors/object/object_bake_api.c34
-rw-r--r--source/blender/editors/object/object_collection.c4
-rw-r--r--source/blender/editors/object/object_constraint.c223
-rw-r--r--source/blender/editors/object/object_data_transfer.c42
-rw-r--r--source/blender/editors/object/object_data_transform.c213
-rw-r--r--source/blender/editors/object/object_edit.c54
-rw-r--r--source/blender/editors/object/object_facemap_ops.c7
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c239
-rw-r--r--source/blender/editors/object/object_hook.c8
-rw-r--r--source/blender/editors/object/object_intern.h9
-rw-r--r--source/blender/editors/object/object_modes.c2
-rw-r--r--source/blender/editors/object/object_modifier.c400
-rw-r--r--source/blender/editors/object/object_ops.c6
-rw-r--r--source/blender/editors/object/object_relations.c653
-rw-r--r--source/blender/editors/object/object_remesh.c24
-rw-r--r--source/blender/editors/object/object_select.c39
-rw-r--r--source/blender/editors/object/object_shader_fx.c195
-rw-r--r--source/blender/editors/object/object_shapekey.c36
-rw-r--r--source/blender/editors/object/object_transform.c58
-rw-r--r--source/blender/editors/object/object_utils.c12
-rw-r--r--source/blender/editors/object/object_vgroup.c335
-rw-r--r--source/blender/editors/object/object_volume.c2
-rw-r--r--source/blender/editors/physics/dynamicpaint_ops.c10
-rw-r--r--source/blender/editors/physics/particle_edit.c20
-rw-r--r--source/blender/editors/physics/particle_object.c8
-rw-r--r--source/blender/editors/physics/physics_fluid.c199
-rw-r--r--source/blender/editors/physics/physics_pointcache.c2
-rw-r--r--source/blender/editors/physics/rigidbody_constraint.c12
-rw-r--r--source/blender/editors/physics/rigidbody_object.c35
-rw-r--r--source/blender/editors/render/render_internal.c10
-rw-r--r--source/blender/editors/render/render_opengl.c25
-rw-r--r--source/blender/editors/render/render_preview.c15
-rw-r--r--source/blender/editors/render/render_shading.c42
-rw-r--r--source/blender/editors/render/render_update.c4
-rw-r--r--source/blender/editors/render/render_view.c4
-rw-r--r--source/blender/editors/scene/scene_edit.c6
-rw-r--r--source/blender/editors/screen/area.c60
-rw-r--r--source/blender/editors/screen/glutil.c394
-rw-r--r--source/blender/editors/screen/screen_context.c118
-rw-r--r--source/blender/editors/screen/screen_draw.c8
-rw-r--r--source/blender/editors/screen/screen_edit.c1
-rw-r--r--source/blender/editors/screen/screen_geometry.c31
-rw-r--r--source/blender/editors/screen/screen_ops.c256
-rw-r--r--source/blender/editors/screen/screendump.c8
-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.c188
-rw-r--r--source/blender/editors/sculpt_paint/paint_curve.c16
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c13
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c35
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c10
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c277
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c13
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c36
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c20
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c19
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c26
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_ops.c30
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_utils.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c1562
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c93
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c252
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c11
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c327
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c55
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h97
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c7
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c492
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c30
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c267
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c13
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c82
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c4
-rw-r--r--source/blender/editors/sound/sound_ops.c8
-rw-r--r--source/blender/editors/space_action/action_data.c81
-rw-r--r--source/blender/editors/space_action/action_edit.c35
-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_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c311
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c111
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c1
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c124
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c4
-rw-r--r--source/blender/editors/space_clip/clip_draw.c66
-rw-r--r--source/blender/editors/space_clip/clip_editor.c29
-rw-r--r--source/blender/editors/space_clip/clip_ops.c56
-rw-r--r--source/blender/editors/space_clip/space_clip.c17
-rw-r--r--source/blender/editors/space_clip/tracking_ops_detect.c8
-rw-r--r--source/blender/editors/space_clip/tracking_ops_orient.c4
-rw-r--r--source/blender/editors/space_clip/tracking_ops_plane.c19
-rw-r--r--source/blender/editors/space_clip/tracking_ops_solve.c2
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c23
-rw-r--r--source/blender/editors/space_clip/tracking_select.c4
-rw-r--r--source/blender/editors/space_console/console_draw.c1
-rw-r--r--source/blender/editors/space_console/console_ops.c46
-rw-r--r--source/blender/editors/space_console/space_console.c7
-rw-r--r--source/blender/editors/space_file/file_draw.c5
-rw-r--r--source/blender/editors/space_file/file_ops.c2
-rw-r--r--source/blender/editors/space_file/filelist.c199
-rw-r--r--source/blender/editors/space_file/filesel.c15
-rw-r--r--source/blender/editors/space_file/fsmenu.c56
-rw-r--r--source/blender/editors/space_file/space_file.c7
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c2
-rw-r--r--source/blender/editors/space_graph/graph_draw.c4
-rw-r--r--source/blender/editors/space_graph/graph_edit.c72
-rw-r--r--source/blender/editors/space_graph/graph_select.c9
-rw-r--r--source/blender/editors/space_graph/graph_utils.c2
-rw-r--r--source/blender/editors/space_graph/space_graph.c36
-rw-r--r--source/blender/editors/space_image/image_buttons.c18
-rw-r--r--source/blender/editors/space_image/image_draw.c42
-rw-r--r--source/blender/editors/space_image/image_edit.c2
-rw-r--r--source/blender/editors/space_image/image_ops.c181
-rw-r--r--source/blender/editors/space_image/image_undo.c7
-rw-r--r--source/blender/editors/space_info/info_draw.c25
-rw-r--r--source/blender/editors/space_info/info_stats.c242
-rw-r--r--source/blender/editors/space_info/space_info.c7
-rw-r--r--source/blender/editors/space_info/textview.c2
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c6
-rw-r--r--source/blender/editors/space_nla/nla_channels.c35
-rw-r--r--source/blender/editors/space_nla/nla_draw.c5
-rw-r--r--source/blender/editors/space_nla/nla_edit.c46
-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.c78
-rw-r--r--source/blender/editors/space_node/node_add.c4
-rw-r--r--source/blender/editors/space_node/node_draw.c28
-rw-r--r--source/blender/editors/space_node/node_edit.c17
-rw-r--r--source/blender/editors/space_node/node_group.c10
-rw-r--r--source/blender/editors/space_node/node_relationships.c28
-rw-r--r--source/blender/editors/space_node/node_select.c6
-rw-r--r--source/blender/editors/space_node/node_templates.c4
-rw-r--r--source/blender/editors/space_node/node_view.c12
-rw-r--r--source/blender/editors/space_node/space_node.c21
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c15
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c84
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c197
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c68
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h51
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c360
-rw-r--r--source/blender/editors/space_outliner/outliner_sync.c29
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c555
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c93
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c47
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c9
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c91
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c624
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h24
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c14
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c22
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c22
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c354
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c9
-rw-r--r--source/blender/editors/space_text/space_text.c2
-rw-r--r--source/blender/editors/space_text/text_autocomplete.c19
-rw-r--r--source/blender/editors/space_text/text_draw.c22
-rw-r--r--source/blender/editors/space_text/text_format.c7
-rw-r--r--source/blender/editors/space_text/text_format_lua.c2
-rw-r--r--source/blender/editors/space_text/text_format_osl.c2
-rw-r--r--source/blender/editors/space_text/text_format_pov.c2
-rw-r--r--source/blender/editors/space_text/text_format_pov_ini.c2
-rw-r--r--source/blender/editors/space_text/text_format_py.c2
-rw-r--r--source/blender/editors/space_text/text_ops.c112
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c53
-rw-r--r--source/blender/editors/space_view3d/view3d_camera_control.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c80
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c96
-rw-r--r--source/blender/editors/space_view3d/view3d_fly.c38
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c171
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c74
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c54
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c24
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c33
-rw-r--r--source/blender/editors/transform/transform.c100
-rw-r--r--source/blender/editors/transform/transform.h1
-rw-r--r--source/blender/editors/transform/transform_constraints.c175
-rw-r--r--source/blender/editors/transform/transform_constraints.h6
-rw-r--r--source/blender/editors/transform/transform_convert.c194
-rw-r--r--source/blender/editors/transform/transform_convert.h6
-rw-r--r--source/blender/editors/transform/transform_convert_action.c13
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c54
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c64
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c58
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c8
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c3
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c6
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c1413
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_edge.c3
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c260
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c1
-rw-r--r--source/blender/editors/transform/transform_convert_object.c21
-rw-r--r--source/blender/editors/transform/transform_convert_paintcurve.c12
-rw-r--r--source/blender/editors/transform/transform_convert_particle.c4
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c1
-rw-r--r--source/blender/editors/transform/transform_data.h4
-rw-r--r--source/blender/editors/transform/transform_generics.c66
-rw-r--r--source/blender/editors/transform/transform_gizmo_2d.c3
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c8
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c1
-rw-r--r--source/blender/editors/transform/transform_input.c8
-rw-r--r--source/blender/editors/transform/transform_mode.c78
-rw-r--r--source/blender/editors/transform/transform_mode.h6
-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.c383
-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_shrink_fatten.c4
-rw-r--r--source/blender/editors/transform/transform_mode_timetranslate.c1
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c55
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c73
-rw-r--r--source/blender/editors/transform/transform_ops.c27
-rw-r--r--source/blender/editors/transform/transform_orientations.c2
-rw-r--r--source/blender/editors/transform/transform_snap.c310
-rw-r--r--source/blender/editors/transform/transform_snap.h2
-rw-r--r--source/blender/editors/transform/transform_snap_object.c169
-rw-r--r--source/blender/editors/undo/ed_undo.c10
-rw-r--r--source/blender/editors/util/CMakeLists.txt2
-rw-r--r--source/blender/editors/util/ed_util.c5
-rw-r--r--source/blender/editors/util/ed_util_imbuf.c7
-rw-r--r--source/blender/editors/util/numinput.c46
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt3
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c125
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h16
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c40
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c62
-rw-r--r--source/blender/editors/uvedit/uvedit_path.c873
-rw-r--r--source/blender/editors/uvedit/uvedit_rip.c981
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c1151
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c44
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c480
-rw-r--r--source/blender/freestyle/intern/application/AppConfig.cpp20
-rw-r--r--source/blender/freestyle/intern/application/AppView.cpp2
-rw-r--r--source/blender/freestyle/intern/application/Controller.cpp4
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp6
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h4
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp8
-rw-r--r--source/blender/freestyle/intern/geometry/Bezier.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/Grid.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/matrix_util.cpp1
-rw-r--r--source/blender/freestyle/intern/image/GaussianFilter.cpp8
-rw-r--r--source/blender/freestyle/intern/image/Image.h2
-rw-r--r--source/blender/freestyle/intern/python/BPy_BinaryPredicate0D.cpp5
-rw-r--r--source/blender/freestyle/intern/python/BPy_BinaryPredicate1D.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_Freestyle.cpp2
-rw-r--r--source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp2
-rw-r--r--source/blender/freestyle/intern/python/BPy_Id.cpp2
-rw-r--r--source/blender/freestyle/intern/python/BPy_Iterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_Operators.cpp8
-rw-r--r--source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_StrokeShader.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_UnaryPredicate0D.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_UnaryPredicate1D.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_ViewMap.cpp4
-rw-r--r--source/blender/freestyle/intern/python/BPy_ViewShape.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp8
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp3
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp3
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp2
-rw-r--r--source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp4
-rw-r--r--source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp6
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.cpp4
-rw-r--r--source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.cpp4
-rw-r--r--source/blender/freestyle/intern/scene_graph/NodeShape.cpp2
-rw-r--r--source/blender/freestyle/intern/scene_graph/SceneHash.cpp2
-rw-r--r--source/blender/freestyle/intern/scene_graph/SceneHash.h2
-rw-r--r--source/blender/freestyle/intern/scene_graph/ScenePrettyPrinter.cpp16
-rw-r--r--source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp2
-rw-r--r--source/blender/freestyle/intern/stroke/Canvas.cpp4
-rw-r--r--source/blender/freestyle/intern/stroke/Stroke.cpp4
-rw-r--r--source/blender/freestyle/intern/system/PythonInterpreter.h2
-rw-r--r--source/blender/freestyle/intern/system/RenderMonitor.h2
-rw-r--r--source/blender/freestyle/intern/system/StringUtils.h2
-rw-r--r--source/blender/freestyle/intern/view_map/AutoPtrHelper.h21
-rw-r--r--source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp2
-rw-r--r--source/blender/freestyle/intern/view_map/Interface0D.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/SteerableViewMap.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp12
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapTesselator.cpp4
-rw-r--r--source/blender/freestyle/intern/winged_edge/Curvature.cpp2
-rw-r--r--source/blender/freestyle/intern/winged_edge/WingedEdgeBuilder.cpp12
-rw-r--r--source/blender/functions/CMakeLists.txt38
-rw-r--r--source/blender/functions/FN_array_spans.hh209
-rw-r--r--source/blender/functions/FN_attributes_ref.hh344
-rw-r--r--source/blender/functions/FN_cpp_type.hh536
-rw-r--r--source/blender/functions/FN_generic_vector_array.hh206
-rw-r--r--source/blender/functions/FN_multi_function.hh128
-rw-r--r--source/blender/functions/FN_multi_function_builder.hh318
-rw-r--r--source/blender/functions/FN_multi_function_context.hh70
-rw-r--r--source/blender/functions/FN_multi_function_data_type.hh130
-rw-r--r--source/blender/functions/FN_multi_function_network.hh539
-rw-r--r--source/blender/functions/FN_multi_function_network_evaluation.hh64
-rw-r--r--source/blender/functions/FN_multi_function_network_optimization.hh32
-rw-r--r--source/blender/functions/FN_multi_function_param_type.hh168
-rw-r--r--source/blender/functions/FN_multi_function_params.hh251
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh173
-rw-r--r--source/blender/functions/FN_spans.hh456
-rw-r--r--source/blender/functions/intern/attributes_ref.cc75
-rw-r--r--source/blender/functions/intern/cpp_types.cc6
-rw-r--r--source/blender/functions/intern/multi_function.cc (renamed from source/blender/io/alembic/intern/abc_writer_nurbs.h)38
-rw-r--r--source/blender/functions/intern/multi_function_builder.cc119
-rw-r--r--source/blender/functions/intern/multi_function_network.cc330
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc1060
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc504
-rw-r--r--source/blender/functions/tests/FN_array_spans_test.cc132
-rw-r--r--source/blender/functions/tests/FN_attributes_ref_test.cc97
-rw-r--r--source/blender/functions/tests/FN_cpp_type_test.cc (renamed from tests/gtests/functions/FN_cpp_type_test.cc)64
-rw-r--r--source/blender/functions/tests/FN_generic_vector_array_test.cc101
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc253
-rw-r--r--source/blender/functions/tests/FN_multi_function_test.cc385
-rw-r--r--source/blender/functions/tests/FN_spans_test.cc222
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt7
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c446
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h66
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c78
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c152
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c107
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c48
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c95
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c67
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c105
-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.c49
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c86
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c56
-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.txt40
-rw-r--r--source/blender/gpu/GPU_batch.h5
-rw-r--r--source/blender/gpu/GPU_buffers.h4
-rw-r--r--source/blender/gpu/GPU_debug.h2
-rw-r--r--source/blender/gpu/GPU_draw.h95
-rw-r--r--source/blender/gpu/GPU_extensions.h4
-rw-r--r--source/blender/gpu/GPU_framebuffer.h48
-rw-r--r--source/blender/gpu/GPU_immediate.h8
-rw-r--r--source/blender/gpu/GPU_material.h15
-rw-r--r--source/blender/gpu/GPU_matrix.h16
-rw-r--r--source/blender/gpu/GPU_platform.h11
-rw-r--r--source/blender/gpu/GPU_shader.h14
-rw-r--r--source/blender/gpu/GPU_state.h25
-rw-r--r--source/blender/gpu/GPU_texture.h43
-rw-r--r--source/blender/gpu/GPU_uniformbuffer.h3
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h8
-rw-r--r--source/blender/gpu/GPU_vertex_format.h14
-rw-r--r--source/blender/gpu/intern/gpu_attr_binding.cc (renamed from source/blender/gpu/intern/gpu_attr_binding.c)0
-rw-r--r--source/blender/gpu/intern/gpu_attr_binding_private.h9
-rw-r--r--source/blender/gpu/intern/gpu_batch.cc (renamed from source/blender/gpu/intern/gpu_batch.c)74
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c44
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c514
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h8
-rw-r--r--source/blender/gpu/intern/gpu_context.cc (renamed from source/blender/gpu/intern/gpu_context.cpp)0
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc (renamed from source/blender/gpu/intern/gpu_debug.c)0
-rw-r--r--source/blender/gpu/intern/gpu_draw.c1452
-rw-r--r--source/blender/gpu/intern/gpu_element.cc (renamed from source/blender/gpu/intern/gpu_element.c)24
-rw-r--r--source/blender/gpu/intern/gpu_extensions.cc (renamed from source/blender/gpu/intern/gpu_extensions.c)67
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc (renamed from source/blender/gpu/intern/gpu_framebuffer.c)232
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc (renamed from source/blender/gpu/intern/gpu_immediate.c)60
-rw-r--r--source/blender/gpu/intern/gpu_material.c12
-rw-r--r--source/blender/gpu/intern/gpu_matrix.c80
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h3
-rw-r--r--source/blender/gpu/intern/gpu_platform.cc (renamed from source/blender/gpu/intern/gpu_platform.c)3
-rw-r--r--source/blender/gpu/intern/gpu_primitive.c29
-rw-r--r--source/blender/gpu/intern/gpu_primitive_private.h9
-rw-r--r--source/blender/gpu/intern/gpu_private.h8
-rw-r--r--source/blender/gpu/intern/gpu_select.c1
-rw-r--r--source/blender/gpu/intern/gpu_select_pick.c9
-rw-r--r--source/blender/gpu/intern/gpu_select_sample_query.c8
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc839
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c (renamed from source/blender/gpu/intern/gpu_shader.c)748
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.h11
-rw-r--r--source/blender/gpu/intern/gpu_state.cc (renamed from source/blender/gpu/intern/gpu_state.c)161
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc (renamed from source/blender/gpu/intern/gpu_texture.c)441
-rw-r--r--source/blender/gpu/intern/gpu_uniformbuffer.cc (renamed from source/blender/gpu/intern/gpu_uniformbuffer.c)308
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc (renamed from source/blender/gpu/intern/gpu_vertex_buffer.c)52
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc (renamed from source/blender/gpu/intern/gpu_vertex_format.c)66
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format_private.h9
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c9
-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.glsl359
-rw-r--r--source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl87
-rw-r--r--source/blender/gpu/shaders/gpu_shader_image_alpha_color_frag.glsl14
-rw-r--r--source/blender/gpu/shaders/gpu_shader_image_mask_uniform_color_frag.glsl12
-rw-r--r--source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl12
-rw-r--r--source/blender/gpu/shaders/gpu_shader_point_varying_color_varying_outline_aa_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_text_frag.glsl1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl4
-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_hair_info.glsl12
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl6
-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_tex_sky.glsl148
-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/gpu/shaders/material/gpu_shader_material_world_normals.glsl2
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp32
-rw-r--r--source/blender/imbuf/CMakeLists.txt2
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h1
-rw-r--r--source/blender/imbuf/IMB_imbuf.h42
-rw-r--r--source/blender/imbuf/IMB_imbuf_types.h2
-rw-r--r--source/blender/imbuf/intern/IMB_indexer.h6
-rw-r--r--source/blender/imbuf/intern/anim_movie.c43
-rw-r--r--source/blender/imbuf/intern/colormanagement.c9
-rw-r--r--source/blender/imbuf/intern/dds/BlockDXT.cpp4
-rw-r--r--source/blender/imbuf/intern/dds/BlockDXT.h4
-rw-r--r--source/blender/imbuf/intern/dds/DirectDrawSurface.cpp4
-rw-r--r--source/blender/imbuf/intern/dds/Image.cpp4
-rw-r--r--source/blender/imbuf/intern/filter.c2
-rw-r--r--source/blender/imbuf/intern/imageprocess.c2
-rw-r--r--source/blender/imbuf/intern/indexer.c4
-rw-r--r--source/blender/imbuf/intern/iris.c4
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp116
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c5
-rw-r--r--source/blender/imbuf/intern/tiff.c16
-rw-r--r--source/blender/imbuf/intern/util_gpu.c261
-rw-r--r--source/blender/io/CMakeLists.txt2
-rw-r--r--source/blender/io/alembic/ABC_alembic.h13
-rw-r--r--source/blender/io/alembic/CMakeLists.txt60
-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.cc225
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc264
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h92
-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)95
-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)129
-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)590
-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)46
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc118
-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.cc675
-rw-r--r--source/blender/io/alembic/intern/abc_exporter.h128
-rw-r--r--source/blender/io/alembic/intern/abc_reader_archive.cc47
-rw-r--r--source/blender/io/alembic/intern/abc_reader_archive.h26
-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.cc15
-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.cc10
-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.cc113
-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.cc247
-rw-r--r--source/blender/io/avi/intern/avi_mjpeg.c15
-rw-r--r--source/blender/io/avi/intern/avi_mjpeg.h5
-rw-r--r--source/blender/io/avi/intern/avi_rgb.c5
-rw-r--r--source/blender/io/avi/intern/avi_rgb.h5
-rw-r--r--source/blender/io/collada/AnimationExporter.cpp10
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp24
-rw-r--r--source/blender/io/collada/ArmatureImporter.cpp8
-rw-r--r--source/blender/io/collada/BCAnimationSampler.cpp4
-rw-r--r--source/blender/io/collada/BCMath.cpp25
-rw-r--r--source/blender/io/collada/DocumentImporter.cpp25
-rw-r--r--source/blender/io/collada/ImageExporter.cpp4
-rw-r--r--source/blender/io/collada/MeshImporter.cpp10
-rw-r--r--source/blender/io/collada/MeshImporter.h2
-rw-r--r--source/blender/io/collada/SkinInfo.cpp2
-rw-r--r--source/blender/io/collada/TransformWriter.cpp6
-rw-r--r--source/blender/io/collada/TransformWriter.h5
-rw-r--r--source/blender/io/collada/collada_internal.cpp2
-rw-r--r--source/blender/io/collada/collada_utils.cpp6
-rw-r--r--source/blender/io/collada/collada_utils.h2
-rw-r--r--source/blender/io/common/CMakeLists.txt (renamed from tests/gtests/blenkernel/CMakeLists.txt)54
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h (renamed from source/blender/io/usd/intern/abstract_hierarchy_iterator.h)136
-rw-r--r--source/blender/io/common/IO_dupli_persistent_id.hh68
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc (renamed from source/blender/io/usd/intern/abstract_hierarchy_iterator.cc)243
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc (renamed from tests/gtests/usd/abstract_hierarchy_iterator_test.cc)174
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.cc104
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.hh62
-rw-r--r--source/blender/io/common/intern/dupli_persistent_id.cc167
-rw-r--r--source/blender/io/common/intern/hierarchy_context_order_test.cc (renamed from tests/gtests/usd/hierarchy_context_order_test.cc)18
-rw-r--r--source/blender/io/common/intern/object_identifier.cc116
-rw-r--r--source/blender/io/common/intern/object_identifier_test.cc234
-rw-r--r--source/blender/io/usd/CMakeLists.txt22
-rw-r--r--source/blender/io/usd/intern/usd_capi.cc29
-rw-r--r--source/blender/io/usd/intern/usd_exporter_context.h8
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.cc16
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.h16
-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.cc10
-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.cc19
-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.cc11
-rw-r--r--source/blender/io/usd/intern/usd_writer_transform.h8
-rw-r--r--source/blender/io/usd/tests/usd_stage_creation_test.cc (renamed from tests/gtests/usd/usd_stage_creation_test.cc)28
-rw-r--r--source/blender/makesdna/DNA_ID.h87
-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.h76
-rw-r--r--source/blender/makesdna/DNA_cloth_types.h5
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h7
-rw-r--r--source/blender/makesdna/DNA_curve_types.h9
-rw-r--r--source/blender/makesdna/DNA_curveprofile_types.h22
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h17
-rw-r--r--source/blender/makesdna/DNA_fluid_types.h163
-rw-r--r--source/blender/makesdna/DNA_genfile.h8
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h16
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h4
-rw-r--r--source/blender/makesdna/DNA_image_types.h20
-rw-r--r--source/blender/makesdna/DNA_key_types.h2
-rw-r--r--source/blender/makesdna/DNA_layer_types.h1
-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.h51
-rw-r--r--source/blender/makesdna/DNA_movieclip_types.h6
-rw-r--r--source/blender/makesdna/DNA_node_types.h15
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h4
-rw-r--r--source/blender/makesdna/DNA_object_types.h2
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h3
-rw-r--r--source/blender/makesdna/DNA_pointcache_types.h1
-rw-r--r--source/blender/makesdna/DNA_rigidbody_types.h2
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h6
-rw-r--r--source/blender/makesdna/DNA_scene_types.h16
-rw-r--r--source/blender/makesdna/DNA_screen_types.h25
-rw-r--r--source/blender/makesdna/DNA_shader_fx_types.h9
-rw-r--r--source/blender/makesdna/DNA_simulation_types.h65
-rw-r--r--source/blender/makesdna/DNA_sound_types.h2
-rw-r--r--source/blender/makesdna/DNA_space_types.h11
-rw-r--r--source/blender/makesdna/DNA_text_types.h22
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h41
-rw-r--r--source/blender/makesdna/DNA_vfont_types.h2
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h1
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h2
-rw-r--r--source/blender/makesdna/DNA_world_types.h6
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c14
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h8
-rw-r--r--source/blender/makesdna/intern/dna_utils.c17
-rw-r--r--source/blender/makesdna/intern/makesdna.c19
-rw-r--r--source/blender/makesrna/RNA_access.h35
-rw-r--r--source/blender/makesrna/RNA_enum_types.h1
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt42
-rw-r--r--source/blender/makesrna/intern/makesrna.c95
-rw-r--r--source/blender/makesrna/intern/rna_ID.c37
-rw-r--r--source/blender/makesrna/intern/rna_access.c176
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c336
-rw-r--r--source/blender/makesrna/intern/rna_access_internal.h5
-rw-r--r--source/blender/makesrna/intern/rna_action.c2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c190
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c9
-rw-r--r--source/blender/makesrna/intern/rna_camera.c2
-rw-r--r--source/blender/makesrna/intern/rna_cloth.c42
-rw-r--r--source/blender/makesrna/intern/rna_collection.c4
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c32
-rw-r--r--source/blender/makesrna/intern/rna_curve.c9
-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_depsgraph.c2
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c4
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c391
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c42
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c18
-rw-r--r--source/blender/makesrna/intern/rna_image.c11
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c19
-rw-r--r--source/blender/makesrna/intern/rna_internal.h17
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h55
-rw-r--r--source/blender/makesrna/intern/rna_key.c10
-rw-r--r--source/blender/makesrna/intern/rna_lattice.c2
-rw-r--r--source/blender/makesrna/intern/rna_layer.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.c16
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c24
-rw-r--r--source/blender/makesrna/intern/rna_material.c3
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c244
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c164
-rw-r--r--source/blender/makesrna/intern/rna_movieclip.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c101
-rw-r--r--source/blender/makesrna/intern/rna_object.c118
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c17
-rw-r--r--source/blender/makesrna/intern/rna_particle.c8
-rw-r--r--source/blender/makesrna/intern/rna_pose.c30
-rw-r--r--source/blender/makesrna/intern/rna_render.c27
-rw-r--r--source/blender/makesrna/intern/rna_rigidbody.c5
-rw-r--r--source/blender/makesrna/intern/rna_rna.c321
-rw-r--r--source/blender/makesrna/intern/rna_scene.c79
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c14
-rw-r--r--source/blender/makesrna/intern/rna_screen.c33
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c6
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c200
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c76
-rw-r--r--source/blender/makesrna/intern/rna_shader_fx.c118
-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.c111
-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_tracking.c13
-rw-r--r--source/blender/makesrna/intern/rna_ui.c5
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c82
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c91
-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.txt10
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c124
-rw-r--r--source/blender/modifiers/intern/MOD_array.c28
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c168
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c5
-rw-r--r--source/blender/modifiers/intern/MOD_build.c2
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c4
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c32
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c26
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c63
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c2
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c6
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c10
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c2
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c2
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c32
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c86
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c33
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c19
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c35
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c2
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c38
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c17
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c15
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c177
-rw-r--r--source/blender/modifiers/intern/MOD_none.c3
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c7
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c143
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c24
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c16
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c19
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c2
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c2
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c9
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c2
-rw-r--r--source/blender/modifiers/intern/MOD_simulation.cc60
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c4
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c6
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c2
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c29
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c4
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c60
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c15
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c66
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c2
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c178
-rw-r--r--source/blender/modifiers/intern/MOD_util.c8
-rw-r--r--source/blender/modifiers/intern/MOD_util.h4
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c2
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c2
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c34
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c14
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c8
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c23
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c2
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c2
-rw-r--r--source/blender/nodes/CMakeLists.txt33
-rw-r--r--source/blender/nodes/NOD_composite.h8
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh (renamed from source/blender/blenkernel/BKE_derived_node_tree.hh)252
-rw-r--r--source/blender/nodes/NOD_function.h1
-rw-r--r--source/blender/nodes/NOD_node_tree_dependencies.hh79
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh393
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh (renamed from source/blender/blenkernel/BKE_node_tree_ref.hh)188
-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_static_types.h2
-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/function/node_function_util.cc2
-rw-r--r--source/blender/nodes/function/node_function_util.hh (renamed from source/blender/nodes/function/node_function_util.h)4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc30
-rw-r--r--source/blender/nodes/function/nodes/node_fn_combine_strings.cc27
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc45
-rw-r--r--source/blender/nodes/function/nodes/node_fn_group_instance_id.cc32
-rw-r--r--source/blender/nodes/function/nodes/node_fn_object_transforms.cc88
-rw-r--r--source/blender/nodes/function/nodes/node_fn_switch.cc2
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc (renamed from source/blender/blenkernel/intern/derived_node_tree.cc)249
-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)278
-rw-r--r--source/blender/nodes/intern/node_tree_dependencies.cc57
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc344
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc (renamed from source/blender/blenkernel/intern/node_tree_ref.cc)113
-rw-r--r--source/blender/nodes/shader/node_shader_util.h16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_clamp.cc (renamed from source/blender/nodes/shader/nodes/node_shader_clamp.c)25
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_geometry.c3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc (renamed from source/blender/nodes/shader/nodes/node_shader_map_range.c)90
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.c115
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc387
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc (renamed from source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c)45
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc (renamed from source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c)45
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.c5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_image.c5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c176
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc (renamed from source/blender/nodes/shader/nodes/node_shader_valToRgb.c)48
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc (renamed from source/blender/nodes/shader/nodes/node_shader_value.c)10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.c144
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc292
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vertex_color.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wireframe.c1
-rw-r--r--source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc2
-rw-r--r--source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc3
-rw-r--r--source/blender/nodes/texture/node_texture_util.h8
-rw-r--r--source/blender/python/BPY_extern.h11
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c3
-rw-r--r--source/blender/python/generic/CMakeLists.txt2
-rw-r--r--source/blender/python/generic/bgl.c2
-rw-r--r--source/blender/python/generic/bl_math_py_api.c162
-rw-r--r--source/blender/python/generic/bl_math_py_api.h (renamed from source/blender/editors/include/ED_logic.h)25
-rw-r--r--source/blender/python/generic/py_capi_utils.c42
-rw-r--r--source/blender/python/gpu/gpu_py_batch.c7
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c14
-rw-r--r--source/blender/python/intern/bpy.h8
-rw-r--r--source/blender/python/intern/bpy_app.c29
-rw-r--r--source/blender/python/intern/bpy_app.h8
-rw-r--r--source/blender/python/intern/bpy_app_alembic.h8
-rw-r--r--source/blender/python/intern/bpy_app_build_options.h8
-rw-r--r--source/blender/python/intern/bpy_app_ffmpeg.h8
-rw-r--r--source/blender/python/intern/bpy_app_handlers.h8
-rw-r--r--source/blender/python/intern/bpy_app_icons.h8
-rw-r--r--source/blender/python/intern/bpy_app_ocio.h8
-rw-r--r--source/blender/python/intern/bpy_app_oiio.h8
-rw-r--r--source/blender/python/intern/bpy_app_opensubdiv.h8
-rw-r--r--source/blender/python/intern/bpy_app_openvdb.h8
-rw-r--r--source/blender/python/intern/bpy_app_sdl.h8
-rw-r--r--source/blender/python/intern/bpy_app_timers.h8
-rw-r--r--source/blender/python/intern/bpy_app_translations.c8
-rw-r--r--source/blender/python/intern/bpy_app_translations.h8
-rw-r--r--source/blender/python/intern/bpy_app_usd.h8
-rw-r--r--source/blender/python/intern/bpy_capi_utils.c39
-rw-r--r--source/blender/python/intern/bpy_capi_utils.h12
-rw-r--r--source/blender/python/intern/bpy_driver.c78
-rw-r--r--source/blender/python/intern/bpy_driver.h8
-rw-r--r--source/blender/python/intern/bpy_gizmo_wrap.h8
-rw-r--r--source/blender/python/intern/bpy_interface.c59
-rw-r--r--source/blender/python/intern/bpy_intern_string.h8
-rw-r--r--source/blender/python/intern/bpy_library.h8
-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_msgbus.h8
-rw-r--r--source/blender/python/intern/bpy_operator.h8
-rw-r--r--source/blender/python/intern/bpy_operator_wrap.h8
-rw-r--r--source/blender/python/intern/bpy_path.h8
-rw-r--r--source/blender/python/intern/bpy_props.c434
-rw-r--r--source/blender/python/intern/bpy_props.h8
-rw-r--r--source/blender/python/intern/bpy_rna.c6
-rw-r--r--source/blender/python/intern/bpy_rna.h8
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c24
-rw-r--r--source/blender/python/intern/bpy_rna_anim.h8
-rw-r--r--source/blender/python/intern/bpy_rna_callback.h8
-rw-r--r--source/blender/python/intern/bpy_rna_driver.h8
-rw-r--r--source/blender/python/intern/bpy_rna_gizmo.h8
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c2
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.h8
-rw-r--r--source/blender/python/intern/bpy_rna_types_capi.h8
-rw-r--r--source/blender/python/intern/bpy_traceback.c8
-rw-r--r--source/blender/python/intern/bpy_traceback.h8
-rw-r--r--source/blender/python/intern/bpy_utils_previews.h8
-rw-r--r--source/blender/python/intern/bpy_utils_units.h8
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c58
-rw-r--r--source/blender/python/mathutils/mathutils_Quaternion.c27
-rw-r--r--source/blender/python/mathutils/mathutils_Vector.c23
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c6
-rw-r--r--source/blender/python/mathutils/mathutils_noise.c2
-rw-r--r--source/blender/render/CMakeLists.txt2
-rw-r--r--source/blender/render/extern/include/RE_bake.h8
-rw-r--r--source/blender/render/extern/include/RE_engine.h9
-rw-r--r--source/blender/render/extern/include/RE_multires_bake.h8
-rw-r--r--source/blender/render/extern/include/RE_render_ext.h8
-rw-r--r--source/blender/render/intern/include/initrender.h8
-rw-r--r--source/blender/render/intern/include/render_result.h8
-rw-r--r--source/blender/render/intern/include/render_types.h8
-rw-r--r--source/blender/render/intern/include/renderpipeline.h8
-rw-r--r--source/blender/render/intern/include/texture.h8
-rw-r--r--source/blender/render/intern/include/zbuf.h8
-rw-r--r--source/blender/render/intern/source/bake_api.c24
-rw-r--r--source/blender/render/intern/source/external_engine.c18
-rw-r--r--source/blender/render/intern/source/multires_bake.c7
-rw-r--r--source/blender/render/intern/source/pipeline.c6
-rw-r--r--source/blender/render/intern/source/pointdensity.c4
-rw-r--r--source/blender/render/intern/source/render_texture.c4
-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.c261
-rw-r--r--source/blender/shader_fx/intern/FX_ui_common.h56
-rw-r--r--source/blender/simulation/CMakeLists.txt (renamed from source/blender/physics/CMakeLists.txt)33
-rw-r--r--source/blender/simulation/SIM_mass_spring.h (renamed from source/blender/physics/BPH_mass_spring.h)28
-rw-r--r--source/blender/simulation/SIM_simulation_update.hh (renamed from source/blender/functions/FN_cpp_types.hh)38
-rw-r--r--source/blender/simulation/intern/ConstrainedConjugateGradient.h (renamed from source/blender/physics/intern/ConstrainedConjugateGradient.h)0
-rw-r--r--source/blender/simulation/intern/SIM_mass_spring.cpp (renamed from source/blender/physics/intern/BPH_mass_spring.cpp)418
-rw-r--r--source/blender/simulation/intern/eigen_utils.h (renamed from source/blender/physics/intern/eigen_utils.h)0
-rw-r--r--source/blender/simulation/intern/hair_volume.cpp (renamed from source/blender/physics/intern/hair_volume.cpp)28
-rw-r--r--source/blender/simulation/intern/implicit.h (renamed from source/blender/physics/intern/implicit.h)102
-rw-r--r--source/blender/simulation/intern/implicit_blender.c (renamed from source/blender/physics/intern/implicit_blender.c)209
-rw-r--r--source/blender/simulation/intern/implicit_eigen.cpp (renamed from source/blender/physics/intern/implicit_eigen.cpp)70
-rw-r--r--source/blender/simulation/intern/particle_allocator.cc86
-rw-r--r--source/blender/simulation/intern/particle_allocator.hh101
-rw-r--r--source/blender/simulation/intern/particle_function.cc167
-rw-r--r--source/blender/simulation/intern/particle_function.hh97
-rw-r--r--source/blender/simulation/intern/particle_mesh_emitter.cc352
-rw-r--r--source/blender/simulation/intern/particle_mesh_emitter.hh49
-rw-r--r--source/blender/simulation/intern/simulation_collect_influences.cc785
-rw-r--r--source/blender/simulation/intern/simulation_collect_influences.hh68
-rw-r--r--source/blender/simulation/intern/simulation_solver.cc355
-rw-r--r--source/blender/simulation/intern/simulation_solver.hh40
-rw-r--r--source/blender/simulation/intern/simulation_solver_influences.cc53
-rw-r--r--source/blender/simulation/intern/simulation_solver_influences.hh194
-rw-r--r--source/blender/simulation/intern/simulation_update.cc356
-rw-r--r--source/blender/simulation/intern/time_interval.hh93
-rw-r--r--source/blender/windowmanager/WM_api.h8
-rw-r--r--source/blender/windowmanager/WM_types.h1
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_api.h8
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h10
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo.c3
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c11
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_fn.h8
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h8
-rw-r--r--source/blender/windowmanager/intern/wm.c4
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c6
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c163
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c12
-rw-r--r--source/blender/windowmanager/intern/wm_files.c114
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c230
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c35
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c2
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c24
-rw-r--r--source/blender/windowmanager/intern/wm_jobs.c4
-rw-r--r--source/blender/windowmanager/intern/wm_menu_type.c5
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c4
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c44
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c26
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c10
-rw-r--r--source/blender/windowmanager/intern/wm_subwindow.c13
-rw-r--r--source/blender/windowmanager/intern/wm_surface.c14
-rw-r--r--source/blender/windowmanager/intern/wm_window.c15
-rw-r--r--source/blender/windowmanager/message_bus/wm_message_bus.h8
-rw-r--r--source/blender/windowmanager/wm.h8
-rw-r--r--source/blender/windowmanager/wm_cursors.h8
-rw-r--r--source/blender/windowmanager/wm_draw.h10
-rw-r--r--source/blender/windowmanager/wm_event_system.h8
-rw-r--r--source/blender/windowmanager/wm_event_types.h8
-rw-r--r--source/blender/windowmanager/wm_files.h8
-rw-r--r--source/blender/windowmanager/wm_surface.h13
-rw-r--r--source/blender/windowmanager/wm_window.h8
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c101
-rw-r--r--source/creator/creator.c29
-rw-r--r--source/creator/creator_args.c18
-rw-r--r--tests/gtests/CMakeLists.txt17
-rw-r--r--tests/gtests/alembic/CMakeLists.txt2
-rw-r--r--tests/gtests/alembic/abc_export_test.cc215
-rw-r--r--tests/gtests/alembic/abc_matrix_test.cc10
-rw-r--r--tests/gtests/blenlib/BLI_array_store_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_array_utils_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_delaunay_2d_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc28
-rw-r--r--tests/gtests/blenlib/BLI_ghash_performance_test.cc38
-rw-r--r--tests/gtests/blenlib/BLI_ghash_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_hash_mm2a_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_heap_simple_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_heap_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_kdopbvh_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_listbase_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_math_bits_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_math_matrix_test.cc99
-rw-r--r--tests/gtests/blenlib/BLI_memiter_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_optional_test.cc61
-rw-r--r--tests/gtests/blenlib/BLI_path_util_test.cc16
-rw-r--r--tests/gtests/blenlib/BLI_polyfill_2d_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_stack_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_string_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_string_utf8_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_task_performance_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_task_test.cc2
-rw-r--r--tests/gtests/blenlib/CMakeLists.txt14
-rw-r--r--tests/gtests/blenloader/blendfile_loading_base_test.cc21
-rw-r--r--tests/gtests/guardedalloc/guardedalloc_alignment_test.cc2
-rw-r--r--tests/gtests/runner/BlenderAddTests.cmake3
-rw-r--r--tests/gtests/runner/CMakeLists.txt87
-rw-r--r--tests/gtests/runner/blender_test.cc25
-rw-r--r--tests/gtests/testing/CMakeLists.txt6
-rw-r--r--tests/gtests/testing/testing.h9
-rw-r--r--tests/gtests/testing/testing_main.cc28
-rw-r--r--tests/gtests/usd/CMakeLists.txt106
-rw-r--r--tests/python/CMakeLists.txt2
-rw-r--r--tests/python/alembic_tests.py99
-rw-r--r--tests/python/bevel_operator.py18
-rw-r--r--tests/python/bl_pyapi_idprop_datablock.py6
2577 files changed, 124452 insertions, 91125 deletions
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 00000000000..7cf2830d438
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,45 @@
+Checks: >
+ -*,
+ readability-*,
+ -readability-uppercase-literal-suffix,
+ -readability-magic-numbers,
+ -readability-isolate-declaration,
+ -readability-convert-member-functions-to-static,
+ -readability-implicit-bool-conversion,
+ -readability-avoid-const-params-in-decls,
+ -readability-simplify-boolean-expr,
+ -readability-make-member-function-const,
+
+ -readability-misleading-indentation,
+
+ -readability-else-after-return,
+ -readability-inconsistent-declaration-parameter-name,
+ -readability-redundant-preprocessor,
+ -readability-function-size,
+ -readability-function-size,
+ -readability-redundant-string-init,
+ -readability-redundant-member-init,
+ -readability-const-return-type,
+ -readability-static-accessed-through-instance,
+ -readability-redundant-declaration,
+ -readability-qualified-auto,
+ -readability-use-anyofallof,
+
+ bugprone-*,
+ -bugprone-narrowing-conversions,
+ -bugprone-unhandled-self-assignment,
+ -bugprone-branch-clone,
+ -bugprone-macro-parentheses,
+ -bugprone-reserved-identifier,
+
+ -bugprone-sizeof-expression,
+ -bugprone-integer-division,
+ -bugprone-incorrect-roundings,
+ -bugprone-suspicious-string-compare,
+ -bugprone-not-null-terminated-result,
+ -bugprone-suspicious-missing-comma,
+ -bugprone-parent-virtual-call,
+ -bugprone-infinite-loop,
+ -bugprone-copy-constructor-init,
+
+WarningsAsErrors: '*'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a4037dc1e3e..bc4d4e032af 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,7 +41,7 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
endif()
endif()
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.10)
# Prever LEGACY OpenGL to eb compatible with all the existing releases and
# platforms which don't hare GLVND yet. Only do it if preference was not set
@@ -278,7 +278,6 @@ option(WITH_CODEC_SNDFILE "Enable libsndfile Support (http://www.mega-nerd
# Alembic support
option(WITH_ALEMBIC "Enable Alembic Support" ON)
-option(WITH_ALEMBIC_HDF5 "Enable Legacy Alembic Support (not officially supported)" OFF)
# Universal Scene Description support
option(WITH_USD "Enable Universal Scene Description (USD) Support" ON)
@@ -322,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)
@@ -371,12 +362,12 @@ 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)
mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL)
-set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 CACHE STRING "CUDA architectures to build binaries for")
+set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 compute_75 CACHE STRING "CUDA architectures to build binaries for")
mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH)
unset(PLATFORM_DEFAULT)
option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON)
@@ -424,6 +415,11 @@ mark_as_advanced(WITH_CXX_GUARDEDALLOC)
option(WITH_ASSERT_ABORT "Call abort() when raising an assertion through BLI_assert()" ON)
mark_as_advanced(WITH_ASSERT_ABORT)
+if(UNIX AND NOT APPLE)
+ option(WITH_CLANG_TIDY "Use Clang Tidy to analyze the source code (only enable for development on Linux using Clang)" OFF)
+ mark_as_advanced(WITH_CLANG_TIDY)
+endif()
+
option(WITH_BOOST "Enable features depending on boost" ON)
option(WITH_TBB "Enable features depending on TBB (OpenVDB, OpenImageDenoise, sculpt multithreading)" ON)
@@ -655,6 +651,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)
@@ -903,7 +900,7 @@ if(MSVC)
# endianess-detection and auto-setting is counterproductive
# so we just set endianness according CMAKE_OSX_ARCHITECTURES
-elseif(CMAKE_OSX_ARCHITECTURES MATCHES i386 OR CMAKE_OSX_ARCHITECTURES MATCHES x86_64)
+elseif(CMAKE_OSX_ARCHITECTURES MATCHES i386 OR CMAKE_OSX_ARCHITECTURES MATCHES x86_64 OR CMAKE_OSX_ARCHITECTURES MATCHES arm64)
add_definitions(-D__LITTLE_ENDIAN__)
elseif(CMAKE_OSX_ARCHITECTURES MATCHES ppc OR CMAKE_OSX_ARCHITECTURES MATCHES ppc64)
add_definitions(-D__BIG_ENDIAN__)
@@ -1246,7 +1243,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()
#-----------------------------------------------------------------------------
@@ -1562,20 +1559,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
@@ -1636,10 +1628,6 @@ endif()
#-----------------------------------------------------------------------------
# Libraries
-if(WITH_GTESTS)
- include(GTestTesting)
-endif()
-
if(WITH_BLENDER)
add_subdirectory(intern)
add_subdirectory(extern)
diff --git a/GNUmakefile b/GNUmakefile
index e3bb3eaff7a..9a5164cd722 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -32,6 +32,7 @@ Convenience Targets
* debug: Build a debug binary.
* full: Enable all supported dependencies & options.
* lite: Disable non essential features for a smaller binary and faster build.
+ * release Complete build with all options enabled including CUDA and Optix, matching the releases on blender.org
* headless: Build without an interface (renderfarm or server automation).
* cycles: Build Cycles standalone only, without Blender.
* bpy: Build as a python module which can be loaded from python directly.
@@ -141,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)
@@ -207,6 +212,10 @@ ifneq "$(findstring lite, $(MAKECMDGOALS))" ""
BUILD_DIR:=$(BUILD_DIR)_lite
CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/blender_lite.cmake" $(CMAKE_CONFIG_ARGS)
endif
+ifneq "$(findstring release, $(MAKECMDGOALS))" ""
+ BUILD_DIR:=$(BUILD_DIR)_release
+ CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/blender_release.cmake" $(CMAKE_CONFIG_ARGS)
+endif
ifneq "$(findstring cycles, $(MAKECMDGOALS))" ""
BUILD_DIR:=$(BUILD_DIR)_cycles
CMAKE_CONFIG_ARGS:=-C"$(BLENDER_DIR)/build_files/cmake/config/cycles_standalone.cmake" $(CMAKE_CONFIG_ARGS)
@@ -317,6 +326,7 @@ all: .FORCE
debug: all
full: all
lite: all
+release: all
cycles: all
headless: all
bpy: all
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index dd90eb9adde..276311fe2d8 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -30,7 +30,7 @@
# build_deps 2015 x64 / build_deps 2015 x86
#
# MAC OS X USAGE:
-# Install with homebrew: brew install cmake autoconf automake libtool yasm nasm
+# Install with homebrew: brew install cmake autoconf automake libtool yasm nasm bison
# Run "make deps" from main Blender directory
#
# LINUX USAGE:
@@ -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()
@@ -80,6 +76,7 @@ include(cmake/llvm.cmake)
include(cmake/clang.cmake)
if(APPLE)
include(cmake/openmp.cmake)
+ include(cmake/nasm.cmake)
endif()
include(cmake/openimageio.cmake)
include(cmake/tiff.cmake)
@@ -97,17 +94,20 @@ if(UNIX)
else()
include(cmake/pugixml.cmake)
endif()
-include(cmake/openimagedenoise.cmake)
-include(cmake/embree.cmake)
-include(cmake/xr_openxr.cmake)
+if((NOT APPLE) OR ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
+ include(cmake/ispc.cmake)
+ include(cmake/openimagedenoise.cmake)
+ include(cmake/embree.cmake)
+endif()
+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/alembic.cmake b/build_files/build_environment/cmake/alembic.cmake
index 95a461c64b3..94b7b56d58f 100644
--- a/build_files/build_environment/cmake/alembic.cmake
+++ b/build_files/build_environment/cmake/alembic.cmake
@@ -16,16 +16,6 @@
#
# ***** END GPL LICENSE BLOCK *****
-if(ALEMBIC_HDF5)
- set(ALEMBIC_HDF5_HL)
- # in debug mode we do not build HDF5_hdf5_hl_LIBRARY which makes cmake really
- # unhappy, stub it with the debug mode lib. it's not linking it in at this
- # point in time anyhow
- if(BUILD_MODE STREQUAL Debug)
- set(ALEMBIC_HDF5_HL -DHDF5_hdf5_hl_LIBRARY=${LIBDIR}/hdf5/lib/libhdf5_hl_D.${LIBEXT})
- endif()
-endif()
-
set(ALEMBIC_EXTRA_ARGS
-DBUILDSTATIC=ON
-DLINKSTATIC=ON
@@ -53,7 +43,6 @@ set(ALEMBIC_EXTRA_ARGS
-DUSE_PRMAN=0
-DUSE_HDF5=Off
-DUSE_STATIC_HDF5=Off
- -DHDF5_ROOT=${LIBDIR}/hdf5
-DUSE_TESTS=Off
-DALEMBIC_NO_OPENGL=1
-DUSE_BINARIES=ON
@@ -62,7 +51,6 @@ set(ALEMBIC_EXTRA_ARGS
-DGLUT_INCLUDE_DIR=""
-DZLIB_LIBRARY=${LIBDIR}/zlib/lib/${ZLIB_LIBRARY}
-DZLIB_INCLUDE_DIR=${LIBDIR}/zlib/include/
- ${ALEMBIC_HDF5_HL}
)
ExternalProject_Add(external_alembic
diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake
index 94c649e9109..6e7ee8c66b1 100644
--- a/build_files/build_environment/cmake/boost.cmake
+++ b/build_files/build_environment/cmake/boost.cmake
@@ -44,7 +44,7 @@ if(WIN32)
elseif(APPLE)
set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh)
set(BOOST_BUILD_COMMAND ./b2)
- set(BOOST_BUILD_OPTIONS toolset=darwin cxxflags=${PLATFORM_CXXFLAGS} linkflags=${PLATFORM_LDFLAGS} visibility=global --disable-icu boost.locale.icu=off)
+ set(BOOST_BUILD_OPTIONS toolset=clang-darwin cxxflags=${PLATFORM_CXXFLAGS} linkflags=${PLATFORM_LDFLAGS} visibility=global --disable-icu boost.locale.icu=off)
set(BOOST_HARVEST_CMD echo .)
set(BOOST_PATCH_COMMAND echo .)
else()
diff --git a/build_files/build_environment/cmake/check_software.cmake b/build_files/build_environment/cmake/check_software.cmake
index f5774551879..384915aba84 100644
--- a/build_files/build_environment/cmake/check_software.cmake
+++ b/build_files/build_environment/cmake/check_software.cmake
@@ -30,6 +30,7 @@ if(UNIX)
nasm
yasm
tclsh
+ bison
)
foreach(_software ${_required_software})
@@ -40,6 +41,12 @@ if(UNIX)
unset(_software_find CACHE)
endforeach()
+ if(APPLE)
+ if(NOT EXISTS "/usr/local/opt/bison/bin/bison")
+ set(_software_missing "${_software_missing} bison")
+ endif()
+ endif()
+
if(_software_missing)
message(
"\n"
@@ -50,7 +57,7 @@ if(UNIX)
" apt install autoconf automake libtool yasm nasm tcl\n"
"\n"
"On macOS (with homebrew):\n"
- " brew install cmake autoconf automake libtool yasm nasm\n"
+ " brew install cmake autoconf automake libtool yasm nasm bison\n"
"\n"
"Other platforms:\n"
" Install equivalent packages.\n")
diff --git a/build_files/build_environment/cmake/clang.cmake b/build_files/build_environment/cmake/clang.cmake
index f7dfd434d4a..8b928f968fd 100644
--- a/build_files/build_environment/cmake/clang.cmake
+++ b/build_files/build_environment/cmake/clang.cmake
@@ -30,6 +30,11 @@ else()
set(CLANG_GENERATOR "Unix Makefiles")
endif()
+if(APPLE)
+ set(CLANG_EXTRA_ARGS ${CLANG_EXTRA_ARGS}
+ -DLIBXML2_LIBRARY=${LIBDIR}/xml2/lib/libxml2.a
+ )
+endif()
ExternalProject_Add(external_clang
URL ${CLANG_URI}
diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake
index 428451d0115..66a86cd5dbd 100644
--- a/build_files/build_environment/cmake/embree.cmake
+++ b/build_files/build_environment/cmake/embree.cmake
@@ -51,6 +51,7 @@ ExternalProject_Add(external_embree
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH MD5=${EMBREE_HASH}
PREFIX ${BUILD_DIR}/embree
+ PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/embree/src/external_embree < ${PATCH_DIR}/embree.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/embree ${DEFAULT_CMAKE_FLAGS} ${EMBREE_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/embree
)
diff --git a/build_files/build_environment/cmake/ffmpeg.cmake b/build_files/build_environment/cmake/ffmpeg.cmake
index 02e78c605af..164997b9aa5 100644
--- a/build_files/build_environment/cmake/ffmpeg.cmake
+++ b/build_files/build_environment/cmake/ffmpeg.cmake
@@ -50,7 +50,8 @@ if(APPLE)
set(FFMPEG_EXTRA_FLAGS
${FFMPEG_EXTRA_FLAGS}
--target-os=darwin
- )
+ --x86asmexe=${LIBDIR}/nasm/bin/nasm
+ )
endif()
ExternalProject_Add(external_ffmpeg
@@ -143,6 +144,12 @@ if(WIN32)
external_zlib_mingw
)
endif()
+if(APPLE)
+ add_dependencies(
+ external_ffmpeg
+ external_nasm
+ )
+endif()
if(BUILD_MODE STREQUAL Release AND WIN32)
ExternalProject_Add_Step(external_ffmpeg after_install
diff --git a/build_files/build_environment/cmake/freetype.cmake b/build_files/build_environment/cmake/freetype.cmake
index 30dd2eed676..fefe2c900bc 100644
--- a/build_files/build_environment/cmake/freetype.cmake
+++ b/build_files/build_environment/cmake/freetype.cmake
@@ -24,7 +24,8 @@ set(FREETYPE_EXTRA_ARGS
-DFT_WITH_HARFBUZZ=OFF
-DFT_WITH_BZIP2=OFF
-DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE
- -DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE)
+ -DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE
+ -DCMAKE_DISABLE_FIND_PACKAGE_BrotliDec=TRUE)
ExternalProject_Add(external_freetype
URL ${FREETYPE_URI}
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index 9acafe8f8e6..3f5d2044dbc 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -42,22 +42,11 @@ if(BUILD_MODE STREQUAL Release)
${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/glew/include/ ${HARVEST_TARGET}/opengl/include/ &&
# 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/
+ ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/tiff/include/ ${HARVEST_TARGET}/tiff/include/
DEPENDS
)
endif()
-if(BUILD_MODE STREQUAL Debug)
- add_custom_target(Harvest_Debug_Results
- COMMAND
- # hdf5
- ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/hdf5/lib ${HARVEST_TARGET}/hdf5/lib &&
- DEPENDS Package_Python
- )
-endif()
-
else(WIN32)
function(harvest from to)
@@ -143,8 +132,12 @@ harvest(openimageio/bin openimageio/bin "maketx")
harvest(openimageio/bin openimageio/bin "oiiotool")
harvest(openimageio/include openimageio/include "*")
harvest(openimageio/lib openimageio/lib "*.a")
-harvest(openimagedenoise/include openimagedenoise/include "*")
-harvest(openimagedenoise/lib openimagedenoise/lib "*.a")
+if((NOT APPLE) OR ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
+ harvest(openimagedenoise/include openimagedenoise/include "*")
+ harvest(openimagedenoise/lib openimagedenoise/lib "*.a")
+ harvest(embree/include embree/include "*.h")
+ harvest(embree/lib embree/lib "*.a")
+endif()
harvest(openjpeg/include/openjpeg-2.3 openjpeg/include "*.h")
harvest(openjpeg/lib openjpeg/lib "*.a")
harvest(opensubdiv/include opensubdiv/include "*.h")
@@ -179,8 +172,6 @@ harvest(vpx/lib ffmpeg/lib "*.a")
harvest(webp/lib ffmpeg/lib "*.a")
harvest(x264/lib ffmpeg/lib "*.a")
harvest(xvidcore/lib ffmpeg/lib "*.a")
-harvest(embree/include embree/include "*.h")
-harvest(embree/lib embree/lib "*.a")
harvest(usd/include usd/include "*.h")
harvest(usd/lib/usd usd/lib/usd "*")
harvest(usd/plugin usd/plugin "*")
diff --git a/build_files/build_environment/cmake/hdf5.cmake b/build_files/build_environment/cmake/hdf5.cmake
deleted file mode 100644
index e28903059f2..00000000000
--- a/build_files/build_environment/cmake/hdf5.cmake
+++ /dev/null
@@ -1,42 +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 *****
-
-set(HDF5_EXTRA_ARGS
- -DHDF5_ENABLE_THREADSAFE=Off
- -DHDF5_BUILD_CPP_LIB=Off
- -DBUILD_TESTING=Off
- -DHDF5_BUILD_TOOLS=Off
- -DHDF5_BUILD_EXAMPLES=Off
- -DHDF5_BUILD_HL_LIB=On
- -DBUILD_STATIC_CRT_LIBS=On
- -DBUILD_SHARED_LIBS=On
-)
-
-if(WIN32)
- set(HDF5_PATCH ${PATCH_CMD} --verbose -p 0 -d ${BUILD_DIR}/hdf5/src/external_hdf5 < ${PATCH_DIR}/hdf5.diff)
-endif()
-
-ExternalProject_Add(external_hdf5
- URL ${HDF5_URI}
- DOWNLOAD_DIR ${DOWNLOAD_DIR}
- URL_HASH MD5=${HDF5_HASH}
- PREFIX ${BUILD_DIR}/hdf5
- PATCH_COMMAND ${HDF5_PATCH}
- CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/hdf5 ${HDF5_EXTRA_ARGS}
- INSTALL_DIR ${LIBDIR}/hdf5
-)
diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake
new file mode 100644
index 00000000000..b67351dcf9f
--- /dev/null
+++ b/build_files/build_environment/cmake/ispc.cmake
@@ -0,0 +1,76 @@
+# ***** 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
+ )
+elseif(APPLE)
+ # Use bison installed via Homebrew.
+ # The one which comes which Xcode toolset is too old.
+ set(ISPC_EXTRA_ARGS_APPLE
+ -DBISON_EXECUTABLE=/usr/local/opt/bison/bin/bison
+ )
+elseif(UNIX)
+ set(ISPC_EXTRA_ARGS_UNIX
+ -DCMAKE_C_COMPILER=${LIBDIR}/clang/bin/clang
+ -DCMAKE_CXX_COMPILER=${LIBDIR}/clang/bin/clang++
+ )
+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}
+ ${ISPC_EXTRA_ARGS_APPLE}
+ ${ISPC_EXTRA_ARGS_UNIX}
+)
+
+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/llvm.cmake b/build_files/build_environment/cmake/llvm.cmake
index 981db9c72b7..8c9a6076068 100644
--- a/build_files/build_environment/cmake/llvm.cmake
+++ b/build_files/build_environment/cmake/llvm.cmake
@@ -16,11 +16,17 @@
#
# ***** END GPL LICENSE BLOCK *****
+if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(LLVM_TARGETS AArch64)
+else()
+ set(LLVM_TARGETS X86)
+endif()
+
set(LLVM_EXTRA_ARGS
-DLLVM_USE_CRT_RELEASE=MD
-DLLVM_USE_CRT_DEBUG=MDd
-DLLVM_INCLUDE_TESTS=OFF
- -DLLVM_TARGETS_TO_BUILD=X86
+ -DLLVM_TARGETS_TO_BUILD=${LLVM_TARGETS}
-DLLVM_INCLUDE_EXAMPLES=OFF
-DLLVM_ENABLE_TERMINFO=OFF
-DLLVM_BUILD_LLVM_C_DYLIB=OFF
diff --git a/build_files/build_environment/cmake/hidapi.cmake b/build_files/build_environment/cmake/nasm.cmake
index 9a3d2ebf8df..51d7ebd8830 100644
--- a/build_files/build_environment/cmake/hidapi.cmake
+++ b/build_files/build_environment/cmake/nasm.cmake
@@ -16,14 +16,14 @@
#
# ***** END GPL LICENSE BLOCK *****
-set(HIDAPI_EXTRA_ARGS)
-
-ExternalProject_Add(external_hidapi
- URL ${HIDAPI_URI}
+ExternalProject_Add(external_nasm
+ URL ${NASM_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
+ URL_HASH SHA256=${NASM_HASH}
+ PREFIX ${BUILD_DIR}/nasm
+ PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/nasm/src/external_nasm < ${PATCH_DIR}/nasm.diff
+ CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/nasm/src/external_nasm/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/nasm
+ BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/nasm/src/external_nasm/ && make -j${MAKE_THREADS}
+ INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/nasm/src/external_nasm/ && make install
+ INSTALL_DIR ${LIBDIR}/nasm
)
diff --git a/build_files/build_environment/cmake/numpy.cmake b/build_files/build_environment/cmake/numpy.cmake
index abf2464e88c..03316a8fc63 100644
--- a/build_files/build_environment/cmake/numpy.cmake
+++ b/build_files/build_environment/cmake/numpy.cmake
@@ -38,6 +38,7 @@ ExternalProject_Add(external_numpy
PREFIX ${BUILD_DIR}/numpy
PATCH_COMMAND ${NUMPY_PATCH}
CONFIGURE_COMMAND ""
+ PATCH_COMMAND COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/numpy/src/external_numpy < ${PATCH_DIR}/numpy.diff
LOG_BUILD 1
BUILD_COMMAND ${PYTHON_BINARY} ${BUILD_DIR}/numpy/src/external_numpy/setup.py build ${NUMPY_BUILD_OPTION} install --old-and-unmanageable
INSTALL_COMMAND ""
diff --git a/build_files/build_environment/cmake/ogg.cmake b/build_files/build_environment/cmake/ogg.cmake
index e2d0f0905b8..808a35c6e4d 100644
--- a/build_files/build_environment/cmake/ogg.cmake
+++ b/build_files/build_environment/cmake/ogg.cmake
@@ -21,6 +21,7 @@ ExternalProject_Add(external_ogg
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH SHA256=${OGG_HASH}
PREFIX ${BUILD_DIR}/ogg
+ PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/ogg/src/external_ogg < ${PATCH_DIR}/ogg.diff
CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/ogg/src/external_ogg/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/ogg --disable-shared --enable-static
BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/ogg/src/external_ogg/ && make -j${MAKE_THREADS}
INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/ogg/src/external_ogg/ && make install
diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake
index 502e9a6c03b..e8b0043edf7 100644
--- a/build_files/build_environment/cmake/opencolorio.cmake
+++ b/build_files/build_environment/cmake/opencolorio.cmake
@@ -30,6 +30,13 @@ set(OPENCOLORIO_EXTRA_ARGS
-DOCIO_STATIC_JNIGLUE=OFF
)
+if(APPLE AND NOT("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
+ set(OPENCOLORIO_EXTRA_ARGS
+ ${OPENCOLORIO_EXTRA_ARGS}
+ -DOCIO_USE_SSE=OFF
+ )
+endif()
+
if(WIN32)
set(OCIO_PATCH opencolorio_win.diff)
set(OPENCOLORIO_EXTRA_ARGS
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/openmp.cmake b/build_files/build_environment/cmake/openmp.cmake
index 05b590e4926..ec0756a6693 100644
--- a/build_files/build_environment/cmake/openmp.cmake
+++ b/build_files/build_environment/cmake/openmp.cmake
@@ -22,6 +22,7 @@ ExternalProject_Add(external_openmp
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH MD5=${OPENMP_HASH}
PREFIX ${BUILD_DIR}/openmp
+ PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/openmp/src/external_openmp < ${PATCH_DIR}/openmp.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openmp ${DEFAULT_CMAKE_FLAGS}
INSTALL_COMMAND cd ${BUILD_DIR}/openmp/src/external_openmp-build && install_name_tool -id @executable_path/../Resources/lib/libomp.dylib runtime/src/libomp.dylib && make install
INSTALL_DIR ${LIBDIR}/openmp
diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake
index ed82f1fd9ee..fe2f1fa34e5 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} &&
@@ -124,16 +113,32 @@ else()
COMMAND xcode-select --print-path
OUTPUT_VARIABLE XCODE_DEV_PATH OUTPUT_STRIP_TRAILING_WHITESPACE
)
- set(OSX_ARCHITECTURES x86_64)
- set(OSX_DEPLOYMENT_TARGET 10.11)
+ execute_process(
+ COMMAND xcodebuild -version -sdk macosx SDKVersion
+ OUTPUT_VARIABLE MACOSX_SDK_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if(NOT CMAKE_OSX_ARCHITECTURES)
+ execute_process(COMMAND uname -m OUTPUT_VARIABLE ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
+ message(STATUS "Detected native architecture ${ARCHITECTURE}.")
+ set(CMAKE_OSX_ARCHITECTURES "${ARCHITECTURE}")
+ endif()
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")
+ set(OSX_DEPLOYMENT_TARGET 10.13)
+ else()
+ set(OSX_DEPLOYMENT_TARGET 11.00)
+ endif()
set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk)
- set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET}")
- set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++")
- set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET}")
- set(PLATFORM_BUILD_TARGET --build=x86_64-apple-darwin15.0.0) # OS X 10.11
+ set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
+ set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++ -arch ${CMAKE_OSX_ARCHITECTURES}")
+ set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")
+ set(PLATFORM_BUILD_TARGET --build=x86_64-apple-darwin17.0.0) # OS X 10.13
+ else()
+ set(PLATFORM_BUILD_TARGET --build=aarch64-apple-darwin20.0.0) # macOS 11.00
+ endif()
set(PLATFORM_CMAKE_FLAGS
- -DCMAKE_OSX_ARCHITECTURES:STRING=${OSX_ARCHITECTURES}
+ -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES}
-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${OSX_DEPLOYMENT_TARGET}
-DCMAKE_OSX_SYSROOT:PATH=${OSX_SYSROOT}
)
@@ -166,6 +171,7 @@ else()
set(CONFIGURE_ENV
export MACOSX_DEPLOYMENT_TARGET=${OSX_DEPLOYMENT_TARGET} &&
+ export MACOSX_SDK_VERSION=${OSX_DEPLOYMENT_TARGET} &&
export CFLAGS=${PLATFORM_CFLAGS} &&
export CXXFLAGS=${PLATFORM_CXXFLAGS} &&
export LDFLAGS=${PLATFORM_LDFLAGS}
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/png.cmake b/build_files/build_environment/cmake/png.cmake
index 8dd3c25b88b..9f8641873e6 100644
--- a/build_files/build_environment/cmake/png.cmake
+++ b/build_files/build_environment/cmake/png.cmake
@@ -22,6 +22,10 @@ set(PNG_EXTRA_ARGS
-DPNG_STATIC=ON
)
+if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+ set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64")
+endif()
+
ExternalProject_Add(external_png
URL ${PNG_URI}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
diff --git a/build_files/build_environment/cmake/python.cmake b/build_files/build_environment/cmake/python.cmake
index 2d64feb9858..9cd56423941 100644
--- a/build_files/build_environment/cmake/python.cmake
+++ b/build_files/build_environment/cmake/python.cmake
@@ -48,7 +48,12 @@ if(WIN32)
else()
if(APPLE)
- # disable functions that can be in 10.13 sdk but aren't available on 10.9 target
+ # Disable functions that can be in 10.13 sdk but aren't available on 10.9 target.
+ #
+ # Disable libintl (gettext library) as it might come from Homebrew, which makes
+ # it so test program compiles, but the Python does not. This is because for Python
+ # we use isysroot, which seems to forbid using libintl.h.
+ # The gettext functionality seems to come from CoreFoundation, so should be all fine.
set(PYTHON_FUNC_CONFIGS
export ac_cv_func_futimens=no &&
export ac_cv_func_utimensat=no &&
@@ -60,13 +65,21 @@ else()
export ac_cv_func_getentropy=no &&
export ac_cv_func_mkostemp=no &&
export ac_cv_func_mkostemps=no &&
- export ac_cv_func_timingsafe_bcmp=no)
+ export ac_cv_func_timingsafe_bcmp=no &&
+ export ac_cv_header_libintl_h=no &&
+ export ac_cv_lib_intl_textdomain=no
+ )
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(PYTHON_FUNC_CONFIGS ${PYTHON_FUNC_CONFIGS} && export PYTHON_DECIMAL_WITH_MACHINE=ansi64)
+ endif()
set(PYTHON_CONFIGURE_ENV ${CONFIGURE_ENV} && ${PYTHON_FUNC_CONFIGS})
set(PYTHON_BINARY ${BUILD_DIR}/python/src/external_python/python.exe)
+ set(PYTHON_PATCH ${PATCH_CMD} --verbose -p1 -d ${BUILD_DIR}/python/src/external_python < ${PATCH_DIR}/python_macos.diff)
else()
set(PYTHON_CONFIGURE_ENV ${CONFIGURE_ENV})
set(PYTHON_BINARY ${BUILD_DIR}/python/src/external_python/python)
- endif()
+ set(PYTHON_PATCH ${PATCH_CMD} --verbose -p1 -d ${BUILD_DIR}/python/src/external_python < ${PATCH_DIR}/python_linux.diff)
+ endif()
set(PYTHON_CONFIGURE_EXTRA_ARGS "--with-openssl=${LIBDIR}/ssl")
set(PYTHON_CFLAGS "-I${LIBDIR}/sqlite/include -I${LIBDIR}/bzip2/include -I${LIBDIR}/lzma/include -I${LIBDIR}/zlib/include")
@@ -76,7 +89,6 @@ else()
export CPPFLAGS=${PYTHON_CFLAGS} &&
export LDFLAGS=${PYTHON_LDFLAGS} &&
export PKG_CONFIG_PATH=${LIBDIR}/ffi/lib/pkgconfig)
- set(PYTHON_PATCH ${PATCH_CMD} --verbose -p1 -d ${BUILD_DIR}/python/src/external_python < ${PATCH_DIR}/python_linux.diff)
ExternalProject_Add(external_python
URL ${PYTHON_URI}
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/sqlite.cmake b/build_files/build_environment/cmake/sqlite.cmake
index 9fa2fa7c708..90330c68811 100644
--- a/build_files/build_environment/cmake/sqlite.cmake
+++ b/build_files/build_environment/cmake/sqlite.cmake
@@ -51,7 +51,7 @@ ExternalProject_Add(external_sqlite
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH SHA1=${SQLITE_HASH}
PREFIX ${BUILD_DIR}/sqlite
- PATCH_COMMAND ${SQLITE_PATCH_CMD}
+ PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/sqlite/src/external_sqlite < ${PATCH_DIR}/sqlite.diff
CONFIGURE_COMMAND ${SQLITE_CONFIGURE_ENV} && cd ${BUILD_DIR}/sqlite/src/external_sqlite/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/sqlite ${SQLITE_CONFIGURATION_ARGS}
BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/sqlite/src/external_sqlite/ && make -j${MAKE_THREADS}
INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/sqlite/src/external_sqlite/ && make install
diff --git a/build_files/build_environment/cmake/ssl.cmake b/build_files/build_environment/cmake/ssl.cmake
index 6d81c6c9a26..e1c168817f4 100644
--- a/build_files/build_environment/cmake/ssl.cmake
+++ b/build_files/build_environment/cmake/ssl.cmake
@@ -20,7 +20,7 @@ set(SSL_CONFIGURE_COMMAND ./Configure)
set(SSL_PATCH_CMD echo .)
if(APPLE)
- set(SSL_OS_COMPILER "blender-darwin-x86_64")
+ set(SSL_OS_COMPILER "blender-darwin-${CMAKE_OSX_ARCHITECTURES}")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(SSL_EXTRA_ARGS enable-ec_nistp_64_gcc_128)
diff --git a/build_files/build_environment/cmake/ssl.conf b/build_files/build_environment/cmake/ssl.conf
index 91f4357f8d8..8a9c9dcab4c 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" ],
@@ -12,4 +12,9 @@
inherit_from => [ "darwin64-x86_64-cc" ],
cflags => add("-fPIC"),
},
+ "blender-darwin-arm64" => {
+ inherit_from => [ "darwin-common" ],
+ cxxflags => add("-fPIC -arch arm64"),
+ cflags => add("-fPIC -arch arm64"),
+ },
);
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/theora.cmake b/build_files/build_environment/cmake/theora.cmake
index b1352c40e37..b6f9c589423 100644
--- a/build_files/build_environment/cmake/theora.cmake
+++ b/build_files/build_environment/cmake/theora.cmake
@@ -27,6 +27,7 @@ ExternalProject_Add(external_theora
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH SHA256=${THEORA_HASH}
PREFIX ${BUILD_DIR}/theora
+ PATCH_COMMAND ${PATCH_CMD} -p 0 -d ${BUILD_DIR}/theora/src/external_theora < ${PATCH_DIR}/theora.diff
CONFIGURE_COMMAND ${THEORA_CONFIGURE_ENV} && cd ${BUILD_DIR}/theora/src/external_theora/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/theora
--disable-shared
--enable-static
diff --git a/build_files/build_environment/cmake/tiff.cmake b/build_files/build_environment/cmake/tiff.cmake
index fa5a1423603..fe2c82a6eaa 100644
--- a/build_files/build_environment/cmake/tiff.cmake
+++ b/build_files/build_environment/cmake/tiff.cmake
@@ -16,6 +16,12 @@
#
# ***** END GPL LICENSE BLOCK *****
+if(WITH_WEBP)
+ set(WITH_TIFF_WEBP ON)
+else()
+ set(WITH_TIFF_WEBP OFF)
+endif()
+
set(TIFF_EXTRA_ARGS
-DZLIB_LIBRARY=${LIBDIR}/zlib/lib/${ZLIB_LIBRARY}
-DZLIB_INCLUDE_DIR=${LIBDIR}/zlib/include
@@ -23,6 +29,8 @@ set(TIFF_EXTRA_ARGS
-DBUILD_SHARED_LIBS=OFF
-Dlzma=OFF
-Djbig=OFF
+ -Dzstd=OFF
+ -Dwebp=${WITH_TIFF_WEBP}
)
ExternalProject_Add(external_tiff
diff --git a/build_files/build_environment/cmake/usd.cmake b/build_files/build_environment/cmake/usd.cmake
index 3e4535a6f92..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.
@@ -78,14 +81,14 @@ if(WIN32)
if(BUILD_MODE STREQUAL Release)
ExternalProject_Add_Step(external_usd after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/usd/ ${HARVEST_TARGET}/usd
- COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Release/libusd_m.lib ${HARVEST_TARGET}/usd/lib/libusd_m.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Release/usd_m.lib ${HARVEST_TARGET}/usd/lib/libusd_m.lib
DEPENDEES install
)
endif()
if(BUILD_MODE STREQUAL Debug)
ExternalProject_Add_Step(external_usd after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/usd/lib ${HARVEST_TARGET}/usd/lib
- COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Debug/libusd_m_d.lib ${HARVEST_TARGET}/usd/lib/libusd_m_d.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Debug/usd_m_d.lib ${HARVEST_TARGET}/usd/lib/libusd_m_d.lib
DEPENDEES install
)
endif()
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index c1dcf98318c..ce2a1191f17 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -78,10 +78,6 @@ set(FREEGLUT_VERSION 3.0.0)
set(FREEGLUT_URI http://pilotfiber.dl.sourceforge.net/project/freeglut/freeglut/${FREEGLUT_VERSION}/freeglut-${FREEGLUT_VERSION}.tar.gz)
set(FREEGLUT_HASH 90c3ca4dd9d51cf32276bc5344ec9754)
-set(HDF5_VERSION 1.8.17)
-set(HDF5_URI https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8/hdf5-${HDF5_VERSION}/src/hdf5-${HDF5_VERSION}.tar.gz)
-set(HDF5_HASH 7d572f8f3b798a628b8245af0391a0ca)
-
set(ALEMBIC_VERSION 1.7.12)
set(ALEMBIC_URI https://github.com/alembic/alembic/archive/${ALEMBIC_VERSION}.tar.gz)
set(ALEMBIC_MD5 e2b3777f23c5c09481a008cc6f0f8a40)
@@ -139,11 +135,11 @@ set(OSL_VERSION 1.10.10)
set(OSL_URI https://github.com/imageworks/OpenShadingLanguage/archive/Release-${OSL_VERSION}.tar.gz)
set(OSL_HASH 00dec08a93c8084e53848b9ad047889f)
-set(PYTHON_VERSION 3.7.4)
+set(PYTHON_VERSION 3.7.7)
set(PYTHON_SHORT_VERSION 3.7)
set(PYTHON_SHORT_VERSION_NO_DOTS 37)
set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz)
-set(PYTHON_HASH d33e4aae66097051c2eca45ee3604803)
+set(PYTHON_HASH 172c650156f7bea68ce31b2fd01fa766)
set(TBB_VERSION 2019_U9)
set(TBB_URI https://github.com/oneapi-src/oneTBB/archive/${TBB_VERSION}.tar.gz)
@@ -153,16 +149,16 @@ set(OPENVDB_VERSION 7.0.0)
set(OPENVDB_URI https://github.com/dreamworksanimation/openvdb/archive/v${OPENVDB_VERSION}.tar.gz)
set(OPENVDB_HASH fd6c4f168282f7e0e494d290cd531fa8)
-set(IDNA_VERSION 2.8)
+set(IDNA_VERSION 2.9)
set(CHARDET_VERSION 3.0.4)
-set(URLLIB3_VERSION 1.25.3)
-set(CERTIFI_VERSION 2019.6.16)
-set(REQUESTS_VERSION 2.22.0)
+set(URLLIB3_VERSION 1.25.9)
+set(CERTIFI_VERSION 2020.4.5.2)
+set(REQUESTS_VERSION 2.23.0)
-set(NUMPY_VERSION v1.17.0)
+set(NUMPY_VERSION 1.17.5)
set(NUMPY_SHORT_VERSION 1.17)
-set(NUMPY_URI https://files.pythonhosted.org/packages/da/32/1b8f2bb5fb50e4db68543eb85ce37b9fa6660cd05b58bddfafafa7ed62da/numpy-1.17.0.zip)
-set(NUMPY_HASH aed49b31bcb44ec73b8155be78566135)
+set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip)
+set(NUMPY_HASH 763a5646fa6eef7a22f4895bca0524f2)
set(LAME_VERSION 3.100)
set(LAME_URI http://downloads.sourceforge.net/project/lame/lame/3.100/lame-${LAME_VERSION}.tar.gz)
@@ -192,8 +188,8 @@ set(OPUS_VERSION 1.3.1)
set(OPUS_URI https://archive.mozilla.org/pub/opus/opus-${OPUS_VERSION}.tar.gz)
set(OPUS_HASH 65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d)
-set(X264_URI https://code.videolan.org/videolan/x264/-/archive/master/x264-33f9e1474613f59392be5ab6a7e7abf60fa63622.tar.gz)
-set(X264_HASH 300dfb5b6c35722516f168868ce9419252a9e9eb77a05d82c9cede925b691bd6)
+set(X264_URI https://code.videolan.org/videolan/x264/-/archive/33f9e1474613f59392be5ab6a7e7abf60fa63622/x264-33f9e1474613f59392be5ab6a7e7abf60fa63622.tar.gz)
+set(X264_HASH 5456450ee1ae02cd2328be3157367a232a0ab73315e8c8f80dab80469524f525)
set(XVIDCORE_VERSION 1.3.7)
set(XVIDCORE_URI https://downloads.xvid.com/downloads/xvidcore-${XVIDCORE_VERSION}.tar.gz)
@@ -216,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)
@@ -240,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)
@@ -273,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)
@@ -297,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 19.11)
+set(USD_VERSION 20.05)
set(USD_URI https://github.com/PixarAnimationStudios/USD/archive/v${USD_VERSION}.tar.gz)
-set(USD_HASH 79ff176167b3fe85f4953abd6cc5e0cc)
+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)
@@ -313,6 +305,13 @@ set(MESA_VERSION 18.3.1)
set(MESA_URI ftp://ftp.freedesktop.org/pub/mesa//mesa-${MESA_VERSION}.tar.xz)
set(MESA_HASH d60828056d77bfdbae0970f9b15fb1be)
+set(NASM_VERSION 2.15.02)
+set(NASM_URI https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/nasm-${NASM_VERSION}.tar.xz)
+set(NASM_HASH f4fd1329b1713e1ccd34b2fc121c4bcd278c9f91cc4cb205ae8fcd2e4728dd14)
+
set(XR_OPENXR_SDK_VERSION 1.0.8)
set(XR_OPENXR_SDK_URI https://github.com/KhronosGroup/OpenXR-SDK/archive/release-${XR_OPENXR_SDK_VERSION}.tar.gz)
set(XR_OPENXR_SDK_HASH c6de63d2e0f9029aa58dfa97cad8ce07)
+set(ISPC_VERSION v1.13.0)
+set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz)
+set(ISPC_HASH 4bf5e8d0020c4b9980faa702c1a6f25f)
diff --git a/build_files/build_environment/cmake/vpx.cmake b/build_files/build_environment/cmake/vpx.cmake
index 741493859e2..799dea0189c 100644
--- a/build_files/build_environment/cmake/vpx.cmake
+++ b/build_files/build_environment/cmake/vpx.cmake
@@ -24,7 +24,11 @@ if(WIN32)
endif()
else()
if(APPLE)
- set(VPX_EXTRA_FLAGS --target=x86_64-darwin13-gcc)
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(VPX_EXTRA_FLAGS --target=generic-gnu)
+ else()
+ set(VPX_EXTRA_FLAGS --target=x86_64-darwin17-gcc)
+ endif()
else()
set(VPX_EXTRA_FLAGS --target=generic-gnu)
endif()
diff --git a/build_files/build_environment/cmake/x264.cmake b/build_files/build_environment/cmake/x264.cmake
index 8bcb5a2938f..993e4591cb7 100644
--- a/build_files/build_environment/cmake/x264.cmake
+++ b/build_files/build_environment/cmake/x264.cmake
@@ -21,12 +21,26 @@ if(WIN32)
endif()
+if(APPLE)
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
+ set(X264_CONFIGURE_ENV echo .)
+ else()
+ set(X264_CONFIGURE_ENV
+ export AS=${LIBDIR}/nasm/bin/nasm
+ )
+ endif()
+else()
+ set(X264_CONFIGURE_ENV echo .)
+endif()
+
ExternalProject_Add(external_x264
URL ${X264_URI}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH SHA256=${X264_HASH}
PREFIX ${BUILD_DIR}/x264
- CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/x264/src/external_x264/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/x264
+ CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${X264_CONFIGURE_ENV} && cd ${BUILD_DIR}/x264/src/external_x264/ &&
+ ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/x264
--enable-static
--enable-pic
--disable-lavf
@@ -39,3 +53,10 @@ ExternalProject_Add(external_x264
if(MSVC)
set_target_properties(external_x264 PROPERTIES FOLDER Mingw)
endif()
+
+if(APPLE)
+ add_dependencies(
+ external_x264
+ external_nasm
+ )
+endif()
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 511ea341bb1..49d7b8209ee 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -376,7 +376,7 @@ USE_CXX11=true
CLANG_FORMAT_VERSION_MIN="6.0"
-PYTHON_VERSION="3.7.4"
+PYTHON_VERSION="3.7.7"
PYTHON_VERSION_MIN="3.7"
PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_MIN
PYTHON_FORCE_BUILD=false
@@ -459,7 +459,7 @@ ALEMBIC_FORCE_BUILD=false
ALEMBIC_FORCE_REBUILD=false
ALEMBIC_SKIP=false
-USD_VERSION="19.11"
+USD_VERSION="20.05"
USD_FORCE_BUILD=false
USD_FORCE_REBUILD=false
USD_SKIP=false
@@ -1574,7 +1574,7 @@ compile_TBB() {
if [ ! -d $_inst ]; then
INFO "Building TBB-$TBB_VERSION$TBB_VERSION_UPDATE"
_is_building=true
-
+
# Rebuild dependencies as well!
_update_deps_tbb
@@ -1691,7 +1691,7 @@ compile_OCIO() {
if [ ! -d $_inst ]; then
INFO "Building OpenColorIO-$OCIO_VERSION"
_is_building=true
-
+
# Rebuild dependencies as well!
_update_deps_ocio
@@ -3678,7 +3678,7 @@ install_DEB() {
check_package_version_ge_DEB python3-dev $PYTHON_VERSION_MIN
if [ $? -eq 0 ]; then
PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python3-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
-
+
install_packages_DEB python3-dev
clean_Python
PRINT ""
@@ -3892,7 +3892,6 @@ install_DEB() {
INFO "Forced Alembic building, as requested..."
compile_ALEMBIC
else
- # No package currently, only HDF5!
compile_ALEMBIC
fi
diff --git a/build_files/build_environment/patches/blosc.diff b/build_files/build_environment/patches/blosc.diff
index ee5826a2e98..0080694fae1 100644
--- a/build_files/build_environment/patches/blosc.diff
+++ b/build_files/build_environment/patches/blosc.diff
@@ -91,3 +91,41 @@ diff -Naur external_blosc.orig/blosc/blosc.c external_blosc/blosc/blosc.c
/* Some useful units */
+ diff --git a/blosc/shuffle.c b/blosc/shuffle.c
+ index 84b5095..23053b4 100644
+ --- a/blosc/shuffle.c
+ +++ b/blosc/shuffle.c
+ @@ -490,12 +490,12 @@ void unshuffle(size_t bytesoftype, size_t blocksize,
+ #else /* no __SSE2__ available */
+
+ void shuffle(size_t bytesoftype, size_t blocksize,
+ - uint8_t* _src, uint8_t* _dest) {
+ + const uint8_t* _src, uint8_t* _dest) {
+ _shuffle(bytesoftype, blocksize, _src, _dest);
+ }
+
+ void unshuffle(size_t bytesoftype, size_t blocksize,
+ - uint8_t* _src, uint8_t* _dest) {
+ + const uint8_t* _src, uint8_t* _dest) {
+ _unshuffle(bytesoftype, blocksize, _src, _dest);
+ }
+ --- a/cmake/FindSSE.cmake
+ +++ b/cmake/FindSSE.cmake
+ @@ -49,6 +49,17 @@
+ set(AVX_FOUND false CACHE BOOL "AVX available on host")
+ ENDIF (AVX_TRUE)
+ ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ + execute_process(COMMAND uname -m OUTPUT_VARIABLE ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
+ + message(STATUS "Detected architecture ${ARCHITECTURE}")
+ + IF("${ARCHITECTURE}" STREQUAL "arm64")
+ + set(SSE2_FOUND false CACHE BOOL "SSE2 available on host")
+ + set(SSE3_FOUND false CACHE BOOL "SSE3 available on host")
+ + set(SSSE3_FOUND false CACHE BOOL "SSSE3 available on host")
+ + set(SSE4_1_FOUND false CACHE BOOL "SSE4.1 available on host")
+ + set(AVX_FOUND false CACHE BOOL "AVX available on host")
+ + return()
+ + ENDIF()
+ +
+ EXEC_PROGRAM("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE
+ CPUINFO)
+ \ No newline at end of file
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/embree.diff b/build_files/build_environment/patches/embree.diff
new file mode 100644
index 00000000000..9b2c66feaf7
--- /dev/null
+++ b/build_files/build_environment/patches/embree.diff
@@ -0,0 +1,14 @@
+diff -Naur orig/common/sys/platform.h external_embree/common/sys/platform.h
+--- orig/common/sys/platform.h 2020-05-13 23:08:53 -0600
++++ external_embree/common/sys/platform.h 2020-06-13 17:40:26 -0600
+@@ -84,8 +84,8 @@
+ ////////////////////////////////////////////////////////////////////////////////
+
+ #ifdef __WIN32__
+-#define dll_export __declspec(dllexport)
+-#define dll_import __declspec(dllimport)
++#define dll_export
++#define dll_import
+ #else
+ #define dll_export __attribute__ ((visibility ("default")))
+ #define dll_import
diff --git a/build_files/build_environment/patches/ffmpeg.diff b/build_files/build_environment/patches/ffmpeg.diff
index 960728ae980..e195ca272de 100644
--- a/build_files/build_environment/patches/ffmpeg.diff
+++ b/build_files/build_environment/patches/ffmpeg.diff
@@ -9,3 +9,62 @@
enabled libopenmpt && require_pkg_config libopenmpt "libopenmpt >= 0.2.6557" libopenmpt/libopenmpt.h openmpt_module_create -lstdc++ && append libopenmpt_extralibs "-lstdc++"
enabled libopus && {
enabled libopus_decoder && {
+--- a/libavcodec/cfhddata.c
++++ b/libavcodec/cfhddata.c
+@@ -276,10 +276,10 @@
+ av_cold int ff_cfhd_init_vlcs(CFHDContext *s)
+ {
+ int i, j, ret = 0;
+- uint32_t new_cfhd_vlc_bits[NB_VLC_TABLE_18 * 2];
+- uint8_t new_cfhd_vlc_len[NB_VLC_TABLE_18 * 2];
+- uint16_t new_cfhd_vlc_run[NB_VLC_TABLE_18 * 2];
+- int16_t new_cfhd_vlc_level[NB_VLC_TABLE_18 * 2];
++ uint32_t *new_cfhd_vlc_bits = av_calloc(sizeof(uint32_t), NB_VLC_TABLE_18 * 2);
++ uint8_t *new_cfhd_vlc_len = av_calloc(sizeof(uint8_t), NB_VLC_TABLE_18 * 2);
++ uint16_t *new_cfhd_vlc_run = av_calloc(sizeof(uint16_t), NB_VLC_TABLE_18 * 2);
++ int16_t *new_cfhd_vlc_level = av_calloc(sizeof(int16_t), NB_VLC_TABLE_18 * 2);
+
+ /** Similar to dv.c, generate signed VLC tables **/
+
+@@ -305,8 +305,13 @@
+
+ ret = init_vlc(&s->vlc_9, VLC_BITS, j, new_cfhd_vlc_len,
+ 1, 1, new_cfhd_vlc_bits, 4, 4, 0);
+- if (ret < 0)
++ if (ret < 0) {
++ av_free(new_cfhd_vlc_bits);
++ av_free(new_cfhd_vlc_len);
++ av_free(new_cfhd_vlc_run);
++ av_free(new_cfhd_vlc_level);
+ return ret;
++ }
+ for (i = 0; i < s->vlc_9.table_size; i++) {
+ int code = s->vlc_9.table[i][0];
+ int len = s->vlc_9.table[i][1];
+@@ -346,8 +351,14 @@
+
+ ret = init_vlc(&s->vlc_18, VLC_BITS, j, new_cfhd_vlc_len,
+ 1, 1, new_cfhd_vlc_bits, 4, 4, 0);
+- if (ret < 0)
++ if (ret < 0) {
++ av_free(new_cfhd_vlc_bits);
++ av_free(new_cfhd_vlc_len);
++ av_free(new_cfhd_vlc_run);
++ av_free(new_cfhd_vlc_level);
+ return ret;
++ }
++
+ av_assert0(s->vlc_18.table_size == 4572);
+
+ for (i = 0; i < s->vlc_18.table_size; i++) {
+@@ -367,5 +378,10 @@
+ s->table_18_rl_vlc[i].run = run;
+ }
+
++ av_free(new_cfhd_vlc_bits);
++ av_free(new_cfhd_vlc_len);
++ av_free(new_cfhd_vlc_run);
++ av_free(new_cfhd_vlc_level);
++
+ return ret;
+ }
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..689dd0abdc5
--- /dev/null
+++ b/build_files/build_environment/patches/ispc.diff
@@ -0,0 +1,85 @@
+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/CMakeLists.txt b/CMakeLists.txt
+index 46a8db8..f53beef 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -36,8 +36,12 @@
+ cmake_minimum_required(VERSION 3.13)
+
+ if (UNIX)
+- set(CMAKE_C_COMPILER "clang")
+- set(CMAKE_CXX_COMPILER "clang++")
++ if (NOT CMAKE_C_COMPILER)
++ set(CMAKE_C_COMPILER "clang")
++ endif()
++ if (NOT CMAKE_CXX_COMPILER)
++ set(CMAKE_CXX_COMPILER "clang++")
++ endif()
+ endif()
+
+ set(PROJECT_NAME ispc)
+@@ -412,6 +416,29 @@ else()
+ endif()
+ endif()
+
++# Link against libstdc++.a which must be provided to the linker after
++# LLVM and CLang libraries.
++# This is needed because some of LLVM/CLang dependencies are using
++# std::make_shared, which is defined in one of those:
++# - libclang-cpp.so
++# - libstdc++.a
++# Using the former one is tricky because then generated binary depends
++# on a library which is outside of the LD_LIBRARY_PATH.
++#
++# Hence, using C++ implementation from G++ which seems to work just fine.
++# In fact, from investigation seems that libclang-cpp.so itself is pulling
++# std::_Sp_make_shared_tag from G++'s libstdc++.a.
++if(UNIX AND NOT APPLE)
++ execute_process(
++ COMMAND g++ --print-file-name libstdc++.a
++ OUTPUT_VARIABLE GCC_LIBSTDCXX_A
++ OUTPUT_STRIP_TRAILING_WHITESPACE
++ )
++ if(GCC_LIBSTDCXX_A AND EXISTS ${GCC_LIBSTDCXX_A})
++ target_link_libraries(${PROJECT_NAME} ${GCC_LIBSTDCXX_A})
++ endif()
++endif()
++
+ # Build target for utility checking host ISA
+ if (ISPC_INCLUDE_UTILS)
+ add_executable(check_isa "")
diff --git a/build_files/build_environment/patches/nasm.diff b/build_files/build_environment/patches/nasm.diff
new file mode 100644
index 00000000000..821e1a1d905
--- /dev/null
+++ b/build_files/build_environment/patches/nasm.diff
@@ -0,0 +1,129 @@
+diff --git a/output/macho.h b/output/macho.h
+index 538c531e..fd5e8849 100644
+--- a/output/macho.h
++++ b/output/macho.h
+@@ -60,6 +60,8 @@
+ #define LC_SEGMENT 0x1
+ #define LC_SEGMENT_64 0x19
+ #define LC_SYMTAB 0x2
++#define LC_VERSION_MIN_MACOSX 0x24
++#define LC_BUILD_VERSION 0x32
+
+ /* Symbol type bits */
+ #define N_STAB 0xe0
+diff --git a/output/outmacho.c b/output/outmacho.c
+index 08147883..de6ec902 100644
+--- a/output/outmacho.c
++++ b/output/outmacho.c
+@@ -38,6 +38,8 @@
+
+ #include "compiler.h"
+
++#include <stdlib.h>
++
+ #include "nctype.h"
+
+ #include "nasm.h"
+@@ -64,6 +66,8 @@
+ #define MACHO_SYMCMD_SIZE 24
+ #define MACHO_NLIST_SIZE 12
+ #define MACHO_RELINFO_SIZE 8
++#define MACHO_BUILD_VERSION_SIZE 24
++#define MACHO_VERSION_MIN_MACOSX_SIZE 16
+
+ #define MACHO_HEADER64_SIZE 32
+ #define MACHO_SEGCMD64_SIZE 72
+@@ -1224,6 +1228,46 @@ static void macho_layout_symbols (uint32_t *numsyms,
+ }
+ }
+
++static bool get_full_version_from_env (const char *variable_name,
++ int *r_major,
++ int *r_minor,
++ int *r_patch) {
++ *r_major = 0;
++ *r_minor = 0;
++ *r_patch = 0;
++
++ const char *value = getenv(variable_name);
++ if (value == NULL || value[0] == '\0') {
++ return false;
++ }
++
++ const char *current_value = value;
++ const char *end_value = value + strlen(value);
++
++ char *endptr;
++
++ *r_major = strtol(current_value, &endptr, 10);
++ if (endptr >= end_value) {
++ return true;
++ }
++ current_value = endptr + 1;
++
++ *r_minor = strtol(current_value, &endptr, 10);
++ if (endptr >= end_value) {
++ return true;
++ }
++ current_value = endptr + 1;
++
++ *r_patch = strtol(current_value, &endptr, 10);
++
++ return true;
++}
++
++static bool need_version_min_macosx_command (void) {
++ return getenv("MACOSX_DEPLOYMENT_TARGET") &&
++ getenv("MACOSX_SDK_VERSION");
++}
++
+ /* Calculate some values we'll need for writing later. */
+
+ static void macho_calculate_sizes (void)
+@@ -1270,6 +1314,12 @@ static void macho_calculate_sizes (void)
+ head_sizeofcmds += fmt.segcmd_size + seg_nsects * fmt.sectcmd_size;
+ }
+
++ /* LC_VERSION_MIN_MACOSX */
++ if (need_version_min_macosx_command()) {
++ ++head_ncmds;
++ head_sizeofcmds += MACHO_VERSION_MIN_MACOSX_SIZE;
++ }
++
+ if (nsyms > 0) {
+ ++head_ncmds;
+ head_sizeofcmds += MACHO_SYMCMD_SIZE;
+@@ -1653,6 +1703,33 @@ static void macho_write (void)
+ else
+ nasm_warn(WARN_OTHER, "no sections?");
+
++#define ENCODE_BUILD_VERSION(major, minor, patch) \
++ (((major) << 16) | ((minor) << 8) | (patch))
++
++ if (0) {
++ fwriteint32_t(LC_BUILD_VERSION, ofile); /* cmd == LC_BUILD_VERSION */
++ fwriteint32_t(MACHO_BUILD_VERSION_SIZE, ofile); /* size of load command */
++ fwriteint32_t(1, ofile); /* platform */
++ fwriteint32_t(ENCODE_BUILD_VERSION(10, 13, 0), ofile); /* minos, X.Y.Z is encoded in nibbles xxxx.yy.zz */
++ fwriteint32_t(ENCODE_BUILD_VERSION(10, 15, 4), ofile); /* sdk, X.Y.Z is encoded in nibbles xxxx.yy.zz */
++ fwriteint32_t(0, ofile); /* number of tool entries following this */
++ }
++
++ if (need_version_min_macosx_command()) {
++ int sdk_major, sdk_minor, sdk_patch;
++ get_full_version_from_env("MACOSX_SDK_VERSION", &sdk_major, &sdk_minor, &sdk_patch);
++
++ int version_major, version_minor, version_patch;
++ get_full_version_from_env("MACOSX_DEPLOYMENT_TARGET", &version_major, &version_minor, &version_patch);
++
++ fwriteint32_t(LC_VERSION_MIN_MACOSX, ofile); /* cmd == LC_VERSION_MIN_MACOSX */
++ fwriteint32_t(MACHO_VERSION_MIN_MACOSX_SIZE, ofile); /* size of load command */
++ fwriteint32_t(ENCODE_BUILD_VERSION(version_major, version_minor, version_patch), ofile); /* minos, X.Y.Z is encoded in nibbles xxxx.yy.zz */
++ fwriteint32_t(ENCODE_BUILD_VERSION(sdk_major, sdk_minor, sdk_patch), ofile); /* sdk, X.Y.Z is encoded in nibbles xxxx.yy.zz */
++ }
++
++#undef ENCODE_BUILD_VERSION
++
+ if (nsyms > 0) {
+ /* write out symbol command */
+ fwriteint32_t(LC_SYMTAB, ofile); /* cmd == LC_SYMTAB */
diff --git a/build_files/build_environment/patches/numpy.diff b/build_files/build_environment/patches/numpy.diff
new file mode 100644
index 00000000000..a9b783dd856
--- /dev/null
+++ b/build_files/build_environment/patches/numpy.diff
@@ -0,0 +1,27 @@
+diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py
+index ba2b1f4..b10f7df 100644
+--- a/numpy/distutils/system_info.py
++++ b/numpy/distutils/system_info.py
+@@ -2164,8 +2164,8 @@ class accelerate_info(system_info):
+ 'accelerate' in libraries):
+ if intel:
+ args.extend(['-msse3'])
+- else:
+- args.extend(['-faltivec'])
++# else:
++# args.extend(['-faltivec'])
+ args.extend([
+ '-I/System/Library/Frameworks/vecLib.framework/Headers'])
+ link_args.extend(['-Wl,-framework', '-Wl,Accelerate'])
+@@ -2174,8 +2174,8 @@ class accelerate_info(system_info):
+ 'veclib' in libraries):
+ if intel:
+ args.extend(['-msse3'])
+- else:
+- args.extend(['-faltivec'])
++# else:
++# args.extend(['-faltivec'])
+ args.extend([
+ '-I/System/Library/Frameworks/vecLib.framework/Headers'])
+ link_args.extend(['-Wl,-framework', '-Wl,vecLib'])
+
diff --git a/build_files/build_environment/patches/ogg.diff b/build_files/build_environment/patches/ogg.diff
new file mode 100644
index 00000000000..fca426e1d35
--- /dev/null
+++ b/build_files/build_environment/patches/ogg.diff
@@ -0,0 +1,12 @@
+diff --git a/include/ogg/os_types.h b/include/ogg/os_types.h
+index eb8a322..6f73b72 100644
+--- a/include/ogg/os_types.h
++++ b/include/ogg/os_types.h
+@@ -71,6 +71,7 @@
+ #elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
+
+ # include <sys/types.h>
++# include <stdint.h>
+ typedef int16_t ogg_int16_t;
+ typedef uint16_t ogg_uint16_t;
+ typedef int32_t ogg_int32_t;
diff --git a/build_files/build_environment/patches/opencollada.diff b/build_files/build_environment/patches/opencollada.diff
index cd4cc2c1652..e8efc1a6909 100644
--- a/build_files/build_environment/patches/opencollada.diff
+++ b/build_files/build_environment/patches/opencollada.diff
@@ -86,3 +86,47 @@ index 1f9a3ee..d151e9a 100644
return isnan( value );
#else
return std::isnan(value);
+
+
+diff --git a/DAEValidator/library/src/Dae.cpp b/DAEValidator/library/src/Dae.cpp
+index 9256ee1..241ad67 100644
+--- a/DAEValidator/library/src/Dae.cpp
++++ b/DAEValidator/library/src/Dae.cpp
+@@ -304,7 +304,7 @@ namespace opencollada
+ if (auto root_node = root())
+ {
+ const auto & nodes = root_node.selectNodes("//*[@id]");
+- for (const auto & node : nodes)
++ for (const auto node : nodes)
+ {
+ string id = node.attribute("id").value();
+ mIdCache.insert(id);
+@@ -312,4 +312,4 @@ namespace opencollada
+ }
+ }
+ }
+-}
+\ No newline at end of file
++}
+diff --git a/DAEValidator/library/src/DaeValidator.cpp b/DAEValidator/library/src/DaeValidator.cpp
+index 715d903..24423ce 100644
+--- a/DAEValidator/library/src/DaeValidator.cpp
++++ b/DAEValidator/library/src/DaeValidator.cpp
+@@ -162,7 +162,7 @@ namespace opencollada
+
+ // Find xsi:schemaLocation attributes in dae and try to validate against specified xsd documents
+ const auto & elements = dae.root().selectNodes("//*[@xsi:schemaLocation]");
+- for (const auto & element : elements)
++ for (const auto element : elements)
+ {
+ if (auto schemaLocation = element.attribute("schemaLocation"))
+ {
+@@ -274,7 +274,7 @@ namespace opencollada
+ int result = 0;
+ map<string, size_t> ids;
+ const auto & nodes = dae.root().selectNodes("//*[@id]");
+- for (const auto & node : nodes)
++ for (const auto node : nodes)
+ {
+ string id = node.attribute("id").value();
+ size_t line = node.line();
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/openmp.diff b/build_files/build_environment/patches/openmp.diff
new file mode 100644
index 00000000000..201ab5c7713
--- /dev/null
+++ b/build_files/build_environment/patches/openmp.diff
@@ -0,0 +1,23 @@
+diff --git a/runtime/src/z_Linux_asm.S b/runtime/src/z_Linux_asm.S
+index 0d8885e..42aa5ad 100644
+--- a/runtime/src/z_Linux_asm.S
++++ b/runtime/src/z_Linux_asm.S
+@@ -1540,10 +1540,12 @@ __kmp_unnamed_critical_addr:
+ .comm .gomp_critical_user_,32,8
+ .data
+ .align 8
+- .global __kmp_unnamed_critical_addr
+-__kmp_unnamed_critical_addr:
++ .global ___kmp_unnamed_critical_addr
++___kmp_unnamed_critical_addr:
+ .8byte .gomp_critical_user_
+- .size __kmp_unnamed_critical_addr,8
++# if !(KMP_OS_DARWIN)
++ .size ___kmp_unnamed_critical_addr,8
++# endif
+ #endif /* KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 */
+
+ #if KMP_OS_LINUX
+
+
+
diff --git a/build_files/build_environment/patches/python_macos.diff b/build_files/build_environment/patches/python_macos.diff
new file mode 100644
index 00000000000..22ccbebee2f
--- /dev/null
+++ b/build_files/build_environment/patches/python_macos.diff
@@ -0,0 +1,289 @@
+diff -ru a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
+--- a/Doc/library/ctypes.rst 2020-03-10 07:11:12.000000000 +0100
++++ b/Doc/library/ctypes.rst 2020-07-14 08:10:10.000000000 +0200
+@@ -1551,6 +1551,13 @@
+ value usable as argument (integer, string, ctypes instance). This allows
+ defining adapters that can adapt custom objects as function parameters.
+
++ .. attribute:: variadic
++
++ Assign a boolean to specify that the function takes a variable number of
++ arguments. This does not matter on most platforms, but for Apple arm64
++ platforms variadic functions have a different calling convention than
++ normal functions.
++
+ .. attribute:: errcheck
+
+ Assign a Python function or another callable to this attribute. The
+diff -ru a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
+--- a/Modules/_ctypes/_ctypes.c 2020-03-10 07:11:12.000000000 +0100
++++ b/Modules/_ctypes/_ctypes.c 2020-07-14 08:14:41.000000000 +0200
+@@ -3175,6 +3175,35 @@
+ }
+
+ static int
++PyCFuncPtr_set_variadic(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
++{
++ StgDictObject *dict = PyObject_stgdict((PyObject *)self);
++ assert(dict);
++ int r = PyObject_IsTrue(ob);
++ if (r == 1) {
++ dict->flags |= FUNCFLAG_VARIADIC;
++ return 0;
++ } else if (r == 0) {
++ dict->flags &= ~FUNCFLAG_VARIADIC;
++ return 0;
++ } else {
++ return -1;
++ }
++}
++
++static PyObject *
++PyCFuncPtr_get_variadic(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
++{
++ StgDictObject *dict = PyObject_stgdict((PyObject *)self);
++ assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
++ if (dict->flags & FUNCFLAG_VARIADIC)
++ Py_RETURN_TRUE;
++ else
++ Py_RETURN_FALSE;
++}
++
++
++static int
+ PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
+ {
+ PyObject *converters;
+@@ -5632,6 +5661,7 @@
+ PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
+ PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
+ PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
++ PyModule_AddObject(m, "FUNCFLAG_VARIADIC", PyLong_FromLong(FUNCFLAG_VARIADIC));
+ PyModule_AddStringConstant(m, "__version__", "1.1.0");
+
+ PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove));
+diff -ru a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
+--- a/Modules/_ctypes/callproc.c 2020-03-10 07:11:12.000000000 +0100
++++ b/Modules/_ctypes/callproc.c 2020-07-14 08:18:33.000000000 +0200
+@@ -767,7 +767,8 @@
+ ffi_type **atypes,
+ ffi_type *restype,
+ void *resmem,
+- int argcount)
++ int argcount,
++ int argtypecount)
+ {
+ PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
+ PyObject *error_object = NULL;
+@@ -793,15 +794,38 @@
+ if ((flags & FUNCFLAG_CDECL) == 0)
+ cc = FFI_STDCALL;
+ #endif
+- if (FFI_OK != ffi_prep_cif(&cif,
+- cc,
+- argcount,
+- restype,
+- atypes)) {
+- PyErr_SetString(PyExc_RuntimeError,
+- "ffi_prep_cif failed");
+- return -1;
+- }
++#if HAVE_FFI_PREP_CIF_VAR
++ /* Everyone SHOULD set f.variadic=True on variadic function pointers, but
++ * lots of existing code will not. If there's at least one arg and more
++ * args are passed than are defined in the prototype, then it must be a
++ * variadic function. */
++ if ((flags & FUNCFLAG_VARIADIC) ||
++ (argtypecount != 0 && argcount > argtypecount))
++ {
++ if (FFI_OK != ffi_prep_cif_var(&cif,
++ cc,
++ argtypecount,
++ argcount,
++ restype,
++ atypes)) {
++ PyErr_SetString(PyExc_RuntimeError,
++ "ffi_prep_cif_var failed");
++ return -1;
++ }
++ } else {
++#endif
++ if (FFI_OK != ffi_prep_cif(&cif,
++ cc,
++ argcount,
++ restype,
++ atypes)) {
++ PyErr_SetString(PyExc_RuntimeError,
++ "ffi_prep_cif failed");
++ return -1;
++ }
++#if HAVE_FFI_PREP_CIF_VAR
++ }
++#endif
+
+ if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
+ error_object = _ctypes_get_errobj(&space);
+@@ -1185,9 +1209,8 @@
+
+ if (-1 == _call_function_pointer(flags, pProc, avalues, atypes,
+ rtype, resbuf,
+- Py_SAFE_DOWNCAST(argcount,
+- Py_ssize_t,
+- int)))
++ Py_SAFE_DOWNCAST(argcount, Py_ssize_t, int),
++ Py_SAFE_DOWNCAST(argtype_count, Py_ssize_t, int)))
+ goto cleanup;
+
+ #ifdef WORDS_BIGENDIAN
+diff -ru a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
+--- a/Modules/_ctypes/ctypes.h 2020-03-10 07:11:12.000000000 +0100
++++ b/Modules/_ctypes/ctypes.h 2020-07-14 08:30:53.000000000 +0200
+@@ -285,6 +285,7 @@
+ #define FUNCFLAG_PYTHONAPI 0x4
+ #define FUNCFLAG_USE_ERRNO 0x8
+ #define FUNCFLAG_USE_LASTERROR 0x10
++#define FUNCFLAG_VARIADIC 0x20
+
+ #define TYPEFLAG_ISPOINTER 0x100
+ #define TYPEFLAG_HASPOINTER 0x200
+diff -ru a/configure b/configure
+--- a/configure 2020-03-10 07:11:12.000000000 +0100
++++ b/configure 2020-07-14 08:03:27.000000000 +0200
+@@ -3374,7 +3374,7 @@
+ # has no effect, don't bother defining them
+ Darwin/[6789].*)
+ define_xopen_source=no;;
+- Darwin/1[0-9].*)
++ Darwin/[12][0-9].*)
+ define_xopen_source=no;;
+ # On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but
+ # used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined
+@@ -9251,6 +9251,9 @@
+ ppc)
+ MACOSX_DEFAULT_ARCH="ppc64"
+ ;;
++ arm64)
++ MACOSX_DEFAULT_ARCH="arm64"
++ ;;
+ *)
+ as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5
+ ;;
+diff -ru a/configure.ac b/configure.ac
+--- a/configure.ac 2020-03-10 07:11:12.000000000 +0100
++++ b/configure.ac 2020-07-14 08:03:27.000000000 +0200
+@@ -2456,6 +2456,9 @@
+ ppc)
+ MACOSX_DEFAULT_ARCH="ppc64"
+ ;;
++ arm64)
++ MACOSX_DEFAULT_ARCH="arm64"
++ ;;
+ *)
+ AC_MSG_ERROR([Unexpected output of 'arch' on OSX])
+ ;;
+diff -ru a/setup.py b/setup.py
+--- a/setup.py 2020-03-10 07:11:12.000000000 +0100
++++ b/setup.py 2020-07-14 08:28:12.000000000 +0200
+@@ -141,6 +141,13 @@
+ os.unlink(tmpfile)
+
+ return MACOS_SDK_ROOT
++
++def is_macosx_at_least(vers):
++ if host_platform == 'darwin':
++ dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
++ if dep_target:
++ return tuple(map(int, dep_target.split('.'))) >= vers
++ return False
+
+ def is_macosx_sdk_path(path):
+ """
+@@ -150,6 +157,13 @@
+ or path.startswith('/System/')
+ or path.startswith('/Library/') )
+
++def grep_headers_for(function, headers):
++ for header in headers:
++ with open(header, 'r') as f:
++ if function in f.read():
++ return True
++ return False
++
+ def find_file(filename, std_dirs, paths):
+ """Searches for the directory where a given file is located,
+ and returns a possibly-empty list of additional directories, or None
+@@ -1972,7 +1986,11 @@
+ return True
+
+ def detect_ctypes(self, inc_dirs, lib_dirs):
+- self.use_system_libffi = False
++ if not sysconfig.get_config_var("LIBFFI_INCLUDEDIR") and is_macosx_at_least((10,15)):
++ self.use_system_libffi = True
++ else:
++ self.use_system_libffi = '--with-system-ffi' in sysconfig.get_config_var("CONFIG_ARGS")
++
+ include_dirs = []
+ extra_compile_args = []
+ extra_link_args = []
+@@ -2016,32 +2034,48 @@
+ ext_test = Extension('_ctypes_test',
+ sources=['_ctypes/_ctypes_test.c'],
+ libraries=['m'])
++ ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR")
++ ffi_lib = None
++
+ self.extensions.extend([ext, ext_test])
+
+ if host_platform == 'darwin':
+- if '--with-system-ffi' not in sysconfig.get_config_var("CONFIG_ARGS"):
++ if not self.use_system_libffi:
+ return
+- # OS X 10.5 comes with libffi.dylib; the include files are
+- # in /usr/include/ffi
+- inc_dirs.append('/usr/include/ffi')
+-
+- ffi_inc = [sysconfig.get_config_var("LIBFFI_INCLUDEDIR")]
+- if not ffi_inc or ffi_inc[0] == '':
+- ffi_inc = find_file('ffi.h', [], inc_dirs)
+- if ffi_inc is not None:
+- ffi_h = ffi_inc[0] + '/ffi.h'
++ ffi_in_sdk = os.path.join(macosx_sdk_root(), "usr/include/ffi")
++ if os.path.exists(ffi_in_sdk):
++ ffi_inc = ffi_in_sdk
++ ffi_lib = 'ffi'
++ else:
++ # OS X 10.5 comes with libffi.dylib; the include files are
++ # in /usr/include/ffi
++ ffi_inc_dirs.append('/usr/include/ffi')
++
++ if not ffi_inc:
++ found = find_file('ffi.h', [], ffi_inc_dirs)
++ if found:
++ ffi_inc = found[0]
++ if ffi_inc:
++ ffi_h = ffi_inc + '/ffi.h'
+ if not os.path.exists(ffi_h):
+ ffi_inc = None
+ print('Header file {} does not exist'.format(ffi_h))
+- ffi_lib = None
+- if ffi_inc is not None:
++ if ffi_lib is None and ffi_inc:
+ for lib_name in ('ffi', 'ffi_pic'):
+ if (self.compiler.find_library_file(lib_dirs, lib_name)):
+ ffi_lib = lib_name
+ break
+
+ if ffi_inc and ffi_lib:
+- ext.include_dirs.extend(ffi_inc)
++ ffi_headers = glob(os.path.join(ffi_inc, '*.h'))
++ if grep_headers_for('ffi_closure_alloc', ffi_headers):
++ try:
++ sources.remove('_ctypes/malloc_closure.c')
++ except ValueError:
++ pass
++ if grep_headers_for('ffi_prep_cif_var', ffi_headers):
++ ext.extra_compile_args.append("-DHAVE_FFI_PREP_CIF_VAR=1")
++ ext.include_dirs.append(ffi_inc)
+ ext.libraries.append(ffi_lib)
+ self.use_system_libffi = True
+
diff --git a/build_files/build_environment/patches/sqlite.diff b/build_files/build_environment/patches/sqlite.diff
new file mode 100644
index 00000000000..80f1384f9cf
--- /dev/null
+++ b/build_files/build_environment/patches/sqlite.diff
@@ -0,0 +1,14 @@
+Only in external_sqlite_orig: config.log
+diff -ru external_sqlite_orig/config.sub external_sqlite/config.sub
+--- external_sqlite_orig/config.sub 2020-07-10 14:06:42.000000000 +0200
++++ external_sqlite/config.sub 2020-07-10 14:10:24.000000000 +0200
+@@ -314,6 +314,7 @@
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
++ | aarch64-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+Only in external_sqlite: mksourceid
+Only in external_sqlite: sqlite3session.h
diff --git a/build_files/build_environment/patches/theora.diff b/build_files/build_environment/patches/theora.diff
new file mode 100644
index 00000000000..3abadb66be9
--- /dev/null
+++ b/build_files/build_environment/patches/theora.diff
@@ -0,0 +1,18 @@
+--- config.sub
++++ config.sub
+@@ -226,6 +226,7 @@
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
++ | aarch64 \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+@@ -286,6 +287,7 @@
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
++ | aarch64-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff
index 6302f13796b..fe767829a70 100644
--- a/build_files/build_environment/patches/usd.diff
+++ b/build_files/build_environment/patches/usd.diff
@@ -1,14 +1,3 @@
-diff -x .git -ur usd.orig/cmake/defaults/Options.cmake external_usd/cmake/defaults/Options.cmake
---- usd.orig/cmake/defaults/Options.cmake 2019-10-24 22:39:53.000000000 +0200
-+++ external_usd/cmake/defaults/Options.cmake 2019-11-28 13:00:33.197957712 +0100
-@@ -25,6 +25,7 @@
- option(PXR_VALIDATE_GENERATED_CODE "Validate script generated code" OFF)
- option(PXR_HEADLESS_TEST_MODE "Disallow GUI based tests, useful for running under headless CI systems." OFF)
- option(PXR_BUILD_TESTS "Build tests" ON)
-+option(PXR_BUILD_USD_TOOLS "Build commandline tools" ON)
- option(PXR_BUILD_IMAGING "Build imaging components" ON)
- option(PXR_BUILD_EMBREE_PLUGIN "Build embree imaging plugin" OFF)
- option(PXR_BUILD_OPENIMAGEIO_PLUGIN "Build OpenImageIO plugin" OFF)
diff -x .git -ur usd.orig/cmake/defaults/Packages.cmake external_usd/cmake/defaults/Packages.cmake
--- usd.orig/cmake/defaults/Packages.cmake 2019-10-24 22:39:53.000000000 +0200
+++ external_usd/cmake/defaults/Packages.cmake 2019-11-28 13:00:33.185957483 +0100
@@ -21,11 +10,11 @@ diff -x .git -ur usd.orig/cmake/defaults/Packages.cmake external_usd/cmake/defau
add_definitions(${TBB_DEFINITIONS})
# --math
-diff -x .git -ur usd.orig/pxr/base/lib/plug/initConfig.cpp external_usd/pxr/base/lib/plug/initConfig.cpp
---- usd.orig/pxr/base/lib/plug/initConfig.cpp 2019-10-24 22:39:53.000000000 +0200
-+++ external_usd/pxr/base/lib/plug/initConfig.cpp 2019-12-11 11:00:37.643323127 +0100
-@@ -69,8 +69,38 @@
-
+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.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(...) */
@@ -50,7 +39,9 @@ diff -x .git -ur usd.orig/pxr/base/lib/plug/initConfig.cpp external_usd/pxr/base
+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);
@@ -60,14 +51,14 @@ diff -x .git -ur usd.orig/pxr/base/lib/plug/initConfig.cpp external_usd/pxr/base
+ 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());
@@ -75,10 +66,10 @@ diff -x .git -ur usd.orig/pxr/base/lib/plug/initConfig.cpp external_usd/pxr/base
+ 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. */
@@ -90,37 +81,6 @@ diff -x .git -ur usd.orig/pxr/base/lib/plug/initConfig.cpp external_usd/pxr/base
+ PXR_NS::usd_initialise_plugin_path(datafiles_usd_path);
+}
+}
-diff -x .git -ur usd.orig/pxr/usd/CMakeLists.txt external_usd/pxr/usd/CMakeLists.txt
---- usd.orig/pxr/usd/CMakeLists.txt 2019-10-24 22:39:53.000000000 +0200
-+++ external_usd/pxr/usd/CMakeLists.txt 2019-11-28 13:00:33.197957712 +0100
-@@ -1,6 +1,5 @@
- set(DIRS
- lib
-- bin
- plugin
- )
-
-@@ -8,3 +7,8 @@
- add_subdirectory(${d})
- endforeach()
-
-+if (PXR_BUILD_USD_TOOLS)
-+ add_subdirectory(bin)
-+else()
-+ message(STATUS "Skipping commandline tools because PXR_BUILD_USD_TOOLS=OFF")
-+endif()
-diff -Naur external_usd_orig/pxr/base/lib/tf/preprocessorUtils.h external_usd/pxr/base/lib/tf/preprocessorUtils.h
---- external_usd_orig/pxr/base/lib/tf/preprocessorUtils.h 2019-10-24 14:39:53 -0600
-+++ external_usd/pxr/base/lib/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
@@ -137,3 +97,36 @@ diff -Naur external_usd_base/cmake/macros/Public.cmake external_usd/cmake/macros
endforeach()
foreach(lib ${PXR_OBJECT_LIBS})
set(objects "${objects};\$<TARGET_OBJECTS:${lib}>")
+
+diff --git a/pxr/base/arch/align.h b/pxr/base/arch/align.h
+index f3cabf4..ebc8a69 100644
+--- a/pxr/base/arch/align.h
++++ b/pxr/base/arch/align.h
+@@ -77,7 +77,11 @@ ArchAlignMemory(void *base)
+ /// The size of a CPU cache line on the current processor architecture in bytes.
+ ///
+ /// \hideinitializer
++#if defined(ARCH_OS_DARWIN) && defined(ARCH_CPU_ARM)
++#define ARCH_CACHE_LINE_SIZE 128
++#else
+ #define ARCH_CACHE_LINE_SIZE 64
++#endif
+
+ ///@}
+
+diff --git a/pxr/base/arch/math.h b/pxr/base/arch/math.h
+index 3e66c37..64a052c 100644
+--- a/pxr/base/arch/math.h
++++ b/pxr/base/arch/math.h
+@@ -42,7 +42,7 @@ PXR_NAMESPACE_OPEN_SCOPE
+ /// \addtogroup group_arch_Math
+ ///@{
+
+-#if defined (ARCH_CPU_INTEL) || defined(doxygen)
++#if defined (ARCH_CPU_INTEL) || defined(ARCH_CPU_ARM) || defined(doxygen)
+
+ /// This is the smallest value e such that 1+e^2 == 1, using floats.
+ /// True for all IEEE754 chipsets.
+
+
+
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 2dc1526c5e8..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
@@ -51,7 +51,7 @@ class Builder:
elif name.startswith('linux'):
self.platform = 'linux'
if is_tool('scl'):
- self.command_prefix = ['scl', 'enable', 'devtoolset-6', '--']
+ self.command_prefix = ['scl', 'enable', 'devtoolset-9', '--']
else:
self.command_prefix = []
elif name.startswith('win'):
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/FindClangTidy.cmake b/build_files/cmake/Modules/FindClangTidy.cmake
new file mode 100644
index 00000000000..f556d05a0b9
--- /dev/null
+++ b/build_files/cmake/Modules/FindClangTidy.cmake
@@ -0,0 +1,104 @@
+# - Find clang-tidy executable
+#
+# Find the native clang-tidy executable
+#
+# This module defines
+# CLANG_TIDY_EXECUTABLE, the ful lpath to clang-tidy executable
+#
+# CLANG_TIDY_VERSION, the full version of the clang-tidy in the
+# major,minor.patch format
+#
+# CLANG_TIDY_VERSION_MAJOR,
+# CLANG_TIDY_VERSION_MINOR,
+# CLANG_TIDY_VERSION_PATCH, individual components of the clang-tidy version.
+#
+# CLANG_TIDY_FOUND, If false, do not try to use Eigen3.
+
+#=============================================================================
+# Copyright 2020 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If CLANG_TIDY_ROOT_DIR was defined in the environment, use it.
+if(NOT CLANG_TIDY_ROOT_DIR AND NOT $ENV{CLANG_TIDY_ROOT_DIR} STREQUAL "")
+ set(CLANG_TIDY_ROOT_DIR $ENV{CLANG_TIDY_ROOT_DIR})
+endif()
+
+set(_clang_tidy_SEARCH_DIRS
+ ${CLANG_TIDY_ROOT_DIR}
+ /usr/local/bin
+)
+
+# TODO(sergey): Find more reliable way of finding the latest clang-tidy.
+find_program(CLANG_TIDY_EXECUTABLE
+ NAMES
+ clang-tidy-10
+ clang-tidy-9
+ clang-tidy-8
+ clang-tidy-7
+ clang-tidy
+ HINTS
+ ${_clang_tidy_SEARCH_DIRS}
+)
+
+if(CLANG_TIDY_EXECUTABLE)
+ # Mark clang-tidy as found.
+ set(CLANG_TIDY_FOUND TRUE)
+
+ # Setup fallback values.
+ set(CLANG_TIDY_VERSION_MAJOR 0)
+ set(CLANG_TIDY_VERSION_MINOR 0)
+ set(CLANG_TIDY_VERSION_PATCH 0)
+
+ # Get version from the output.
+ #
+ # NOTE: Don't use name of the executable file since that only includes a
+ # major version. Also, even the major version might be missing in the
+ # executable name.
+ execute_process(COMMAND ${CLANG_TIDY_EXECUTABLE} -version
+ OUTPUT_VARIABLE CLANG_TIDY_VERSION_RAW
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ # Parse parts.
+ if(CLANG_TIDY_VERSION_RAW MATCHES "LLVM version .*")
+ # Strip the LLVM prefix and get list of individual version components.
+ string(REGEX REPLACE
+ ".*LLVM version ([.0-9]+).*" "\\1"
+ CLANG_SEMANTIC_VERSION "${CLANG_TIDY_VERSION_RAW}")
+ string(REPLACE "." ";" CLANG_VERSION_PARTS "${CLANG_SEMANTIC_VERSION}")
+ list(LENGTH CLANG_VERSION_PARTS NUM_CLANG_TIDY_VERSION_PARTS)
+
+ # Extract components into corresponding variables.
+ if(NUM_CLANG_TIDY_VERSION_PARTS GREATER 0)
+ list(GET CLANG_VERSION_PARTS 0 CLANG_TIDY_VERSION_MAJOR)
+ endif()
+ if(NUM_CLANG_TIDY_VERSION_PARTS GREATER 1)
+ list(GET CLANG_VERSION_PARTS 1 CLANG_TIDY_VERSION_MINOR)
+ endif()
+ if(NUM_CLANG_TIDY_VERSION_PARTS GREATER 2)
+ list(GET CLANG_VERSION_PARTS 2 CLANG_TIDY_VERSION_PATCH)
+ endif()
+
+ # Unset temp variables.
+ unset(NUM_CLANG_TIDY_VERSION_PARTS)
+ unset(CLANG_SEMANTIC_VERSION)
+ unset(CLANG_VERSION_PARTS)
+ endif()
+
+ # Construct full semantic version.
+ set(CLANG_TIDY_VERSION "${CLANG_TIDY_VERSION_MAJOR}.\
+${CLANG_TIDY_VERSION_MINOR}.\
+${CLANG_TIDY_VERSION_PATCH}")
+ unset(CLANG_TIDY_VERSION_RAW)
+
+ message(STATUS "Found clang-tidy ${CLANG_TIDY_EXECUTABLE} (${CLANG_TIDY_VERSION})")
+else()
+ set(CLANG_TIDY_FOUND FALSE)
+endif()
diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake
index d9d525d4586..fa613f62308 100644
--- a/build_files/cmake/Modules/FindEmbree.cmake
+++ b/build_files/cmake/Modules/FindEmbree.cmake
@@ -82,7 +82,7 @@ FIND_LIBRARY(EMBREE_LIBRARY
# handle the QUIETLY and REQUIRED arguments and set EMBREE_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(EMBREE DEFAULT_MSG
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Embree DEFAULT_MSG
_embree_LIBRARIES EMBREE_INCLUDE_DIR)
IF(EMBREE_FOUND)
diff --git a/build_files/cmake/Modules/FindHDF5.cmake b/build_files/cmake/Modules/FindHDF5.cmake
deleted file mode 100644
index ea77c5d7826..00000000000
--- a/build_files/cmake/Modules/FindHDF5.cmake
+++ /dev/null
@@ -1,65 +0,0 @@
-# - Find HDF5 library
-# Find the native HDF5 includes and libraries
-# This module defines
-# HDF5_INCLUDE_DIRS, where to find hdf5.h, Set when HDF5_INCLUDE_DIR is found.
-# HDF5_LIBRARIES, libraries to link against to use HDF5.
-# HDF5_ROOT_DIR, The base directory to search for HDF5.
-# This can also be an environment variable.
-# HDF5_FOUND, If false, do not try to use HDF5.
-#
-
-#=============================================================================
-# Copyright 2016 Blender Foundation.
-#
-# Distributed under the OSI-approved BSD License (the "License");
-# see accompanying file Copyright.txt for details.
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-
-# If HDF5_ROOT_DIR was defined in the environment, use it.
-IF(NOT HDF5_ROOT_DIR AND NOT $ENV{HDF5_ROOT_DIR} STREQUAL "")
- SET(HDF5_ROOT_DIR $ENV{HDF5_ROOT_DIR})
-ENDIF()
-
-SET(_hdf5_SEARCH_DIRS
- ${HDF5_ROOT_DIR}
- /opt/lib/hdf5
-)
-
-FIND_LIBRARY(HDF5_LIBRARY
- NAMES
- hdf5
- HINTS
- ${_hdf5_SEARCH_DIRS}
- PATH_SUFFIXES
- lib64 lib
-)
-
-FIND_PATH(HDF5_INCLUDE_DIR
- NAMES
- hdf5.h
- HINTS
- ${_hdf5_SEARCH_DIRS}
- PATH_SUFFIXES
- include
-)
-
-# handle the QUIETLY and REQUIRED arguments and set HDF5_FOUND to TRUE if
-# all listed variables are TRUE
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(HDF5 DEFAULT_MSG HDF5_LIBRARY HDF5_INCLUDE_DIR)
-
-IF(HDF5_FOUND)
- SET(HDF5_LIBRARIES ${HDF5_LIBRARY})
- SET(HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIR})
-ENDIF(HDF5_FOUND)
-
-MARK_AS_ADVANCED(
- HDF5_INCLUDE_DIR
- HDF5_LIBRARY
-)
-
-UNSET(_hdf5_SEARCH_DIRS)
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/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake
index c36b264a300..ea9a1edeb43 100644
--- a/build_files/cmake/Modules/GTestTesting.cmake
+++ b/build_files/cmake/Modules/GTestTesting.cmake
@@ -37,6 +37,11 @@ macro(BLENDER_SRC_GTEST_EX)
if(WIN32)
set(MANIFEST "${CMAKE_BINARY_DIR}/tests.exe.manifest")
endif()
+
+ add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
+ add_definitions(${GFLAGS_DEFINES})
+ add_definitions(${GLOG_DEFINES})
+
add_executable(${TARGET_NAME} ${ARG_SRC} ${MANIFEST})
target_include_directories(${TARGET_NAME} PUBLIC "${TEST_INC}")
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC "${TEST_INC_SYS}")
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..e6fc73a75ed 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)
@@ -52,7 +53,7 @@ set(WITH_USD ON CACHE BOOL "" FORCE)
set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE)
-set(CYCLES_CUDA_BINARIES_ARCH sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm_75 CACHE STRING "" FORCE)
+set(CYCLES_CUDA_BINARIES_ARCH sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm_75;compute_75 CACHE STRING "" FORCE)
set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE)
# platform dependent options
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index a906dbc0bf4..af9fb92e3ad 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -169,6 +169,26 @@ function(blender_include_dirs_sys
include_directories(SYSTEM ${_ALL_INCS})
endfunction()
+# Set include paths for header files included with "*.h" syntax.
+# This enables auto-complete suggestions for user header files on Xcode.
+# Build process is not affected since the include paths are the same
+# as in HEADER_SEARCH_PATHS.
+function(blender_user_header_search_paths
+ name
+ includes
+ )
+
+ if(XCODE)
+ set(_ALL_INCS "")
+ foreach(_INC ${includes})
+ get_filename_component(_ABS_INC ${_INC} ABSOLUTE)
+ # _ALL_INCS is a space-separated string of file paths in quotes.
+ set(_ALL_INCS "${_ALL_INCS} \"${_ABS_INC}\"")
+ endforeach()
+ set_target_properties(${name} PROPERTIES XCODE_ATTRIBUTE_USER_HEADER_SEARCH_PATHS "${_ALL_INCS}")
+ endif()
+endfunction()
+
function(blender_source_group
name
sources
@@ -317,6 +337,7 @@ function(blender_add_lib__impl
# works fine without having the includes
# listed is helpful for IDE's (QtCreator/MSVC)
blender_source_group("${name}" "${sources}")
+ blender_user_header_search_paths("${name}" "${includes}")
list_assert_duplicates("${sources}")
list_assert_duplicates("${includes}")
@@ -354,6 +375,42 @@ function(blender_add_lib
set_property(GLOBAL APPEND PROPERTY BLENDER_LINK_LIBS ${name})
endfunction()
+# blender_add_test_lib() is used to define a test library. It is intended to be
+# called in tandem with blender_add_lib(). The test library will be linked into
+# the bf_gtest_runner_test executable (see tests/gtests/CMakeLists.txt).
+function(blender_add_test_lib
+ name
+ sources
+ includes
+ includes_sys
+ library_deps
+ )
+
+ add_cc_flags_custom_test(${name} PARENT_SCOPE)
+
+ # Otherwise external projects will produce warnings that we cannot fix.
+ remove_strict_flags()
+
+ # This duplicates logic that's also in GTestTesting.cmake, macro BLENDER_SRC_GTEST_EX.
+ # TODO(Sybren): deduplicate after the general approach in D7649 has been approved.
+ LIST(APPEND includes
+ ${CMAKE_SOURCE_DIR}/tests/gtests
+ )
+ LIST(APPEND includes_sys
+ ${GLOG_INCLUDE_DIRS}
+ ${GFLAGS_INCLUDE_DIRS}
+ ${CMAKE_SOURCE_DIR}/extern/gtest/include
+ ${CMAKE_SOURCE_DIR}/extern/gmock/include
+ )
+ add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
+ add_definitions(${GFLAGS_DEFINES})
+ add_definitions(${GLOG_DEFINES})
+
+ blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}" "${library_deps}")
+
+ set_property(GLOBAL APPEND PROPERTY BLENDER_TEST_LIBS ${name})
+endfunction()
+
# Ninja only: assign 'heavy pool' to some targets that are especially RAM-consuming to build.
function(setup_heavy_lib_pool)
if(WITH_NINJA_POOL_JOBS AND NINJA_MAX_NUM_PARALLEL_COMPILE_HEAVY_JOBS)
@@ -437,7 +494,6 @@ function(SETUP_LIBDIRS)
if(WITH_ALEMBIC)
link_directories(${ALEMBIC_LIBPATH})
- link_directories(${HDF5_LIBPATH})
endif()
if(WITH_GHOST_WAYLAND)
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index d8ee82d4c10..a80b0b56901 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -20,7 +20,11 @@
# Libraries configuration for Apple.
-set(MACOSX_DEPLOYMENT_TARGET "10.11")
+if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(MACOSX_DEPLOYMENT_TARGET 11.00)
+else()
+ set(MACOSX_DEPLOYMENT_TARGET 10.13)
+endif()
macro(find_package_wrapper)
# do nothing, just satisfy the macro
@@ -378,6 +382,12 @@ if(WITH_CYCLES_OSL)
endif()
endif()
+if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(WITH_CYCLES_EMBREE OFF)
+ set(WITH_OPENIMAGEDENOISE OFF)
+ set(WITH_CPU_SSE OFF)
+endif()
+
if(WITH_CYCLES_EMBREE)
find_package(Embree 3.8.0 REQUIRED)
set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -Xlinker -stack_size -Xlinker 0x100000")
@@ -439,8 +449,8 @@ if(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" OR CMAKE_OSX_ARCHITECTURES MATCHES "
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ftree-vectorize -fvariable-expansion-in-unroller")
endif()
else()
- set(CMAKE_C_FLAGS_RELEASE "-mdynamic-no-pic -fno-strict-aliasing")
- set(CMAKE_CXX_FLAGS_RELEASE "-mdynamic-no-pic -fno-strict-aliasing")
+ set(CMAKE_C_FLAGS_RELEASE "-O2 -mdynamic-no-pic -fno-strict-aliasing")
+ set(CMAKE_CXX_FLAGS_RELEASE "-O2 -mdynamic-no-pic -fno-strict-aliasing")
endif()
if(${XCODE_VERSION} VERSION_EQUAL 5 OR ${XCODE_VERSION} VERSION_GREATER 5)
diff --git a/build_files/cmake/platform/platform_apple_xcode.cmake b/build_files/cmake/platform/platform_apple_xcode.cmake
index f1f02c151ee..3a43ca317dd 100644
--- a/build_files/cmake/platform/platform_apple_xcode.cmake
+++ b/build_files/cmake/platform/platform_apple_xcode.cmake
@@ -21,8 +21,10 @@
# Xcode and system configuration for Apple.
if(NOT CMAKE_OSX_ARCHITECTURES)
- set(CMAKE_OSX_ARCHITECTURES x86_64 CACHE STRING
- "Choose the architecture you want to build Blender for: i386, x86_64 or ppc"
+ execute_process(COMMAND uname -m OUTPUT_VARIABLE ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
+ message(STATUS "Detected native architecture ${ARCHITECTURE}.")
+ set(CMAKE_OSX_ARCHITECTURES ${ARCHITECTURE} CACHE STRING
+ "Choose the architecture you want to build Blender for: arm64 or x86_64"
FORCE)
endif()
@@ -65,13 +67,9 @@ endif()
message(STATUS "Detected OS X ${OSX_SYSTEM} and Xcode ${XCODE_VERSION} at ${XCODE_BUNDLE}")
-# Older Xcode versions had different approach to the directory hiearchy.
-# Require newer Xcode which is also have better chances of being able to compile with the
-# required deployment target.
-#
-# NOTE: Xcode version 8.2 is the latest one which runs on macOS 10.11.
-if(${XCODE_VERSION} VERSION_LESS 8.2)
- message(FATAL_ERROR "Only Xcode version 8.2 and newer is supported")
+# Require a relatively recent Xcode version.
+if(${XCODE_VERSION} VERSION_LESS 10.0)
+ message(FATAL_ERROR "Only Xcode version 10.0 and newer is supported")
endif()
# note: xcode-select path could be ambiguous,
@@ -133,14 +131,21 @@ if(${CMAKE_GENERATOR} MATCHES "Xcode")
endif()
unset(OSX_SDKROOT)
-# 10.11 is our min. target, if you use higher sdk, weak linking happens
+
+# 10.13 is our min. target, if you use higher sdk, weak linking happens
+if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(OSX_MIN_DEPLOYMENT_TARGET 11.00)
+else()
+ set(OSX_MIN_DEPLOYMENT_TARGET 10.13)
+endif()
+
if(CMAKE_OSX_DEPLOYMENT_TARGET)
- if(${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_LESS 10.11)
- message(STATUS "Setting deployment target to 10.11, lower versions are not supported")
- set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "" FORCE)
+ if(${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_LESS ${OSX_MIN_DEPLOYMENT_TARGET})
+ message(STATUS "Setting deployment target to ${OSX_MIN_DEPLOYMENT_TARGET}, lower versions are not supported")
+ set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
endif()
else()
- set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "" FORCE)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
endif()
if(NOT ${CMAKE_GENERATOR} MATCHES "Xcode")
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 6e00a2dec7b..c5e8893424b 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -36,6 +36,11 @@ if(NOT DEFINED LIBDIR)
elseif(EXISTS ${LIBDIR_CENTOS7_ABI})
set(LIBDIR ${LIBDIR_CENTOS7_ABI})
set(WITH_CXX11_ABI OFF)
+
+ if(CMAKE_COMPILER_IS_GNUCC AND
+ CMAKE_C_COMPILER_VERSION VERSION_LESS 9.3)
+ message(FATAL_ERROR "GCC version must be at least 9.3 for precompiled libraries, found ${CMAKE_C_COMPILER_VERSION}")
+ endif()
endif()
# Avoid namespace pollustion.
@@ -265,14 +270,8 @@ endif()
if(WITH_ALEMBIC)
find_package_wrapper(Alembic)
- if(WITH_ALEMBIC_HDF5)
- set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
- find_package_wrapper(HDF5)
- endif()
-
- if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+ if(NOT ALEMBIC_FOUND)
set(WITH_ALEMBIC OFF)
- set(WITH_ALEMBIC_HDF5 OFF)
endif()
endif()
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index db939b1d00c..ebafe92c962 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -26,6 +26,10 @@ if(NOT MSVC)
message(FATAL_ERROR "Compiler is unsupported")
endif()
+if(WITH_GTESTS AND ${CMAKE_VERSION} VERSION_LESS "3.18.0")
+ message(FATAL_ERROR "CMake 3.18.0 is required for building WITH_GTESTS on windows, currently installed cmake version is ${CMAKE_VERSION}")
+endif()
+
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(MSVC_CLANG On)
set(VC_TOOLS_DIR $ENV{VCToolsRedistDir} CACHE STRING "Location of the msvc redistributables")
@@ -539,10 +543,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/build_files/utils/make_test.py b/build_files/utils/make_test.py
index 309ab36ecdd..15bd6dde352 100755
--- a/build_files/utils/make_test.py
+++ b/build_files/utils/make_test.py
@@ -40,7 +40,8 @@ if make_utils.command_missing(git_command):
# Test if we are building a specific release version.
branch = make_utils.git_branch(git_command)
-release_version = make_utils.git_branch_release_version(branch)
+tag = make_utils.git_tag(git_command)
+release_version = make_utils.git_branch_release_version(branch, tag)
lib_tests_dirpath = os.path.join('..', 'lib', "tests")
if not os.path.exists(lib_tests_dirpath):
diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py
index b6157107c23..324bb6944bf 100755
--- a/build_files/utils/make_update.py
+++ b/build_files/utils/make_update.py
@@ -197,7 +197,8 @@ if __name__ == "__main__":
# Test if we are building a specific release version.
branch = make_utils.git_branch(args.git_command)
- release_version = make_utils.git_branch_release_version(branch)
+ tag = make_utils.git_tag(args.git_command)
+ release_version = make_utils.git_branch_release_version(branch, tag)
if not args.no_libraries:
svn_update(args, release_version)
diff --git a/build_files/utils/make_utils.py b/build_files/utils/make_utils.py
index 7b21bc607a4..e94c8e3550a 100755
--- a/build_files/utils/make_utils.py
+++ b/build_files/utils/make_utils.py
@@ -36,7 +36,7 @@ def check_output(cmd, exit_on_error=True):
return output.strip()
def git_branch(git_command):
- # Test if we are building a specific release version.
+ # Get current branch name.
try:
branch = subprocess.check_output([git_command, "rev-parse", "--abbrev-ref", "HEAD"])
except subprocess.CalledProcessError as e:
@@ -45,10 +45,23 @@ def git_branch(git_command):
return branch.strip().decode('utf8')
-def git_branch_release_version(branch):
+def git_tag(git_command):
+ # Get current tag name.
+ try:
+ tag = subprocess.check_output([git_command, "describe", "--exact-match"], stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ return None
+
+ return tag.strip().decode('utf8')
+
+def git_branch_release_version(branch, tag):
release_version = re.search("^blender-v(.*)-release$", branch)
if release_version:
release_version = release_version.group(1)
+ elif tag:
+ release_version = re.search("^v([0-9]*\.[0-9]*).*", tag)
+ if release_version:
+ release_version = release_version.group(1)
return release_version
def svn_libraries_base_url(release_version):
diff --git a/cmake-build-debug/CMakeCache.txt b/cmake-build-debug/CMakeCache.txt
new file mode 100644
index 00000000000..c26299d42e8
--- /dev/null
+++ b/cmake-build-debug/CMakeCache.txt
@@ -0,0 +1,823 @@
+# This is the CMakeCache file.
+# For build in directory: e:/code/blender-git/blender/cmake-build-debug
+# It was generated by CMake: C:/Program Files/JetBrains/CLion 2020.1.1/bin/cmake/win/bin/cmake.exe
+# You can edit this file to change values found and used by cmake.
+# If you do not want to change any of the values, simply exit the editor.
+# If you do want to change a value, simply edit, save, and exit the editor.
+# The syntax for the file is as follows:
+# KEY:TYPE=VALUE
+# KEY is the name of a variable in the cache.
+# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
+# VALUE is the current value for the KEY.
+
+########################
+# EXTERNAL cache entries
+########################
+
+//Use instead of the current date for reproducible builds (empty
+// string disables this option)
+BUILDINFO_OVERRIDE_DATE:STRING=
+
+//Use instead of the current time for reproducible builds (empty
+// string disables this option)
+BUILDINFO_OVERRIDE_TIME:STRING=
+
+//Value Computed by CMake
+Blender_BINARY_DIR:STATIC=E:/code/blender-git/blender/cmake-build-debug
+
+//Value Computed by CMake
+Blender_SOURCE_DIR:STATIC=E:/code/blender-git/blender
+
+//Choose the type of build, options are: None Debug Release RelWithDebInfo
+// MinSizeRel ...
+CMAKE_BUILD_TYPE:STRING=Debug
+
+//Id string of the compiler for the CodeBlocks IDE. Automatically
+// detected when left empty
+CMAKE_CODEBLOCKS_COMPILER_ID:STRING=
+
+//The CodeBlocks executable
+CMAKE_CODEBLOCKS_EXECUTABLE:FILEPATH=CMAKE_CODEBLOCKS_EXECUTABLE-NOTFOUND
+
+//Additional command line arguments when CodeBlocks invokes make.
+// Enter e.g. -j<some_number> to get parallel builds
+CMAKE_CODEBLOCKS_MAKE_ARGUMENTS:STRING=
+
+//Enable/Disable color output during build.
+CMAKE_COLOR_MAKEFILE:BOOL=ON
+
+//CXX compiler
+CMAKE_CXX_COMPILER:FILEPATH=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe
+
+//Flags used by the CXX compiler during all build types.
+CMAKE_CXX_FLAGS:STRING=/DWIN32 /D_WINDOWS /W3 /GR /EHsc
+
+//Flags used by the CXX compiler during DEBUG builds.
+CMAKE_CXX_FLAGS_DEBUG:STRING=/MDd /Zi /Ob0 /Od /RTC1
+
+//Flags used by the CXX compiler during MINSIZEREL builds.
+CMAKE_CXX_FLAGS_MINSIZEREL:STRING=/MD /O1 /Ob1 /DNDEBUG
+
+CMAKE_CXX_FLAGS_RELEASE:STRING=/MD /O2 /Ob2 /DNDEBUG
+
+//Flags used by the CXX compiler during RELWITHDEBINFO builds.
+CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=/MD /Zi /O2 /Ob1 /DNDEBUG
+
+//Libraries linked by default with all C++ applications.
+CMAKE_CXX_STANDARD_LIBRARIES:STRING=kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
+
+//C compiler
+CMAKE_C_COMPILER:FILEPATH=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe
+
+//Flags used by the C compiler during all build types.
+CMAKE_C_FLAGS:STRING=/DWIN32 /D_WINDOWS /W3
+
+//Flags used by the C compiler during DEBUG builds.
+CMAKE_C_FLAGS_DEBUG:STRING=/MDd /Zi /Ob0 /Od /RTC1
+
+//Flags used by the C compiler during MINSIZEREL builds.
+CMAKE_C_FLAGS_MINSIZEREL:STRING=/MD /O1 /Ob1 /DNDEBUG
+
+CMAKE_C_FLAGS_RELEASE:STRING=/MD /O2 /Ob2 /DNDEBUG
+
+//Flags used by the C compiler during RELWITHDEBINFO builds.
+CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=/MD /Zi /O2 /Ob1 /DNDEBUG
+
+//Libraries linked by default with all C applications.
+CMAKE_C_STANDARD_LIBRARIES:STRING=kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
+
+//Flags used by the linker during all build types.
+CMAKE_EXE_LINKER_FLAGS:STRING=/machine:X86
+
+//Flags used by the linker during DEBUG builds.
+CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=/debug /INCREMENTAL
+
+//Flags used by the linker during MINSIZEREL builds.
+CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=/INCREMENTAL:NO
+
+//Flags used by the linker during RELEASE builds.
+CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=/INCREMENTAL:NO
+
+//Flags used by the linker during RELWITHDEBINFO builds.
+CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=/debug /INCREMENTAL
+
+//default install path
+CMAKE_INSTALL_PREFIX:PATH=E:/code/blender-git/blender/cmake-build-debug/bin/${BUILD_TYPE}
+
+//Path to a program.
+CMAKE_LINKER:FILEPATH=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/link.exe
+
+//Program used to build from makefiles.
+CMAKE_MAKE_PROGRAM:STRING=nmake
+
+//Flags used by the linker during the creation of modules during
+// all build types.
+CMAKE_MODULE_LINKER_FLAGS:STRING=/machine:X86
+
+//Flags used by the linker during the creation of modules during
+// DEBUG builds.
+CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=/debug /INCREMENTAL
+
+//Flags used by the linker during the creation of modules during
+// MINSIZEREL builds.
+CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=/INCREMENTAL:NO
+
+//Flags used by the linker during the creation of modules during
+// RELEASE builds.
+CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=/INCREMENTAL:NO
+
+//Flags used by the linker during the creation of modules during
+// RELWITHDEBINFO builds.
+CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=/debug /INCREMENTAL
+
+//Path to a program.
+CMAKE_MT:FILEPATH=C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x86/mt.exe
+
+//Value Computed by CMake
+CMAKE_PROJECT_DESCRIPTION:STATIC=
+
+//Value Computed by CMake
+CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
+
+//Value Computed by CMake
+CMAKE_PROJECT_NAME:STATIC=Blender
+
+//RC compiler
+CMAKE_RC_COMPILER:FILEPATH=C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x86/rc.exe
+
+//Flags for Windows Resource Compiler during all build types.
+CMAKE_RC_FLAGS:STRING=-DWIN32
+
+//Flags for Windows Resource Compiler during DEBUG builds.
+CMAKE_RC_FLAGS_DEBUG:STRING=-D_DEBUG
+
+//Flags for Windows Resource Compiler during MINSIZEREL builds.
+CMAKE_RC_FLAGS_MINSIZEREL:STRING=
+
+//Flags for Windows Resource Compiler during RELEASE builds.
+CMAKE_RC_FLAGS_RELEASE:STRING=
+
+//Flags for Windows Resource Compiler during RELWITHDEBINFO builds.
+CMAKE_RC_FLAGS_RELWITHDEBINFO:STRING=
+
+//Flags used by the linker during the creation of shared libraries
+// during all build types.
+CMAKE_SHARED_LINKER_FLAGS:STRING=/machine:X86
+
+//Flags used by the linker during the creation of shared libraries
+// during DEBUG builds.
+CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=/debug /INCREMENTAL
+
+//Flags used by the linker during the creation of shared libraries
+// during MINSIZEREL builds.
+CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=/INCREMENTAL:NO
+
+//Flags used by the linker during the creation of shared libraries
+// during RELEASE builds.
+CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=/INCREMENTAL:NO
+
+//Flags used by the linker during the creation of shared libraries
+// during RELWITHDEBINFO builds.
+CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=/debug /INCREMENTAL
+
+//If set, runtime paths are not added when installing shared libraries,
+// but are added when building.
+CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
+
+//If set, runtime paths are not added when using shared libraries.
+CMAKE_SKIP_RPATH:BOOL=NO
+
+//Flags used by the linker during the creation of static libraries
+// during all build types.
+CMAKE_STATIC_LINKER_FLAGS:STRING=/machine:X86
+
+//Flags used by the linker during the creation of static libraries
+// during DEBUG builds.
+CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
+
+//Flags used by the linker during the creation of static libraries
+// during MINSIZEREL builds.
+CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
+
+//Flags used by the linker during the creation of static libraries
+// during RELEASE builds.
+CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
+
+//Flags used by the linker during the creation of static libraries
+// during RELWITHDEBINFO builds.
+CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
+
+//If this value is on, makefiles will be generated without the
+// .SILENT directive, and all commands will be echoed to the console
+// during the make. This is useful for debugging only. With Visual
+// Studio IDE projects all commands are done without /nologo.
+CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
+
+//Use instead of the standard packagename (empty string disables
+// this option)
+CPACK_OVERRIDE_PACKAGENAME:STRING=
+
+//CUDA architectures to build binaries for
+CYCLES_CUDA_BINARIES_ARCH:STRING=sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm_75;compute_75
+
+//Git command line client
+GIT_EXECUTABLE:FILEPATH=C:/Program Files/Git/cmd/git.exe
+
+//Link with LLVM static libraries
+LLVM_STATIC:BOOL=OFF
+
+//Path to a file.
+MSVC_REDIST_DIR:PATH=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Redist/MSVC/14.25.28508
+
+//Run given CMake script after installation process
+POSTINSTALL_SCRIPT:BOOL=OFF
+
+//Python executable to run unit tests
+TEST_PYTHON_EXE:PATH=
+
+//Path to a file.
+WINDOWS_KITS_DIR:PATH=C:/Program Files (x86)/Windows Kits/10
+
+//Include the files needed for debugging python scripts with visual
+// studio 2017+.
+WINDOWS_PYTHON_DEBUG:BOOL=OFF
+
+//Organize the visual studio projects according to source folder
+// structure.
+WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS:BOOL=ON
+
+//Organize the source files in filters matching the source folders.
+WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS:BOOL=ON
+
+//Enable Alembic Support
+WITH_ALEMBIC:BOOL=ON
+
+//Call abort() when raising an assertion through BLI_assert()
+WITH_ASSERT_ABORT:BOOL=ON
+
+//Build with blenders audio library (only disable if you know what
+// you're doing!)
+WITH_AUDASPACE:BOOL=ON
+
+//Build blender (disable to build only the blender player)
+WITH_BLENDER:BOOL=ON
+
+//Enable features depending on boost
+WITH_BOOST:BOOL=ON
+
+//Include extra build details (only disable for development & faster
+// builds)
+WITH_BUILDINFO:BOOL=ON
+
+//Enable Bullet (Physics Engine)
+WITH_BULLET:BOOL=ON
+
+//Enable Blenders own AVI file support (raw/jpeg)
+WITH_CODEC_AVI:BOOL=ON
+
+//Enable FFMPeg Support (http://ffmpeg.org)
+WITH_CODEC_FFMPEG:BOOL=ON
+
+//Enable libsndfile Support (http://www.mega-nerd.com/libsndfile)
+WITH_CODEC_SNDFILE:BOOL=ON
+
+//Enable the tile based nodal compositor
+WITH_COMPOSITOR:BOOL=ON
+
+//Enable SIMD instruction if they're detected on the host machine
+WITH_CPU_SSE:BOOL=ON
+
+//Dynamically load CUDA libraries at runtime
+WITH_CUDA_DYNLOAD:BOOL=ON
+
+//Enable GuardedAlloc for C++ memory allocation tracking (only
+// enable for development)
+WITH_CXX_GUARDEDALLOC:BOOL=OFF
+
+//Enable Cycles Render Engine
+WITH_CYCLES:BOOL=ON
+
+//Build cubins with nvrtc based compiler instead of nvcc
+WITH_CYCLES_CUBIN_COMPILER:BOOL=OFF
+
+//Build Cycles CUDA binaries
+WITH_CYCLES_CUDA_BINARIES:BOOL=OFF
+
+//Build cubins one after another (useful on machines with limited
+// RAM)
+WITH_CYCLES_CUDA_BUILD_SERIAL:BOOL=OFF
+
+//Build Cycles with extra debug capabilities
+WITH_CYCLES_DEBUG:BOOL=OFF
+
+//Enable Cycles CUDA compute support
+WITH_CYCLES_DEVICE_CUDA:BOOL=ON
+
+//Enable Cycles OpenCL compute support
+WITH_CYCLES_DEVICE_OPENCL:BOOL=ON
+
+//Enable Cycles OptiX support
+WITH_CYCLES_DEVICE_OPTIX:BOOL=OFF
+
+//Build Cycles with Embree support
+WITH_CYCLES_EMBREE:BOOL=ON
+
+//Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN
+// is on, even if it's very slow
+WITH_CYCLES_KERNEL_ASAN:BOOL=OFF
+
+//Build Cycles with logging support
+WITH_CYCLES_LOGGING:BOOL=ON
+
+//Build Cycles with native kernel only (which fits current CPU,
+// use for development only)
+WITH_CYCLES_NATIVE_ONLY:BOOL=OFF
+
+//Enable Cycles compute over network support (EXPERIMENTAL and
+// unfinished)
+WITH_CYCLES_NETWORK:BOOL=OFF
+
+//Build Cycles with OSL support
+WITH_CYCLES_OSL:BOOL=ON
+
+//Build Cycles standalone application
+WITH_CYCLES_STANDALONE:BOOL=OFF
+
+//Build Cycles standalone with GUI
+WITH_CYCLES_STANDALONE_GUI:BOOL=OFF
+
+//Enable Draco mesh compression Python module (used for glTF)
+WITH_DRACO:BOOL=ON
+
+//Enable FFTW3 support (Used for smoke, ocean sim, and audio effects)
+WITH_FFTW3:BOOL=ON
+
+//Enable Freestyle (advanced edges rendering)
+WITH_FREESTYLE:BOOL=ON
+
+//Enable debugging output for the GHOST library
+WITH_GHOST_DEBUG:BOOL=OFF
+
+//Enable building Blender against SDL for windowing rather than
+// the native APIs
+WITH_GHOST_SDL:BOOL=OFF
+
+//Switches to experimental copy of GLEW that has support for OpenGL
+// ES. (temporary option for development purposes)
+WITH_GLEW_ES:BOOL=OFF
+
+//Link with the ANGLE library, an OpenGL ES 2.0 implementation
+// based on Direct3D, instead of the system OpenGL library.
+WITH_GL_ANGLE:BOOL=OFF
+
+//Use the EGL OpenGL system library instead of the platform specific
+// OpenGL system library (CGL, glX, or WGL)
+WITH_GL_EGL:BOOL=OFF
+
+//Support using OpenGL ES 2.0. (through either EGL or the AGL/WGL/XGL
+// 'es20' profile)
+WITH_GL_PROFILE_ES20:BOOL=OFF
+
+//Enable GTest unit testing
+WITH_GTESTS:BOOL=OFF
+
+//Build without graphical support (renderfarm, server mode only)
+WITH_HEADLESS:BOOL=OFF
+
+//Enable ITASC IK solver (only disable for development & for incompatible
+// C++ compilers)
+WITH_IK_ITASC:BOOL=ON
+
+//Enable Legacy IK solver (only disable for development)
+WITH_IK_SOLVER:BOOL=ON
+
+//Enable CINEON and DPX Image Support
+WITH_IMAGE_CINEON:BOOL=ON
+
+//Enable DDS Image Support
+WITH_IMAGE_DDS:BOOL=ON
+
+//Enable HDR Image Support
+WITH_IMAGE_HDR:BOOL=ON
+
+//Enable OpenEXR Support (http://www.openexr.com)
+WITH_IMAGE_OPENEXR:BOOL=ON
+
+//Enable OpenJpeg Support (http://www.openjpeg.org)
+WITH_IMAGE_OPENJPEG:BOOL=ON
+
+//Enable LibTIFF Support
+WITH_IMAGE_TIFF:BOOL=ON
+
+//Enable Input Method Editor (IME) for complex Asian character
+// input
+WITH_INPUT_IME:BOOL=ON
+
+//Enable NDOF input devices (SpaceNavigator and friends)
+WITH_INPUT_NDOF:BOOL=ON
+
+//Enable I18N (International fonts and text)
+WITH_INTERNATIONAL:BOOL=ON
+
+//Enable Libmv structure from motion library
+WITH_LIBMV:BOOL=ON
+
+//Enable fixed-size schur specializations.
+WITH_LIBMV_SCHUR_SPECIALIZATIONS:BOOL=ON
+
+WITH_LLVM:BOOL=ON
+
+//Enable best LZMA compression, (used for pointcache)
+WITH_LZMA:BOOL=ON
+
+//Enable fast LZO compression (used for pointcache)
+WITH_LZO:BOOL=ON
+
+//Enable malloc replacement (http://www.canonware.com/jemalloc)
+WITH_MEM_JEMALLOC:BOOL=ON
+
+//Enable extended valgrind support for better reporting
+WITH_MEM_VALGRIND:BOOL=OFF
+
+//Enable Mantaflow Fluid Simulation Framework
+WITH_MOD_FLUID:BOOL=ON
+
+//Enable Ocean Modifier
+WITH_MOD_OCEANSIM:BOOL=ON
+
+//Enable Remesh Modifier
+WITH_MOD_REMESH:BOOL=ON
+
+//Enable OpenAL Support (http://www.openal.org)
+WITH_OPENAL:BOOL=ON
+
+//Enable OpenCollada Support (http://www.opencollada.org)
+WITH_OPENCOLLADA:BOOL=ON
+
+//Enable OpenColorIO color management
+WITH_OPENCOLORIO:BOOL=ON
+
+//When off limits visibility of the opengl headers to just bf_gpu
+// and gawain (temporary option for development purposes)
+WITH_OPENGL:BOOL=ON
+
+//Enable OpenGL UI drawing related unit testing (Experimental)
+WITH_OPENGL_DRAW_TESTS:BOOL=OFF
+
+//Enable OpenGL render related unit testing (Experimental)
+WITH_OPENGL_RENDER_TESTS:BOOL=OFF
+
+//Enable the OpenImageDenoise compositing node
+WITH_OPENIMAGEDENOISE:BOOL=ON
+
+//Enable OpenImageIO Support (http://www.openimageio.org)
+WITH_OPENIMAGEIO:BOOL=ON
+
+//Enable OpenMP (has to be supported by the compiler)
+WITH_OPENMP:BOOL=ON
+
+//Enable OpenSubdiv for surface subdivision
+WITH_OPENSUBDIV:BOOL=ON
+
+//Enable features relying on OpenVDB
+WITH_OPENVDB:BOOL=ON
+
+//Assume OpenVDB library has been compiled with version 3 ABI compatibility
+WITH_OPENVDB_3_ABI_COMPATIBLE:BOOL=OFF
+
+//Enable blosc compression for OpenVDB, only enable if OpenVDB
+// was built with blosc support
+WITH_OPENVDB_BLOSC:BOOL=ON
+
+//Enable Embedded Python API (only disable for development)
+WITH_PYTHON:BOOL=ON
+
+//Copy system python into the blender install folder
+WITH_PYTHON_INSTALL:BOOL=ON
+
+//Copy system numpy into the blender install folder
+WITH_PYTHON_INSTALL_NUMPY:BOOL=ON
+
+//Enable building as a python module which runs without a user
+// interface, like running regular blender in background mode (experimental,
+// only enable for development), installs to PYTHON_SITE_PACKAGES
+// (or CMAKE_INSTALL_PREFIX if WITH_INSTALL_PORTABLE is enabled).
+WITH_PYTHON_MODULE:BOOL=OFF
+
+//Enable internal API error checking to track invalid data to prevent
+// crash on access (at the expense of some efficiency, only enable
+// for development).
+WITH_PYTHON_SAFETY:BOOL=OFF
+
+//Disables execution of scripts within blend files by default
+WITH_PYTHON_SECURITY:BOOL=ON
+
+//Build with quadriflow remesher support
+WITH_QUADRIFLOW:BOOL=ON
+
+//Enable SDL for sound and joystick support
+WITH_SDL:BOOL=ON
+
+//Build with external audaspace library installed on the system
+// (only enable if you know what you're doing!)
+WITH_SYSTEM_AUDASPACE:BOOL=OFF
+
+//Use the systems bullet library (currently unsupported due to
+// missing features in upstream!)
+WITH_SYSTEM_BULLET:BOOL=OFF
+
+//Use system-wide Gflags instead of a bundled one
+WITH_SYSTEM_GFLAGS:BOOL=OFF
+
+//Use system-wide Glog instead of a bundled one
+WITH_SYSTEM_GLOG:BOOL=OFF
+
+//Enable features depending on TBB (OpenVDB, OpenImageDenoise,
+// sculpt multithreading)
+WITH_TBB:BOOL=ON
+
+//Enable the TBB malloc replacement
+WITH_TBB_MALLOC_PROXY:BOOL=ON
+
+//Enable Universal Scene Description (USD) Support
+WITH_USD:BOOL=ON
+
+//Bundle the C runtime for install free distribution.
+WITH_WINDOWS_BUNDLE_CRT:BOOL=ON
+
+//Use find_package to locate libraries
+WITH_WINDOWS_FIND_MODULES:BOOL=OFF
+
+//Generate a pdb file for client side stacktraces
+WITH_WINDOWS_PDB:BOOL=ON
+
+//Use sccache to speed up builds (Ninja builder only)
+WITH_WINDOWS_SCCACHE:BOOL=OFF
+
+//Use a stripped PDB file
+WITH_WINDOWS_STRIPPED_PDB:BOOL=ON
+
+//Enable VR features through the OpenXR specification
+WITH_XR_OPENXR:BOOL=ON
+
+
+########################
+# INTERNAL cache entries
+########################
+
+//ADVANCED property for variable: BUILDINFO_OVERRIDE_DATE
+BUILDINFO_OVERRIDE_DATE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: BUILDINFO_OVERRIDE_TIME
+BUILDINFO_OVERRIDE_TIME-ADVANCED:INTERNAL=1
+//This is the directory where this CMakeCache.txt was created
+CMAKE_CACHEFILE_DIR:INTERNAL=e:/code/blender-git/blender/cmake-build-debug
+//Major version of cmake used to create the current loaded cache
+CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
+//Minor version of cmake used to create the current loaded cache
+CMAKE_CACHE_MINOR_VERSION:INTERNAL=16
+//Patch version of cmake used to create the current loaded cache
+CMAKE_CACHE_PATCH_VERSION:INTERNAL=5
+//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE
+CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1
+//Path to CMake executable.
+CMAKE_COMMAND:INTERNAL=C:/Program Files/JetBrains/CLion 2020.1.1/bin/cmake/win/bin/cmake.exe
+//Path to cpack program executable.
+CMAKE_CPACK_COMMAND:INTERNAL=C:/Program Files/JetBrains/CLion 2020.1.1/bin/cmake/win/bin/cpack.exe
+//Path to ctest program executable.
+CMAKE_CTEST_COMMAND:INTERNAL=C:/Program Files/JetBrains/CLion 2020.1.1/bin/cmake/win/bin/ctest.exe
+//ADVANCED property for variable: CMAKE_CXX_COMPILER
+CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS
+CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG
+CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL
+CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE
+CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO
+CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_STANDARD_LIBRARIES
+CMAKE_CXX_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_COMPILER
+CMAKE_C_COMPILER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS
+CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
+CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
+CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
+CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
+CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_STANDARD_LIBRARIES
+CMAKE_C_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1
+//Executable file format
+CMAKE_EXECUTABLE_FORMAT:INTERNAL=Unknown
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
+CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
+CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
+CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
+CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//Name of external makefile project generator.
+CMAKE_EXTRA_GENERATOR:INTERNAL=CodeBlocks
+//CXX compiler system include directories
+CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS:INTERNAL=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\ATLMFC\include;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\cppwinrt
+//C compiler system include directories
+CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS:INTERNAL=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\ATLMFC\include;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\cppwinrt
+//Name of generator.
+CMAKE_GENERATOR:INTERNAL=NMake Makefiles
+//Generator instance identifier.
+CMAKE_GENERATOR_INSTANCE:INTERNAL=
+//Name of generator platform.
+CMAKE_GENERATOR_PLATFORM:INTERNAL=
+//Name of generator toolset.
+CMAKE_GENERATOR_TOOLSET:INTERNAL=
+//Source directory with the top level CMakeLists.txt file for this
+// project
+CMAKE_HOME_DIRECTORY:INTERNAL=E:/code/blender-git/blender
+//ADVANCED property for variable: CMAKE_LINKER
+CMAKE_LINKER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
+CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
+CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
+CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
+CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
+CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MT
+CMAKE_MT-ADVANCED:INTERNAL=1
+//number of local generators
+CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
+//Platform information initialized
+CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RC_COMPILER
+CMAKE_RC_COMPILER-ADVANCED:INTERNAL=1
+CMAKE_RC_COMPILER_WORKS:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RC_FLAGS
+CMAKE_RC_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RC_FLAGS_DEBUG
+CMAKE_RC_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RC_FLAGS_MINSIZEREL
+CMAKE_RC_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RC_FLAGS_RELEASE
+CMAKE_RC_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RC_FLAGS_RELWITHDEBINFO
+CMAKE_RC_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//Path to CMake installation.
+CMAKE_ROOT:INTERNAL=C:/Program Files/JetBrains/CLion 2020.1.1/bin/cmake/win/share/cmake-3.16
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
+CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
+CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
+CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
+CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
+CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SKIP_RPATH
+CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
+CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
+CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
+CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
+CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
+CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CPACK_OVERRIDE_PACKAGENAME
+CPACK_OVERRIDE_PACKAGENAME-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CYCLES_CUDA_BINARIES_ARCH
+CYCLES_CUDA_BINARIES_ARCH-ADVANCED:INTERNAL=1
+EXECUTABLE_OUTPUT_PATH:INTERNAL=E:/code/blender-git/blender/cmake-build-debug/bin
+//Details about finding Git
+FIND_PACKAGE_MESSAGE_DETAILS_Git:INTERNAL=[C:/Program Files/Git/cmd/git.exe][v2.26.0.windows.1()]
+//ADVANCED property for variable: GIT_EXECUTABLE
+GIT_EXECUTABLE-ADVANCED:INTERNAL=1
+LIBRARY_OUTPUT_PATH:INTERNAL=E:/code/blender-git/blender/cmake-build-debug/lib
+//ADVANCED property for variable: LLVM_STATIC
+LLVM_STATIC-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: MSVC_REDIST_DIR
+MSVC_REDIST_DIR-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: POSTINSTALL_SCRIPT
+POSTINSTALL_SCRIPT-ADVANCED:INTERNAL=1
+//Test SUPPORT_SSE2_BUILD
+SUPPORT_SSE2_BUILD:INTERNAL=1
+//Result of TRY_COMPILE
+SUPPORT_SSE2_BUILD_COMPILED:INTERNAL=TRUE
+//Result of TRY_RUN
+SUPPORT_SSE2_BUILD_EXITCODE:INTERNAL=0
+//Test SUPPORT_SSE_BUILD
+SUPPORT_SSE_BUILD:INTERNAL=1
+//Result of TRY_COMPILE
+SUPPORT_SSE_BUILD_COMPILED:INTERNAL=TRUE
+//Result of TRY_RUN
+SUPPORT_SSE_BUILD_EXITCODE:INTERNAL=0
+TESTS_OUTPUT_DIR:INTERNAL=E:/code/blender-git/blender/cmake-build-debug/bin/tests/$<CONFIG>/
+//ADVANCED property for variable: TEST_PYTHON_EXE
+TEST_PYTHON_EXE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WINDOWS_KITS_DIR
+WINDOWS_KITS_DIR-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WINDOWS_PYTHON_DEBUG
+WINDOWS_PYTHON_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS
+WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS
+WINDOWS_USE_VISUAL_STUDIO_SOURCE_FOLDERS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_ASSERT_ABORT
+WITH_ASSERT_ABORT-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_AUDASPACE
+WITH_AUDASPACE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_BLENDER
+WITH_BLENDER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CPU_SSE
+WITH_CPU_SSE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CUDA_DYNLOAD
+WITH_CUDA_DYNLOAD-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CXX_GUARDEDALLOC
+WITH_CXX_GUARDEDALLOC-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_CUBIN_COMPILER
+WITH_CYCLES_CUBIN_COMPILER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_CUDA_BUILD_SERIAL
+WITH_CYCLES_CUDA_BUILD_SERIAL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_DEBUG
+WITH_CYCLES_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_DEVICE_CUDA
+WITH_CYCLES_DEVICE_CUDA-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_DEVICE_OPENCL
+WITH_CYCLES_DEVICE_OPENCL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_KERNEL_ASAN
+WITH_CYCLES_KERNEL_ASAN-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_LOGGING
+WITH_CYCLES_LOGGING-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_NATIVE_ONLY
+WITH_CYCLES_NATIVE_ONLY-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_CYCLES_NETWORK
+WITH_CYCLES_NETWORK-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_GHOST_DEBUG
+WITH_GHOST_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_GHOST_SDL
+WITH_GHOST_SDL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_GLEW_ES
+WITH_GLEW_ES-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_GL_ANGLE
+WITH_GL_ANGLE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_GL_EGL
+WITH_GL_EGL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_GL_PROFILE_ES20
+WITH_GL_PROFILE_ES20-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_HEADLESS
+WITH_HEADLESS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_LIBMV_SCHUR_SPECIALIZATIONS
+WITH_LIBMV_SCHUR_SPECIALIZATIONS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_MEM_JEMALLOC
+WITH_MEM_JEMALLOC-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_MEM_VALGRIND
+WITH_MEM_VALGRIND-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_OPENGL
+WITH_OPENGL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_OPENVDB_3_ABI_COMPATIBLE
+WITH_OPENVDB_3_ABI_COMPATIBLE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_PYTHON
+WITH_PYTHON-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_PYTHON_SAFETY
+WITH_PYTHON_SAFETY-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_PYTHON_SECURITY
+WITH_PYTHON_SECURITY-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_SYSTEM_AUDASPACE
+WITH_SYSTEM_AUDASPACE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_SYSTEM_BULLET
+WITH_SYSTEM_BULLET-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_SYSTEM_GFLAGS
+WITH_SYSTEM_GFLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_SYSTEM_GLOG
+WITH_SYSTEM_GLOG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_WINDOWS_BUNDLE_CRT
+WITH_WINDOWS_BUNDLE_CRT-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_WINDOWS_FIND_MODULES
+WITH_WINDOWS_FIND_MODULES-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_WINDOWS_PDB
+WITH_WINDOWS_PDB-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_WINDOWS_SCCACHE
+WITH_WINDOWS_SCCACHE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: WITH_WINDOWS_STRIPPED_PDB
+WITH_WINDOWS_STRIPPED_PDB-ADVANCED:INTERNAL=1
+
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CMakeCCompiler.cmake b/cmake-build-debug/CMakeFiles/3.16.5/CMakeCCompiler.cmake
new file mode 100644
index 00000000000..e624393b774
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CMakeCCompiler.cmake
@@ -0,0 +1,76 @@
+set(CMAKE_C_COMPILER "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe")
+set(CMAKE_C_COMPILER_ARG1 "")
+set(CMAKE_C_COMPILER_ID "MSVC")
+set(CMAKE_C_COMPILER_VERSION "19.25.28614.0")
+set(CMAKE_C_COMPILER_VERSION_INTERNAL "")
+set(CMAKE_C_COMPILER_WRAPPER "")
+set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "90")
+set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_std_99;c_std_11;c_function_prototypes;c_variadic_macros")
+set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes")
+set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_variadic_macros")
+set(CMAKE_C11_COMPILE_FEATURES "c_std_11")
+
+set(CMAKE_C_PLATFORM_ID "Windows")
+set(CMAKE_C_SIMULATE_ID "")
+set(CMAKE_C_COMPILER_FRONTEND_VARIANT "")
+set(CMAKE_C_SIMULATE_VERSION "")
+set(CMAKE_C_COMPILER_ARCHITECTURE_ID X86)
+set(MSVC_C_ARCHITECTURE_ID X86)
+
+set(CMAKE_AR "")
+set(CMAKE_C_COMPILER_AR "")
+set(CMAKE_RANLIB "")
+set(CMAKE_C_COMPILER_RANLIB "")
+set(CMAKE_LINKER "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/link.exe")
+set(CMAKE_MT "C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x86/mt.exe")
+set(CMAKE_COMPILER_IS_GNUCC )
+set(CMAKE_C_COMPILER_LOADED 1)
+set(CMAKE_C_COMPILER_WORKS TRUE)
+set(CMAKE_C_ABI_COMPILED TRUE)
+set(CMAKE_COMPILER_IS_MINGW )
+set(CMAKE_COMPILER_IS_CYGWIN )
+if(CMAKE_COMPILER_IS_CYGWIN)
+ set(CYGWIN 1)
+ set(UNIX 1)
+endif()
+
+set(CMAKE_C_COMPILER_ENV_VAR "CC")
+
+if(CMAKE_COMPILER_IS_MINGW)
+ set(MINGW 1)
+endif()
+set(CMAKE_C_COMPILER_ID_RUN 1)
+set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m)
+set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
+set(CMAKE_C_LINKER_PREFERENCE 10)
+
+# Save compiler ABI information.
+set(CMAKE_C_SIZEOF_DATA_PTR "4")
+set(CMAKE_C_COMPILER_ABI "")
+set(CMAKE_C_LIBRARY_ARCHITECTURE "")
+
+if(CMAKE_C_SIZEOF_DATA_PTR)
+ set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}")
+endif()
+
+if(CMAKE_C_COMPILER_ABI)
+ set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}")
+endif()
+
+if(CMAKE_C_LIBRARY_ARCHITECTURE)
+ set(CMAKE_LIBRARY_ARCHITECTURE "")
+endif()
+
+set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "")
+if(CMAKE_C_CL_SHOWINCLUDES_PREFIX)
+ set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}")
+endif()
+
+
+
+
+
+set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "")
+set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "")
+set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "")
+set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CMakeCXXCompiler.cmake b/cmake-build-debug/CMakeFiles/3.16.5/CMakeCXXCompiler.cmake
new file mode 100644
index 00000000000..2e5b74e2a98
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CMakeCXXCompiler.cmake
@@ -0,0 +1,88 @@
+set(CMAKE_CXX_COMPILER "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe")
+set(CMAKE_CXX_COMPILER_ARG1 "")
+set(CMAKE_CXX_COMPILER_ID "MSVC")
+set(CMAKE_CXX_COMPILER_VERSION "19.25.28614.0")
+set(CMAKE_CXX_COMPILER_VERSION_INTERNAL "")
+set(CMAKE_CXX_COMPILER_WRAPPER "")
+set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "14")
+set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17;cxx_std_20")
+set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters")
+set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates")
+set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates")
+set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17")
+set(CMAKE_CXX20_COMPILE_FEATURES "cxx_std_20")
+
+set(CMAKE_CXX_PLATFORM_ID "Windows")
+set(CMAKE_CXX_SIMULATE_ID "")
+set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT "")
+set(CMAKE_CXX_SIMULATE_VERSION "")
+set(CMAKE_CXX_COMPILER_ARCHITECTURE_ID X86)
+set(MSVC_CXX_ARCHITECTURE_ID X86)
+
+set(CMAKE_AR "")
+set(CMAKE_CXX_COMPILER_AR "")
+set(CMAKE_RANLIB "")
+set(CMAKE_CXX_COMPILER_RANLIB "")
+set(CMAKE_LINKER "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/link.exe")
+set(CMAKE_MT "C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x86/mt.exe")
+set(CMAKE_COMPILER_IS_GNUCXX )
+set(CMAKE_CXX_COMPILER_LOADED 1)
+set(CMAKE_CXX_COMPILER_WORKS TRUE)
+set(CMAKE_CXX_ABI_COMPILED TRUE)
+set(CMAKE_COMPILER_IS_MINGW )
+set(CMAKE_COMPILER_IS_CYGWIN )
+if(CMAKE_COMPILER_IS_CYGWIN)
+ set(CYGWIN 1)
+ set(UNIX 1)
+endif()
+
+set(CMAKE_CXX_COMPILER_ENV_VAR "CXX")
+
+if(CMAKE_COMPILER_IS_MINGW)
+ set(MINGW 1)
+endif()
+set(CMAKE_CXX_COMPILER_ID_RUN 1)
+set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;CPP)
+set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
+
+foreach (lang C OBJC OBJCXX)
+ if (CMAKE_${lang}_COMPILER_ID_RUN)
+ foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS)
+ list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension})
+ endforeach()
+ endif()
+endforeach()
+
+set(CMAKE_CXX_LINKER_PREFERENCE 30)
+set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1)
+
+# Save compiler ABI information.
+set(CMAKE_CXX_SIZEOF_DATA_PTR "4")
+set(CMAKE_CXX_COMPILER_ABI "")
+set(CMAKE_CXX_LIBRARY_ARCHITECTURE "")
+
+if(CMAKE_CXX_SIZEOF_DATA_PTR)
+ set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}")
+endif()
+
+if(CMAKE_CXX_COMPILER_ABI)
+ set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}")
+endif()
+
+if(CMAKE_CXX_LIBRARY_ARCHITECTURE)
+ set(CMAKE_LIBRARY_ARCHITECTURE "")
+endif()
+
+set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "")
+if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX)
+ set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}")
+endif()
+
+
+
+
+
+set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "")
+set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
+set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
+set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_C.bin b/cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_C.bin
new file mode 100644
index 00000000000..e978b5d0a79
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_C.bin
Binary files differ
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_CXX.bin b/cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_CXX.bin
new file mode 100644
index 00000000000..9ea0b35af80
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CMakeDetermineCompilerABI_CXX.bin
Binary files differ
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CMakeRCCompiler.cmake b/cmake-build-debug/CMakeFiles/3.16.5/CMakeRCCompiler.cmake
new file mode 100644
index 00000000000..31f8d5755a2
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CMakeRCCompiler.cmake
@@ -0,0 +1,6 @@
+set(CMAKE_RC_COMPILER "C:/Program Files (x86)/Windows Kits/10/bin/10.0.18362.0/x86/rc.exe")
+set(CMAKE_RC_COMPILER_ARG1 "")
+set(CMAKE_RC_COMPILER_LOADED 1)
+set(CMAKE_RC_SOURCE_FILE_EXTENSIONS rc;RC)
+set(CMAKE_RC_OUTPUT_EXTENSION .res)
+set(CMAKE_RC_COMPILER_ENV_VAR "RC")
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CMakeSystem.cmake b/cmake-build-debug/CMakeFiles/3.16.5/CMakeSystem.cmake
new file mode 100644
index 00000000000..5014573fee1
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CMakeSystem.cmake
@@ -0,0 +1,15 @@
+set(CMAKE_HOST_SYSTEM "Windows-10.0.18363")
+set(CMAKE_HOST_SYSTEM_NAME "Windows")
+set(CMAKE_HOST_SYSTEM_VERSION "10.0.18363")
+set(CMAKE_HOST_SYSTEM_PROCESSOR "AMD64")
+
+
+
+set(CMAKE_SYSTEM "Windows-10.0.18363")
+set(CMAKE_SYSTEM_NAME "Windows")
+set(CMAKE_SYSTEM_VERSION "10.0.18363")
+set(CMAKE_SYSTEM_PROCESSOR "AMD64")
+
+set(CMAKE_CROSSCOMPILING "FALSE")
+
+set(CMAKE_SYSTEM_LOADED 1)
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.c b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.c
new file mode 100644
index 00000000000..d884b50908c
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.c
@@ -0,0 +1,671 @@
+#ifdef __cplusplus
+# error "A C++ compiler has been selected for C."
+#endif
+
+#if defined(__18CXX)
+# define ID_VOID_MAIN
+#endif
+#if defined(__CLASSIC_C__)
+/* cv-qualifiers did not exist in K&R C */
+# define const
+# define volatile
+#endif
+
+
+/* Version number components: V=Version, R=Revision, P=Patch
+ Version date components: YYYY=Year, MM=Month, DD=Day */
+
+#if defined(__INTEL_COMPILER) || defined(__ICC)
+# define COMPILER_ID "Intel"
+# if defined(_MSC_VER)
+# define SIMULATE_ID "MSVC"
+# endif
+# if defined(__GNUC__)
+# define SIMULATE_ID "GNU"
+# endif
+ /* __INTEL_COMPILER = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100)
+# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10)
+# if defined(__INTEL_COMPILER_UPDATE)
+# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE)
+# else
+# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10)
+# endif
+# if defined(__INTEL_COMPILER_BUILD_DATE)
+ /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */
+# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE)
+# endif
+# if defined(_MSC_VER)
+ /* _MSC_VER = VVRR */
+# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
+# endif
+# if defined(__GNUC__)
+# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
+# elif defined(__GNUG__)
+# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
+# endif
+# if defined(__GNUC_MINOR__)
+# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
+# endif
+# if defined(__GNUC_PATCHLEVEL__)
+# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
+# endif
+
+#elif defined(__PATHCC__)
+# define COMPILER_ID "PathScale"
+# define COMPILER_VERSION_MAJOR DEC(__PATHCC__)
+# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__)
+# if defined(__PATHCC_PATCHLEVEL__)
+# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__)
+# endif
+
+#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)
+# define COMPILER_ID "Embarcadero"
+# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF)
+# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF)
+# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF)
+
+#elif defined(__BORLANDC__)
+# define COMPILER_ID "Borland"
+ /* __BORLANDC__ = 0xVRR */
+# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8)
+# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF)
+
+#elif defined(__WATCOMC__) && __WATCOMC__ < 1200
+# define COMPILER_ID "Watcom"
+ /* __WATCOMC__ = VVRR */
+# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100)
+# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
+# if (__WATCOMC__ % 10) > 0
+# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
+# endif
+
+#elif defined(__WATCOMC__)
+# define COMPILER_ID "OpenWatcom"
+ /* __WATCOMC__ = VVRP + 1100 */
+# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100)
+# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
+# if (__WATCOMC__ % 10) > 0
+# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
+# endif
+
+#elif defined(__SUNPRO_C)
+# define COMPILER_ID "SunPro"
+# if __SUNPRO_C >= 0x5100
+ /* __SUNPRO_C = 0xVRRP */
+# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>12)
+# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xFF)
+# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF)
+# else
+ /* __SUNPRO_CC = 0xVRP */
+# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_C>>8)
+# define COMPILER_VERSION_MINOR HEX(__SUNPRO_C>>4 & 0xF)
+# define COMPILER_VERSION_PATCH HEX(__SUNPRO_C & 0xF)
+# endif
+
+#elif defined(__HP_cc)
+# define COMPILER_ID "HP"
+ /* __HP_cc = VVRRPP */
+# define COMPILER_VERSION_MAJOR DEC(__HP_cc/10000)
+# define COMPILER_VERSION_MINOR DEC(__HP_cc/100 % 100)
+# define COMPILER_VERSION_PATCH DEC(__HP_cc % 100)
+
+#elif defined(__DECC)
+# define COMPILER_ID "Compaq"
+ /* __DECC_VER = VVRRTPPPP */
+# define COMPILER_VERSION_MAJOR DEC(__DECC_VER/10000000)
+# define COMPILER_VERSION_MINOR DEC(__DECC_VER/100000 % 100)
+# define COMPILER_VERSION_PATCH DEC(__DECC_VER % 10000)
+
+#elif defined(__IBMC__) && defined(__COMPILER_VER__)
+# define COMPILER_ID "zOS"
+ /* __IBMC__ = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
+# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
+
+#elif defined(__ibmxl__) && defined(__clang__)
+# define COMPILER_ID "XLClang"
+# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__)
+# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__)
+# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__)
+# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__)
+
+
+#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ >= 800
+# define COMPILER_ID "XL"
+ /* __IBMC__ = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
+# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
+
+#elif defined(__IBMC__) && !defined(__COMPILER_VER__) && __IBMC__ < 800
+# define COMPILER_ID "VisualAge"
+ /* __IBMC__ = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__IBMC__/100)
+# define COMPILER_VERSION_MINOR DEC(__IBMC__/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__IBMC__ % 10)
+
+#elif defined(__PGI)
+# define COMPILER_ID "PGI"
+# define COMPILER_VERSION_MAJOR DEC(__PGIC__)
+# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__)
+# if defined(__PGIC_PATCHLEVEL__)
+# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__)
+# endif
+
+#elif defined(_CRAYC)
+# define COMPILER_ID "Cray"
+# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR)
+# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR)
+
+#elif defined(__TI_COMPILER_VERSION__)
+# define COMPILER_ID "TI"
+ /* __TI_COMPILER_VERSION__ = VVVRRRPPP */
+# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000)
+# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000)
+# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000)
+
+#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version)
+# define COMPILER_ID "Fujitsu"
+
+#elif defined(__ghs__)
+# define COMPILER_ID "GHS"
+/* __GHS_VERSION_NUMBER = VVVVRP */
+# ifdef __GHS_VERSION_NUMBER
+# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100)
+# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10)
+# endif
+
+#elif defined(__TINYC__)
+# define COMPILER_ID "TinyCC"
+
+#elif defined(__BCC__)
+# define COMPILER_ID "Bruce"
+
+#elif defined(__SCO_VERSION__)
+# define COMPILER_ID "SCO"
+
+#elif defined(__ARMCC_VERSION) && !defined(__clang__)
+# define COMPILER_ID "ARMCC"
+#if __ARMCC_VERSION >= 1000000
+ /* __ARMCC_VERSION = VRRPPPP */
+ # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000)
+ # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100)
+ # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
+#else
+ /* __ARMCC_VERSION = VRPPPP */
+ # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000)
+ # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10)
+ # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
+#endif
+
+
+#elif defined(__clang__) && defined(__apple_build_version__)
+# define COMPILER_ID "AppleClang"
+# if defined(_MSC_VER)
+# define SIMULATE_ID "MSVC"
+# endif
+# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
+# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
+# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
+# if defined(_MSC_VER)
+ /* _MSC_VER = VVRR */
+# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
+# endif
+# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__)
+
+#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION)
+# define COMPILER_ID "ARMClang"
+ # define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000)
+ # define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100)
+ # define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION % 10000)
+# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION)
+
+#elif defined(__clang__)
+# define COMPILER_ID "Clang"
+# if defined(_MSC_VER)
+# define SIMULATE_ID "MSVC"
+# endif
+# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
+# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
+# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
+# if defined(_MSC_VER)
+ /* _MSC_VER = VVRR */
+# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
+# endif
+
+#elif defined(__GNUC__)
+# define COMPILER_ID "GNU"
+# define COMPILER_VERSION_MAJOR DEC(__GNUC__)
+# if defined(__GNUC_MINOR__)
+# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__)
+# endif
+# if defined(__GNUC_PATCHLEVEL__)
+# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
+# endif
+
+#elif defined(_MSC_VER)
+# define COMPILER_ID "MSVC"
+ /* _MSC_VER = VVRR */
+# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100)
+# if defined(_MSC_FULL_VER)
+# if _MSC_VER >= 1400
+ /* _MSC_FULL_VER = VVRRPPPPP */
+# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000)
+# else
+ /* _MSC_FULL_VER = VVRRPPPP */
+# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000)
+# endif
+# endif
+# if defined(_MSC_BUILD)
+# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD)
+# endif
+
+#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__)
+# define COMPILER_ID "ADSP"
+#if defined(__VISUALDSPVERSION__)
+ /* __VISUALDSPVERSION__ = 0xVVRRPP00 */
+# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24)
+# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF)
+# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF)
+#endif
+
+#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
+# define COMPILER_ID "IAR"
+# if defined(__VER__) && defined(__ICCARM__)
+# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000)
+# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000)
+# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000)
+# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
+# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__))
+# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100)
+# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100))
+# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__)
+# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
+# endif
+
+#elif defined(__SDCC_VERSION_MAJOR) || defined(SDCC)
+# define COMPILER_ID "SDCC"
+# if defined(__SDCC_VERSION_MAJOR)
+# define COMPILER_VERSION_MAJOR DEC(__SDCC_VERSION_MAJOR)
+# define COMPILER_VERSION_MINOR DEC(__SDCC_VERSION_MINOR)
+# define COMPILER_VERSION_PATCH DEC(__SDCC_VERSION_PATCH)
+# else
+ /* SDCC = VRP */
+# define COMPILER_VERSION_MAJOR DEC(SDCC/100)
+# define COMPILER_VERSION_MINOR DEC(SDCC/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(SDCC % 10)
+# endif
+
+
+/* These compilers are either not known or too old to define an
+ identification macro. Try to identify the platform and guess that
+ it is the native compiler. */
+#elif defined(__hpux) || defined(__hpua)
+# define COMPILER_ID "HP"
+
+#else /* unknown compiler */
+# define COMPILER_ID ""
+#endif
+
+/* Construct the string literal in pieces to prevent the source from
+ getting matched. Store it in a pointer rather than an array
+ because some compilers will just produce instructions to fill the
+ array rather than assigning a pointer to a static array. */
+char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
+#ifdef SIMULATE_ID
+char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
+#endif
+
+#ifdef __QNXNTO__
+char const* qnxnto = "INFO" ":" "qnxnto[]";
+#endif
+
+#if defined(__CRAYXE) || defined(__CRAYXC)
+char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]";
+#endif
+
+#define STRINGIFY_HELPER(X) #X
+#define STRINGIFY(X) STRINGIFY_HELPER(X)
+
+/* Identify known platforms by name. */
+#if defined(__linux) || defined(__linux__) || defined(linux)
+# define PLATFORM_ID "Linux"
+
+#elif defined(__CYGWIN__)
+# define PLATFORM_ID "Cygwin"
+
+#elif defined(__MINGW32__)
+# define PLATFORM_ID "MinGW"
+
+#elif defined(__APPLE__)
+# define PLATFORM_ID "Darwin"
+
+#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+# define PLATFORM_ID "Windows"
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD)
+# define PLATFORM_ID "FreeBSD"
+
+#elif defined(__NetBSD__) || defined(__NetBSD)
+# define PLATFORM_ID "NetBSD"
+
+#elif defined(__OpenBSD__) || defined(__OPENBSD)
+# define PLATFORM_ID "OpenBSD"
+
+#elif defined(__sun) || defined(sun)
+# define PLATFORM_ID "SunOS"
+
+#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__)
+# define PLATFORM_ID "AIX"
+
+#elif defined(__hpux) || defined(__hpux__)
+# define PLATFORM_ID "HP-UX"
+
+#elif defined(__HAIKU__)
+# define PLATFORM_ID "Haiku"
+
+#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS)
+# define PLATFORM_ID "BeOS"
+
+#elif defined(__QNX__) || defined(__QNXNTO__)
+# define PLATFORM_ID "QNX"
+
+#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__)
+# define PLATFORM_ID "Tru64"
+
+#elif defined(__riscos) || defined(__riscos__)
+# define PLATFORM_ID "RISCos"
+
+#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__)
+# define PLATFORM_ID "SINIX"
+
+#elif defined(__UNIX_SV__)
+# define PLATFORM_ID "UNIX_SV"
+
+#elif defined(__bsdos__)
+# define PLATFORM_ID "BSDOS"
+
+#elif defined(_MPRAS) || defined(MPRAS)
+# define PLATFORM_ID "MP-RAS"
+
+#elif defined(__osf) || defined(__osf__)
+# define PLATFORM_ID "OSF1"
+
+#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv)
+# define PLATFORM_ID "SCO_SV"
+
+#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX)
+# define PLATFORM_ID "ULTRIX"
+
+#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX)
+# define PLATFORM_ID "Xenix"
+
+#elif defined(__WATCOMC__)
+# if defined(__LINUX__)
+# define PLATFORM_ID "Linux"
+
+# elif defined(__DOS__)
+# define PLATFORM_ID "DOS"
+
+# elif defined(__OS2__)
+# define PLATFORM_ID "OS2"
+
+# elif defined(__WINDOWS__)
+# define PLATFORM_ID "Windows3x"
+
+# else /* unknown platform */
+# define PLATFORM_ID
+# endif
+
+#elif defined(__INTEGRITY)
+# if defined(INT_178B)
+# define PLATFORM_ID "Integrity178"
+
+# else /* regular Integrity */
+# define PLATFORM_ID "Integrity"
+# endif
+
+#else /* unknown platform */
+# define PLATFORM_ID
+
+#endif
+
+/* For windows compilers MSVC and Intel we can determine
+ the architecture of the compiler being used. This is because
+ the compilers do not have flags that can change the architecture,
+ but rather depend on which compiler is being used
+*/
+#if defined(_WIN32) && defined(_MSC_VER)
+# if defined(_M_IA64)
+# define ARCHITECTURE_ID "IA64"
+
+# elif defined(_M_X64) || defined(_M_AMD64)
+# define ARCHITECTURE_ID "x64"
+
+# elif defined(_M_IX86)
+# define ARCHITECTURE_ID "X86"
+
+# elif defined(_M_ARM64)
+# define ARCHITECTURE_ID "ARM64"
+
+# elif defined(_M_ARM)
+# if _M_ARM == 4
+# define ARCHITECTURE_ID "ARMV4I"
+# elif _M_ARM == 5
+# define ARCHITECTURE_ID "ARMV5I"
+# else
+# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
+# endif
+
+# elif defined(_M_MIPS)
+# define ARCHITECTURE_ID "MIPS"
+
+# elif defined(_M_SH)
+# define ARCHITECTURE_ID "SHx"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+
+#elif defined(__WATCOMC__)
+# if defined(_M_I86)
+# define ARCHITECTURE_ID "I86"
+
+# elif defined(_M_IX86)
+# define ARCHITECTURE_ID "X86"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+
+#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
+# if defined(__ICCARM__)
+# define ARCHITECTURE_ID "ARM"
+
+# elif defined(__ICCRX__)
+# define ARCHITECTURE_ID "RX"
+
+# elif defined(__ICCRH850__)
+# define ARCHITECTURE_ID "RH850"
+
+# elif defined(__ICCRL78__)
+# define ARCHITECTURE_ID "RL78"
+
+# elif defined(__ICCRISCV__)
+# define ARCHITECTURE_ID "RISCV"
+
+# elif defined(__ICCAVR__)
+# define ARCHITECTURE_ID "AVR"
+
+# elif defined(__ICC430__)
+# define ARCHITECTURE_ID "MSP430"
+
+# elif defined(__ICCV850__)
+# define ARCHITECTURE_ID "V850"
+
+# elif defined(__ICC8051__)
+# define ARCHITECTURE_ID "8051"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+
+#elif defined(__ghs__)
+# if defined(__PPC64__)
+# define ARCHITECTURE_ID "PPC64"
+
+# elif defined(__ppc__)
+# define ARCHITECTURE_ID "PPC"
+
+# elif defined(__ARM__)
+# define ARCHITECTURE_ID "ARM"
+
+# elif defined(__x86_64__)
+# define ARCHITECTURE_ID "x64"
+
+# elif defined(__i386__)
+# define ARCHITECTURE_ID "X86"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+#else
+# define ARCHITECTURE_ID
+#endif
+
+/* Convert integer to decimal digit literals. */
+#define DEC(n) \
+ ('0' + (((n) / 10000000)%10)), \
+ ('0' + (((n) / 1000000)%10)), \
+ ('0' + (((n) / 100000)%10)), \
+ ('0' + (((n) / 10000)%10)), \
+ ('0' + (((n) / 1000)%10)), \
+ ('0' + (((n) / 100)%10)), \
+ ('0' + (((n) / 10)%10)), \
+ ('0' + ((n) % 10))
+
+/* Convert integer to hex digit literals. */
+#define HEX(n) \
+ ('0' + ((n)>>28 & 0xF)), \
+ ('0' + ((n)>>24 & 0xF)), \
+ ('0' + ((n)>>20 & 0xF)), \
+ ('0' + ((n)>>16 & 0xF)), \
+ ('0' + ((n)>>12 & 0xF)), \
+ ('0' + ((n)>>8 & 0xF)), \
+ ('0' + ((n)>>4 & 0xF)), \
+ ('0' + ((n) & 0xF))
+
+/* Construct a string literal encoding the version number components. */
+#ifdef COMPILER_VERSION_MAJOR
+char const info_version[] = {
+ 'I', 'N', 'F', 'O', ':',
+ 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[',
+ COMPILER_VERSION_MAJOR,
+# ifdef COMPILER_VERSION_MINOR
+ '.', COMPILER_VERSION_MINOR,
+# ifdef COMPILER_VERSION_PATCH
+ '.', COMPILER_VERSION_PATCH,
+# ifdef COMPILER_VERSION_TWEAK
+ '.', COMPILER_VERSION_TWEAK,
+# endif
+# endif
+# endif
+ ']','\0'};
+#endif
+
+/* Construct a string literal encoding the internal version number. */
+#ifdef COMPILER_VERSION_INTERNAL
+char const info_version_internal[] = {
+ 'I', 'N', 'F', 'O', ':',
+ 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_',
+ 'i','n','t','e','r','n','a','l','[',
+ COMPILER_VERSION_INTERNAL,']','\0'};
+#endif
+
+/* Construct a string literal encoding the version number components. */
+#ifdef SIMULATE_VERSION_MAJOR
+char const info_simulate_version[] = {
+ 'I', 'N', 'F', 'O', ':',
+ 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[',
+ SIMULATE_VERSION_MAJOR,
+# ifdef SIMULATE_VERSION_MINOR
+ '.', SIMULATE_VERSION_MINOR,
+# ifdef SIMULATE_VERSION_PATCH
+ '.', SIMULATE_VERSION_PATCH,
+# ifdef SIMULATE_VERSION_TWEAK
+ '.', SIMULATE_VERSION_TWEAK,
+# endif
+# endif
+# endif
+ ']','\0'};
+#endif
+
+/* Construct the string literal in pieces to prevent the source from
+ getting matched. Store it in a pointer rather than an array
+ because some compilers will just produce instructions to fill the
+ array rather than assigning a pointer to a static array. */
+char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
+char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";
+
+
+
+
+#if !defined(__STDC__)
+# if (defined(_MSC_VER) && !defined(__clang__)) \
+ || (defined(__ibmxl__) || defined(__IBMC__))
+# define C_DIALECT "90"
+# else
+# define C_DIALECT
+# endif
+#elif __STDC_VERSION__ >= 201000L
+# define C_DIALECT "11"
+#elif __STDC_VERSION__ >= 199901L
+# define C_DIALECT "99"
+#else
+# define C_DIALECT "90"
+#endif
+const char* info_language_dialect_default =
+ "INFO" ":" "dialect_default[" C_DIALECT "]";
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef ID_VOID_MAIN
+void main() {}
+#else
+# if defined(__CLASSIC_C__)
+int main(argc, argv) int argc; char *argv[];
+# else
+int main(int argc, char* argv[])
+# endif
+{
+ int require = 0;
+ require += info_compiler[argc];
+ require += info_platform[argc];
+ require += info_arch[argc];
+#ifdef COMPILER_VERSION_MAJOR
+ require += info_version[argc];
+#endif
+#ifdef COMPILER_VERSION_INTERNAL
+ require += info_version_internal[argc];
+#endif
+#ifdef SIMULATE_ID
+ require += info_simulate[argc];
+#endif
+#ifdef SIMULATE_VERSION_MAJOR
+ require += info_simulate_version[argc];
+#endif
+#if defined(__CRAYXE) || defined(__CRAYXC)
+ require += info_cray[argc];
+#endif
+ require += info_language_dialect_default[argc];
+ (void)argv;
+ return require;
+}
+#endif
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.exe b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.exe
new file mode 100644
index 00000000000..c62fad863b5
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.exe
Binary files differ
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.obj b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.obj
new file mode 100644
index 00000000000..315ad6a7082
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.obj
Binary files differ
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.cpp b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.cpp
new file mode 100644
index 00000000000..69cfdba6bc7
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.cpp
@@ -0,0 +1,660 @@
+/* This source file must have a .cpp extension so that all C++ compilers
+ recognize the extension without flags. Borland does not know .cxx for
+ example. */
+#ifndef __cplusplus
+# error "A C compiler has been selected for C++."
+#endif
+
+
+/* Version number components: V=Version, R=Revision, P=Patch
+ Version date components: YYYY=Year, MM=Month, DD=Day */
+
+#if defined(__COMO__)
+# define COMPILER_ID "Comeau"
+ /* __COMO_VERSION__ = VRR */
+# define COMPILER_VERSION_MAJOR DEC(__COMO_VERSION__ / 100)
+# define COMPILER_VERSION_MINOR DEC(__COMO_VERSION__ % 100)
+
+#elif defined(__INTEL_COMPILER) || defined(__ICC)
+# define COMPILER_ID "Intel"
+# if defined(_MSC_VER)
+# define SIMULATE_ID "MSVC"
+# endif
+# if defined(__GNUC__)
+# define SIMULATE_ID "GNU"
+# endif
+ /* __INTEL_COMPILER = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100)
+# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10)
+# if defined(__INTEL_COMPILER_UPDATE)
+# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE)
+# else
+# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10)
+# endif
+# if defined(__INTEL_COMPILER_BUILD_DATE)
+ /* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */
+# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE)
+# endif
+# if defined(_MSC_VER)
+ /* _MSC_VER = VVRR */
+# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
+# endif
+# if defined(__GNUC__)
+# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
+# elif defined(__GNUG__)
+# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
+# endif
+# if defined(__GNUC_MINOR__)
+# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
+# endif
+# if defined(__GNUC_PATCHLEVEL__)
+# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
+# endif
+
+#elif defined(__PATHCC__)
+# define COMPILER_ID "PathScale"
+# define COMPILER_VERSION_MAJOR DEC(__PATHCC__)
+# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__)
+# if defined(__PATHCC_PATCHLEVEL__)
+# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__)
+# endif
+
+#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)
+# define COMPILER_ID "Embarcadero"
+# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF)
+# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF)
+# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF)
+
+#elif defined(__BORLANDC__)
+# define COMPILER_ID "Borland"
+ /* __BORLANDC__ = 0xVRR */
+# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8)
+# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF)
+
+#elif defined(__WATCOMC__) && __WATCOMC__ < 1200
+# define COMPILER_ID "Watcom"
+ /* __WATCOMC__ = VVRR */
+# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100)
+# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
+# if (__WATCOMC__ % 10) > 0
+# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
+# endif
+
+#elif defined(__WATCOMC__)
+# define COMPILER_ID "OpenWatcom"
+ /* __WATCOMC__ = VVRP + 1100 */
+# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100)
+# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
+# if (__WATCOMC__ % 10) > 0
+# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
+# endif
+
+#elif defined(__SUNPRO_CC)
+# define COMPILER_ID "SunPro"
+# if __SUNPRO_CC >= 0x5100
+ /* __SUNPRO_CC = 0xVRRP */
+# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12)
+# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF)
+# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF)
+# else
+ /* __SUNPRO_CC = 0xVRP */
+# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8)
+# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF)
+# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF)
+# endif
+
+#elif defined(__HP_aCC)
+# define COMPILER_ID "HP"
+ /* __HP_aCC = VVRRPP */
+# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000)
+# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100)
+# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100)
+
+#elif defined(__DECCXX)
+# define COMPILER_ID "Compaq"
+ /* __DECCXX_VER = VVRRTPPPP */
+# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000)
+# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100)
+# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000)
+
+#elif defined(__IBMCPP__) && defined(__COMPILER_VER__)
+# define COMPILER_ID "zOS"
+ /* __IBMCPP__ = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
+# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
+
+#elif defined(__ibmxl__) && defined(__clang__)
+# define COMPILER_ID "XLClang"
+# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__)
+# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__)
+# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__)
+# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__)
+
+
+#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800
+# define COMPILER_ID "XL"
+ /* __IBMCPP__ = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
+# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
+
+#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800
+# define COMPILER_ID "VisualAge"
+ /* __IBMCPP__ = VRP */
+# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
+# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
+
+#elif defined(__PGI)
+# define COMPILER_ID "PGI"
+# define COMPILER_VERSION_MAJOR DEC(__PGIC__)
+# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__)
+# if defined(__PGIC_PATCHLEVEL__)
+# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__)
+# endif
+
+#elif defined(_CRAYC)
+# define COMPILER_ID "Cray"
+# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR)
+# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR)
+
+#elif defined(__TI_COMPILER_VERSION__)
+# define COMPILER_ID "TI"
+ /* __TI_COMPILER_VERSION__ = VVVRRRPPP */
+# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000)
+# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000)
+# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000)
+
+#elif defined(__FUJITSU) || defined(__FCC_VERSION) || defined(__fcc_version)
+# define COMPILER_ID "Fujitsu"
+
+#elif defined(__ghs__)
+# define COMPILER_ID "GHS"
+/* __GHS_VERSION_NUMBER = VVVVRP */
+# ifdef __GHS_VERSION_NUMBER
+# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100)
+# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10)
+# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10)
+# endif
+
+#elif defined(__SCO_VERSION__)
+# define COMPILER_ID "SCO"
+
+#elif defined(__ARMCC_VERSION) && !defined(__clang__)
+# define COMPILER_ID "ARMCC"
+#if __ARMCC_VERSION >= 1000000
+ /* __ARMCC_VERSION = VRRPPPP */
+ # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000)
+ # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100)
+ # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
+#else
+ /* __ARMCC_VERSION = VRPPPP */
+ # define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000)
+ # define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10)
+ # define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
+#endif
+
+
+#elif defined(__clang__) && defined(__apple_build_version__)
+# define COMPILER_ID "AppleClang"
+# if defined(_MSC_VER)
+# define SIMULATE_ID "MSVC"
+# endif
+# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
+# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
+# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
+# if defined(_MSC_VER)
+ /* _MSC_VER = VVRR */
+# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
+# endif
+# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__)
+
+#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION)
+# define COMPILER_ID "ARMClang"
+ # define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000)
+ # define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100)
+ # define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION % 10000)
+# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION)
+
+#elif defined(__clang__)
+# define COMPILER_ID "Clang"
+# if defined(_MSC_VER)
+# define SIMULATE_ID "MSVC"
+# endif
+# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
+# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
+# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
+# if defined(_MSC_VER)
+ /* _MSC_VER = VVRR */
+# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
+# endif
+
+#elif defined(__GNUC__) || defined(__GNUG__)
+# define COMPILER_ID "GNU"
+# if defined(__GNUC__)
+# define COMPILER_VERSION_MAJOR DEC(__GNUC__)
+# else
+# define COMPILER_VERSION_MAJOR DEC(__GNUG__)
+# endif
+# if defined(__GNUC_MINOR__)
+# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__)
+# endif
+# if defined(__GNUC_PATCHLEVEL__)
+# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
+# endif
+
+#elif defined(_MSC_VER)
+# define COMPILER_ID "MSVC"
+ /* _MSC_VER = VVRR */
+# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100)
+# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100)
+# if defined(_MSC_FULL_VER)
+# if _MSC_VER >= 1400
+ /* _MSC_FULL_VER = VVRRPPPPP */
+# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000)
+# else
+ /* _MSC_FULL_VER = VVRRPPPP */
+# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000)
+# endif
+# endif
+# if defined(_MSC_BUILD)
+# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD)
+# endif
+
+#elif defined(__VISUALDSPVERSION__) || defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__)
+# define COMPILER_ID "ADSP"
+#if defined(__VISUALDSPVERSION__)
+ /* __VISUALDSPVERSION__ = 0xVVRRPP00 */
+# define COMPILER_VERSION_MAJOR HEX(__VISUALDSPVERSION__>>24)
+# define COMPILER_VERSION_MINOR HEX(__VISUALDSPVERSION__>>16 & 0xFF)
+# define COMPILER_VERSION_PATCH HEX(__VISUALDSPVERSION__>>8 & 0xFF)
+#endif
+
+#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
+# define COMPILER_ID "IAR"
+# if defined(__VER__) && defined(__ICCARM__)
+# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000)
+# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000)
+# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000)
+# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
+# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__))
+# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100)
+# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100))
+# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__)
+# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
+# endif
+
+
+/* These compilers are either not known or too old to define an
+ identification macro. Try to identify the platform and guess that
+ it is the native compiler. */
+#elif defined(__hpux) || defined(__hpua)
+# define COMPILER_ID "HP"
+
+#else /* unknown compiler */
+# define COMPILER_ID ""
+#endif
+
+/* Construct the string literal in pieces to prevent the source from
+ getting matched. Store it in a pointer rather than an array
+ because some compilers will just produce instructions to fill the
+ array rather than assigning a pointer to a static array. */
+char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
+#ifdef SIMULATE_ID
+char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
+#endif
+
+#ifdef __QNXNTO__
+char const* qnxnto = "INFO" ":" "qnxnto[]";
+#endif
+
+#if defined(__CRAYXE) || defined(__CRAYXC)
+char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]";
+#endif
+
+#define STRINGIFY_HELPER(X) #X
+#define STRINGIFY(X) STRINGIFY_HELPER(X)
+
+/* Identify known platforms by name. */
+#if defined(__linux) || defined(__linux__) || defined(linux)
+# define PLATFORM_ID "Linux"
+
+#elif defined(__CYGWIN__)
+# define PLATFORM_ID "Cygwin"
+
+#elif defined(__MINGW32__)
+# define PLATFORM_ID "MinGW"
+
+#elif defined(__APPLE__)
+# define PLATFORM_ID "Darwin"
+
+#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+# define PLATFORM_ID "Windows"
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD)
+# define PLATFORM_ID "FreeBSD"
+
+#elif defined(__NetBSD__) || defined(__NetBSD)
+# define PLATFORM_ID "NetBSD"
+
+#elif defined(__OpenBSD__) || defined(__OPENBSD)
+# define PLATFORM_ID "OpenBSD"
+
+#elif defined(__sun) || defined(sun)
+# define PLATFORM_ID "SunOS"
+
+#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__)
+# define PLATFORM_ID "AIX"
+
+#elif defined(__hpux) || defined(__hpux__)
+# define PLATFORM_ID "HP-UX"
+
+#elif defined(__HAIKU__)
+# define PLATFORM_ID "Haiku"
+
+#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS)
+# define PLATFORM_ID "BeOS"
+
+#elif defined(__QNX__) || defined(__QNXNTO__)
+# define PLATFORM_ID "QNX"
+
+#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__)
+# define PLATFORM_ID "Tru64"
+
+#elif defined(__riscos) || defined(__riscos__)
+# define PLATFORM_ID "RISCos"
+
+#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__)
+# define PLATFORM_ID "SINIX"
+
+#elif defined(__UNIX_SV__)
+# define PLATFORM_ID "UNIX_SV"
+
+#elif defined(__bsdos__)
+# define PLATFORM_ID "BSDOS"
+
+#elif defined(_MPRAS) || defined(MPRAS)
+# define PLATFORM_ID "MP-RAS"
+
+#elif defined(__osf) || defined(__osf__)
+# define PLATFORM_ID "OSF1"
+
+#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv)
+# define PLATFORM_ID "SCO_SV"
+
+#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX)
+# define PLATFORM_ID "ULTRIX"
+
+#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX)
+# define PLATFORM_ID "Xenix"
+
+#elif defined(__WATCOMC__)
+# if defined(__LINUX__)
+# define PLATFORM_ID "Linux"
+
+# elif defined(__DOS__)
+# define PLATFORM_ID "DOS"
+
+# elif defined(__OS2__)
+# define PLATFORM_ID "OS2"
+
+# elif defined(__WINDOWS__)
+# define PLATFORM_ID "Windows3x"
+
+# else /* unknown platform */
+# define PLATFORM_ID
+# endif
+
+#elif defined(__INTEGRITY)
+# if defined(INT_178B)
+# define PLATFORM_ID "Integrity178"
+
+# else /* regular Integrity */
+# define PLATFORM_ID "Integrity"
+# endif
+
+#else /* unknown platform */
+# define PLATFORM_ID
+
+#endif
+
+/* For windows compilers MSVC and Intel we can determine
+ the architecture of the compiler being used. This is because
+ the compilers do not have flags that can change the architecture,
+ but rather depend on which compiler is being used
+*/
+#if defined(_WIN32) && defined(_MSC_VER)
+# if defined(_M_IA64)
+# define ARCHITECTURE_ID "IA64"
+
+# elif defined(_M_X64) || defined(_M_AMD64)
+# define ARCHITECTURE_ID "x64"
+
+# elif defined(_M_IX86)
+# define ARCHITECTURE_ID "X86"
+
+# elif defined(_M_ARM64)
+# define ARCHITECTURE_ID "ARM64"
+
+# elif defined(_M_ARM)
+# if _M_ARM == 4
+# define ARCHITECTURE_ID "ARMV4I"
+# elif _M_ARM == 5
+# define ARCHITECTURE_ID "ARMV5I"
+# else
+# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
+# endif
+
+# elif defined(_M_MIPS)
+# define ARCHITECTURE_ID "MIPS"
+
+# elif defined(_M_SH)
+# define ARCHITECTURE_ID "SHx"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+
+#elif defined(__WATCOMC__)
+# if defined(_M_I86)
+# define ARCHITECTURE_ID "I86"
+
+# elif defined(_M_IX86)
+# define ARCHITECTURE_ID "X86"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+
+#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
+# if defined(__ICCARM__)
+# define ARCHITECTURE_ID "ARM"
+
+# elif defined(__ICCRX__)
+# define ARCHITECTURE_ID "RX"
+
+# elif defined(__ICCRH850__)
+# define ARCHITECTURE_ID "RH850"
+
+# elif defined(__ICCRL78__)
+# define ARCHITECTURE_ID "RL78"
+
+# elif defined(__ICCRISCV__)
+# define ARCHITECTURE_ID "RISCV"
+
+# elif defined(__ICCAVR__)
+# define ARCHITECTURE_ID "AVR"
+
+# elif defined(__ICC430__)
+# define ARCHITECTURE_ID "MSP430"
+
+# elif defined(__ICCV850__)
+# define ARCHITECTURE_ID "V850"
+
+# elif defined(__ICC8051__)
+# define ARCHITECTURE_ID "8051"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+
+#elif defined(__ghs__)
+# if defined(__PPC64__)
+# define ARCHITECTURE_ID "PPC64"
+
+# elif defined(__ppc__)
+# define ARCHITECTURE_ID "PPC"
+
+# elif defined(__ARM__)
+# define ARCHITECTURE_ID "ARM"
+
+# elif defined(__x86_64__)
+# define ARCHITECTURE_ID "x64"
+
+# elif defined(__i386__)
+# define ARCHITECTURE_ID "X86"
+
+# else /* unknown architecture */
+# define ARCHITECTURE_ID ""
+# endif
+#else
+# define ARCHITECTURE_ID
+#endif
+
+/* Convert integer to decimal digit literals. */
+#define DEC(n) \
+ ('0' + (((n) / 10000000)%10)), \
+ ('0' + (((n) / 1000000)%10)), \
+ ('0' + (((n) / 100000)%10)), \
+ ('0' + (((n) / 10000)%10)), \
+ ('0' + (((n) / 1000)%10)), \
+ ('0' + (((n) / 100)%10)), \
+ ('0' + (((n) / 10)%10)), \
+ ('0' + ((n) % 10))
+
+/* Convert integer to hex digit literals. */
+#define HEX(n) \
+ ('0' + ((n)>>28 & 0xF)), \
+ ('0' + ((n)>>24 & 0xF)), \
+ ('0' + ((n)>>20 & 0xF)), \
+ ('0' + ((n)>>16 & 0xF)), \
+ ('0' + ((n)>>12 & 0xF)), \
+ ('0' + ((n)>>8 & 0xF)), \
+ ('0' + ((n)>>4 & 0xF)), \
+ ('0' + ((n) & 0xF))
+
+/* Construct a string literal encoding the version number components. */
+#ifdef COMPILER_VERSION_MAJOR
+char const info_version[] = {
+ 'I', 'N', 'F', 'O', ':',
+ 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[',
+ COMPILER_VERSION_MAJOR,
+# ifdef COMPILER_VERSION_MINOR
+ '.', COMPILER_VERSION_MINOR,
+# ifdef COMPILER_VERSION_PATCH
+ '.', COMPILER_VERSION_PATCH,
+# ifdef COMPILER_VERSION_TWEAK
+ '.', COMPILER_VERSION_TWEAK,
+# endif
+# endif
+# endif
+ ']','\0'};
+#endif
+
+/* Construct a string literal encoding the internal version number. */
+#ifdef COMPILER_VERSION_INTERNAL
+char const info_version_internal[] = {
+ 'I', 'N', 'F', 'O', ':',
+ 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_',
+ 'i','n','t','e','r','n','a','l','[',
+ COMPILER_VERSION_INTERNAL,']','\0'};
+#endif
+
+/* Construct a string literal encoding the version number components. */
+#ifdef SIMULATE_VERSION_MAJOR
+char const info_simulate_version[] = {
+ 'I', 'N', 'F', 'O', ':',
+ 's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[',
+ SIMULATE_VERSION_MAJOR,
+# ifdef SIMULATE_VERSION_MINOR
+ '.', SIMULATE_VERSION_MINOR,
+# ifdef SIMULATE_VERSION_PATCH
+ '.', SIMULATE_VERSION_PATCH,
+# ifdef SIMULATE_VERSION_TWEAK
+ '.', SIMULATE_VERSION_TWEAK,
+# endif
+# endif
+# endif
+ ']','\0'};
+#endif
+
+/* Construct the string literal in pieces to prevent the source from
+ getting matched. Store it in a pointer rather than an array
+ because some compilers will just produce instructions to fill the
+ array rather than assigning a pointer to a static array. */
+char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
+char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";
+
+
+
+
+#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) && _MSVC_LANG < 201403L
+# if defined(__INTEL_CXX11_MODE__)
+# if defined(__cpp_aggregate_nsdmi)
+# define CXX_STD 201402L
+# else
+# define CXX_STD 201103L
+# endif
+# else
+# define CXX_STD 199711L
+# endif
+#elif defined(_MSC_VER) && defined(_MSVC_LANG)
+# define CXX_STD _MSVC_LANG
+#else
+# define CXX_STD __cplusplus
+#endif
+
+const char* info_language_dialect_default = "INFO" ":" "dialect_default["
+#if CXX_STD > 201703L
+ "20"
+#elif CXX_STD >= 201703L
+ "17"
+#elif CXX_STD >= 201402L
+ "14"
+#elif CXX_STD >= 201103L
+ "11"
+#else
+ "98"
+#endif
+"]";
+
+/*--------------------------------------------------------------------------*/
+
+int main(int argc, char* argv[])
+{
+ int require = 0;
+ require += info_compiler[argc];
+ require += info_platform[argc];
+#ifdef COMPILER_VERSION_MAJOR
+ require += info_version[argc];
+#endif
+#ifdef COMPILER_VERSION_INTERNAL
+ require += info_version_internal[argc];
+#endif
+#ifdef SIMULATE_ID
+ require += info_simulate[argc];
+#endif
+#ifdef SIMULATE_VERSION_MAJOR
+ require += info_simulate_version[argc];
+#endif
+#if defined(__CRAYXE) || defined(__CRAYXC)
+ require += info_cray[argc];
+#endif
+ require += info_language_dialect_default[argc];
+ (void)argv;
+ return require;
+}
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.exe b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.exe
new file mode 100644
index 00000000000..6a307bcff09
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.exe
Binary files differ
diff --git a/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.obj b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.obj
new file mode 100644
index 00000000000..bc92073717b
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.obj
Binary files differ
diff --git a/cmake-build-debug/CMakeFiles/CMakeOutput.log b/cmake-build-debug/CMakeFiles/CMakeOutput.log
new file mode 100644
index 00000000000..b9c8fa565db
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/CMakeOutput.log
@@ -0,0 +1,137 @@
+The system is: Windows - 10.0.18363 - AMD64
+Compiling the C compiler identification source file "CMakeCCompilerId.c" succeeded.
+Compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe
+Build flags:
+Id flags:
+
+The output was:
+0
+Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28614 for x86
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+CMakeCCompilerId.c
+Microsoft (R) Incremental Linker Version 14.25.28614.0
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+/out:CMakeCCompilerId.exe
+CMakeCCompilerId.obj
+
+
+Compilation of the C compiler identification source "CMakeCCompilerId.c" produced "CMakeCCompilerId.exe"
+
+Compilation of the C compiler identification source "CMakeCCompilerId.c" produced "CMakeCCompilerId.obj"
+
+The C compiler identification is MSVC, found in "E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdC/CMakeCCompilerId.exe"
+
+Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" succeeded.
+Compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe
+Build flags:
+Id flags:
+
+The output was:
+0
+Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28614 for x86
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+CMakeCXXCompilerId.cpp
+Microsoft (R) Incremental Linker Version 14.25.28614.0
+Copyright (C) Microsoft Corporation. All rights reserved.
+
+/out:CMakeCXXCompilerId.exe
+CMakeCXXCompilerId.obj
+
+
+Compilation of the CXX compiler identification source "CMakeCXXCompilerId.cpp" produced "CMakeCXXCompilerId.exe"
+
+Compilation of the CXX compiler identification source "CMakeCXXCompilerId.cpp" produced "CMakeCXXCompilerId.obj"
+
+The CXX compiler identification is MSVC, found in "E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/3.16.5/CompilerIdCXX/CMakeCXXCompilerId.exe"
+
+Determining if the C compiler works passed with the following output:
+Change Dir: E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeTmp
+
+Run Build Command(s):nmake /nologo cmTC_657d4\fast && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\HostX86\x86\nmake.exe" -f CMakeFiles\cmTC_657d4.dir\build.make /nologo -L CMakeFiles\cmTC_657d4.dir\build
+Building C object CMakeFiles/cmTC_657d4.dir/testCCompiler.c.obj
+ C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\cl.exe @C:\Users\peter\AppData\Local\Temp\nm8154.tmp
+testCCompiler.c
+Linking C executable cmTC_657d4.exe
+ "C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_657d4.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\mt.exe --manifests -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\cmTC_657d4.dir\objects1.rsp @C:\Users\peter\AppData\Local\Temp\nm82AD.tmp
+
+
+
+Detecting C compiler ABI info compiled with the following output:
+Change Dir: E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeTmp
+
+Run Build Command(s):nmake /nologo cmTC_63ba1\fast && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\HostX86\x86\nmake.exe" -f CMakeFiles\cmTC_63ba1.dir\build.make /nologo -L CMakeFiles\cmTC_63ba1.dir\build
+Building C object CMakeFiles/cmTC_63ba1.dir/CMakeCCompilerABI.c.obj
+ C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\cl.exe @C:\Users\peter\AppData\Local\Temp\nm84CF.tmp
+CMakeCCompilerABI.c
+Linking C executable cmTC_63ba1.exe
+ "C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_63ba1.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\mt.exe --manifests -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\cmTC_63ba1.dir\objects1.rsp @C:\Users\peter\AppData\Local\Temp\nm851E.tmp
+
+
+
+Determining if the CXX compiler works passed with the following output:
+Change Dir: E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeTmp
+
+Run Build Command(s):nmake /nologo cmTC_ad8ea\fast && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\HostX86\x86\nmake.exe" -f CMakeFiles\cmTC_ad8ea.dir\build.make /nologo -L CMakeFiles\cmTC_ad8ea.dir\build
+Building CXX object CMakeFiles/cmTC_ad8ea.dir/testCXXCompiler.cxx.obj
+ C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\cl.exe @C:\Users\peter\AppData\Local\Temp\nm8730.tmp
+testCXXCompiler.cxx
+Linking CXX executable cmTC_ad8ea.exe
+ "C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_ad8ea.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\mt.exe --manifests -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\cmTC_ad8ea.dir\objects1.rsp @C:\Users\peter\AppData\Local\Temp\nm879F.tmp
+
+
+
+Detecting CXX compiler ABI info compiled with the following output:
+Change Dir: E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeTmp
+
+Run Build Command(s):nmake /nologo cmTC_0b66f\fast && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\HostX86\x86\nmake.exe" -f CMakeFiles\cmTC_0b66f.dir\build.make /nologo -L CMakeFiles\cmTC_0b66f.dir\build
+Building CXX object CMakeFiles/cmTC_0b66f.dir/CMakeCXXCompilerABI.cpp.obj
+ C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\cl.exe @C:\Users\peter\AppData\Local\Temp\nm8953.tmp
+CMakeCXXCompilerABI.cpp
+Linking CXX executable cmTC_0b66f.exe
+ "C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_0b66f.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\mt.exe --manifests -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\cmTC_0b66f.dir\objects1.rsp @C:\Users\peter\AppData\Local\Temp\nm89B2.tmp
+
+
+
+Performing C SOURCE FILE Test SUPPORT_SSE_BUILD succeeded with the following compile output:
+Change Dir: E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeTmp
+
+Run Build Command(s):nmake /nologo cmTC_dfa41\fast && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\HostX86\x86\nmake.exe" -f CMakeFiles\cmTC_dfa41.dir\build.make /nologo -L CMakeFiles\cmTC_dfa41.dir\build
+Building C object CMakeFiles/cmTC_dfa41.dir/src.c.obj
+ C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\cl.exe @C:\Users\peter\AppData\Local\Temp\nm8B95.tmp
+cl : Command line warning D9025 : overriding '/arch:SSE' with '/arch:SSE2'
+src.c
+E:\code\blender-git\blender\cmake-build-debug\CMakeFiles\CMakeTmp\src.c(3): warning C4101: 'v': unreferenced local variable
+Linking C executable cmTC_dfa41.exe
+ "C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_dfa41.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\mt.exe --manifests -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\cmTC_dfa41.dir\objects1.rsp @C:\Users\peter\AppData\Local\Temp\nm8BE4.tmp
+
+
+...and run output:
+
+Return value: 1
+Source file was:
+
+ #include <xmmintrin.h>
+ int main(void) { __m128 v = _mm_setzero_ps(); return 0; }
+Performing C SOURCE FILE Test SUPPORT_SSE2_BUILD succeeded with the following compile output:
+Change Dir: E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeTmp
+
+Run Build Command(s):nmake /nologo cmTC_47c41\fast && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\HostX86\x86\nmake.exe" -f CMakeFiles\cmTC_47c41.dir\build.make /nologo -L CMakeFiles\cmTC_47c41.dir\build
+Building C object CMakeFiles/cmTC_47c41.dir/src.c.obj
+ C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\cl.exe @C:\Users\peter\AppData\Local\Temp\nm8D89.tmp
+cl : Command line warning D9025 : overriding '/arch:SSE' with '/arch:SSE2'
+src.c
+E:\code\blender-git\blender\cmake-build-debug\CMakeFiles\CMakeTmp\src.c(3): warning C4101: 'v': unreferenced local variable
+Linking C executable cmTC_47c41.exe
+ "C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_47c41.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x86\mt.exe --manifests -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1425~1.286\bin\Hostx86\x86\link.exe /nologo @CMakeFiles\cmTC_47c41.dir\objects1.rsp @C:\Users\peter\AppData\Local\Temp\nm8DF8.tmp
+
+
+...and run output:
+
+Return value: 1
+Source file was:
+
+ #include <emmintrin.h>
+ int main(void) { __m128d v = _mm_setzero_pd(); return 0; }
diff --git a/cmake-build-debug/CMakeFiles/clion-environment.txt b/cmake-build-debug/CMakeFiles/clion-environment.txt
new file mode 100644
index 00000000000..c983303177a
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/clion-environment.txt
@@ -0,0 +1,4 @@
+ToolSet: 16.0 (local)@C:\Program Files (x86)\Microsoft Visual Studio\2019\Community
+Options:
+
+Options: \ No newline at end of file
diff --git a/cmake-build-debug/CMakeFiles/clion-log.txt b/cmake-build-debug/CMakeFiles/clion-log.txt
new file mode 100644
index 00000000000..0fbdfbc81a8
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/clion-log.txt
@@ -0,0 +1,32 @@
+"C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe" -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - NMake Makefiles" E:\code\blender-git\blender
+-- The C compiler identification is MSVC 19.25.28614.0
+-- The CXX compiler identification is MSVC 19.25.28614.0
+-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe
+-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe -- works
+-- Detecting C compiler ABI info
+-- Detecting C compiler ABI info - done
+-- Detecting C compile features
+-- Detecting C compile features - done
+-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe
+-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.25.28610/bin/Hostx86/x86/cl.exe -- works
+-- Detecting CXX compiler ABI info
+-- Detecting CXX compiler ABI info - done
+-- Detecting CXX compile features
+-- Detecting CXX compile features - done
+-- Performing Test SUPPORT_SSE_BUILD
+-- Performing Test SUPPORT_SSE_BUILD - Success
+-- SSE Support: detected.
+-- Performing Test SUPPORT_SSE2_BUILD
+-- Performing Test SUPPORT_SSE2_BUILD - Success
+-- SSE2 Support: detected.
+-- Found Git: C:/Program Files/Git/cmd/git.exe (found version "2.26.0.windows.1")
+CMake Error at build_files/cmake/platform/platform_win32.cmake:219 (message):
+ 32 bit compiler detected, blender no longer provides pre-build libraries
+ for 32 bit windows, please set the LIBDIR cmake variable to your own
+ library folder
+Call Stack (most recent call first):
+ CMakeLists.txt:832 (include)
+
+
+-- Configuring incomplete, errors occurred!
+See also "E:/code/blender-git/blender/cmake-build-debug/CMakeFiles/CMakeOutput.log".
diff --git a/cmake-build-debug/CMakeFiles/cmake.check_cache b/cmake-build-debug/CMakeFiles/cmake.check_cache
new file mode 100644
index 00000000000..3dccd731726
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/cmake.check_cache
@@ -0,0 +1 @@
+# This file is generated by cmake for dependency checking of the CMakeCache.txt file
diff --git a/cmake-build-debug/CMakeFiles/crt_0b14e42ed68c80dc9fdae2cd4c7e8bbd617f3095 b/cmake-build-debug/CMakeFiles/crt_0b14e42ed68c80dc9fdae2cd4c7e8bbd617f3095
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/cmake-build-debug/CMakeFiles/crt_0b14e42ed68c80dc9fdae2cd4c7e8bbd617f3095
diff --git a/cmake-build-debug/blender.crt.manifest b/cmake-build-debug/blender.crt.manifest
new file mode 100644
index 00000000000..1fc4e629004
--- /dev/null
+++ b/cmake-build-debug/blender.crt.manifest
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity type="win32" name="Blender.CRT" version="1.0.0.0" />
+ <file name="msvcp140.dll" hash="8106f2dd4665aec0d1c652e29378ef46ea4e5801" hashalg="SHA1" />
+ <file name="msvcp140_1.dll" hash="6ba029a7c545d64c044aaad93a3dd00702bdf44e" hashalg="SHA1" />
+ <file name="msvcp140_2.dll" hash="40be57a54102ea5af3d3173c8815bdf35761e5f5" hashalg="SHA1" />
+ <file name="msvcp140_codecvt_ids.dll" hash="d34e3f579507f23c6b3378da44e666b85fff6e3b" hashalg="SHA1" />
+ <file name="vcruntime140.dll" hash="20a2e66fd577293b33ba1c9d01ef04582deaf3a5" hashalg="SHA1" />
+ <file name="concrt140.dll" hash="70bcb3c04ddf9a07f4fa65e94fc6997e58606699" hashalg="SHA1" />
+ <file name="API-MS-Win-core-xstate-l2-1-0.dll" hash="28f0ff95c64faa31eafdc4e5e95cd7dbeb54ca22" hashalg="SHA1" />
+ <file name="api-ms-win-core-console-l1-1-0.dll" hash="2d2e5e097ba5d92e6977cbb23afcc60b2e1d1c8c" hashalg="SHA1" />
+ <file name="api-ms-win-core-console-l1-2-0.dll" hash="b5e00a0516b6c8c6f6a51ea40fae1beba3dd49ba" hashalg="SHA1" />
+ <file name="api-ms-win-core-datetime-l1-1-0.dll" hash="8e0fcc170f5d66beea796b38cd544a045375204b" hashalg="SHA1" />
+ <file name="api-ms-win-core-debug-l1-1-0.dll" hash="505b7e21e237d7f8c454bdfb37b19932ae6980d3" hashalg="SHA1" />
+ <file name="api-ms-win-core-errorhandling-l1-1-0.dll" hash="1cb405eb7339ef121df51f5eba44e0b0177a76d3" hashalg="SHA1" />
+ <file name="api-ms-win-core-file-l1-1-0.dll" hash="e0fa94d72626531aa971c3f1385f03ded6bde6a0" hashalg="SHA1" />
+ <file name="api-ms-win-core-file-l1-2-0.dll" hash="c4643779a0f0f377323503f2db8d2e4d74c738ca" hashalg="SHA1" />
+ <file name="api-ms-win-core-file-l2-1-0.dll" hash="1aadbbd43eff2df7bab51c6f3bda2eb2623b281a" hashalg="SHA1" />
+ <file name="api-ms-win-core-handle-l1-1-0.dll" hash="8b3a44beceb81727071337a9c9e7d0f3b1370455" hashalg="SHA1" />
+ <file name="api-ms-win-core-heap-l1-1-0.dll" hash="deb18c79ab7def1f7ce1b22f90d21b3f6c5d8ef3" hashalg="SHA1" />
+ <file name="api-ms-win-core-interlocked-l1-1-0.dll" hash="2acb3bdfb7209323d586866e276e152d540d5ae3" hashalg="SHA1" />
+ <file name="api-ms-win-core-libraryloader-l1-1-0.dll" hash="f6fbe3fe91884d3aa19ce93156423da55bdd6ced" hashalg="SHA1" />
+ <file name="api-ms-win-core-localization-l1-2-0.dll" hash="7833ac2c20263c8be42f67151f9234eb8e4a5515" hashalg="SHA1" />
+ <file name="api-ms-win-core-memory-l1-1-0.dll" hash="fa1609389caea2192f37017a23ec66e0c7f21d65" hashalg="SHA1" />
+ <file name="api-ms-win-core-namedpipe-l1-1-0.dll" hash="52c9bf4137e466124eab9aa639671795d05125f1" hashalg="SHA1" />
+ <file name="api-ms-win-core-processenvironment-l1-1-0.dll" hash="05727b747b29845e025d2efde0e43ee36927439e" hashalg="SHA1" />
+ <file name="api-ms-win-core-processthreads-l1-1-0.dll" hash="2fdf383c24a697a0cc29231dab4d0a77207a29f1" hashalg="SHA1" />
+ <file name="api-ms-win-core-processthreads-l1-1-1.dll" hash="83a8fcc777c7e8c42fa4c59ee627baf6cbed1969" hashalg="SHA1" />
+ <file name="api-ms-win-core-profile-l1-1-0.dll" hash="f6e3da76e7de8a0d5f2e254b080ba973c92ba817" hashalg="SHA1" />
+ <file name="api-ms-win-core-rtlsupport-l1-1-0.dll" hash="14c89f2ade657eb9249b95f9290fb4284908c9c6" hashalg="SHA1" />
+ <file name="api-ms-win-core-string-l1-1-0.dll" hash="4a902cf7e4500c736ab4830e762cc1e18bb224ec" hashalg="SHA1" />
+ <file name="api-ms-win-core-synch-l1-1-0.dll" hash="6e8a961767f5ac308d569fd57e84b56b145c6c53" hashalg="SHA1" />
+ <file name="api-ms-win-core-synch-l1-2-0.dll" hash="5a60eebe67ed90f3171970f8339e1404ca1bb311" hashalg="SHA1" />
+ <file name="api-ms-win-core-sysinfo-l1-1-0.dll" hash="4c83bd868c963c3afa29d92f75d185ad612c9b11" hashalg="SHA1" />
+ <file name="api-ms-win-core-timezone-l1-1-0.dll" hash="2f0fe3eb94fa90577846d49c03c4fd08ef9d3fb2" hashalg="SHA1" />
+ <file name="api-ms-win-core-util-l1-1-0.dll" hash="c063a309aeff016f0a7d728c44fe169ce6da12c5" hashalg="SHA1" />
+ <file name="api-ms-win-crt-conio-l1-1-0.dll" hash="e025b0afc3b9a8046f83e5df718bac4ad05c9c2c" hashalg="SHA1" />
+ <file name="api-ms-win-crt-convert-l1-1-0.dll" hash="8ada489b9ff33530a3fb7161cc07b5b11dfb8909" hashalg="SHA1" />
+ <file name="api-ms-win-crt-environment-l1-1-0.dll" hash="4051c6eb37a4c0dba47b58301e63df76bff347dd" hashalg="SHA1" />
+ <file name="api-ms-win-crt-filesystem-l1-1-0.dll" hash="5516de099c49e0e6d1224286c3dc9b4d7985e913" hashalg="SHA1" />
+ <file name="api-ms-win-crt-heap-l1-1-0.dll" hash="cdb6d8bd1fbd1c71d85437cff55ddeb76139dbe7" hashalg="SHA1" />
+ <file name="api-ms-win-crt-locale-l1-1-0.dll" hash="f35b3157818d4a5af3486b5e2e70bb510ac05eff" hashalg="SHA1" />
+ <file name="api-ms-win-crt-math-l1-1-0.dll" hash="935b00c88c2065f98746e2b4353d4369216f1812" hashalg="SHA1" />
+ <file name="api-ms-win-crt-multibyte-l1-1-0.dll" hash="18fee669be0aa8a1839a75a167980f3f246c93a4" hashalg="SHA1" />
+ <file name="api-ms-win-crt-private-l1-1-0.dll" hash="bcfb984771542970488bd6132dfa2746267b7fbc" hashalg="SHA1" />
+ <file name="api-ms-win-crt-process-l1-1-0.dll" hash="ba58c577311e39ff7e92a6be0dd6b80abfee6edc" hashalg="SHA1" />
+ <file name="api-ms-win-crt-runtime-l1-1-0.dll" hash="40cf36e07b738aa8bba58bc5587643326ff412a9" hashalg="SHA1" />
+ <file name="api-ms-win-crt-stdio-l1-1-0.dll" hash="6913bf1624599e55680a0292e22c89cab559db81" hashalg="SHA1" />
+ <file name="api-ms-win-crt-string-l1-1-0.dll" hash="5899230b0d7ad96121c3be0df99235ddd8a47dc6" hashalg="SHA1" />
+ <file name="api-ms-win-crt-time-l1-1-0.dll" hash="8396e1e02dace6ae4dde33b3e432a3581bc38f5d" hashalg="SHA1" />
+ <file name="api-ms-win-crt-utility-l1-1-0.dll" hash="5da1d02b764917b38fdc34f4b41fb9a599105dd9" hashalg="SHA1" />
+ <file name="ucrtbase.dll" hash="918bc19f55e656f6d6b1e4713604483eb997ea15" hashalg="SHA1" />
+ <file name="vcomp140.dll" hash="eda3a7cd19d4bb362be37ec06290c1309962d4d4" hashalg="SHA1" />
+</assembly>
diff --git a/cmake-build-debug/blender.exe.manifest b/cmake-build-debug/blender.exe.manifest
new file mode 100644
index 00000000000..78c4ce3971f
--- /dev/null
+++ b/cmake-build-debug/blender.exe.manifest
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+ <dependency><dependentAssembly><assemblyIdentity type="win32" name="blender.crt" version="1.0.0.0" /></dependentAssembly></dependency>
+</assembly>
diff --git a/cmake-build-debug/source/blender/blenkernel/BKE_blender_version.h.done b/cmake-build-debug/source/blender/blenkernel/BKE_blender_version.h.done
new file mode 100644
index 00000000000..800a3a426b7
--- /dev/null
+++ b/cmake-build-debug/source/blender/blenkernel/BKE_blender_version.h.done
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef __BKE_BLENDER_VERSION_H__
+#define __BKE_BLENDER_VERSION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+ * \ingroup bke
+ */
+
+/**
+ * The lines below use regex from scripts to extract their values,
+ * Keep this in mind when modifying this file and keep this comment above the defines.
+ *
+ * \note Use #STRINGIFY() rather than defining with quotes.
+ */
+
+/* Blender major and minor version. */
+#define BLENDER_VERSION 291
+/* Blender patch version for bugfix releases. */
+#define BLENDER_VERSION_PATCH 0
+/** Blender release cycle stage: alpha/beta/rc/release. */
+#define BLENDER_VERSION_CYCLE alpha
+
+/* Blender file format version. */
+#define BLENDER_FILE_VERSION BLENDER_VERSION
+#define BLENDER_FILE_SUBVERSION 0
+
+/* Minimum Blender version that supports reading file written with the current
+ * version. Older Blender versions will test this and show a warning if the file
+ * was written with too new a version. */
+#define BLENDER_FILE_MIN_VERSION 280
+#define BLENDER_FILE_MIN_SUBVERSION 0
+
+/** User readable version string. */
+const char *BKE_blender_version_string(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BKE_BLENDER_VERSION_H__ */
diff --git a/cmake-build-debug/tests.exe.manifest b/cmake-build-debug/tests.exe.manifest
new file mode 100644
index 00000000000..97f78242e35
--- /dev/null
+++ b/cmake-build-debug/tests.exe.manifest
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ </application>
+ </compatibility>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+
+</assembly>
diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile
index 1e430823b10..6e4a087fa36 100644
--- a/doc/doxygen/Doxyfile
+++ b/doc/doxygen/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = Blender
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = "V2.90"
+PROJECT_NUMBER = "V2.91"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/doc/python_api/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..50928963f60 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,14 +242,13 @@ 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.
.. note::
There are some exceptions to this for class names which aren't guarantee to be unique.
- In this case use: :func:`bpy.types.Struct.bl_rna_get_subclass`.
+ In this case use: :func:`bpy.types.Struct.bl_rna_get_subclass_py`.
When loading a class, Blender performs sanity checks making sure all required properties and functions are found,
@@ -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..2c9445dce97 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'
@@ -223,6 +225,7 @@ else:
"aud",
"bgl",
"blf",
+ "bl_math",
"imbuf",
"bmesh",
"bmesh.ops",
@@ -357,7 +360,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 +441,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")
@@ -688,13 +696,11 @@ def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
doc = undocumented_message(module_name, type_name, identifier)
if type(descr) == GetSetDescriptorType:
- fw(ident + ".. attribute:: %s\n" % identifier)
- fw(ident + " :noindex:\n\n")
+ fw(ident + ".. attribute:: %s\n\n" % identifier)
write_indented_lines(ident + " ", fw, doc, False)
fw("\n")
elif type(descr) == MemberDescriptorType: # same as above but use 'data'
- fw(ident + ".. data:: %s\n" % identifier)
- fw(ident + " :noindex:\n\n")
+ fw(ident + ".. data:: %s\n\n" % identifier)
write_indented_lines(ident + " ", fw, doc, False)
fw("\n")
elif type(descr) in {MethodDescriptorType, ClassMethodDescriptorType}:
@@ -734,14 +740,11 @@ def pyprop2sphinx(ident, fw, identifier, py_prop):
'''
# readonly properties use "data" directive, variables use "attribute" directive
if py_prop.fset is None:
- fw(ident + ".. data:: %s\n" % identifier)
- fw(ident + " :noindex:\n\n")
+ fw(ident + ".. data:: %s\n\n" % identifier)
else:
- fw(ident + ".. attribute:: %s\n" % identifier)
- fw(ident + " :noindex:\n\n")
+ fw(ident + ".. attribute:: %s\n\n" % identifier)
write_indented_lines(ident + " ", fw, py_prop.__doc__)
if py_prop.fset is None:
- fw("\n")
fw(ident + " (readonly)\n\n")
else:
fw("\n")
@@ -907,8 +910,7 @@ def pymodule2sphinx(basepath, module_name, module, title):
elif issubclass(value_type, (bool, int, float, str, tuple)):
# constant, not much fun we can do here except to list it.
# TODO, figure out some way to document these!
- fw(".. data:: %s\n" % attribute)
- fw(" :noindex:\n\n")
+ fw(".. data:: %s\n\n" % attribute)
write_indented_lines(" ", fw, "constant value %s" % repr(value), False)
fw("\n")
else:
@@ -1026,6 +1028,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 +1045,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),
@@ -1116,8 +1120,7 @@ def pycontext2sphinx(basepath):
type_descr = prop.get_type_description(
class_fmt=":class:`bpy.types.%s`", collection_id=_BPY_PROP_COLLECTION_ID)
- fw(".. data:: %s\n" % prop.identifier)
- fw(" :noindex:\n\n")
+ fw(".. data:: %s\n\n" % prop.identifier)
if prop.description:
fw(" %s\n\n" % prop.description)
@@ -1162,8 +1165,7 @@ def pycontext2sphinx(basepath):
i = 0
while char_array[i] is not None:
member = ctypes.string_at(char_array[i]).decode(encoding="ascii")
- fw(".. data:: %s\n" % member)
- fw(" :noindex:\n\n")
+ fw(".. data:: %s\n\n" % member)
member_type, is_seq = context_type_map[member]
fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type))
unique.add(member)
@@ -1196,12 +1198,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 +1273,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:
@@ -1366,11 +1371,9 @@ def pyrna2sphinx(basepath):
type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
# readonly properties use "data" directive, variables properties use "attribute" directive
if 'readonly' in type_descr:
- fw(" .. data:: %s\n" % prop.identifier)
- fw(" :noindex:\n\n")
+ fw(" .. data:: %s\n\n" % prop.identifier)
else:
- fw(" .. attribute:: %s\n" % prop.identifier)
- fw(" :noindex:\n\n")
+ fw(" .. attribute:: %s\n\n" % prop.identifier)
if prop.description:
fw(" %s\n\n" % prop.description)
@@ -1714,7 +1717,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()
@@ -1786,8 +1789,18 @@ def write_rst_contents(basepath):
standalone_modules = (
# submodules are added in parent page
- "mathutils", "freestyle", "bgl", "blf", "imbuf", "gpu", "gpu_extras",
- "aud", "bpy_extras", "idprop.types", "bmesh",
+ "aud",
+ "bgl",
+ "bl_math",
+ "blf",
+ "bmesh",
+ "bpy_extras",
+ "freestyle",
+ "gpu",
+ "gpu_extras",
+ "idprop.types",
+ "imbuf",
+ "mathutils",
)
for mod in standalone_modules:
@@ -1939,6 +1952,7 @@ def write_rst_importable_modules(basepath):
"mathutils.kdtree": "KDTree Utilities",
"mathutils.interpolate": "Interpolation Utilities",
"mathutils.noise": "Noise Utilities",
+ "bl_math": "Additional Math Functions",
"freestyle": "Freestyle Module",
"freestyle.types": "Freestyle Types",
"freestyle.predicates": "Freestyle Predicates",
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/audaspace/plugins/sdl/SDLDevice.cpp b/extern/audaspace/plugins/sdl/SDLDevice.cpp
index 603e16001b8..8d7a605fa36 100644
--- a/extern/audaspace/plugins/sdl/SDLDevice.cpp
+++ b/extern/audaspace/plugins/sdl/SDLDevice.cpp
@@ -52,7 +52,7 @@ SDLDevice::SDLDevice(DeviceSpecs specs, int buffersize) :
if(specs.channels == CHANNELS_INVALID)
specs.channels = CHANNELS_STEREO;
if(specs.format == FORMAT_INVALID)
- specs.format = FORMAT_S16;
+ specs.format = FORMAT_FLOAT32;
if(specs.rate == RATE_INVALID)
specs.rate = RATE_48000;
@@ -61,10 +61,25 @@ SDLDevice::SDLDevice(DeviceSpecs specs, int buffersize) :
SDL_AudioSpec format, obtained;
format.freq = m_specs.rate;
- if(m_specs.format == FORMAT_U8)
+ switch(m_specs.format)
+ {
+ case FORMAT_U8:
format.format = AUDIO_U8;
- else
+ break;
+ case FORMAT_S16:
format.format = AUDIO_S16SYS;
+ break;
+ case FORMAT_S32:
+ format.format = AUDIO_S32SYS;
+ break;
+ case FORMAT_FLOAT32:
+ format.format = AUDIO_F32SYS;
+ break;
+ default:
+ format.format = AUDIO_F32SYS;
+ break;
+ }
+
format.channels = m_specs.channels;
format.samples = buffersize;
format.callback = SDLDevice::SDL_mix;
@@ -75,14 +90,25 @@ SDLDevice::SDLDevice(DeviceSpecs specs, int buffersize) :
m_specs.rate = (SampleRate)obtained.freq;
m_specs.channels = (Channels)obtained.channels;
- if(obtained.format == AUDIO_U8)
+
+ switch(obtained.format)
+ {
+ case AUDIO_U8:
m_specs.format = FORMAT_U8;
- else if(obtained.format == AUDIO_S16LSB || obtained.format == AUDIO_S16MSB)
+ break;
+ case AUDIO_S16SYS:
m_specs.format = FORMAT_S16;
- else
- {
+ break;
+ case AUDIO_S32SYS:
+ m_specs.format = FORMAT_S32;
+ break;
+ case AUDIO_F32SYS:
+ m_specs.format = FORMAT_FLOAT32;
+ break;
+ default:
SDL_CloseAudio();
AUD_THROW(DeviceException, "The sample format obtained from SDL is not supported.");
+ break;
}
create();
diff --git a/extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch b/extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch
new file mode 100644
index 00000000000..abafb5855dd
--- /dev/null
+++ b/extern/bullet2/patches/btPolyhedralConvexShape_Inertia_fix.patch
@@ -0,0 +1,41 @@
+diff --git a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp
+index 9095c592d87..b831e20c2f9 100644
+--- a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp
++++ b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp
+@@ -406,17 +406,17 @@ void btPolyhedralConvexShape::calculateLocalInertia(btScalar mass,btVector3& ine
+ #ifndef __SPU__
+ //not yet, return box inertia
+
+- btScalar margin = getMargin();
++ //btScalar margin = getMargin();
+
+ btTransform ident;
+ ident.setIdentity();
+ btVector3 aabbMin,aabbMax;
+- getAabb(ident,aabbMin,aabbMax);
++ getAabb(ident,aabbMin,aabbMax); // This already contains the margin
+ btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5);
+
+- btScalar lx=btScalar(2.)*(halfExtents.x()+margin);
+- btScalar ly=btScalar(2.)*(halfExtents.y()+margin);
+- btScalar lz=btScalar(2.)*(halfExtents.z()+margin);
++ btScalar lx=btScalar(2.)*(halfExtents.x());
++ btScalar ly=btScalar(2.)*(halfExtents.y());
++ btScalar lz=btScalar(2.)*(halfExtents.z());
+ const btScalar x2 = lx*lx;
+ const btScalar y2 = ly*ly;
+ const btScalar z2 = lz*lz;
+@@ -476,10 +476,10 @@ void btPolyhedralConvexAabbCachingShape::recalcLocalAabb()
+
+ for ( int i = 0; i < 3; ++i )
+ {
+- m_localAabbMax[i] = _supporting[i][i] + m_collisionMargin;
+- m_localAabbMin[i] = _supporting[i + 3][i] - m_collisionMargin;
++ m_localAabbMax[i] = _supporting[i][i];
++ m_localAabbMin[i] = _supporting[i + 3][i];
+ }
+-
++
+ #else
+
+ for (int i=0;i<3;i++)
diff --git a/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp b/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp
index e8c8c336cd2..1e7f36e0a17 100644
--- a/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp
+++ b/extern/bullet2/src/BulletCollision/CollisionShapes/btCompoundShape.cpp
@@ -180,7 +180,7 @@ void btCompoundShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVect
localHalfExtents += btVector3(getMargin(),getMargin(),getMargin());
- btMatrix3x3 abs_b = trans.getBasis().absolute();
+ btMatrix3x3 abs_b = trans.getBasis().absolute();
btVector3 center = trans(localCenter);
diff --git a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp
index 9095c592d87..b831e20c2f9 100644
--- a/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp
+++ b/extern/bullet2/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp
@@ -406,17 +406,17 @@ void btPolyhedralConvexShape::calculateLocalInertia(btScalar mass,btVector3& ine
#ifndef __SPU__
//not yet, return box inertia
- btScalar margin = getMargin();
+ //btScalar margin = getMargin();
btTransform ident;
ident.setIdentity();
btVector3 aabbMin,aabbMax;
- getAabb(ident,aabbMin,aabbMax);
+ getAabb(ident,aabbMin,aabbMax); // This already contains the margin
btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5);
- btScalar lx=btScalar(2.)*(halfExtents.x()+margin);
- btScalar ly=btScalar(2.)*(halfExtents.y()+margin);
- btScalar lz=btScalar(2.)*(halfExtents.z()+margin);
+ btScalar lx=btScalar(2.)*(halfExtents.x());
+ btScalar ly=btScalar(2.)*(halfExtents.y());
+ btScalar lz=btScalar(2.)*(halfExtents.z());
const btScalar x2 = lx*lx;
const btScalar y2 = ly*ly;
const btScalar z2 = lz*lz;
@@ -476,10 +476,10 @@ void btPolyhedralConvexAabbCachingShape::recalcLocalAabb()
for ( int i = 0; i < 3; ++i )
{
- m_localAabbMax[i] = _supporting[i][i] + m_collisionMargin;
- m_localAabbMin[i] = _supporting[i + 3][i] - m_collisionMargin;
+ m_localAabbMax[i] = _supporting[i][i];
+ m_localAabbMin[i] = _supporting[i + 3][i];
}
-
+
#else
for (int i=0;i<3;i++)
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/cuew/src/cuew.c b/extern/cuew/src/cuew.c
index f477ec48a18..7a1b0018a24 100644
--- a/extern/cuew/src/cuew.c
+++ b/extern/cuew/src/cuew.c
@@ -879,7 +879,7 @@ int cuewCompilerVersion(void) {
}
/* get --version output */
- strncat(command, "\"", 1);
+ strcat(command, "\"");
strncat(command, path, sizeof(command) - 1);
strncat(command, "\" --version", sizeof(command) - strlen(path) - 1);
pipe = popen(command, "r");
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..9f66b42c6bf 100644
--- a/extern/mantaflow/CMakeLists.txt
+++ b/extern/mantaflow/CMakeLists.txt
@@ -31,19 +31,32 @@ if(MSVC_CLANG AND WITH_OPENMP AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.1
remove_cc_flag("-fopenmp")
endif()
-set(MANTAVERSION "0.12")
+set(MANTAVERSION "0.13")
add_definitions(-DWITH_FLUID=1)
-set(MANTA_DEP
- dependencies
-)
+# Compile Mantaflow dependencies too (e.g. cnpy for numpy file IO).
+# Make sure that dependencies exist before enabling this option by updating the source files in extern/
+set(WITH_MANTA_DEPENDENCIES 0)
+
+# Enable Mantaflow numpy support
+set(WITH_MANTA_NUMPY 0)
+
+if(NOT WITH_MANTA_DEPENDENCIES)
+ add_definitions(-DNO_CNPY=1)
+endif()
+
set(MANTA_HLP
helper
)
set(MANTA_PP
preprocessed
)
+if(WITH_MANTA_DEPENDENCIES)
+ set(MANTA_DEP
+ dependencies
+ )
+endif()
if(WITH_TBB)
add_definitions(-DTBB=1)
@@ -54,10 +67,18 @@ 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()
+if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
+ add_definitions(-DNUMPY=1)
+endif()
+
set(INC
${MANTA_PP}
${MANTA_PP}/fileio
@@ -65,14 +86,25 @@ set(INC
${MANTA_PP}/plugin
${MANTA_HLP}/pwrapper
${MANTA_HLP}/util
- ${MANTA_DEP}/cnpy
)
+if(WITH_MANTA_DEPENDENCIES)
+ list(APPEND INC
+ ${MANTA_DEP}/cnpy
+ )
+endif()
+
set(INC_SYS
${PYTHON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
)
+if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
+ list(APPEND INC_SYS
+ ${PYTHON_NUMPY_INCLUDE_DIRS}
+ )
+endif()
+
if(WITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
@@ -92,9 +124,6 @@ if(WITH_OPENVDB)
endif()
set(SRC
- ${MANTA_DEP}/cnpy/cnpy.cpp
- ${MANTA_DEP}/cnpy/cnpy.h
-
${MANTA_PP}/commonkernels.h
${MANTA_PP}/commonkernels.h.reg.cpp
${MANTA_PP}/conjugategrad.cpp
@@ -106,10 +135,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
@@ -155,14 +186,10 @@ set(SRC
${MANTA_PP}/plugin/initplugins.cpp
${MANTA_PP}/plugin/kepsilon.cpp
${MANTA_PP}/plugin/meshplugins.cpp
-# TODO (sebbas): add numpy to libraries
-# ${MANTA_PP}/plugin/numpyconvert.cpp
${MANTA_PP}/plugin/pressure.cpp
${MANTA_PP}/plugin/ptsplugins.cpp
${MANTA_PP}/plugin/secondaryparticles.cpp
${MANTA_PP}/plugin/surfaceturbulence.cpp
-# TODO (sebbas): add numpy to libraries
-# ${MANTA_PP}/plugin/tfplugins.cpp
${MANTA_PP}/plugin/vortexplugins.cpp
${MANTA_PP}/plugin/waveletturbulence.cpp
${MANTA_PP}/plugin/waves.cpp
@@ -187,9 +214,6 @@ set(SRC
${MANTA_PP}/vortexsheet.h.reg.cpp
${MANTA_HLP}/pwrapper/manta.h
-# TODO (sebbas): add numpy to libraries
-# ${MANTA_HLP}/pwrapper/numpyWrap.cpp
-# ${MANTA_HLP}/pwrapper/numpyWrap.h
${MANTA_HLP}/pwrapper/pclass.cpp
${MANTA_HLP}/pwrapper/pclass.h
${MANTA_HLP}/pwrapper/pconvert.cpp
@@ -215,6 +239,22 @@ set(SRC
${MANTA_HLP}/util/vectorbase.h
)
+if(WITH_MANTA_DEPENDENCIES)
+ list(APPEND SRC
+ ${MANTA_DEP}/cnpy/cnpy.cpp
+ ${MANTA_DEP}/cnpy/cnpy.h
+ )
+endif()
+
+if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
+ list(APPEND SRC
+ ${MANTA_PP}/plugin/numpyconvert.cpp
+ ${MANTA_PP}/plugin/tfplugins.cpp
+ ${MANTA_HLP}/pwrapper/numpyWrap.cpp
+ ${MANTA_HLP}/pwrapper/numpyWrap.h
+ )
+endif()
+
set(LIB
${PYTHON_LINKFLAGS}
${PYTHON_LIBRARIES}
diff --git a/extern/mantaflow/UPDATE.sh b/extern/mantaflow/UPDATE.sh
index 3feb1ba9226..aed4e2a9b71 100644
--- a/extern/mantaflow/UPDATE.sh
+++ b/extern/mantaflow/UPDATE.sh
@@ -13,6 +13,12 @@ BLENDER_INSTALLATION=/Users/sebbas/Developer/Blender/fluid-mantaflow
# Try to check out Mantaflow repository before building?
CLEAN_REPOSITORY=0
+# Skip copying dependency files?
+WITH_DEPENDENCIES=0
+
+# Build with numpy support?
+USE_NUMPY=0
+
# Choose which multithreading platform to use for Mantaflow preprocessing
USE_OMP=0
USE_TBB=1
@@ -50,17 +56,21 @@ fi
MANTA_BUILD_PATH=$MANTA_INSTALLATION/build_blender/
mkdir -p $MANTA_BUILD_PATH
cd $MANTA_BUILD_PATH
-cmake ../mantaflowgit -DGUI=OFF -DOPENMP=$USE_OMP -DTBB=$USE_TBB -DBLENDER=ON -DPREPDEBUG=ON && make -j8
+cmake ../mantaflowgit -DGUI=0 -DOPENMP=$USE_OMP -DTBB=$USE_TBB -DBLENDER=1 -DPREPDEBUG=1 -DNUMPY=$USE_NUMPY && make -j8
# ==================== 3) COPY MANTAFLOW FILES TO BLENDER ROOT ===========================
-mkdir -p $BLENDER_INSTALLATION/blender/tmp/dependencies/ && cp -Rf $MANTA_INSTALLATION/mantaflowgit/dependencies/cnpy "$_"
+if [[ "$WITH_DEPENDENCIES" -eq "1" ]]; then
+ mkdir -p $BLENDER_INSTALLATION/blender/tmp/dependencies/ && cp -Rf $MANTA_INSTALLATION/mantaflowgit/dependencies/cnpy "$_"
+fi
mkdir -p $BLENDER_INSTALLATION/blender/tmp/helper/ && cp -Rf $MANTA_INSTALLATION/mantaflowgit/source/util "$_"
mkdir -p $BLENDER_INSTALLATION/blender/tmp/helper/ && cp -Rf $MANTA_INSTALLATION/mantaflowgit/source/pwrapper "$_"
mkdir -p $BLENDER_INSTALLATION/blender/tmp/preprocessed/ && cp -Rf $MANTA_INSTALLATION/build_blender/pp/source/. "$_"
# Remove some files that are not need in Blender
-rm $BLENDER_INSTALLATION/blender/tmp/dependencies/cnpy/example1.cpp
+if [[ "$WITH_DEPENDENCIES" -eq "1" ]]; then
+ rm $BLENDER_INSTALLATION/blender/tmp/dependencies/cnpy/example1.cpp
+fi
rm $BLENDER_INSTALLATION/blender/tmp/helper/pwrapper/pymain.cpp
rm $BLENDER_INSTALLATION/blender/tmp/preprocessed/*.reg
rm $BLENDER_INSTALLATION/blender/tmp/preprocessed/python/*.reg
@@ -82,8 +92,13 @@ BLENDER_TMP_DEP=$BLENDER_TMP/dependencies
BLENDER_TMP_HLP=$BLENDER_TMP/helper
BLENDER_TMP_PP=$BLENDER_TMP/preprocessed
+# Before moving new files, delete all existing file in the Blender repository
+rm -Rf $BLENDER_MANTA_EXTERN/dependencies $BLENDER_MANTA_EXTERN/helper $BLENDER_MANTA_EXTERN/preprocessed
+
# Move files from tmp dir to extern/
-cp -Rf $BLENDER_TMP_DEP $BLENDER_MANTA_EXTERN
+if [[ "$WITH_DEPENDENCIES" -eq "1" ]]; then
+ cp -Rf $BLENDER_TMP_DEP $BLENDER_MANTA_EXTERN
+fi
cp -Rf $BLENDER_TMP_HLP $BLENDER_MANTA_EXTERN
cp -Rf $BLENDER_TMP_PP $BLENDER_MANTA_EXTERN
diff --git a/extern/mantaflow/dependencies/cnpy/LICENSE b/extern/mantaflow/dependencies/cnpy/LICENSE
deleted file mode 100644
index e60eadbccb3..00000000000
--- a/extern/mantaflow/dependencies/cnpy/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) Carl Rogers, 2011
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/extern/mantaflow/dependencies/cnpy/cnpy.cpp b/extern/mantaflow/dependencies/cnpy/cnpy.cpp
deleted file mode 100644
index 7f0ce21ece8..00000000000
--- a/extern/mantaflow/dependencies/cnpy/cnpy.cpp
+++ /dev/null
@@ -1,385 +0,0 @@
-// Copyright (C) 2011 Carl Rogers
-// Released under MIT License
-// license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
-
-#include "cnpy.h"
-#include <complex>
-#include <cstdlib>
-#include <algorithm>
-#include <cstring>
-#include <iomanip>
-#include <stdint.h>
-#include <stdexcept>
-#include <regex>
-
-char cnpy::BigEndianTest()
-{
- int x = 1;
- return (((char *)&x)[0]) ? '<' : '>';
-}
-
-char cnpy::map_type(const std::type_info &t)
-{
- if (t == typeid(float))
- return 'f';
- if (t == typeid(double))
- return 'f';
- if (t == typeid(long double))
- return 'f';
-
- if (t == typeid(int))
- return 'i';
- if (t == typeid(char))
- return 'i';
- if (t == typeid(short))
- return 'i';
- if (t == typeid(long))
- return 'i';
- if (t == typeid(long long))
- return 'i';
-
- if (t == typeid(unsigned char))
- return 'u';
- if (t == typeid(unsigned short))
- return 'u';
- if (t == typeid(unsigned long))
- return 'u';
- if (t == typeid(unsigned long long))
- return 'u';
- if (t == typeid(unsigned int))
- return 'u';
-
- if (t == typeid(bool))
- return 'b';
-
- if (t == typeid(std::complex<float>))
- return 'c';
- if (t == typeid(std::complex<double>))
- return 'c';
- if (t == typeid(std::complex<long double>))
- return 'c';
-
- else
- return '?';
-}
-
-template<> std::vector<char> &cnpy::operator+=(std::vector<char> &lhs, const std::string rhs)
-{
- lhs.insert(lhs.end(), rhs.begin(), rhs.end());
- return lhs;
-}
-
-template<> std::vector<char> &cnpy::operator+=(std::vector<char> &lhs, const char *rhs)
-{
- // write in little endian
- size_t len = strlen(rhs);
- lhs.reserve(len);
- for (size_t byte = 0; byte < len; byte++) {
- lhs.push_back(rhs[byte]);
- }
- return lhs;
-}
-
-void cnpy::parse_npy_header(unsigned char *buffer,
- size_t &word_size,
- std::vector<size_t> &shape,
- bool &fortran_order)
-{
- // std::string magic_string(buffer,6);
- uint8_t major_version = *reinterpret_cast<uint8_t *>(buffer + 6);
- uint8_t minor_version = *reinterpret_cast<uint8_t *>(buffer + 7);
- uint16_t header_len = *reinterpret_cast<uint16_t *>(buffer + 8);
- std::string header(reinterpret_cast<char *>(buffer + 9), header_len);
-
- size_t loc1, loc2;
-
- // fortran order
- loc1 = header.find("fortran_order") + 16;
- fortran_order = (header.substr(loc1, 4) == "True" ? true : false);
-
- // shape
- loc1 = header.find("(");
- loc2 = header.find(")");
-
- std::regex num_regex("[0-9][0-9]*");
- std::smatch sm;
- shape.clear();
-
- std::string str_shape = header.substr(loc1 + 1, loc2 - loc1 - 1);
- while (std::regex_search(str_shape, sm, num_regex)) {
- shape.push_back(std::stoi(sm[0].str()));
- str_shape = sm.suffix().str();
- }
-
- // endian, word size, data type
- // byte order code | stands for not applicable.
- // not sure when this applies except for byte array
- loc1 = header.find("descr") + 9;
- bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
- assert(littleEndian);
-
- // char type = header[loc1+1];
- // assert(type == map_type(T));
-
- std::string str_ws = header.substr(loc1 + 2);
- loc2 = str_ws.find("'");
- word_size = atoi(str_ws.substr(0, loc2).c_str());
-}
-
-void cnpy::parse_npy_header(FILE *fp,
- size_t &word_size,
- std::vector<size_t> &shape,
- bool &fortran_order)
-{
- char buffer[256];
- size_t res = fread(buffer, sizeof(char), 11, fp);
- if (res != 11)
- throw std::runtime_error("parse_npy_header: failed fread");
- std::string header = fgets(buffer, 256, fp);
- assert(header[header.size() - 1] == '\n');
-
- size_t loc1, loc2;
-
- // fortran order
- loc1 = header.find("fortran_order");
- if (loc1 == std::string::npos)
- throw std::runtime_error("parse_npy_header: failed to find header keyword: 'fortran_order'");
- loc1 += 16;
- fortran_order = (header.substr(loc1, 4) == "True" ? true : false);
-
- // shape
- loc1 = header.find("(");
- loc2 = header.find(")");
- if (loc1 == std::string::npos || loc2 == std::string::npos)
- throw std::runtime_error("parse_npy_header: failed to find header keyword: '(' or ')'");
-
- std::regex num_regex("[0-9][0-9]*");
- std::smatch sm;
- shape.clear();
-
- std::string str_shape = header.substr(loc1 + 1, loc2 - loc1 - 1);
- while (std::regex_search(str_shape, sm, num_regex)) {
- shape.push_back(std::stoi(sm[0].str()));
- str_shape = sm.suffix().str();
- }
-
- // endian, word size, data type
- // byte order code | stands for not applicable.
- // not sure when this applies except for byte array
- loc1 = header.find("descr");
- if (loc1 == std::string::npos)
- throw std::runtime_error("parse_npy_header: failed to find header keyword: 'descr'");
- loc1 += 9;
- bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
- assert(littleEndian);
-
- // char type = header[loc1+1];
- // assert(type == map_type(T));
-
- std::string str_ws = header.substr(loc1 + 2);
- loc2 = str_ws.find("'");
- word_size = atoi(str_ws.substr(0, loc2).c_str());
-}
-
-void cnpy::parse_zip_footer(FILE *fp,
- uint16_t &nrecs,
- size_t &global_header_size,
- size_t &global_header_offset)
-{
- std::vector<char> footer(22);
- fseek(fp, -22, SEEK_END);
- size_t res = fread(&footer[0], sizeof(char), 22, fp);
- if (res != 22)
- throw std::runtime_error("parse_zip_footer: failed fread");
-
- uint16_t disk_no, disk_start, nrecs_on_disk, comment_len;
- disk_no = *(uint16_t *)&footer[4];
- disk_start = *(uint16_t *)&footer[6];
- nrecs_on_disk = *(uint16_t *)&footer[8];
- nrecs = *(uint16_t *)&footer[10];
- global_header_size = *(uint32_t *)&footer[12];
- global_header_offset = *(uint32_t *)&footer[16];
- comment_len = *(uint16_t *)&footer[20];
-
- assert(disk_no == 0);
- assert(disk_start == 0);
- assert(nrecs_on_disk == nrecs);
- assert(comment_len == 0);
-}
-
-cnpy::NpyArray load_the_npy_file(FILE *fp)
-{
- std::vector<size_t> shape;
- size_t word_size;
- bool fortran_order;
- cnpy::parse_npy_header(fp, word_size, shape, fortran_order);
-
- cnpy::NpyArray arr(shape, word_size, fortran_order);
- size_t nread = fread(arr.data<char>(), 1, arr.num_bytes(), fp);
- if (nread != arr.num_bytes())
- throw std::runtime_error("load_the_npy_file: failed fread");
- return arr;
-}
-
-cnpy::NpyArray load_the_npz_array(FILE *fp, uint32_t compr_bytes, uint32_t uncompr_bytes)
-{
-
- std::vector<unsigned char> buffer_compr(compr_bytes);
- std::vector<unsigned char> buffer_uncompr(uncompr_bytes);
- size_t nread = fread(&buffer_compr[0], 1, compr_bytes, fp);
- if (nread != compr_bytes)
- throw std::runtime_error("load_the_npy_file: failed fread");
-
- int err;
- z_stream d_stream;
-
- d_stream.zalloc = Z_NULL;
- d_stream.zfree = Z_NULL;
- d_stream.opaque = Z_NULL;
- d_stream.avail_in = 0;
- d_stream.next_in = Z_NULL;
- err = inflateInit2(&d_stream, -MAX_WBITS);
-
- d_stream.avail_in = compr_bytes;
- d_stream.next_in = &buffer_compr[0];
- d_stream.avail_out = uncompr_bytes;
- d_stream.next_out = &buffer_uncompr[0];
-
- err = inflate(&d_stream, Z_FINISH);
- err = inflateEnd(&d_stream);
-
- std::vector<size_t> shape;
- size_t word_size;
- bool fortran_order;
- cnpy::parse_npy_header(&buffer_uncompr[0], word_size, shape, fortran_order);
-
- cnpy::NpyArray array(shape, word_size, fortran_order);
-
- size_t offset = uncompr_bytes - array.num_bytes();
- memcpy(array.data<unsigned char>(), &buffer_uncompr[0] + offset, array.num_bytes());
-
- return array;
-}
-
-cnpy::npz_t cnpy::npz_load(std::string fname)
-{
- FILE *fp = fopen(fname.c_str(), "rb");
-
- if (!fp) {
- throw std::runtime_error("npz_load: Error! Unable to open file " + fname + "!");
- }
-
- cnpy::npz_t arrays;
-
- while (1) {
- std::vector<char> local_header(30);
- size_t headerres = fread(&local_header[0], sizeof(char), 30, fp);
- if (headerres != 30)
- throw std::runtime_error("npz_load: failed fread");
-
- // if we've reached the global header, stop reading
- if (local_header[2] != 0x03 || local_header[3] != 0x04)
- break;
-
- // read in the variable name
- uint16_t name_len = *(uint16_t *)&local_header[26];
- std::string varname(name_len, ' ');
- size_t vname_res = fread(&varname[0], sizeof(char), name_len, fp);
- if (vname_res != name_len)
- throw std::runtime_error("npz_load: failed fread");
-
- // erase the lagging .npy
- varname.erase(varname.end() - 4, varname.end());
-
- // read in the extra field
- uint16_t extra_field_len = *(uint16_t *)&local_header[28];
- if (extra_field_len > 0) {
- std::vector<char> buff(extra_field_len);
- size_t efield_res = fread(&buff[0], sizeof(char), extra_field_len, fp);
- if (efield_res != extra_field_len)
- throw std::runtime_error("npz_load: failed fread");
- }
-
- uint16_t compr_method = *reinterpret_cast<uint16_t *>(&local_header[0] + 8);
- uint32_t compr_bytes = *reinterpret_cast<uint32_t *>(&local_header[0] + 18);
- uint32_t uncompr_bytes = *reinterpret_cast<uint32_t *>(&local_header[0] + 22);
-
- if (compr_method == 0) {
- arrays[varname] = load_the_npy_file(fp);
- }
- else {
- arrays[varname] = load_the_npz_array(fp, compr_bytes, uncompr_bytes);
- }
- }
-
- fclose(fp);
- return arrays;
-}
-
-cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname)
-{
- FILE *fp = fopen(fname.c_str(), "rb");
-
- if (!fp)
- throw std::runtime_error("npz_load: Unable to open file " + fname);
-
- while (1) {
- std::vector<char> local_header(30);
- size_t header_res = fread(&local_header[0], sizeof(char), 30, fp);
- if (header_res != 30)
- throw std::runtime_error("npz_load: failed fread");
-
- // if we've reached the global header, stop reading
- if (local_header[2] != 0x03 || local_header[3] != 0x04)
- break;
-
- // read in the variable name
- uint16_t name_len = *(uint16_t *)&local_header[26];
- std::string vname(name_len, ' ');
- size_t vname_res = fread(&vname[0], sizeof(char), name_len, fp);
- if (vname_res != name_len)
- throw std::runtime_error("npz_load: failed fread");
- vname.erase(vname.end() - 4, vname.end()); // erase the lagging .npy
-
- // read in the extra field
- uint16_t extra_field_len = *(uint16_t *)&local_header[28];
- fseek(fp, extra_field_len, SEEK_CUR); // skip past the extra field
-
- uint16_t compr_method = *reinterpret_cast<uint16_t *>(&local_header[0] + 8);
- uint32_t compr_bytes = *reinterpret_cast<uint32_t *>(&local_header[0] + 18);
- uint32_t uncompr_bytes = *reinterpret_cast<uint32_t *>(&local_header[0] + 22);
-
- if (vname == varname) {
- NpyArray array = (compr_method == 0) ? load_the_npy_file(fp) :
- load_the_npz_array(fp, compr_bytes, uncompr_bytes);
- fclose(fp);
- return array;
- }
- else {
- // skip past the data
- // uint32_t size = *(uint32_t*) &local_header[22];
- uint32_t size = *(uint32_t *)&local_header[18]; // using index 18 instead of 22 enables
- // support for compressed data
- fseek(fp, size, SEEK_CUR);
- }
- }
-
- fclose(fp);
-
- // if we get here, we haven't found the variable in the file
- throw std::runtime_error("npz_load: Variable name " + varname + " not found in " + fname);
-}
-
-cnpy::NpyArray cnpy::npy_load(std::string fname)
-{
-
- FILE *fp = fopen(fname.c_str(), "rb");
-
- if (!fp)
- throw std::runtime_error("npy_load: Unable to open file " + fname);
-
- NpyArray arr = load_the_npy_file(fp);
-
- fclose(fp);
- return arr;
-}
diff --git a/extern/mantaflow/dependencies/cnpy/cnpy.h b/extern/mantaflow/dependencies/cnpy/cnpy.h
deleted file mode 100644
index e4b6365cb6f..00000000000
--- a/extern/mantaflow/dependencies/cnpy/cnpy.h
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright (C) 2011 Carl Rogers
-// Released under MIT License
-// license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
-
-#ifndef LIBCNPY_H_
-#define LIBCNPY_H_
-
-#include <string>
-#include <stdexcept>
-#include <sstream>
-#include <vector>
-#include <cstdio>
-#include <typeinfo>
-#include <iostream>
-#include <cassert>
-#include <zlib.h>
-#include <map>
-#include <memory>
-#include <stdint.h>
-#include <numeric>
-
-namespace cnpy {
-
-struct NpyArray {
- NpyArray(const std::vector<size_t> &_shape, size_t _word_size, bool _fortran_order)
- : shape(_shape), word_size(_word_size), fortran_order(_fortran_order)
- {
- num_vals = 1;
- for (size_t i = 0; i < shape.size(); i++)
- num_vals *= shape[i];
- data_holder = std::shared_ptr<std::vector<char>>(new std::vector<char>(num_vals * word_size));
- }
-
- NpyArray() : shape(0), word_size(0), fortran_order(0), num_vals(0)
- {
- }
-
- template<typename T> T *data()
- {
- return reinterpret_cast<T *>(&(*data_holder)[0]);
- }
-
- template<typename T> const T *data() const
- {
- return reinterpret_cast<T *>(&(*data_holder)[0]);
- }
-
- template<typename T> std::vector<T> as_vec() const
- {
- const T *p = data<T>();
- return std::vector<T>(p, p + num_vals);
- }
-
- size_t num_bytes() const
- {
- return data_holder->size();
- }
-
- std::shared_ptr<std::vector<char>> data_holder;
- std::vector<size_t> shape;
- size_t word_size;
- bool fortran_order;
- size_t num_vals;
-};
-
-using npz_t = std::map<std::string, NpyArray>;
-
-char BigEndianTest();
-char map_type(const std::type_info &t);
-template<typename T> std::vector<char> create_npy_header(const std::vector<size_t> &shape);
-void parse_npy_header(FILE *fp,
- size_t &word_size,
- std::vector<size_t> &shape,
- bool &fortran_order);
-void parse_npy_header(unsigned char *buffer,
- size_t &word_size,
- std::vector<size_t> &shape,
- bool &fortran_order);
-void parse_zip_footer(FILE *fp,
- uint16_t &nrecs,
- size_t &global_header_size,
- size_t &global_header_offset);
-npz_t npz_load(std::string fname);
-NpyArray npz_load(std::string fname, std::string varname);
-NpyArray npy_load(std::string fname);
-
-template<typename T> std::vector<char> &operator+=(std::vector<char> &lhs, const T rhs)
-{
- // write in little endian
- for (size_t byte = 0; byte < sizeof(T); byte++) {
- char val = *((char *)&rhs + byte);
- lhs.push_back(val);
- }
- return lhs;
-}
-
-template<> std::vector<char> &operator+=(std::vector<char> &lhs, const std::string rhs);
-template<> std::vector<char> &operator+=(std::vector<char> &lhs, const char *rhs);
-
-template<typename T>
-void npy_save(std::string fname,
- const T *data,
- const std::vector<size_t> shape,
- std::string mode = "w")
-{
- FILE *fp = NULL;
- std::vector<size_t> true_data_shape; // if appending, the shape of existing + new data
-
- if (mode == "a")
- fp = fopen(fname.c_str(), "r+b");
-
- if (fp) {
- // file exists. we need to append to it. read the header, modify the array size
- size_t word_size;
- bool fortran_order;
- parse_npy_header(fp, word_size, true_data_shape, fortran_order);
- assert(!fortran_order);
-
- if (word_size != sizeof(T)) {
- std::cout << "libnpy error: " << fname << " has word size " << word_size
- << " but npy_save appending data sized " << sizeof(T) << "\n";
- assert(word_size == sizeof(T));
- }
- if (true_data_shape.size() != shape.size()) {
- std::cout << "libnpy error: npy_save attempting to append misdimensioned data to " << fname
- << "\n";
- assert(true_data_shape.size() != shape.size());
- }
-
- for (size_t i = 1; i < shape.size(); i++) {
- if (shape[i] != true_data_shape[i]) {
- std::cout << "libnpy error: npy_save attempting to append misshaped data to " << fname
- << "\n";
- assert(shape[i] == true_data_shape[i]);
- }
- }
- true_data_shape[0] += shape[0];
- }
- else {
- fp = fopen(fname.c_str(), "wb");
- true_data_shape = shape;
- }
-
- std::vector<char> header = create_npy_header<T>(true_data_shape);
- size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
-
- fseek(fp, 0, SEEK_SET);
- fwrite(&header[0], sizeof(char), header.size(), fp);
- fseek(fp, 0, SEEK_END);
- fwrite(data, sizeof(T), nels, fp);
- fclose(fp);
-}
-
-template<typename T>
-void npz_save(std::string zipname,
- std::string fname,
- const T *data,
- const std::vector<size_t> &shape,
- std::string mode = "w")
-{
- // first, append a .npy to the fname
- fname += ".npy";
-
- // now, on with the show
- FILE *fp = NULL;
- uint16_t nrecs = 0;
- size_t global_header_offset = 0;
- std::vector<char> global_header;
-
- if (mode == "a")
- fp = fopen(zipname.c_str(), "r+b");
-
- if (fp) {
- // zip file exists. we need to add a new npy file to it.
- // first read the footer. this gives us the offset and size of the global header
- // then read and store the global header.
- // below, we will write the the new data at the start of the global header then append the
- // global header and footer below it
- size_t global_header_size;
- parse_zip_footer(fp, nrecs, global_header_size, global_header_offset);
- fseek(fp, global_header_offset, SEEK_SET);
- global_header.resize(global_header_size);
- size_t res = fread(&global_header[0], sizeof(char), global_header_size, fp);
- if (res != global_header_size) {
- throw std::runtime_error("npz_save: header read error while adding to existing zip");
- }
- fseek(fp, global_header_offset, SEEK_SET);
- }
- else {
- fp = fopen(zipname.c_str(), "wb");
- }
-
- std::vector<char> npy_header = create_npy_header<T>(shape);
-
- size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
- size_t nbytes = nels * sizeof(T) + npy_header.size();
-
- // get the CRC of the data to be added
- uint32_t crc = crc32(0L, (uint8_t *)&npy_header[0], npy_header.size());
- crc = crc32(crc, (uint8_t *)data, nels * sizeof(T));
-
- // build the local header
- std::vector<char> local_header;
- local_header += "PK"; // first part of sig
- local_header += (uint16_t)0x0403; // second part of sig
- local_header += (uint16_t)20; // min version to extract
- local_header += (uint16_t)0; // general purpose bit flag
- local_header += (uint16_t)0; // compression method
- local_header += (uint16_t)0; // file last mod time
- local_header += (uint16_t)0; // file last mod date
- local_header += (uint32_t)crc; // crc
- local_header += (uint32_t)nbytes; // compressed size
- local_header += (uint32_t)nbytes; // uncompressed size
- local_header += (uint16_t)fname.size(); // fname length
- local_header += (uint16_t)0; // extra field length
- local_header += fname;
-
- // build global header
- global_header += "PK"; // first part of sig
- global_header += (uint16_t)0x0201; // second part of sig
- global_header += (uint16_t)20; // version made by
- global_header.insert(global_header.end(), local_header.begin() + 4, local_header.begin() + 30);
- global_header += (uint16_t)0; // file comment length
- global_header += (uint16_t)0; // disk number where file starts
- global_header += (uint16_t)0; // internal file attributes
- global_header += (uint32_t)0; // external file attributes
- global_header += (uint32_t)
- global_header_offset; // relative offset of local file header, since it begins where the
- // global header used to begin
- global_header += fname;
-
- // build footer
- std::vector<char> footer;
- footer += "PK"; // first part of sig
- footer += (uint16_t)0x0605; // second part of sig
- footer += (uint16_t)0; // number of this disk
- footer += (uint16_t)0; // disk where footer starts
- footer += (uint16_t)(nrecs + 1); // number of records on this disk
- footer += (uint16_t)(nrecs + 1); // total number of records
- footer += (uint32_t)global_header.size(); // nbytes of global headers
- footer += (uint32_t)(global_header_offset + nbytes +
- local_header.size()); // offset of start of global headers, since global
- // header now starts after newly written array
- footer += (uint16_t)0; // zip file comment length
-
- // write everything
- fwrite(&local_header[0], sizeof(char), local_header.size(), fp);
- fwrite(&npy_header[0], sizeof(char), npy_header.size(), fp);
- fwrite(data, sizeof(T), nels, fp);
- fwrite(&global_header[0], sizeof(char), global_header.size(), fp);
- fwrite(&footer[0], sizeof(char), footer.size(), fp);
- fclose(fp);
-}
-
-template<typename T>
-void npy_save(std::string fname, const std::vector<T> data, std::string mode = "w")
-{
- std::vector<size_t> shape;
- shape.push_back(data.size());
- npy_save(fname, &data[0], shape, mode);
-}
-
-template<typename T>
-void npz_save(std::string zipname,
- std::string fname,
- const std::vector<T> data,
- std::string mode = "w")
-{
- std::vector<size_t> shape;
- shape.push_back(data.size());
- npz_save(zipname, fname, &data[0], shape, mode);
-}
-
-template<typename T> std::vector<char> create_npy_header(const std::vector<size_t> &shape)
-{
-
- std::vector<char> dict;
- dict += "{'descr': '";
- dict += BigEndianTest();
- dict += map_type(typeid(T));
- dict += std::to_string(sizeof(T));
- dict += "', 'fortran_order': False, 'shape': (";
- dict += std::to_string(shape[0]);
- for (size_t i = 1; i < shape.size(); i++) {
- dict += ", ";
- dict += std::to_string(shape[i]);
- }
- if (shape.size() == 1)
- dict += ",";
- dict += "), }";
- // pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to
- // end with \n
- int remainder = 16 - (10 + dict.size()) % 16;
- dict.insert(dict.end(), remainder, ' ');
- dict.back() = '\n';
-
- std::vector<char> header;
- header += (char)0x93;
- header += "NUMPY";
- header += (char)0x01; // major version of numpy format
- header += (char)0x00; // minor version of numpy format
- header += (uint16_t)dict.size();
- header.insert(header.end(), dict.begin(), dict.end());
-
- return header;
-}
-
-} // namespace cnpy
-
-#endif
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/helper/util/vectorbase.h b/extern/mantaflow/helper/util/vectorbase.h
index 41584663a0f..9b4d9c83f0b 100644
--- a/extern/mantaflow/helper/util/vectorbase.h
+++ b/extern/mantaflow/helper/util/vectorbase.h
@@ -248,12 +248,14 @@ template<class S> class Vector3D {
protected:
};
-//! helper to check whether float/double value is non-zero
-inline bool notZero(Real f)
+//! helper to check whether value is non-zero
+template<class S> inline bool notZero(S v)
{
- if (std::abs(f) > VECTOR_EPSILON)
- return true;
- return false;
+ return (std::abs(v) > VECTOR_EPSILON);
+}
+template<class S> inline bool notZero(Vector3D<S> v)
+{
+ return (std::abs(norm(v)) > VECTOR_EPSILON);
}
//************************************************************************
@@ -437,6 +439,36 @@ inline Real normSquare(const int v)
return square(v);
}
+//! Compute sum of all components, allow use of int, Real too
+template<class S> inline S sum(const S v)
+{
+ return v;
+}
+template<class S> inline S sum(const Vector3D<S> &v)
+{
+ return v.x + v.y + v.z;
+}
+
+//! Get absolute representation of vector, allow use of int, Real too
+inline Real abs(const Real v)
+{
+ return std::fabs(v);
+}
+inline int abs(const int v)
+{
+ return std::abs(v);
+}
+
+template<class S> inline Vector3D<S> abs(const Vector3D<S> &v)
+{
+ Vector3D<S> cp(v.x, v.y, v.z);
+ for (int i = 0; i < 3; ++i) {
+ if (cp[i] < 0)
+ cp[i] *= (-1.0);
+ }
+ return cp;
+}
+
//! Returns a normalized vector
template<class S> inline Vector3D<S> getNormalized(const Vector3D<S> &v)
{
diff --git a/extern/mantaflow/preprocessed/fileio/iogrids.cpp b/extern/mantaflow/preprocessed/fileio/iogrids.cpp
index acd1bda5174..825ce0ae8b5 100644
--- a/extern/mantaflow/preprocessed/fileio/iogrids.cpp
+++ b/extern/mantaflow/preprocessed/fileio/iogrids.cpp
@@ -27,11 +27,10 @@ extern "C" {
}
#endif
-#if OPENVDB == 1
-# include "openvdb/openvdb.h"
+#if NO_CNPY != 1
+# include "cnpy.h"
#endif
-#include "cnpy.h"
#include "mantaio.h"
#include "grid.h"
#include "vector4d.h"
@@ -279,54 +278,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;
}
-template<class T> void writeGridRaw(const string &name, Grid<T> *grid)
+int writeGridsTxt(const string &name, std::vector<PbClass *> *grids)
+{
+ errMsg("writeGridsTxt: writing multiple grids to one .txt file not supported yet");
+ return 0;
+}
+
+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 +505,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 +528,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 +551,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 +648,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 +697,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 +717,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 +733,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 +751,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 +763,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 +799,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 +823,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 +850,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 +865,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 +940,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,314 +958,83 @@ 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)
{
+#if NO_ZLIB != 1
gzFile gzf = NULL;
if (fileHandle) {
gzf = (gzFile)(*fileHandle);
gzclose(gzf);
*fileHandle = NULL;
}
+#else
+ debMsg("file format not supported without zlib", 1);
+#endif
}
-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;
-#endif
+
#if FLOATINGPOINT_PRECISION != 1
errMsg("writeGridNumpy: Double precision not yet supported");
+ return 0;
#endif
+#if NO_CNPY != 1
// find suffix to differentiate between npy <-> npz , TODO: check for actual "npy" string
std::string::size_type idx;
bool bUseNpz = false;
@@ -1211,8 +1054,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,18 +1079,22 @@ template<class T> void writeGridNumpy(const string &name, Grid<T> *grid)
else {
cnpy::npy_save(name, &grid[0], shape, "w");
}
-};
+ return 1;
+#else
+ debMsg("file format not supported without cnpy", 1);
+ return 0;
+#endif
+}
-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;
-#endif
+
#if FLOATINGPOINT_PRECISION != 1
errMsg("readGridNumpy: Double precision not yet supported");
+ return 0;
#endif
+#if NO_CNPY != 1
// find suffix to differentiate between npy <-> npz
std::string::size_type idx;
bool bUseNpz = false;
@@ -1279,8 +1128,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,20 +1150,36 @@ 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;
+#else
+ debMsg("file format not supported without cnpy", 1);
+ return 0;
+#endif
+}
+
+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)
{
x = y = z = 0;
-#if NO_ZLIB != 1
- debMsg("file format not supported without zlib", 1);
- return;
-#endif
+
#if FLOATINGPOINT_PRECISION != 1
errMsg("getNpzFileSize: Double precision not yet supported");
#endif
+
+#if NO_CNPY != 1
// find suffix to differentiate between npy <-> npz
cnpy::NpyArray gridArr;
cnpy::npz_t fNpz = cnpy::npz_load(name);
@@ -1323,6 +1190,9 @@ void getNpzFileSize(
x = gridArr.shape[2];
if (t)
(*t) = 0; // unused for now
+#else
+ debMsg("file format not supported without cnpy", 1);
+#endif
}
Vec3 getNpzFileSize(const string &name)
{
@@ -1521,77 +1391,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..b5e51625077 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,23 +299,30 @@ 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;
+ }
+
+ const Real dx = mesh->getParent()->getDx();
+ const Vec3 gs = toVec3(mesh->getParent()->getGridSize());
if (!append)
mesh->clear();
int nodebase = mesh->numNodes();
- int cnt = nodebase;
+ int cntNodes = nodebase, cntNormals = nodebase;
+
while (ifs.good() && !ifs.eof()) {
string id;
ifs >> id;
@@ -323,17 +337,23 @@ void readObjFile(const std::string &name, Mesh *mesh, bool append)
}
else if (id == "vn") {
// normals
- if (!mesh->numNodes())
+ if (mesh->numNodes() != cntNodes) {
errMsg("invalid amount of nodes");
- Node n = mesh->nodes(cnt);
- ifs >> n.normal.x >> n.normal.y >> n.normal.z;
- cnt++;
+ return 0;
+ }
+ Node *n = &mesh->nodes(cntNormals);
+ ifs >> n->normal.x >> n->normal.y >> n->normal.z;
+ cntNormals++;
}
else if (id == "v") {
// vertex
Node n;
ifs >> n.pos.x >> n.pos.y >> n.pos.z;
+ // convert to grid space
+ n.pos /= dx;
+ n.pos += gs * 0.5;
mesh->addNode(n);
+ cntNodes++;
}
else if (id == "g") {
// group
@@ -349,8 +369,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 +385,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";
@@ -391,7 +416,6 @@ void writeObjFile(const string &name, Mesh *mesh)
// write normals
for (int i = 0; i < numVerts; i++) {
Vector3D<float> n = toVec3f(mesh->nodes(i).normal);
- // normalize to unit cube around 0
ofs << "vn " << n.value[0] << " " << n.value[1] << " " << n.value[2] << " "
<< "\n";
}
@@ -405,16 +429,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 +467,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 +489,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 +504,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..36e10aa1644 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);
@@ -310,6 +322,7 @@ template<class T> void readPdataUni(const std::string &name, ParticleDataImpl<T>
UniPartHeader head;
assertMsg(gzread(gzf, &head, sizeof(UniPartHeader)) == sizeof(UniPartHeader),
"can't read file, no header present");
+ pdata->getParticleSys()->resize(head.dim); // ensure that parent particle system has same size
pdata->resize(head.dim);
assertMsg(head.dim == pdata->size(), "pdata size doesn't match");
@@ -327,18 +340,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..cf40d71fcc4 100644
--- a/extern/mantaflow/preprocessed/fileio/ioutil.cpp
+++ b/extern/mantaflow/preprocessed/fileio/ioutil.cpp
@@ -18,21 +18,26 @@
#include "mantaio.h"
+#if OPENVDB == 1
+# include "openvdb/openvdb.h"
+#endif
+
#if NO_ZLIB != 1
extern "C" {
# include <zlib.h>
}
+#endif
-# if defined(WIN32) || defined(_WIN32)
-# include <windows.h>
-# include <string>
-# endif
+#if defined(WIN32) || defined(_WIN32)
+# include <windows.h>
+# include <string>
+#endif
using namespace std;
namespace Manta {
-# if defined(WIN32) || defined(_WIN32)
+#if defined(WIN32) || defined(_WIN32)
static wstring stringToWstring(const char *str)
{
const int length_wc = MultiByteToWideChar(CP_UTF8, 0, str, strlen(str), NULL, 0);
@@ -40,10 +45,11 @@ 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)
{
+#if NO_ZLIB != 1
gzFile gzfile;
# if defined(WIN32) || defined(_WIN32)
@@ -54,7 +60,58 @@ void *safeGzopen(const char *filename, const char *mode)
# endif
return gzfile;
+#else
+ debMsg("safeGzopen not supported without zlib", 1);
+ return nullptr;
+#endif // NO_ZLIB != 1
}
-#endif
-} // namespace
+#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 Manta
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..ce088d6c400 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 841bfd09c068dfb95637c0ec14fa78305286a433"
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/mesh.cpp b/extern/mantaflow/preprocessed/mesh.cpp
index d93c2ac04c0..6a9b0283bef 100644
--- a/extern/mantaflow/preprocessed/mesh.cpp
+++ b/extern/mantaflow/preprocessed/mesh.cpp
@@ -213,34 +213,36 @@ Mesh &Mesh::operator=(const Mesh &o)
return *this;
}
-void Mesh::load(string name, bool append)
+int Mesh::load(string name, bool append)
{
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 == ".gz") // assume bobj gz
- readBobjFile(name, this, append);
+ return readBobjFile(name, this, append);
else if (ext == ".obj")
- readObjFile(name, this, append);
+ return readObjFile(name, this, append);
else
errMsg("file '" + name + "' filetype not supported");
// dont always rebuild...
// rebuildCorners();
// rebuildLookup();
+ return 0;
}
-void Mesh::save(string name)
+int Mesh::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 == ".obj")
- writeObjFile(name, this);
+ return writeObjFile(name, this);
else if (ext == ".gz")
- writeBobjFile(name, this);
+ return writeBobjFile(name, this);
else
errMsg("file '" + name + "' filetype not supported");
+ return 0;
}
void Mesh::fromShape(Shape &shape, bool append)
@@ -1339,8 +1341,8 @@ template<class T> void MeshDataImpl<T>::setSource(Grid<T> *grid, bool isMAC)
{
mpGridSource = grid;
mGridSourceMAC = isMAC;
- if (isMAC)
- assertMsg(dynamic_cast<MACGrid *>(grid) != NULL, "Given grid is not a valid MAC grid");
+ if (grid && isMAC)
+ assertMsg(grid->getType() & GridBase::TypeMAC, "Given grid is not a valid MAC grid");
}
template<class T> void MeshDataImpl<T>::initNewValue(IndexInt idx, Vec3 pos)
@@ -1371,38 +1373,40 @@ void Mesh::updateDataFields()
for (size_t i = 0; i < mNodes.size(); ++i) {
Vec3 pos = mNodes[i].pos;
for (IndexInt md = 0; md < (IndexInt)mMdataReal.size(); ++md)
- mMdataReal[md]->initNewValue(i, mNodes[i].pos);
+ mMdataReal[md]->initNewValue(i, pos);
for (IndexInt md = 0; md < (IndexInt)mMdataVec3.size(); ++md)
- mMdataVec3[md]->initNewValue(i, mNodes[i].pos);
+ mMdataVec3[md]->initNewValue(i, pos);
for (IndexInt md = 0; md < (IndexInt)mMdataInt.size(); ++md)
- mMdataInt[md]->initNewValue(i, mNodes[i].pos);
+ mMdataInt[md]->initNewValue(i, pos);
}
}
-template<typename T> void MeshDataImpl<T>::load(string name)
+template<typename T> int MeshDataImpl<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")
- readMdataUni<T>(name, this);
+ return readMdataUni<T>(name, this);
else if (ext == ".raw") // raw = uni for now
- readMdataUni<T>(name, this);
+ return readMdataUni<T>(name, this);
else
errMsg("mesh data '" + name + "' filetype not supported for loading");
+ return 0;
}
-template<typename T> void MeshDataImpl<T>::save(string name)
+template<typename T> int MeshDataImpl<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")
- writeMdataUni<T>(name, this);
+ return writeMdataUni<T>(name, this);
else if (ext == ".raw") // raw = uni for now
- writeMdataUni<T>(name, this);
+ return writeMdataUni<T>(name, this);
else
errMsg("mesh data '" + name + "' filetype not supported for saving");
+ return 0;
}
// specializations
diff --git a/extern/mantaflow/preprocessed/mesh.h b/extern/mantaflow/preprocessed/mesh.h
index f49619515ce..4235b9508af 100644
--- a/extern/mantaflow/preprocessed/mesh.h
+++ b/extern/mantaflow/preprocessed/mesh.h
@@ -240,215 +240,214 @@ class Mesh : public PbClass {
}
}
- void load(std::string name, bool append = false);
+ void fromShape(Shape &shape, bool append = false);
static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::load", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::fromShape", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- std::string name = _args.get<std::string>("name", 0, &_lock);
+ Shape &shape = *_args.getPtr<Shape>("shape", 0, &_lock);
bool append = _args.getOpt<bool>("append", 1, false, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
- pbo->load(name, append);
+ pbo->fromShape(shape, append);
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::load", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::fromShape", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::load", e.what());
+ pbSetError("Mesh::fromShape", e.what());
return 0;
}
}
- void fromShape(Shape &shape, bool append = false);
+ void advectInGrid(FlagGrid &flags, MACGrid &vel, int integrationMode);
static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::fromShape", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::advectInGrid", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- Shape &shape = *_args.getPtr<Shape>("shape", 0, &_lock);
- bool append = _args.getOpt<bool>("append", 1, false, &_lock);
+ FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
+ MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
+ int integrationMode = _args.get<int>("integrationMode", 2, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
- pbo->fromShape(shape, append);
+ pbo->advectInGrid(flags, vel, integrationMode);
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::fromShape", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::advectInGrid", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::fromShape", e.what());
+ pbSetError("Mesh::advectInGrid", e.what());
return 0;
}
}
- void save(std::string name);
+ void scale(Vec3 s);
static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::save", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::scale", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- std::string name = _args.get<std::string>("name", 0, &_lock);
+ Vec3 s = _args.get<Vec3>("s", 0, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
- pbo->save(name);
+ pbo->scale(s);
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::save", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::scale", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::save", e.what());
+ pbSetError("Mesh::scale", e.what());
return 0;
}
}
- void advectInGrid(FlagGrid &flags, MACGrid &vel, int integrationMode);
+ void offset(Vec3 o);
static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::advectInGrid", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::offset", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- FlagGrid &flags = *_args.getPtr<FlagGrid>("flags", 0, &_lock);
- MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
- int integrationMode = _args.get<int>("integrationMode", 2, &_lock);
+ Vec3 o = _args.get<Vec3>("o", 0, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
- pbo->advectInGrid(flags, vel, integrationMode);
+ pbo->offset(o);
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::advectInGrid", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::offset", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::advectInGrid", e.what());
+ pbSetError("Mesh::offset", e.what());
return 0;
}
}
- void scale(Vec3 s);
+ void rotate(Vec3 thetas);
static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::scale", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::rotate", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- Vec3 s = _args.get<Vec3>("s", 0, &_lock);
+ Vec3 thetas = _args.get<Vec3>("thetas", 0, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
- pbo->scale(s);
+ pbo->rotate(thetas);
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::scale", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::rotate", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::scale", e.what());
+ pbSetError("Mesh::rotate", e.what());
return 0;
}
}
- void offset(Vec3 o);
+ void computeVelocity(Mesh &oldMesh, MACGrid &vel);
static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::offset", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::computeVelocity", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- Vec3 o = _args.get<Vec3>("o", 0, &_lock);
+ Mesh &oldMesh = *_args.getPtr<Mesh>("oldMesh", 0, &_lock);
+ MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
pbo->_args.copy(_args);
_retval = getPyNone();
- pbo->offset(o);
+ pbo->computeVelocity(oldMesh, vel);
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::offset", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::computeVelocity", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::offset", e.what());
+ pbSetError("Mesh::computeVelocity", e.what());
return 0;
}
}
- void rotate(Vec3 thetas);
+ //! file io
+ int load(std::string name, bool append = false);
static PyObject *_W_8(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::rotate", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::load", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- Vec3 thetas = _args.get<Vec3>("thetas", 0, &_lock);
+ std::string name = _args.get<std::string>("name", 0, &_lock);
+ bool append = _args.getOpt<bool>("append", 1, false, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->rotate(thetas);
+ _retval = toPy(pbo->load(name, append));
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::rotate", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::load", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::rotate", e.what());
+ pbSetError("Mesh::load", e.what());
return 0;
}
}
- void computeVelocity(Mesh &oldMesh, MACGrid &vel);
+ int save(std::string name);
static PyObject *_W_9(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
Mesh *pbo = dynamic_cast<Mesh *>(Pb::objFromPy(_self));
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
- pbPreparePlugin(pbo->getParent(), "Mesh::computeVelocity", !noTiming);
+ pbPreparePlugin(pbo->getParent(), "Mesh::save", !noTiming);
PyObject *_retval = 0;
{
ArgLocker _lock;
- Mesh &oldMesh = *_args.getPtr<Mesh>("oldMesh", 0, &_lock);
- MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock);
+ std::string name = _args.get<std::string>("name", 0, &_lock);
pbo->_args.copy(_args);
- _retval = getPyNone();
- pbo->computeVelocity(oldMesh, vel);
+ _retval = toPy(pbo->save(name));
pbo->_args.check();
}
- pbFinalizePlugin(pbo->getParent(), "Mesh::computeVelocity", !noTiming);
+ pbFinalizePlugin(pbo->getParent(), "Mesh::save", !noTiming);
return _retval;
}
catch (std::exception &e) {
- pbSetError("Mesh::computeVelocity", e.what());
+ pbSetError("Mesh::save", e.what());
return 0;
}
}
@@ -1564,7 +1563,7 @@ template<class T> class MeshDataImpl : public MeshDataBase {
}
//! file io
- void save(const std::string name);
+ int save(const std::string name);
static PyObject *_W_41(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -1577,8 +1576,7 @@ template<class T> class MeshDataImpl : public MeshDataBase {
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(), "MeshDataImpl::save", !noTiming);
@@ -1590,7 +1588,7 @@ template<class T> class MeshDataImpl : public MeshDataBase {
}
}
- void load(const std::string name);
+ int load(const std::string name);
static PyObject *_W_42(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -1603,8 +1601,7 @@ template<class T> class MeshDataImpl : public MeshDataBase {
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(), "MeshDataImpl::load", !noTiming);
diff --git a/extern/mantaflow/preprocessed/mesh.h.reg.cpp b/extern/mantaflow/preprocessed/mesh.h.reg.cpp
index b2ba3e22032..664e4c3ff6e 100644
--- a/extern/mantaflow/preprocessed/mesh.h.reg.cpp
+++ b/extern/mantaflow/preprocessed/mesh.h.reg.cpp
@@ -10,14 +10,14 @@ static const Pb::Register _R_12("Mesh", "Mesh", "PbClass");
template<> const char *Namify<Mesh>::S = "Mesh";
static const Pb::Register _R_13("Mesh", "Mesh", Mesh::_W_0);
static const Pb::Register _R_14("Mesh", "clear", Mesh::_W_1);
-static const Pb::Register _R_15("Mesh", "load", Mesh::_W_2);
-static const Pb::Register _R_16("Mesh", "fromShape", Mesh::_W_3);
-static const Pb::Register _R_17("Mesh", "save", Mesh::_W_4);
-static const Pb::Register _R_18("Mesh", "advectInGrid", Mesh::_W_5);
-static const Pb::Register _R_19("Mesh", "scale", Mesh::_W_6);
-static const Pb::Register _R_20("Mesh", "offset", Mesh::_W_7);
-static const Pb::Register _R_21("Mesh", "rotate", Mesh::_W_8);
-static const Pb::Register _R_22("Mesh", "computeVelocity", Mesh::_W_9);
+static const Pb::Register _R_15("Mesh", "fromShape", Mesh::_W_2);
+static const Pb::Register _R_16("Mesh", "advectInGrid", Mesh::_W_3);
+static const Pb::Register _R_17("Mesh", "scale", Mesh::_W_4);
+static const Pb::Register _R_18("Mesh", "offset", Mesh::_W_5);
+static const Pb::Register _R_19("Mesh", "rotate", Mesh::_W_6);
+static const Pb::Register _R_20("Mesh", "computeVelocity", Mesh::_W_7);
+static const Pb::Register _R_21("Mesh", "load", Mesh::_W_8);
+static const Pb::Register _R_22("Mesh", "save", Mesh::_W_9);
static const Pb::Register _R_23("Mesh", "computeLevelset", Mesh::_W_10);
static const Pb::Register _R_24("Mesh", "getLevelset", Mesh::_W_11);
static const Pb::Register _R_25("Mesh", "applyMeshToGrid", Mesh::_W_12);
diff --git a/extern/mantaflow/preprocessed/particle.cpp b/extern/mantaflow/preprocessed/particle.cpp
index 41af5d3ba81..8c26156358d 100644
--- a/extern/mantaflow/preprocessed/particle.cpp
+++ b/extern/mantaflow/preprocessed/particle.cpp
@@ -29,7 +29,7 @@ using namespace std;
namespace Manta {
ParticleBase::ParticleBase(FluidSolver *parent)
- : PbClass(parent), mAllowCompress(true), mFreePdata(false)
+ : PbClass(parent), mMaxParticles(0), mAllowCompress(true), mFreePdata(false)
{
}
@@ -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)
@@ -347,8 +359,8 @@ template<class T> void ParticleDataImpl<T>::setSource(Grid<T> *grid, bool isMAC)
{
mpGridSource = grid;
mGridSourceMAC = isMAC;
- if (isMAC)
- assertMsg(dynamic_cast<MACGrid *>(grid) != NULL, "Given grid is not a valid MAC grid");
+ if (grid && isMAC)
+ assertMsg(grid->getType() & GridBase::TypeMAC, "Given grid is not a valid MAC grid");
}
template<class T> void ParticleDataImpl<T>::initNewValue(IndexInt idx, Vec3 pos)
@@ -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..da6733b6845 100644
--- a/extern/mantaflow/preprocessed/particle.h
+++ b/extern/mantaflow/preprocessed/particle.h
@@ -100,6 +100,17 @@ class ParticleBase : public PbClass {
//! threads)
inline void addBuffered(const Vec3 &pos, int flag = 0);
+ virtual void resize(IndexInt size)
+ {
+ assertMsg(false, "Dont use, override...");
+ return;
+ }
+ virtual void resizeAll(IndexInt size)
+ {
+ assertMsg(false, "Dont use, override...");
+ return;
+ }
+
//! particle data functions
//! create a particle data object
@@ -152,6 +163,20 @@ class ParticleBase : public PbClass {
return mPartData[i];
}
+ //! expose maximum number of particles to python
+ int mMaxParticles;
+ static PyObject *_GET_mMaxParticles(PyObject *self, void *cl)
+ {
+ ParticleBase *pbo = dynamic_cast<ParticleBase *>(Pb::objFromPy(self));
+ return toPy(pbo->mMaxParticles);
+ }
+ static int _SET_mMaxParticles(PyObject *self, PyObject *val, void *cl)
+ {
+ ParticleBase *pbo = dynamic_cast<ParticleBase *>(Pb::objFromPy(self));
+ pbo->mMaxParticles = fromPy<int>(val);
+ return 0;
+ }
+
protected:
//! new particle candidates
std::vector<Vec3> mNewBufferPos;
@@ -431,8 +456,14 @@ template<class S> class ParticleSystem : public ParticleBase {
}
//! insert buffered positions as new particles, update additional particle data
void insertBufferedParticles();
+ //! resize only the data vector, only use if you know what you're doing, otherwise use
+ //! resizeAll()
+ virtual void resize(IndexInt size)
+ {
+ mData.resize(size);
+ }
//! resize data vector, and all pdata fields
- void resizeAll(IndexInt newsize);
+ virtual void resizeAll(IndexInt size);
//! adding and deleting
inline void kill(IndexInt idx);
@@ -646,7 +677,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 +690,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 +702,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 +715,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);
@@ -879,11 +908,6 @@ class ParticleIndexSystem : public ParticleSystem<ParticleIndexData> {
return -1;
}
};
- //! we only need a resize function...
- void resize(IndexInt size)
- {
- mData.resize(size);
- }
public:
PbArgs _args;
}
@@ -1022,11 +1046,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 +1122,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 +1751,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 +1764,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 +1776,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 +1789,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);
@@ -2472,28 +2505,66 @@ template<class S> void ParticleSystem<S>::insertBufferedParticles()
for (IndexInt i = 0; i < (IndexInt)mData.size(); ++i)
mData[i].flag &= ~PNEW;
- if (mNewBufferPos.size() == 0)
+ if (mNewBufferPos.empty())
return;
- IndexInt newCnt = mData.size();
- resizeAll(newCnt + mNewBufferPos.size());
-
- for (IndexInt i = 0; i < (IndexInt)mNewBufferPos.size(); ++i) {
- int flag = (mNewBufferFlag.size() > 0) ? mNewBufferFlag[i] : 0;
- // note, other fields are not initialized here...
- mData[newCnt].pos = mNewBufferPos[i];
- mData[newCnt].flag = PNEW | flag;
+ IndexInt bufferSize = mNewBufferPos.size();
+ IndexInt partsSize = mData.size();
+
+ if (mMaxParticles > 0)
+ assertMsg(mMaxParticles >= partsSize,
+ "Particle system cannot contain more particles that the maximum allowed number");
+
+ // max number of new particles that can be inserted, adjusted buffer size when using maxParticles
+ // field
+ IndexInt numNewParts = (mMaxParticles > 0) ? mMaxParticles - mData.size() : bufferSize;
+ if (numNewParts > bufferSize)
+ numNewParts = bufferSize; // upper clamp
+
+ assertMsg(numNewParts >= 0, "Must not have negative number of new particles");
+
+ // new size of particle system
+ IndexInt newSize = mData.size() + numNewParts;
+ if (mMaxParticles > 0)
+ assertMsg(newSize <= mMaxParticles,
+ "Particle system cannot contain more particles that the maximum allowed number");
+ resizeAll(newSize);
+
+ int insertFlag;
+ Vec3 insertPos;
+ static RandomStream mRand(9832);
+ for (IndexInt i = 0; i < numNewParts; ++i) {
+
+ // get random index in newBuffer vector
+ // we are inserting particles randomly so that they are sampled uniformly in the fluid region
+ // otherwise, regions of fluid can remain completely empty once mData.size() == maxParticles is
+ // reached.
+ int randIndex = floor(mRand.getReal() * mNewBufferPos.size());
+
+ // get elements from new buffers with random index
+ std::swap(mNewBufferPos[randIndex], mNewBufferPos.back());
+ insertPos = mNewBufferPos.back();
+ mNewBufferPos.pop_back();
+
+ insertFlag = 0;
+ if (!mNewBufferFlag.empty()) {
+ std::swap(mNewBufferFlag[randIndex], mNewBufferFlag.back());
+ insertFlag = mNewBufferFlag.back();
+ mNewBufferFlag.pop_back();
+ }
+
+ mData[partsSize].pos = insertPos;
+ mData[partsSize].flag = PNEW | insertFlag;
+
// now init pdata fields from associated grids...
for (IndexInt pd = 0; pd < (IndexInt)mPdataReal.size(); ++pd)
- mPdataReal[pd]->initNewValue(newCnt, mNewBufferPos[i]);
+ mPdataReal[pd]->initNewValue(partsSize, insertPos);
for (IndexInt pd = 0; pd < (IndexInt)mPdataVec3.size(); ++pd)
- mPdataVec3[pd]->initNewValue(newCnt, mNewBufferPos[i]);
+ mPdataVec3[pd]->initNewValue(partsSize, insertPos);
for (IndexInt pd = 0; pd < (IndexInt)mPdataInt.size(); ++pd)
- mPdataInt[pd]->initNewValue(newCnt, mNewBufferPos[i]);
- newCnt++;
+ mPdataInt[pd]->initNewValue(partsSize, insertPos);
+ partsSize++;
}
- if (mNewBufferPos.size() > 0)
- debMsg("Added & initialized " << (IndexInt)mNewBufferPos.size() << " particles",
- 2); // debug info
+ debMsg("Added & initialized " << numNewParts << " particles", 2); // debug info
mNewBufferPos.clear();
mNewBufferFlag.clear();
}
diff --git a/extern/mantaflow/preprocessed/particle.h.reg.cpp b/extern/mantaflow/preprocessed/particle.h.reg.cpp
index 6e0466d0203..e9e538ad097 100644
--- a/extern/mantaflow/preprocessed/particle.h.reg.cpp
+++ b/extern/mantaflow/preprocessed/particle.h.reg.cpp
@@ -29,279 +29,283 @@ static const Pb::Register _R_21("ParticleBase", "ParticleBase", "PbClass");
template<> const char *Namify<ParticleBase>::S = "ParticleBase";
static const Pb::Register _R_22("ParticleBase", "ParticleBase", ParticleBase::_W_0);
static const Pb::Register _R_23("ParticleBase", "create", ParticleBase::_W_1);
+static const Pb::Register _R_24("ParticleBase",
+ "maxParticles",
+ ParticleBase::_GET_mMaxParticles,
+ ParticleBase::_SET_mMaxParticles);
#endif
#ifdef _C_ParticleDataBase
-static const Pb::Register _R_24("ParticleDataBase", "ParticleDataBase", "PbClass");
+static const Pb::Register _R_25("ParticleDataBase", "ParticleDataBase", "PbClass");
template<> const char *Namify<ParticleDataBase>::S = "ParticleDataBase";
-static const Pb::Register _R_25("ParticleDataBase", "ParticleDataBase", ParticleDataBase::_W_21);
+static const Pb::Register _R_26("ParticleDataBase", "ParticleDataBase", ParticleDataBase::_W_21);
#endif
#ifdef _C_ParticleDataImpl
-static const Pb::Register _R_26("ParticleDataImpl<int>",
+static const Pb::Register _R_27("ParticleDataImpl<int>",
"ParticleDataImpl<int>",
"ParticleDataBase");
template<> const char *Namify<ParticleDataImpl<int>>::S = "ParticleDataImpl<int>";
-static const Pb::Register _R_27("ParticleDataImpl<int>",
+static const Pb::Register _R_28("ParticleDataImpl<int>",
"ParticleDataImpl",
ParticleDataImpl<int>::_W_22);
-static const Pb::Register _R_28("ParticleDataImpl<int>", "clear", ParticleDataImpl<int>::_W_23);
-static const Pb::Register _R_29("ParticleDataImpl<int>",
+static const Pb::Register _R_29("ParticleDataImpl<int>", "clear", ParticleDataImpl<int>::_W_23);
+static const Pb::Register _R_30("ParticleDataImpl<int>",
"setSource",
ParticleDataImpl<int>::_W_24);
-static const Pb::Register _R_30("ParticleDataImpl<int>", "copyFrom", ParticleDataImpl<int>::_W_25);
-static const Pb::Register _R_31("ParticleDataImpl<int>", "setConst", ParticleDataImpl<int>::_W_26);
-static const Pb::Register _R_32("ParticleDataImpl<int>",
+static const Pb::Register _R_31("ParticleDataImpl<int>", "copyFrom", ParticleDataImpl<int>::_W_25);
+static const Pb::Register _R_32("ParticleDataImpl<int>", "setConst", ParticleDataImpl<int>::_W_26);
+static const Pb::Register _R_33("ParticleDataImpl<int>",
"setConstRange",
ParticleDataImpl<int>::_W_27);
-static const Pb::Register _R_33("ParticleDataImpl<int>", "add", ParticleDataImpl<int>::_W_28);
-static const Pb::Register _R_34("ParticleDataImpl<int>", "sub", ParticleDataImpl<int>::_W_29);
-static const Pb::Register _R_35("ParticleDataImpl<int>", "addConst", ParticleDataImpl<int>::_W_30);
-static const Pb::Register _R_36("ParticleDataImpl<int>",
+static const Pb::Register _R_34("ParticleDataImpl<int>", "add", ParticleDataImpl<int>::_W_28);
+static const Pb::Register _R_35("ParticleDataImpl<int>", "sub", ParticleDataImpl<int>::_W_29);
+static const Pb::Register _R_36("ParticleDataImpl<int>", "addConst", ParticleDataImpl<int>::_W_30);
+static const Pb::Register _R_37("ParticleDataImpl<int>",
"addScaled",
ParticleDataImpl<int>::_W_31);
-static const Pb::Register _R_37("ParticleDataImpl<int>", "mult", ParticleDataImpl<int>::_W_32);
-static const Pb::Register _R_38("ParticleDataImpl<int>",
+static const Pb::Register _R_38("ParticleDataImpl<int>", "mult", ParticleDataImpl<int>::_W_32);
+static const Pb::Register _R_39("ParticleDataImpl<int>",
"multConst",
ParticleDataImpl<int>::_W_33);
-static const Pb::Register _R_39("ParticleDataImpl<int>", "safeDiv", ParticleDataImpl<int>::_W_34);
-static const Pb::Register _R_40("ParticleDataImpl<int>", "clamp", ParticleDataImpl<int>::_W_35);
-static const Pb::Register _R_41("ParticleDataImpl<int>", "clampMin", ParticleDataImpl<int>::_W_36);
-static const Pb::Register _R_42("ParticleDataImpl<int>", "clampMax", ParticleDataImpl<int>::_W_37);
-static const Pb::Register _R_43("ParticleDataImpl<int>",
+static const Pb::Register _R_40("ParticleDataImpl<int>", "safeDiv", ParticleDataImpl<int>::_W_34);
+static const Pb::Register _R_41("ParticleDataImpl<int>", "clamp", ParticleDataImpl<int>::_W_35);
+static const Pb::Register _R_42("ParticleDataImpl<int>", "clampMin", ParticleDataImpl<int>::_W_36);
+static const Pb::Register _R_43("ParticleDataImpl<int>", "clampMax", ParticleDataImpl<int>::_W_37);
+static const Pb::Register _R_44("ParticleDataImpl<int>",
"getMaxAbs",
ParticleDataImpl<int>::_W_38);
-static const Pb::Register _R_44("ParticleDataImpl<int>", "getMax", ParticleDataImpl<int>::_W_39);
-static const Pb::Register _R_45("ParticleDataImpl<int>", "getMin", ParticleDataImpl<int>::_W_40);
-static const Pb::Register _R_46("ParticleDataImpl<int>", "sum", ParticleDataImpl<int>::_W_41);
-static const Pb::Register _R_47("ParticleDataImpl<int>",
+static const Pb::Register _R_45("ParticleDataImpl<int>", "getMax", ParticleDataImpl<int>::_W_39);
+static const Pb::Register _R_46("ParticleDataImpl<int>", "getMin", ParticleDataImpl<int>::_W_40);
+static const Pb::Register _R_47("ParticleDataImpl<int>", "sum", ParticleDataImpl<int>::_W_41);
+static const Pb::Register _R_48("ParticleDataImpl<int>",
"sumSquare",
ParticleDataImpl<int>::_W_42);
-static const Pb::Register _R_48("ParticleDataImpl<int>",
+static const Pb::Register _R_49("ParticleDataImpl<int>",
"sumMagnitude",
ParticleDataImpl<int>::_W_43);
-static const Pb::Register _R_49("ParticleDataImpl<int>",
+static const Pb::Register _R_50("ParticleDataImpl<int>",
"setConstIntFlag",
ParticleDataImpl<int>::_W_44);
-static const Pb::Register _R_50("ParticleDataImpl<int>",
+static const Pb::Register _R_51("ParticleDataImpl<int>",
"printPdata",
ParticleDataImpl<int>::_W_45);
-static const Pb::Register _R_51("ParticleDataImpl<int>", "save", ParticleDataImpl<int>::_W_46);
-static const Pb::Register _R_52("ParticleDataImpl<int>", "load", ParticleDataImpl<int>::_W_47);
-static const Pb::Register _R_53("ParticleDataImpl<int>",
+static const Pb::Register _R_52("ParticleDataImpl<int>", "save", ParticleDataImpl<int>::_W_46);
+static const Pb::Register _R_53("ParticleDataImpl<int>", "load", ParticleDataImpl<int>::_W_47);
+static const Pb::Register _R_54("ParticleDataImpl<int>",
"getDataPointer",
ParticleDataImpl<int>::_W_48);
-static const Pb::Register _R_54("ParticleDataImpl<Real>",
+static const Pb::Register _R_55("ParticleDataImpl<Real>",
"ParticleDataImpl<Real>",
"ParticleDataBase");
template<> const char *Namify<ParticleDataImpl<Real>>::S = "ParticleDataImpl<Real>";
-static const Pb::Register _R_55("ParticleDataImpl<Real>",
+static const Pb::Register _R_56("ParticleDataImpl<Real>",
"ParticleDataImpl",
ParticleDataImpl<Real>::_W_22);
-static const Pb::Register _R_56("ParticleDataImpl<Real>", "clear", ParticleDataImpl<Real>::_W_23);
-static const Pb::Register _R_57("ParticleDataImpl<Real>",
+static const Pb::Register _R_57("ParticleDataImpl<Real>", "clear", ParticleDataImpl<Real>::_W_23);
+static const Pb::Register _R_58("ParticleDataImpl<Real>",
"setSource",
ParticleDataImpl<Real>::_W_24);
-static const Pb::Register _R_58("ParticleDataImpl<Real>",
+static const Pb::Register _R_59("ParticleDataImpl<Real>",
"copyFrom",
ParticleDataImpl<Real>::_W_25);
-static const Pb::Register _R_59("ParticleDataImpl<Real>",
+static const Pb::Register _R_60("ParticleDataImpl<Real>",
"setConst",
ParticleDataImpl<Real>::_W_26);
-static const Pb::Register _R_60("ParticleDataImpl<Real>",
+static const Pb::Register _R_61("ParticleDataImpl<Real>",
"setConstRange",
ParticleDataImpl<Real>::_W_27);
-static const Pb::Register _R_61("ParticleDataImpl<Real>", "add", ParticleDataImpl<Real>::_W_28);
-static const Pb::Register _R_62("ParticleDataImpl<Real>", "sub", ParticleDataImpl<Real>::_W_29);
-static const Pb::Register _R_63("ParticleDataImpl<Real>",
+static const Pb::Register _R_62("ParticleDataImpl<Real>", "add", ParticleDataImpl<Real>::_W_28);
+static const Pb::Register _R_63("ParticleDataImpl<Real>", "sub", ParticleDataImpl<Real>::_W_29);
+static const Pb::Register _R_64("ParticleDataImpl<Real>",
"addConst",
ParticleDataImpl<Real>::_W_30);
-static const Pb::Register _R_64("ParticleDataImpl<Real>",
+static const Pb::Register _R_65("ParticleDataImpl<Real>",
"addScaled",
ParticleDataImpl<Real>::_W_31);
-static const Pb::Register _R_65("ParticleDataImpl<Real>", "mult", ParticleDataImpl<Real>::_W_32);
-static const Pb::Register _R_66("ParticleDataImpl<Real>",
+static const Pb::Register _R_66("ParticleDataImpl<Real>", "mult", ParticleDataImpl<Real>::_W_32);
+static const Pb::Register _R_67("ParticleDataImpl<Real>",
"multConst",
ParticleDataImpl<Real>::_W_33);
-static const Pb::Register _R_67("ParticleDataImpl<Real>",
+static const Pb::Register _R_68("ParticleDataImpl<Real>",
"safeDiv",
ParticleDataImpl<Real>::_W_34);
-static const Pb::Register _R_68("ParticleDataImpl<Real>", "clamp", ParticleDataImpl<Real>::_W_35);
-static const Pb::Register _R_69("ParticleDataImpl<Real>",
+static const Pb::Register _R_69("ParticleDataImpl<Real>", "clamp", ParticleDataImpl<Real>::_W_35);
+static const Pb::Register _R_70("ParticleDataImpl<Real>",
"clampMin",
ParticleDataImpl<Real>::_W_36);
-static const Pb::Register _R_70("ParticleDataImpl<Real>",
+static const Pb::Register _R_71("ParticleDataImpl<Real>",
"clampMax",
ParticleDataImpl<Real>::_W_37);
-static const Pb::Register _R_71("ParticleDataImpl<Real>",
+static const Pb::Register _R_72("ParticleDataImpl<Real>",
"getMaxAbs",
ParticleDataImpl<Real>::_W_38);
-static const Pb::Register _R_72("ParticleDataImpl<Real>", "getMax", ParticleDataImpl<Real>::_W_39);
-static const Pb::Register _R_73("ParticleDataImpl<Real>", "getMin", ParticleDataImpl<Real>::_W_40);
-static const Pb::Register _R_74("ParticleDataImpl<Real>", "sum", ParticleDataImpl<Real>::_W_41);
-static const Pb::Register _R_75("ParticleDataImpl<Real>",
+static const Pb::Register _R_73("ParticleDataImpl<Real>", "getMax", ParticleDataImpl<Real>::_W_39);
+static const Pb::Register _R_74("ParticleDataImpl<Real>", "getMin", ParticleDataImpl<Real>::_W_40);
+static const Pb::Register _R_75("ParticleDataImpl<Real>", "sum", ParticleDataImpl<Real>::_W_41);
+static const Pb::Register _R_76("ParticleDataImpl<Real>",
"sumSquare",
ParticleDataImpl<Real>::_W_42);
-static const Pb::Register _R_76("ParticleDataImpl<Real>",
+static const Pb::Register _R_77("ParticleDataImpl<Real>",
"sumMagnitude",
ParticleDataImpl<Real>::_W_43);
-static const Pb::Register _R_77("ParticleDataImpl<Real>",
+static const Pb::Register _R_78("ParticleDataImpl<Real>",
"setConstIntFlag",
ParticleDataImpl<Real>::_W_44);
-static const Pb::Register _R_78("ParticleDataImpl<Real>",
+static const Pb::Register _R_79("ParticleDataImpl<Real>",
"printPdata",
ParticleDataImpl<Real>::_W_45);
-static const Pb::Register _R_79("ParticleDataImpl<Real>", "save", ParticleDataImpl<Real>::_W_46);
-static const Pb::Register _R_80("ParticleDataImpl<Real>", "load", ParticleDataImpl<Real>::_W_47);
-static const Pb::Register _R_81("ParticleDataImpl<Real>",
+static const Pb::Register _R_80("ParticleDataImpl<Real>", "save", ParticleDataImpl<Real>::_W_46);
+static const Pb::Register _R_81("ParticleDataImpl<Real>", "load", ParticleDataImpl<Real>::_W_47);
+static const Pb::Register _R_82("ParticleDataImpl<Real>",
"getDataPointer",
ParticleDataImpl<Real>::_W_48);
-static const Pb::Register _R_82("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_83("ParticleDataImpl<Vec3>",
"ParticleDataImpl<Vec3>",
"ParticleDataBase");
template<> const char *Namify<ParticleDataImpl<Vec3>>::S = "ParticleDataImpl<Vec3>";
-static const Pb::Register _R_83("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_84("ParticleDataImpl<Vec3>",
"ParticleDataImpl",
ParticleDataImpl<Vec3>::_W_22);
-static const Pb::Register _R_84("ParticleDataImpl<Vec3>", "clear", ParticleDataImpl<Vec3>::_W_23);
-static const Pb::Register _R_85("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_85("ParticleDataImpl<Vec3>", "clear", ParticleDataImpl<Vec3>::_W_23);
+static const Pb::Register _R_86("ParticleDataImpl<Vec3>",
"setSource",
ParticleDataImpl<Vec3>::_W_24);
-static const Pb::Register _R_86("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_87("ParticleDataImpl<Vec3>",
"copyFrom",
ParticleDataImpl<Vec3>::_W_25);
-static const Pb::Register _R_87("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_88("ParticleDataImpl<Vec3>",
"setConst",
ParticleDataImpl<Vec3>::_W_26);
-static const Pb::Register _R_88("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_89("ParticleDataImpl<Vec3>",
"setConstRange",
ParticleDataImpl<Vec3>::_W_27);
-static const Pb::Register _R_89("ParticleDataImpl<Vec3>", "add", ParticleDataImpl<Vec3>::_W_28);
-static const Pb::Register _R_90("ParticleDataImpl<Vec3>", "sub", ParticleDataImpl<Vec3>::_W_29);
-static const Pb::Register _R_91("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_90("ParticleDataImpl<Vec3>", "add", ParticleDataImpl<Vec3>::_W_28);
+static const Pb::Register _R_91("ParticleDataImpl<Vec3>", "sub", ParticleDataImpl<Vec3>::_W_29);
+static const Pb::Register _R_92("ParticleDataImpl<Vec3>",
"addConst",
ParticleDataImpl<Vec3>::_W_30);
-static const Pb::Register _R_92("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_93("ParticleDataImpl<Vec3>",
"addScaled",
ParticleDataImpl<Vec3>::_W_31);
-static const Pb::Register _R_93("ParticleDataImpl<Vec3>", "mult", ParticleDataImpl<Vec3>::_W_32);
-static const Pb::Register _R_94("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_94("ParticleDataImpl<Vec3>", "mult", ParticleDataImpl<Vec3>::_W_32);
+static const Pb::Register _R_95("ParticleDataImpl<Vec3>",
"multConst",
ParticleDataImpl<Vec3>::_W_33);
-static const Pb::Register _R_95("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_96("ParticleDataImpl<Vec3>",
"safeDiv",
ParticleDataImpl<Vec3>::_W_34);
-static const Pb::Register _R_96("ParticleDataImpl<Vec3>", "clamp", ParticleDataImpl<Vec3>::_W_35);
-static const Pb::Register _R_97("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_97("ParticleDataImpl<Vec3>", "clamp", ParticleDataImpl<Vec3>::_W_35);
+static const Pb::Register _R_98("ParticleDataImpl<Vec3>",
"clampMin",
ParticleDataImpl<Vec3>::_W_36);
-static const Pb::Register _R_98("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_99("ParticleDataImpl<Vec3>",
"clampMax",
ParticleDataImpl<Vec3>::_W_37);
-static const Pb::Register _R_99("ParticleDataImpl<Vec3>",
- "getMaxAbs",
- ParticleDataImpl<Vec3>::_W_38);
static const Pb::Register _R_100("ParticleDataImpl<Vec3>",
+ "getMaxAbs",
+ ParticleDataImpl<Vec3>::_W_38);
+static const Pb::Register _R_101("ParticleDataImpl<Vec3>",
"getMax",
ParticleDataImpl<Vec3>::_W_39);
-static const Pb::Register _R_101("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_102("ParticleDataImpl<Vec3>",
"getMin",
ParticleDataImpl<Vec3>::_W_40);
-static const Pb::Register _R_102("ParticleDataImpl<Vec3>", "sum", ParticleDataImpl<Vec3>::_W_41);
-static const Pb::Register _R_103("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_103("ParticleDataImpl<Vec3>", "sum", ParticleDataImpl<Vec3>::_W_41);
+static const Pb::Register _R_104("ParticleDataImpl<Vec3>",
"sumSquare",
ParticleDataImpl<Vec3>::_W_42);
-static const Pb::Register _R_104("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_105("ParticleDataImpl<Vec3>",
"sumMagnitude",
ParticleDataImpl<Vec3>::_W_43);
-static const Pb::Register _R_105("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_106("ParticleDataImpl<Vec3>",
"setConstIntFlag",
ParticleDataImpl<Vec3>::_W_44);
-static const Pb::Register _R_106("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_107("ParticleDataImpl<Vec3>",
"printPdata",
ParticleDataImpl<Vec3>::_W_45);
-static const Pb::Register _R_107("ParticleDataImpl<Vec3>", "save", ParticleDataImpl<Vec3>::_W_46);
-static const Pb::Register _R_108("ParticleDataImpl<Vec3>", "load", ParticleDataImpl<Vec3>::_W_47);
-static const Pb::Register _R_109("ParticleDataImpl<Vec3>",
+static const Pb::Register _R_108("ParticleDataImpl<Vec3>", "save", ParticleDataImpl<Vec3>::_W_46);
+static const Pb::Register _R_109("ParticleDataImpl<Vec3>", "load", ParticleDataImpl<Vec3>::_W_47);
+static const Pb::Register _R_110("ParticleDataImpl<Vec3>",
"getDataPointer",
ParticleDataImpl<Vec3>::_W_48);
#endif
#ifdef _C_ParticleIndexSystem
-static const Pb::Register _R_110("ParticleIndexSystem",
+static const Pb::Register _R_111("ParticleIndexSystem",
"ParticleIndexSystem",
"ParticleSystem<ParticleIndexData>");
template<> const char *Namify<ParticleIndexSystem>::S = "ParticleIndexSystem";
-static const Pb::Register _R_111("ParticleIndexSystem",
+static const Pb::Register _R_112("ParticleIndexSystem",
"ParticleIndexSystem",
ParticleIndexSystem::_W_19);
#endif
#ifdef _C_ParticleSystem
-static const Pb::Register _R_112("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_113("ParticleSystem<BasicParticleData>",
"ParticleSystem<BasicParticleData>",
"ParticleBase");
template<>
const char *Namify<ParticleSystem<BasicParticleData>>::S = "ParticleSystem<BasicParticleData>";
-static const Pb::Register _R_113("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_114("ParticleSystem<BasicParticleData>",
"ParticleSystem",
ParticleSystem<BasicParticleData>::_W_2);
-static const Pb::Register _R_114("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_115("ParticleSystem<BasicParticleData>",
"pySize",
ParticleSystem<BasicParticleData>::_W_3);
-static const Pb::Register _R_115("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_116("ParticleSystem<BasicParticleData>",
"setPos",
ParticleSystem<BasicParticleData>::_W_4);
-static const Pb::Register _R_116("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_117("ParticleSystem<BasicParticleData>",
"getPos",
ParticleSystem<BasicParticleData>::_W_5);
-static const Pb::Register _R_117("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_118("ParticleSystem<BasicParticleData>",
"getPosPdata",
ParticleSystem<BasicParticleData>::_W_6);
-static const Pb::Register _R_118("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_119("ParticleSystem<BasicParticleData>",
"setPosPdata",
ParticleSystem<BasicParticleData>::_W_7);
-static const Pb::Register _R_119("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_120("ParticleSystem<BasicParticleData>",
"clear",
ParticleSystem<BasicParticleData>::_W_8);
-static const Pb::Register _R_120("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_121("ParticleSystem<BasicParticleData>",
"advectInGrid",
ParticleSystem<BasicParticleData>::_W_9);
-static const Pb::Register _R_121("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_122("ParticleSystem<BasicParticleData>",
"projectOutside",
ParticleSystem<BasicParticleData>::_W_10);
-static const Pb::Register _R_122("ParticleSystem<BasicParticleData>",
+static const Pb::Register _R_123("ParticleSystem<BasicParticleData>",
"projectOutOfBnd",
ParticleSystem<BasicParticleData>::_W_11);
-static const Pb::Register _R_123("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_124("ParticleSystem<ParticleIndexData>",
"ParticleSystem<ParticleIndexData>",
"ParticleBase");
template<>
const char *Namify<ParticleSystem<ParticleIndexData>>::S = "ParticleSystem<ParticleIndexData>";
-static const Pb::Register _R_124("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_125("ParticleSystem<ParticleIndexData>",
"ParticleSystem",
ParticleSystem<ParticleIndexData>::_W_2);
-static const Pb::Register _R_125("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_126("ParticleSystem<ParticleIndexData>",
"pySize",
ParticleSystem<ParticleIndexData>::_W_3);
-static const Pb::Register _R_126("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_127("ParticleSystem<ParticleIndexData>",
"setPos",
ParticleSystem<ParticleIndexData>::_W_4);
-static const Pb::Register _R_127("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_128("ParticleSystem<ParticleIndexData>",
"getPos",
ParticleSystem<ParticleIndexData>::_W_5);
-static const Pb::Register _R_128("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_129("ParticleSystem<ParticleIndexData>",
"getPosPdata",
ParticleSystem<ParticleIndexData>::_W_6);
-static const Pb::Register _R_129("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_130("ParticleSystem<ParticleIndexData>",
"setPosPdata",
ParticleSystem<ParticleIndexData>::_W_7);
-static const Pb::Register _R_130("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_131("ParticleSystem<ParticleIndexData>",
"clear",
ParticleSystem<ParticleIndexData>::_W_8);
-static const Pb::Register _R_131("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_132("ParticleSystem<ParticleIndexData>",
"advectInGrid",
ParticleSystem<ParticleIndexData>::_W_9);
-static const Pb::Register _R_132("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_133("ParticleSystem<ParticleIndexData>",
"projectOutside",
ParticleSystem<ParticleIndexData>::_W_10);
-static const Pb::Register _R_133("ParticleSystem<ParticleIndexData>",
+static const Pb::Register _R_134("ParticleSystem<ParticleIndexData>",
"projectOutOfBnd",
ParticleSystem<ParticleIndexData>::_W_11);
#endif
@@ -432,6 +436,7 @@ void PbRegister_file_10()
KEEP_UNUSED(_R_131);
KEEP_UNUSED(_R_132);
KEEP_UNUSED(_R_133);
+ KEEP_UNUSED(_R_134);
}
}
} // namespace Manta \ No newline at end of file
diff --git a/extern/mantaflow/preprocessed/plugin/initplugins.cpp b/extern/mantaflow/preprocessed/plugin/initplugins.cpp
index 7a765813f9f..6ccd3afc8d1 100644
--- a/extern/mantaflow/preprocessed/plugin/initplugins.cpp
+++ b/extern/mantaflow/preprocessed/plugin/initplugins.cpp
@@ -1479,48 +1479,24 @@ void PbRegister_addTestParts()
}
//! calculate the difference between two pdata fields (note - slow!, not parallelized)
-
-Real pdataMaxDiff(const ParticleDataBase *a, const ParticleDataBase *b)
+template<class T> Real getPdataMaxDiff(const ParticleDataImpl<T> *a, const ParticleDataImpl<T> *b)
{
- double maxVal = 0.;
- // debMsg(" PD "<< a->getType()<<" as"<<a->getSizeSlow()<<" bs"<<b->getSizeSlow() , 1);
assertMsg(a->getType() == b->getType(), "pdataMaxDiff problem - different pdata types!");
assertMsg(a->getSizeSlow() == b->getSizeSlow(), "pdataMaxDiff problem - different pdata sizes!");
- if (a->getType() & ParticleDataBase::TypeReal) {
- const ParticleDataImpl<Real> &av = *dynamic_cast<const ParticleDataImpl<Real> *>(a);
- const ParticleDataImpl<Real> &bv = *dynamic_cast<const ParticleDataImpl<Real> *>(b);
- FOR_PARTS(av)
- {
- maxVal = std::max(maxVal, (double)fabs(av[idx] - bv[idx]));
- }
- }
- else if (a->getType() & ParticleDataBase::TypeInt) {
- const ParticleDataImpl<int> &av = *dynamic_cast<const ParticleDataImpl<int> *>(a);
- const ParticleDataImpl<int> &bv = *dynamic_cast<const ParticleDataImpl<int> *>(b);
- FOR_PARTS(av)
- {
- maxVal = std::max(maxVal, (double)fabs((double)av[idx] - bv[idx]));
- }
- }
- else if (a->getType() & ParticleDataBase::TypeVec3) {
- const ParticleDataImpl<Vec3> &av = *dynamic_cast<const ParticleDataImpl<Vec3> *>(a);
- const ParticleDataImpl<Vec3> &bv = *dynamic_cast<const ParticleDataImpl<Vec3> *>(b);
- FOR_PARTS(av)
- {
- double d = 0.;
- for (int c = 0; c < 3; ++c) {
- d += fabs((double)av[idx][c] - (double)bv[idx][c]);
- }
- maxVal = std::max(maxVal, d);
- }
- }
- else {
- errMsg("pdataMaxDiff: Grid Type is not supported (only Real, Vec3, int)");
+ Real maxVal = 0.;
+ FOR_PARTS(*a)
+ {
+ T diff = a->get(idx) - b->get(idx);
+ Real s = (Real)sum(abs(diff));
+ maxVal = std::max(maxVal, s);
}
-
return maxVal;
}
+Real pdataMaxDiff(const ParticleDataImpl<Real> *a, const ParticleDataImpl<Real> *b)
+{
+ return getPdataMaxDiff(a, b);
+}
static PyObject *_W_15(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
@@ -1531,8 +1507,8 @@ static PyObject *_W_15(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
PyObject *_retval = 0;
{
ArgLocker _lock;
- const ParticleDataBase *a = _args.getPtr<ParticleDataBase>("a", 0, &_lock);
- const ParticleDataBase *b = _args.getPtr<ParticleDataBase>("b", 1, &_lock);
+ const ParticleDataImpl<Real> *a = _args.getPtr<ParticleDataImpl<Real>>("a", 0, &_lock);
+ const ParticleDataImpl<Real> *b = _args.getPtr<ParticleDataImpl<Real>>("b", 1, &_lock);
_retval = toPy(pdataMaxDiff(a, b));
_args.check();
}
@@ -1552,6 +1528,76 @@ void PbRegister_pdataMaxDiff()
}
}
+Real pdataMaxDiffInt(const ParticleDataImpl<int> *a, const ParticleDataImpl<int> *b)
+{
+ return getPdataMaxDiff(a, b);
+}
+static PyObject *_W_16(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, "pdataMaxDiffInt", !noTiming);
+ PyObject *_retval = 0;
+ {
+ ArgLocker _lock;
+ const ParticleDataImpl<int> *a = _args.getPtr<ParticleDataImpl<int>>("a", 0, &_lock);
+ const ParticleDataImpl<int> *b = _args.getPtr<ParticleDataImpl<int>>("b", 1, &_lock);
+ _retval = toPy(pdataMaxDiffInt(a, b));
+ _args.check();
+ }
+ pbFinalizePlugin(parent, "pdataMaxDiffInt", !noTiming);
+ return _retval;
+ }
+ catch (std::exception &e) {
+ pbSetError("pdataMaxDiffInt", e.what());
+ return 0;
+ }
+}
+static const Pb::Register _RP_pdataMaxDiffInt("", "pdataMaxDiffInt", _W_16);
+extern "C" {
+void PbRegister_pdataMaxDiffInt()
+{
+ KEEP_UNUSED(_RP_pdataMaxDiffInt);
+}
+}
+
+Real pdataMaxDiffVec3(const ParticleDataImpl<Vec3> *a, const ParticleDataImpl<Vec3> *b)
+{
+ return getPdataMaxDiff(a, b);
+}
+static PyObject *_W_17(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, "pdataMaxDiffVec3", !noTiming);
+ PyObject *_retval = 0;
+ {
+ ArgLocker _lock;
+ const ParticleDataImpl<Vec3> *a = _args.getPtr<ParticleDataImpl<Vec3>>("a", 0, &_lock);
+ const ParticleDataImpl<Vec3> *b = _args.getPtr<ParticleDataImpl<Vec3>>("b", 1, &_lock);
+ _retval = toPy(pdataMaxDiffVec3(a, b));
+ _args.check();
+ }
+ pbFinalizePlugin(parent, "pdataMaxDiffVec3", !noTiming);
+ return _retval;
+ }
+ catch (std::exception &e) {
+ pbSetError("pdataMaxDiffVec3", e.what());
+ return 0;
+ }
+}
+static const Pb::Register _RP_pdataMaxDiffVec3("", "pdataMaxDiffVec3", _W_17);
+extern "C" {
+void PbRegister_pdataMaxDiffVec3()
+{
+ KEEP_UNUSED(_RP_pdataMaxDiffVec3);
+}
+}
+
//! calculate center of mass given density grid, for re-centering
Vec3 calcCenterOfMass(const Grid<Real> &density)
@@ -1567,7 +1613,7 @@ Vec3 calcCenterOfMass(const Grid<Real> &density)
p /= w;
return p;
}
-static PyObject *_W_16(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1589,7 +1635,7 @@ static PyObject *_W_16(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
return 0;
}
}
-static const Pb::Register _RP_calcCenterOfMass("", "calcCenterOfMass", _W_16);
+static const Pb::Register _RP_calcCenterOfMass("", "calcCenterOfMass", _W_18);
extern "C" {
void PbRegister_calcCenterOfMass()
{
@@ -1789,7 +1835,7 @@ void updateFractions(const FlagGrid &flags,
fractions.setConst(Vec3(0.));
KnUpdateFractions(flags, phiObs, fractions, boundaryWidth, fracThreshold);
}
-static PyObject *_W_17(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1816,7 +1862,7 @@ static PyObject *_W_17(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
return 0;
}
}
-static const Pb::Register _RP_updateFractions("", "updateFractions", _W_17);
+static const Pb::Register _RP_updateFractions("", "updateFractions", _W_19);
extern "C" {
void PbRegister_updateFractions()
{
@@ -1968,7 +2014,7 @@ void setObstacleFlags(FlagGrid &flags,
{
KnUpdateFlagsObs(flags, fractions, phiObs, phiOut, phiIn, boundaryWidth);
}
-static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1996,7 +2042,7 @@ static PyObject *_W_18(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
return 0;
}
}
-static const Pb::Register _RP_setObstacleFlags("", "setObstacleFlags", _W_18);
+static const Pb::Register _RP_setObstacleFlags("", "setObstacleFlags", _W_20);
extern "C" {
void PbRegister_setObstacleFlags()
{
@@ -2113,7 +2159,7 @@ void initVortexVelocity(const Grid<Real> &phiObs,
{
kninitVortexVelocity(phiObs, vel, center, radius);
}
-static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+static PyObject *_W_21(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -2139,7 +2185,7 @@ static PyObject *_W_19(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
return 0;
}
}
-static const Pb::Register _RP_initVortexVelocity("", "initVortexVelocity", _W_19);
+static const Pb::Register _RP_initVortexVelocity("", "initVortexVelocity", _W_21);
extern "C" {
void PbRegister_initVortexVelocity()
{
@@ -2465,7 +2511,7 @@ int blurMacGrid(MACGrid &oG, MACGrid &tG, float si)
}
return tmGK.mDim;
}
-static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+static PyObject *_W_22(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -2489,7 +2535,7 @@ static PyObject *_W_20(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
return 0;
}
}
-static const Pb::Register _RP_blurMacGrid("", "blurMacGrid", _W_20);
+static const Pb::Register _RP_blurMacGrid("", "blurMacGrid", _W_22);
extern "C" {
void PbRegister_blurMacGrid()
{
@@ -2501,7 +2547,7 @@ int blurRealGrid(Grid<Real> &oG, Grid<Real> &tG, float si)
{
return blurGrid<Real>(oG, tG, si);
}
-static PyObject *_W_21(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+static PyObject *_W_23(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -2525,7 +2571,7 @@ static PyObject *_W_21(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
return 0;
}
}
-static const Pb::Register _RP_blurRealGrid("", "blurRealGrid", _W_21);
+static const Pb::Register _RP_blurRealGrid("", "blurRealGrid", _W_23);
extern "C" {
void PbRegister_blurRealGrid()
{
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..d5dae479f0e 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();
@@ -109,6 +111,8 @@ extern void PbRegister_checkSymmetryVec3();
extern void PbRegister_projectPpmFull();
extern void PbRegister_addTestParts();
extern void PbRegister_pdataMaxDiff();
+extern void PbRegister_pdataMaxDiffInt();
+extern void PbRegister_pdataMaxDiffVec3();
extern void PbRegister_calcCenterOfMass();
extern void PbRegister_updateFractions();
extern void PbRegister_setObstacleFlags();
@@ -238,6 +242,8 @@ void MantaEnsureRegistration()
PbRegister_getNpzFileSize();
PbRegister_quantizeGrid();
PbRegister_quantizeGridVec3();
+ PbRegister_load();
+ PbRegister_save();
PbRegister_resetPhiInObs();
PbRegister_advectSemiLagrange();
PbRegister_addGravity();
@@ -302,6 +308,8 @@ void MantaEnsureRegistration()
PbRegister_projectPpmFull();
PbRegister_addTestParts();
PbRegister_pdataMaxDiff();
+ PbRegister_pdataMaxDiffInt();
+ PbRegister_pdataMaxDiffVec3();
PbRegister_calcCenterOfMass();
PbRegister_updateFractions();
PbRegister_setObstacleFlags();
diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt
index fa18f4d793a..0758567bb78 100644
--- a/intern/CMakeLists.txt
+++ b/intern/CMakeLists.txt
@@ -30,6 +30,7 @@ add_subdirectory(opensubdiv)
add_subdirectory(mikktspace)
add_subdirectory(glew-mx)
add_subdirectory(eigen)
+add_subdirectory(sky)
if(WITH_AUDASPACE)
add_subdirectory(audaspace)
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
index 921ee17a672..d384b9a89e6 100644
--- a/intern/clog/clog.c
+++ b/intern/clog/clog.c
@@ -153,7 +153,6 @@ static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
cstr->data = data;
cstr->is_alloc = true;
}
- cstr->len_alloc = len;
}
}
@@ -179,26 +178,34 @@ static void clg_str_vappendf(CLogStringBuf *cstr, const char *fmt, va_list args)
{
/* Use limit because windows may use '-1' for a formatting error. */
const uint len_max = 65535;
- uint len_avail = (cstr->len_alloc - cstr->len);
- if (len_avail == 0) {
- len_avail = CLOG_BUF_LEN_INIT;
- clg_str_reserve(cstr, len_avail);
- }
while (true) {
+ uint len_avail = cstr->len_alloc - cstr->len;
+
va_list args_cpy;
va_copy(args_cpy, args);
int retval = vsnprintf(cstr->data + cstr->len, len_avail, fmt, args_cpy);
va_end(args_cpy);
- if (retval != -1) {
- cstr->len += retval;
+
+ if (retval < 0) {
+ /* Some encoding error happened, not much we can do here, besides skipping/cancelling this
+ * message. */
+ break;
+ }
+ else if ((uint)retval <= len_avail) {
+ /* Copy was successful. */
+ cstr->len += (uint)retval;
break;
}
else {
- len_avail *= 2;
- if (len_avail >= len_max) {
+ /* vsnprintf was not successful, due to lack of allocated space, retval contains expected
+ * length of the formated string, use it to allocate required amount of memory. */
+ uint len_alloc = cstr->len + (uint)retval;
+ if (len_alloc >= len_max) {
+ /* Safe upper-limit, just in case... */
break;
}
- clg_str_reserve(cstr, len_avail);
+ clg_str_reserve(cstr, len_alloc);
+ len_avail = cstr->len_alloc - cstr->len;
}
}
}
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/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index ef374f91a65..a2b0ed03925 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -35,7 +35,7 @@ if(WITH_CYCLES_OSL)
endif()
if(NOT CYCLES_STANDALONE_REPOSITORY)
- list(APPEND LIBRARIES bf_intern_glew_mx bf_intern_guardedalloc bf_intern_numaapi)
+ list(APPEND LIBRARIES bf_intern_glew_mx bf_intern_guardedalloc bf_intern_numaapi bf_intern_sky)
endif()
if(WITH_CYCLES_LOGGING)
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..67e448db859 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()
@@ -223,7 +224,7 @@ def system_info():
import _cycles
return _cycles.system_info()
-def list_render_passes(srl):
+def list_render_passes(scene, srl):
# Builtin Blender passes.
yield ("Combined", "RGBA", 'COLOR')
@@ -278,14 +279,17 @@ def list_render_passes(srl):
yield ("Denoising Normal", "XYZ", 'VECTOR')
yield ("Denoising Albedo", "RGB", 'COLOR')
yield ("Denoising Depth", "Z", 'VALUE')
- yield ("Denoising Shadowing", "X", 'VALUE')
- yield ("Denoising Variance", "RGB", 'COLOR')
- yield ("Denoising Intensity", "X", 'VALUE')
- clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect",
- "denoising_glossy_direct", "denoising_glossy_indirect",
- "denoising_transmission_direct", "denoising_transmission_indirect")
- if any(getattr(crl, option) for option in clean_options):
- yield ("Denoising Clean", "RGB", 'COLOR')
+
+ if scene.cycles.denoiser == 'NLM':
+ yield ("Denoising Shadowing", "X", 'VALUE')
+ yield ("Denoising Variance", "RGB", 'COLOR')
+ yield ("Denoising Intensity", "X", 'VALUE')
+
+ clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect",
+ "denoising_glossy_direct", "denoising_glossy_indirect",
+ "denoising_transmission_direct", "denoising_transmission_indirect")
+ if any(getattr(crl, option) for option in clean_options):
+ yield ("Denoising Clean", "RGB", 'COLOR')
# Custom AOV passes.
for aov in crl.aovs:
@@ -297,15 +301,15 @@ def list_render_passes(srl):
def register_passes(engine, scene, view_layer):
# Detect duplicate render pass names, first one wins.
listed = set()
- for name, channelids, channeltype in list_render_passes(view_layer):
+ for name, channelids, channeltype in list_render_passes(scene, view_layer):
if name not in listed:
engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype)
listed.add(name)
-def detect_conflicting_passes(view_layer):
+def detect_conflicting_passes(scene, view_layer):
# Detect conflicting render pass names for UI.
counter = {}
- for name, _, _ in list_render_passes(view_layer):
+ for name, _, _ in list_render_passes(scene, view_layer):
counter[name] = counter.get(name, 0) + 1
for aov in view_layer.cycles.aovs:
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 1635afab210..45d25720aff 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,17 +182,50 @@ 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),
-)
-enum_denoising_optix_input_passes = (
+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) or len(oidn_items):
+ items = [('AUTO', "Automatic", "Use the fastest available denoiser for viewport rendering (OptiX if available, OpenImageDenoise otherwise)", 0)]
+ else:
+ items = [('AUTO', "None", "Blender was compiled without a viewport denoiser", 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)
+ items += enum_openimagedenoise_denoiser(self, context)
+ return items
+
+enum_denoising_input_passes = (
('RGB', "Color", "Use only color as input", 1),
('RGB_ALBEDO', "Color + Albedo", "Use color and albedo data as input", 2),
('RGB_ALBEDO_NORMAL', "Color + Albedo + Normal", "Use color, albedo and normal data as input", 3),
)
+
+def update_render_passes(self, context):
+ scene = context.scene
+ view_layer = context.view_layer
+ view_layer.update_render_passes()
+ engine.detect_conflicting_passes(scene, view_layer)
+
+
class CyclesRenderSettings(bpy.types.PropertyGroup):
device: EnumProperty(
@@ -236,11 +257,32 @@ 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. "
+ "For denoising the image after rendering, denoising data render passes "
+ "also adapt to the selected denoiser",
+ items=enum_denoiser,
+ default=1,
+ update=update_render_passes,
+ )
+ 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 +298,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 +518,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 +671,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 +823,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)
@@ -794,6 +831,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
debug_use_cuda_split_kernel: BoolProperty(name="Split Kernel", default=False)
debug_optix_cuda_streams: IntProperty(name="CUDA Streams", default=1, min=1)
+ debug_optix_curves_api: BoolProperty(name="Native OptiX Curve Primitive", default=False)
debug_opencl_kernel_type: EnumProperty(
name="OpenCL Kernel Type",
@@ -1241,39 +1279,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
@@ -1289,12 +1305,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
del bpy.types.Scene.cycles_curves
-def update_render_passes(self, context):
- view_layer = context.view_layer
- view_layer.update_render_passes()
- engine.detect_conflicting_passes(view_layer)
-
-
class CyclesAOVPass(bpy.types.PropertyGroup):
name: StringProperty(
name="Name",
@@ -1369,7 +1379,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(
@@ -1428,7 +1438,7 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
)
denoising_store_passes: BoolProperty(
name="Store Denoising Passes",
- description="Store the denoising feature passes and the noisy image",
+ description="Store the denoising feature passes and the noisy image. The passes adapt to the denoiser selected for rendering",
default=False,
update=update_render_passes,
)
@@ -1439,19 +1449,20 @@ 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)",
- items=enum_denoising_optix_input_passes,
+ description="Passes used by the denoiser to distinguish noise from shader and geometry detail",
+ items=enum_denoising_input_passes,
default='RGB_ALBEDO',
)
+ denoising_openimagedenoise_input_passes: EnumProperty(
+ name="Input Passes",
+ description="Passes used by the denoiser to distinguish noise from shader and geometry detail",
+ items=enum_denoising_input_passes,
+ default='RGB_ALBEDO_NORMAL',
+ )
+
use_pass_crypto_object: BoolProperty(
name="Cryptomatte Object",
description="Render cryptomatte object pass, for isolating objects in compositing",
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 78a44881743..03b1675c309 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,44 @@ 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
+ for view_layer in scene.view_layers:
+ if view_layer.cycles.denoising_store_passes:
+ sub.active = True
+
+ 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 +416,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 +424,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 +707,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 +759,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"
@@ -769,10 +782,6 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel):
col.prop(view_layer, "use_solid", text="Surfaces")
col.prop(view_layer, "use_strand", text="Hair")
col.prop(view_layer, "use_volumes", text="Volumes")
- if with_freestyle:
- sub = col.row(align=True)
- sub.prop(view_layer, "use_freestyle", text="Freestyle")
- sub.active = rd.use_freestyle
class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel):
@@ -968,12 +977,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 +998,18 @@ 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':
+ col.prop(cycles_view_layer, "denoising_openimagedenoise_input_passes")
+ return
col.prop(cycles_view_layer, "denoising_radius", text="Radius")
@@ -1190,6 +1204,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,16 +1214,17 @@ 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):
return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or
(ob.instance_type == 'COLLECTION' and ob.instance_collection))
+
class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel):
bl_label = "Shading"
bl_context = "object"
@@ -1231,6 +1247,7 @@ class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel):
col = flow.column()
col.prop(cob, "shadow_terminator_offset")
+
class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel):
bl_label = "Visibility"
bl_context = "object"
@@ -1575,17 +1592,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 +1993,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
@@ -2007,6 +2028,7 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel):
col = layout.column()
col.label(text="OptiX Flags:")
col.prop(cscene, "debug_optix_cuda_streams")
+ col.prop(cscene, "debug_optix_curves_api")
col.separator()
@@ -2248,6 +2270,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..e1ab3b3fbc1 100644
--- a/intern/cycles/blender/blender_camera.cpp
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -76,6 +76,9 @@ struct BlenderCamera {
int full_width;
int full_height;
+ int render_width;
+ int render_height;
+
BoundBox2D border;
BoundBox2D pano_viewplane;
BoundBox2D viewport_camera_border;
@@ -126,8 +129,10 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende
bcam->matrix = transform_identity();
/* render resolution */
- bcam->full_width = render_resolution_x(b_render);
- bcam->full_height = render_resolution_y(b_render);
+ bcam->render_width = render_resolution_x(b_render);
+ bcam->render_height = render_resolution_y(b_render);
+ bcam->full_width = bcam->render_width;
+ bcam->full_height = bcam->render_height;
}
static float blender_camera_focal_distance(BL::RenderEngine &b_engine,
@@ -398,8 +403,8 @@ static void blender_camera_sync(Camera *cam,
/* panorama sensor */
if (bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) {
- float fit_xratio = (float)bcam->full_width * bcam->pixelaspect.x;
- float fit_yratio = (float)bcam->full_height * bcam->pixelaspect.y;
+ float fit_xratio = (float)bcam->render_width * bcam->pixelaspect.x;
+ float fit_yratio = (float)bcam->render_height * bcam->pixelaspect.y;
bool horizontal_fit;
float sensor_size;
@@ -709,6 +714,10 @@ static void blender_camera_from_view(BlenderCamera *bcam,
/* 3d view transform */
bcam->matrix = transform_inverse(get_transform(b_rv3d.view_matrix()));
+
+ /* dimensions */
+ bcam->full_width = width;
+ bcam->full_height = height;
}
static void blender_camera_view_subset(BL::RenderEngine &b_engine,
@@ -867,13 +876,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 +916,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_id_map.h b/intern/cycles/blender/blender_id_map.h
index 3bc42e349ae..b5f6aaa67a8 100644
--- a/intern/cycles/blender/blender_id_map.h
+++ b/intern/cycles/blender/blender_id_map.h
@@ -200,7 +200,7 @@ template<typename K, typename T> class id_map {
* To uniquely identify instances, we use the parent, object and persistent instance ID.
* We also export separate object for a mesh and its particle hair. */
-enum { OBJECT_PERSISTENT_ID_SIZE = 16 };
+enum { OBJECT_PERSISTENT_ID_SIZE = 8 /* MAX_DUPLI_RECUR in Blender. */ };
struct ObjectKey {
void *parent;
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..3ea6892a349 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -59,7 +59,7 @@ bool BlenderSync::BKE_object_is_modified(BL::Object &b_ob)
return false;
}
-bool BlenderSync::object_is_mesh(BL::Object &b_ob)
+bool BlenderSync::object_is_geometry(BL::Object &b_ob)
{
BL::ID b_ob_data = b_ob.data();
@@ -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;
}
@@ -147,7 +143,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
/* only interested in object that we can create meshes from */
- if (!object_is_mesh(b_ob)) {
+ if (!object_is_geometry(b_ob)) {
return NULL;
}
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 0be19dbffd1..25c77b74ce3 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
@@ -90,6 +92,7 @@ bool debug_flags_sync_from_scene(BL::Scene b_scene)
flags.cuda.split_kernel = get_boolean(cscene, "debug_use_cuda_split_kernel");
/* Synchronize OptiX flags. */
flags.optix.cuda_streams = get_int(cscene, "debug_optix_cuda_streams");
+ flags.optix.curves_api = get_boolean(cscene, "debug_optix_curves_api");
/* Synchronize OpenCL device type. */
switch (get_enum(cscene, "debug_opencl_device_type")) {
case 0:
@@ -1075,5 +1078,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..ca363802919 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,39 +489,18 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
BL::RenderLayer b_rlay = *b_single_rlay;
b_rlay_name = b_view_layer.name();
- /* add passes */
+ /* Update denoising parameters. */
+ session->set_denoising(session_params.denoising);
+
+ /* Compute render passes and film settings. */
vector<Pass> passes = sync->sync_render_passes(
- b_rlay, b_view_layer, session_params.adaptive_sampling);
- buffer_params.passes = passes;
+ b_rlay, b_view_layer, session_params.adaptive_sampling, 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");
-
- buffer_params.denoising_data_pass = use_denoising || write_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;
-
- 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;
-
- scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
- scene->film->tag_passes_update(scene, passes);
- scene->film->tag_update(scene);
- scene->integrator->tag_update(scene);
+ /* Set buffer params, using film settings from sync_render_passes. */
+ buffer_params.passes = passes;
+ buffer_params.denoising_data_pass = scene->film->denoising_data_pass;
+ buffer_params.denoising_clean_pass = scene->film->denoising_clean_pass;
+ buffer_params.denoising_prefiltered_pass = scene->film->denoising_prefiltered_pass;
BL::RenderResult::views_iterator b_view_iter;
@@ -798,7 +781,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 +814,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 +896,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 +914,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..33e73b5a4b9 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -813,6 +813,15 @@ 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_intensity = b_sky_node.sun_intensity();
+ sky->sun_elevation = b_sky_node.sun_elevation();
+ sky->sun_rotation = b_sky_node.sun_rotation();
+ sky->altitude = 1000.0f * 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..511061db08a 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
@@ -146,30 +147,43 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
/* Object */
else if (b_id.is_a(&RNA_Object)) {
BL::Object b_ob(b_id);
- const bool updated_geometry = b_update->is_updated_geometry();
+ const bool is_geometry = object_is_geometry(b_ob);
+ const bool is_light = !is_geometry && object_is_light(b_ob);
- if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
- object_map.set_recalc(b_ob);
- light_map.set_recalc(b_ob);
- }
+ if (is_geometry || is_light) {
+ const bool updated_geometry = b_update->is_updated_geometry();
- if (object_is_mesh(b_ob)) {
- if (updated_geometry ||
- (object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE)) {
- BL::ID key = BKE_object_is_modified(b_ob) ? b_ob : b_ob.data();
- geometry_map.set_recalc(key);
- }
- }
- else if (object_is_light(b_ob)) {
- if (updated_geometry) {
- light_map.set_recalc(b_ob);
+ /* Geometry (mesh, hair, volume). */
+ if (is_geometry) {
+ if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
+ object_map.set_recalc(b_ob);
+ }
+
+ if (updated_geometry ||
+ (object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE)) {
+ BL::ID key = BKE_object_is_modified(b_ob) ? b_ob : b_ob.data();
+ geometry_map.set_recalc(key);
+ }
+
+ if (updated_geometry) {
+ BL::Object::particle_systems_iterator b_psys;
+ for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end();
+ ++b_psys) {
+ particle_system_map.set_recalc(b_ob);
+ }
+ }
}
- }
+ /* Light */
+ else if (is_light) {
+ if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
+ object_map.set_recalc(b_ob);
+ light_map.set_recalc(b_ob);
+ }
- if (updated_geometry) {
- BL::Object::particle_systems_iterator b_psys;
- for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys)
- particle_system_map.set_recalc(b_ob);
+ if (updated_geometry) {
+ light_map.set_recalc(b_ob);
+ }
+ }
}
}
/* Mesh */
@@ -212,7 +226,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 +551,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 +569,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 +588,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 +604,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 +652,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 +661,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 +670,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");
@@ -686,6 +697,16 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
}
RNA_END;
+ scene->film->denoising_data_pass = denoising.use || denoising.store_passes;
+ scene->film->denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
+ scene->film->denoising_prefiltered_pass = denoising.store_passes &&
+ denoising.type == DENOISER_NLM;
+
+ scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
+ scene->film->tag_passes_update(scene, passes);
+ scene->film->tag_update(scene);
+ scene->integrator->tag_update(scene);
+
return passes;
}
@@ -732,6 +753,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 +777,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 +795,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 +874,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 +942,61 @@ 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.input_passes = (DenoiserInput)get_enum(
+ clayer,
+ (denoising.type == DENOISER_OPTIX) ? "denoising_optix_input_passes" :
+ "denoising_openimagedenoise_input_passes",
+ DENOISER_INPUT_NUM,
+ DENOISER_INPUT_RGB_ALBEDO_NORMAL);
+
+ 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..a551ec31e04 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 */
@@ -205,7 +208,7 @@ class BlenderSync {
/* util */
void find_shader(BL::ID &id, vector<Shader *> &used_shaders, Shader *default_shader);
bool BKE_object_is_modified(BL::Object &b_ob);
- bool object_is_mesh(BL::Object &b_ob);
+ bool object_is_geometry(BL::Object &b_ob);
bool object_is_light(BL::Object &b_ob);
/* variables */
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/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index 0b082b11cf7..b09f442bd16 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -133,9 +133,9 @@ if(CYCLES_STANDALONE_REPOSITORY)
set(BOOST_DEFINITIONS "-DBOOST_ALL_NO_LIB")
####
- # embree
+ # Embree
if(WITH_CYCLES_EMBREE)
- find_package(embree 3.8.0 REQUIRED)
+ find_package(Embree 3.8.0 REQUIRED)
endif()
####
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 9f31ed12cf4..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,15 +97,11 @@ 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 cuda_error_documentation();
-
- bool cuda_error_(CUresult result, const string &stmt);
-
- void cuda_error_message(const string &message);
+ void set_error(const string &error) override;
CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_);
@@ -112,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();
@@ -126,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();
@@ -144,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);
@@ -256,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 64c7f5e7d34..3a2eb8df95b 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. */
@@ -135,8 +135,10 @@ BVHLayoutMask CUDADevice::get_bvh_layout_mask() const
return BVH_LAYOUT_BVH2;
}
-void CUDADevice::cuda_error_documentation()
+void CUDADevice::set_error(const string &error)
{
+ Device::set_error(error);
+
if (first_error) {
fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n");
fprintf(stderr,
@@ -148,42 +150,13 @@ void CUDADevice::cuda_error_documentation()
# define cuda_assert(stmt) \
{ \
CUresult result = stmt; \
-\
if (result != CUDA_SUCCESS) { \
- string message = string_printf( \
- "CUDA error: %s in %s, line %d", cuewErrorString(result), #stmt, __LINE__); \
- if (error_msg == "") \
- error_msg = message; \
- fprintf(stderr, "%s\n", message.c_str()); \
- /*cuda_abort();*/ \
- cuda_error_documentation(); \
+ const char *name = cuewErrorString(result); \
+ set_error(string_printf("%s in %s (device_cuda_impl.cpp:%d)", name, #stmt, __LINE__)); \
} \
} \
(void)0
-bool CUDADevice::cuda_error_(CUresult result, const string &stmt)
-{
- if (result == CUDA_SUCCESS)
- return false;
-
- string message = string_printf("CUDA error at %s: %s", stmt.c_str(), cuewErrorString(result));
- if (error_msg == "")
- error_msg = message;
- fprintf(stderr, "%s\n", message.c_str());
- cuda_error_documentation();
- return true;
-}
-
-# define cuda_error(stmt) cuda_error_(stmt, # stmt)
-
-void CUDADevice::cuda_error_message(const string &message)
-{
- if (error_msg == "")
- error_msg = message;
- fprintf(stderr, "%s\n", message.c_str());
- cuda_error_documentation();
-}
-
CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_)
: Device(info, stats, profiler, background_), texture_info(this, "__texture_info", MEM_GLOBAL)
{
@@ -212,12 +185,19 @@ CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool
functions.loaded = false;
/* Intialize CUDA. */
- if (cuda_error(cuInit(0)))
+ CUresult result = cuInit(0);
+ if (result != CUDA_SUCCESS) {
+ set_error(string_printf("Failed to initialize CUDA runtime (%s)", cuewErrorString(result)));
return;
+ }
/* Setup device and context. */
- if (cuda_error(cuDeviceGet(&cuDevice, cuDevId)))
+ result = cuDeviceGet(&cuDevice, cuDevId);
+ if (result != CUDA_SUCCESS) {
+ set_error(string_printf("Failed to get CUDA device handle from ordinal (%s)",
+ cuewErrorString(result)));
return;
+ }
/* CU_CTX_MAP_HOST for mapping host memory when out of device memory.
* CU_CTX_LMEM_RESIZE_TO_MAX for reserving local memory ahead of render,
@@ -235,8 +215,6 @@ CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool
}
/* Create context. */
- CUresult result;
-
if (background) {
result = cuCtxCreate(&cuContext, ctx_flags, cuDevice);
}
@@ -249,8 +227,10 @@ CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool
}
}
- if (cuda_error_(result, "cuCtxCreate"))
+ if (result != CUDA_SUCCESS) {
+ set_error(string_printf("Failed to create CUDA context (%s)", cuewErrorString(result)));
return;
+ }
int major, minor;
cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId);
@@ -263,7 +243,7 @@ CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool
CUDADevice::~CUDADevice()
{
- task_pool.stop();
+ task_pool.cancel();
delete split_kernel;
@@ -280,10 +260,8 @@ bool CUDADevice::support_device(const DeviceRequestedFeatures & /*requested_feat
/* We only support sm_30 and above */
if (major < 3) {
- cuda_error_message(
- string_printf("CUDA device supported only with compute capability 3.0 or up, found %d.%d.",
- major,
- minor));
+ set_error(string_printf(
+ "CUDA backend requires compute capability 3.0 or up, but found %d.%d.", major, minor));
return false;
}
@@ -319,13 +297,19 @@ bool CUDADevice::check_peer_access(Device *peer_device)
// Enable peer access in both directions
{
const CUDAContextScope scope(this);
- if (cuda_error(cuCtxEnablePeerAccess(peer_device_cuda->cuContext, 0))) {
+ CUresult result = cuCtxEnablePeerAccess(peer_device_cuda->cuContext, 0);
+ if (result != CUDA_SUCCESS) {
+ set_error(string_printf("Failed to enable peer access on CUDA context (%s)",
+ cuewErrorString(result)));
return false;
}
}
{
const CUDAContextScope scope(peer_device_cuda);
- if (cuda_error(cuCtxEnablePeerAccess(cuContext, 0))) {
+ CUresult result = cuCtxEnablePeerAccess(cuContext, 0);
+ if (result != CUDA_SUCCESS) {
+ set_error(string_printf("Failed to enable peer access on CUDA context (%s)",
+ cuewErrorString(result)));
return false;
}
}
@@ -399,11 +383,24 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu
}
}
- const string ptx = path_get(string_printf("lib/%s_compute_%d%d.ptx", name, major, minor));
- VLOG(1) << "Testing for pre-compiled kernel " << ptx << ".";
- if (path_exists(ptx)) {
- VLOG(1) << "Using precompiled kernel.";
- return ptx;
+ /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */
+ int ptx_major = major, ptx_minor = minor;
+ while (ptx_major >= 3) {
+ const string ptx = path_get(
+ string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor));
+ VLOG(1) << "Testing for pre-compiled kernel " << ptx << ".";
+ if (path_exists(ptx)) {
+ VLOG(1) << "Using precompiled kernel.";
+ return ptx;
+ }
+
+ if (ptx_minor > 0) {
+ ptx_minor--;
+ }
+ else {
+ ptx_major--;
+ ptx_minor = 9;
+ }
}
}
@@ -432,14 +429,14 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu
# ifdef _WIN32
if (!use_adaptive_compilation() && have_precompiled_kernels()) {
if (major < 3) {
- cuda_error_message(
- string_printf("CUDA device requires compute capability 3.0 or up, "
- "found %d.%d. Your GPU is not supported.",
+ set_error(
+ string_printf("CUDA backend requires compute capability 3.0 or up, but found %d.%d. "
+ "Your GPU is not supported.",
major,
minor));
}
else {
- cuda_error_message(
+ set_error(
string_printf("CUDA binary kernel for this graphics card compute "
"capability (%d.%d) not found.",
major,
@@ -452,7 +449,7 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu
/* Compile. */
const char *const nvcc = cuewCompilerPath();
if (nvcc == NULL) {
- cuda_error_message(
+ set_error(
"CUDA nvcc compiler not found. "
"Install CUDA toolkit in default location.");
return string();
@@ -504,7 +501,7 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu
command = "call " + command;
# endif
if (system(command.c_str()) != 0) {
- cuda_error_message(
+ set_error(
"Failed to execute compilation command, "
"see console for details.");
return string();
@@ -512,7 +509,7 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu
/* Verify if compilation succeeded */
if (!path_exists(cubin)) {
- cuda_error_message(
+ set_error(
"CUDA kernel compilation failed, "
"see console for details.");
return string();
@@ -565,16 +562,19 @@ bool CUDADevice::load_kernels(const DeviceRequestedFeatures &requested_features)
else
result = CUDA_ERROR_FILE_NOT_FOUND;
- if (cuda_error_(result, "cuModuleLoad"))
- cuda_error_message(string_printf("Failed loading CUDA kernel %s.", cubin.c_str()));
+ if (result != CUDA_SUCCESS)
+ set_error(string_printf(
+ "Failed to load CUDA kernel from '%s' (%s)", cubin.c_str(), cuewErrorString(result)));
if (path_read_text(filter_cubin, cubin_data))
result = cuModuleLoadData(&cuFilterModule, cubin_data.c_str());
else
result = CUDA_ERROR_FILE_NOT_FOUND;
- if (cuda_error_(result, "cuModuleLoad"))
- cuda_error_message(string_printf("Failed loading CUDA kernel %s.", filter_cubin.c_str()));
+ if (result != CUDA_SUCCESS)
+ set_error(string_printf("Failed to load CUDA kernel from '%s' (%s)",
+ filter_cubin.c_str(),
+ cuewErrorString(result)));
if (result == CUDA_SUCCESS) {
reserve_local_memory(requested_features);
@@ -870,7 +870,7 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
if (mem_alloc_result != CUDA_SUCCESS) {
status = " failed, out of device and host memory";
- cuda_assert(mem_alloc_result);
+ set_error("System is out of GPU and shared host memory");
}
if (mem.name) {
@@ -1773,7 +1773,7 @@ void CUDADevice::denoise(RenderTile &rtile, DenoisingTask &denoising)
denoising.render_buffer.samples = rtile.sample;
denoising.buffer.gpu_temporary_mem = true;
- denoising.run_denoising(&rtile);
+ denoising.run_denoising(rtile);
}
void CUDADevice::adaptive_sampling_filter(uint filter_sample,
@@ -2339,11 +2339,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) {
@@ -2356,72 +2356,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);
@@ -2437,7 +2429,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);
+ });
}
}
@@ -2458,14 +2453,10 @@ void CUDADevice::task_cancel()
# define cuda_assert(stmt) \
{ \
CUresult result = stmt; \
-\
if (result != CUDA_SUCCESS) { \
- string message = string_printf("CUDA error: %s in %s", cuewErrorString(result), #stmt); \
- if (device->error_msg == "") \
- device->error_msg = message; \
- fprintf(stderr, "%s\n", message.c_str()); \
- /*cuda_abort();*/ \
- device->cuda_error_documentation(); \
+ const char *name = cuewErrorString(result); \
+ device->set_error( \
+ string_printf("%s in %s (device_cuda_impl.cpp:%d)", name, #stmt, __LINE__)); \
} \
} \
(void)0
@@ -2647,14 +2638,15 @@ bool CUDASplitKernel::enqueue_split_kernel_data_init(const KernelDimensions &dim
SplitKernelFunction *CUDASplitKernel::get_split_kernel_function(const string &kernel_name,
const DeviceRequestedFeatures &)
{
- CUDAContextScope scope(device);
- CUfunction func;
+ const CUDAContextScope scope(device);
- cuda_assert(
- cuModuleGetFunction(&func, device->cuModule, (string("kernel_cuda_") + kernel_name).data()));
- if (device->have_error()) {
- device->cuda_error_message(
- string_printf("kernel \"kernel_cuda_%s\" not found in module", kernel_name.data()));
+ CUfunction func;
+ const CUresult result = cuModuleGetFunction(
+ &func, device->cuModule, (string("kernel_cuda_") + kernel_name).data());
+ if (result != CUDA_SUCCESS) {
+ device->set_error(string_printf("Could not find kernel \"kernel_cuda_%s\" in module (%s)",
+ kernel_name.data(),
+ cuewErrorString(result)));
return NULL;
}
@@ -2668,7 +2660,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..407f73e8451 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) {
@@ -209,13 +209,13 @@ bool Device::bind_fallback_display_space_shader(const float width, const float h
glUseProgram(fallback_shader_program);
image_texture_location = glGetUniformLocation(fallback_shader_program, "image_texture");
if (image_texture_location < 0) {
- LOG(ERROR) << "Shader doesn't containt the 'image_texture' uniform.";
+ LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform.";
return false;
}
fullscreen_location = glGetUniformLocation(fallback_shader_program, "fullscreen");
if (fullscreen_location < 0) {
- LOG(ERROR) << "Shader doesn't containt the 'fullscreen' uniform.";
+ LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform.";
return false;
}
@@ -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..115b05e3911 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;
@@ -430,10 +439,10 @@ class Device {
{
return 0;
}
- virtual void map_neighbor_tiles(Device * /*sub_device*/, RenderTile * /*tiles*/)
+ virtual void map_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/)
{
}
- virtual void unmap_neighbor_tiles(Device * /*sub_device*/, RenderTile * /*tiles*/)
+ virtual void unmap_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/)
{
}
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index fc6febd8cee..ee3a3ddea64 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,11 @@ class CPUDevice : public Device {
#ifdef WITH_OSL
OSLGlobals osl_globals;
#endif
+#ifdef WITH_OPENIMAGEDENOISE
+ oidn::DeviceRef oidn_device;
+ oidn::FilterRef oidn_filter;
+#endif
+ thread_spin_lock oidn_task_lock;
bool use_split_kernel;
@@ -332,7 +339,7 @@ class CPUDevice : public Device {
~CPUDevice()
{
- task_pool.stop();
+ task_pool.cancel();
texture_info.free();
}
@@ -344,17 +351,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 +523,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 +949,222 @@ class CPUDevice : public Device {
}
}
- void denoise(DenoisingTask &denoising, RenderTile &tile)
+ void denoise_openimagedenoise_buffer(DeviceTask &task,
+ float *buffer,
+ const size_t offset,
+ const size_t stride,
+ const size_t x,
+ const size_t y,
+ const size_t w,
+ const size_t h,
+ const float scale)
+ {
+#ifdef WITH_OPENIMAGEDENOISE
+ assert(openimagedenoise_supported());
+
+ /* Only one at a time, since OpenImageDenoise itself is multithreaded for full
+ * buffers, and for tiled rendering because creating multiple devices and filters
+ * is slow and memory hungry as well.
+ *
+ * TODO: optimize tiled rendering case, by batching together denoising of many
+ * tiles somehow? */
+ 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");
+ oidn_filter.set("hdr", true);
+ oidn_filter.set("srgb", false);
+ }
+
+ /* Set images with appropriate stride for our interleaved pass storage. */
+ struct {
+ const char *name;
+ const int offset;
+ const bool scale;
+ const bool use;
+ array<float> scaled_buffer;
+ } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR, false, true},
+ {"albedo",
+ task.pass_denoising_data + DENOISING_PASS_ALBEDO,
+ true,
+ task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO},
+ {"normal",
+ task.pass_denoising_data + DENOISING_PASS_NORMAL,
+ true,
+ task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO_NORMAL},
+ {"output", 0, false, true},
+ { NULL,
+ 0 }};
+
+ for (int i = 0; passes[i].name; i++) {
+ if (!passes[i].use) {
+ continue;
+ }
+
+ const int64_t pixel_offset = offset + x + y * stride;
+ const int64_t buffer_offset = (pixel_offset * task.pass_stride + passes[i].offset);
+ const int64_t pixel_stride = task.pass_stride;
+ const int64_t row_stride = stride * pixel_stride;
+
+ if (passes[i].scale && scale != 1.0f) {
+ /* Normalize albedo and normal passes as they are scaled by the number of samples.
+ * For the color passes OIDN will perform auto-exposure making it unnecessary. */
+ array<float> &scaled_buffer = passes[i].scaled_buffer;
+ scaled_buffer.resize(w * h * 3);
+
+ for (int y = 0; y < h; y++) {
+ const float *pass_row = buffer + buffer_offset + y * row_stride;
+ float *scaled_row = scaled_buffer.data() + y * w * 3;
+
+ for (int x = 0; x < w; x++) {
+ scaled_row[x * 3 + 0] = pass_row[x * pixel_stride + 0] * scale;
+ scaled_row[x * 3 + 1] = pass_row[x * pixel_stride + 1] * scale;
+ scaled_row[x * 3 + 2] = pass_row[x * pixel_stride + 2] * scale;
+ }
+ }
+
+ oidn_filter.setImage(
+ passes[i].name, scaled_buffer.data(), oidn::Format::Float3, w, h, 0, 0, 0);
+ }
+ else {
+ oidn_filter.setImage(passes[i].name,
+ buffer + buffer_offset,
+ oidn::Format::Float3,
+ w,
+ h,
+ 0,
+ pixel_stride * sizeof(float),
+ row_stride * sizeof(float));
+ }
+ }
+
+ /* Execute filter. */
+ oidn_filter.commit();
+ oidn_filter.execute();
+#else
+ (void)task;
+ (void)buffer;
+ (void)offset;
+ (void)stride;
+ (void)x;
+ (void)y;
+ (void)w;
+ (void)h;
+ (void)scale;
+#endif
+ }
+
+ void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile)
+ {
+ if (task.type == DeviceTask::DENOISE_BUFFER) {
+ /* Copy pixels from compute device to CPU (no-op for CPU device). */
+ rtile.buffers->buffer.copy_from_device();
+
+ denoise_openimagedenoise_buffer(task,
+ (float *)rtile.buffer,
+ rtile.offset,
+ rtile.stride,
+ rtile.x,
+ rtile.y,
+ rtile.w,
+ rtile.h,
+ 1.0f / rtile.sample);
+
+ /* 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 {
+ /* Per-tile denoising. */
+ rtile.sample = rtile.start_sample + rtile.num_samples;
+ const float scale = 1.0f / rtile.sample;
+ const float invscale = rtile.sample;
+ const size_t pass_stride = task.pass_stride;
+
+ /* Map neighboring tiles into one buffer for denoising. */
+ RenderTileNeighbors neighbors(rtile);
+ task.map_neighbor_tiles(neighbors, this);
+ RenderTile &center_tile = neighbors.tiles[RenderTileNeighbors::CENTER];
+ rtile = center_tile;
+
+ /* Calculate size of the tile to denoise (including overlap). The overlap
+ * size was chosen empirically. OpenImageDenoise specifies an overlap size
+ * of 128 but this is significantly bigger than typical tile size. */
+ const int4 rect = rect_clip(rect_expand(center_tile.bounds(), 64), neighbors.bounds());
+ const int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y);
+
+ /* Adjacent tiles are in separate memory regions, copy into single buffer. */
+ array<float> merged(rect_size.x * rect_size.y * task.pass_stride);
+
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ RenderTile &ntile = neighbors.tiles[i];
+ if (!ntile.buffer) {
+ continue;
+ }
+
+ const int xmin = max(ntile.x, rect.x);
+ const int ymin = max(ntile.y, rect.y);
+ const int xmax = min(ntile.x + ntile.w, rect.z);
+ const int ymax = min(ntile.y + ntile.h, rect.w);
+
+ const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride;
+ const float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride;
+
+ const size_t merged_stride = rect_size.x;
+ const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride;
+ float *merged_buffer = merged.data() + merged_offset * pass_stride;
+
+ for (int y = ymin; y < ymax; y++) {
+ for (int x = 0; x < pass_stride * (xmax - xmin); x++) {
+ merged_buffer[x] = tile_buffer[x] * scale;
+ }
+ tile_buffer += ntile.stride * pass_stride;
+ merged_buffer += merged_stride * pass_stride;
+ }
+ }
+
+ /* Denoise */
+ denoise_openimagedenoise_buffer(
+ task, merged.data(), 0, rect_size.x, 0, 0, rect_size.x, rect_size.y, 1.0f);
+
+ /* Copy back result from merged buffer. */
+ RenderTile &ntile = neighbors.target;
+ if (ntile.buffer) {
+ const int xmin = max(ntile.x, rect.x);
+ const int ymin = max(ntile.y, rect.y);
+ const int xmax = min(ntile.x + ntile.w, rect.z);
+ const int ymax = min(ntile.y + ntile.h, rect.w);
+
+ const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride;
+ float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride;
+
+ const size_t merged_stride = rect_size.x;
+ const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride;
+ const float *merged_buffer = merged.data() + merged_offset * pass_stride;
+
+ for (int y = ymin; y < ymax; y++) {
+ for (int x = 0; x < pass_stride * (xmax - xmin); x += pass_stride) {
+ tile_buffer[x + 0] = merged_buffer[x + 0] * invscale;
+ tile_buffer[x + 1] = merged_buffer[x + 1] * invscale;
+ tile_buffer[x + 2] = merged_buffer[x + 2] * invscale;
+ }
+ tile_buffer += ntile.stride * pass_stride;
+ merged_buffer += merged_stride * pass_stride;
+ }
+ }
+
+ task.unmap_neighbor_tiles(neighbors, this);
+ }
+ }
+
+ void denoise_nlm(DenoisingTask &denoising, RenderTile &tile)
{
ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING);
@@ -989,7 +1192,7 @@ class CPUDevice : public Device {
denoising.render_buffer.samples = tile.sample;
denoising.buffer.gpu_temporary_mem = false;
- denoising.run_denoising(&tile);
+ denoising.run_denoising(tile);
}
void thread_render(DeviceTask &task)
@@ -1019,15 +1222,27 @@ class CPUDevice : public Device {
}
}
- RenderTile tile;
- DenoisingTask denoising(this, task);
- denoising.profiler = &kg->profiler;
+ /* NLM denoiser. */
+ DenoisingTask *denoising = NULL;
+
+ /* OpenImageDenoise: we can only denoise with one thread at a time, so to
+ * avoid waiting with mutex locks in the denoiser, we let only a single
+ * thread acquire denoising tiles. */
+ uint tile_types = task.tile_types;
+ bool hold_denoise_lock = false;
+ if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
+ if (!oidn_task_lock.try_lock()) {
+ tile_types &= ~RenderTile::DENOISE;
+ hold_denoise_lock = true;
+ }
+ }
- while (task.acquire_tile(this, tile, task.tile_types)) {
+ RenderTile tile;
+ while (task.acquire_tile(this, tile, 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 +1252,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);
}
@@ -1049,12 +1273,17 @@ class CPUDevice : public Device {
}
}
+ if (hold_denoise_lock) {
+ oidn_task_lock.unlock();
+ }
+
profiler.remove_state(&kg->profiler);
thread_kernel_globals_free((KernelGlobals *)kgbuffer.device_pointer);
kg->~KernelGlobals();
kgbuffer.free();
delete split_kernel;
+ delete denoising;
}
void thread_denoise(DeviceTask &task)
@@ -1072,16 +1301,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 +1390,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 +1572,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 +1604,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..38c42d15cab 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()
@@ -71,29 +71,30 @@ DenoisingTask::~DenoisingTask()
tile_info_mem.free();
}
-void DenoisingTask::set_render_buffer(RenderTile *rtiles)
+void DenoisingTask::set_render_buffer(RenderTileNeighbors &neighbors)
{
- for (int i = 0; i < 9; i++) {
- tile_info->offsets[i] = rtiles[i].offset;
- tile_info->strides[i] = rtiles[i].stride;
- tile_info->buffers[i] = rtiles[i].buffer;
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ RenderTile &rtile = neighbors.tiles[i];
+ tile_info->offsets[i] = rtile.offset;
+ tile_info->strides[i] = rtile.stride;
+ tile_info->buffers[i] = rtile.buffer;
}
- tile_info->x[0] = rtiles[3].x;
- tile_info->x[1] = rtiles[4].x;
- tile_info->x[2] = rtiles[5].x;
- tile_info->x[3] = rtiles[5].x + rtiles[5].w;
- tile_info->y[0] = rtiles[1].y;
- tile_info->y[1] = rtiles[4].y;
- tile_info->y[2] = rtiles[7].y;
- tile_info->y[3] = rtiles[7].y + rtiles[7].h;
-
- target_buffer.offset = rtiles[9].offset;
- target_buffer.stride = rtiles[9].stride;
- target_buffer.ptr = rtiles[9].buffer;
-
- if (write_passes && rtiles[9].buffers) {
+ tile_info->x[0] = neighbors.tiles[3].x;
+ tile_info->x[1] = neighbors.tiles[4].x;
+ tile_info->x[2] = neighbors.tiles[5].x;
+ tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w;
+ tile_info->y[0] = neighbors.tiles[1].y;
+ tile_info->y[1] = neighbors.tiles[4].y;
+ tile_info->y[2] = neighbors.tiles[7].y;
+ tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h;
+
+ target_buffer.offset = neighbors.target.offset;
+ target_buffer.stride = neighbors.target.stride;
+ target_buffer.ptr = neighbors.target.buffer;
+
+ if (do_prefilter && neighbors.target.buffers) {
target_buffer.denoising_output_offset =
- rtiles[9].buffers->params.get_denoising_prefiltered_offset();
+ neighbors.target.buffers->params.get_denoising_prefiltered_offset();
}
else {
target_buffer.denoising_output_offset = 0;
@@ -111,7 +112,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);
@@ -320,12 +321,11 @@ void DenoisingTask::reconstruct()
functions.solve(target_buffer.ptr);
}
-void DenoisingTask::run_denoising(RenderTile *tile)
+void DenoisingTask::run_denoising(RenderTile &tile)
{
- RenderTile rtiles[10];
- rtiles[4] = *tile;
- functions.map_neighbor_tiles(rtiles);
- set_render_buffer(rtiles);
+ RenderTileNeighbors neighbors(tile);
+ functions.map_neighbor_tiles(neighbors);
+ set_render_buffer(neighbors);
setup_denoising_buffer();
@@ -343,11 +343,11 @@ void DenoisingTask::run_denoising(RenderTile *tile)
reconstruct();
}
- if (write_passes) {
+ if (do_prefilter) {
write_buffer();
}
- functions.unmap_neighbor_tiles(rtiles);
+ functions.unmap_neighbor_tiles(neighbors);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device_denoising.h b/intern/cycles/device/device_denoising.h
index bd1d0193dbd..2c0dc23b44a 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 {
@@ -102,8 +102,8 @@ class DenoisingTask {
device_ptr output_ptr)>
detect_outliers;
function<bool(int out_offset, device_ptr frop_ptr, device_ptr buffer_ptr)> write_feature;
- function<void(RenderTile *rtiles)> map_neighbor_tiles;
- function<void(RenderTile *rtiles)> unmap_neighbor_tiles;
+ function<void(RenderTileNeighbors &neighbors)> map_neighbor_tiles;
+ function<void(RenderTileNeighbors &neighbors)> unmap_neighbor_tiles;
} functions;
/* Stores state of the current Reconstruction operation,
@@ -154,7 +154,7 @@ class DenoisingTask {
DenoisingTask(Device *device, const DeviceTask &task);
~DenoisingTask();
- void run_denoising(RenderTile *tile);
+ void run_denoising(RenderTile &tile);
struct DenoiseBuffers {
int pass_stride;
@@ -179,7 +179,7 @@ class DenoisingTask {
protected:
Device *device;
- void set_render_buffer(RenderTile *rtiles);
+ void set_render_buffer(RenderTileNeighbors &neighbors);
void setup_denoising_buffer();
void prefilter_shadowing();
void prefilter_features();
diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp
index 020b9e10e60..9ea8782d0f0 100644
--- a/intern/cycles/device/device_multi.cpp
+++ b/intern/cycles/device/device_multi.cpp
@@ -177,8 +177,11 @@ class MultiDevice : public Device {
return false;
if (requested_features.use_denoising) {
+ /* Only need denoising feature, everything else is unused. */
+ DeviceRequestedFeatures denoising_features;
+ denoising_features.use_denoising = true;
foreach (SubDevice &sub, denoising_devices)
- if (!sub.device->load_kernels(requested_features))
+ if (!sub.device->load_kernels(denoising_features))
return false;
}
@@ -396,8 +399,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());
@@ -581,20 +584,22 @@ class MultiDevice : public Device {
return -1;
}
- void map_neighbor_tiles(Device *sub_device, RenderTile *tiles)
+ void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
{
- for (int i = 0; i < 9; i++) {
- if (!tiles[i].buffers) {
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ RenderTile &tile = neighbors.tiles[i];
+
+ if (!tile.buffers) {
continue;
}
- device_vector<float> &mem = tiles[i].buffers->buffer;
- tiles[i].buffer = mem.device_pointer;
+ device_vector<float> &mem = tile.buffers->buffer;
+ tile.buffer = mem.device_pointer;
if (mem.device == this && matching_rendering_and_denoising_devices) {
/* Skip unnecessary copies in viewport mode (buffer covers the
* whole image), but still need to fix up the tile device pointer. */
- map_tile(sub_device, tiles[i]);
+ map_tile(sub_device, tile);
continue;
}
@@ -607,15 +612,15 @@ class MultiDevice : public Device {
* also required for the case where a CPU thread is denoising
* a tile rendered on the GPU. In that case we have to avoid
* overwriting the buffer being de-noised by the CPU thread. */
- if (!tiles[i].buffers->map_neighbor_copied) {
- tiles[i].buffers->map_neighbor_copied = true;
+ if (!tile.buffers->map_neighbor_copied) {
+ tile.buffers->map_neighbor_copied = true;
mem.copy_from_device();
}
if (mem.device == this) {
/* Can re-use memory if tile is already allocated on the sub device. */
- map_tile(sub_device, tiles[i]);
- mem.swap_device(sub_device, mem.device_size, tiles[i].buffer);
+ map_tile(sub_device, tile);
+ mem.swap_device(sub_device, mem.device_size, tile.buffer);
}
else {
mem.swap_device(sub_device, 0, 0);
@@ -623,40 +628,42 @@ class MultiDevice : public Device {
mem.copy_to_device();
- tiles[i].buffer = mem.device_pointer;
- tiles[i].device_size = mem.device_size;
+ tile.buffer = mem.device_pointer;
+ tile.device_size = mem.device_size;
mem.restore_device();
}
}
}
- void unmap_neighbor_tiles(Device *sub_device, RenderTile *tiles)
+ void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
{
- device_vector<float> &mem = tiles[9].buffers->buffer;
+ RenderTile &target_tile = neighbors.target;
+ device_vector<float> &mem = target_tile.buffers->buffer;
if (mem.device == this && matching_rendering_and_denoising_devices) {
return;
}
/* Copy denoised result back to the host. */
- mem.swap_device(sub_device, tiles[9].device_size, tiles[9].buffer);
+ mem.swap_device(sub_device, target_tile.device_size, target_tile.buffer);
mem.copy_from_device();
mem.restore_device();
/* Copy denoised result to the original device. */
mem.copy_to_device();
- for (int i = 0; i < 9; i++) {
- if (!tiles[i].buffers) {
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ RenderTile &tile = neighbors.tiles[i];
+ if (!tile.buffers) {
continue;
}
- device_vector<float> &mem = tiles[i].buffers->buffer;
+ device_vector<float> &mem = tile.buffers->buffer;
if (mem.device != sub_device && mem.device != this) {
/* Free up memory again if it was allocated for the copy above. */
- mem.swap_device(sub_device, tiles[i].device_size, tiles[i].buffer);
+ mem.swap_device(sub_device, tile.device_size, tile.buffer);
sub_device->mem_free(mem);
mem.restore_device();
}
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 db04c13d083..1cc45983565 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -70,7 +70,7 @@ struct KernelParams {
if (res != CUDA_SUCCESS) { \
const char *name; \
cuGetErrorName(res, &name); \
- set_error(string_printf("OptiX CUDA error %s in %s, line %d", name, #stmt, __LINE__)); \
+ set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \
return; \
} \
} \
@@ -81,7 +81,7 @@ struct KernelParams {
if (res != CUDA_SUCCESS) { \
const char *name; \
cuGetErrorName(res, &name); \
- set_error(string_printf("OptiX CUDA error %s in %s, line %d", name, #stmt, __LINE__)); \
+ set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \
return false; \
} \
} \
@@ -92,7 +92,7 @@ struct KernelParams {
enum OptixResult res = stmt; \
if (res != OPTIX_SUCCESS) { \
const char *name = optixGetErrorName(res); \
- set_error(string_printf("OptiX error %s in %s, line %d", name, #stmt, __LINE__)); \
+ set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \
return; \
} \
} \
@@ -102,7 +102,7 @@ struct KernelParams {
enum OptixResult res = stmt; \
if (res != OPTIX_SUCCESS) { \
const char *name = optixGetErrorName(res); \
- set_error(string_printf("OptiX error %s in %s, line %d", name, #stmt, __LINE__)); \
+ set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \
return false; \
} \
} \
@@ -131,8 +131,12 @@ class OptiXDevice : public CUDADevice {
PG_RGEN,
PG_MISS,
PG_HITD, // Default hit group
- PG_HITL, // __BVH_LOCAL__ hit group
PG_HITS, // __SHADOW_RECORD_ALL__ hit group
+ PG_HITL, // __BVH_LOCAL__ hit group (only used for triangles)
+# if OPTIX_ABI_VERSION >= 36
+ PG_HITD_MOTION,
+ PG_HITS_MOTION,
+# endif
# ifdef WITH_CYCLES_DEBUG
PG_EXCP,
# endif
@@ -177,6 +181,7 @@ class OptiXDevice : public CUDADevice {
OptixDeviceContext context = NULL;
OptixModule optix_module = NULL; // All necessary OptiX kernels are in one module
+ OptixModule builtin_modules[2] = {};
OptixPipeline pipelines[NUM_PIPELINES] = {};
bool motion_blur = false;
@@ -246,7 +251,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);
@@ -264,6 +269,9 @@ class OptiXDevice : public CUDADevice {
// Unload modules
if (optix_module != NULL)
optixModuleDestroy(optix_module);
+ for (unsigned int i = 0; i < 2; ++i)
+ if (builtin_modules[i] != NULL)
+ optixModuleDestroy(builtin_modules[i]);
for (unsigned int i = 0; i < NUM_PIPELINES; ++i)
if (pipelines[i] != NULL)
optixPipelineDestroy(pipelines[i]);
@@ -322,12 +330,12 @@ class OptiXDevice : public CUDADevice {
// Disable baking for now, since its kernel is not well-suited for inlining and is very slow
if (requested_features.use_baking) {
- set_error("OptiX implementation does not support baking yet");
+ set_error("OptiX backend does not support baking yet");
return false;
}
// Disable shader raytracing support for now, since continuation callables are slow
if (requested_features.use_shader_raytrace) {
- set_error("OptiX implementation does not support shader raytracing yet");
+ set_error("OptiX backend does not support 'Ambient Occlusion' and 'Bevel' shader nodes yet");
return false;
}
@@ -338,6 +346,12 @@ class OptiXDevice : public CUDADevice {
optixModuleDestroy(optix_module);
optix_module = NULL;
}
+ for (unsigned int i = 0; i < 2; ++i) {
+ if (builtin_modules[i] != NULL) {
+ optixModuleDestroy(builtin_modules[i]);
+ builtin_modules[i] = NULL;
+ }
+ }
for (unsigned int i = 0; i < NUM_PIPELINES; ++i) {
if (pipelines[i] != NULL) {
optixPipelineDestroy(pipelines[i]);
@@ -369,6 +383,18 @@ class OptiXDevice : public CUDADevice {
# endif
pipeline_options.pipelineLaunchParamsVariableName = "__params"; // See kernel_globals.h
+# if OPTIX_ABI_VERSION >= 36
+ pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE;
+ if (requested_features.use_hair) {
+ if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) {
+ pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE;
+ }
+ else {
+ pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM;
+ }
+ }
+# endif
+
// Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds
// This is necessary since objects may be reported to have motion if the Vector pass is
// active, but may still need to be rendered without motion blur if that isn't active as well
@@ -386,14 +412,14 @@ class OptiXDevice : public CUDADevice {
if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) {
if (!getenv("OPTIX_ROOT_DIR")) {
set_error(
- "OPTIX_ROOT_DIR environment variable not set, must be set with the path to the "
- "Optix SDK in order to compile the Optix kernel on demand.");
+ "Missing OPTIX_ROOT_DIR environment variable (which must be set with the path to "
+ "the Optix SDK to be able to compile Optix kernels on demand).");
return false;
}
ptx_filename = compile_kernel(requested_features, "kernel_optix", "optix", true);
}
if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) {
- set_error("Failed loading OptiX kernel " + ptx_filename + ".");
+ set_error("Failed to load OptiX kernel from '" + ptx_filename + "'");
return false;
}
@@ -428,11 +454,48 @@ 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 OPTIX_ABI_VERSION >= 36
+ if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) {
+ OptixBuiltinISOptions builtin_options;
+ builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE;
+ builtin_options.usesMotionBlur = false;
+
+ check_result_optix_ret(optixBuiltinISModuleGet(
+ context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[0]));
+
+ group_descs[PG_HITD].hitgroup.moduleIS = builtin_modules[0];
+ group_descs[PG_HITD].hitgroup.entryFunctionNameIS = nullptr;
+ group_descs[PG_HITS].hitgroup.moduleIS = builtin_modules[0];
+ group_descs[PG_HITS].hitgroup.entryFunctionNameIS = nullptr;
+
+ if (motion_blur) {
+ builtin_options.usesMotionBlur = true;
+
+ check_result_optix_ret(optixBuiltinISModuleGet(
+ context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[1]));
+
+ group_descs[PG_HITD_MOTION] = group_descs[PG_HITD];
+ group_descs[PG_HITD_MOTION].hitgroup.moduleIS = builtin_modules[1];
+ group_descs[PG_HITS_MOTION] = group_descs[PG_HITS];
+ group_descs[PG_HITS_MOTION].hitgroup.moduleIS = builtin_modules[1];
+ }
+ }
+# endif
}
if (requested_features.use_subsurface || requested_features.use_shader_raytrace) {
@@ -484,8 +547,14 @@ class OptiXDevice : public CUDADevice {
unsigned int trace_css = stack_size[PG_HITD].cssCH;
// This is based on the maximum of closest-hit and any-hit/intersection programs
trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH);
- trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH);
trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH);
+ trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH);
+# if OPTIX_ABI_VERSION >= 36
+ trace_css = std::max(trace_css,
+ stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH);
+ trace_css = std::max(trace_css,
+ stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH);
+# endif
OptixPipelineLinkOptions link_options;
link_options.maxTraceDepth = 1;
@@ -494,17 +563,23 @@ class OptiXDevice : public CUDADevice {
# else
link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO;
# endif
- link_options.overrideUsesMotionBlur = pipeline_options.usesMotionBlur;
+# if OPTIX_ABI_VERSION < 24
+ link_options.overrideUsesMotionBlur = motion_blur;
+# endif
{ // Create path tracing pipeline
OptixProgramGroup pipeline_groups[] = {
- groups[PG_RGEN],
- groups[PG_MISS],
- groups[PG_HITD],
- groups[PG_HITS],
- groups[PG_HITL],
+ groups[PG_RGEN],
+ groups[PG_MISS],
+ groups[PG_HITD],
+ groups[PG_HITS],
+ groups[PG_HITL],
+# if OPTIX_ABI_VERSION >= 36
+ groups[PG_HITD_MOTION],
+ groups[PG_HITS_MOTION],
+# endif
# ifdef WITH_CYCLES_DEBUG
- groups[PG_EXCP],
+ groups[PG_EXCP],
# endif
};
check_result_optix_ret(
@@ -521,8 +596,8 @@ class OptiXDevice : public CUDADevice {
const unsigned int css = stack_size[PG_RGEN].cssRG + link_options.maxTraceDepth * trace_css;
// Set stack size depending on pipeline options
- check_result_optix_ret(optixPipelineSetStackSize(
- pipelines[PIP_PATH_TRACE], 0, 0, css, (pipeline_options.usesMotionBlur ? 3 : 2)));
+ check_result_optix_ret(
+ optixPipelineSetStackSize(pipelines[PIP_PATH_TRACE], 0, 0, css, (motion_blur ? 3 : 2)));
}
// Only need to create shader evaluation pipeline if one of these features is used:
@@ -532,15 +607,19 @@ class OptiXDevice : public CUDADevice {
if (use_shader_eval_pipeline) { // Create shader evaluation pipeline
OptixProgramGroup pipeline_groups[] = {
- groups[PG_BAKE],
- groups[PG_DISP],
- groups[PG_BACK],
- groups[PG_MISS],
- groups[PG_HITD],
- groups[PG_HITS],
- groups[PG_HITL],
+ groups[PG_BAKE],
+ groups[PG_DISP],
+ groups[PG_BACK],
+ groups[PG_MISS],
+ groups[PG_HITD],
+ groups[PG_HITS],
+ groups[PG_HITL],
+# if OPTIX_ABI_VERSION >= 36
+ groups[PG_HITD_MOTION],
+ groups[PG_HITS_MOTION],
+# endif
# ifdef WITH_CYCLES_DEBUG
- groups[PG_EXCP],
+ groups[PG_EXCP],
# endif
};
check_result_optix_ret(
@@ -663,7 +742,11 @@ class OptiXDevice : public CUDADevice {
sbt_params.missRecordCount = 1;
sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord);
sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord);
- sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITL, PG_HITS
+# if OPTIX_ABI_VERSION >= 36
+ sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL
+# else
+ sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL
+# endif
// Launch the ray generation program
check_result_optix(optixLaunch(pipelines[PIP_PATH_TRACE],
@@ -712,25 +795,24 @@ 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
// 3 4 5
// 6 7 8 9
- RenderTile rtiles[10];
- rtiles[4] = rtile;
- task.map_neighbor_tiles(rtiles, this);
- rtile = rtiles[4]; // Tile may have been modified by mapping code
+ RenderTileNeighbors neighbors(rtile);
+ task.map_neighbor_tiles(neighbors, this);
+ RenderTile &center_tile = neighbors.tiles[RenderTileNeighbors::CENTER];
+ RenderTile &target_tile = neighbors.target;
+ rtile = center_tile; // Tile may have been modified by mapping code
// Calculate size of the tile to denoise (including overlap)
- int4 rect = make_int4(
- rtiles[4].x, rtiles[4].y, rtiles[4].x + rtiles[4].w, rtiles[4].y + rtiles[4].h);
+ int4 rect = center_tile.bounds();
// Overlap between tiles has to be at least 64 pixels
// TODO(pmours): Query this value from OptiX
rect = rect_expand(rect, 64);
- int4 clip_rect = make_int4(
- rtiles[3].x, rtiles[1].y, rtiles[5].x + rtiles[5].w, rtiles[7].y + rtiles[7].h);
+ int4 clip_rect = neighbors.bounds();
rect = rect_clip(rect, clip_rect);
int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y);
int2 overlap_offset = make_int2(rtile.x - rect.x, rtile.y - rect.y);
@@ -751,14 +833,14 @@ class OptiXDevice : public CUDADevice {
device_only_memory<float> input(this, "denoiser input");
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_WRITE);
- if ((!rtiles[0].buffer || rtiles[0].buffer == rtile.buffer) &&
- (!rtiles[1].buffer || rtiles[1].buffer == rtile.buffer) &&
- (!rtiles[2].buffer || rtiles[2].buffer == rtile.buffer) &&
- (!rtiles[3].buffer || rtiles[3].buffer == rtile.buffer) &&
- (!rtiles[5].buffer || rtiles[5].buffer == rtile.buffer) &&
- (!rtiles[6].buffer || rtiles[6].buffer == rtile.buffer) &&
- (!rtiles[7].buffer || rtiles[7].buffer == rtile.buffer) &&
- (!rtiles[8].buffer || rtiles[8].buffer == rtile.buffer)) {
+ bool contiguous_memory = true;
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ if (neighbors.tiles[i].buffer && neighbors.tiles[i].buffer != rtile.buffer) {
+ contiguous_memory = false;
+ }
+ }
+
+ if (contiguous_memory) {
// Tiles are in continous memory, so can just subtract overlap offset
input_ptr -= (overlap_offset.x + overlap_offset.y * rtile.stride) * pixel_stride;
// Stride covers the whole width of the image and not just a single tile
@@ -773,19 +855,19 @@ class OptiXDevice : public CUDADevice {
input_stride *= rect_size.x;
TileInfo *tile_info = tile_info_mem.alloc(1);
- for (int i = 0; i < 9; i++) {
- tile_info->offsets[i] = rtiles[i].offset;
- tile_info->strides[i] = rtiles[i].stride;
- tile_info->buffers[i] = rtiles[i].buffer;
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ tile_info->offsets[i] = neighbors.tiles[i].offset;
+ tile_info->strides[i] = neighbors.tiles[i].stride;
+ tile_info->buffers[i] = neighbors.tiles[i].buffer;
}
- tile_info->x[0] = rtiles[3].x;
- tile_info->x[1] = rtiles[4].x;
- tile_info->x[2] = rtiles[5].x;
- tile_info->x[3] = rtiles[5].x + rtiles[5].w;
- tile_info->y[0] = rtiles[1].y;
- tile_info->y[1] = rtiles[4].y;
- tile_info->y[2] = rtiles[7].y;
- tile_info->y[3] = rtiles[7].y + rtiles[7].h;
+ tile_info->x[0] = neighbors.tiles[3].x;
+ tile_info->x[1] = neighbors.tiles[4].x;
+ tile_info->x[2] = neighbors.tiles[5].x;
+ tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w;
+ tile_info->y[0] = neighbors.tiles[1].y;
+ tile_info->y[1] = neighbors.tiles[4].y;
+ tile_info->y[2] = neighbors.tiles[7].y;
+ tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h;
tile_info_mem.copy_to_device();
void *args[] = {
@@ -795,7 +877,7 @@ class OptiXDevice : public CUDADevice {
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
device_only_memory<float> input_rgb(this, "denoiser input rgb");
- input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.optix_input_passes);
+ input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes);
void *input_args[] = {&input_rgb.device_pointer,
&input_ptr,
@@ -804,7 +886,7 @@ class OptiXDevice : public CUDADevice {
&input_stride,
&task.pass_stride,
const_cast<int *>(pass_offset),
- &task.denoising.optix_input_passes,
+ &task.denoising.input_passes,
&rtile.sample};
launch_filter_kernel(
"kernel_cuda_filter_convert_to_rgb", rect_size.x, rect_size.y, input_args);
@@ -815,7 +897,7 @@ class OptiXDevice : public CUDADevice {
# endif
const bool recreate_denoiser = (denoiser == NULL) ||
- (task.denoising.optix_input_passes != denoiser_input_passes);
+ (task.denoising.input_passes != denoiser_input_passes);
if (recreate_denoiser) {
// Destroy existing handle before creating new one
if (denoiser != NULL) {
@@ -824,23 +906,29 @@ class OptiXDevice : public CUDADevice {
// Create OptiX denoiser handle on demand when it is first used
OptixDenoiserOptions denoiser_options;
- assert(task.denoising.optix_input_passes >= 1 && task.denoising.optix_input_passes <= 3);
+ assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3);
denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>(
- OPTIX_DENOISER_INPUT_RGB + (task.denoising.optix_input_passes - 1));
+ OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1));
+# if OPTIX_ABI_VERSION < 28
denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3;
+# endif
check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser));
check_result_optix_ret(
optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0));
// OptiX denoiser handle was created with the requested number of input passes
- denoiser_input_passes = task.denoising.optix_input_passes;
+ denoiser_input_passes = task.denoising.input_passes;
}
OptixDenoiserSizes sizes = {};
check_result_optix_ret(
optixDenoiserComputeMemoryResources(denoiser, rect_size.x, rect_size.y, &sizes));
+# if OPTIX_ABI_VERSION < 28
const size_t scratch_size = sizes.recommendedScratchSizeInBytes;
+# else
+ const size_t scratch_size = sizes.withOverlapScratchSizeInBytes;
+# endif
const size_t scratch_offset = sizes.stateSizeInBytes;
// Allocate denoiser state if tile size has changed since last setup
@@ -888,10 +976,10 @@ class OptiXDevice : public CUDADevice {
int2 output_offset = overlap_offset;
overlap_offset = make_int2(0, 0); // Not supported by denoiser API, so apply manually
# else
- output_layers[0].data = rtiles[9].buffer + pixel_offset;
- output_layers[0].width = rtiles[9].w;
- output_layers[0].height = rtiles[9].h;
- output_layers[0].rowStrideInBytes = rtiles[9].stride * pixel_stride;
+ output_layers[0].data = target_tile.buffer + pixel_offset;
+ output_layers[0].width = target_tile.w;
+ output_layers[0].height = target_tile.h;
+ output_layers[0].rowStrideInBytes = target_tile.stride * pixel_stride;
output_layers[0].pixelStrideInBytes = pixel_stride;
# endif
output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3;
@@ -904,7 +992,7 @@ class OptiXDevice : public CUDADevice {
denoiser_state.device_pointer,
scratch_offset,
input_layers,
- task.denoising.optix_input_passes,
+ task.denoising.input_passes,
overlap_offset.x,
overlap_offset.y,
output_layers,
@@ -913,26 +1001,26 @@ class OptiXDevice : public CUDADevice {
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
void *output_args[] = {&input_ptr,
- &rtiles[9].buffer,
+ &target_tile.buffer,
&output_offset.x,
&output_offset.y,
&rect_size.x,
&rect_size.y,
- &rtiles[9].x,
- &rtiles[9].y,
- &rtiles[9].w,
- &rtiles[9].h,
- &rtiles[9].offset,
- &rtiles[9].stride,
+ &target_tile.x,
+ &target_tile.y,
+ &target_tile.w,
+ &target_tile.h,
+ &target_tile.offset,
+ &target_tile.stride,
&task.pass_stride,
&rtile.sample};
launch_filter_kernel(
- "kernel_cuda_filter_convert_from_rgb", rtiles[9].w, rtiles[9].h, output_args);
+ "kernel_cuda_filter_convert_from_rgb", target_tile.w, target_tile.h, output_args);
# endif
check_result_cuda_ret(cuStreamSynchronize(0));
- task.unmap_neighbor_tiles(rtiles, this);
+ task.unmap_neighbor_tiles(neighbors, this);
}
else {
// Run CUDA denoising kernels
@@ -984,7 +1072,11 @@ class OptiXDevice : public CUDADevice {
sbt_params.missRecordCount = 1;
sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord);
sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord);
- sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITL, PG_HITS
+# if OPTIX_ABI_VERSION >= 36
+ sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL
+# else
+ sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL
+# endif
check_result_optix(optixLaunch(pipelines[PIP_SHADER_EVAL],
cuda_stream[thread_index],
@@ -1061,7 +1153,7 @@ class OptiXDevice : public CUDADevice {
&build_input,
1,
temp_mem.device_pointer,
- temp_mem.device_size,
+ sizes.tempSizeInBytes,
out_data,
sizes.outputSizeInBytes,
&out_handle,
@@ -1133,7 +1225,6 @@ class OptiXDevice : public CUDADevice {
continue;
}
- const size_t num_curves = hair->num_curves();
const size_t num_segments = hair->num_segments();
size_t num_motion_steps = 1;
@@ -1143,7 +1234,18 @@ class OptiXDevice : public CUDADevice {
}
device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY);
- aabb_data.alloc(num_segments * num_motion_steps);
+# if OPTIX_ABI_VERSION >= 36
+ device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
+ device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
+ // Four control points for each curve segment
+ const size_t num_vertices = num_segments * 4;
+ if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
+ index_data.alloc(num_segments);
+ vertex_data.alloc(num_vertices * num_motion_steps);
+ }
+ else
+# endif
+ aabb_data.alloc(num_segments * num_motion_steps);
// Get AABBs for each motion step
for (size_t step = 0; step < num_motion_steps; ++step) {
@@ -1156,44 +1258,127 @@ class OptiXDevice : public CUDADevice {
keys = motion_keys->data_float3() + attr_offset * hair->curve_keys.size();
}
- size_t i = step * num_segments;
- for (size_t j = 0; j < num_curves; ++j) {
- const Hair::Curve c = hair->get_curve(j);
-
- for (size_t k = 0; k < c.num_segments(); ++i, ++k) {
- BoundBox bounds = BoundBox::empty;
- c.bounds_grow(k, keys, hair->curve_radius.data(), bounds);
-
- aabb_data[i].minX = bounds.min.x;
- aabb_data[i].minY = bounds.min.y;
- aabb_data[i].minZ = bounds.min.z;
- aabb_data[i].maxX = bounds.max.x;
- aabb_data[i].maxY = bounds.max.y;
- aabb_data[i].maxZ = bounds.max.z;
+ for (size_t j = 0, i = 0; j < hair->num_curves(); ++j) {
+ const Hair::Curve curve = hair->get_curve(j);
+
+ for (int segment = 0; segment < curve.num_segments(); ++segment, ++i) {
+# if OPTIX_ABI_VERSION >= 36
+ if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
+ int k0 = curve.first_key + segment;
+ int k1 = k0 + 1;
+ int ka = max(k0 - 1, curve.first_key);
+ int kb = min(k1 + 1, curve.first_key + curve.num_keys - 1);
+
+ const float4 px = make_float4(keys[ka].x, keys[k0].x, keys[k1].x, keys[kb].x);
+ const float4 py = make_float4(keys[ka].y, keys[k0].y, keys[k1].y, keys[kb].y);
+ const float4 pz = make_float4(keys[ka].z, keys[k0].z, keys[k1].z, keys[kb].z);
+ const float4 pw = make_float4(hair->curve_radius[ka],
+ hair->curve_radius[k0],
+ hair->curve_radius[k1],
+ hair->curve_radius[kb]);
+
+ // Convert Catmull-Rom data to Bezier spline
+ static const float4 cr2bsp0 = make_float4(+7, -4, +5, -2) / 6.f;
+ static const float4 cr2bsp1 = make_float4(-2, 11, -4, +1) / 6.f;
+ static const float4 cr2bsp2 = make_float4(+1, -4, 11, -2) / 6.f;
+ static const float4 cr2bsp3 = make_float4(-2, +5, -4, +7) / 6.f;
+
+ index_data[i] = i * 4;
+ float4 *const v = vertex_data.data() + step * num_vertices + index_data[i];
+ v[0] = make_float4(
+ dot(cr2bsp0, px), dot(cr2bsp0, py), dot(cr2bsp0, pz), dot(cr2bsp0, pw));
+ v[1] = make_float4(
+ dot(cr2bsp1, px), dot(cr2bsp1, py), dot(cr2bsp1, pz), dot(cr2bsp1, pw));
+ v[2] = make_float4(
+ dot(cr2bsp2, px), dot(cr2bsp2, py), dot(cr2bsp2, pz), dot(cr2bsp2, pw));
+ v[3] = make_float4(
+ dot(cr2bsp3, px), dot(cr2bsp3, py), dot(cr2bsp3, pz), dot(cr2bsp3, pw));
+ }
+ else
+# endif
+ {
+ BoundBox bounds = BoundBox::empty;
+ curve.bounds_grow(segment, keys, hair->curve_radius.data(), bounds);
+
+ const size_t index = step * num_segments + i;
+ aabb_data[index].minX = bounds.min.x;
+ aabb_data[index].minY = bounds.min.y;
+ aabb_data[index].minZ = bounds.min.z;
+ aabb_data[index].maxX = bounds.max.x;
+ aabb_data[index].maxY = bounds.max.y;
+ aabb_data[index].maxZ = bounds.max.z;
+ }
}
}
}
// Upload AABB data to GPU
aabb_data.copy_to_device();
+# if OPTIX_ABI_VERSION >= 36
+ index_data.copy_to_device();
+ vertex_data.copy_to_device();
+# endif
vector<device_ptr> aabb_ptrs;
aabb_ptrs.reserve(num_motion_steps);
+# if OPTIX_ABI_VERSION >= 36
+ vector<device_ptr> width_ptrs;
+ vector<device_ptr> vertex_ptrs;
+ width_ptrs.reserve(num_motion_steps);
+ vertex_ptrs.reserve(num_motion_steps);
+# endif
for (size_t step = 0; step < num_motion_steps; ++step) {
aabb_ptrs.push_back(aabb_data.device_pointer + step * num_segments * sizeof(OptixAabb));
+# if OPTIX_ABI_VERSION >= 36
+ const device_ptr base_ptr = vertex_data.device_pointer +
+ step * num_vertices * sizeof(float4);
+ width_ptrs.push_back(base_ptr + 3 * sizeof(float)); // Offset by vertex size
+ vertex_ptrs.push_back(base_ptr);
+# endif
}
- // Disable visibility test anyhit program, since it is already checked during intersection
- // Those trace calls that require anyhit can force it with OPTIX_RAY_FLAG_ENFORCE_ANYHIT
- unsigned int build_flags = OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT;
+ // Force a single any-hit call, so shadow record-all behavior works correctly
+ unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;
OptixBuildInput build_input = {};
- build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES;
- build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
- build_input.aabbArray.numPrimitives = num_segments;
- build_input.aabbArray.strideInBytes = sizeof(OptixAabb);
- build_input.aabbArray.flags = &build_flags;
- build_input.aabbArray.numSbtRecords = 1;
- build_input.aabbArray.primitiveIndexOffset = hair->optix_prim_offset;
+# if OPTIX_ABI_VERSION >= 36
+ if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
+ build_input.type = OPTIX_BUILD_INPUT_TYPE_CURVES;
+ build_input.curveArray.curveType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE;
+ build_input.curveArray.numPrimitives = num_segments;
+ build_input.curveArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data();
+ build_input.curveArray.numVertices = num_vertices;
+ build_input.curveArray.vertexStrideInBytes = sizeof(float4);
+ build_input.curveArray.widthBuffers = (CUdeviceptr *)width_ptrs.data();
+ build_input.curveArray.widthStrideInBytes = sizeof(float4);
+ build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer;
+ build_input.curveArray.indexStrideInBytes = sizeof(int);
+ build_input.curveArray.flag = build_flags;
+ build_input.curveArray.primitiveIndexOffset = hair->optix_prim_offset;
+ }
+ else
+# endif
+ {
+ // Disable visibility test any-hit program, since it is already checked during
+ // intersection. Those trace calls that require anyhit can force it with a ray flag.
+ build_flags |= OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT;
+
+ build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES;
+# if OPTIX_ABI_VERSION < 23
+ build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
+ build_input.aabbArray.numPrimitives = num_segments;
+ build_input.aabbArray.strideInBytes = sizeof(OptixAabb);
+ build_input.aabbArray.flags = &build_flags;
+ build_input.aabbArray.numSbtRecords = 1;
+ build_input.aabbArray.primitiveIndexOffset = hair->optix_prim_offset;
+# else
+ build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data();
+ build_input.customPrimitiveArray.numPrimitives = num_segments;
+ build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb);
+ build_input.customPrimitiveArray.flags = &build_flags;
+ build_input.customPrimitiveArray.numSbtRecords = 1;
+ build_input.customPrimitiveArray.primitiveIndexOffset = hair->optix_prim_offset;
+# endif
+ }
// Allocate memory for new BLAS and build it
OptixTraversableHandle handle;
@@ -1248,8 +1433,8 @@ class OptiXDevice : public CUDADevice {
vertex_ptrs.push_back(vertex_data.device_pointer + num_verts * step * sizeof(float3));
}
- // No special build flags for triangle primitives
- unsigned int build_flags = OPTIX_GEOMETRY_FLAG_NONE;
+ // Force a single any-hit call, so shadow record-all behavior works correctly
+ unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL;
OptixBuildInput build_input = {};
build_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES;
build_input.triangleArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data();
@@ -1315,9 +1500,26 @@ class OptiXDevice : public CUDADevice {
// Set user instance ID to object index
instance.instanceId = ob->get_device_index();
- // Volumes have a special bit set in the visibility mask so a trace can mask only volumes
- // See 'scene_intersect_volume' in bvh.h
- instance.visibilityMask = (ob->geometry->has_volume ? 3 : 1);
+ // Have to have at least one bit in the mask, or else instance would always be culled
+ instance.visibilityMask = 1;
+
+ if (ob->geometry->has_volume) {
+ // Volumes have a special bit set in the visibility mask so a trace can mask only volumes
+ instance.visibilityMask |= 2;
+ }
+
+ if (ob->geometry->type == Geometry::HAIR) {
+ // Same applies to curves (so they can be skipped in local trace calls)
+ instance.visibilityMask |= 4;
+
+# if OPTIX_ABI_VERSION >= 36
+ if (motion_blur && ob->geometry->has_motion_blur() && DebugFlags().optix.curves_api &&
+ static_cast<const Hair *>(ob->geometry)->curve_shape == CURVE_THICK) {
+ // Select between motion blur and non-motion blur built-in intersection module
+ instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
+ }
+# endif
+ }
// Insert motion traversable if object has motion
if (motion_blur && ob->use_motion()) {
@@ -1436,21 +1638,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 +1665,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 +1676,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 +1689,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 +1754,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..fd380788282 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
@@ -30,10 +29,44 @@ CCL_NAMESPACE_BEGIN
class Device;
class RenderBuffers;
class RenderTile;
+class RenderTileNeighbors;
class Tile;
+enum DenoiserType {
+ DENOISER_NLM = 1,
+ DENOISER_OPTIX = 2,
+ DENOISER_OPENIMAGEDENOISE = 4,
+ DENOISER_NUM,
+
+ DENOISER_NONE = 0,
+ DENOISER_ALL = ~0,
+};
+
+enum DenoiserInput {
+ DENOISER_INPUT_RGB = 1,
+ DENOISER_INPUT_RGB_ALBEDO = 2,
+ DENOISER_INPUT_RGB_ALBEDO_NORMAL = 3,
+
+ DENOISER_INPUT_NUM,
+};
+
+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 +80,36 @@ class DenoiseParams {
int neighbor_frames;
/* Clamp the input to the range of +-1e8. Should be enough for any legitimate data. */
bool clamp_input;
- /* Passes handed over to the OptiX denoiser (default to color + albedo). */
- int optix_input_passes;
+
+ /** OIDN/Optix Denoiser **/
+
+ /* Passes handed over to the OIDN/OptiX denoiser (default to color + albedo). */
+ DenoiserInput 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;
+
+ input_passes = DENOISER_INPUT_RGB_ALBEDO_NORMAL;
+
+ 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 +126,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 +149,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);
@@ -108,18 +159,14 @@ class DeviceTask : public Task {
function<void(RenderTile &)> update_tile_sample;
function<void(RenderTile &)> release_tile;
function<bool()> get_cancel;
- function<void(RenderTile *, Device *)> map_neighbor_tiles;
- function<void(RenderTile *, Device *)> unmap_neighbor_tiles;
+ function<void(RenderTileNeighbors &, Device *)> map_neighbor_tiles;
+ function<void(RenderTileNeighbors &, Device *)> unmap_neighbor_tiles;
uint tile_types;
DenoiseParams denoising;
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..e851749949d 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);
}
}
@@ -1845,7 +1850,7 @@ void OpenCLDevice::denoise(RenderTile &rtile, DenoisingTask &denoising)
denoising.render_buffer.samples = rtile.sample;
denoising.buffer.gpu_temporary_mem = true;
- denoising.run_denoising(&rtile);
+ denoising.run_denoising(rtile);
}
void OpenCLDevice::shader(DeviceTask &task)
@@ -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/device/opencl/memory_manager.cpp b/intern/cycles/device/opencl/memory_manager.cpp
index fedb3ea8c6a..0285dc969ec 100644
--- a/intern/cycles/device/opencl/memory_manager.cpp
+++ b/intern/cycles/device/opencl/memory_manager.cpp
@@ -64,6 +64,9 @@ void MemoryManager::DeviceBuffer::update_device_memory(OpenCLDevice *device)
total_size += alloc_size;
}
+ /* Always allocate non-empty buffer, NULL pointers cause problems with some drivers. */
+ total_size = max(total_size, 16);
+
if (need_realloc) {
cl_ulong max_buffer_size;
clGetDeviceInfo(
@@ -251,7 +254,7 @@ void MemoryManager::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg)
device->kernel_set_args(kernel, (*narg)++, *device_buffer.buffer);
}
else {
- device->kernel_set_args(kernel, (*narg)++, 0);
+ device->kernel_set_args(kernel, (*narg)++);
}
}
}
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 2e839a616e9..db146226dc7 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
@@ -549,7 +539,7 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
${SRC_UTIL_HEADERS}
COMMAND ${CUBIN_CC_ENV}
"$<TARGET_FILE:cycles_cubin_cc>"
- -target 30
+ -target 50
-ptx
-i ${CMAKE_CURRENT_SOURCE_DIR}/${input}
${cuda_flags}
@@ -573,7 +563,7 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
COMMAND
${CUDA_NVCC_EXECUTABLE}
--ptx
- -arch=sm_30
+ -arch=sm_50
${cuda_flags}
${input}
WORKING_DIRECTORY
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 9b9df883b62..3049f243ae9 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__ */
@@ -204,11 +172,11 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg,
0.0f,
ray->t,
ray->time,
- 0xFF,
+ 0xF,
OPTIX_RAY_FLAG_NONE,
+ 0, // SBT offset for PG_HITD
0,
0,
- 0, // SBT offset for PG_HITD
p0,
p1,
p2,
@@ -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__
@@ -309,12 +264,13 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg,
0.0f,
ray->t,
ray->time,
+ // Skip curves
+ 0x3,
// Need to always call into __anyhit__kernel_optix_local_hit
- 0xFF,
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
- 1,
+ 2, // SBT offset for PG_HITL
+ 0,
0,
- 0, // SBT offset for PG_HITL
p0,
p1,
p2,
@@ -419,12 +375,12 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg,
0.0f,
ray->t,
ray->time,
+ 0xF,
// Need to always call into __anyhit__kernel_optix_shadow_all_hit
- 0xFF,
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
- 2,
+ 1, // SBT offset for PG_HITS
+ 0,
0,
- 0, // SBT offset for PG_HITS
p0,
p1,
*num_hits,
@@ -476,21 +432,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__ */
@@ -516,12 +459,12 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg,
0.0f,
ray->t,
ray->time,
- // Visibility mask set to only intersect objects with volumes
- 0x02,
+ // Skip everything but volumes
+ 0x2,
OPTIX_RAY_FLAG_NONE,
+ 0, // SBT offset for PG_HITD
0,
0,
- 0, // SBT offset for PG_HITD
p0,
p1,
p2,
@@ -548,21 +491,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 +529,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..06d2c016f5b 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,63 @@ 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;
- 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;
+ sd->v = isect->v;
- 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;
+ /* 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));
+ const float3 Ng = normalize(P - P_inside);
+
+ sd->N = Ng;
+ sd->Ng = Ng;
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);
- }
-
- 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 +787,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_camera.h b/intern/cycles/kernel/kernel_camera.h
index 445cf9eb44b..efe46d5b0dd 100644
--- a/intern/cycles/kernel/kernel_camera.h
+++ b/intern/cycles/kernel/kernel_camera.h
@@ -379,7 +379,7 @@ ccl_device_inline void camera_sample(KernelGlobals *kg,
const int shutter_table_offset = kernel_data.cam.shutter_table_offset;
ray->time = lookup_table_read(kg, time, shutter_table_offset, SHUTTER_TABLE_SIZE);
/* TODO(sergey): Currently single rolling shutter effect type only
- * where scan-lines are acquired from top to bottom and whole scanline
+ * where scan-lines are acquired from top to bottom and whole scan-line
* is acquired at once (no delay in acquisition happens between pixels
* of single scan-line).
*
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_path.h b/intern/cycles/kernel/kernel_path.h
index ba46d84d158..c332d5ad3ec 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -284,19 +284,11 @@ ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg,
#ifdef __HOLDOUT__
if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) &&
(state->flag & PATH_RAY_TRANSPARENT_BACKGROUND)) {
+ const float3 holdout_weight = shader_holdout_apply(kg, sd);
if (kernel_data.background.transparent) {
- float3 holdout_weight;
- if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) {
- holdout_weight = make_float3(1.0f, 1.0f, 1.0f);
- }
- else {
- holdout_weight = shader_holdout_eval(kg, sd);
- }
- /* any throughput is ok, should all be identical here */
L->transparent += average(holdout_weight * throughput);
}
-
- if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) {
+ if (isequal_float3(holdout_weight, make_float3(1.0f, 1.0f, 1.0f))) {
return false;
}
}
@@ -673,11 +665,9 @@ ccl_device void kernel_path_trace(
kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray);
-# ifndef __KERNEL_OPTIX__
if (ray.t == 0.0f) {
return;
}
-# endif
/* Initialize state. */
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 9700aaba80f..e461e1642b6 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 */
@@ -1046,15 +1017,36 @@ ccl_device float3 shader_emissive_eval(ShaderData *sd)
/* Holdout */
-ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd)
+ccl_device float3 shader_holdout_apply(KernelGlobals *kg, ShaderData *sd)
{
float3 weight = make_float3(0.0f, 0.0f, 0.0f);
- for (int i = 0; i < sd->num_closure; i++) {
- ShaderClosure *sc = &sd->closure[i];
+ /* For objects marked as holdout, preserve transparency and remove all other
+ * closures, replacing them with a holdout weight. */
+ if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) {
+ if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) {
+ weight = make_float3(1.0f, 1.0f, 1.0f) - sd->closure_transparent_extinction;
- if (CLOSURE_IS_HOLDOUT(sc->type))
- weight += sc->weight;
+ for (int i = 0; i < sd->num_closure; i++) {
+ ShaderClosure *sc = &sd->closure[i];
+ if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) {
+ sc->type = NBUILTIN_CLOSURES;
+ }
+ }
+
+ sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF));
+ }
+ else {
+ weight = make_float3(1.0f, 1.0f, 1.0f);
+ }
+ }
+ else {
+ for (int i = 0; i < sd->num_closure; i++) {
+ ShaderClosure *sc = &sd->closure[i];
+ if (CLOSURE_IS_HOLDOUT(sc->type)) {
+ weight += sc->weight;
+ }
+ }
}
return weight;
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/opencl/kernel_opencl_image.h b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h
index 89fcb0ae60f..9ab374d1fba 100644
--- a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h
+++ b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h
@@ -77,7 +77,7 @@ ccl_device_inline float4 svm_image_texture_read(KernelGlobals *kg,
return make_float4(f, f, f, 1.0f);
}
/* Byte */
-#ifdef cl_khr_fp16
+#ifdef __KERNEL_CL_KHR_FP16__
/* half and half4 are optional in OpenCL */
else if (texture_type == IMAGE_DATA_TYPE_HALF) {
float f = tex_fetch(half, info, offset);
diff --git a/intern/cycles/kernel/kernels/optix/kernel_optix.cu b/intern/cycles/kernel/kernels/optix/kernel_optix.cu
index e03504316ad..3b166e59dfd 100644
--- a/intern/cycles/kernel/kernels/optix/kernel_optix.cu
+++ b/intern/cycles/kernel/kernels/optix/kernel_optix.cu
@@ -15,6 +15,7 @@
* limitations under the License.
*/
+// clang-format off
#include "kernel/kernel_compat_optix.h"
#include "util/util_atomic.h"
#include "kernel/kernel_types.h"
@@ -23,6 +24,7 @@
#include "kernel/kernel_path.h"
#include "kernel/kernel_bake.h"
+// clang-format on
template<typename T> ccl_device_forceinline T *get_payload_ptr_0()
{
@@ -139,8 +141,8 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
}
else {
if (local_isect->num_hits && optixGetRayTmax() > local_isect->hits[0].t) {
- // Record closest intersection only (do not terminate ray here, since there is no guarantee
- // about distance ordering in anyhit)
+ // Record closest intersection only
+ // Do not terminate ray here, since there is no guarantee about distance ordering in any-hit
return optixIgnoreIntersection();
}
@@ -153,15 +155,9 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit()
isect->object = get_object_id();
isect->type = kernel_tex_fetch(__prim_type, isect->prim);
- if (optixIsTriangleHit()) {
- const float2 barycentrics = optixGetTriangleBarycentrics();
- isect->u = 1.0f - barycentrics.y - barycentrics.x;
- isect->v = barycentrics.x;
- }
- else {
- isect->u = __uint_as_float(optixGetAttribute_0());
- isect->v = __uint_as_float(optixGetAttribute_1());
- }
+ const float2 barycentrics = optixGetTriangleBarycentrics();
+ isect->u = 1.0f - barycentrics.y - barycentrics.x;
+ isect->v = barycentrics.x;
// Record geometric normal
const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim);
@@ -198,10 +194,18 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
isect->u = 1.0f - barycentrics.y - barycentrics.x;
isect->v = barycentrics.x;
}
+# ifdef __HAIR__
else {
- isect->u = __uint_as_float(optixGetAttribute_0());
+ const float u = __uint_as_float(optixGetAttribute_0());
+ isect->u = u;
isect->v = __uint_as_float(optixGetAttribute_1());
+
+ // Filter out curve endcaps
+ if (u == 0.0f || u == 1.0f) {
+ return optixIgnoreIntersection();
+ }
}
+# endif
# ifdef __TRANSPARENT_SHADOWS__
// Detect if this surface has a shader with transparent shadows
@@ -213,7 +217,6 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
# ifdef __TRANSPARENT_SHADOWS__
}
- // TODO(pmours): Do we need REQUIRE_UNIQUE_ANYHIT for this to work?
optixSetPayload_2(optixGetPayload_2() + 1); // num_hits++
// Continue tracing
@@ -227,13 +230,25 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
uint visibility = optixGetPayload_4();
#ifdef __VISIBILITY_FLAG__
const uint prim = optixGetPrimitiveIndex();
- if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0)
+ if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) {
return optixIgnoreIntersection();
+ }
+#endif
+
+#ifdef __HAIR__
+ if (!optixIsTriangleHit()) {
+ // Filter out curve endcaps
+ const float u = __uint_as_float(optixGetAttribute_0());
+ if (u == 0.0f || u == 1.0f) {
+ return optixIgnoreIntersection();
+ }
+ }
#endif
// Shadow ray early termination
- if (visibility & PATH_RAY_SHADOW_OPAQUE)
+ if (visibility & PATH_RAY_SHADOW_OPAQUE) {
return optixTerminateRay();
+ }
}
extern "C" __global__ void __closesthit__kernel_optix_hit()
@@ -250,17 +265,15 @@ extern "C" __global__ void __closesthit__kernel_optix_hit()
optixSetPayload_2(__float_as_uint(barycentrics.x));
}
else {
- optixSetPayload_1(optixGetAttribute_0());
+ optixSetPayload_1(optixGetAttribute_0()); // Same as 'optixGetCurveParameter()'
optixSetPayload_2(optixGetAttribute_1());
}
}
#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,15 +295,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
#ifdef __KERNEL_DEBUG__
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..a12e7a9dc17 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,25 +116,122 @@ color sky_radiance_new(normal dir,
return xyz_to_rgb(x, y, z) * (M_2PI / 683);
}
-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,
- 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},
- output color Color = color(0.0, 0.0, 0.0))
+/* Nishita improved */
+vector geographical_to_direction(float lat, float lon)
+{
+ return vector(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat));
+}
+
+float precise_angle(vector a, vector b)
+{
+ return 2.0 * atan2(length(a - b), length(a + b));
+}
+
+color sky_radiance_nishita(vector dir, float nishita_data[10], string filename)
+{
+ /* definitions */
+ float sun_elevation = nishita_data[6];
+ float sun_rotation = nishita_data[7];
+ float angular_diameter = nishita_data[8];
+ float sun_intensity = nishita_data[9];
+ 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 = precise_angle(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 2 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) * sun_intensity;
+ }
+ }
+ else {
+ if (sun_elevation + half_angular > 0.0) {
+ y = dir_elevation / (sun_elevation + half_angular);
+ xyz = mix(pixel_bottom, pixel_top, y) * sun_intensity;
+ }
+ }
+ /* 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;
+ /* more pixels toward horizon compensation */
+ float y = 1.0 - sqrt(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 */
+ return xyz_to_rgb(xyz[0], xyz[1], xyz[2]);
+}
+
+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[10] = {0.0, 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;
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..f824184c1d4 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,209 @@ 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];
+ float sun_intensity = nishita_data[9];
+ 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 = precise_angle(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 2 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) * sun_intensity;
+ }
+ }
+ else {
+ if (sun_elevation + half_angular > 0.0f) {
+ y = dir_elevation / (sun_elevation + half_angular);
+ xyz = interp(pixel_bottom, pixel_top, y) * sun_intensity;
+ }
+ }
+ /* 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;
+ /* more pixels toward horizon compensation */
+ float y = safe_sqrtf(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 */
+ return xyz_to_rgb(kg, xyz);
+}
+
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[10];
+
+ 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;
+ nishita_data[9] = data.y;
+ uint texture_id = __float_as_uint(data.z);
+
+ /* 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..6a1335dc5dd 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -2,6 +2,7 @@
set(INC
..
../../glew-mx
+ ../../sky/include
)
set(INC_SYS
@@ -24,6 +25,7 @@ set(SRC
hair.cpp
image.cpp
image_oiio.cpp
+ image_sky.cpp
image_vdb.cpp
integrator.cpp
jitter.cpp
@@ -64,6 +66,7 @@ set(SRC_HEADERS
hair.h
image.h
image_oiio.h
+ image_sky.h
image_vdb.h
integrator.h
light.h
@@ -90,6 +93,7 @@ set(LIB
cycles_device
cycles_subd
cycles_util
+ bf_intern_sky
)
if(WITH_CYCLES_OSL)
diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h
index 975bae2239c..06b6094e6c9 100644
--- a/intern/cycles/render/buffers.h
+++ b/intern/cycles/render/buffers.h
@@ -52,7 +52,7 @@ class BufferParams {
/* passes */
vector<Pass> passes;
bool denoising_data_pass;
- /* If only some light path types should be denoised, an additional pass is needed. */
+ /* If only some light path types should be target, an additional pass is needed. */
bool denoising_clean_pass;
/* When we're prefiltering the passes during rendering, we need to keep both the
* original and the prefiltered data around because neighboring tiles might still
@@ -149,6 +149,50 @@ class RenderTile {
RenderBuffers *buffers;
RenderTile();
+
+ int4 bounds() const
+ {
+ return make_int4(x, /* xmin */
+ y, /* ymin */
+ x + w, /* xmax */
+ y + h); /* ymax */
+ }
+};
+
+/* Render Tile Neighbors
+ * Set of neighboring tiles used for denoising. Tile order:
+ * 0 1 2
+ * 3 4 5
+ * 6 7 8 */
+
+class RenderTileNeighbors {
+ public:
+ static const int SIZE = 9;
+ static const int CENTER = 4;
+
+ RenderTile tiles[SIZE];
+ RenderTile target;
+
+ RenderTileNeighbors(const RenderTile &center)
+ {
+ tiles[CENTER] = center;
+ }
+
+ int4 bounds() const
+ {
+ return make_int4(tiles[3].x, /* xmin */
+ tiles[1].y, /* ymin */
+ tiles[5].x + tiles[5].w, /* xmax */
+ tiles[7].y + tiles[7].h); /* ymax */
+ }
+
+ void set_bounds_from_center()
+ {
+ tiles[3].x = tiles[CENTER].x;
+ tiles[1].y = tiles[CENTER].y;
+ tiles[5].x = tiles[CENTER].x + tiles[CENTER].w;
+ tiles[7].y = tiles[CENTER].y + tiles[CENTER].h;
+ }
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp
index 74953afae9d..bbc111cb798 100644
--- a/intern/cycles/render/camera.cpp
+++ b/intern/cycles/render/camera.cpp
@@ -26,6 +26,7 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_math_cdf.h"
+#include "util/util_task.h"
#include "util/util_vector.h"
/* needed for calculating differentials */
@@ -496,20 +497,35 @@ void Camera::device_update_volume(Device * /*device*/, DeviceScene *dscene, Scen
if (!need_device_update && !need_flags_update) {
return;
}
- KernelCamera *kcam = &dscene->data.cam;
- BoundBox viewplane_boundbox = viewplane_bounds_get();
- for (size_t i = 0; i < scene->objects.size(); ++i) {
- Object *object = scene->objects[i];
- if (object->geometry->has_volume && viewplane_boundbox.intersects(object->bounds)) {
- /* TODO(sergey): Consider adding more grained check. */
- VLOG(1) << "Detected camera inside volume.";
- kcam->is_inside_volume = 1;
- break;
+
+ KernelIntegrator *kintegrator = &dscene->data.integrator;
+ if (kintegrator->use_volumes) {
+ KernelCamera *kcam = &dscene->data.cam;
+ BoundBox viewplane_boundbox = viewplane_bounds_get();
+
+ /* 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 *object = scene->objects[i];
+ if (object->geometry->has_volume &&
+ viewplane_boundbox.intersects(object->bounds)) {
+ /* TODO(sergey): Consider adding more grained check. */
+ VLOG(1) << "Detected camera inside volume.";
+ kcam->is_inside_volume = 1;
+ parallel_for_cancel();
+ break;
+ }
+ }
+ });
+
+ if (!kcam->is_inside_volume) {
+ VLOG(1) << "Camera is outside of the volume.";
}
}
- if (!kcam->is_inside_volume) {
- VLOG(1) << "Camera is outside of the volume.";
- }
+
need_device_update = false;
need_flags_update = false;
}
diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/render/colorspace.cpp
index 7605fcaf5ff..57979d5f225 100644
--- a/intern/cycles/render/colorspace.cpp
+++ b/intern/cycles/render/colorspace.cpp
@@ -266,7 +266,7 @@ inline void processor_apply_pixels(const OCIO::Processor *processor, T *pixels,
{
/* TODO: implement faster version for when we know the conversion
* is a simple matrix transform between linear spaces. In that case
- * unpremultiply is not needed. */
+ * un-premultiply is not needed. */
/* Process large images in chunks to keep temporary memory requirement down. */
const size_t chunk_size = std::min((size_t)(16 * 1024 * 1024), num_pixels);
@@ -354,7 +354,7 @@ void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
processor->applyRGB(pixel);
}
else {
- /* Unassociate and associate alpha since color management should not
+ /* Un-associate and associate alpha since color management should not
* be affected by transparency. */
float alpha = pixel[3];
float inv_alpha = 1.0f / alpha;
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..76408ca4849 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>
@@ -270,42 +271,45 @@ bool DenoiseTask::acquire_tile(Device *device, Device *tile_device, RenderTile &
*
* However, since there is only one large memory, the denoised result has to be written to
* a different buffer to avoid having to copy an entire horizontal slice of the image. */
-void DenoiseTask::map_neighboring_tiles(RenderTile *tiles, Device *tile_device)
+void DenoiseTask::map_neighboring_tiles(RenderTileNeighbors &neighbors, Device *tile_device)
{
+ RenderTile &center_tile = neighbors.tiles[RenderTileNeighbors::CENTER];
+ RenderTile &target_tile = neighbors.target;
+
/* Fill tile information. */
- for (int i = 0; i < 9; i++) {
- if (i == 4) {
+ for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
+ if (i == RenderTileNeighbors::CENTER) {
continue;
}
+ RenderTile &tile = neighbors.tiles[i];
int dx = (i % 3) - 1;
int dy = (i / 3) - 1;
- tiles[i].x = clamp(tiles[4].x + dx * denoiser->tile_size.x, 0, image.width);
- tiles[i].w = clamp(tiles[4].x + (dx + 1) * denoiser->tile_size.x, 0, image.width) - tiles[i].x;
- tiles[i].y = clamp(tiles[4].y + dy * denoiser->tile_size.y, 0, image.height);
- tiles[i].h = clamp(tiles[4].y + (dy + 1) * denoiser->tile_size.y, 0, image.height) -
- tiles[i].y;
+ tile.x = clamp(center_tile.x + dx * denoiser->tile_size.x, 0, image.width);
+ tile.w = clamp(center_tile.x + (dx + 1) * denoiser->tile_size.x, 0, image.width) - tile.x;
+ tile.y = clamp(center_tile.y + dy * denoiser->tile_size.y, 0, image.height);
+ tile.h = clamp(center_tile.y + (dy + 1) * denoiser->tile_size.y, 0, image.height) - tile.y;
- tiles[i].buffer = tiles[4].buffer;
- tiles[i].offset = tiles[4].offset;
- tiles[i].stride = image.width;
+ tile.buffer = center_tile.buffer;
+ tile.offset = center_tile.offset;
+ tile.stride = image.width;
}
/* Allocate output buffer. */
device_vector<float> *output_mem = new device_vector<float>(
tile_device, "denoising_output", MEM_READ_WRITE);
- output_mem->alloc(OUTPUT_NUM_CHANNELS * tiles[4].w * tiles[4].h);
+ output_mem->alloc(OUTPUT_NUM_CHANNELS * center_tile.w * center_tile.h);
/* Fill output buffer with noisy image, assumed by kernel_filter_finalize
* when skipping denoising of some pixels. */
float *result = output_mem->data();
- float *in = &image.pixels[image.num_channels * (tiles[4].y * image.width + tiles[4].x)];
+ float *in = &image.pixels[image.num_channels * (center_tile.y * image.width + center_tile.x)];
const DenoiseImageLayer &layer = image.layers[current_layer];
const int *input_to_image_channel = layer.input_to_image_channel.data();
- for (int y = 0; y < tiles[4].h; y++) {
- for (int x = 0; x < tiles[4].w; x++, result += OUTPUT_NUM_CHANNELS) {
+ for (int y = 0; y < center_tile.h; y++) {
+ for (int x = 0; x < center_tile.w; x++, result += OUTPUT_NUM_CHANNELS) {
for (int i = 0; i < OUTPUT_NUM_CHANNELS; i++) {
result[i] = in[image.num_channels * x + input_to_image_channel[INPUT_NOISY_IMAGE + i]];
}
@@ -316,35 +320,38 @@ void DenoiseTask::map_neighboring_tiles(RenderTile *tiles, Device *tile_device)
output_mem->copy_to_device();
/* Fill output tile info. */
- tiles[9] = tiles[4];
- tiles[9].buffer = output_mem->device_pointer;
- tiles[9].stride = tiles[9].w;
- tiles[9].offset -= tiles[9].x + tiles[9].y * tiles[9].stride;
+ target_tile = center_tile;
+ target_tile.buffer = output_mem->device_pointer;
+ target_tile.stride = target_tile.w;
+ target_tile.offset -= target_tile.x + target_tile.y * target_tile.stride;
thread_scoped_lock output_lock(output_mutex);
- assert(output_pixels.count(tiles[4].tile_index) == 0);
- output_pixels[tiles[9].tile_index] = output_mem;
+ assert(output_pixels.count(center_tile.tile_index) == 0);
+ output_pixels[target_tile.tile_index] = output_mem;
}
-void DenoiseTask::unmap_neighboring_tiles(RenderTile *tiles)
+void DenoiseTask::unmap_neighboring_tiles(RenderTileNeighbors &neighbors)
{
+ RenderTile &center_tile = neighbors.tiles[RenderTileNeighbors::CENTER];
+ RenderTile &target_tile = neighbors.target;
+
thread_scoped_lock output_lock(output_mutex);
- assert(output_pixels.count(tiles[4].tile_index) == 1);
- device_vector<float> *output_mem = output_pixels[tiles[9].tile_index];
- output_pixels.erase(tiles[4].tile_index);
+ assert(output_pixels.count(center_tile.tile_index) == 1);
+ device_vector<float> *output_mem = output_pixels[target_tile.tile_index];
+ output_pixels.erase(center_tile.tile_index);
output_lock.unlock();
/* Copy denoised pixels from device. */
- output_mem->copy_from_device(0, OUTPUT_NUM_CHANNELS * tiles[9].w, tiles[9].h);
+ output_mem->copy_from_device(0, OUTPUT_NUM_CHANNELS * target_tile.w, target_tile.h);
float *result = output_mem->data();
- float *out = &image.pixels[image.num_channels * (tiles[9].y * image.width + tiles[9].x)];
+ float *out = &image.pixels[image.num_channels * (target_tile.y * image.width + target_tile.x)];
const DenoiseImageLayer &layer = image.layers[current_layer];
const int *output_to_image_channel = layer.output_to_image_channel.data();
- for (int y = 0; y < tiles[9].h; y++) {
- for (int x = 0; x < tiles[9].w; x++, result += OUTPUT_NUM_CHANNELS) {
+ for (int y = 0; y < target_tile.h; y++) {
+ for (int x = 0; x < target_tile.w; x++, result += OUTPUT_NUM_CHANNELS) {
for (int i = 0; i < OUTPUT_NUM_CHANNELS; i++) {
out[image.num_channels * x + output_to_image_channel[i]] = result[i];
}
@@ -377,8 +384,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/denoising.h b/intern/cycles/render/denoising.h
index 5c6f913cb38..c1b4d0a5596 100644
--- a/intern/cycles/render/denoising.h
+++ b/intern/cycles/render/denoising.h
@@ -196,8 +196,8 @@ class DenoiseTask {
/* Device task callbacks */
bool acquire_tile(Device *device, Device *tile_device, RenderTile &tile);
- void map_neighboring_tiles(RenderTile *tiles, Device *tile_device);
- void unmap_neighboring_tiles(RenderTile *tiles);
+ void map_neighboring_tiles(RenderTileNeighbors &neighbors, Device *tile_device);
+ void unmap_neighboring_tiles(RenderTileNeighbors &neighbors);
void release_tile();
bool get_cancel();
};
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/graph.cpp b/intern/cycles/render/graph.cpp
index d2db59894ea..436324b00ba 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -942,12 +942,12 @@ void ShaderGraph::refine_bump_nodes()
foreach (NodePair &pair, nodes_dy)
add(pair.second);
- /* connect what is connected is bump to samplecenter input*/
+ /* Connect what is connected is bump to sample-center input. */
connect(out, node->input("SampleCenter"));
- /* bump input is just for connectivity purpose for the graph input,
- * we re-connected this input to samplecenter, so lets disconnect it
- * from bump input */
+ /* Bump input is just for connectivity purpose for the graph input,
+ * we re-connected this input to sample-center, so lets disconnect it
+ * from bump input. */
disconnect(bump_input);
}
}
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..0560907c63e
--- /dev/null
+++ b/intern/cycles/render/image_sky.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "sky_model.h"
+
+#include "util/util_image.h"
+#include "util/util_logging.h"
+#include "util/util_path.h"
+#include "util/util_task.h"
+
+CCL_NAMESPACE_BEGIN
+
+SkyLoader::SkyLoader(float sun_elevation,
+ float 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;
+
+ /* 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) {
+ SKY_nishita_skymodel_precompute_texture(pixel_data,
+ metadata.channels,
+ r.begin(),
+ r.end(),
+ width,
+ height,
+ sun_elevation,
+ altitude,
+ 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..686f4e5b885
--- /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;
+ float altitude;
+ float air_density;
+ float dust_density;
+ float ozone_density;
+
+ public:
+ SkyLoader(float sun_elevation,
+ float 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..183c02cb6b9 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,85 @@ 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;
+ }
+ }
+
+ /* Determine sun direction from lat/long and texture mapping. */
+ float latitude = sky->sun_elevation;
+ float longitude = M_2PI_F - sky->sun_rotation + M_PI_2_F;
+ float3 sun_direction = make_float3(
+ cosf(latitude) * cosf(longitude), cosf(latitude) * sinf(longitude), sinf(latitude));
+ Transform sky_transform = transform_inverse(sky->tex_mapping.compute_transform());
+ sun_direction = transform_direction(&sky_transform, sun_direction);
+
+ /* Pack sun direction and size. */
+ float half_angle = sky->sun_size * 0.5f;
+ kbackground->sun = make_float4(
+ sun_direction.x, sun_direction.y, sun_direction.z, 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 +671,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 +686,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..d5f65fb54db 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"
@@ -26,9 +27,10 @@
#include "render/scene.h"
#include "render/svm.h"
+#include "sky_model.h"
+
#include "util/util_foreach.h"
#include "util/util_logging.h"
-#include "util/util_sky_model.h"
#include "util/util_transform.h"
#include "kernel/svm/svm_color_util.h"
@@ -630,7 +632,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[10];
} SunSky;
/* Preetham model */
@@ -640,7 +642,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 +705,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);
@@ -725,8 +727,8 @@ static void sky_texture_precompute_new(SunSky *sunsky,
float solarElevation = M_PI_2_F - theta;
/* Initialize Sky Model */
- ArHosekSkyModelState *sky_state;
- sky_state = arhosek_xyz_skymodelstate_alloc_init(
+ SKY_ArHosekSkyModelState *sky_state;
+ sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init(
(double)turbidity, (double)ground_albedo, (double)solarElevation);
/* Copy values from sky_state to SunSky */
@@ -740,7 +742,42 @@ static void sky_texture_precompute_new(SunSky *sunsky,
sunsky->radiance_z = (float)sky_state->radiances[2];
/* Free sky_state */
- arhosekskymodelstate_free(sky_state);
+ SKY_arhosekskymodelstate_free(sky_state);
+}
+
+/* Nishita improved */
+static void sky_texture_precompute_nishita(SunSky *sunsky,
+ bool sun_disc,
+ float sun_size,
+ float sun_intensity,
+ float sun_elevation,
+ float sun_rotation,
+ float altitude,
+ float air_density,
+ float dust_density)
+{
+ /* sample 2 sun pixels */
+ float pixel_bottom[3];
+ float pixel_top[3];
+ SKY_nishita_skymodel_precompute_sun(
+ sun_elevation, sun_size, altitude, air_density, dust_density, pixel_bottom, pixel_top);
+ /* limit sun rotation between 0 and 360 degrees */
+ sun_rotation = fmodf(sun_rotation, M_2PI_F);
+ if (sun_rotation < 0.0f) {
+ sun_rotation += M_2PI_F;
+ }
+ sun_rotation = M_2PI_F - sun_rotation;
+ /* 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] = sun_rotation;
+ sunsky->nishita_data[8] = sun_disc ? sun_size : 0.0f;
+ sunsky->nishita_data[9] = sun_intensity;
}
NODE_DEFINE(SkyTextureNode)
@@ -750,13 +787,23 @@ 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_intensity, "Sun Intensity", 1.0f);
+ SOCKET_FLOAT(sun_elevation, "Sun Elevation", 15.0f * M_PI_F / 180.0f);
+ SOCKET_FLOAT(sun_rotation, "Sun Rotation", 0.0f);
+ SOCKET_FLOAT(altitude, "Altitude", 1.0f);
+ 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 +823,37 @@ 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) {
+ /* Clamp altitude to reasonable values.
+ * Below 1m causes numerical issues and above 60km is space. */
+ float clamped_altitude = clamp(altitude, 1.0f, 59999.0f);
+
+ sky_texture_precompute_nishita(&sunsky,
+ sun_disc,
+ sun_size,
+ sun_intensity,
+ sun_elevation,
+ sun_rotation,
+ clamped_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, clamped_altitude, air_density, dust_density, ozone_density);
+ handle = image_manager->add_image(loader, impar);
+ }
+ }
else
assert(false);
@@ -787,38 +861,55 @@ 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]),
+ __float_as_uint(sunsky.nishita_data[9]),
+ handle.svm_slot(),
+ 0);
+ }
tex_mapping.compile_end(compiler, vector_in, vector_offset);
}
@@ -828,10 +919,37 @@ 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) {
+ /* Clamp altitude to reasonable values.
+ * Below 1m causes numerical issues and above 60km is space. */
+ float clamped_altitude = clamp(altitude, 1.0f, 59999.0f);
+
+ sky_texture_precompute_nishita(&sunsky,
+ sun_disc,
+ sun_size,
+ sun_intensity,
+ sun_elevation,
+ sun_rotation,
+ clamped_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, clamped_altitude, air_density, dust_density, ozone_density);
+ handle = image_manager->add_image(loader, impar);
+ }
+ }
else
assert(false);
@@ -843,6 +961,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, 10);
+ /* 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..326f1d14168 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -168,7 +168,17 @@ class SkyTextureNode : public TextureNode {
float3 sun_direction;
float turbidity;
float ground_albedo;
+ bool sun_disc;
+ float sun_size;
+ float sun_intensity;
+ float sun_elevation;
+ float sun_rotation;
+ float 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..f200e409b9e 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);
@@ -870,6 +823,12 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
Mesh *mesh = static_cast<Mesh *>(geom);
apply = apply && mesh->subdivision_type == Mesh::SUBDIVISION_NONE;
}
+ else if (geom->type == Geometry::HAIR) {
+ /* Can't apply non-uniform scale to curves, this can't be represented by
+ * control points and radius alone. */
+ float scale;
+ apply = apply && transform_uniform_scale(object->tfm, scale);
+ }
if (apply) {
if (!(motion_blur && object->use_motion())) {
@@ -885,22 +844,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 7c50140ecfe..c5033359c6b 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;
@@ -531,7 +536,7 @@ void Session::release_tile(RenderTile &rtile, const bool need_denoise)
denoising_cond.notify_all();
}
-void Session::map_neighbor_tiles(RenderTile *tiles, Device *tile_device)
+void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device)
{
thread_scoped_lock tile_lock(tile_mutex);
@@ -541,75 +546,77 @@ void Session::map_neighbor_tiles(RenderTile *tiles, Device *tile_device)
tile_manager.state.buffer.full_x + tile_manager.state.buffer.width,
tile_manager.state.buffer.full_y + tile_manager.state.buffer.height);
+ RenderTile &center_tile = neighbors.tiles[RenderTileNeighbors::CENTER];
+
if (!tile_manager.schedule_denoising) {
/* Fix up tile slices with overlap. */
if (tile_manager.slice_overlap != 0) {
- int y = max(tiles[4].y - tile_manager.slice_overlap, image_region.y);
- tiles[4].h = min(tiles[4].y + tiles[4].h + tile_manager.slice_overlap, image_region.w) - y;
- tiles[4].y = y;
+ int y = max(center_tile.y - tile_manager.slice_overlap, image_region.y);
+ center_tile.h = min(center_tile.y + center_tile.h + tile_manager.slice_overlap,
+ image_region.w) -
+ y;
+ center_tile.y = y;
}
/* Tiles are not being denoised individually, which means the entire image is processed. */
- tiles[3].x = tiles[4].x;
- tiles[1].y = tiles[4].y;
- tiles[5].x = tiles[4].x + tiles[4].w;
- tiles[7].y = tiles[4].y + tiles[4].h;
+ neighbors.set_bounds_from_center();
}
else {
- int center_idx = tiles[4].tile_index;
+ int center_idx = center_tile.tile_index;
assert(tile_manager.state.tiles[center_idx].state == Tile::DENOISE);
for (int dy = -1, i = 0; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++, i++) {
+ RenderTile &rtile = neighbors.tiles[i];
int nindex = tile_manager.get_neighbor_index(center_idx, i);
if (nindex >= 0) {
Tile *tile = &tile_manager.state.tiles[nindex];
- tiles[i].x = image_region.x + tile->x;
- tiles[i].y = image_region.y + tile->y;
- tiles[i].w = tile->w;
- tiles[i].h = tile->h;
+ rtile.x = image_region.x + tile->x;
+ rtile.y = image_region.y + tile->y;
+ rtile.w = tile->w;
+ rtile.h = tile->h;
if (buffers) {
- tile_manager.state.buffer.get_offset_stride(tiles[i].offset, tiles[i].stride);
+ tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride);
- tiles[i].buffer = buffers->buffer.device_pointer;
- tiles[i].buffers = buffers;
+ rtile.buffer = buffers->buffer.device_pointer;
+ rtile.buffers = buffers;
}
else {
assert(tile->buffers);
- tile->buffers->params.get_offset_stride(tiles[i].offset, tiles[i].stride);
+ tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride);
- tiles[i].buffer = tile->buffers->buffer.device_pointer;
- tiles[i].buffers = tile->buffers;
+ rtile.buffer = tile->buffers->buffer.device_pointer;
+ rtile.buffers = tile->buffers;
}
}
else {
- int px = tiles[4].x + dx * params.tile_size.x;
- int py = tiles[4].y + dy * params.tile_size.y;
+ int px = center_tile.x + dx * params.tile_size.x;
+ int py = center_tile.y + dy * params.tile_size.y;
- tiles[i].x = clamp(px, image_region.x, image_region.z);
- tiles[i].y = clamp(py, image_region.y, image_region.w);
- tiles[i].w = tiles[i].h = 0;
+ rtile.x = clamp(px, image_region.x, image_region.z);
+ rtile.y = clamp(py, image_region.y, image_region.w);
+ rtile.w = rtile.h = 0;
- tiles[i].buffer = (device_ptr)NULL;
- tiles[i].buffers = NULL;
+ rtile.buffer = (device_ptr)NULL;
+ rtile.buffers = NULL;
}
}
}
}
- assert(tiles[4].buffers);
- device->map_neighbor_tiles(tile_device, tiles);
+ assert(center_tile.buffers);
+ device->map_neighbor_tiles(tile_device, neighbors);
/* The denoised result is written back to the original tile. */
- tiles[9] = tiles[4];
+ neighbors.target = center_tile;
}
-void Session::unmap_neighbor_tiles(RenderTile *tiles, Device *tile_device)
+void Session::unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device)
{
thread_scoped_lock tile_lock(tile_mutex);
- device->unmap_neighbor_tiles(tile_device, tiles);
+ device->unmap_neighbor_tiles(tile_device, neighbors);
}
void Session::run_cpu()
@@ -773,6 +780,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 +812,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;
}
@@ -833,7 +841,7 @@ bool Session::load_kernels(bool lock_scene)
message = "Failed loading render kernel, see console for errors";
progress.set_error(message);
- progress.set_status("Error", message);
+ progress.set_status(message);
progress.set_update();
return false;
}
@@ -872,7 +880,7 @@ void Session::run()
/* progress update */
if (progress.get_cancel())
- progress.set_status("Cancel", progress.get_cancel_message());
+ progress.set_status(progress.get_cancel_message());
else
progress.set_update();
}
@@ -941,24 +949,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;
- params.run_denoising = denoising;
- params.full_denoising = !optix_denoising;
- params.optix_denoising = optix_denoising;
+ if (!(params.device.denoisers & denoising.type)) {
+ if (need_denoise) {
+ progress.set_error("Denoiser type not supported by compute device");
+ }
+
+ 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();
}
@@ -986,7 +1005,7 @@ bool Session::update_scene()
int height = tile_manager.state.buffer.full_height;
int resolution = tile_manager.state.resolution_divider;
- if (width != cam->width || height != cam->height) {
+ if (width != cam->width || height != cam->height || resolution != cam->resolution) {
cam->width = width;
cam->height = height;
cam->resolution = resolution;
@@ -1078,10 +1097,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());
}
}
@@ -1109,8 +1128,13 @@ bool Session::render_need_denoise(bool &delayed)
{
delayed = false;
+ /* Not supported yet for baking. */
+ if (read_bake_tile_cb) {
+ return false;
+ }
+
/* Denoising enabled? */
- if (!params.run_denoising) {
+ if (!params.denoising.need_denoising_task()) {
return false;
}
@@ -1127,7 +1151,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 +1202,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..e3ac054ead3 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();
@@ -206,8 +198,8 @@ class Session {
void update_tile_sample(RenderTile &tile);
void release_tile(RenderTile &tile, const bool need_denoise);
- void map_neighbor_tiles(RenderTile *tiles, Device *tile_device);
- void unmap_neighbor_tiles(RenderTile *tiles, Device *tile_device);
+ void map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device);
+ void unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device);
bool device_use_gl;
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..f5e488d1bd2 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
@@ -97,11 +98,9 @@ set(SRC_HEADERS
util_rect.h
util_set.h
util_simd.h
- util_sky_model.cpp
- util_sky_model.h
- util_sky_model_data.h
util_avxf.h
util_avxb.h
+ util_avxi.h
util_semaphore.h
util_sseb.h
util_ssef.h
@@ -112,6 +111,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..74ecefa1917 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;
}
@@ -94,6 +83,7 @@ DebugFlags::OptiX::OptiX()
void DebugFlags::OptiX::reset()
{
cuda_streams = 1;
+ curves_api = false;
}
DebugFlags::OpenCL::OpenCL() : device_type(DebugFlags::OpenCL::DEVICE_ALL), debug(false)
diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h
index cf6b442b878..6ac4beb55b8 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;
@@ -108,6 +108,9 @@ class DebugFlags {
/* Number of CUDA streams to launch kernels concurrently from. */
int cuda_streams;
+
+ /* Use OptiX curves API for hair instead of custom implementation. */
+ bool curves_api;
};
/* Descriptor of OpenCL feature-set to be used. */
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index 737c834e073..8caabf6eac3 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -787,6 +787,16 @@ ccl_device_inline float compare_floats(float a, float b, float abs_diff, int ulp
return (abs(__float_as_int(a) - __float_as_int(b)) < ulp_diff);
}
+/* Calculate the angle between the two vectors a and b.
+ * The usual approach acos(dot(a, b)) has severe precision issues for small angles,
+ * which are avoided by this method.
+ * Based on "Mangled Angles" from https://people.eecs.berkeley.edu/~wkahan/Mindless.pdf
+ */
+ccl_device_inline float precise_angle(float3 a, float3 b)
+{
+ return 2.0f * atan2f(len(a - b), len(a + b));
+}
+
CCL_NAMESPACE_END
#endif /* __UTIL_MATH_H__ */
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_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..206ba106ca6
--- /dev/null
+++ b/intern/cycles/util/util_tbb.h
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+static inline void parallel_for_cancel()
+{
+ tbb::task::self().cancel_group_execution();
+}
+
+CCL_NAMESPACE_END
+
+#endif /* __UTIL_TBB_H__ */
diff --git a/intern/cycles/util/util_thread.h b/intern/cycles/util/util_thread.h
index f6dbc9186b8..29f9becbefe 100644
--- a/intern/cycles/util/util_thread.h
+++ b/intern/cycles/util/util_thread.h
@@ -29,9 +29,9 @@
# include <pthread.h>
#endif
-#ifdef __APPLE__
-# include <libkern/OSAtomic.h>
-#endif
+/* NOTE: Use tbb/spin_mutex.h instead of util_tbb.h because some of the TBB
+ * functionality requires RTTI, which is disabled for OSL kernel. */
+#include <tbb/spin_mutex.h>
#include "util/util_function.h"
@@ -65,76 +65,7 @@ class thread {
int node_;
};
-/* Own wrapper around pthread's spin lock to make it's use easier. */
-
-class thread_spin_lock {
- public:
-#ifdef __APPLE__
- inline thread_spin_lock()
- {
- spin_ = OS_SPINLOCK_INIT;
- }
-
- inline void lock()
- {
- OSSpinLockLock(&spin_);
- }
-
- inline void unlock()
- {
- OSSpinLockUnlock(&spin_);
- }
-#elif defined(_WIN32)
- inline thread_spin_lock()
- {
- const DWORD SPIN_COUNT = 50000;
- InitializeCriticalSectionAndSpinCount(&cs_, SPIN_COUNT);
- }
-
- inline ~thread_spin_lock()
- {
- DeleteCriticalSection(&cs_);
- }
-
- inline void lock()
- {
- EnterCriticalSection(&cs_);
- }
-
- inline void unlock()
- {
- LeaveCriticalSection(&cs_);
- }
-#else
- inline thread_spin_lock()
- {
- pthread_spin_init(&spin_, 0);
- }
-
- inline ~thread_spin_lock()
- {
- pthread_spin_destroy(&spin_);
- }
-
- inline void lock()
- {
- pthread_spin_lock(&spin_);
- }
-
- inline void unlock()
- {
- pthread_spin_unlock(&spin_);
- }
-#endif
- protected:
-#ifdef __APPLE__
- OSSpinLock spin_;
-#elif defined(_WIN32)
- CRITICAL_SECTION cs_;
-#else
- pthread_spinlock_t spin_;
-#endif
-};
+using thread_spin_lock = tbb::spin_mutex;
class thread_scoped_spin_lock {
public:
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/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 699ac4afe88..77e777db872 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -429,6 +429,7 @@ if(WITH_XR_OPENXR)
GHOST_IXrContext.h
intern/GHOST_IXrGraphicsBinding.h
intern/GHOST_XrContext.h
+ intern/GHOST_XrException.h
intern/GHOST_XrSession.h
intern/GHOST_XrSwapchain.h
intern/GHOST_Xr_intern.h
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index c5e2caa54e4..266830efa49 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -362,8 +362,8 @@ extern GHOST_TSuccess GHOST_HasCursorShape(GHOST_WindowHandle windowhandle,
* \param mask The mask data for the cursor.
* \param sizex The width of the cursor
* \param sizey The height of the cursor
- * \param hotX The X coordinate of the cursor hotspot.
- * \param hotY The Y coordinate of the cursor hotspot.
+ * \param hotX The X coordinate of the cursor hot-spot.
+ * \param hotY The Y coordinate of the cursor hot-spot.
* \param canInvertColor Let macOS invert cursor color to match platform convention.
* \return Indication of success.
*/
diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h
index 62290d20f1c..9c72b6f07f9 100644
--- a/intern/ghost/GHOST_IWindow.h
+++ b/intern/ghost/GHOST_IWindow.h
@@ -287,8 +287,8 @@ class GHOST_IWindow {
* Set the shape of the cursor to a custom cursor.
* \param bitmap The bitmap data for the cursor.
* \param mask The mask data for the cursor.
- * \param hotX The X coordinate of the cursor hotspot.
- * \param hotY The Y coordinate of the cursor hotspot.
+ * \param hotX The X coordinate of the cursor hot-spot.
+ * \param hotY The Y coordinate of the cursor hot-spot.
* \return Indication of success.
*/
virtual GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap,
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_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 5c1f34e3a63..96073c21e79 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -1324,7 +1324,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
int revert_to;
/* as ICCCM say, we need reply this event
- * with a SetInputFocus, the data[1] have
+ * with a #SetInputFocus, the data[1] have
* the valid timestamp (send by the wm).
*
* Some WM send this event before the
@@ -1345,7 +1345,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
else {
#ifdef WITH_XDND
/* try to handle drag event
- * (if there's no such events, GHOST_HandleClientMessage will return zero) */
+ * (if there's no such events, #GHOST_HandleClientMessage will return zero) */
if (window->getDropTarget()->GHOST_HandleClientMessage(xe) == false) {
/* Unknown client message, ignore */
}
@@ -1366,12 +1366,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case EnterNotify:
case LeaveNotify: {
- /* XCrossingEvents pointer leave enter window.
- * also do cursor move here, MotionNotify only
+ /* #XCrossingEvents pointer leave enter window.
+ * also do cursor move here, #MotionNotify only
* happens when motion starts & ends inside window.
* we only do moves when the crossing mode is 'normal'
- * (really crossing between windows) since some windowmanagers
- * also send grab/ungrab crossings for mousewheel events.
+ * (really crossing between windows) since some window-managers
+ * also send grab/un-grab crossings for mouse-wheel events.
*/
XCrossingEvent &xce = xe->xcrossing;
if (xce.mode == NotifyNormal) {
@@ -1396,11 +1396,11 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case MapNotify:
/*
* From ICCCM:
- * [ Clients can select for StructureNotify on their
+ * [ Clients can select for #StructureNotify on their
* top-level windows to track transition between
- * Normal and Iconic states. Receipt of a MapNotify
+ * Normal and Iconic states. Receipt of a #MapNotify
* event will indicate a transition to the Normal
- * state, and receipt of an UnmapNotify event will
+ * state, and receipt of an #UnmapNotify event will
* indicate a transition to the Iconic state. ]
*/
if (window->m_post_init == True) {
@@ -1441,7 +1441,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
nxe.xselection.target = xse->target;
nxe.xselection.time = xse->time;
- /* Check to see if the requestor is asking for String */
+ /* Check to see if the requester is asking for String */
if (xse->target == utf8_string || xse->target == string || xse->target == compound_text ||
xse->target == c_string) {
if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) {
@@ -1487,7 +1487,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
nxe.xselection.property = None;
}
- /* Send the event to the client 0 0 == False, SelectionNotify */
+ /* Send the event to the client 0 0 == False, #SelectionNotify */
XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
XFlush(m_display);
break;
@@ -1513,7 +1513,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
/* Note: This event might be generated with incomplete data-set
* (don't exactly know why, looks like in some cases, if the value does not change,
- * it is not included in subsequent XDeviceMotionEvent events).
+ * it is not included in subsequent #XDeviceMotionEvent events).
* So we have to check which values this event actually contains!
*/
@@ -1569,14 +1569,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
GHOST_TSuccess GHOST_SystemX11::getModifierKeys(GHOST_ModifierKeys &keys) const
{
- /* Analyze the masks returned from XQueryPointer. */
+ /* Analyze the masks returned from #XQueryPointer. */
memset((void *)m_keyboard_vector, 0, sizeof(m_keyboard_vector));
XQueryKeymap(m_display, (char *)m_keyboard_vector);
- /* now translate key symbols into keycodes and
- * test with vector. */
+ /* Now translate key symbols into key-codes and test with vector. */
const static KeyCode shift_l = XKeysymToKeycode(m_display, XK_Shift_L);
const static KeyCode shift_r = XKeysymToKeycode(m_display, XK_Shift_R);
@@ -1671,7 +1670,7 @@ GHOST_TSuccess GHOST_SystemX11::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y
{
/* This is a brute force move in screen coordinates
- * XWarpPointer does relative moves so first determine the
+ * #XWarpPointer does relative moves so first determine the
* current pointer position. */
int cx, cy;
diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h
index 472149148e6..7cfea5110c5 100644
--- a/intern/ghost/intern/GHOST_Window.h
+++ b/intern/ghost/intern/GHOST_Window.h
@@ -34,7 +34,7 @@ class GHOST_Context;
* Dimensions are given in screen coordinates that are relative to the
* upper-left corner of the screen.
* Implements part of the GHOST_IWindow interface and adds some methods to
- * be implemented by childs of this class.
+ * be implemented by sub-classes of this class.
*/
class GHOST_Window : public GHOST_IWindow {
public:
@@ -124,8 +124,8 @@ class GHOST_Window : public GHOST_IWindow {
* Set the shape of the cursor to a custom cursor.
* \param bitmap The bitmap data for the cursor.
* \param mask The mask data for the cursor.
- * \param hotX The X coordinate of the cursor hotspot.
- * \param hotY The Y coordinate of the cursor hotspot.
+ * \param hotX The X coordinate of the cursor hot-spot.
+ * \param hotY The Y coordinate of the cursor hot-spot.
* \return Indication of success.
*/
GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap,
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index ef02db7abc3..fe65162d168 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -180,6 +180,10 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
wl_surface_commit(w->surface);
wl_display_roundtrip(m_system->display());
+#ifdef GHOST_OPENGL_ALPHA
+ setOpaque();
+#endif
+
setState(state);
setTitle(title);
@@ -214,6 +218,10 @@ GHOST_TSuccess GHOST_WindowWayland::deactivate()
GHOST_TSuccess GHOST_WindowWayland::notify_size()
{
+#ifdef GHOST_OPENGL_ALPHA
+ setOpaque();
+#endif
+
return m_system->pushEvent(
new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this));
}
@@ -385,6 +393,19 @@ bool GHOST_WindowWayland::isDialog() const
return w->is_dialog;
}
+#ifdef GHOST_OPENGL_ALPHA
+void GHOST_WindowWayland::setOpaque() const
+{
+ struct wl_region *region;
+
+ /* Make the window opaque. */
+ region = wl_compositor_create_region(m_system->compositor());
+ wl_region_add(region, 0, 0, w->width, w->height);
+ wl_surface_set_opaque_region(w->surface, region);
+ wl_region_destroy(region);
+}
+#endif
+
/**
* \param type The type of rendering context create.
* \return Indication of success.
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index 23e55fcd6e4..ff1eb60e154 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -109,6 +109,10 @@ class GHOST_WindowWayland : public GHOST_Window {
bool isDialog() const override;
+#ifdef GHOST_OPENGL_ALPHA
+ void setOpaque() const;
+#endif
+
private:
GHOST_SystemWayland *m_system;
struct window_t *w;
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 5d7f7437cf8..f81814cff4d 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -126,6 +126,7 @@ void GHOST_XrContext::storeInstanceProperties()
const std::map<std::string, GHOST_TXrOpenXRRuntimeID> runtime_map = {
{"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO},
{"Oculus", OPENXR_RUNTIME_OCULUS},
+ {"SteamVR/OpenXR", OPENXR_RUNTIME_STEAMVR},
{"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}};
decltype(runtime_map)::const_iterator runtime_map_iter;
diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h
index 083af4ae8f8..5f4154fb788 100644
--- a/intern/ghost/intern/GHOST_XrContext.h
+++ b/intern/ghost/intern/GHOST_XrContext.h
@@ -49,6 +49,7 @@ struct GHOST_XrCustomFuncs {
enum GHOST_TXrOpenXRRuntimeID {
OPENXR_RUNTIME_MONADO,
OPENXR_RUNTIME_OCULUS,
+ OPENXR_RUNTIME_STEAMVR,
OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */
OPENXR_RUNTIME_UNKNOWN
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 5658c504ee1..d2ccd2593cb 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -783,14 +783,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). */
- return (context->getOpenXRRuntimeID() == OPENXR_RUNTIME_MONADO);
-}
-
void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain,
XrCompositionLayerProjectionView &r_proj_layer_view,
XrSpaceLocation &view_location,
@@ -805,7 +797,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/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt
index cb24df65ba0..1ab365a376a 100644
--- a/intern/guardedalloc/CMakeLists.txt
+++ b/intern/guardedalloc/CMakeLists.txt
@@ -28,6 +28,7 @@ set(INC_SYS
)
set(SRC
+ ./intern/leak_detector.cc
./intern/mallocn.c
./intern/mallocn_guarded_impl.c
./intern/mallocn_lockfree_impl.c
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index 602297576c8..604330bd1d3 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)
@@ -210,6 +211,10 @@ extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
extern const char *(*MEM_name_ptr)(void *vmemh);
#endif
+/** This should be called as early as possible in the program. When it has been called, information
+ * about memory leaks will be printed on exit. */
+void MEM_initialize_memleak_detection(void);
+
/* Switch allocator to slower but fully guarded mode. */
void MEM_use_guarded_allocator(void);
@@ -238,6 +243,15 @@ void MEM_use_guarded_allocator(void);
{ \
if (mem) \
MEM_freeN(mem); \
+ } \
+ void *operator new(size_t /*count*/, void *ptr) \
+ { \
+ return ptr; \
+ } \
+ /* This is the matching delete operator to the placement-new operator above. Both parameters \
+ * will have the same value. Without this, we get the warning C4291 on windows. */ \
+ void operator delete(void * /*ptr_to_free*/, void * /*ptr*/) \
+ { \
}
/* Needed when type includes a namespace, then the namespace should not be
diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc
new file mode 100644
index 00000000000..4b2689ee28c
--- /dev/null
+++ b/intern/guardedalloc/intern/leak_detector.cc
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup MEM
+ */
+
+#include "MEM_guardedalloc.h"
+#include "mallocn_intern.h"
+
+bool leak_detector_has_run = false;
+char free_after_leak_detection_message[] =
+ "Freeing memory after the leak detector has run. This can happen when using "
+ "static variables in C++ that are defined outside of functions. To fix this "
+ "error, use the 'construct on first use' idiom.";
+
+namespace {
+class MemLeakPrinter {
+ public:
+ ~MemLeakPrinter()
+ {
+ leak_detector_has_run = true;
+ const uint leaked_blocks = MEM_get_memory_blocks_in_use();
+ if (leaked_blocks == 0) {
+ return;
+ }
+ const size_t mem_in_use = MEM_get_memory_in_use();
+ printf("Error: Not freed memory blocks: %u, total unfreed memory %f MB\n",
+ leaked_blocks,
+ (double)mem_in_use / 1024 / 1024);
+ MEM_printmemlist();
+ }
+};
+} // namespace
+
+void MEM_initialize_memleak_detection(void)
+{
+ /**
+ * This variable is constructed when this function is first called. This should happen as soon as
+ * possible when the program starts.
+ *
+ * It is destructed when the program exits. During destruction, it will print information about
+ * leaked memory blocks. Static variables are destructed in reversed order of their
+ * construction. Therefore, all static variables that own memory have to be constructed after
+ * this function has been called.
+ */
+ static MemLeakPrinter printer;
+}
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
index 20dcbed7235..2c207935e43 100644
--- a/intern/guardedalloc/intern/mallocn_guarded_impl.c
+++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c
@@ -198,10 +198,12 @@ print_error(const char *str, ...)
va_end(ap);
buf[sizeof(buf) - 1] = '\0';
- if (error_callback)
+ if (error_callback) {
error_callback(buf);
- else
+ }
+ else {
fputs(buf, stderr);
+ }
}
static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -261,13 +263,16 @@ void *MEM_guarded_dupallocN(const void *vmemh)
memh--;
#ifndef DEBUG_MEMDUPLINAME
- if (LIKELY(memh->alignment == 0))
+ if (LIKELY(memh->alignment == 0)) {
newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
- else
+ }
+ else {
newp = MEM_guarded_mallocN_aligned(memh->len, (size_t)memh->alignment, "dupli_alloc");
+ }
- if (newp == NULL)
+ if (newp == NULL) {
return NULL;
+ }
#else
{
MemHead *nmemh;
@@ -450,8 +455,9 @@ void *MEM_guarded_mallocN(size_t len, const char *str)
if (LIKELY(memh)) {
make_memhead_header(memh, len, str);
- if (UNLIKELY(malloc_debug_memset && len))
+ if (UNLIKELY(malloc_debug_memset && len)) {
memset(memh + 1, 255, len);
+ }
#ifdef DEBUG_MEMCOUNTER
if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
@@ -522,8 +528,9 @@ void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str)
make_memhead_header(memh, len, str);
memh->alignment = (short)alignment;
- if (UNLIKELY(malloc_debug_memset && len))
+ if (UNLIKELY(malloc_debug_memset && len)) {
memset(memh + 1, 255, len);
+ }
#ifdef DEBUG_MEMCOUNTER
if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
@@ -601,12 +608,15 @@ static int compare_len(const void *p1, const void *p2)
const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
- if (pb1->len < pb2->len)
+ if (pb1->len < pb2->len) {
return 1;
- else if (pb1->len == pb2->len)
+ }
+ else if (pb1->len == pb2->len) {
return 0;
- else
+ }
+ else {
return -1;
+ }
}
void MEM_guarded_printmemlist_stats(void)
@@ -636,8 +646,9 @@ void MEM_guarded_printmemlist_stats(void)
totpb = 0;
membl = membase->first;
- if (membl)
+ if (membl) {
membl = MEMNEXT(membl);
+ }
while (membl && pb) {
pb->name = membl->name;
@@ -654,10 +665,12 @@ void MEM_guarded_printmemlist_stats(void)
}
#endif
- if (membl->next)
+ if (membl->next) {
membl = MEMNEXT(membl->next);
- else
+ }
+ else {
break;
+ }
}
/* sort by name and add together blocks with the same name */
@@ -737,8 +750,9 @@ static void MEM_guarded_printmemlist_internal(int pydict)
mem_lock_thread();
membl = membase->first;
- if (membl)
+ if (membl) {
membl = MEMNEXT(membl);
+ }
if (pydict) {
print_error("# membase_debug.py\n");
@@ -771,10 +785,12 @@ static void MEM_guarded_printmemlist_internal(int pydict)
print_memhead_backtrace(membl);
#endif
}
- if (membl->next)
+ if (membl->next) {
membl = MEMNEXT(membl->next);
- else
+ }
+ else {
break;
+ }
}
if (pydict) {
print_error("]\n\n");
@@ -791,15 +807,18 @@ void MEM_guarded_callbackmemlist(void (*func)(void *))
mem_lock_thread();
membl = membase->first;
- if (membl)
+ if (membl) {
membl = MEMNEXT(membl);
+ }
while (membl) {
func(membl + 1);
- if (membl->next)
+ if (membl->next) {
membl = MEMNEXT(membl->next);
- else
+ }
+ else {
break;
+ }
}
mem_unlock_thread();
@@ -879,6 +898,10 @@ void MEM_guarded_freeN(void *vmemh)
memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + memh->len);
if (memt->tag3 == MEMTAG3) {
+ if (leak_detector_has_run) {
+ MemorY_ErroR(memh->name, free_after_leak_detection_message);
+ }
+
memh->tag1 = MEMFREE;
memh->tag2 = MEMFREE;
memt->tag3 = MEMFREE;
@@ -890,24 +913,25 @@ void MEM_guarded_freeN(void *vmemh)
MemorY_ErroR(memh->name, "end corrupt");
name = check_memlist(memh);
if (name != NULL) {
- if (name != memh->name)
+ if (name != memh->name) {
MemorY_ErroR(name, "is also corrupt");
+ }
}
}
else {
mem_lock_thread();
name = check_memlist(memh);
mem_unlock_thread();
- if (name == NULL)
+ if (name == NULL) {
MemorY_ErroR("free", "pointer not in memlist");
- else
+ }
+ else {
MemorY_ErroR(name, "error in header");
+ }
}
totblock--;
/* here a DUMP should happen */
-
- return;
}
/* --------------------------------------------------------------------- */
@@ -930,10 +954,12 @@ static void addtail(volatile localListBase *listbase, void *vlink)
link->next = NULL;
link->prev = listbase->last;
- if (listbase->last)
+ if (listbase->last) {
((struct localLink *)listbase->last)->next = link;
- if (listbase->first == NULL)
+ }
+ if (listbase->first == NULL) {
listbase->first = link;
+ }
listbase->last = link;
}
@@ -950,15 +976,19 @@ static void remlink(volatile localListBase *listbase, void *vlink)
return;
#endif
- if (link->next)
+ if (link->next) {
link->next->prev = link->prev;
- if (link->prev)
+ }
+ if (link->prev) {
link->prev->next = link->next;
+ }
- if (listbase->last == link)
+ if (listbase->last == link) {
listbase->last = link->prev;
- if (listbase->first == link)
+ }
+ if (listbase->first == link) {
listbase->first = link->next;
+ }
}
static void rem_memblock(MemHead *memh)
@@ -966,10 +996,12 @@ static void rem_memblock(MemHead *memh)
mem_lock_thread();
remlink(membase, &memh->next);
if (memh->prev) {
- if (memh->next)
+ if (memh->next) {
MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
- else
+ }
+ else {
MEMNEXT(memh->prev)->nextname = NULL;
+ }
}
mem_unlock_thread();
@@ -981,8 +1013,9 @@ static void rem_memblock(MemHead *memh)
free((char *)memh->name);
#endif
- if (UNLIKELY(malloc_debug_memset && memh->len))
+ if (UNLIKELY(malloc_debug_memset && memh->len)) {
memset(memh + 1, 255, memh->len);
+ }
if (LIKELY(memh->alignment == 0)) {
free(memh);
}
@@ -1006,78 +1039,100 @@ static const char *check_memlist(MemHead *memh)
const char *name;
forw = membase->first;
- if (forw)
+ if (forw) {
forw = MEMNEXT(forw);
+ }
forwok = NULL;
while (forw) {
- if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2)
+ if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
break;
+ }
forwok = forw;
- if (forw->next)
+ if (forw->next) {
forw = MEMNEXT(forw->next);
- else
+ }
+ else {
forw = NULL;
+ }
}
back = (MemHead *)membase->last;
- if (back)
+ if (back) {
back = MEMNEXT(back);
+ }
backok = NULL;
while (back) {
- if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2)
+ if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
break;
+ }
backok = back;
- if (back->prev)
+ if (back->prev) {
back = MEMNEXT(back->prev);
- else
+ }
+ else {
back = NULL;
+ }
}
- if (forw != back)
+ if (forw != back) {
return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
+ }
if (forw == NULL && back == NULL) {
/* no wrong headers found then but in search of memblock */
forw = membase->first;
- if (forw)
+ if (forw) {
forw = MEMNEXT(forw);
+ }
forwok = NULL;
while (forw) {
- if (forw == memh)
+ if (forw == memh) {
break;
- if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2)
+ }
+ if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) {
break;
+ }
forwok = forw;
- if (forw->next)
+ if (forw->next) {
forw = MEMNEXT(forw->next);
- else
+ }
+ else {
forw = NULL;
+ }
}
- if (forw == NULL)
+ if (forw == NULL) {
return NULL;
+ }
back = (MemHead *)membase->last;
- if (back)
+ if (back) {
back = MEMNEXT(back);
+ }
backok = NULL;
while (back) {
- if (back == memh)
+ if (back == memh) {
break;
- if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2)
+ }
+ if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) {
break;
+ }
backok = back;
- if (back->prev)
+ if (back->prev) {
back = MEMNEXT(back->prev);
- else
+ }
+ else {
back = NULL;
+ }
}
}
- if (forwok)
+ if (forwok) {
name = forwok->nextname;
- else
+ }
+ else {
name = "No name found";
+ }
if (forw == memh) {
/* to be sure but this block is removed from the list */
diff --git a/intern/guardedalloc/intern/mallocn_inline.h b/intern/guardedalloc/intern/mallocn_inline.h
index f8bb7861fc9..4e73eb9bad6 100644
--- a/intern/guardedalloc/intern/mallocn_inline.h
+++ b/intern/guardedalloc/intern/mallocn_inline.h
@@ -33,6 +33,10 @@
#ifndef __MALLOCN_INLINE_H__
#define __MALLOCN_INLINE_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result)
{
/* A size_t with its high-half bits all set to 1. */
@@ -52,4 +56,8 @@ MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result)
return ((high_bits & (a | b)) == 0 || (*result / b == a));
}
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __MALLOCN_INLINE_H__ */
diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h
index ef8845a66b3..8fc3e432157 100644
--- a/intern/guardedalloc/intern/mallocn_intern.h
+++ b/intern/guardedalloc/intern/mallocn_intern.h
@@ -100,11 +100,18 @@ size_t malloc_usable_size(void *ptr);
#include "mallocn_inline.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define ALIGNED_MALLOC_MINIMUM_ALIGNMENT sizeof(void *)
void *aligned_malloc(size_t size, size_t alignment);
void aligned_free(void *ptr);
+extern bool leak_detector_has_run;
+extern char free_after_leak_detection_message[];
+
/* Prototypes for counted allocator functions */
size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
void MEM_lockfree_freeN(void *vmemh);
@@ -191,4 +198,8 @@ size_t MEM_guarded_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT;
const char *MEM_guarded_name_ptr(void *vmemh);
#endif
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __MALLOCN_INTERN_H__ */
diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
index 205cc688d72..b71e2c963eb 100644
--- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c
+++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -101,6 +101,10 @@ size_t MEM_lockfree_allocN_len(const void *vmemh)
void MEM_lockfree_freeN(void *vmemh)
{
+ if (leak_detector_has_run) {
+ print_error("%s\n", free_after_leak_detection_message);
+ }
+
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t len = MEM_lockfree_allocN_len(vmemh);
diff --git a/intern/guardedalloc/test/simpletest/memtest.c b/intern/guardedalloc/test/simpletest/memtest.c
index 5c37ceb5b24..f7a9a785f5a 100644
--- a/intern/guardedalloc/test/simpletest/memtest.c
+++ b/intern/guardedalloc/test/simpletest/memtest.c
@@ -148,7 +148,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "|\n|--* Errors were detected\n");
}
else {
- fprintf(stderr, "|\n|--* Test exited succesfully\n");
+ fprintf(stderr, "|\n|--* Test exited successfully\n");
}
fprintf(stderr, "|\n*** Finished test\n\n");
diff --git a/intern/libc_compat/libc_compat.c b/intern/libc_compat/libc_compat.c
index 8da3ca218af..78e387e3117 100644
--- a/intern/libc_compat/libc_compat.c
+++ b/intern/libc_compat/libc_compat.c
@@ -28,6 +28,7 @@
# if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 31)
double __exp_finite(double x);
+double __exp2_finite(double x);
double __acos_finite(double x);
double __asin_finite(double x);
double __log2_finite(double x);
@@ -35,6 +36,7 @@ double __log10_finite(double x);
double __log_finite(double x);
double __pow_finite(double x, double y);
float __expf_finite(float x);
+float __exp2f_finite(float x);
float __acosf_finite(float x);
float __asinf_finite(float x);
float __log2f_finite(float x);
@@ -47,6 +49,11 @@ double __exp_finite(double x)
return exp(x);
}
+double __exp2_finite(double x)
+{
+ return exp2(x);
+}
+
double __acos_finite(double x)
{
return acos(x);
@@ -82,6 +89,11 @@ float __expf_finite(float x)
return expf(x);
}
+float __exp2f_finite(float x)
+{
+ return exp2f(x);
+}
+
float __acosf_finite(float x)
{
return acosf(x);
diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt
index c6078268512..5bb66649529 100644
--- a/intern/libmv/CMakeLists.txt
+++ b/intern/libmv/CMakeLists.txt
@@ -212,6 +212,8 @@ if(WITH_LIBMV)
if(WITH_GTESTS)
+ include(GTestTesting)
+
blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "" "")
BLENDER_SRC_GTEST("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
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/libmv/libmv/simple_pipeline/pipeline.cc b/intern/libmv/libmv/simple_pipeline/pipeline.cc
index 6c8592baa00..728601f3732 100644
--- a/intern/libmv/libmv/simple_pipeline/pipeline.cc
+++ b/intern/libmv/libmv/simple_pipeline/pipeline.cc
@@ -316,8 +316,8 @@ double InternalReprojectionError(
}
LG << "Skipped " << num_skipped << " markers.";
LG << "Reprojected " << num_reprojected << " markers.";
- LG << "Total error: " << total_error;
- LG << "Average error: " << (total_error / num_reprojected) << " [pixels].";
+ LG << "Total error: " << total_error << " px";
+ LG << "Average error: " << (total_error / num_reprojected) << " px";
return total_error / num_reprojected;
}
diff --git a/intern/mantaflow/extern/manta_fluid_API.h b/intern/mantaflow/extern/manta_fluid_API.h
index 7825ad14d7d..3da1d8f53f0 100644
--- a/intern/mantaflow/extern/manta_fluid_API.h
+++ b/intern/mantaflow/extern/manta_fluid_API.h
@@ -31,52 +31,52 @@ extern "C" {
struct MANTA;
/* Fluid functions */
-struct MANTA *manta_init(int *res, struct FluidModifierData *mmd);
+struct MANTA *manta_init(int *res, struct FluidModifierData *fmd);
void manta_free(struct MANTA *fluid);
-void manta_ensure_obstacle(struct MANTA *fluid, struct FluidModifierData *mmd);
-void manta_ensure_guiding(struct MANTA *fluid, struct FluidModifierData *mmd);
-void manta_ensure_invelocity(struct MANTA *fluid, struct FluidModifierData *mmd);
-void manta_ensure_outflow(struct MANTA *fluid, struct FluidModifierData *mmd);
-int manta_write_config(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-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_mesh(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_read_particles(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
+void manta_ensure_obstacle(struct MANTA *fluid, struct FluidModifierData *fmd);
+void manta_ensure_guiding(struct MANTA *fluid, struct FluidModifierData *fmd);
+void manta_ensure_invelocity(struct MANTA *fluid, struct FluidModifierData *fmd);
+void manta_ensure_outflow(struct MANTA *fluid, struct FluidModifierData *fmd);
+int manta_write_config(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_write_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_write_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_read_config(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_read_data(struct MANTA *fluid,
+ struct FluidModifierData *fmd,
+ int framenr,
+ bool resumable);
+int manta_read_noise(struct MANTA *fluid,
+ struct FluidModifierData *fmd,
+ int framenr,
+ bool resumable);
+int manta_read_mesh(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_read_particles(struct MANTA *fluid,
+ struct FluidModifierData *fmd,
+ int framenr,
+ bool resumable);
int manta_read_guiding(struct MANTA *fluid,
- struct FluidModifierData *mmd,
+ struct FluidModifierData *fmd,
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);
-int manta_bake_particles(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_bake_guiding(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_has_data(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_has_noise(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_has_mesh(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
-int manta_has_particles(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
+int manta_bake_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_bake_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_bake_mesh(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_bake_particles(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_bake_guiding(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_has_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_has_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_has_mesh(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
+int manta_has_particles(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr);
int manta_has_guiding(struct MANTA *fluid,
- struct FluidModifierData *mmd,
+ struct FluidModifierData *fmd,
int framenr,
bool domain);
-void manta_update_variables(struct MANTA *fluid, struct FluidModifierData *mmd);
+void manta_update_variables(struct MANTA *fluid, struct FluidModifierData *fmd);
int manta_get_frame(struct MANTA *fluid);
float manta_get_timestep(struct MANTA *fluid);
void manta_adapt_timestep(struct MANTA *fluid);
-bool manta_needs_realloc(struct MANTA *fluid, struct FluidModifierData *mmd);
+bool manta_needs_realloc(struct MANTA *fluid, struct FluidModifierData *fmd);
/* Fluid accessors */
size_t manta_get_index(int x, int max_x, int y, int max_y, int z /*, int max_z */);
@@ -110,50 +110,20 @@ float *manta_get_phiout_in(struct MANTA *fluid);
float *manta_get_phioutstatic_in(struct MANTA *fluid);
/* Smoke functions */
-void manta_smoke_export_script(struct MANTA *smoke, struct FluidModifierData *mmd);
-void manta_smoke_export(struct MANTA *smoke,
- float *dt,
- float *dx,
- float **dens,
- float **react,
- float **flame,
- float **fuel,
- float **heat,
- float **vx,
- float **vy,
- float **vz,
- float **r,
- float **g,
- float **b,
- int **flags,
- float **shadow);
-void manta_smoke_turbulence_export(struct MANTA *smoke,
- float **dens,
- float **react,
- float **flame,
- float **fuel,
- float **r,
- float **g,
- float **b,
- float **tcu,
- float **tcv,
- float **tcw,
- float **tcu2,
- float **tcv2,
- float **tcw2);
+void manta_smoke_export_script(struct MANTA *smoke, struct FluidModifierData *fmd);
void manta_smoke_get_rgba(struct MANTA *smoke, float *data, int sequential);
-void manta_smoke_turbulence_get_rgba(struct MANTA *smoke, float *data, int sequential);
+void manta_noise_get_rgba(struct MANTA *smoke, float *data, int sequential);
void manta_smoke_get_rgba_fixed_color(struct MANTA *smoke,
float color[3],
float *data,
int sequential);
-void manta_smoke_turbulence_get_rgba_fixed_color(struct MANTA *smoke,
- float color[3],
- float *data,
- int sequential);
-void manta_smoke_ensure_heat(struct MANTA *smoke, struct FluidModifierData *mmd);
-void manta_smoke_ensure_fire(struct MANTA *smoke, struct FluidModifierData *mmd);
-void manta_smoke_ensure_colors(struct MANTA *smoke, struct FluidModifierData *mmd);
+void manta_noise_get_rgba_fixed_color(struct MANTA *smoke,
+ float color[3],
+ float *data,
+ int sequential);
+void manta_smoke_ensure_heat(struct MANTA *smoke, struct FluidModifierData *fmd);
+void manta_smoke_ensure_fire(struct MANTA *smoke, struct FluidModifierData *fmd);
+void manta_smoke_ensure_colors(struct MANTA *smoke, struct FluidModifierData *fmd);
/* Smoke accessors */
float *manta_smoke_get_density(struct MANTA *smoke);
@@ -177,21 +147,27 @@ float *manta_smoke_get_emission_in(struct MANTA *smoke);
int manta_smoke_has_heat(struct MANTA *smoke);
int manta_smoke_has_fuel(struct MANTA *smoke);
int manta_smoke_has_colors(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_density(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_fuel(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_react(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_color_r(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_color_g(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_color_b(struct MANTA *smoke);
-float *manta_smoke_turbulence_get_flame(struct MANTA *smoke);
-int manta_smoke_turbulence_has_fuel(struct MANTA *smoke);
-int manta_smoke_turbulence_has_colors(struct MANTA *smoke);
-void manta_smoke_turbulence_get_res(struct MANTA *smoke, int *res);
-int manta_smoke_turbulence_get_cells(struct MANTA *smoke);
+float *manta_noise_get_density(struct MANTA *smoke);
+float *manta_noise_get_fuel(struct MANTA *smoke);
+float *manta_noise_get_react(struct MANTA *smoke);
+float *manta_noise_get_color_r(struct MANTA *smoke);
+float *manta_noise_get_color_g(struct MANTA *smoke);
+float *manta_noise_get_color_b(struct MANTA *smoke);
+float *manta_noise_get_texture_u(struct MANTA *smoke);
+float *manta_noise_get_texture_v(struct MANTA *smoke);
+float *manta_noise_get_texture_w(struct MANTA *smoke);
+float *manta_noise_get_texture_u2(struct MANTA *smoke);
+float *manta_noise_get_texture_v2(struct MANTA *smoke);
+float *manta_noise_get_texture_w2(struct MANTA *smoke);
+float *manta_noise_get_flame(struct MANTA *smoke);
+int manta_noise_has_fuel(struct MANTA *smoke);
+int manta_noise_has_colors(struct MANTA *smoke);
+void manta_noise_get_res(struct MANTA *smoke, int *res);
+int manta_noise_get_cells(struct MANTA *smoke);
/* Liquid functions */
-void manta_liquid_export_script(struct MANTA *smoke, struct FluidModifierData *mmd);
-void manta_liquid_ensure_sndparts(struct MANTA *fluid, struct FluidModifierData *mmd);
+void manta_liquid_export_script(struct MANTA *smoke, struct FluidModifierData *fmd);
+void manta_liquid_ensure_sndparts(struct MANTA *fluid, struct FluidModifierData *fmd);
/* Liquid accessors */
int manta_liquid_get_particle_res_x(struct MANTA *liquid);
diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp
index e760cef8641..5b2cbb09979 100644
--- a/intern/mantaflow/intern/MANTA_main.cpp
+++ b/intern/mantaflow/intern/MANTA_main.cpp
@@ -67,34 +67,34 @@ int MANTA::with_debug(0);
/* Number of mesh triangles that the cache reads at once (with zlib). */
#define TRIANGLE_CHUNK 20000
-MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
+MANTA::MANTA(int *res, FluidModifierData *fmd) : mCurrentID(++solverID)
{
if (with_debug)
cout << "FLUID: " << mCurrentID << " with res(" << res[0] << ", " << res[1] << ", " << res[2]
<< ")" << endl;
- FluidDomainSettings *mds = mmd->domain;
- mds->fluid = this;
-
- mUsingLiquid = (mds->type == FLUID_DOMAIN_TYPE_LIQUID);
- mUsingSmoke = (mds->type == FLUID_DOMAIN_TYPE_GAS);
- mUsingNoise = (mds->flags & FLUID_DOMAIN_USE_NOISE) && mUsingSmoke;
- mUsingFractions = (mds->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid;
- mUsingMesh = (mds->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid;
- mUsingDiffusion = (mds->flags & FLUID_DOMAIN_USE_DIFFUSION) && mUsingLiquid;
- mUsingMVel = (mds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid;
- mUsingGuiding = (mds->flags & FLUID_DOMAIN_USE_GUIDE);
- mUsingDrops = (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid;
- mUsingBubbles = (mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && mUsingLiquid;
- mUsingFloats = (mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && mUsingLiquid;
- mUsingTracers = (mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) && mUsingLiquid;
-
- mUsingHeat = (mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) && mUsingSmoke;
- mUsingFire = (mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) && mUsingSmoke;
- mUsingColors = (mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) && mUsingSmoke;
- mUsingObstacle = (mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE);
- mUsingInvel = (mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL);
- mUsingOutflow = (mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW);
+ FluidDomainSettings *fds = fmd->domain;
+ fds->fluid = this;
+
+ mUsingLiquid = (fds->type == FLUID_DOMAIN_TYPE_LIQUID);
+ mUsingSmoke = (fds->type == FLUID_DOMAIN_TYPE_GAS);
+ mUsingNoise = (fds->flags & FLUID_DOMAIN_USE_NOISE) && mUsingSmoke;
+ mUsingFractions = (fds->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid;
+ mUsingMesh = (fds->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid;
+ mUsingDiffusion = (fds->flags & FLUID_DOMAIN_USE_DIFFUSION) && mUsingLiquid;
+ mUsingMVel = (fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid;
+ mUsingGuiding = (fds->flags & FLUID_DOMAIN_USE_GUIDE);
+ mUsingDrops = (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid;
+ mUsingBubbles = (fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && mUsingLiquid;
+ mUsingFloats = (fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && mUsingLiquid;
+ mUsingTracers = (fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) && mUsingLiquid;
+
+ mUsingHeat = (fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) && mUsingSmoke;
+ mUsingFire = (fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) && mUsingSmoke;
+ mUsingColors = (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) && mUsingSmoke;
+ mUsingObstacle = (fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE);
+ mUsingInvel = (fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL);
+ mUsingOutflow = (fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW);
// Simulation constants
mTempAmb = 0; // TODO: Maybe use this later for buoyancy calculation
@@ -103,7 +103,7 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
mResZ = res[2];
mMaxRes = MAX3(mResX, mResY, mResZ);
mTotalCells = mResX * mResY * mResZ;
- mResGuiding = mds->res;
+ mResGuiding = fds->res;
// Smoke low res grids
mDensity = nullptr;
@@ -194,7 +194,7 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
initializeMantaflow();
// Initializa RNA map with values that Python will need
- initializeRNAMap(mmd);
+ initializeRNAMap(fmd);
// Initialize Mantaflow variables in Python
// Liquid
@@ -209,7 +209,7 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
initOutflow();
if (mUsingDrops || mUsingBubbles || mUsingFloats || mUsingTracers) {
- mUpresParticle = mds->particle_scale;
+ mUpresParticle = fds->particle_scale;
mResXParticle = mUpresParticle * mResX;
mResYParticle = mUpresParticle * mResY;
mResZParticle = mUpresParticle * mResZ;
@@ -220,7 +220,7 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
}
if (mUsingMesh) {
- mUpresMesh = mds->mesh_scale;
+ mUpresMesh = fds->mesh_scale;
mResXMesh = mUpresMesh * mResX;
mResYMesh = mUpresMesh * mResY;
mResZMesh = mUpresMesh * mResZ;
@@ -236,7 +236,7 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
}
if (mUsingGuiding) {
- mResGuiding = (mds->guide_parent) ? mds->guide_res : mds->res;
+ mResGuiding = (fds->guide_parent) ? fds->guide_res : fds->res;
initGuiding();
}
if (mUsingFractions) {
@@ -262,12 +262,12 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
initOutflow();
if (mUsingGuiding) {
- mResGuiding = (mds->guide_parent) ? mds->guide_res : mds->res;
+ mResGuiding = (fds->guide_parent) ? fds->guide_res : fds->res;
initGuiding();
}
if (mUsingNoise) {
- int amplify = mds->noise_scale;
+ int amplify = fds->noise_scale;
mResXNoise = amplify * mResX;
mResYNoise = amplify * mResY;
mResZNoise = amplify * mResZ;
@@ -285,7 +285,7 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID)
updatePointers();
}
-void MANTA::initDomain(FluidModifierData *mmd)
+void MANTA::initDomain(FluidModifierData *fmd)
{
// Vector will hold all python commands that are to be executed
vector<string> pythonCommands;
@@ -301,53 +301,52 @@ 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;
- string finalString = parseScript(tmpString, mmd);
+ fluid_file_import + fluid_file_export + fluid_pre_step + fluid_post_step +
+ fluid_adapt_time_step + fluid_time_stepping;
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
}
-void MANTA::initNoise(FluidModifierData *mmd)
+void MANTA::initNoise(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = fluid_variables_noise + fluid_solver_noise;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
}
-void MANTA::initSmoke(FluidModifierData *mmd)
+void MANTA::initSmoke(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = smoke_variables + smoke_alloc + smoke_adaptive_step + smoke_save_data +
smoke_load_data + smoke_step;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
}
-void MANTA::initSmokeNoise(FluidModifierData *mmd)
+void MANTA::initSmokeNoise(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = smoke_variables_noise + smoke_alloc_noise + smoke_wavelet_noise +
smoke_save_noise + smoke_load_noise + smoke_step_noise;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
mUsingNoise = true;
}
-void MANTA::initHeat(FluidModifierData *mmd)
+void MANTA::initHeat(FluidModifierData *fmd)
{
if (!mHeat) {
vector<string> pythonCommands;
string tmpString = smoke_alloc_heat + smoke_with_heat;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -355,12 +354,12 @@ void MANTA::initHeat(FluidModifierData *mmd)
}
}
-void MANTA::initFire(FluidModifierData *mmd)
+void MANTA::initFire(FluidModifierData *fmd)
{
if (!mFuel) {
vector<string> pythonCommands;
string tmpString = smoke_alloc_fire + smoke_with_fire;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -368,12 +367,12 @@ void MANTA::initFire(FluidModifierData *mmd)
}
}
-void MANTA::initFireHigh(FluidModifierData *mmd)
+void MANTA::initFireHigh(FluidModifierData *fmd)
{
if (!mFuelHigh) {
vector<string> pythonCommands;
string tmpString = smoke_alloc_fire_noise + smoke_with_fire;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -381,12 +380,12 @@ void MANTA::initFireHigh(FluidModifierData *mmd)
}
}
-void MANTA::initColors(FluidModifierData *mmd)
+void MANTA::initColors(FluidModifierData *fmd)
{
if (!mColorR) {
vector<string> pythonCommands;
string tmpString = smoke_alloc_colors + smoke_init_colors + smoke_with_colors;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -394,12 +393,12 @@ void MANTA::initColors(FluidModifierData *mmd)
}
}
-void MANTA::initColorsHigh(FluidModifierData *mmd)
+void MANTA::initColorsHigh(FluidModifierData *fmd)
{
if (!mColorRHigh) {
vector<string> pythonCommands;
string tmpString = smoke_alloc_colors_noise + smoke_init_colors_noise + smoke_with_colors;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -407,13 +406,13 @@ void MANTA::initColorsHigh(FluidModifierData *mmd)
}
}
-void MANTA::initLiquid(FluidModifierData *mmd)
+void MANTA::initLiquid(FluidModifierData *fmd)
{
if (!mPhiIn) {
vector<string> pythonCommands;
string tmpString = liquid_variables + liquid_alloc + liquid_init_phi + liquid_save_data +
liquid_load_data + liquid_adaptive_step + liquid_step;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -421,44 +420,44 @@ void MANTA::initLiquid(FluidModifierData *mmd)
}
}
-void MANTA::initMesh(FluidModifierData *mmd)
+void MANTA::initMesh(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = fluid_variables_mesh + fluid_solver_mesh + liquid_load_mesh;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
mUsingMesh = true;
}
-void MANTA::initLiquidMesh(FluidModifierData *mmd)
+void MANTA::initLiquidMesh(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = liquid_alloc_mesh + liquid_step_mesh + liquid_save_mesh;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
mUsingMesh = true;
}
-void MANTA::initCurvature(FluidModifierData *mmd)
+void MANTA::initCurvature(FluidModifierData *fmd)
{
std::vector<std::string> pythonCommands;
- std::string finalString = parseScript(liquid_alloc_curvature, mmd);
+ std::string finalString = parseScript(liquid_alloc_curvature, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
mUsingDiffusion = true;
}
-void MANTA::initObstacle(FluidModifierData *mmd)
+void MANTA::initObstacle(FluidModifierData *fmd)
{
if (!mPhiObsIn) {
vector<string> pythonCommands;
string tmpString = fluid_alloc_obstacle + fluid_with_obstacle;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -466,13 +465,13 @@ void MANTA::initObstacle(FluidModifierData *mmd)
}
}
-void MANTA::initGuiding(FluidModifierData *mmd)
+void MANTA::initGuiding(FluidModifierData *fmd)
{
if (!mPhiGuideIn) {
vector<string> pythonCommands;
string tmpString = fluid_variables_guiding + fluid_solver_guiding + fluid_alloc_guiding +
fluid_save_guiding + fluid_load_vel + fluid_load_guiding;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -480,23 +479,23 @@ void MANTA::initGuiding(FluidModifierData *mmd)
}
}
-void MANTA::initFractions(FluidModifierData *mmd)
+void MANTA::initFractions(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = fluid_alloc_fractions + fluid_with_fractions;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
mUsingFractions = true;
}
-void MANTA::initInVelocity(FluidModifierData *mmd)
+void MANTA::initInVelocity(FluidModifierData *fmd)
{
if (!mInVelocityX) {
vector<string> pythonCommands;
string tmpString = fluid_alloc_invel + fluid_with_invel;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -504,12 +503,12 @@ void MANTA::initInVelocity(FluidModifierData *mmd)
}
}
-void MANTA::initOutflow(FluidModifierData *mmd)
+void MANTA::initOutflow(FluidModifierData *fmd)
{
if (!mPhiOutIn) {
vector<string> pythonCommands;
string tmpString = fluid_alloc_outflow + fluid_with_outflow;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -517,24 +516,24 @@ void MANTA::initOutflow(FluidModifierData *mmd)
}
}
-void MANTA::initSndParts(FluidModifierData *mmd)
+void MANTA::initSndParts(FluidModifierData *fmd)
{
vector<string> pythonCommands;
string tmpString = fluid_variables_particles + fluid_solver_particles;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
}
-void MANTA::initLiquidSndParts(FluidModifierData *mmd)
+void MANTA::initLiquidSndParts(FluidModifierData *fmd)
{
if (!mSndParticleData) {
vector<string> pythonCommands;
string tmpString = liquid_alloc_particles + liquid_variables_particles +
liquid_step_particles + fluid_with_sndparts + liquid_load_particles +
liquid_save_particles;
- string finalString = parseScript(tmpString, mmd);
+ string finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
runPythonString(pythonCommands);
@@ -558,7 +557,7 @@ MANTA::~MANTA()
// Initializa RNA map with values that Python will need
initializeRNAMap();
- // Leave out mmd argument in parseScript since only looking up IDs
+ // Leave out fmd argument in parseScript since only looking up IDs
string finalString = parseScript(tmpString);
pythonCommands.push_back(finalString);
result = runPythonString(pythonCommands);
@@ -665,56 +664,56 @@ static string getBooleanString(int value)
return (value) ? "True" : "False";
}
-void MANTA::initializeRNAMap(FluidModifierData *mmd)
+void MANTA::initializeRNAMap(FluidModifierData *fmd)
{
if (with_debug)
cout << "MANTA::initializeRNAMap()" << endl;
mRNAMap["ID"] = to_string(mCurrentID);
- if (!mmd) {
+ if (!fmd) {
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;
}
- FluidDomainSettings *mds = mmd->domain;
- bool is2D = (mds->solver_res == 2);
+ FluidDomainSettings *fds = fmd->domain;
+ bool is2D = (fds->solver_res == 2);
string borderCollisions = "";
- if ((mds->border_collisions & FLUID_DOMAIN_BORDER_LEFT) == 0)
+ if ((fds->border_collisions & FLUID_DOMAIN_BORDER_LEFT) == 0)
borderCollisions += "x";
- if ((mds->border_collisions & FLUID_DOMAIN_BORDER_RIGHT) == 0)
+ if ((fds->border_collisions & FLUID_DOMAIN_BORDER_RIGHT) == 0)
borderCollisions += "X";
- if ((mds->border_collisions & FLUID_DOMAIN_BORDER_FRONT) == 0)
+ if ((fds->border_collisions & FLUID_DOMAIN_BORDER_FRONT) == 0)
borderCollisions += "y";
- if ((mds->border_collisions & FLUID_DOMAIN_BORDER_BACK) == 0)
+ if ((fds->border_collisions & FLUID_DOMAIN_BORDER_BACK) == 0)
borderCollisions += "Y";
- if ((mds->border_collisions & FLUID_DOMAIN_BORDER_BOTTOM) == 0)
+ if ((fds->border_collisions & FLUID_DOMAIN_BORDER_BOTTOM) == 0)
borderCollisions += "z";
- if ((mds->border_collisions & FLUID_DOMAIN_BORDER_TOP) == 0)
+ if ((fds->border_collisions & FLUID_DOMAIN_BORDER_TOP) == 0)
borderCollisions += "Z";
string simulationMethod = "";
- if (mds->simulation_method & FLUID_DOMAIN_METHOD_FLIP)
+ if (fds->simulation_method & FLUID_DOMAIN_METHOD_FLIP)
simulationMethod += "'FLIP'";
- else if (mds->simulation_method & FLUID_DOMAIN_METHOD_APIC)
+ else if (fds->simulation_method & FLUID_DOMAIN_METHOD_APIC)
simulationMethod += "'APIC'";
string particleTypesStr = "";
- if (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY)
+ if (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY)
particleTypesStr += "PtypeSpray";
- if (mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) {
+ if (fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) {
if (!particleTypesStr.empty())
particleTypesStr += "|";
particleTypesStr += "PtypeBubble";
}
- if (mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) {
+ if (fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) {
if (!particleTypesStr.empty())
particleTypesStr += "|";
particleTypesStr += "PtypeFoam";
}
- if (mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) {
+ if (fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) {
if (!particleTypesStr.empty())
particleTypesStr += "|";
particleTypesStr += "PtypeTracer";
@@ -725,59 +724,73 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
int particleTypes = (FLUID_DOMAIN_PARTICLE_SPRAY | FLUID_DOMAIN_PARTICLE_BUBBLE |
FLUID_DOMAIN_PARTICLE_FOAM | FLUID_DOMAIN_PARTICLE_TRACER);
- string cacheDirectory(mds->cache_directory);
-
- 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]);
-
- 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);
- mRNAMap["USING_HEAT"] = getBooleanString(mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT);
- mRNAMap["USING_FIRE"] = getBooleanString(mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE);
- mRNAMap["USING_NOISE"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_NOISE);
- mRNAMap["USING_OBSTACLE"] = getBooleanString(mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE);
- mRNAMap["USING_GUIDING"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_GUIDE);
- mRNAMap["USING_INVEL"] = getBooleanString(mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL);
- 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["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);
- mRNAMap["DELETE_IN_OBSTACLE"] = getBooleanString(mds->flags & FLUID_DOMAIN_DELETE_IN_OBSTACLE);
- mRNAMap["USING_DIFFUSION"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_DIFFUSION);
- mRNAMap["USING_MESH"] = getBooleanString(mds->flags & FLUID_DOMAIN_USE_MESH);
- mRNAMap["USING_IMPROVED_MESH"] = getBooleanString(mds->mesh_generator ==
+ string cacheDirectory(fds->cache_directory);
+
+ float viscosity = fds->viscosity_base * pow(10.0f, -fds->viscosity_exponent);
+ float domainSize = MAX3(fds->global_size[0], fds->global_size[1], fds->global_size[2]);
+
+ string vdbCompressionMethod = "Compression_None";
+ if (fds->openvdb_compression == VDB_COMPRESSION_NONE)
+ vdbCompressionMethod = "Compression_None";
+ else if (fds->openvdb_compression == VDB_COMPRESSION_ZIP)
+ vdbCompressionMethod = "Compression_Zip";
+ else if (fds->openvdb_compression == VDB_COMPRESSION_BLOSC)
+ vdbCompressionMethod = "Compression_Blosc";
+
+ string vdbPrecisionHalf = "True";
+ if (fds->openvdb_data_depth == VDB_PRECISION_HALF_FLOAT)
+ vdbPrecisionHalf = "True";
+ else if (fds->openvdb_data_depth == VDB_PRECISION_FULL_FLOAT)
+ vdbPrecisionHalf = "False";
+
+ mRNAMap["USING_SMOKE"] = getBooleanString(fds->type == FLUID_DOMAIN_TYPE_GAS);
+ mRNAMap["USING_LIQUID"] = getBooleanString(fds->type == FLUID_DOMAIN_TYPE_LIQUID);
+ mRNAMap["USING_COLORS"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS);
+ mRNAMap["USING_HEAT"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT);
+ mRNAMap["USING_FIRE"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE);
+ mRNAMap["USING_NOISE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_NOISE);
+ mRNAMap["USING_OBSTACLE"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE);
+ mRNAMap["USING_GUIDING"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_GUIDE);
+ mRNAMap["USING_INVEL"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL);
+ mRNAMap["USING_OUTFLOW"] = getBooleanString(fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW);
+ mRNAMap["USING_LOG_DISSOLVE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_DISSOLVE_LOG);
+ mRNAMap["USING_DISSOLVE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_DISSOLVE);
+ mRNAMap["DOMAIN_CLOSED"] = getBooleanString(borderCollisions.compare("") == 0);
+ mRNAMap["CACHE_RESUMABLE"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE);
+ mRNAMap["USING_ADAPTIVETIME"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_TIME);
+ mRNAMap["USING_SPEEDVECTORS"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS);
+ mRNAMap["USING_FRACTIONS"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_FRACTIONS);
+ mRNAMap["DELETE_IN_OBSTACLE"] = getBooleanString(fds->flags & FLUID_DOMAIN_DELETE_IN_OBSTACLE);
+ mRNAMap["USING_DIFFUSION"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_DIFFUSION);
+ mRNAMap["USING_MESH"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_MESH);
+ mRNAMap["USING_IMPROVED_MESH"] = getBooleanString(fds->mesh_generator ==
FLUID_DOMAIN_MESH_IMPROVED);
- mRNAMap["USING_SNDPARTS"] = getBooleanString(mds->particle_type & particleTypes);
- mRNAMap["SNDPARTICLE_BOUNDARY_DELETE"] = getBooleanString(mds->sndparticle_boundary ==
+ mRNAMap["USING_SNDPARTS"] = getBooleanString(fds->particle_type & particleTypes);
+ mRNAMap["SNDPARTICLE_BOUNDARY_DELETE"] = getBooleanString(fds->sndparticle_boundary ==
SNDPARTICLE_BOUNDARY_DELETE);
- mRNAMap["SNDPARTICLE_BOUNDARY_PUSHOUT"] = getBooleanString(mds->sndparticle_boundary ==
+ mRNAMap["SNDPARTICLE_BOUNDARY_PUSHOUT"] = getBooleanString(fds->sndparticle_boundary ==
SNDPARTICLE_BOUNDARY_PUSHOUT);
- mRNAMap["SOLVER_DIM"] = to_string(mds->solver_res);
+ mRNAMap["SOLVER_DIM"] = to_string(fds->solver_res);
mRNAMap["BOUND_CONDITIONS"] = borderCollisions;
- mRNAMap["BOUNDARY_WIDTH"] = to_string(mds->boundary_width);
+ mRNAMap["BOUNDARY_WIDTH"] = to_string(fds->boundary_width);
mRNAMap["RES"] = to_string(mMaxRes);
mRNAMap["RESX"] = to_string(mResX);
mRNAMap["RESY"] = (is2D) ? to_string(mResZ) : to_string(mResY);
mRNAMap["RESZ"] = (is2D) ? to_string(1) : to_string(mResZ);
- mRNAMap["TIME_SCALE"] = to_string(mds->time_scale);
- mRNAMap["FRAME_LENGTH"] = to_string(mds->frame_length);
- mRNAMap["CFL"] = to_string(mds->cfl_condition);
- mRNAMap["DT"] = to_string(mds->dt);
- mRNAMap["TIMESTEPS_MIN"] = to_string(mds->timesteps_minimum);
- mRNAMap["TIMESTEPS_MAX"] = to_string(mds->timesteps_maximum);
- mRNAMap["TIME_TOTAL"] = to_string(mds->time_total);
- mRNAMap["TIME_PER_FRAME"] = to_string(mds->time_per_frame);
- mRNAMap["VORTICITY"] = to_string(mds->vorticity);
- mRNAMap["FLAME_VORTICITY"] = to_string(mds->flame_vorticity);
- mRNAMap["NOISE_SCALE"] = to_string(mds->noise_scale);
- mRNAMap["MESH_SCALE"] = to_string(mds->mesh_scale);
- mRNAMap["PARTICLE_SCALE"] = to_string(mds->particle_scale);
+ mRNAMap["TIME_SCALE"] = to_string(fds->time_scale);
+ mRNAMap["FRAME_LENGTH"] = to_string(fds->frame_length);
+ mRNAMap["CFL"] = to_string(fds->cfl_condition);
+ mRNAMap["DT"] = to_string(fds->dt);
+ mRNAMap["TIMESTEPS_MIN"] = to_string(fds->timesteps_minimum);
+ mRNAMap["TIMESTEPS_MAX"] = to_string(fds->timesteps_maximum);
+ mRNAMap["TIME_TOTAL"] = to_string(fds->time_total);
+ mRNAMap["TIME_PER_FRAME"] = to_string(fds->time_per_frame);
+ mRNAMap["VORTICITY"] = to_string(fds->vorticity);
+ mRNAMap["FLAME_VORTICITY"] = to_string(fds->flame_vorticity);
+ mRNAMap["NOISE_SCALE"] = to_string(fds->noise_scale);
+ mRNAMap["MESH_SCALE"] = to_string(fds->mesh_scale);
+ mRNAMap["PARTICLE_SCALE"] = to_string(fds->particle_scale);
mRNAMap["NOISE_RESX"] = to_string(mResXNoise);
mRNAMap["NOISE_RESY"] = (is2D) ? to_string(mResZNoise) : to_string(mResYNoise);
mRNAMap["NOISE_RESZ"] = (is2D) ? to_string(1) : to_string(mResZNoise);
@@ -790,74 +803,80 @@ void MANTA::initializeRNAMap(FluidModifierData *mmd)
mRNAMap["GUIDING_RESX"] = to_string(mResGuiding[0]);
mRNAMap["GUIDING_RESY"] = (is2D) ? to_string(mResGuiding[2]) : to_string(mResGuiding[1]);
mRNAMap["GUIDING_RESZ"] = (is2D) ? to_string(1) : to_string(mResGuiding[2]);
- mRNAMap["MIN_RESX"] = to_string(mds->res_min[0]);
- mRNAMap["MIN_RESY"] = to_string(mds->res_min[1]);
- mRNAMap["MIN_RESZ"] = to_string(mds->res_min[2]);
- mRNAMap["BASE_RESX"] = to_string(mds->base_res[0]);
- mRNAMap["BASE_RESY"] = to_string(mds->base_res[1]);
- mRNAMap["BASE_RESZ"] = to_string(mds->base_res[2]);
- mRNAMap["WLT_STR"] = to_string(mds->noise_strength);
- mRNAMap["NOISE_POSSCALE"] = to_string(mds->noise_pos_scale);
- mRNAMap["NOISE_TIMEANIM"] = to_string(mds->noise_time_anim);
- mRNAMap["COLOR_R"] = to_string(mds->active_color[0]);
- mRNAMap["COLOR_G"] = to_string(mds->active_color[1]);
- mRNAMap["COLOR_B"] = to_string(mds->active_color[2]);
- mRNAMap["BUOYANCY_ALPHA"] = to_string(mds->alpha);
- mRNAMap["BUOYANCY_BETA"] = to_string(mds->beta);
- mRNAMap["DISSOLVE_SPEED"] = to_string(mds->diss_speed);
- mRNAMap["BURNING_RATE"] = to_string(mds->burning_rate);
- mRNAMap["FLAME_SMOKE"] = to_string(mds->flame_smoke);
- mRNAMap["IGNITION_TEMP"] = to_string(mds->flame_ignition);
- mRNAMap["MAX_TEMP"] = to_string(mds->flame_max_temp);
- mRNAMap["FLAME_SMOKE_COLOR_X"] = to_string(mds->flame_smoke_color[0]);
- mRNAMap["FLAME_SMOKE_COLOR_Y"] = to_string(mds->flame_smoke_color[1]);
- mRNAMap["FLAME_SMOKE_COLOR_Z"] = to_string(mds->flame_smoke_color[2]);
- mRNAMap["CURRENT_FRAME"] = to_string(int(mmd->time));
- mRNAMap["START_FRAME"] = to_string(mds->cache_frame_start);
- mRNAMap["END_FRAME"] = to_string(mds->cache_frame_end);
- mRNAMap["CACHE_DATA_FORMAT"] = getCacheFileEnding(mds->cache_data_format);
- mRNAMap["CACHE_MESH_FORMAT"] = getCacheFileEnding(mds->cache_mesh_format);
- mRNAMap["CACHE_NOISE_FORMAT"] = getCacheFileEnding(mds->cache_noise_format);
- mRNAMap["CACHE_PARTICLE_FORMAT"] = getCacheFileEnding(mds->cache_particle_format);
+ mRNAMap["MIN_RESX"] = to_string(fds->res_min[0]);
+ mRNAMap["MIN_RESY"] = to_string(fds->res_min[1]);
+ mRNAMap["MIN_RESZ"] = to_string(fds->res_min[2]);
+ mRNAMap["BASE_RESX"] = to_string(fds->base_res[0]);
+ mRNAMap["BASE_RESY"] = to_string(fds->base_res[1]);
+ mRNAMap["BASE_RESZ"] = to_string(fds->base_res[2]);
+ mRNAMap["WLT_STR"] = to_string(fds->noise_strength);
+ mRNAMap["NOISE_POSSCALE"] = to_string(fds->noise_pos_scale);
+ mRNAMap["NOISE_TIMEANIM"] = to_string(fds->noise_time_anim);
+ mRNAMap["COLOR_R"] = to_string(fds->active_color[0]);
+ mRNAMap["COLOR_G"] = to_string(fds->active_color[1]);
+ mRNAMap["COLOR_B"] = to_string(fds->active_color[2]);
+ mRNAMap["BUOYANCY_ALPHA"] = to_string(fds->alpha);
+ mRNAMap["BUOYANCY_BETA"] = to_string(fds->beta);
+ mRNAMap["DISSOLVE_SPEED"] = to_string(fds->diss_speed);
+ mRNAMap["BURNING_RATE"] = to_string(fds->burning_rate);
+ mRNAMap["FLAME_SMOKE"] = to_string(fds->flame_smoke);
+ mRNAMap["IGNITION_TEMP"] = to_string(fds->flame_ignition);
+ mRNAMap["MAX_TEMP"] = to_string(fds->flame_max_temp);
+ mRNAMap["FLAME_SMOKE_COLOR_X"] = to_string(fds->flame_smoke_color[0]);
+ mRNAMap["FLAME_SMOKE_COLOR_Y"] = to_string(fds->flame_smoke_color[1]);
+ mRNAMap["FLAME_SMOKE_COLOR_Z"] = to_string(fds->flame_smoke_color[2]);
+ mRNAMap["CURRENT_FRAME"] = to_string(int(fmd->time));
+ mRNAMap["START_FRAME"] = to_string(fds->cache_frame_start);
+ mRNAMap["END_FRAME"] = to_string(fds->cache_frame_end);
+ mRNAMap["CACHE_DATA_FORMAT"] = getCacheFileEnding(fds->cache_data_format);
+ mRNAMap["CACHE_MESH_FORMAT"] = getCacheFileEnding(fds->cache_mesh_format);
+ mRNAMap["CACHE_NOISE_FORMAT"] = getCacheFileEnding(fds->cache_noise_format);
+ mRNAMap["CACHE_PARTICLE_FORMAT"] = getCacheFileEnding(fds->cache_particle_format);
mRNAMap["SIMULATION_METHOD"] = simulationMethod;
- mRNAMap["FLIP_RATIO"] = to_string(mds->flip_ratio);
- mRNAMap["PARTICLE_RANDOMNESS"] = to_string(mds->particle_randomness);
- mRNAMap["PARTICLE_NUMBER"] = to_string(mds->particle_number);
- mRNAMap["PARTICLE_MINIMUM"] = to_string(mds->particle_minimum);
- mRNAMap["PARTICLE_MAXIMUM"] = to_string(mds->particle_maximum);
- mRNAMap["PARTICLE_RADIUS"] = to_string(mds->particle_radius);
- mRNAMap["FRACTIONS_THRESHOLD"] = to_string(mds->fractions_threshold);
- mRNAMap["MESH_CONCAVE_UPPER"] = to_string(mds->mesh_concave_upper);
- mRNAMap["MESH_CONCAVE_LOWER"] = to_string(mds->mesh_concave_lower);
- mRNAMap["MESH_PARTICLE_RADIUS"] = to_string(mds->mesh_particle_radius);
- mRNAMap["MESH_SMOOTHEN_POS"] = to_string(mds->mesh_smoothen_pos);
- mRNAMap["MESH_SMOOTHEN_NEG"] = to_string(mds->mesh_smoothen_neg);
- mRNAMap["PARTICLE_BAND_WIDTH"] = to_string(mds->particle_band_width);
- mRNAMap["SNDPARTICLE_TAU_MIN_WC"] = to_string(mds->sndparticle_tau_min_wc);
- mRNAMap["SNDPARTICLE_TAU_MAX_WC"] = to_string(mds->sndparticle_tau_max_wc);
- mRNAMap["SNDPARTICLE_TAU_MIN_TA"] = to_string(mds->sndparticle_tau_min_ta);
- mRNAMap["SNDPARTICLE_TAU_MAX_TA"] = to_string(mds->sndparticle_tau_max_ta);
- mRNAMap["SNDPARTICLE_TAU_MIN_K"] = to_string(mds->sndparticle_tau_min_k);
- mRNAMap["SNDPARTICLE_TAU_MAX_K"] = to_string(mds->sndparticle_tau_max_k);
- mRNAMap["SNDPARTICLE_K_WC"] = to_string(mds->sndparticle_k_wc);
- mRNAMap["SNDPARTICLE_K_TA"] = to_string(mds->sndparticle_k_ta);
- mRNAMap["SNDPARTICLE_K_B"] = to_string(mds->sndparticle_k_b);
- mRNAMap["SNDPARTICLE_K_D"] = to_string(mds->sndparticle_k_d);
- mRNAMap["SNDPARTICLE_L_MIN"] = to_string(mds->sndparticle_l_min);
- mRNAMap["SNDPARTICLE_L_MAX"] = to_string(mds->sndparticle_l_max);
- mRNAMap["SNDPARTICLE_POTENTIAL_RADIUS"] = to_string(mds->sndparticle_potential_radius);
- mRNAMap["SNDPARTICLE_UPDATE_RADIUS"] = to_string(mds->sndparticle_update_radius);
- mRNAMap["LIQUID_SURFACE_TENSION"] = to_string(mds->surface_tension);
+ mRNAMap["FLIP_RATIO"] = to_string(fds->flip_ratio);
+ mRNAMap["PARTICLE_RANDOMNESS"] = to_string(fds->particle_randomness);
+ mRNAMap["PARTICLE_NUMBER"] = to_string(fds->particle_number);
+ mRNAMap["PARTICLE_MINIMUM"] = to_string(fds->particle_minimum);
+ mRNAMap["PARTICLE_MAXIMUM"] = to_string(fds->particle_maximum);
+ mRNAMap["PARTICLE_RADIUS"] = to_string(fds->particle_radius);
+ mRNAMap["FRACTIONS_THRESHOLD"] = to_string(fds->fractions_threshold);
+ mRNAMap["MESH_CONCAVE_UPPER"] = to_string(fds->mesh_concave_upper);
+ mRNAMap["MESH_CONCAVE_LOWER"] = to_string(fds->mesh_concave_lower);
+ mRNAMap["MESH_PARTICLE_RADIUS"] = to_string(fds->mesh_particle_radius);
+ mRNAMap["MESH_SMOOTHEN_POS"] = to_string(fds->mesh_smoothen_pos);
+ mRNAMap["MESH_SMOOTHEN_NEG"] = to_string(fds->mesh_smoothen_neg);
+ mRNAMap["PARTICLE_BAND_WIDTH"] = to_string(fds->particle_band_width);
+ mRNAMap["SNDPARTICLE_TAU_MIN_WC"] = to_string(fds->sndparticle_tau_min_wc);
+ mRNAMap["SNDPARTICLE_TAU_MAX_WC"] = to_string(fds->sndparticle_tau_max_wc);
+ mRNAMap["SNDPARTICLE_TAU_MIN_TA"] = to_string(fds->sndparticle_tau_min_ta);
+ mRNAMap["SNDPARTICLE_TAU_MAX_TA"] = to_string(fds->sndparticle_tau_max_ta);
+ mRNAMap["SNDPARTICLE_TAU_MIN_K"] = to_string(fds->sndparticle_tau_min_k);
+ mRNAMap["SNDPARTICLE_TAU_MAX_K"] = to_string(fds->sndparticle_tau_max_k);
+ mRNAMap["SNDPARTICLE_K_WC"] = to_string(fds->sndparticle_k_wc);
+ mRNAMap["SNDPARTICLE_K_TA"] = to_string(fds->sndparticle_k_ta);
+ mRNAMap["SNDPARTICLE_K_B"] = to_string(fds->sndparticle_k_b);
+ mRNAMap["SNDPARTICLE_K_D"] = to_string(fds->sndparticle_k_d);
+ mRNAMap["SNDPARTICLE_L_MIN"] = to_string(fds->sndparticle_l_min);
+ mRNAMap["SNDPARTICLE_L_MAX"] = to_string(fds->sndparticle_l_max);
+ mRNAMap["SNDPARTICLE_POTENTIAL_RADIUS"] = to_string(fds->sndparticle_potential_radius);
+ mRNAMap["SNDPARTICLE_UPDATE_RADIUS"] = to_string(fds->sndparticle_update_radius);
+ mRNAMap["LIQUID_SURFACE_TENSION"] = to_string(fds->surface_tension);
mRNAMap["FLUID_VISCOSITY"] = to_string(viscosity);
mRNAMap["FLUID_DOMAIN_SIZE"] = to_string(domainSize);
+ mRNAMap["FLUID_DOMAIN_SIZE_X"] = to_string(fds->global_size[0]);
+ mRNAMap["FLUID_DOMAIN_SIZE_Y"] = to_string(fds->global_size[1]);
+ mRNAMap["FLUID_DOMAIN_SIZE_Z"] = to_string(fds->global_size[2]);
mRNAMap["SNDPARTICLE_TYPES"] = particleTypesStr;
- mRNAMap["GUIDING_ALPHA"] = to_string(mds->guide_alpha);
- mRNAMap["GUIDING_BETA"] = to_string(mds->guide_beta);
- mRNAMap["GUIDING_FACTOR"] = to_string(mds->guide_vel_factor);
- mRNAMap["GRAVITY_X"] = to_string(mds->gravity[0]);
- mRNAMap["GRAVITY_Y"] = to_string(mds->gravity[1]);
- mRNAMap["GRAVITY_Z"] = to_string(mds->gravity[2]);
+ mRNAMap["GUIDING_ALPHA"] = to_string(fds->guide_alpha);
+ mRNAMap["GUIDING_BETA"] = to_string(fds->guide_beta);
+ mRNAMap["GUIDING_FACTOR"] = to_string(fds->guide_vel_factor);
+ mRNAMap["GRAVITY_X"] = to_string(fds->gravity_final[0]);
+ mRNAMap["GRAVITY_Y"] = to_string(fds->gravity_final[1]);
+ mRNAMap["GRAVITY_Z"] = to_string(fds->gravity_final[2]);
mRNAMap["CACHE_DIR"] = cacheDirectory;
+ mRNAMap["COMPRESSION_OPENVDB"] = vdbCompressionMethod;
+ mRNAMap["PRECISION_OPENVDB"] = vdbPrecisionHalf;
+ mRNAMap["PP_PARTICLE_MAXIMUM"] = to_string(fds->sys_particle_maximum);
/* Fluid object names. */
mRNAMap["NAME_FLAGS"] = FLUID_NAME_FLAGS;
@@ -900,6 +919,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 +942,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 +990,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 +1027,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 +1035,6 @@ string MANTA::getRealValue(const string &varName)
<< endl;
return "";
}
- if (with_debug) {
- cout << "Found variable " << varName << " with value " << it->second << endl;
- }
return it->second;
}
@@ -1043,7 +1064,7 @@ string MANTA::parseLine(const string &line)
return res;
}
-string MANTA::parseScript(const string &setup_string, FluidModifierData *mmd)
+string MANTA::parseScript(const string &setup_string, FluidModifierData *fmd)
{
if (MANTA::with_debug)
cout << "MANTA::parseScript()" << endl;
@@ -1053,8 +1074,8 @@ string MANTA::parseScript(const string &setup_string, FluidModifierData *mmd)
string line = "";
// Update RNA map if modifier data is handed over
- if (mmd) {
- initializeRNAMap(mmd);
+ if (fmd) {
+ initializeRNAMap(fmd);
}
while (getline(f, line)) {
res << parseLine(line) << "\n";
@@ -1062,409 +1083,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)
{
@@ -1479,16 +1097,16 @@ static string escapeSlashes(string const &s)
return result;
}
-bool MANTA::writeConfiguration(FluidModifierData *mmd, int framenr)
+bool MANTA::writeConfiguration(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::writeConfiguration()" << endl;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
- string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_CONFIG);
+ string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_CONFIG);
string format = FLUID_DOMAIN_EXTENSION_UNI;
- string file = getFile(mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, format, framenr);
+ string file = getFile(fmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, format, framenr);
/* Create 'config' subdir if it does not exist already. */
BLI_dir_create_recursive(directory.c_str());
@@ -1499,128 +1117,120 @@ bool MANTA::writeConfiguration(FluidModifierData *mmd, int framenr)
return false;
}
- gzwrite(gzf, &mds->active_fields, sizeof(int));
- gzwrite(gzf, &mds->res, 3 * sizeof(int));
- gzwrite(gzf, &mds->dx, sizeof(float));
- gzwrite(gzf, &mds->dt, sizeof(float));
- gzwrite(gzf, &mds->p0, 3 * sizeof(float));
- gzwrite(gzf, &mds->p1, 3 * sizeof(float));
- gzwrite(gzf, &mds->dp0, 3 * sizeof(float));
- gzwrite(gzf, &mds->shift, 3 * sizeof(int));
- gzwrite(gzf, &mds->obj_shift_f, 3 * sizeof(float));
- gzwrite(gzf, &mds->obmat, 16 * sizeof(float));
- gzwrite(gzf, &mds->base_res, 3 * sizeof(int));
- gzwrite(gzf, &mds->res_min, 3 * sizeof(int));
- 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, &fds->active_fields, sizeof(int));
+ gzwrite(gzf, &fds->res, 3 * sizeof(int));
+ gzwrite(gzf, &fds->dx, sizeof(float));
+ gzwrite(gzf, &fds->dt, sizeof(float));
+ gzwrite(gzf, &fds->p0, 3 * sizeof(float));
+ gzwrite(gzf, &fds->p1, 3 * sizeof(float));
+ gzwrite(gzf, &fds->dp0, 3 * sizeof(float));
+ gzwrite(gzf, &fds->shift, 3 * sizeof(int));
+ gzwrite(gzf, &fds->obj_shift_f, 3 * sizeof(float));
+ gzwrite(gzf, &fds->obmat, 16 * sizeof(float));
+ gzwrite(gzf, &fds->base_res, 3 * sizeof(int));
+ gzwrite(gzf, &fds->res_min, 3 * sizeof(int));
+ gzwrite(gzf, &fds->res_max, 3 * sizeof(int));
+ gzwrite(gzf, &fds->active_color, 3 * sizeof(float));
+ gzwrite(gzf, &fds->time_total, sizeof(int));
+ gzwrite(gzf, &FLUID_CACHE_VERSION, 4 * sizeof(char));
return (gzclose(gzf) == Z_OK);
}
-bool MANTA::writeData(FluidModifierData *mmd, int framenr)
+bool MANTA::writeData(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::writeData()" << endl;
ostringstream ss;
vector<string> pythonCommands;
- 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);
+ FluidDomainSettings *fds = fmd->domain;
- 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 directory = getDirectory(fmd, FLUID_DOMAIN_DIR_DATA);
+ string volume_format = getCacheFileEnding(fds->cache_data_format);
+ string resumable_cache = !(fds->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);
}
-bool MANTA::writeNoise(FluidModifierData *mmd, int framenr)
+bool MANTA::writeNoise(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::writeNoise()" << endl;
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
-
- string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE);
- string nformat = getCacheFileEnding(mds->cache_noise_format);
+ FluidDomainSettings *fds = fmd->domain;
- bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL);
- string resumable_cache = (final_cache) ? "False" : "True";
+ string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_NOISE);
+ string volume_format = getCacheFileEnding(fds->cache_data_format);
+ string resumable_cache = !(fds->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);
}
-bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr)
+bool MANTA::readConfiguration(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::readConfiguration()" << endl;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
float dummy;
- string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_CONFIG);
+ string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_CONFIG);
string format = FLUID_DOMAIN_EXTENSION_UNI;
- string file = getFile(mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, format, framenr);
+ string file = getFile(fmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, format, framenr);
- if (!hasConfig(mmd, framenr))
+ if (!hasConfig(fmd, 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;
}
- 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, &mds->p0, 3 * sizeof(float));
- gzread(gzf, &mds->p1, 3 * sizeof(float));
- gzread(gzf, &mds->dp0, 3 * sizeof(float));
- gzread(gzf, &mds->shift, 3 * sizeof(int));
- gzread(gzf, &mds->obj_shift_f, 3 * sizeof(float));
- gzread(gzf, &mds->obmat, 16 * sizeof(float));
- gzread(gzf, &mds->base_res, 3 * sizeof(int));
- gzread(gzf, &mds->res_min, 3 * sizeof(int));
- gzread(gzf, &mds->res_max, 3 * sizeof(int));
- gzread(gzf, &mds->active_color, 3 * sizeof(float));
- gzread(gzf, &mds->time_total, sizeof(int));
-
- mds->total_cells = mds->res[0] * mds->res[1] * mds->res[2];
+ gzread(gzf, &fds->active_fields, sizeof(int));
+ gzread(gzf, &fds->res, 3 * sizeof(int));
+ gzread(gzf, &fds->dx, sizeof(float));
+ gzread(gzf, &dummy, sizeof(float)); /* dt not needed right now. */
+ gzread(gzf, &fds->p0, 3 * sizeof(float));
+ gzread(gzf, &fds->p1, 3 * sizeof(float));
+ gzread(gzf, &fds->dp0, 3 * sizeof(float));
+ gzread(gzf, &fds->shift, 3 * sizeof(int));
+ gzread(gzf, &fds->obj_shift_f, 3 * sizeof(float));
+ gzread(gzf, &fds->obmat, 16 * sizeof(float));
+ gzread(gzf, &fds->base_res, 3 * sizeof(int));
+ gzread(gzf, &fds->res_min, 3 * sizeof(int));
+ gzread(gzf, &fds->res_max, 3 * sizeof(int));
+ gzread(gzf, &fds->active_color, 3 * sizeof(float));
+ gzread(gzf, &fds->time_total, sizeof(int));
+ gzread(gzf, &fds->cache_id, 4 * sizeof(char)); /* Older caches might have no id. */
+
+ fds->total_cells = fds->res[0] * fds->res[1] * fds->res[2];
return (gzclose(gzf) == Z_OK);
}
-bool MANTA::readData(FluidModifierData *mmd, int framenr)
+bool MANTA::readData(FluidModifierData *fmd, int framenr, bool resumable)
{
if (with_debug)
cout << "MANTA::readData()" << endl;
@@ -1630,43 +1240,37 @@ bool MANTA::readData(FluidModifierData *mmd, int framenr)
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
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 directory = getDirectory(fmd, FLUID_DOMAIN_DIR_DATA);
+ string volume_format = getCacheFileEnding(fds->cache_data_format);
+ string resumable_cache = (!resumable) ? "False" : "True";
/* Sanity check: Are cache files present? */
- if (!hasData(mmd, framenr))
+ if (!hasData(fmd, 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);
+ return (mSmokeFromFile = result);
}
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 (mFlipFromFile = result);
}
return result;
}
-bool MANTA::readNoise(FluidModifierData *mmd, int framenr)
+bool MANTA::readNoise(FluidModifierData *fmd, int framenr, bool resumable)
{
if (with_debug)
cout << "MANTA::readNoise()" << endl;
@@ -1676,30 +1280,29 @@ bool MANTA::readNoise(FluidModifierData *mmd, int framenr)
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
- string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE);
- string nformat = getCacheFileEnding(mds->cache_noise_format);
+ string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_NOISE);
+ 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(fds->cache_id, FLUID_CACHE_VERSION)) ? fds->cache_data_format :
+ fds->cache_noise_format;
+ string volume_format = getCacheFileEnding(format);
/* Sanity check: Are cache files present? */
- if (!hasNoise(mmd, framenr))
+ if (!hasNoise(fmd, framenr))
return false;
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);
+ return (mNoiseFromFile = 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)
+bool MANTA::readMesh(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::readMesh()" << endl;
@@ -1709,35 +1312,32 @@ bool MANTA::readMesh(FluidModifierData *mmd, int framenr)
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
- string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_MESH);
- string mformat = getCacheFileEnding(mds->cache_mesh_format);
- string dformat = getCacheFileEnding(mds->cache_data_format);
+ string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_MESH);
+ string mesh_format = getCacheFileEnding(fds->cache_mesh_format);
+ string volume_format = getCacheFileEnding(fds->cache_data_format);
/* Sanity check: Are cache files present? */
- if (!hasMesh(mmd, framenr))
+ if (!hasMesh(fmd, framenr))
return false;
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);
+ return (mMeshFromFile = 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 *fmd, int framenr, bool resumable)
{
if (with_debug)
cout << "MANTA::readParticles()" << endl;
@@ -1749,65 +1349,67 @@ bool MANTA::readParticles(FluidModifierData *mmd, int framenr)
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
- string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_PARTICLES);
- string pformat = getCacheFileEnding(mds->cache_particle_format);
+ string directory = getDirectory(fmd, FLUID_DOMAIN_DIR_PARTICLES);
+ 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(fds->cache_id, FLUID_CACHE_VERSION)) ? fds->cache_data_format :
+ fds->cache_particle_format;
+ string volume_format = getCacheFileEnding(format);
/* Sanity check: Are cache files present? */
- if (!hasParticles(mmd, framenr))
+ if (!hasParticles(fmd, framenr))
return false;
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);
+ return (mParticlesFromFile = runPythonString(pythonCommands));
}
-bool MANTA::readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain)
+bool MANTA::readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain)
{
if (with_debug)
cout << "MANTA::readGuiding()" << endl;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
if (!mUsingGuiding)
return false;
- if (!mds)
+ if (!fds)
return false;
ostringstream ss;
vector<string> pythonCommands;
- string directory = (sourceDomain) ? getDirectory(mmd, FLUID_DOMAIN_DIR_DATA) :
- getDirectory(mmd, FLUID_DOMAIN_DIR_GUIDE);
- string gformat = getCacheFileEnding(mds->cache_data_format);
+ string directory = (sourceDomain) ? getDirectory(fmd, FLUID_DOMAIN_DIR_DATA) :
+ getDirectory(fmd, FLUID_DOMAIN_DIR_GUIDE);
+ string volume_format = getCacheFileEnding(fds->cache_data_format);
/* Sanity check: Are cache files present? */
- if (!hasGuiding(mmd, framenr, sourceDomain))
+ if (!hasGuiding(fmd, framenr, sourceDomain))
return false;
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());
return runPythonString(pythonCommands);
}
-bool MANTA::bakeData(FluidModifierData *mmd, int framenr)
+bool MANTA::bakeData(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::bakeData()" << endl;
@@ -1815,174 +1417,145 @@ bool MANTA::bakeData(FluidModifierData *mmd, int framenr)
string tmpString, finalString;
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
char cacheDirData[FILE_MAX], cacheDirGuiding[FILE_MAX];
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(fds->cache_data_format);
BLI_path_join(
- cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr);
+ cacheDirData, sizeof(cacheDirData), fds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr);
BLI_path_join(cacheDirGuiding,
sizeof(cacheDirGuiding),
- mds->cache_directory,
+ fds->cache_directory,
FLUID_DOMAIN_DIR_GUIDE,
nullptr);
BLI_path_make_safe(cacheDirData);
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);
}
-bool MANTA::bakeNoise(FluidModifierData *mmd, int framenr)
+bool MANTA::bakeNoise(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::bakeNoise()" << endl;
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->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(fds->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);
+ cacheDirNoise, sizeof(cacheDirNoise), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE, nullptr);
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);
}
-bool MANTA::bakeMesh(FluidModifierData *mmd, int framenr)
+bool MANTA::bakeMesh(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::bakeMesh()" << endl;
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->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(fds->cache_data_format);
+ string mesh_format = getCacheFileEnding(fds->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);
+ cacheDirMesh, sizeof(cacheDirMesh), fds->cache_directory, FLUID_DOMAIN_DIR_MESH, nullptr);
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);
}
-bool MANTA::bakeParticles(FluidModifierData *mmd, int framenr)
+bool MANTA::bakeParticles(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::bakeParticles()" << endl;
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->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(fds->cache_data_format);
+ string resumable_cache = !(fds->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,
+ fds->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);
}
-bool MANTA::bakeGuiding(FluidModifierData *mmd, int framenr)
+bool MANTA::bakeGuiding(FluidModifierData *fmd, int framenr)
{
if (with_debug)
cout << "MANTA::bakeGuiding()" << endl;
ostringstream ss;
vector<string> pythonCommands;
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
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(fds->cache_data_format);
BLI_path_join(cacheDirGuiding,
sizeof(cacheDirGuiding),
- mds->cache_directory,
+ fds->cache_directory,
FLUID_DOMAIN_DIR_GUIDE,
nullptr);
BLI_path_make_safe(cacheDirGuiding);
ss.str("");
ss << "bake_guiding_" << mCurrentID << "('" << escapeSlashes(cacheDirGuiding) << "', " << framenr
- << ", '" << gformat << "', " << resumable_cache << ")";
+ << ", '" << volume_format << "')";
pythonCommands.push_back(ss.str());
return runPythonString(pythonCommands);
}
-bool MANTA::updateVariables(FluidModifierData *mmd)
+bool MANTA::updateVariables(FluidModifierData *fmd)
{
string tmpString, finalString;
vector<string> pythonCommands;
@@ -2006,13 +1579,13 @@ bool MANTA::updateVariables(FluidModifierData *mmd)
if (mUsingMesh)
tmpString += fluid_variables_mesh;
- finalString = parseScript(tmpString, mmd);
+ finalString = parseScript(tmpString, fmd);
pythonCommands.push_back(finalString);
return runPythonString(pythonCommands);
}
-void MANTA::exportSmokeScript(FluidModifierData *mmd)
+void MANTA::exportSmokeScript(FluidModifierData *fmd)
{
if (with_debug)
cout << "MANTA::exportSmokeScript()" << endl;
@@ -2020,10 +1593,10 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd)
char cacheDir[FILE_MAX] = "\0";
char cacheDirScript[FILE_MAX] = "\0";
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
BLI_path_join(
- cacheDir, sizeof(cacheDir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr);
+ cacheDir, sizeof(cacheDir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr);
BLI_path_make_safe(cacheDir);
/* Create 'script' subdir if it does not exist already */
BLI_dir_create_recursive(cacheDir);
@@ -2031,14 +1604,14 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd)
cacheDirScript, sizeof(cacheDirScript), cacheDir, FLUID_DOMAIN_SMOKE_SCRIPT, nullptr);
BLI_path_make_safe(cacheDir);
- bool noise = mds->flags & FLUID_DOMAIN_USE_NOISE;
- bool heat = mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT;
- bool colors = mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS;
- bool fire = mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE;
- bool obstacle = mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE;
- bool guiding = mds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE;
- bool invel = mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL;
- bool outflow = mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW;
+ bool noise = fds->flags & FLUID_DOMAIN_USE_NOISE;
+ bool heat = fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT;
+ bool colors = fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS;
+ bool fire = fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE;
+ bool obstacle = fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE;
+ bool guiding = fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE;
+ bool invel = fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL;
+ bool outflow = fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW;
string manta_script;
@@ -2092,8 +1665,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)
@@ -2112,7 +1684,7 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd)
manta_script += header_main + smoke_standalone + fluid_standalone;
// Fill in missing variables in script
- string final_script = MANTA::parseScript(manta_script, mmd);
+ string final_script = MANTA::parseScript(manta_script, fmd);
// Write script
ofstream myfile;
@@ -2121,7 +1693,7 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd)
myfile.close();
}
-void MANTA::exportLiquidScript(FluidModifierData *mmd)
+void MANTA::exportLiquidScript(FluidModifierData *fmd)
{
if (with_debug)
cout << "MANTA::exportLiquidScript()" << endl;
@@ -2129,10 +1701,10 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd)
char cacheDir[FILE_MAX] = "\0";
char cacheDirScript[FILE_MAX] = "\0";
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
BLI_path_join(
- cacheDir, sizeof(cacheDir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr);
+ cacheDir, sizeof(cacheDir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr);
BLI_path_make_safe(cacheDir);
/* Create 'script' subdir if it does not exist already */
BLI_dir_create_recursive(cacheDir);
@@ -2140,16 +1712,16 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd)
cacheDirScript, sizeof(cacheDirScript), cacheDir, FLUID_DOMAIN_LIQUID_SCRIPT, nullptr);
BLI_path_make_safe(cacheDirScript);
- bool mesh = mds->flags & FLUID_DOMAIN_USE_MESH;
- bool drops = mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY;
- bool bubble = mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE;
- bool floater = mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM;
- bool tracer = mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER;
- bool obstacle = mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE;
- bool fractions = mds->flags & FLUID_DOMAIN_USE_FRACTIONS;
- bool guiding = mds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE;
- bool invel = mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL;
- bool outflow = mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW;
+ bool mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
+ bool drops = fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY;
+ bool bubble = fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE;
+ bool floater = fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM;
+ bool tracer = fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER;
+ bool obstacle = fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE;
+ bool fractions = fds->flags & FLUID_DOMAIN_USE_FRACTIONS;
+ bool guiding = fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE;
+ bool invel = fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL;
+ bool outflow = fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW;
string manta_script;
@@ -2198,8 +1770,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)
@@ -2221,7 +1792,7 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd)
manta_script += header_main + liquid_standalone + fluid_standalone;
// Fill in missing variables in script
- string final_script = MANTA::parseScript(manta_script, mmd);
+ string final_script = MANTA::parseScript(manta_script, fmd);
// Write script
ofstream myfile;
@@ -2241,7 +1812,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;
}
@@ -2364,10 +1935,10 @@ float MANTA::getTimestep()
return (float)pyObjectToDouble(callPythonFunction(solver, func, true));
}
-bool MANTA::needsRealloc(FluidModifierData *mmd)
+bool MANTA::needsRealloc(FluidModifierData *fmd)
{
- FluidDomainSettings *mds = mmd->domain;
- return (mds->res[0] != mResX || mds->res[1] != mResY || mds->res[2] != mResZ);
+ FluidDomainSettings *fds = fmd->domain;
+ return (fds->res[0] != mResX || fds->res[1] != mResY || fds->res[2] != mResZ);
}
void MANTA::adaptTimestep()
@@ -2384,952 +1955,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)
@@ -3469,86 +2094,125 @@ void MANTA::updatePointers()
mNoiseFromFile = false;
}
-bool MANTA::hasConfig(FluidModifierData *mmd, int framenr)
+bool MANTA::hasConfig(FluidModifierData *fmd, int framenr)
{
string extension = FLUID_DOMAIN_EXTENSION_UNI;
return BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, extension, framenr).c_str());
+ getFile(fmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_NAME_CONFIG, extension, framenr).c_str());
}
-bool MANTA::hasData(FluidModifierData *mmd, int framenr)
+bool MANTA::hasData(FluidModifierData *fmd, int framenr)
{
- string extension = getCacheFileEnding(mmd->domain->cache_data_format);
+ string extension = getCacheFileEnding(fmd->domain->cache_data_format);
bool exists = BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_DATA, extension, framenr).c_str());
+ getFile(fmd, FLUID_DOMAIN_DIR_DATA, FLUID_NAME_DATA, extension, framenr).c_str());
/* Check single file naming. */
if (!exists) {
string filename = (mUsingSmoke) ? FLUID_NAME_DENSITY : FLUID_NAME_PP;
- exists = BLI_exists(getFile(mmd, FLUID_DOMAIN_DIR_DATA, filename, extension, framenr).c_str());
+ exists = BLI_exists(getFile(fmd, 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)
+bool MANTA::hasNoise(FluidModifierData *fmd, int framenr)
{
- string extension = getCacheFileEnding(mmd->domain->cache_noise_format);
+ string extension = getCacheFileEnding(fmd->domain->cache_data_format);
bool exists = BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_NOISE, extension, framenr).c_str());
+ getFile(fmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_NOISE, extension, framenr).c_str());
/* Check single file naming. */
if (!exists) {
+ extension = getCacheFileEnding(fmd->domain->cache_data_format);
exists = BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, extension, framenr)
+ getFile(fmd, FLUID_DOMAIN_DIR_NOISE, FLUID_NAME_DENSITY_NOISE, extension, framenr)
.c_str());
}
+ /* Check single file naming with deprecated extension. */
+ if (!exists) {
+ extension = getCacheFileEnding(fmd->domain->cache_noise_format);
+ exists = BLI_exists(
+ getFile(fmd, 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)
+bool MANTA::hasMesh(FluidModifierData *fmd, 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());
+ string extension = getCacheFileEnding(fmd->domain->cache_mesh_format);
+ bool exists = BLI_exists(
+ getFile(fmd, FLUID_DOMAIN_DIR_MESH, FLUID_NAME_MESH, extension, framenr).c_str());
+
+ /* Check old file naming. */
+ if (!exists) {
+ exists = BLI_exists(
+ getFile(fmd, 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)
+bool MANTA::hasParticles(FluidModifierData *fmd, int framenr)
{
- string extension = getCacheFileEnding(mmd->domain->cache_particle_format);
+ string extension = getCacheFileEnding(fmd->domain->cache_data_format);
bool exists = BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PARTICLES, extension, framenr).c_str());
+ getFile(fmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PARTICLES, extension, framenr).c_str());
/* Check single file naming. */
if (!exists) {
+ extension = getCacheFileEnding(fmd->domain->cache_data_format);
exists = BLI_exists(
- getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr)
+ getFile(fmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr)
.c_str());
}
+ /* Check single file naming with deprecated extension. */
+ if (!exists) {
+ extension = getCacheFileEnding(fmd->domain->cache_particle_format);
+ exists = BLI_exists(
+ getFile(fmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_NAME_PP_PARTICLES, extension, framenr)
+ .c_str());
+ }
+ if (with_debug)
+ cout << "Fluid: Has Particles: " << exists << endl;
+
return exists;
}
-bool MANTA::hasGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain)
+bool MANTA::hasGuiding(FluidModifierData *fmd, 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());
+ string extension = getCacheFileEnding(fmd->domain->cache_data_format);
+ bool exists = BLI_exists(getFile(fmd, subdirectory, filename, extension, framenr).c_str());
+ if (with_debug)
+ cout << "Fluid: Has Guiding: " << exists << endl;
+
+ return exists;
}
-string MANTA::getDirectory(FluidModifierData *mmd, string subdirectory)
+string MANTA::getDirectory(FluidModifierData *fmd, string subdirectory)
{
char directory[FILE_MAX];
BLI_path_join(
- directory, sizeof(directory), mmd->domain->cache_directory, subdirectory.c_str(), nullptr);
+ directory, sizeof(directory), fmd->domain->cache_directory, subdirectory.c_str(), nullptr);
BLI_path_make_safe(directory);
return directory;
}
string MANTA::getFile(
- FluidModifierData *mmd, string subdirectory, string fname, string extension, int framenr)
+ FluidModifierData *fmd, string subdirectory, string fname, string extension, int framenr)
{
char targetFile[FILE_MAX];
- string path = getDirectory(mmd, subdirectory);
+ string path = getDirectory(fmd, subdirectory);
string filename = fname + "_####" + extension;
BLI_join_dirfile(targetFile, sizeof(targetFile), path.c_str(), filename.c_str());
BLI_path_frame(targetFile, framenr, 0);
diff --git a/intern/mantaflow/intern/MANTA_main.h b/intern/mantaflow/intern/MANTA_main.h
index 6a8484c75d9..dae2aea4e08 100644
--- a/intern/mantaflow/intern/MANTA_main.h
+++ b/intern/mantaflow/intern/MANTA_main.h
@@ -37,7 +37,7 @@ using std::vector;
struct MANTA {
public:
- MANTA(int *res, struct FluidModifierData *mmd);
+ MANTA(int *res, struct FluidModifierData *fmd);
MANTA(){};
virtual ~MANTA();
@@ -60,82 +60,69 @@ 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);
+ void step(struct FluidModifierData *fmd, int startFrame);
// Grid initialization functions
- void initHeat(struct FluidModifierData *mmd = NULL);
- void initFire(struct FluidModifierData *mmd = NULL);
- void initColors(struct FluidModifierData *mmd = NULL);
- void initFireHigh(struct FluidModifierData *mmd = NULL);
- void initColorsHigh(struct FluidModifierData *mmd = NULL);
- void initLiquid(FluidModifierData *mmd = NULL);
- void initLiquidMesh(FluidModifierData *mmd = NULL);
- void initObstacle(FluidModifierData *mmd = NULL);
- void initCurvature(FluidModifierData *mmd = NULL);
- void initGuiding(FluidModifierData *mmd = NULL);
- void initFractions(FluidModifierData *mmd = NULL);
- void initInVelocity(FluidModifierData *mmd = NULL);
- void initOutflow(FluidModifierData *mmd = NULL);
- void initSndParts(FluidModifierData *mmd = NULL);
- void initLiquidSndParts(FluidModifierData *mmd = NULL);
+ void initHeat(struct FluidModifierData *fmd = NULL);
+ void initFire(struct FluidModifierData *fmd = NULL);
+ void initColors(struct FluidModifierData *fmd = NULL);
+ void initFireHigh(struct FluidModifierData *fmd = NULL);
+ void initColorsHigh(struct FluidModifierData *fmd = NULL);
+ void initLiquid(FluidModifierData *fmd = NULL);
+ void initLiquidMesh(FluidModifierData *fmd = NULL);
+ void initObstacle(FluidModifierData *fmd = NULL);
+ void initCurvature(FluidModifierData *fmd = NULL);
+ void initGuiding(FluidModifierData *fmd = NULL);
+ void initFractions(FluidModifierData *fmd = NULL);
+ void initInVelocity(FluidModifierData *fmd = NULL);
+ void initOutflow(FluidModifierData *fmd = NULL);
+ void initSndParts(FluidModifierData *fmd = NULL);
+ void initLiquidSndParts(FluidModifierData *fmd = NULL);
// Pointer transfer: Mantaflow -> Blender
void updatePointers();
// Write cache
- bool writeConfiguration(FluidModifierData *mmd, int framenr);
- bool writeData(FluidModifierData *mmd, int framenr);
- bool writeNoise(FluidModifierData *mmd, int framenr);
+ bool writeConfiguration(FluidModifierData *fmd, int framenr);
+ bool writeData(FluidModifierData *fmd, int framenr);
+ bool writeNoise(FluidModifierData *fmd, int framenr);
// write calls for mesh and particles were left in bake calls for now
// 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 readMesh(FluidModifierData *mmd, int framenr);
- bool readParticles(FluidModifierData *mmd, int framenr);
- bool readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain);
+ bool readConfiguration(FluidModifierData *fmd, int framenr);
+ bool readData(FluidModifierData *fmd, int framenr, bool resumable);
+ bool readNoise(FluidModifierData *fmd, int framenr, bool resumable);
+ bool readMesh(FluidModifierData *fmd, int framenr);
+ bool readParticles(FluidModifierData *fmd, int framenr, bool resumable);
+ bool readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain);
// Read cache (via file read functions in MANTA - e.g. read .bobj.gz meshes, .uni particles)
- bool updateMeshStructures(FluidModifierData *mmd, int framenr);
- bool updateFlipStructures(FluidModifierData *mmd, int framenr);
- bool updateParticleStructures(FluidModifierData *mmd, int framenr);
- bool updateSmokeStructures(FluidModifierData *mmd, int framenr);
- bool updateNoiseStructures(FluidModifierData *mmd, int framenr);
- bool updateVariables(FluidModifierData *mmd);
+ bool updateMeshStructures(FluidModifierData *fmd, int framenr);
+ bool updateFlipStructures(FluidModifierData *fmd, int framenr);
+ bool updateParticleStructures(FluidModifierData *fmd, int framenr);
+ bool updateSmokeStructures(FluidModifierData *fmd, int framenr);
+ bool updateNoiseStructures(FluidModifierData *fmd, int framenr);
+ bool updateVariables(FluidModifierData *fmd);
// Bake cache
- bool bakeData(FluidModifierData *mmd, int framenr);
- bool bakeNoise(FluidModifierData *mmd, int framenr);
- bool bakeMesh(FluidModifierData *mmd, int framenr);
- bool bakeParticles(FluidModifierData *mmd, int framenr);
- bool bakeGuiding(FluidModifierData *mmd, int framenr);
+ bool bakeData(FluidModifierData *fmd, int framenr);
+ bool bakeNoise(FluidModifierData *fmd, int framenr);
+ bool bakeMesh(FluidModifierData *fmd, int framenr);
+ bool bakeParticles(FluidModifierData *fmd, int framenr);
+ bool bakeGuiding(FluidModifierData *fmd, int framenr);
// IO for Mantaflow scene script
- void exportSmokeScript(struct FluidModifierData *mmd);
- void exportLiquidScript(struct FluidModifierData *mmd);
+ void exportSmokeScript(struct FluidModifierData *fmd);
+ void exportLiquidScript(struct FluidModifierData *fmd);
// Check cache status by frame
- bool hasConfig(FluidModifierData *mmd, int framenr);
- bool hasData(FluidModifierData *mmd, int framenr);
- bool hasNoise(FluidModifierData *mmd, int framenr);
- bool hasMesh(FluidModifierData *mmd, int framenr);
- bool hasParticles(FluidModifierData *mmd, int framenr);
- bool hasGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain);
+ bool hasConfig(FluidModifierData *fmd, int framenr);
+ bool hasData(FluidModifierData *fmd, int framenr);
+ bool hasNoise(FluidModifierData *fmd, int framenr);
+ bool hasMesh(FluidModifierData *fmd, int framenr);
+ bool hasParticles(FluidModifierData *fmd, int framenr);
+ bool hasGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain);
inline size_t getTotalCells()
{
@@ -752,7 +739,7 @@ struct MANTA {
float getTimestep();
void adaptTimestep();
- bool needsRealloc(FluidModifierData *mmd);
+ bool needsRealloc(FluidModifierData *fmd);
private:
// simulation constants
@@ -887,30 +874,20 @@ struct MANTA {
vector<pVel> *mSndParticleVelocity;
vector<float> *mSndParticleLife;
- void initializeRNAMap(struct FluidModifierData *mmd = NULL);
- void initDomain(struct FluidModifierData *mmd = NULL);
- void initNoise(struct FluidModifierData *mmd = NULL);
- void initMesh(struct FluidModifierData *mmd = NULL);
- void initSmoke(struct FluidModifierData *mmd = NULL);
- void initSmokeNoise(struct FluidModifierData *mmd = NULL);
+ void initializeRNAMap(struct FluidModifierData *fmd = NULL);
+ void initDomain(struct FluidModifierData *fmd = NULL);
+ void initNoise(struct FluidModifierData *fmd = NULL);
+ void initMesh(struct FluidModifierData *fmd = NULL);
+ void initSmoke(struct FluidModifierData *fmd = NULL);
+ void initSmokeNoise(struct FluidModifierData *fmd = NULL);
void initializeMantaflow();
void terminateMantaflow();
bool runPythonString(vector<string> commands);
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 parseScript(const string &setup_string, FluidModifierData *fmd = NULL);
+ string getDirectory(struct FluidModifierData *fmd, string subdirectory);
+ string getFile(struct FluidModifierData *fmd,
string subdirectory,
string fname,
string extension,
diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp
index 49bc224b3fa..60546bc1183 100644
--- a/intern/mantaflow/intern/manta_fluid_API.cpp
+++ b/intern/mantaflow/intern/manta_fluid_API.cpp
@@ -27,9 +27,9 @@
#include "manta_fluid_API.h"
/* Fluid functions */
-MANTA *manta_init(int *res, struct FluidModifierData *mmd)
+MANTA *manta_init(int *res, struct FluidModifierData *fmd)
{
- return new MANTA(res, mmd);
+ return new MANTA(res, fmd);
}
void manta_free(MANTA *fluid)
{
@@ -37,208 +37,173 @@ void manta_free(MANTA *fluid)
fluid = nullptr;
}
-void manta_ensure_obstacle(MANTA *fluid, struct FluidModifierData *mmd)
+void manta_ensure_obstacle(MANTA *fluid, struct FluidModifierData *fmd)
{
if (!fluid)
return;
- fluid->initObstacle(mmd);
+ fluid->initObstacle(fmd);
fluid->updatePointers();
}
-void manta_ensure_guiding(MANTA *fluid, struct FluidModifierData *mmd)
+void manta_ensure_guiding(MANTA *fluid, struct FluidModifierData *fmd)
{
if (!fluid)
return;
- fluid->initGuiding(mmd);
+ fluid->initGuiding(fmd);
fluid->updatePointers();
}
-void manta_ensure_invelocity(MANTA *fluid, struct FluidModifierData *mmd)
+void manta_ensure_invelocity(MANTA *fluid, struct FluidModifierData *fmd)
{
if (!fluid)
return;
- fluid->initInVelocity(mmd);
+ fluid->initInVelocity(fmd);
fluid->updatePointers();
}
-void manta_ensure_outflow(MANTA *fluid, struct FluidModifierData *mmd)
+void manta_ensure_outflow(MANTA *fluid, struct FluidModifierData *fmd)
{
if (!fluid)
return;
- fluid->initOutflow(mmd);
+ fluid->initOutflow(fmd);
fluid->updatePointers();
}
-int manta_write_config(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_write_config(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->writeConfiguration(mmd, framenr);
+ return fluid->writeConfiguration(fmd, framenr);
}
-int manta_write_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_write_data(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->writeData(mmd, framenr);
+ return fluid->writeData(fmd, framenr);
}
-int manta_write_noise(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_write_noise(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->writeNoise(mmd, framenr);
+ return fluid->writeNoise(fmd, framenr);
}
-int manta_read_config(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_config(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->readConfiguration(mmd, framenr);
+ return fluid->readConfiguration(fmd, framenr);
}
-int manta_read_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_data(MANTA *fluid, FluidModifierData *fmd, int framenr, bool resumable)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->readData(mmd, framenr);
+ return fluid->readData(fmd, framenr, resumable);
}
-int manta_read_noise(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_noise(MANTA *fluid, FluidModifierData *fmd, int framenr, bool resumable)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->readNoise(mmd, framenr);
+ return fluid->readNoise(fmd, framenr, resumable);
}
-int manta_read_mesh(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_mesh(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->readMesh(mmd, framenr);
+ return fluid->readMesh(fmd, framenr);
}
-int manta_read_particles(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_read_particles(MANTA *fluid, FluidModifierData *fmd, int framenr, bool resumable)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->readParticles(mmd, framenr);
+ return fluid->readParticles(fmd, framenr, resumable);
}
-int manta_read_guiding(MANTA *fluid, FluidModifierData *mmd, int framenr, bool sourceDomain)
+int manta_read_guiding(MANTA *fluid, FluidModifierData *fmd, int framenr, bool sourceDomain)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->readGuiding(mmd, framenr, sourceDomain);
+ return fluid->readGuiding(fmd, framenr, sourceDomain);
}
-int manta_update_liquid_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_bake_data(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->updateFlipStructures(mmd, framenr);
+ return fluid->bakeData(fmd, framenr);
}
-int manta_update_mesh_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_bake_noise(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->updateMeshStructures(mmd, framenr);
+ return fluid->bakeNoise(fmd, framenr);
}
-int manta_update_particle_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_bake_mesh(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->updateParticleStructures(mmd, framenr);
+ return fluid->bakeMesh(fmd, framenr);
}
-int manta_update_smoke_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_bake_particles(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->updateSmokeStructures(mmd, framenr);
+ return fluid->bakeParticles(fmd, framenr);
}
-int manta_update_noise_structures(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_bake_guiding(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->updateNoiseStructures(mmd, framenr);
+ return fluid->bakeGuiding(fmd, framenr);
}
-int manta_bake_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_has_data(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->bakeData(mmd, framenr);
+ return fluid->hasData(fmd, framenr);
}
-int manta_bake_noise(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_has_noise(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->bakeNoise(mmd, framenr);
+ return fluid->hasNoise(fmd, framenr);
}
-int manta_bake_mesh(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_has_mesh(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->bakeMesh(mmd, framenr);
+ return fluid->hasMesh(fmd, framenr);
}
-int manta_bake_particles(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_has_particles(MANTA *fluid, FluidModifierData *fmd, int framenr)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->bakeParticles(mmd, framenr);
+ return fluid->hasParticles(fmd, framenr);
}
-int manta_bake_guiding(MANTA *fluid, FluidModifierData *mmd, int framenr)
+int manta_has_guiding(MANTA *fluid, FluidModifierData *fmd, int framenr, bool domain)
{
- if (!fluid || !mmd)
+ if (!fluid || !fmd)
return 0;
- return fluid->bakeGuiding(mmd, framenr);
+ return fluid->hasGuiding(fmd, framenr, domain);
}
-int manta_has_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->hasData(mmd, framenr);
-}
-
-int manta_has_noise(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->hasNoise(mmd, framenr);
-}
-
-int manta_has_mesh(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->hasMesh(mmd, framenr);
-}
-
-int manta_has_particles(MANTA *fluid, FluidModifierData *mmd, int framenr)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->hasParticles(mmd, framenr);
-}
-
-int manta_has_guiding(MANTA *fluid, FluidModifierData *mmd, int framenr, bool domain)
-{
- if (!fluid || !mmd)
- return 0;
- return fluid->hasGuiding(mmd, framenr, domain);
-}
-
-void manta_update_variables(MANTA *fluid, FluidModifierData *mmd)
+void manta_update_variables(MANTA *fluid, FluidModifierData *fmd)
{
if (!fluid)
return;
- fluid->updateVariables(mmd);
+ fluid->updateVariables(fmd);
}
int manta_get_frame(MANTA *fluid)
@@ -262,11 +227,11 @@ void manta_adapt_timestep(MANTA *fluid)
fluid->adaptTimestep();
}
-bool manta_needs_realloc(MANTA *fluid, FluidModifierData *mmd)
+bool manta_needs_realloc(MANTA *fluid, FluidModifierData *fmd)
{
if (!fluid)
return false;
- return fluid->needsRealloc(mmd);
+ return fluid->needsRealloc(fmd);
}
/* Fluid accessors */
@@ -396,94 +361,11 @@ float *manta_get_phioutstatic_in(MANTA *fluid)
}
/* Smoke functions */
-void manta_smoke_export_script(MANTA *smoke, FluidModifierData *mmd)
+void manta_smoke_export_script(MANTA *smoke, FluidModifierData *fmd)
{
- if (!smoke || !mmd)
+ if (!smoke || !fmd)
return;
- smoke->exportSmokeScript(mmd);
-}
-
-void manta_smoke_export(MANTA *smoke,
- float *dt,
- float *dx,
- float **dens,
- float **react,
- float **flame,
- float **fuel,
- float **heat,
- float **vx,
- float **vy,
- float **vz,
- float **r,
- float **g,
- float **b,
- int **flags,
- float **shadow)
-{
- if (dens)
- *dens = smoke->getDensity();
- if (fuel)
- *fuel = smoke->getFuel();
- if (react)
- *react = smoke->getReact();
- if (flame)
- *flame = smoke->getFlame();
- if (heat)
- *heat = smoke->getHeat();
- *vx = smoke->getVelocityX();
- *vy = smoke->getVelocityY();
- *vz = smoke->getVelocityZ();
- if (r)
- *r = smoke->getColorR();
- if (g)
- *g = smoke->getColorG();
- if (b)
- *b = smoke->getColorB();
- *flags = smoke->getFlags();
- if (shadow)
- *shadow = smoke->getShadow();
- *dt = 1; // dummy value, not needed for smoke
- *dx = 1; // dummy value, not needed for smoke
-}
-
-void manta_smoke_turbulence_export(MANTA *smoke,
- float **dens,
- float **react,
- float **flame,
- float **fuel,
- float **r,
- float **g,
- float **b,
- float **tcu,
- float **tcv,
- float **tcw,
- float **tcu2,
- float **tcv2,
- float **tcw2)
-{
- if (!smoke && !(smoke->usingNoise()))
- return;
-
- *dens = smoke->getDensityHigh();
- if (fuel)
- *fuel = smoke->getFuelHigh();
- if (react)
- *react = smoke->getReactHigh();
- if (flame)
- *flame = smoke->getFlameHigh();
- if (r)
- *r = smoke->getColorRHigh();
- if (g)
- *g = smoke->getColorGHigh();
- if (b)
- *b = smoke->getColorBHigh();
- *tcu = smoke->getTextureU();
- *tcv = smoke->getTextureV();
- *tcw = smoke->getTextureW();
-
- *tcu2 = smoke->getTextureU2();
- *tcv2 = smoke->getTextureV2();
- *tcw2 = smoke->getTextureW2();
+ smoke->exportSmokeScript(fmd);
}
static void get_rgba(
@@ -519,7 +401,7 @@ void manta_smoke_get_rgba(MANTA *smoke, float *data, int sequential)
sequential);
}
-void manta_smoke_turbulence_get_rgba(MANTA *smoke, float *data, int sequential)
+void manta_noise_get_rgba(MANTA *smoke, float *data, int sequential)
{
get_rgba(smoke->getColorRHigh(),
smoke->getColorGHigh(),
@@ -554,39 +436,36 @@ void manta_smoke_get_rgba_fixed_color(MANTA *smoke, float color[3], float *data,
get_rgba_fixed_color(color, smoke->getTotalCells(), data, sequential);
}
-void manta_smoke_turbulence_get_rgba_fixed_color(MANTA *smoke,
- float color[3],
- float *data,
- int sequential)
+void manta_noise_get_rgba_fixed_color(MANTA *smoke, float color[3], float *data, int sequential)
{
get_rgba_fixed_color(color, smoke->getTotalCellsHigh(), data, sequential);
}
-void manta_smoke_ensure_heat(MANTA *smoke, struct FluidModifierData *mmd)
+void manta_smoke_ensure_heat(MANTA *smoke, struct FluidModifierData *fmd)
{
if (smoke) {
- smoke->initHeat(mmd);
+ smoke->initHeat(fmd);
smoke->updatePointers();
}
}
-void manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *mmd)
+void manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *fmd)
{
if (smoke) {
- smoke->initFire(mmd);
+ smoke->initFire(fmd);
if (smoke->usingNoise()) {
- smoke->initFireHigh(mmd);
+ smoke->initFireHigh(fmd);
}
smoke->updatePointers();
}
}
-void manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *mmd)
+void manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *fmd)
{
if (smoke) {
- smoke->initColors(mmd);
+ smoke->initColors(fmd);
if (smoke->usingNoise()) {
- smoke->initColorsHigh(mmd);
+ smoke->initColorsHigh(fmd);
}
smoke->updatePointers();
}
@@ -682,45 +561,69 @@ int manta_smoke_has_colors(MANTA *smoke)
return (smoke->getColorR() && smoke->getColorG() && smoke->getColorB()) ? 1 : 0;
}
-float *manta_smoke_turbulence_get_density(MANTA *smoke)
+float *manta_noise_get_density(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getDensityHigh() : nullptr;
}
-float *manta_smoke_turbulence_get_fuel(MANTA *smoke)
+float *manta_noise_get_fuel(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getFuelHigh() : nullptr;
}
-float *manta_smoke_turbulence_get_react(MANTA *smoke)
+float *manta_noise_get_react(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getReactHigh() : nullptr;
}
-float *manta_smoke_turbulence_get_color_r(MANTA *smoke)
+float *manta_noise_get_color_r(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getColorRHigh() : nullptr;
}
-float *manta_smoke_turbulence_get_color_g(MANTA *smoke)
+float *manta_noise_get_color_g(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getColorGHigh() : nullptr;
}
-float *manta_smoke_turbulence_get_color_b(MANTA *smoke)
+float *manta_noise_get_color_b(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getColorBHigh() : nullptr;
}
-float *manta_smoke_turbulence_get_flame(MANTA *smoke)
+float *manta_noise_get_flame(MANTA *smoke)
{
return (smoke && smoke->usingNoise()) ? smoke->getFlameHigh() : nullptr;
}
+float *manta_noise_get_texture_u(MANTA *smoke)
+{
+ return (smoke && smoke->usingNoise()) ? smoke->getTextureU() : nullptr;
+}
+float *manta_noise_get_texture_v(MANTA *smoke)
+{
+ return (smoke && smoke->usingNoise()) ? smoke->getTextureV() : nullptr;
+}
+float *manta_noise_get_texture_w(MANTA *smoke)
+{
+ return (smoke && smoke->usingNoise()) ? smoke->getTextureW() : nullptr;
+}
+float *manta_noise_get_texture_u2(MANTA *smoke)
+{
+ return (smoke && smoke->usingNoise()) ? smoke->getTextureU2() : nullptr;
+}
+float *manta_noise_get_texture_v2(MANTA *smoke)
+{
+ return (smoke && smoke->usingNoise()) ? smoke->getTextureV2() : nullptr;
+}
+float *manta_noise_get_texture_w2(MANTA *smoke)
+{
+ return (smoke && smoke->usingNoise()) ? smoke->getTextureW2() : nullptr;
+}
-int manta_smoke_turbulence_has_fuel(MANTA *smoke)
+int manta_noise_has_fuel(MANTA *smoke)
{
return (smoke->getFuelHigh()) ? 1 : 0;
}
-int manta_smoke_turbulence_has_colors(MANTA *smoke)
+int manta_noise_has_colors(MANTA *smoke)
{
return (smoke->getColorRHigh() && smoke->getColorGHigh() && smoke->getColorBHigh()) ? 1 : 0;
}
-void manta_smoke_turbulence_get_res(MANTA *smoke, int *res)
+void manta_noise_get_res(MANTA *smoke, int *res)
{
if (smoke && smoke->usingNoise()) {
res[0] = smoke->getResXHigh();
@@ -728,24 +631,24 @@ void manta_smoke_turbulence_get_res(MANTA *smoke, int *res)
res[2] = smoke->getResZHigh();
}
}
-int manta_smoke_turbulence_get_cells(MANTA *smoke)
+int manta_noise_get_cells(MANTA *smoke)
{
int total_cells_high = smoke->getResXHigh() * smoke->getResYHigh() * smoke->getResZHigh();
return (smoke && smoke->usingNoise()) ? total_cells_high : 0;
}
/* Liquid functions */
-void manta_liquid_export_script(MANTA *liquid, FluidModifierData *mmd)
+void manta_liquid_export_script(MANTA *liquid, FluidModifierData *fmd)
{
- if (!liquid || !mmd)
+ if (!liquid || !fmd)
return;
- liquid->exportLiquidScript(mmd);
+ liquid->exportLiquidScript(fmd);
}
-void manta_liquid_ensure_sndparts(MANTA *liquid, struct FluidModifierData *mmd)
+void manta_liquid_ensure_sndparts(MANTA *liquid, struct FluidModifierData *fmd)
{
if (liquid) {
- liquid->initLiquidSndParts(mmd);
+ liquid->initLiquidSndParts(fmd);
liquid->updatePointers();
}
}
diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h
index 637dd22f128..0045d839be4 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\
@@ -146,22 +146,34 @@ mantaMsg('1 Mantaflow cell is ' + str(ratioMetersToRes_s$ID$) + ' Blender length
ratioResToBLength_s$ID$ = float(res_s$ID$) / float(domainSize_s$ID$) # [cells / blength] (blength: cm, m, or km, ... )\n\
mantaMsg('1 Blender length unit is ' + str(ratioResToBLength_s$ID$) + ' Mantaflow cells long.')\n\
\n\
-ratioBTimeToTimstep_s$ID$ = float(1) / float(frameLengthRaw_s$ID$) # the time within 1 blender time unit, see also fluid.c\n\
-mantaMsg('1 Blender time unit is ' + str(ratioBTimeToTimstep_s$ID$) + ' Mantaflow time units long.')\n\
+ratioBTimeToTimestep_s$ID$ = float(1) / float(frameLengthRaw_s$ID$) # the time within 1 blender time unit, see also fluid.c\n\
+mantaMsg('1 Blender time unit is ' + str(ratioBTimeToTimestep_s$ID$) + ' Mantaflow time units long.')\n\
\n\
ratioFrameToFramelength_s$ID$ = float(1) / float(frameLengthUnscaled_s$ID$ ) # the time within 1 frame\n\
mantaMsg('frame / frameLength is ' + str(ratioFrameToFramelength_s$ID$) + ' Mantaflow time units long.')\n\
\n\
-scaleAcceleration_s$ID$ = ratioResToBLength_s$ID$ * (ratioBTimeToTimstep_s$ID$**2)# [meters/btime^2] to [cells/timestep^2] (btime: sec, min, or h, ...)\n\
+scaleAcceleration_s$ID$ = ratioResToBLength_s$ID$ * (ratioBTimeToTimestep_s$ID$**2)# [meters/btime^2] to [cells/timestep^2] (btime: sec, min, or h, ...)\n\
mantaMsg('scaleAcceleration is ' + str(scaleAcceleration_s$ID$))\n\
\n\
scaleSpeedFrames_s$ID$ = ratioResToBLength_s$ID$ * ratioFrameToFramelength_s$ID$ # [blength/frame] to [cells/frameLength]\n\
mantaMsg('scaleSpeed is ' + str(scaleSpeedFrames_s$ID$))\n\
\n\
-scaleSpeedTime_s$ID$ = ratioResToBLength_s$ID$ * ratioBTimeToTimstep_s$ID$ # [blength/btime] to [cells/frameLength]\n\
+scaleSpeedTime_s$ID$ = ratioResToBLength_s$ID$ * ratioBTimeToTimestep_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\
@@ -449,14 +461,14 @@ if 'fluid_data_dict_resume_s$ID$' in globals(): fluid_data_dict_resume_s$ID$.cle
if 'fluid_guiding_dict_s$ID$' in globals(): fluid_guiding_dict_s$ID$.clear()\n\
if 'fluid_vel_dict_s$ID$' in globals(): fluid_vel_dict_s$ID$.clear()\n\
\n\
-# Delete all childs from objects (e.g. pdata for particles)\n\
-mantaMsg('Release solver childs childs')\n\
+# Delete all children from objects (e.g. pdata for particles)\n\
+mantaMsg('Release solver childrens children')\n\
for var in list(globals()):\n\
if var.endswith('_pp$ID$') or var.endswith('_mesh$ID$'):\n\
del globals()[var]\n\
\n\
-# Now delete childs from solver objects\n\
-mantaMsg('Release solver childs')\n\
+# Now delete children from solver objects\n\
+mantaMsg('Release solver children')\n\
for var in list(globals()):\n\
if var.endswith('_s$ID$') or var.endswith('_sn$ID$') or var.endswith('_sm$ID$') or var.endswith('_sp$ID$') or var.endswith('_sg$ID$'):\n\
del globals()[var]\n\
@@ -490,17 +502,21 @@ gc.collect()\n";
// BAKE
//////////////////////////////////////////////////////////////////////
+/* This has to match the behavior of BLI_path_frame,
+ * for positive and negative frame numbers. */
const std::string fluid_cache_helper =
"\n\
def fluid_cache_get_framenr_formatted_$ID$(framenr):\n\
- return str(framenr).zfill(4) # framenr with leading zeroes\n";
+ return str(framenr).zfill(4) if framenr >= 0 else str(framenr).zfill(5)\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 +547,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 +561,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 +579,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 +603,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 +625,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 +666,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 +715,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 +723,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
@@ -749,8 +770,6 @@ if (GUI):\n\
cache_resumable = $CACHE_RESUMABLE$\n\
cache_dir = '$CACHE_DIR$'\n\
file_format_data = '$CACHE_DATA_FORMAT$'\n\
-file_format_noise = '$CACHE_NOISE_FORMAT$'\n\
-file_format_particles = '$CACHE_PARTICLE_FORMAT$'\n\
file_format_mesh = '$CACHE_MESH_FORMAT$'\n\
\n\
# How many frame to load from cache\n\
diff --git a/intern/mantaflow/intern/strings/liquid_script.h b/intern/mantaflow/intern/strings/liquid_script.h
index 26b6644f231..08d8dcd7de3 100644
--- a/intern/mantaflow/intern/strings/liquid_script.h
+++ b/intern/mantaflow/intern/strings/liquid_script.h
@@ -48,7 +48,8 @@ meshRadiusFactor_s$ID$ = $MESH_PARTICLE_RADIUS$\n\
smoothenPos_s$ID$ = $MESH_SMOOTHEN_POS$\n\
smoothenNeg_s$ID$ = $MESH_SMOOTHEN_NEG$\n\
randomness_s$ID$ = $PARTICLE_RANDOMNESS$\n\
-surfaceTension_s$ID$ = $LIQUID_SURFACE_TENSION$\n";
+surfaceTension_s$ID$ = $LIQUID_SURFACE_TENSION$\n\
+maxSysParticles_s$ID$ = $PP_PARTICLE_MAXIMUM$\n";
const std::string liquid_variables_particles =
"\n\
@@ -86,16 +87,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 +105,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 +120,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 +132,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 +153,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,11 +212,12 @@ 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\
+ pp_s$ID$.maxParticles = maxSysParticles_s$ID$ # remember, 0 means no particle cap\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\
\n\
@@ -257,7 +259,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 +300,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 +312,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 +349,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 +403,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 +435,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,10 +477,9 @@ 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\
+ liquid_load_particles_$ID$(os.path.join(cache_dir, 'particles'), frame, file_format_data, cache_resumable)\n\
if using_mesh_s$ID$:\n\
liquid_load_mesh_$ID$(os.path.join(cache_dir, 'mesh'), frame, file_format_mesh)\n\
if using_guiding_s$ID$:\n\
diff --git a/intern/mantaflow/intern/strings/smoke_script.h b/intern/mantaflow/intern/strings/smoke_script.h
index a592ad8644a..f81259115c5 100644
--- a/intern/mantaflow/intern/strings/smoke_script.h
+++ b/intern/mantaflow/intern/strings/smoke_script.h
@@ -100,9 +100,12 @@ color_r_in_s$ID$ = None\n\
color_g_in_s$ID$ = None\n\
color_b_in_s$ID$ = None\n\
\n\
+# Set some initial values\n\
+shadow_s$ID$.setConst(-1)\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 +216,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 +368,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 +409,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\
@@ -490,6 +493,9 @@ def step_noise_$ID$():\n\
advectSemiLagrange(flags=flags_s$ID$, vel=vel_s$ID$, grid=uvGrid1_s$ID$, order=2)\n\
updateUvWeight(resetTime=sn$ID$.timestep*10.0 , index=1, numUvs=uvs_s$ID$, uv=uvGrid1_s$ID$, offset=uvs_offset_s$ID$)\n\
\n\
+ if not domainClosed_s$ID$ or using_outflow_s$ID$:\n\
+ resetOutflow(flags=flags_sn$ID$, real=density_sn$ID$)\n\
+ \n\
mantaMsg('Energy')\n\
computeEnergy(flags=flags_s$ID$, vel=vel_s$ID$, energy=energy_s$ID$)\n\
\n\
@@ -542,19 +548,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 +574,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,10 +599,9 @@ 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\
+ smoke_load_noise_$ID$(os.path.join(cache_dir, 'noise'), frame, file_format_data, cache_resumable)\n\
if using_guiding_s$ID$:\n\
fluid_load_guiding_$ID$(os.path.join(cache_dir, 'guiding'), frame, file_format_data)\n\
\n\
diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc
index 43416f734c5..ab81227ff1a 100644
--- a/intern/opencolorio/ocio_impl_glsl.cc
+++ b/intern/opencolorio/ocio_impl_glsl.cc
@@ -47,6 +47,7 @@
extern "C" {
#include "GPU_immediate.h"
+#include "GPU_shader.h"
}
using namespace OCIO_NAMESPACE;
@@ -94,18 +95,15 @@ struct OCIO_GLSLCurveMappingParameters {
struct OCIO_GLSLShader {
/** Cache IDs */
std::string cacheId;
- /** TODO(fclem): Remove. IMM shader interface. */
- struct GPUShaderInterface *interface;
- /** OpenGL Shader objects handles. */
- GLuint frag;
- GLuint vert;
- GLuint program;
+
+ struct GPUShader *shader;
/** Uniform locations. */
GLint dither_loc;
GLint overlay_loc;
GLint overlay_tex_loc;
GLint predivide_loc;
GLint curve_mapping_loc;
+ GLint ubo_bind;
/** Error checking. */
bool valid;
};
@@ -152,56 +150,6 @@ static OCIO_GLSLDrawState *allocateOpenGLState(void)
/** \name Shader
* \{ */
-static GLuint compileShaderText(GLenum shader_type, const char *text)
-{
- GLuint shader;
- GLint stat;
-
- shader = glCreateShader(shader_type);
- glShaderSource(shader, 1, (const GLchar **)&text, NULL);
- glCompileShader(shader);
- glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
-
- if (!stat) {
- GLchar log[1000];
- GLsizei len;
- glGetShaderInfoLog(shader, 1000, &len, log);
- fprintf(stderr, "Shader compile error:\n%s\n", log);
- return 0;
- }
-
- return shader;
-}
-
-static GLuint linkShaders(GLuint frag, GLuint vert)
-{
- if (!frag || !vert) {
- return 0;
- }
-
- GLuint program = glCreateProgram();
-
- glAttachShader(program, frag);
- glAttachShader(program, vert);
-
- glLinkProgram(program);
-
- /* check link */
- {
- GLint stat;
- glGetProgramiv(program, GL_LINK_STATUS, &stat);
- if (!stat) {
- GLchar log[1000];
- GLsizei len;
- glGetProgramInfoLog(program, 1000, &len, log);
- fprintf(stderr, "Shader link error:\n%s\n", log);
- return 0;
- }
- }
-
- return program;
-}
-
static void updateGLSLShader(OCIO_GLSLShader *shader,
ConstProcessorRcPtr *processor_scene_to_ui,
ConstProcessorRcPtr *processpr_ui_to_display,
@@ -213,28 +161,14 @@ static void updateGLSLShader(OCIO_GLSLShader *shader,
}
/* Delete any previous shader. */
- glDeleteProgram(shader->program);
- glDeleteShader(shader->frag);
- glDeleteShader(shader->vert);
-
- if (shader->interface) {
- GPU_shaderinterface_discard(shader->interface);
+ if (shader->shader) {
+ GPU_shader_free(shader->shader);
}
- {
- /* Vertex shader */
- std::ostringstream osv;
-
- osv << "#version 330\n";
- osv << datatoc_gpu_shader_display_transform_vertex_glsl;
-
- shader->vert = compileShaderText(GL_VERTEX_SHADER, osv.str().c_str());
- }
+ std::ostringstream os;
{
/* Fragment shader */
- std::ostringstream os;
- os << "#version 330\n";
/* Work around OpenColorIO not supporting latest GLSL yet. */
os << "#define texture2D texture\n";
os << "#define texture3D texture\n";
@@ -246,41 +180,36 @@ static void updateGLSLShader(OCIO_GLSLShader *shader,
os << (*processpr_ui_to_display)->getGpuShaderText(*shader_desc) << "\n";
os << datatoc_gpu_shader_display_transform_glsl;
-
- shader->frag = compileShaderText(GL_FRAGMENT_SHADER, os.str().c_str());
}
- /* shader_Program */
- if (shader->frag && shader->vert) {
- shader->program = linkShaders(shader->frag, shader->vert);
- }
+ shader->shader = GPU_shader_create(datatoc_gpu_shader_display_transform_vertex_glsl,
+ os.str().c_str(),
+ NULL,
+ NULL,
+ NULL,
+ __func__);
- if (shader->program) {
- shader->dither_loc = glGetUniformLocation(shader->program, "dither");
- shader->overlay_tex_loc = glGetUniformLocation(shader->program, "overlay_texture");
- shader->overlay_loc = glGetUniformLocation(shader->program, "overlay");
- shader->predivide_loc = glGetUniformLocation(shader->program, "predivide");
- shader->curve_mapping_loc = glGetUniformLocation(shader->program, "curve_mapping");
+ if (shader->shader) {
+ shader->dither_loc = GPU_shader_get_uniform(shader->shader, "dither");
+ shader->overlay_tex_loc = GPU_shader_get_uniform(shader->shader, "overlay_texture");
+ shader->overlay_loc = GPU_shader_get_uniform(shader->shader, "overlay");
+ shader->predivide_loc = GPU_shader_get_uniform(shader->shader, "predivide");
+ shader->curve_mapping_loc = GPU_shader_get_uniform(shader->shader, "curve_mapping");
+ shader->ubo_bind = GPU_shader_get_uniform_block_binding(shader->shader,
+ "OCIO_GLSLCurveMappingParameters");
- glUseProgram(shader->program);
-
- /* TODO(fclem) Remove this. Make caller always assume viewport space and
- * specify texco via vertex attribs. */
- shader->interface = GPU_shaderinterface_create(shader->program);
-
- /* Set UBO binding location. */
- GLuint index = glGetUniformBlockIndex(shader->program, "OCIO_GLSLCurveMappingParameters");
- glUniformBlockBinding(shader->program, index, UBO_BIND_LOC);
+ GPU_shader_bind(shader->shader);
/* Set texture bind point uniform once. This is saved by the shader. */
- glUniform1i(glGetUniformLocation(shader->program, "image_texture"), 0);
- glUniform1i(glGetUniformLocation(shader->program, "lut3d_texture"), 2);
- glUniform1i(glGetUniformLocation(shader->program, "lut3d_display_texture"), 3);
- glUniform1i(glGetUniformLocation(shader->program, "curve_mapping_texture"), 4);
+ GPUShader *sh = shader->shader;
+ GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "image_texture"), 0);
+ GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "lut3d_texture"), 2);
+ GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "lut3d_display_texture"), 3);
+ GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "curve_mapping_texture"), 4);
}
shader->cacheId = cache_id;
- shader->valid = (shader->program != 0);
+ shader->valid = (shader->shader != NULL);
}
static void ensureGLSLShader(OCIO_GLSLShader **shader_ptr,
@@ -302,12 +231,8 @@ static void ensureGLSLShader(OCIO_GLSLShader **shader_ptr,
static void freeGLSLShader(OCIO_GLSLShader *shader)
{
- glDeleteProgram(shader->program);
- glDeleteShader(shader->frag);
- glDeleteShader(shader->vert);
-
- if (shader->interface) {
- GPU_shaderinterface_discard(shader->interface);
+ if (shader->shader) {
+ GPU_shader_free(shader->shader);
}
OBJECT_GUARDED_DELETE(shader, OCIO_GLSLShader);
@@ -674,10 +599,10 @@ bool OCIOImpl::setupGLSLDraw(OCIO_GLSLDrawState **state_r,
glActiveTexture(GL_TEXTURE0);
/* Bind UBO. */
- glBindBufferBase(GL_UNIFORM_BUFFER, 0, shader_curvemap->buffer);
+ glBindBufferBase(GL_UNIFORM_BUFFER, shader->ubo_bind, shader_curvemap->buffer);
/* TODO(fclem) remove remains of IMM. */
- immBindProgram(shader->program, shader->interface);
+ immBindShader(shader->shader);
/* Bind Shader and set uniforms. */
// glUseProgram(shader->program);
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..07cda49e04b 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);
@@ -238,6 +238,14 @@ rbCollisionShape *RB_shape_new_trimesh(rbMeshData *mesh);
/* 2b - GImpact Meshes */
rbCollisionShape *RB_shape_new_gimpact_mesh(rbMeshData *mesh);
+/* Compound Shape ---------------- */
+
+rbCollisionShape *RB_shape_new_compound(void);
+void RB_compound_add_child_shape(rbCollisionShape *collisionShape,
+ rbCollisionShape *shape,
+ const float loc[3],
+ const float rot[4]);
+
/* Cleanup --------------------------- */
void RB_shape_delete(rbCollisionShape *shape);
diff --git a/intern/rigidbody/rb_bullet_api.cpp b/intern/rigidbody/rb_bullet_api.cpp
index a8bf1420523..db8c062990c 100644
--- a/intern/rigidbody/rb_bullet_api.cpp
+++ b/intern/rigidbody/rb_bullet_api.cpp
@@ -98,6 +98,8 @@ struct rbMeshData {
struct rbCollisionShape {
btCollisionShape *cshape;
rbMeshData *mesh;
+ rbCollisionShape **compoundChildShapes;
+ int compoundChilds;
};
struct rbFilterCallback : public btOverlapFilterCallback {
@@ -331,6 +333,7 @@ rbRigidBody *RB_body_new(rbCollisionShape *shape, const float loc[3], const floa
rbRigidBody *object = new rbRigidBody;
/* current transform */
btTransform trans;
+ trans.setIdentity();
trans.setOrigin(btVector3(loc[0], loc[1], loc[2]));
trans.setRotation(btQuaternion(rot[1], rot[2], rot[3], rot[0]));
@@ -413,6 +416,10 @@ void RB_body_set_mass(rbRigidBody *object, float value)
shape->calculateLocalInertia(value, localInertia);
}
+ btVector3 minAabb, maxAabb;
+ btTransform ident;
+ ident.setIdentity();
+ body->getCollisionShape()->getAabb(ident, minAabb, maxAabb);
body->setMassProps(value, localInertia);
body->updateInertiaTensor();
}
@@ -597,6 +604,7 @@ void RB_body_set_loc_rot(rbRigidBody *object, const float loc[3], const float ro
/* set transform matrix */
btTransform trans;
+ trans.setIdentity();
trans.setOrigin(btVector3(loc[0], loc[1], loc[2]));
trans.setRotation(btQuaternion(rot[1], rot[2], rot[3], rot[0]));
@@ -655,6 +663,8 @@ rbCollisionShape *RB_shape_new_box(float x, float y, float z)
rbCollisionShape *shape = new rbCollisionShape;
shape->cshape = new btBoxShape(btVector3(x, y, z));
shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -663,6 +673,8 @@ rbCollisionShape *RB_shape_new_sphere(float radius)
rbCollisionShape *shape = new rbCollisionShape;
shape->cshape = new btSphereShape(radius);
shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -671,6 +683,8 @@ rbCollisionShape *RB_shape_new_capsule(float radius, float height)
rbCollisionShape *shape = new rbCollisionShape;
shape->cshape = new btCapsuleShapeZ(radius, height);
shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -679,6 +693,8 @@ rbCollisionShape *RB_shape_new_cone(float radius, float height)
rbCollisionShape *shape = new rbCollisionShape;
shape->cshape = new btConeShapeZ(radius, height);
shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -687,6 +703,8 @@ rbCollisionShape *RB_shape_new_cylinder(float radius, float height)
rbCollisionShape *shape = new rbCollisionShape;
shape->cshape = new btCylinderShapeZ(btVector3(radius, radius, height));
shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -709,6 +727,8 @@ rbCollisionShape *RB_shape_new_convex_hull(
shape->cshape = hull_shape;
shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -773,6 +793,8 @@ rbCollisionShape *RB_shape_new_trimesh(rbMeshData *mesh)
shape->cshape = new btScaledBvhTriangleMeshShape(unscaledShape, btVector3(1.0f, 1.0f, 1.0f));
shape->mesh = mesh;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
@@ -813,9 +835,46 @@ rbCollisionShape *RB_shape_new_gimpact_mesh(rbMeshData *mesh)
shape->cshape = gimpactShape;
shape->mesh = mesh;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
return shape;
}
+/* Compound Shape ---------------- */
+
+rbCollisionShape *RB_shape_new_compound()
+{
+ rbCollisionShape *shape = new rbCollisionShape;
+ btCompoundShape *compoundShape = new btCompoundShape();
+
+ shape->cshape = compoundShape;
+ shape->mesh = NULL;
+ shape->compoundChilds = 0;
+ shape->compoundChildShapes = NULL;
+ return shape;
+}
+
+void RB_compound_add_child_shape(rbCollisionShape *parentShape,
+ rbCollisionShape *shape,
+ const float loc[3],
+ const float rot[4])
+{
+ /* set transform matrix */
+ btTransform trans;
+ trans.setIdentity();
+ trans.setOrigin(btVector3(loc[0], loc[1], loc[2]));
+ trans.setRotation(btQuaternion(rot[1], rot[2], rot[3], rot[0]));
+
+ btCompoundShape *compoundShape = (btCompoundShape *)(parentShape->cshape);
+ compoundShape->addChildShape(trans, shape->cshape);
+
+ /* Store shapes for deletion later */
+ parentShape->compoundChildShapes = (rbCollisionShape **)(realloc(
+ parentShape->compoundChildShapes,
+ sizeof(rbCollisionShape *) * (++parentShape->compoundChilds)));
+ parentShape->compoundChildShapes[parentShape->compoundChilds - 1] = shape;
+}
+
/* Cleanup --------------------------- */
void RB_shape_delete(rbCollisionShape *shape)
@@ -829,6 +888,15 @@ void RB_shape_delete(rbCollisionShape *shape)
if (shape->mesh)
RB_trimesh_data_delete(shape->mesh);
delete shape->cshape;
+
+ /* Delete compound child shapes if there are any */
+ for (int i = 0; i < shape->compoundChilds; i++) {
+ RB_shape_delete(shape->compoundChildShapes[i]);
+ }
+ if (shape->compoundChildShapes != NULL) {
+ free(shape->compoundChildShapes);
+ }
+
delete shape;
}
@@ -873,6 +941,7 @@ static void make_constraint_transforms(btTransform &transform1,
float orn[4])
{
btTransform pivot_transform = btTransform();
+ pivot_transform.setIdentity();
pivot_transform.setOrigin(btVector3(pivot[0], pivot[1], pivot[2]));
pivot_transform.setRotation(btQuaternion(orn[1], orn[2], orn[3], orn[0]));
diff --git a/tests/gtests/functions/CMakeLists.txt b/intern/sky/CMakeLists.txt
index 413761ed0c8..f67941605aa 100644
--- a/tests/gtests/functions/CMakeLists.txt
+++ b/intern/sky/CMakeLists.txt
@@ -17,23 +17,23 @@
# ***** END GPL LICENSE BLOCK *****
set(INC
- .
- ..
- ../../../source/blender/blenlib
- ../../../source/blender/functions
- ../../../source/blender/makesdna
- ../../../intern/guardedalloc
+ include
)
-setup_libdirs()
-include_directories(${INC})
+set(INC_SYS
+)
+
+set(SRC
+ source/sky_model.cpp
+ source/sky_nishita.cpp
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
-set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
+ include/sky_model.h
+ source/sky_float3.h
+ source/sky_model_data.h
+)
-if(WITH_BUILDINFO)
- set(BUILDINFO buildinfoobj)
-endif()
+set(LIB
+)
-BLENDER_TEST(FN_cpp_type "bf_blenlib;bf_functions;${BUILDINFO}")
+blender_add_lib(bf_intern_sky "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/cycles/util/util_sky_model.h b/intern/sky/include/sky_model.h
index 84340614b2c..021086e1e02 100644
--- a/intern/cycles/util/util_sky_model.h
+++ b/intern/sky/include/sky_model.h
@@ -298,12 +298,14 @@ HINT #1: if you want to model the sky of an earth-like planet that orbits
previous paragraph.
*/
-CCL_NAMESPACE_BEGIN
+#ifndef __SKY_MODEL_H__
+#define __SKY_MODEL_H__
-#ifndef _SKY_MODEL_H_
-# define _SKY_MODEL_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
-typedef double ArHosekSkyModelConfiguration[9];
+typedef double SKY_ArHosekSkyModelConfiguration[9];
// Spectral version of the model
@@ -333,8 +335,8 @@ typedef double ArHosekSkyModelConfiguration[9];
---------------------------------------------------------------------------- */
-typedef struct ArHosekSkyModelState {
- ArHosekSkyModelConfiguration configs[11];
+typedef struct SKY_ArHosekSkyModelState {
+ SKY_ArHosekSkyModelConfiguration configs[11];
double radiances[11];
double turbidity;
double solar_radius;
@@ -342,7 +344,7 @@ typedef struct ArHosekSkyModelState {
double emission_correction_factor_sun[11];
double albedo;
double elevation;
-} ArHosekSkyModelState;
+} SKY_ArHosekSkyModelState;
/* ----------------------------------------------------------------------------
@@ -353,9 +355,9 @@ typedef struct ArHosekSkyModelState {
---------------------------------------------------------------------------- */
-ArHosekSkyModelState *arhosekskymodelstate_alloc_init(const double solar_elevation,
- const double atmospheric_turbidity,
- const double ground_albedo);
+SKY_ArHosekSkyModelState *SKY_arhosekskymodelstate_alloc_init(const double solar_elevation,
+ const double atmospheric_turbidity,
+ const double ground_albedo);
/* ----------------------------------------------------------------------------
@@ -386,44 +388,68 @@ ArHosekSkyModelState *arhosekskymodelstate_alloc_init(const double solar_elevati
---------------------------------------------------------------------------- */
-ArHosekSkyModelState *arhosekskymodelstate_alienworld_alloc_init(
+SKY_ArHosekSkyModelState *SKY_arhosekskymodelstate_alienworld_alloc_init(
const double solar_elevation,
const double solar_intensity,
const double solar_surface_temperature_kelvin,
const double atmospheric_turbidity,
const double ground_albedo);
-void arhosekskymodelstate_free(ArHosekSkyModelState *state);
+void SKY_arhosekskymodelstate_free(SKY_ArHosekSkyModelState *state);
-double arhosekskymodel_radiance(ArHosekSkyModelState *state,
- double theta,
- double gamma,
- double wavelength);
+double SKY_arhosekskymodel_radiance(SKY_ArHosekSkyModelState *state,
+ double theta,
+ double gamma,
+ double wavelength);
// CIE XYZ and RGB versions
-ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidity,
- const double albedo,
- const double elevation);
+SKY_ArHosekSkyModelState *SKY_arhosek_xyz_skymodelstate_alloc_init(const double turbidity,
+ const double albedo,
+ const double elevation);
-ArHosekSkyModelState *arhosek_rgb_skymodelstate_alloc_init(const double turbidity,
- const double albedo,
- const double elevation);
+SKY_ArHosekSkyModelState *SKY_arhosek_rgb_skymodelstate_alloc_init(const double turbidity,
+ const double albedo,
+ const double elevation);
-double arhosek_tristim_skymodel_radiance(ArHosekSkyModelState *state,
- double theta,
- double gamma,
- int channel);
+double SKY_arhosek_tristim_skymodel_radiance(SKY_ArHosekSkyModelState *state,
+ double theta,
+ double gamma,
+ int channel);
// Delivers the complete function: sky + sun, including limb darkening.
// Please read the above description before using this - there are several
// caveats!
-double arhosekskymodel_solar_radiance(ArHosekSkyModelState *state,
- double theta,
- double gamma,
- double wavelength);
-
-#endif // _SKY_MODEL_H_
-
-CCL_NAMESPACE_END
+double SKY_arhosekskymodel_solar_radiance(SKY_ArHosekSkyModelState *state,
+ double theta,
+ double gamma,
+ double wavelength);
+
+/* Nishita improved sky model */
+
+void SKY_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 SKY_nishita_skymodel_precompute_sun(float sun_elevation,
+ float angular_diameter,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float *r_pixel_bottom,
+ float *r_pixel_top);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __SKY_MODEL_H__
diff --git a/intern/sky/source/sky_float3.h b/intern/sky/source/sky_float3.h
new file mode 100644
index 00000000000..2a9b9c89623
--- /dev/null
+++ b/intern/sky/source/sky_float3.h
@@ -0,0 +1,157 @@
+/*
+ * 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 __SKY_FLOAT3_H__
+#define __SKY_FLOAT3_H__
+
+// minimal float3 + util_math.h implementation for nishita sky model
+
+#include <math.h>
+
+#ifndef M_PI_F
+# define M_PI_F (3.1415926535897932f) /* pi */
+#endif
+#ifndef M_PI_2_F
+# define M_PI_2_F (1.5707963267948966f) /* pi/2 */
+#endif
+#ifndef M_2PI_F
+# define M_2PI_F (6.2831853071795864f) /* 2*pi */
+#endif
+
+struct float3 {
+ float x, y, z;
+
+ float3() = default;
+
+ float3(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
+ {
+ }
+
+ float3(const float (*ptr)[3]) : float3((const float *)ptr)
+ {
+ }
+
+ explicit float3(float value) : x(value), y(value), z(value)
+ {
+ }
+
+ explicit float3(int value) : x(value), y(value), z(value)
+ {
+ }
+
+ float3(float x, float y, float z) : x{x}, y{y}, z{z}
+ {
+ }
+
+ operator const float *() const
+ {
+ return &x;
+ }
+
+ operator float *()
+ {
+ return &x;
+ }
+
+ friend float3 operator*(const float3 &a, float b)
+ {
+ return {a.x * b, a.y * b, a.z * b};
+ }
+
+ friend float3 operator*(float b, const float3 &a)
+ {
+ return {a.x * b, a.y * b, a.z * b};
+ }
+
+ friend float3 operator-(const float3 &a, const float3 &b)
+ {
+ return {a.x - b.x, a.y - b.y, a.z - b.z};
+ }
+
+ friend float3 operator-(const float3 &a)
+ {
+ return {-a.x, -a.y, -a.z};
+ }
+
+ float length_squared() const
+ {
+ return x * x + y * y + z * z;
+ }
+
+ float length() const
+ {
+ return sqrt(length_squared());
+ }
+
+ static float distance(const float3 &a, const float3 &b)
+ {
+ return (a - b).length();
+ }
+
+ friend float3 operator+(const float3 &a, const float3 &b)
+ {
+ return {a.x + b.x, a.y + b.y, a.z + b.z};
+ }
+
+ void operator+=(const float3 &b)
+ {
+ this->x += b.x;
+ this->y += b.y;
+ this->z += b.z;
+ }
+
+ friend float3 operator*(const float3 &a, const float3 &b)
+ {
+ return {a.x * b.x, a.y * b.y, a.z * b.z};
+ }
+};
+
+inline float sqr(float a)
+{
+ return a * a;
+}
+
+inline float3 make_float3(float x, float y, float z)
+{
+ return float3(x, y, z);
+}
+
+inline float dot(const float3 &a, const float3 &b)
+{
+ return a.x * b.x + a.y * b.y + a.z * b.z;
+}
+
+inline float distance(const float3 &a, const float3 &b)
+{
+ return float3::distance(a, b);
+}
+
+inline float len_squared(float3 f)
+{
+ return f.length_squared();
+}
+
+inline float len(float3 f)
+{
+ return f.length();
+}
+
+inline float reduce_add(float3 f)
+{
+ return f.x + f.y + f.z;
+}
+
+#endif /* __SKY_FLOAT3_H__ */
diff --git a/intern/cycles/util/util_sky_model.cpp b/intern/sky/source/sky_model.cpp
index 8cdad8a90a4..64cf14ec030 100644
--- a/intern/cycles/util/util_sky_model.cpp
+++ b/intern/sky/source/sky_model.cpp
@@ -97,16 +97,14 @@ All instructions on how to use this code are in the accompanying header file.
*/
-#include "util/util_sky_model.h"
-#include "util/util_sky_model_data.h"
+#include "sky_model.h"
+#include "sky_model_data.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-CCL_NAMESPACE_BEGIN
-
// Some macro definitions that occur elsewhere in ART, and that have to be
// replicated to make this a stand-alone module.
@@ -138,7 +136,7 @@ typedef const double *ArHosekSkyModel_Radiance_Dataset;
// internal functions
static void ArHosekSkyModel_CookConfiguration(ArHosekSkyModel_Dataset dataset,
- ArHosekSkyModelConfiguration config,
+ SKY_ArHosekSkyModelConfiguration config,
double turbidity,
double albedo,
double solar_elevation)
@@ -272,7 +270,7 @@ static double ArHosekSkyModel_CookRadianceConfiguration(ArHosekSkyModel_Radiance
return res;
}
-static double ArHosekSkyModel_GetRadianceInternal(ArHosekSkyModelConfiguration configuration,
+static double ArHosekSkyModel_GetRadianceInternal(SKY_ArHosekSkyModelConfiguration configuration,
double theta,
double gamma)
{
@@ -288,15 +286,15 @@ static double ArHosekSkyModel_GetRadianceInternal(ArHosekSkyModelConfiguration c
configuration[6] * mieM + configuration[7] * zenith);
}
-void arhosekskymodelstate_free(ArHosekSkyModelState *state)
+void SKY_arhosekskymodelstate_free(SKY_ArHosekSkyModelState *state)
{
free(state);
}
-double arhosekskymodel_radiance(ArHosekSkyModelState *state,
- double theta,
- double gamma,
- double wavelength)
+double SKY_arhosekskymodel_radiance(SKY_ArHosekSkyModelState *state,
+ double theta,
+ double gamma,
+ double wavelength)
{
int low_wl = (int)((wavelength - 320.0) / 40.0);
@@ -324,11 +322,11 @@ double arhosekskymodel_radiance(ArHosekSkyModelState *state,
// xyz and rgb versions
-ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidity,
- const double albedo,
- const double elevation)
+SKY_ArHosekSkyModelState *SKY_arhosek_xyz_skymodelstate_alloc_init(const double turbidity,
+ const double albedo,
+ const double elevation)
{
- ArHosekSkyModelState *state = ALLOC(ArHosekSkyModelState);
+ SKY_ArHosekSkyModelState *state = ALLOC(SKY_ArHosekSkyModelState);
state->solar_radius = TERRESTRIAL_SOLAR_RADIUS;
state->turbidity = turbidity;
@@ -345,5 +343,3 @@ ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidit
return state;
}
-
-CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_sky_model_data.h b/intern/sky/source/sky_model_data.h
index a2a3935eb84..8d98f84cdae 100644
--- a/intern/cycles/util/util_sky_model_data.h
+++ b/intern/sky/source/sky_model_data.h
@@ -91,8 +91,6 @@ an updated version of this code has been published!
============================================================================ */
-CCL_NAMESPACE_BEGIN
-
/*
This file contains the coefficient data for the XYZ colour space version of
@@ -3843,5 +3841,3 @@ static const double datasetXYZRad3[] = {
static const double *datasetsXYZ[] = {datasetXYZ1, datasetXYZ2, datasetXYZ3};
static const double *datasetsXYZRad[] = {datasetXYZRad1, datasetXYZRad2, datasetXYZRad3};
-
-CCL_NAMESPACE_END
diff --git a/intern/sky/source/sky_nishita.cpp b/intern/sky/source/sky_nishita.cpp
new file mode 100644
index 00000000000..f36bfcc3d7b
--- /dev/null
+++ b/intern/sky/source/sky_nishita.cpp
@@ -0,0 +1,376 @@
+/*
+ * 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 "sky_float3.h"
+#include "sky_model.h"
+
+/* Constants */
+static const float rayleigh_scale = 8e3f; // Rayleigh scale height (m)
+static const float mie_scale = 1.2e3f; // Mie scale height (m)
+static const float mie_coeff = 2e-5f; // Mie scattering coefficient (m^-1)
+static const float mie_G = 0.76f; // aerosols anisotropy
+static const float sqr_G = mie_G * mie_G; // squared aerosols anisotropy
+static const float earth_radius = 6360e3f; // radius of Earth (m)
+static const float atmosphere_radius = 6420e3f; // radius of atmosphere (m)
+static const int steps = 32; // segments of primary ray
+static const int steps_light = 16; // segments of sun connection ray
+static const int num_wavelengths = 21; // number of wavelengths
+static const int min_wavelength = 380; // lowest sampled wavelength (nm)
+static const int max_wavelength = 780; // highest sampled wavelength (nm)
+// step between each sampled wavelength (nm)
+static const float step_lambda = (max_wavelength - min_wavelength) / (num_wavelengths - 1);
+/* Sun irradiance on top of the atmosphere (W*m^-2*nm^-1) */
+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 (m^-1) */
+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 (m^-1) */
+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 * step_lambda;
+}
+
+/* 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)
+{
+ 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 b = -2.0f * dot(dir, -pos);
+ float c = len_squared(pos) - sqr(earth_radius);
+ float t = b * b - 4.0f * c;
+ if (t >= 0.0f)
+ return true;
+ else
+ return false;
+}
+
+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++) {
+ /* 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 += density;
+
+ /* advance along ray */
+ P += segment;
+ }
+
+ return optical_depth * segment_length;
+}
+
+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;
+ }
+
+ /* 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++) {
+ /* 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;
+ }
+}
+
+void SKY_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;
+ float half_lat_step = latitude_step / 2.0f;
+
+ for (int y = start_y; y < end_y; y++) {
+ /* sample more pixels toward the horizon */
+ float latitude = (M_PI_2_F + half_lat_step) * sqr((float)y / height);
+
+ 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);
+
+ /* store pixels */
+ int pos_x = x * stride;
+ pixel_row[pos_x] = xyz.x;
+ pixel_row[pos_x + 1] = xyz.y;
+ pixel_row[pos_x + 2] = xyz.z;
+ /* mirror sky */
+ int mirror_x = (width - x - 1) * stride;
+ pixel_row[mirror_x] = xyz.x;
+ pixel_row[mirror_x + 1] = xyz.y;
+ pixel_row[mirror_x + 2] = xyz.z;
+ }
+ }
+}
+
+/*********** Sun ***********/
+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] * expf(-transmittance) / solid_angle;
+ }
+}
+
+void SKY_nishita_skymodel_precompute_sun(float sun_elevation,
+ float angular_diameter,
+ float altitude,
+ float air_density,
+ float dust_density,
+ float *r_pixel_bottom,
+ float *r_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 */
+ r_pixel_bottom[0] = pix_bottom.x;
+ r_pixel_bottom[1] = pix_bottom.y;
+ r_pixel_bottom[2] = pix_bottom.z;
+ r_pixel_top[0] = pix_top.x;
+ r_pixel_top[1] = pix_top.y;
+ r_pixel_top[2] = pix_top.z;
+}
diff --git a/release/darwin/background.tif b/release/darwin/background.tif
index d8785b9c9a4..65d73a86389 100644
--- a/release/darwin/background.tif
+++ b/release/darwin/background.tif
Binary files differ
diff --git a/release/darwin/buildbot/background.tif b/release/darwin/buildbot/background.tif
index 5253a6bf439..8cf364599b1 100644
--- a/release/darwin/buildbot/background.tif
+++ b/release/darwin/buildbot/background.tif
Binary files differ
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/preview_grease_pencil.blend b/release/datafiles/preview_grease_pencil.blend
index 82661d80029..46c90d8af08 100644
--- a/release/datafiles/preview_grease_pencil.blend
+++ b/release/datafiles/preview_grease_pencil.blend
Binary files differ
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index 31d0eb8e923..df1be29f642 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -109,7 +109,7 @@ const UserDef U_default = {
.keyconfigstr = "blender",
.undosteps = 32,
.undomemory = 0,
- .gp_manhattendist = 1,
+ .gp_manhattandist = 1,
.gp_euclideandist = 2,
.gp_eraser = 25,
.gp_settings = 0,
@@ -229,6 +229,8 @@ const UserDef U_default = {
.collection_instance_empty_size = 1.0f,
+ .statusbar_flag = STATUSBAR_SHOW_VERSION,
+
.runtime =
{
.is_dirty = 0,
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index a65ff15393a..af6d4b1cd29 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>")),
@@ -367,7 +367,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
if mod.bl_info.get("blender", (0, 0, 0)) < (2, 80, 0):
if _bpy.app.debug:
- print(f"Warning: Add-on '{module_name:s}' was not upgraded for 2.80, ignoring")
+ print("Warning: Add-on '%s' was not upgraded for 2.80, ignoring" % module_name)
return None
# 2) Try register collected modules.
@@ -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_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py
index e522ec3fcf9..a6101474aa9 100644
--- a/release/scripts/modules/bl_i18n_utils/settings.py
+++ b/release/scripts/modules/bl_i18n_utils/settings.py
@@ -601,8 +601,11 @@ class I18nSettings:
return json.dumps(export_dict)
def load(self, fname, reset=False):
+ reset = reset or fname is None
if reset:
self.__dict__ = {uid: data for uid, data in globals().items() if not uid.startswith("_")}
+ if fname is None:
+ return
if isinstance(fname, str):
if not os.path.isfile(fname):
# Assume it is already real JSon string...
diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
index 738554e8f2c..aa80611ac6b 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
@@ -69,6 +69,7 @@ class SpellChecker:
"spacings",
"teleport", "teleporting",
"vertices",
+ "wasn", # wasn't
# Merged words
"antialiasing",
@@ -178,7 +179,7 @@ class SpellChecker:
"reinject", "reinjected",
"rekey",
"remesh",
- "reprojection", "reproject",
+ "reprojection", "reproject", "reprojecting",
"resize",
"restpose",
"retarget", "retargets", "retargeting", "retargeted",
@@ -569,7 +570,7 @@ class SpellChecker:
"shrinkwrap",
"softbody",
"stucci",
- "subsurf",
+ "subdiv",
"subtype",
"sunsky",
"tessface", "tessfaces",
@@ -654,6 +655,7 @@ class SpellChecker:
"dx",
"eo",
"fh",
+ "fk",
"fov",
"fft",
"futura",
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..bcb2fe8324d 100644
--- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py
+++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
@@ -314,7 +314,7 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
do_save = True
if do_data_intern:
- bpy.ops.wm.previews_clear(id_type='SHADING')
+ bpy.ops.wm.previews_clear(id_type={'SHADING'})
bpy.ops.wm.previews_ensure()
render_contexts = {}
@@ -439,7 +439,7 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
def do_clear_previews(do_objects, do_collections, do_scenes, do_data_intern):
if do_data_intern:
- bpy.ops.wm.previews_clear(id_type='SHADING')
+ bpy.ops.wm.previews_clear(id_type={'SHADING'})
if do_objects:
for ob in ids_nolib(bpy.data.objects):
@@ -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/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index 19450bb38ec..8a67a598ccd 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -789,7 +789,7 @@ def register_tool(tool_cls, *, after=None, separator=False, group=False):
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
if cls is None:
- raise Exception(f"Space type {space_type!r} has no toolbar")
+ raise Exception("Space type %r has no toolbar" % space_type)
tools = cls._tools[context_mode]
# First sanity check
@@ -799,9 +799,9 @@ def register_tool(tool_cls, *, after=None, separator=False, group=False):
if item is not None
}
if not issubclass(tool_cls, WorkSpaceTool):
- raise Exception(f"Expected WorkSpaceTool subclass, not {type(tool_cls)!r}")
+ raise Exception("Expected WorkSpaceTool subclass, not %r" % type(tool_cls))
if tool_cls.bl_idname in tools_id:
- raise Exception(f"Tool {tool_cls.bl_idname!r} already exists!")
+ raise Exception("Tool %r already exists!" % tool_cls.bl_idname)
del tools_id, WorkSpaceTool
# Convert the class into a ToolDef.
@@ -900,7 +900,7 @@ def unregister_tool(tool_cls):
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
if cls is None:
- raise Exception(f"Space type {space_type!r} has no toolbar")
+ raise Exception("Space type %r has no toolbar" % space_type)
tools = cls._tools[context_mode]
tool_def = tool_cls._bl_tool
@@ -952,7 +952,7 @@ def unregister_tool(tool_cls):
break
if not changed:
- raise Exception(f"Unable to remove {tool_cls!r}")
+ raise Exception("Unable to remove %r" % tool_cls)
del tool_cls._bl_tool
keymap_data = tool_def.keymap
@@ -963,7 +963,7 @@ def unregister_tool(tool_cls):
for kc in (keyconfigs.default, keyconfigs.addon):
km = kc.keymaps.get(keymap_data[0])
if km is None:
- print(f"Warning keymap {keymap_data[0]!r} not found in {kc.name!r}!")
+ print("Warning keymap %r not found in %r!" % (keymap_data[0], kc.name))
else:
kc.keymaps.remove(km)
diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py
index bfdf28e0db4..511df853d66 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()
@@ -86,7 +85,7 @@ class ImagePreviewCollection(dict):
def new(self, name):
if name in self:
- raise KeyError(f"key {name!r} already exists")
+ raise KeyError("key %r already exists" % name)
p = self[name] = _utils_previews.new(
self._gen_key(name))
return p
@@ -94,7 +93,7 @@ class ImagePreviewCollection(dict):
def load(self, name, path, path_type, force_reload=False):
if name in self:
- raise KeyError(f"key {name!r} already exists")
+ raise KeyError("key %r already exists" % name)
p = self[name] = _utils_previews.load(
self._gen_key(name), path, path_type, force_reload)
return p
@@ -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_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index b76f57c4545..673b33a1e93 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -43,6 +43,7 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"),
("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"),
("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"),
+ ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"),
("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"),
("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"),
("bpy.types.fluiddomainsettings.use_collision_border_bottom*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-bottom"),
@@ -68,7 +69,6 @@ url_manual_mapping = (
("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"),
("bpy.ops.view3d.edit_mesh_extrude_move_shrink_fatten*", "modeling/meshes/editing/face/extrude_faces_normal.html#bpy-ops-view3d-edit-mesh-extrude-move-shrink-fatten"),
("bpy.types.cyclesrendersettings.distance_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-distance-cull-margin"),
- ("bpy.types.fluiddomainsettings.cache_particle_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-particle-format"),
("bpy.types.fluiddomainsettings.display_interpolation*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-display-interpolation"),
("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"),
("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"),
@@ -91,15 +91,18 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.particle_band_width*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-band-width"),
("bpy.types.fluiddomainsettings.particle_randomness*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-randomness"),
("bpy.types.fluiddomainsettings.use_adaptive_domain*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-use-adaptive-domain"),
+ ("bpy.types.fluiddomainsettings.use_resumable_cache*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-use-resumable-cache"),
("bpy.types.fluiddomainsettings.use_spray_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-spray-particles"),
("bpy.types.fluiddomainsettings.vector_display_type*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-display-type"),
("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"),
("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"),
("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"),
("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"),
+ ("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"),
("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"),
("bpy.types.fluiddomainsettings.mesh_concave_lower*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-concave-lower"),
("bpy.types.fluiddomainsettings.mesh_concave_upper*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-concave-upper"),
+ ("bpy.types.fluiddomainsettings.openvdb_data_depth*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-data-depth"),
("bpy.types.fluiddomainsettings.use_dissolve_smoke*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke"),
("bpy.types.fluiddomainsettings.use_flip_particles*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-flip-particles"),
("bpy.types.fluiddomainsettings.use_foam_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-foam-particles"),
@@ -132,6 +135,7 @@ url_manual_mapping = (
("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/parameter_editor/line_style/modifiers/color/distance_from_object.html#bpy-types-linestyle-modifier-distancefromobject"),
("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"),
("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"),
+ ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"),
("bpy.types.rendersettings_simplify_gpencil_blend*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-blend"),
("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"),
("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"),
@@ -140,6 +144,7 @@ url_manual_mapping = (
("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"),
("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"),
("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"),
+ ("bpy.ops.sequencer.deinterlace_selected_movies*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-deinterlace-selected-movies"),
("bpy.types.brush.surface_smooth_current_vertex*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-current-vertex"),
("bpy.types.brush.use_multiplane_scrape_dynamic*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-use-multiplane-scrape-dynamic"),
("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"),
@@ -216,8 +221,11 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.volume_density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-volume-density"),
("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-show-stroke"),
("bpy.types.posebone.use_ik_rotation_control*", "animation/armatures/posing/bone_constraints/inverse_kinematics/introduction.html#bpy-types-posebone-use-ik-rotation-control"),
+ ("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"),
("bpy.ops.constraint.disable_keep_transform*", "animation/constraints/interface/common.html#bpy-ops-constraint-disable-keep-transform"),
("bpy.ops.object.vertex_group_normalize_all*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize-all"),
+ ("bpy.ops.sculpt.face_set_change_visibility*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-set-change-visibility"),
+ ("bpy.ops.sculpt.face_sets_randomize_colors*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-sets-randomize-colors"),
("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"),
("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-pen-jitter"),
("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"),
@@ -267,7 +275,10 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_placeholder*", "render/output/settings.html#bpy-types-rendersettings-use-placeholder"),
("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"),
("bpy.types.spacedopesheeteditor.auto_snap*", "editors/dope_sheet/editing.html#bpy-types-spacedopesheeteditor-auto-snap"),
+ ("bpy.types.spacetexteditor.use_match_case*", "editors/text_editor.html#bpy-types-spacetexteditor-use-match-case"),
+ ("bpy.types.spaceview3d.show_object_select*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-select"),
("bpy.types.volumedisplay.wireframe_detail*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-detail"),
+ ("bpy.ops.object.assign_property_defaults*", "animation/armatures/posing/editing/apply.html#bpy-ops-object-assign-property-defaults"),
("bpy.ops.object.vertex_group_limit_total*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-limit-total"),
("bpy.ops.object.vertex_group_remove_from*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-remove-from"),
("bpy.types.animdata.action_extrapolation*", "editors/nla/properties_modifiers.html#bpy-types-animdata-action-extrapolation"),
@@ -276,7 +287,6 @@ url_manual_mapping = (
("bpy.types.clothsettings.pressure_factor*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-pressure-factor"),
("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"),
("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"),
- ("bpy.types.ffmpegsettings.audio_channels*", "scene_layout/scene/properties.html#bpy-types-ffmpegsettings-audio-channels"),
("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"),
("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"),
("bpy.types.fluiddomainsettings.vorticity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-vorticity"),
@@ -288,15 +298,17 @@ url_manual_mapping = (
("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"),
("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"),
("bpy.types.sceneeevee.taa_render_samples*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-taa-render-samples"),
+ ("bpy.types.spacetexteditor.margin_column*", "editors/text_editor.html#bpy-types-spacetexteditor-margin-column"),
+ ("bpy.types.spacetexteditor.use_find_wrap*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-wrap"),
("bpy.types.spaceuveditor.pixel_snap_mode*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-pixel-snap-mode"),
("bpy.types.spaceuveditor.use_live_unwrap*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-use-live-unwrap"),
+ ("bpy.types.toolsettings.mesh_select_mode*", "modeling/meshes/selecting/introduction.html#bpy-types-toolsettings-mesh-select-mode"),
("bpy.types.vertexweightproximitymodifier*", "modeling/modifiers/modify/weight_proximity.html#bpy-types-vertexweightproximitymodifier"),
("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"),
("bpy.types.brush.pose_smooth_iterations*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-smooth-iterations"),
("bpy.types.brush.use_grab_active_vertex*", "sculpt_paint/sculpting/tools/grab.html#bpy-types-brush-use-grab-active-vertex"),
("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"),
("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"),
- ("bpy.types.ffmpegsettings.audio_mixrate*", "scene_layout/scene/properties.html#bpy-types-ffmpegsettings-audio-mixrate"),
("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"),
("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"),
("bpy.types.material.preview_render_type*", "render/materials/preview.html#bpy-types-material-preview-render-type"),
@@ -309,12 +321,16 @@ url_manual_mapping = (
("bpy.types.shadernodevectordisplacement*", "render/shader_nodes/vector/vector_displacement.html#bpy-types-shadernodevectordisplacement"),
("bpy.types.spacegrapheditor.show_cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-cursor"),
("bpy.types.spaceimageeditor.show_repeat*", "editors/image/view_tab.html#bpy-types-spaceimageeditor-show-repeat"),
+ ("bpy.types.spacetexteditor.replace_text*", "editors/text_editor.html#bpy-types-spacetexteditor-replace-text"),
+ ("bpy.types.spacetexteditor.use_find_all*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-all"),
("bpy.types.volumedisplay.wireframe_type*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-type"),
("bpy.ops.curve.normals_make_consistent*", "modeling/curves/editing/control_points.html#bpy-ops-curve-normals-make-consistent"),
("bpy.ops.gpencil.stroke_simplify_fixed*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify-fixed"),
+ ("bpy.ops.mesh.faces_select_linked_flat*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-faces-select-linked-flat"),
("bpy.ops.object.gpencil_modifier_apply*", "grease_pencil/modifiers/introduction.html#bpy-ops-object-gpencil-modifier-apply"),
("bpy.ops.object.vertex_group_normalize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize"),
("bpy.ops.object.visual_transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-visual-transform-apply"),
+ ("bpy.ops.sequencer.change_effect_input*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-input"),
("bpy.types.brush.texture_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-texture-overlay-alpha"),
("bpy.types.brushgpencilsettings.random*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-random"),
("bpy.types.clothsettings.target_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-target-volume"),
@@ -327,16 +343,20 @@ url_manual_mapping = (
("bpy.types.linestyle*modifier_material*", "render/freestyle/parameter_editor/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"),
("bpy.types.particlesettingstextureslot*", "physics/particles/texture_influence.html#bpy-types-particlesettingstextureslot"),
("bpy.types.posebone.ik_rotation_weight*", "animation/armatures/posing/bone_constraints/inverse_kinematics/introduction.html#bpy-types-posebone-ik-rotation-weight"),
+ ("bpy.types.regionview3d.show_sync_view*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-show-sync-view"),
("bpy.types.sceneeevee.volumetric_light*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-light"),
("bpy.types.sculpt.symmetrize_direction*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-types-sculpt-symmetrize-direction"),
("bpy.types.sequenceeditor.show_overlay*", "video_editing/preview/properties.html#bpy-types-sequenceeditor-show-overlay"),
+ ("bpy.types.spacetexteditor.show_margin*", "editors/text_editor.html#bpy-types-spacetexteditor-show-margin"),
("bpy.types.spline.radius_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-radius-interpolation"),
("bpy.types.viewlayer.material_override*", "render/layers/layers.html#bpy-types-viewlayer-material-override"),
("bpy.ops.gpencil.interpolate_sequence*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-interpolate-sequence"),
("bpy.ops.mesh.normals_make_consistent*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-make-consistent"),
+ ("bpy.ops.mesh.offset_edge_loops_slide*", "modeling/meshes/editing/edge/offset_edge_slide.html#bpy-ops-mesh-offset-edge-loops-slide"),
("bpy.ops.object.duplicate_move_linked*", "scene_layout/object/editing/duplicate_linked.html#bpy-ops-object-duplicate-move-linked"),
("bpy.ops.object.vertex_group_quantize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-quantize"),
- ("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/views.html#bpy-ops-view3d-localview-remove-from"),
+ ("bpy.ops.sequencer.change_effect_type*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-type"),
+ ("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview-remove-from"),
("bpy.types.animdata.action_blend_type*", "editors/nla/properties_modifiers.html#bpy-types-animdata-action-blend-type"),
("bpy.types.brush.cursor_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-overlay-alpha"),
("bpy.types.brush.normal_radius_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-normal-radius-factor"),
@@ -354,6 +374,9 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.color*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-color"),
("bpy.types.movietrackingstabilization*", "movie_clip/tracking/clip/properties/stabilization/index.html#bpy-types-movietrackingstabilization"),
("bpy.types.object.display_bounds_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-bounds-type"),
+ ("bpy.types.regionview3d.lock_rotation*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-lock-rotation"),
+ ("bpy.types.scene.audio_distance_model*", "scene_layout/scene/properties.html#bpy-types-scene-audio-distance-model"),
+ ("bpy.types.scene.audio_doppler_factor*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-factor"),
("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"),
("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"),
("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"),
@@ -368,11 +391,13 @@ url_manual_mapping = (
("bpy.ops.mesh.vert_connect_nonplanar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-vert-connect-nonplanar"),
("bpy.ops.object.duplicates_make_real*", "scene_layout/object/editing/apply.html#bpy-ops-object-duplicates-make-real"),
("bpy.ops.object.transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-transforms-to-deltas"),
+ ("bpy.ops.pose.visual_transform_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-visual-transform-apply"),
("bpy.ops.sequencer.view_ghost_border*", "video_editing/preview/properties.html#bpy-ops-sequencer-view-ghost-border"),
("bpy.types.animdata.action_influence*", "editors/nla/properties_modifiers.html#bpy-types-animdata-action-influence"),
("bpy.types.brush.crease_pinch_factor*", "sculpt_paint/sculpting/tools/snake_hook.html#bpy-types-brush-crease-pinch-factor"),
("bpy.types.brush.elastic_deform_type*", "sculpt_paint/sculpting/tools/elastic_deform.html#bpy-types-brush-elastic-deform-type"),
("bpy.types.brush.use_primary_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-primary-overlay"),
+ ("bpy.types.camera.passepartout_alpha*", "render/cameras.html#bpy-types-camera-passepartout-alpha"),
("bpy.types.compositornodechromamatte*", "compositing/types/matte/chroma_key.html#bpy-types-compositornodechromamatte"),
("bpy.types.compositornodedilateerode*", "compositing/types/filter/dilate_erode.html#bpy-types-compositornodedilateerode"),
("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"),
@@ -385,18 +410,25 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.mode*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-mode"),
("bpy.types.object.empty_display_size*", "modeling/empties.html#bpy-types-object-empty-display-size"),
("bpy.types.object.empty_display_type*", "modeling/empties.html#bpy-types-object-empty-display-type"),
+ ("bpy.types.regionview3d.use_box_clip*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-use-box-clip"),
("bpy.types.rendersettings.use_border*", "render/output/settings.html#bpy-types-rendersettings-use-border"),
+ ("bpy.types.scene.audio_doppler_speed*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-speed"),
("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"),
("bpy.types.shadernodebsdfanisotropic*", "render/shader_nodes/shader/anisotropic.html#bpy-types-shadernodebsdfanisotropic"),
("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"),
("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"),
("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"),
+ ("bpy.types.spacetexteditor.find_text*", "editors/text_editor.html#bpy-types-spacetexteditor-find-text"),
+ ("bpy.types.spacetexteditor.font_size*", "editors/text_editor.html#bpy-types-spacetexteditor-font-size"),
+ ("bpy.types.spacetexteditor.tab_width*", "editors/text_editor.html#bpy-types-spacetexteditor-tab-width"),
("bpy.types.spaceuveditor.lock_bounds*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-lock-bounds"),
("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"),
("bpy.ops.mesh.customdata_mask_clear*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-mesh-customdata-mask-clear"),
("bpy.ops.mesh.extrude_vertices_move*", "modeling/meshes/editing/vertex/extrude_vertices.html#bpy-ops-mesh-extrude-vertices-move"),
("bpy.ops.mesh.mod_weighted_strength*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-mod-weighted-strength"),
("bpy.ops.mesh.quads_convert_to_tris*", "modeling/meshes/editing/face/triangulate_faces.html#bpy-ops-mesh-quads-convert-to-tris"),
+ ("bpy.ops.mesh.select_interior_faces*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-interior-faces"),
+ ("bpy.ops.mesh.select_similar_region*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar-region"),
("bpy.ops.mesh.tris_convert_to_quads*", "modeling/meshes/editing/face/triangles_quads.html#bpy-ops-mesh-tris-convert-to-quads"),
("bpy.ops.node.read_fullsamplelayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-fullsamplelayers"),
("bpy.ops.object.datalayout_transfer*", "scene_layout/object/editing/relations.html#bpy-ops-object-datalayout-transfer"),
@@ -406,6 +438,7 @@ url_manual_mapping = (
("bpy.ops.object.vertex_group_mirror*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-mirror"),
("bpy.ops.object.vertex_group_remove*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-remove"),
("bpy.ops.object.vertex_group_smooth*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-smooth"),
+ ("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"),
("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"),
("bpy.ops.sequencer.crossfade_sounds*", "video_editing/sequencer/strips/transitions/cross.html#bpy-ops-sequencer-crossfade-sounds"),
("bpy.ops.sequencer.export_subtitles*", "video_editing/preview/introduction.html#bpy-ops-sequencer-export-subtitles"),
@@ -414,6 +447,7 @@ url_manual_mapping = (
("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"),
("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"),
("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"),
+ ("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"),
("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"),
("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"),
("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill.html#bpy-types-compositornodecolorspill"),
@@ -430,7 +464,7 @@ url_manual_mapping = (
("bpy.types.linestyle*modifier_noise*", "render/freestyle/parameter_editor/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"),
("bpy.types.maintainvolumeconstraint*", "animation/constraints/transform/maintain_volume.html#bpy-types-maintainvolumeconstraint"),
("bpy.types.mesh.use_mirror_topology*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-topology"),
- ("bpy.types.particleinstancemodifier*", "modeling/modifiers/simulate/particle_instance.html#bpy-types-particleinstancemodifier"),
+ ("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"),
("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"),
("bpy.types.shadernodebsdfprincipled*", "render/shader_nodes/shader/principled.html#bpy-types-shadernodebsdfprincipled"),
("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"),
@@ -444,10 +478,14 @@ url_manual_mapping = (
("bpy.ops.curve.match_texture_space*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-ops-curve-match-texture-space"),
("bpy.ops.font.text_paste_from_file*", "modeling/texts/editing.html#bpy-ops-font-text-paste-from-file"),
("bpy.ops.gpencil.frame_clean_loose*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-loose"),
+ ("bpy.ops.mesh.select_face_by_sides*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-face-by-sides"),
+ ("bpy.ops.mesh.shortest_path_select*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-shortest-path-select"),
("bpy.ops.mesh.vert_connect_concave*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-vert-connect-concave"),
("bpy.ops.object.vertex_group_clean*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-clean"),
("bpy.ops.render.play-rendered-anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"),
("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-set-pivot-position"),
+ ("bpy.ops.sequencer.reassign_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reassign-inputs"),
+ ("bpy.ops.view3d.view_center_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-view-center-camera"),
("bpy.types.armaturegpencilmodifier*", "grease_pencil/modifiers/deform/armature.html#bpy-types-armaturegpencilmodifier"),
("bpy.types.camera.show_composition*", "render/cameras.html#bpy-types-camera-show-composition"),
("bpy.types.compositornodealphaover*", "compositing/types/color/alpha_over.html#bpy-types-compositornodealphaover"),
@@ -462,6 +500,7 @@ url_manual_mapping = (
("bpy.types.compositornodestabilize*", "compositing/types/distort/stabilize_2d.html#bpy-types-compositornodestabilize"),
("bpy.types.compositornodetransform*", "compositing/types/distort/transform.html#bpy-types-compositornodetransform"),
("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"),
+ ("bpy.types.constraint.target_space*", "animation/constraints/interface/common.html#bpy-types-constraint-target-space"),
("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"),
("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"),
("bpy.types.gpencillayer.mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-mask-layer"),
@@ -481,7 +520,7 @@ url_manual_mapping = (
("bpy.types.simplifygpencilmodifier*", "grease_pencil/modifiers/generate/simplify.html#bpy-types-simplifygpencilmodifier"),
("bpy.types.spacegrapheditor.cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-cursor"),
("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"),
- ("bpy.types.viewlayer.use_freestyle*", "render/layers/layers.html#bpy-types-viewlayer-use-freestyle"),
+ ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"),
("bpy.ops.gpencil.frame_clean_fill*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-fill"),
("bpy.ops.gpencil.stroke_subdivide*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-subdivide"),
("bpy.ops.graph.interpolation_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-interpolation-type"),
@@ -489,6 +528,7 @@ url_manual_mapping = (
("bpy.ops.mesh.face_split_by_edges*", "modeling/meshes/editing/face/weld_edges_faces.html#bpy-ops-mesh-face-split-by-edges"),
("bpy.ops.mesh.intersect_boolean()*", "modeling/meshes/editing/face/intersect_boolean.html#bpy-ops-mesh-intersect-boolean"),
("bpy.ops.mesh.mark_freestyle_face*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-mark-freestyle-face"),
+ ("bpy.ops.mesh.select_non_manifold*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-non-manifold"),
("bpy.ops.object.constraints_clear*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-clear"),
("bpy.ops.object.quadriflow_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-quadriflow-remesh"),
("bpy.ops.object.vertex_group_copy*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy"),
@@ -498,6 +538,7 @@ url_manual_mapping = (
("bpy.ops.object.vertex_parent_set*", "modeling/meshes/editing/vertex/make_vertex_parent.html#bpy-ops-object-vertex-parent-set"),
("bpy.ops.paint.mask_lasso_gesture*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask-lasso-gesture"),
("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"),
+ ("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"),
("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"),
("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/properties/modifiers.html#bpy-types-brightcontrastmodifier"),
("bpy.types.brush.cursor_color_add*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-color-add"),
@@ -516,6 +557,7 @@ url_manual_mapping = (
("bpy.types.compositornodesunbeams*", "compositing/types/filter/sun_beams.html#bpy-types-compositornodesunbeams"),
("bpy.types.compositornodetrackpos*", "compositing/types/input/track_position.html#bpy-types-compositornodetrackpos"),
("bpy.types.compositornodezcombine*", "compositing/types/color/z_combine.html#bpy-types-compositornodezcombine"),
+ ("bpy.types.constraint.owner_space*", "animation/constraints/interface/common.html#bpy-types-constraint-owner-space"),
("bpy.types.copylocationconstraint*", "animation/constraints/transform/copy_location.html#bpy-types-copylocationconstraint"),
("bpy.types.copyrotationconstraint*", "animation/constraints/transform/copy_rotation.html#bpy-types-copyrotationconstraint"),
("bpy.types.cyclesmaterialsettings*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings"),
@@ -533,8 +575,10 @@ url_manual_mapping = (
("bpy.types.shadernodelightfalloff*", "render/shader_nodes/color/light_falloff.html#bpy-types-shadernodelightfalloff"),
("bpy.types.shadernodeparticleinfo*", "render/shader_nodes/input/particle_info.html#bpy-types-shadernodeparticleinfo"),
("bpy.types.shadernodevectorrotate*", "render/shader_nodes/vector/vector_rotate.html#bpy-types-shadernodevectorrotate"),
+ ("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"),
("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"),
("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"),
+ ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"),
("bpy.ops.curve.spline_weight_set*", "modeling/curves/editing/other.html#bpy-ops-curve-spline-weight-set"),
("bpy.ops.gpencil.blank_frame_add*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-blank-frame-add"),
("bpy.ops.gpencil.frame_duplicate*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-frame-duplicate"),
@@ -543,6 +587,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.stroke_simplify*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify"),
("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"),
("bpy.ops.mesh.extrude_faces_move*", "modeling/meshes/editing/face/extrude_individual_faces.html#bpy-ops-mesh-extrude-faces-move"),
+ ("bpy.ops.mesh.faces_shade_smooth*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-smooth"),
("bpy.ops.mesh.subdivide_edgering*", "modeling/meshes/editing/edge/subdivide_edge_ring.html#bpy-ops-mesh-subdivide-edgering"),
("bpy.ops.object.constraints_copy*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-copy"),
("bpy.ops.object.gpencil_modifier*", "grease_pencil/modifiers/index.html#bpy-ops-object-gpencil-modifier"),
@@ -554,6 +599,7 @@ url_manual_mapping = (
("bpy.ops.paint.weight_from_bones*", "sculpt_paint/weight_paint/editing.html#bpy-ops-paint-weight-from-bones"),
("bpy.ops.scene.view_layer_remove*", "render/layers/layers.html#bpy-ops-scene-view-layer-remove"),
("bpy.ops.screen.screen_full_area*", "interface/window_system/areas.html#bpy-ops-screen-screen-full-area"),
+ ("bpy.ops.sculpt.face_sets_create*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-sets-create"),
("bpy.ops.transform.rotate_normal*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-transform-rotate-normal"),
("bpy.ops.transform.shrink_fatten*", "modeling/meshes/editing/mesh/transform/shrink-fatten.html#bpy-ops-transform-shrink-fatten"),
("bpy.ops.transform.vertex_random*", "modeling/meshes/editing/mesh/transform/randomize.html#bpy-ops-transform-vertex-random"),
@@ -585,6 +631,7 @@ url_manual_mapping = (
("bpy.types.particlefluidsettings*", "physics/particles/emitter/physics/fluid.html#bpy-types-particlefluidsettings"),
("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"),
("bpy.types.sceneeevee.volumetric*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric"),
+ ("bpy.types.sculpt.gravity_object*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity-object"),
("bpy.types.shadernodebsdfdiffuse*", "render/shader_nodes/shader/diffuse.html#bpy-types-shadernodebsdfdiffuse"),
("bpy.types.shadernodelayerweight*", "render/shader_nodes/input/layer_weight.html#bpy-types-shadernodelayerweight"),
("bpy.types.shadernodeoutputlight*", "render/shader_nodes/output/light.html#bpy-types-shadernodeoutputlight"),
@@ -596,7 +643,6 @@ url_manual_mapping = (
("bpy.types.smoothgpencilmodifier*", "grease_pencil/modifiers/deform/smooth.html#bpy-types-smoothgpencilmodifier"),
("bpy.types.spline.use_endpoint_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-endpoint-u"),
("bpy.types.surfacedeformmodifier*", "modeling/modifiers/deform/surface_deform.html#bpy-types-surfacedeformmodifier"),
- ("bpy.types.viewlayer.use_volumes*", "render/layers/layers.html#bpy-types-viewlayer-use-volumes"),
("bpy.types.volume.frame_duration*", "modeling/volumes/properties.html#bpy-types-volume-frame-duration"),
("bpy.types.volumedisplay.density*", "modeling/volumes/properties.html#bpy-types-volumedisplay-density"),
("bpy.types.volumerender.clipping*", "modeling/volumes/properties.html#bpy-types-volumerender-clipping"),
@@ -604,6 +650,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"),
("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"),
("bpy.ops.mesh.bridge-edge-loops*", "modeling/meshes/editing/edge/bridge_edge_loops.html#bpy-ops-mesh-bridge-edge-loops"),
+ ("bpy.ops.mesh.loop_multi_select*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-loop-multi-select"),
("bpy.ops.mesh.vert_connect_path*", "modeling/meshes/editing/vertex/connect_vertex_path.html#bpy-ops-mesh-vert-connect-path"),
("bpy.ops.nla.action_sync_length*", "editors/nla/editing.html#bpy-ops-nla-action-sync-length"),
("bpy.ops.object.paths_calculate*", "animation/motion_paths.html#bpy-ops-object-paths-calculate"),
@@ -611,6 +658,7 @@ url_manual_mapping = (
("bpy.ops.outliner.lib_operation*", "files/linked_libraries/introduction.html#bpy-ops-outliner-lib-operation"),
("bpy.ops.outliner.orphans_purge*", "editors/outliner.html#bpy-ops-outliner-orphans-purge"),
("bpy.ops.screen.region_quadview*", "editors/3dview/navigate/views.html#bpy-ops-screen-region-quadview"),
+ ("bpy.ops.sequencer.offset_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-offset-clear"),
("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"),
("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"),
("bpy.types.brush.use_persistent*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-use-persistent"),
@@ -669,19 +717,27 @@ url_manual_mapping = (
("bpy.ops.mesh.blend_from_shape*", "modeling/meshes/editing/vertex/blend_shape.html#bpy-ops-mesh-blend-from-shape"),
("bpy.ops.mesh.dissolve_limited*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-limited"),
("bpy.ops.mesh.face_make_planar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-face-make-planar"),
+ ("bpy.ops.mesh.faces_shade_flat*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-flat"),
("bpy.ops.mesh.paint_mask_slice*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-mesh-paint-mask-slice"),
+ ("bpy.ops.mesh.select_ungrouped*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-ungrouped"),
("bpy.ops.object.duplicate_move*", "scene_layout/object/editing/duplicate.html#bpy-ops-object-duplicate-move"),
("bpy.ops.object.hook_add_selob*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook-add-selob"),
("bpy.ops.object.select_by_type*", "scene_layout/object/selecting.html#bpy-ops-object-select-by-type"),
("bpy.ops.object.select_grouped*", "scene_layout/object/selecting.html#bpy-ops-object-select-grouped"),
("bpy.ops.object.select_pattern*", "scene_layout/object/selecting.html#bpy-ops-object-select-pattern"),
("bpy.ops.paint.mask_flood_fill*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask-flood-fill"),
+ ("bpy.ops.pose.quaternions_flip*", "animation/armatures/posing/editing/flip_quats.html#bpy-ops-pose-quaternions-flip"),
+ ("bpy.ops.pose.transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-transforms-clear"),
("bpy.ops.screen.repeat_history*", "interface/undo_redo.html#bpy-ops-screen-repeat-history"),
+ ("bpy.ops.sculpt.face_sets_init*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-sets-init"),
+ ("bpy.ops.sequencer.change_path*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-path"),
("bpy.ops.sequencer.refresh_all*", "video_editing/sequencer/navigating.html#bpy-ops-sequencer-refresh-all"),
+ ("bpy.ops.sequencer.swap_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-inputs"),
("bpy.ops.surface.primitive*add*", "modeling/surfaces/primitives.html#bpy-ops-surface-primitive-add"),
+ ("bpy.ops.text.resolve_conflict*", "editors/text_editor.html#bpy-ops-text-resolve-conflict"),
("bpy.ops.transform.edge_crease*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-crease"),
("bpy.ops.transform.skin_resize*", "modeling/meshes/editing/mesh/transform/skin_resize.html#bpy-ops-transform-skin-resize"),
- ("bpy.ops.uv.seams_from_islands*", "modeling/meshes/uv/unwrapping/seams.html#bpy-ops-uv-seams-from-islands"),
+ ("bpy.ops.uv.seams_from_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-seams-from-islands"),
("bpy.types.brush.icon_filepath*", "sculpt_paint/brush/brush.html#bpy-types-brush-icon-filepath"),
("bpy.types.brush.smooth_stroke*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brush-smooth-stroke"),
("bpy.types.brush.tip_roundness*", "sculpt_paint/sculpting/tools/clay_strips.html#bpy-types-brush-tip-roundness"),
@@ -731,6 +787,7 @@ url_manual_mapping = (
("bpy.types.windowmanager.addon*", "editors/preferences/addons.html#bpy-types-windowmanager-addon"),
("bpy.ops.anim.keyframe_delete*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-delete"),
("bpy.ops.anim.keyframe_insert*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-insert"),
+ ("bpy.ops.clip.detect_features*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-detect-features"),
("bpy.ops.console.autocomplete*", "editors/python_console.html#bpy-ops-console-autocomplete"),
("bpy.ops.curve.dissolve_verts*", "modeling/curves/editing/curve.html#bpy-ops-curve-dissolve-verts"),
("bpy.ops.curve.duplicate_move*", "modeling/curves/editing/curve.html#bpy-ops-curve-duplicate-move"),
@@ -752,6 +809,10 @@ url_manual_mapping = (
("bpy.ops.object.select_random*", "scene_layout/object/selecting.html#bpy-ops-object-select-random"),
("bpy.ops.paint.add_simple_uvs*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-ops-paint-add-simple-uvs"),
("bpy.ops.scene.view_layer_add*", "render/layers/layers.html#bpy-ops-scene-view-layer-add"),
+ ("bpy.ops.sculpt.face_set_edit*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-set-edit"),
+ ("bpy.ops.sequencer.gap_insert*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-insert"),
+ ("bpy.ops.sequencer.gap_remove*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-remove"),
+ ("bpy.ops.sequencer.rendersize*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-rendersize"),
("bpy.ops.sound.bake_animation*", "scene_layout/scene/properties.html#bpy-ops-sound-bake-animation"),
("bpy.ops.transform.edge_slide*", "modeling/meshes/editing/edge/edge_slide.html#bpy-ops-transform-edge-slide"),
("bpy.ops.transform.vert_slide*", "modeling/meshes/editing/vertex/slide_vertices.html#bpy-ops-transform-vert-slide"),
@@ -769,7 +830,7 @@ url_manual_mapping = (
("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"),
("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"),
("bpy.types.curve.resolution_v*", "modeling/surfaces/properties/shape.html#bpy-types-curve-resolution-v"),
- ("bpy.types.curvepaintsettings*", "modeling/curves/editing/other.html#bpy-types-curvepaintsettings"),
+ ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"),
("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"),
("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"),
("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-gammacrosssequence"),
@@ -797,6 +858,7 @@ url_manual_mapping = (
("bpy.types.shadernodetexmagic*", "render/shader_nodes/textures/magic.html#bpy-types-shadernodetexmagic"),
("bpy.types.shadernodetexnoise*", "render/shader_nodes/textures/noise.html#bpy-types-shadernodetexnoise"),
("bpy.types.shrinkwrapmodifier*", "modeling/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapmodifier"),
+ ("bpy.types.simulationmodifier*", "modeling/modifiers/physics/simulation.html#bpy-types-simulationmodifier"),
("bpy.types.splineikconstraint*", "animation/constraints/tracking/spline_ik.html#bpy-types-splineikconstraint"),
("bpy.types.texturenodetexture*", "editors/texture_node/types/input/texture.html#bpy-types-texturenodetexture"),
("bpy.types.view3dshading.type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-type"),
@@ -804,6 +866,7 @@ url_manual_mapping = (
("bpy.types.volume.is_sequence*", "modeling/volumes/properties.html#bpy-types-volume-is-sequence"),
("bpy.types.volumerender.space*", "modeling/volumes/properties.html#bpy-types-volumerender-space"),
("bpy.ops.anim.keyframe_clear*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-clear"),
+ ("bpy.ops.armature.flip_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-flip-names"),
("bpy.ops.curve.cyclic_toggle*", "modeling/curves/editing/curve.html#bpy-ops-curve-cyclic-toggle"),
("bpy.ops.curve.primitive*add*", "modeling/curves/primitives.html#bpy-ops-curve-primitive-add"),
("bpy.ops.curve.smooth_radius*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-radius"),
@@ -818,14 +881,21 @@ url_manual_mapping = (
("bpy.ops.mesh.dissolve_faces*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-faces"),
("bpy.ops.mesh.dissolve_verts*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-verts"),
("bpy.ops.mesh.duplicate_move*", "modeling/meshes/editing/mesh/duplicate.html#bpy-ops-mesh-duplicate-move"),
+ ("bpy.ops.mesh.extrude_region*", "modeling/meshes/tools/extrude_region.html#bpy-ops-mesh-extrude-region"),
("bpy.ops.mesh.extrude_repeat*", "modeling/meshes/editing/mesh/extrude.html#bpy-ops-mesh-extrude-repeat"),
+ ("bpy.ops.mesh.loop_to_region*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-loop-to-region"),
+ ("bpy.ops.mesh.region_to_loop*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-region-to-loop"),
("bpy.ops.mesh.remove_doubles*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-remove-doubles"),
+ ("bpy.ops.mesh.select_similar*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar"),
("bpy.ops.mesh.smooth_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-smooth-normals"),
("bpy.ops.nla.tweakmode_enter*", "editors/nla/editing.html#bpy-ops-nla-tweakmode-enter"),
("bpy.ops.object.parent_clear*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-clear"),
("bpy.ops.object.shade_smooth*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-smooth"),
("bpy.ops.object.voxel_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-voxel-remesh"),
+ ("bpy.ops.pose.armature_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-armature-apply"),
+ ("bpy.ops.sequencer.swap_data*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-data"),
("bpy.ops.transform.push_pull*", "modeling/meshes/editing/mesh/transform/push_pull.html#bpy-ops-transform-push-pull"),
+ ("bpy.ops.transform.seq_slide*", "video_editing/sequencer/editing.html#bpy-ops-transform-seq-slide"),
("bpy.ops.transform.trackball*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-trackball"),
("bpy.ops.transform.transform*", "scene_layout/object/editing/transform/align_transform_orientation.html#bpy-ops-transform-transform"),
("bpy.ops.transform.translate*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-translate"),
@@ -888,12 +958,14 @@ url_manual_mapping = (
("bpy.ops.mesh.edge_collapse*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-edge-collapse"),
("bpy.ops.mesh.edge_face_add*", "modeling/meshes/editing/vertex/make_face_edge.html#bpy-ops-mesh-edge-face-add"),
("bpy.ops.mesh.knife_project*", "modeling/meshes/editing/mesh/knife_project.html#bpy-ops-mesh-knife-project"),
- ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/tools/loop.html#bpy-ops-mesh-loopcut-slide"),
+ ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/editing/edge/loopcut_slide.html#bpy-ops-mesh-loopcut-slide"),
("bpy.ops.mesh.merge_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-merge-normals"),
("bpy.ops.mesh.normals_tools*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-tools"),
("bpy.ops.mesh.point_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-point-normals"),
("bpy.ops.mesh.primitive*add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-add"),
("bpy.ops.mesh.rip_edge_move*", "modeling/meshes/editing/vertex/rip_vertices_extend.html#bpy-ops-mesh-rip-edge-move"),
+ ("bpy.ops.mesh.select_linked*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-select-linked"),
+ ("bpy.ops.mesh.select_random*", "modeling/meshes/selecting/random.html#bpy-ops-mesh-select-random"),
("bpy.ops.mesh.sort_elements*", "modeling/meshes/editing/mesh/sort_elements.html#bpy-ops-mesh-sort-elements"),
("bpy.ops.mesh.split_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-split-normals"),
("bpy.ops.mesh.symmetry_snap*", "modeling/meshes/editing/mesh/snap_symmetry.html#bpy-ops-mesh-symmetry-snap"),
@@ -905,6 +977,7 @@ url_manual_mapping = (
("bpy.ops.sculpt.mask_expand*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-sculpt-mask-expand"),
("bpy.ops.sculpt.mask_filter*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-sculpt-mask-filter"),
("bpy.ops.transform.tosphere*", "modeling/meshes/editing/mesh/transform/to_sphere.html#bpy-ops-transform-tosphere"),
+ ("bpy.ops.view3d.clip_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-clip-border"),
("bpy.ops.wm.previews_ensure*", "files/blend/previews.html#bpy-ops-wm-previews-ensure"),
("bpy.types.actionconstraint*", "animation/constraints/relationship/action.html#bpy-types-actionconstraint"),
("bpy.types.addonpreferences*", "editors/preferences/addons.html#bpy-types-addonpreferences"),
@@ -943,7 +1016,9 @@ url_manual_mapping = (
("bpy.types.spacegrapheditor*", "editors/graph_editor/index.html#bpy-types-spacegrapheditor"),
("bpy.types.spacepreferences*", "editors/preferences/index.html#bpy-types-spacepreferences"),
("bpy.types.spaceview3d.lock*", "editors/3dview/properties/sidebar.html#bpy-types-spaceview3d-lock"),
+ ("bpy.types.spaceview3d.show*", "editors/3dview/display/index.html#bpy-types-spaceview3d-show"),
("bpy.types.subtractsequence*", "video_editing/sequencer/strips/effects/subtract.html#bpy-types-subtractsequence"),
+ ("bpy.types.text.indentation*", "editors/text_editor.html#bpy-types-text-indentation"),
("bpy.types.texturenodegroup*", "editors/texture_node/types/groups.html#bpy-types-texturenodegroup"),
("bpy.types.texturenodeimage*", "editors/texture_node/types/input/image.html#bpy-types-texturenodeimage"),
("bpy.types.viewlayer.use_ao*", "render/layers/layers.html#bpy-types-viewlayer-use-ao"),
@@ -956,6 +1031,7 @@ url_manual_mapping = (
("bpy.ops.graph.handle_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-handle-type"),
("bpy.ops.mesh.delete_loose*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-delete-loose"),
("bpy.ops.mesh.flip_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-flip-normals"),
+ ("bpy.ops.mesh.select_loose*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-loose"),
("bpy.ops.mesh.vert_connect*", "modeling/meshes/editing/vertex/connect_vertex_pairs.html#bpy-ops-mesh-vert-connect"),
("bpy.ops.nla.tracks_delete*", "editors/nla/editing.html#bpy-ops-nla-tracks-delete"),
("bpy.ops.object.lightprobe*", "render/eevee/light_probes/index.html#bpy-ops-object-lightprobe"),
@@ -977,7 +1053,7 @@ url_manual_mapping = (
("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"),
("bpy.types.brush.mask_tool*", "sculpt_paint/sculpting/tools/mask.html#bpy-types-brush-mask-tool"),
("bpy.types.constraint.mute*", "animation/constraints/interface/header.html#bpy-types-constraint-mute"),
- ("bpy.types.explodemodifier*", "modeling/modifiers/simulate/explode.html#bpy-types-explodemodifier"),
+ ("bpy.types.explodemodifier*", "modeling/modifiers/physics/explode.html#bpy-types-explodemodifier"),
("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fcurvemodifiers"),
("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"),
("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"),
@@ -1020,13 +1096,20 @@ url_manual_mapping = (
("bpy.ops.mesh.uvs_reverse*", "modeling/meshes/uv/editing.html#bpy-ops-mesh-uvs-reverse"),
("bpy.ops.object.hide_view*", "scene_layout/object/editing/show_hide.html#bpy-ops-object-hide-view"),
("bpy.ops.object.track_set*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-track-set"),
+ ("bpy.ops.pose.scale_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-scale-clear"),
("bpy.ops.scene.view_layer*", "render/layers/layers.html#bpy-ops-scene-view-layer"),
+ ("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"),
+ ("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"),
+ ("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"),
+ ("bpy.ops.sequencer.unmute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unmute"),
("bpy.ops.transform.mirror*", "scene_layout/object/editing/mirror.html#bpy-ops-transform-mirror"),
("bpy.ops.transform.resize*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-resize"),
("bpy.ops.transform.rotate*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-rotate"),
("bpy.ops.uv.lightmap_pack*", "modeling/meshes/editing/uv.html#bpy-ops-uv-lightmap-pack"),
("bpy.ops.uv.smart_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-smart-project"),
- ("bpy.ops.view3d.localview*", "editors/3dview/navigate/views.html#bpy-ops-view3d-localview"),
+ ("bpy.ops.uv.snap_selected*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-selected"),
+ ("bpy.ops.view3d.localview*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview"),
+ ("bpy.ops.view3d.view_axis*", "editors/3dview/navigate/viewpoint.html#bpy-ops-view3d-view-axis"),
("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"),
("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"),
("bpy.types.curvesmodifier*", "video_editing/sequencer/properties/modifiers.html#bpy-types-curvesmodifier"),
@@ -1043,6 +1126,7 @@ url_manual_mapping = (
("bpy.types.rigidbodyworld*", "physics/rigid_body/world.html#bpy-types-rigidbodyworld"),
("bpy.types.sceneeevee.ssr*", "render/eevee/render_settings/screen_space_reflections.html#bpy-types-sceneeevee-ssr"),
("bpy.types.sceneeevee.sss*", "render/eevee/render_settings/subsurface_scattering.html#bpy-types-sceneeevee-sss"),
+ ("bpy.types.sculpt.gravity*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity"),
("bpy.types.shaderfxshadow*", "grease_pencil/visual_effects/shadow.html#bpy-types-shaderfxshadow"),
("bpy.types.shadernodebump*", "render/shader_nodes/vector/bump.html#bpy-types-shadernodebump"),
("bpy.types.shadernodemath*", "render/shader_nodes/converter/math.html#bpy-types-shadernodemath"),
@@ -1068,6 +1152,7 @@ url_manual_mapping = (
("bpy.ops.mesh.edge_split*", "modeling/meshes/editing/mesh/split.html#bpy-ops-mesh-edge-split"),
("bpy.ops.mesh.fill_holes*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-fill-holes"),
("bpy.ops.mesh.mark_sharp*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-mesh-mark-sharp"),
+ ("bpy.ops.mesh.select_nth*", "modeling/meshes/selecting/checker_deselect.html#bpy-ops-mesh-select-nth"),
("bpy.ops.mesh.symmetrize*", "modeling/meshes/editing/mesh/symmetrize.html#bpy-ops-mesh-symmetrize"),
("bpy.ops.mesh.uvs_rotate*", "modeling/meshes/uv/editing.html#bpy-ops-mesh-uvs-rotate"),
("bpy.ops.nla.apply_scale*", "editors/nla/editing.html#bpy-ops-nla-apply-scale"),
@@ -1075,10 +1160,14 @@ url_manual_mapping = (
("bpy.ops.nla.mute_toggle*", "editors/nla/editing.html#bpy-ops-nla-mute-toggle"),
("bpy.ops.object.armature*", "animation/armatures/index.html#bpy-ops-object-armature"),
("bpy.ops.object.face_map*", "modeling/meshes/properties/object_data.html#bpy-ops-object-face-map"),
+ ("bpy.ops.pose.relax_rest*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-relax-rest"),
("bpy.ops.rigidbody.world*", "physics/rigid_body/world.html#bpy-ops-rigidbody-world"),
+ ("bpy.ops.sculpt.optimize*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-optimize"),
+ ("bpy.ops.sequencer.split*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-split"),
("bpy.ops.transform.shear*", "modeling/meshes/editing/mesh/transform/shear.html#bpy-ops-transform-shear"),
("bpy.ops.uv.cube_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-cube-project"),
("bpy.ops.uv.pack_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pack-islands"),
+ ("bpy.ops.uv.select_split*", "modeling/meshes/uv/editing.html#bpy-ops-uv-select-split"),
("bpy.ops.wm.app_template*", "advanced/app_templates.html#bpy-ops-wm-app-template"),
("bpy.ops.wm.batch_rename*", "files/blend/rename.html#bpy-ops-wm-batch-rename"),
("bpy.ops.wm.redraw_timer*", "advanced/operators.html#bpy-ops-wm-redraw-timer"),
@@ -1103,7 +1192,7 @@ url_manual_mapping = (
("bpy.types.nlastrip.mute*", "editors/nla/properties_modifiers.html#bpy-types-nlastrip-mute"),
("bpy.types.nlastrip.name*", "editors/nla/properties_modifiers.html#bpy-types-nlastrip-name"),
("bpy.types.object.parent*", "scene_layout/object/editing/parent.html#bpy-types-object-parent"),
- ("bpy.types.oceanmodifier*", "modeling/modifiers/simulate/ocean.html#bpy-types-oceanmodifier"),
+ ("bpy.types.oceanmodifier*", "modeling/modifiers/physics/ocean.html#bpy-types-oceanmodifier"),
("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"),
("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"),
("bpy.types.sceneeevee.gi*", "render/eevee/render_settings/indirect_lighting.html#bpy-types-sceneeevee-gi"),
@@ -1116,6 +1205,7 @@ url_manual_mapping = (
("bpy.types.spaceoutliner*", "editors/outliner.html#bpy-types-spaceoutliner"),
("bpy.types.spacetimeline*", "editors/timeline.html#bpy-types-spacetimeline"),
("bpy.types.stuccitexture*", "render/materials/legacy_textures/types/stucci.html#bpy-types-stuccitexture"),
+ ("bpy.types.view3doverlay*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay"),
("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"),
("bpy.types.windowmanager*", "interface/index.html#bpy-types-windowmanager"),
("bpy.ops.*.select_lasso*", "interface/selecting.html#bpy-ops-select-lasso"),
@@ -1135,8 +1225,19 @@ url_manual_mapping = (
("bpy.ops.object.convert*", "scene_layout/object/editing/convert.html#bpy-ops-object-convert"),
("bpy.ops.object.gpencil*", "grease_pencil/index.html#bpy-ops-object-gpencil"),
("bpy.ops.object.speaker*", "render/output/audio/speaker.html#bpy-ops-object-speaker"),
+ ("bpy.ops.pose.breakdown*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-breakdown"),
+ ("bpy.ops.pose.loc_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-loc-clear"),
+ ("bpy.ops.pose.propagate*", "animation/armatures/posing/editing/propagate.html#bpy-ops-pose-propagate"),
+ ("bpy.ops.pose.push_rest*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-push-rest"),
+ ("bpy.ops.pose.rot_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-rot-clear"),
+ ("bpy.ops.sequencer.lock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-lock"),
+ ("bpy.ops.sequencer.mute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-mute"),
+ ("bpy.ops.sequencer.slip*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-slip"),
+ ("bpy.ops.sequencer.snap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-snap"),
+ ("bpy.ops.sequencer.swap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap"),
("bpy.ops.transform.bend*", "modeling/meshes/editing/mesh/transform/bend.html#bpy-ops-transform-bend"),
("bpy.ops.transform.tilt*", "modeling/curves/editing/control_points.html#bpy-ops-transform-tilt"),
+ ("bpy.ops.uv.snap_cursor*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-cursor"),
("bpy.ops.wm.search_menu*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search-menu"),
("bpy.types.bakesettings*", "render/cycles/baking.html#bpy-types-bakesettings"),
("bpy.types.blendtexture*", "render/materials/legacy_textures/types/blend.html#bpy-types-blendtexture"),
@@ -1219,6 +1320,7 @@ url_manual_mapping = (
("bpy.ops.object.align*", "scene_layout/object/editing/transform/align_objects.html#bpy-ops-object-align"),
("bpy.ops.object.empty*", "modeling/empties.html#bpy-ops-object-empty"),
("bpy.ops.object.quick*", "physics/introduction.html#bpy-ops-object-quick"),
+ ("bpy.ops.text.replace*", "editors/text_editor.html#bpy-ops-text-replace"),
("bpy.ops.uv.mark_seam*", "modeling/meshes/uv/unwrapping/seams.html#bpy-ops-uv-mark-seam"),
("bpy.ops.view3d.ruler*", "editors/3dview/toolbar/measure.html#bpy-ops-view3d-ruler"),
("bpy.types.areaspaces*", "interface/window_system/areas.html#bpy-types-areaspaces"),
@@ -1264,7 +1366,7 @@ url_manual_mapping = (
("bpy.types.viewlayer*", "render/layers/layers.html#bpy-types-viewlayer"),
("bpy.ops.collection*", "scene_layout/collections/collections.html#bpy-ops-collection"),
("bpy.ops.constraint*", "animation/constraints/index.html#bpy-ops-constraint"),
- ("bpy.ops.curve.draw*", "modeling/curves/editing/other.html#bpy-ops-curve-draw"),
+ ("bpy.ops.curve.draw*", "modeling/curves/tools/draw.html#bpy-ops-curve-draw"),
("bpy.ops.curve.hide*", "modeling/curves/editing/curve.html#bpy-ops-curve-hide"),
("bpy.ops.curve.spin*", "modeling/surfaces/editing/surface.html#bpy-ops-curve-spin"),
("bpy.ops.graph.bake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-bake"),
@@ -1279,6 +1381,8 @@ url_manual_mapping = (
("bpy.ops.mesh.split*", "modeling/meshes/editing/mesh/split.html#bpy-ops-mesh-split"),
("bpy.ops.nla.delete*", "editors/nla/editing.html#bpy-ops-nla-delete"),
("bpy.ops.paint.mask*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask"),
+ ("bpy.ops.pose.paste*", "animation/armatures/posing/editing/copy_paste.html#bpy-ops-pose-paste"),
+ ("bpy.ops.pose.relax*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-relax"),
("bpy.ops.safe_areas*", "render/cameras.html#bpy-ops-safe-areas"),
("bpy.types.armature*", "animation/armatures/index.html#bpy-types-armature"),
("bpy.types.editbone*", "animation/armatures/bones/editing/index.html#bpy-types-editbone"),
@@ -1301,8 +1405,11 @@ url_manual_mapping = (
("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"),
("bpy.ops.mesh.spin*", "modeling/meshes/tools/spin.html#bpy-ops-mesh-spin"),
("bpy.ops.nla.split*", "editors/nla/editing.html#bpy-ops-nla-split"),
+ ("bpy.ops.pose.copy*", "animation/armatures/posing/editing/copy_paste.html#bpy-ops-pose-copy"),
+ ("bpy.ops.pose.push*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-push"),
("bpy.ops.rigidbody*", "physics/rigid_body/index.html#bpy-ops-rigidbody"),
("bpy.ops.sequencer*", "video_editing/index.html#bpy-ops-sequencer"),
+ ("bpy.ops.text.find*", "editors/text_editor.html#bpy-ops-text-find"),
("bpy.ops.transform*", "scene_layout/object/editing/transform/index.html#bpy-ops-transform"),
("bpy.ops.uv.select*", "editors/uv/selecting.html#bpy-ops-uv-select"),
("bpy.ops.uv.stitch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-stitch"),
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/blender.py b/release/scripts/presets/keyconfig/blender.py
index 0bff9974aaa..cbdd01b3cbe 100644
--- a/release/scripts/presets/keyconfig/blender.py
+++ b/release/scripts/presets/keyconfig/blender.py
@@ -54,6 +54,15 @@ class Prefs(bpy.types.KeyConfigPreferences):
default='PLAY',
update=update_fn,
)
+ use_alt_click_leader: BoolProperty(
+ name="Alt Click Tool Prompt",
+ description=(
+ "Tapping Alt (without pressing any other keys) shows a prompt in the status-bar\n"
+ "prompting a second keystroke to activate the tool"
+ ),
+ default=False,
+ update=update_fn,
+ )
use_select_all_toggle: BoolProperty(
name="Select All Toggles",
description=(
@@ -164,13 +173,16 @@ class Prefs(bpy.types.KeyConfigPreferences):
col = layout.column()
col.row().prop(self, "select_mouse", text="Select with Mouse Button", expand=True)
col.row().prop(self, "spacebar_action", text="Spacebar Action", expand=True)
+
if is_select_left:
col.row().prop(self, "gizmo_action", text="Activate Gizmo Event", expand=True)
# Checkboxes sub-layout.
col = layout.column()
sub = col.column(align=True)
- sub.prop(self, "use_select_all_toggle")
+ row = sub.row()
+ row.prop(self, "use_select_all_toggle")
+ row.prop(self, "use_alt_click_leader")
# 3DView settings.
col = layout.column()
@@ -217,6 +229,7 @@ def load():
kc_prefs.select_mouse == 'LEFT' and
kc_prefs.gizmo_action == 'DRAG'
),
+ use_alt_click_leader=kc_prefs.use_alt_click_leader,
use_pie_click_drag=kc_prefs.use_pie_click_drag,
),
)
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 878c3bfb01f..ef4e1a9f77b 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -52,6 +52,8 @@ class Params:
"use_v3d_shade_ex_pie",
# Swap orbit/pan keys (for 2D workflows).
"use_v3d_mmb_pan",
+ # Alt click to access tools.
+ "use_alt_click_leader",
# Experimental option.
"use_pie_click_drag",
"v3d_tilde_action",
@@ -73,6 +75,7 @@ class Params:
use_v3d_tab_menu=False,
use_v3d_shade_ex_pie=False,
use_v3d_mmb_pan=False,
+ use_alt_click_leader=False,
use_pie_click_drag=False,
v3d_tilde_action='VIEW',
v3d_alt_mmb_drag_action='RELATIVE',
@@ -126,6 +129,7 @@ class Params:
self.v3d_tilde_action = v3d_tilde_action
self.v3d_alt_mmb_drag_action = v3d_alt_mmb_drag_action
+ self.use_alt_click_leader = use_alt_click_leader
self.use_pie_click_drag = use_pie_click_drag
if not use_pie_click_drag:
self.pie_value = 'PRESS'
@@ -449,11 +453,15 @@ def km_window(params):
op_menu("TOPBAR_MT_file_context_menu", {"type": 'F4', "value": 'PRESS'}),
# Pass through when when no tool-system exists or the fallback isn't available.
("wm.toolbar_fallback_pie", {"type": 'W', "value": 'PRESS', "alt": True}, None),
- # Alt as "Leader-Key".
- ("wm.toolbar_prompt", {"type": 'LEFT_ALT', "value": 'CLICK'}, None),
- ("wm.toolbar_prompt", {"type": 'RIGHT_ALT', "value": 'CLICK'}, None),
])
+ if params.use_alt_click_leader:
+ items.extend([
+ # Alt as "Leader-Key".
+ ("wm.toolbar_prompt", {"type": 'LEFT_ALT', "value": 'CLICK'}, None),
+ ("wm.toolbar_prompt", {"type": 'RIGHT_ALT', "value": 'CLICK'}, None),
+ ])
+
if params.spacebar_action == 'TOOL':
items.append(
("wm.toolbar", {"type": 'SPACE', "value": 'PRESS'}, None),
@@ -695,9 +703,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 +736,22 @@ 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)]}),
+ # ShaderFX panels
+ ("object.shaderfx_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ # Constraint panels
+ ("constraint.delete", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
])
return keymap
@@ -834,6 +861,14 @@ def km_uv_editor(params):
{"properties": [("extend", False)]}),
("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True},
{"properties": [("extend", True)]}),
+ ("uv.select_edge_ring", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "alt": True},
+ {"properties": [("extend", False)]}),
+ ("uv.select_edge_ring", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "shift": True, "alt": True},
+ {"properties": [("extend", True)]}),
+ ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True},
+ {"properties": [("use_fill", False)]}),
+ ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "shift": True},
+ {"properties": [("use_fill", True)]}),
("uv.select_split", {"type": 'Y', "value": 'PRESS'}, None),
("uv.select_box", {"type": 'B', "value": 'PRESS'},
{"properties": [("pinned", False)]}),
@@ -853,8 +888,11 @@ def km_uv_editor(params):
("uv.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
*_template_items_select_actions(params, "uv.select_all"),
("uv.select_pinned", {"type": 'P', "value": 'PRESS', "shift": True}, None),
- op_menu("IMAGE_MT_uvs_weldalign", {"type": 'W', "value": 'PRESS', "shift": True}),
- ("uv.stitch", {"type": 'V', "value": 'PRESS'}, None),
+ op_menu("IMAGE_MT_uvs_merge", {"type": 'M', "value": 'PRESS'}),
+ op_menu("IMAGE_MT_uvs_split", {"type": 'M', "value": 'PRESS', "alt": True}),
+ op_menu("IMAGE_MT_uvs_align", {"type": 'W', "value": 'PRESS', "shift": True}),
+ ("uv.stitch", {"type": 'V', "value": 'PRESS', "alt": True}, None),
+ ("uv.rip_move", {"type": 'V', "value": 'PRESS'}, None),
("uv.pin", {"type": 'P', "value": 'PRESS'},
{"properties": [("clear", False)]}),
("uv.pin", {"type": 'P', "value": 'PRESS', "alt": True},
@@ -1893,7 +1931,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'},
@@ -2391,7 +2429,8 @@ def km_sequencercommon(_params):
),
("wm.context_toggle", {"type": 'O', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'scene.sequence_editor.show_overlay')]}),
- ("sequencer.view_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
+ ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}),
])
if _params.select_mouse == 'LEFTMOUSE' and not _params.legacy:
@@ -2463,7 +2502,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),
*(
@@ -2519,14 +2558,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),
])
@@ -3145,7 +3176,7 @@ def km_grease_pencil_stroke_edit_mode(params):
("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("gpencil.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
# Snap
- op_menu("GPENCIL_MT_snap", {"type": 'S', "value": 'PRESS', "shift": True}),
+ op_menu_pie("GPENCIL_MT_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True}),
# Show/hide
("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
("gpencil.hide", {"type": 'H', "value": 'PRESS'},
@@ -4347,6 +4378,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)]}),
@@ -4369,7 +4402,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')]}),
@@ -4423,9 +4456,9 @@ def km_mesh(params):
{"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False), ],)]}),
("mesh.inset", {"type": 'I', "value": 'PRESS'}, None),
("mesh.bevel", {"type": 'B', "value": 'PRESS', "ctrl": True},
- {"properties": [("vertex_only", False)]}),
+ {"properties": [("affect", 'EDGES')]}),
("mesh.bevel", {"type": 'B', "value": 'PRESS', "shift": True, "ctrl": True},
- {"properties": [("vertex_only", True)]}),
+ {"properties": [("affect", 'VERTICES')]}),
# Selection modes.
*_template_items_editmode_mesh_select_mode(params),
# Loop Select with alt. Double click in case MMB emulation is on (below).
@@ -5177,13 +5210,13 @@ def km_bevel_modal_map(_params):
("SEGMENTS_DOWN", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "any": True}, None),
("OFFSET_MODE_CHANGE", {"type": 'M', "value": 'PRESS', "any": True}, None),
("CLAMP_OVERLAP_TOGGLE", {"type": 'C', "value": 'PRESS', "any": True}, None),
- ("VERTEX_ONLY_TOGGLE", {"type": 'V', "value": 'PRESS', "any": True}, None),
+ ("AFFECT_CHANGE", {"type": 'V', "value": 'PRESS', "any": True}, None),
("HARDEN_NORMALS_TOGGLE", {"type": 'H', "value": 'PRESS', "any": True}, None),
("MARK_SEAM_TOGGLE", {"type": 'U', "value": 'PRESS', "any": True}, None),
("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),
])
@@ -5581,6 +5614,17 @@ def km_image_editor_tool_uv_select_lasso(params):
)
+def km_image_editor_tool_uv_rip_region(params):
+ return (
+ "Image Editor Tool: Uv, Rip Region",
+ {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
+ {"items": [
+ ("uv.rip_move", {"type": params.tool_tweak, "value": 'ANY'},
+ {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
+ ]},
+ )
+
+
def km_image_editor_tool_uv_sculpt_stroke(params):
return (
"Image Editor Tool: Uv, Sculpt Stroke",
@@ -5913,12 +5957,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", [
@@ -6214,9 +6258,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),
@@ -6284,6 +6328,28 @@ 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_sculpt_mask_by_color(params):
+ return (
+ "3D View Tool: Sculpt, Mask By Color",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'},
+ None),
+ ("sculpt.mask_by_color", {"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",
@@ -6761,6 +6827,7 @@ def generate_keymaps(params=None):
km_image_editor_tool_uv_select_box(params),
km_image_editor_tool_uv_select_circle(params),
km_image_editor_tool_uv_select_lasso(params),
+ km_image_editor_tool_uv_rip_region(params),
km_image_editor_tool_uv_sculpt_stroke(params),
km_image_editor_tool_uv_move(params),
km_image_editor_tool_uv_rotate(params),
@@ -6791,7 +6858,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),
@@ -6818,12 +6885,14 @@ 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_sculpt_mask_by_color(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..eedf07935c8 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},
@@ -2316,7 +2316,7 @@ def km_grease_pencil_stroke_edit_mode(params):
("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("gpencil.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
# Snap
- op_menu("GPENCIL_MT_snap", {"type": 'X', "value": 'PRESS', "shift": True}),
+ op_menu_pie("GPENCIL_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}),
# Show/hide
("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
("gpencil.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index d7df29f1769..c927cc184a3 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -46,9 +46,9 @@ _modules = [
"userpref",
"uvcalc_follow_active",
"uvcalc_lightmap",
- "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..ec7ff970537
--- /dev/null
+++ b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py
@@ -0,0 +1,157 @@
+# ##### 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):
+ 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 12d7984b3b2..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:
@@ -237,7 +238,7 @@ class SubdivisionSet(Operator):
)
relative: BoolProperty(
name="Relative",
- description=("Apply the subsurf level as an offset "
+ description=("Apply the subdivision surface level as an offset "
"relative to the current level"),
default=False,
)
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 71153ba8b74..d0344b88be8 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
@@ -560,9 +561,61 @@ class QuickLiquid(Operator):
return {'FINISHED'}
+class QuickParticles(Operator):
+ """Use active object as particle emitter"""
+ bl_idname = "object.quick_particles"
+ bl_label = "Quick Particles"
+
+ @classmethod
+ def poll(cls, context):
+ if not context.preferences.experimental.use_new_particle_system:
+ return False
+ if context.mode != 'OBJECT':
+ return False
+ if context.active_object is None:
+ return False
+ if context.active_object.type != 'MESH':
+ return False
+ return True
+
+ def execute(self, context):
+ pointcloud = bpy.data.pointclouds.new("Particles")
+ pointcloud_object = bpy.data.objects.new("Particles", pointcloud)
+ modifier = pointcloud_object.modifiers.new("Simulation", 'SIMULATION')
+ simulation = bpy.data.simulations.new("Particle Simulation")
+ tree = simulation.node_tree
+
+ default_name = "Particles"
+ particle_simulation_node = tree.nodes.new('SimulationNodeParticleSimulation')
+ particle_simulation_node.name = default_name
+ emitter_node = tree.nodes.new('SimulationNodeParticleMeshEmitter')
+ emitter_node.location.x -= 200
+ emitter_node.location.y += 50
+ emitter_node.inputs["Object"].default_value = context.active_object
+ force_node = tree.nodes.new('SimulationNodeForce')
+ force_node.location.x -= 200
+ force_node.location.y -= 100
+ force_node.inputs["Force"].default_value = (0, 0, -1)
+
+ tree.links.new(particle_simulation_node.inputs["Emitters"], emitter_node.outputs["Emitter"])
+ tree.links.new(particle_simulation_node.inputs["Forces"], force_node.outputs["Force"])
+
+ modifier.simulation = simulation
+ modifier.data_path = default_name
+
+ for obj in context.selected_objects:
+ obj.select_set(False)
+
+ context.collection.objects.link(pointcloud_object)
+ pointcloud_object.select_set(True)
+ context.view_layer.objects.active = pointcloud_object
+ pointcloud_object.show_bounds = True
+ return {'FINISHED'}
+
classes = (
QuickExplode,
QuickFur,
QuickSmoke,
QuickLiquid,
+ QuickParticles,
)
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..6c29c07c62e 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,13 +134,19 @@ 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}"]
+ opts = [file, "%d-%d" % (scene.frame_start, scene.frame_end)]
cmd.extend(opts)
elif preset == 'RV':
- opts = ["-fps", str(rd.fps), "-play", f"[ {file:s} ]"]
+ opts = ["-fps", str(rd.fps), "-play", "[ %s ]" % file]
cmd.extend(opts)
elif preset == 'MPLAYER':
opts = []
@@ -164,7 +156,7 @@ class PlayRenderedAnim(Operator):
opts += [
("mf://" + file.replace("#", "?")),
"-mf",
- f"fps={fps_final:4f}"
+ "fps=%.4f" % fps_final,
]
opts += ["-loop", "0", "-really-quiet", "-fs"]
@@ -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..a332f938afd 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_cache('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_cache('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/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index 2e14df1920f..e92f493960a 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -119,8 +119,11 @@ class PREFERENCES_OT_copy_prev(Operator):
# Find config folder from previous version.
import os
version = bpy.app.version
+ version_new = ((version[0] * 100) + version[1])
version_old = ((version[0] * 100) + version[1]) - 1
- while version_old % 10 > 0:
+ # Ensure we only try to copy files from a point release.
+ # The check below ensures the second numbers match.
+ while (version_new % 100) // 10 == (version_old % 100) // 10:
version_split = version_old // 100, version_old % 100
if os.path.isdir(cls._old_version_path(version_split)):
return version_split
diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
index 6f441238606..4343717f264 100644
--- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py
+++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
@@ -160,7 +160,12 @@ def extend(obj, EXTEND_MODE):
l_b_uv = [l[uv_act].uv for l in l_b]
if EXTEND_MODE == 'LENGTH_AVERAGE':
- fac = edge_lengths[l_b[2].edge.index][0] / edge_lengths[l_a[1].edge.index][0]
+ d1 = edge_lengths[l_a[1].edge.index][0]
+ d2 = edge_lengths[l_b[2].edge.index][0]
+ try:
+ fac = d2 / d1
+ except ZeroDivisionError:
+ fac = 1.0
elif EXTEND_MODE == 'LENGTH':
a0, b0, c0 = l_a[3].vert.co, l_a[0].vert.co, l_b[3].vert.co
a1, b1, c1 = l_a[2].vert.co, l_a[1].vert.co, l_b[2].vert.co
diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
deleted file mode 100644
index 1f56cbe6d57..00000000000
--- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py
+++ /dev/null
@@ -1,1053 +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 #####
-
-# TODO <pep8 compliant>
-
-from mathutils import (
- Matrix,
- Vector,
- geometry,
-)
-import bpy
-from bpy.types import Operator
-
-DEG_TO_RAD = 0.017453292519943295 # pi/180.0
-# see bugs:
-# - T31598 (when too small).
-# - T48086 (when too big).
-SMALL_NUM = 1e-12
-
-
-global USER_FILL_HOLES
-global USER_FILL_HOLES_QUALITY
-USER_FILL_HOLES = None
-USER_FILL_HOLES_QUALITY = None
-
-
-def pointInTri2D(v, v1, v2, v3):
- key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
-
- # Commented because its slower to do the bounds check, we should really cache the bounds info for each face.
- '''
- # BOUNDS CHECK
- xmin= 1000000
- ymin= 1000000
-
- xmax= -1000000
- ymax= -1000000
-
- for i in (0,2,4):
- x= key[i]
- y= key[i+1]
-
- if xmax<x: xmax= x
- if ymax<y: ymax= y
- if xmin>x: xmin= x
- if ymin>y: ymin= y
-
- x= v.x
- y= v.y
-
- if x<xmin or x>xmax or y < ymin or y > ymax:
- return False
- # Done with bounds check
- '''
- try:
- mtx = dict_matrix[key]
- if not mtx:
- return False
- except:
- side1 = v2 - v1
- side2 = v3 - v1
-
- nor = side1.cross(side2)
-
- mtx = Matrix((side1, side2, nor))
-
- # Zero area 2d tri, even tho we throw away zero area faces
- # the projection UV can result in a zero area UV.
- if not mtx.determinant():
- dict_matrix[key] = None
- return False
-
- mtx.invert()
-
- dict_matrix[key] = mtx
-
- uvw = (v - v1) @ mtx
- return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
-
-
-def boundsIsland(faces):
- minx = maxx = faces[0].uv[0][0] # Set initial bounds.
- miny = maxy = faces[0].uv[0][1]
- # print len(faces), minx, maxx, miny , maxy
- for f in faces:
- for uv in f.uv:
- x = uv.x
- y = uv.y
- if x < minx:
- minx = x
- if y < miny:
- miny = y
- if x > maxx:
- maxx = x
- if y > maxy:
- maxy = y
-
- return minx, miny, maxx, maxy
-
-
-"""
-def boundsEdgeLoop(edges):
- minx = maxx = edges[0][0] # Set initial bounds.
- miny = maxy = edges[0][1]
- # print len(faces), minx, maxx, miny , maxy
- for ed in edges:
- for pt in ed:
- x= pt[0]
- y= pt[1]
- if x<minx: x= minx
- if y<miny: y= miny
- if x>maxx: x= maxx
- if y>maxy: y= maxy
-
- return minx, miny, maxx, maxy
-"""
-
-# Turns the islands into a list of unpordered edges (Non internal)
-# Only for UV's
-# only returns outline edges for intersection tests. and unique points.
-
-
-def island2Edge(island):
-
- # Vert index edges
- edges = {}
-
- unique_points = {}
-
- for f in island:
- f_uvkey = list(map(tuple, f.uv))
-
- for vIdx in range(len(f_uvkey)):
- unique_points[f_uvkey[vIdx]] = f.uv[vIdx]
-
- if f.v[vIdx].index > f.v[vIdx - 1].index:
- i1 = vIdx - 1
- i2 = vIdx
- else:
- i1 = vIdx
- i2 = vIdx - 1
-
- try:
- edges[f_uvkey[i1], f_uvkey[i2]] *= 0 # sets any edge with more than 1 user to 0 are not returned.
- except:
- edges[f_uvkey[i1], f_uvkey[i2]] = (f.uv[i1] - f.uv[i2]).length
-
- # If 2 are the same then they will be together, but full [a,b] order is not correct.
-
- # Sort by length
- length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0]
-
- length_sorted_edges.sort(key=lambda a: -a[2]) # largest first
-
- # Its okay to leave the length in there.
- # for e in length_sorted_edges:
- # e.pop(2)
-
- # return edges and unique points
- return length_sorted_edges, [v.to_3d() for v in unique_points.values()]
-
-
-def pointInIsland(pt, island):
- vec1, vec2, vec3 = Vector(), Vector(), Vector()
- for f in island:
- vec1.x, vec1.y = f.uv[0]
- vec2.x, vec2.y = f.uv[1]
- vec3.x, vec3.y = f.uv[2]
-
- if pointInTri2D(pt, vec1, vec2, vec3):
- return True
-
- if len(f.v) == 4:
- vec1.x, vec1.y = f.uv[0]
- vec2.x, vec2.y = f.uv[2]
- vec3.x, vec3.y = f.uv[3]
- if pointInTri2D(pt, vec1, vec2, vec3):
- return True
- return False
-
-
-# box is (left,bottom, right, top)
-def islandIntersectUvIsland(source, target, SourceOffset):
- # Is 1 point in the box, inside the vertLoops
- edgeLoopsSource = source[6] # Pretend this is offset
- edgeLoopsTarget = target[6]
-
- # Edge intersect test
- for ed in edgeLoopsSource:
- for seg in edgeLoopsTarget:
- i = geometry.intersect_line_line_2d(seg[0],
- seg[1],
- SourceOffset + ed[0],
- SourceOffset + ed[1],
- )
- if i:
- return 1 # LINE INTERSECTION
-
- # 1 test for source being totally inside target
- SourceOffset.resize_3d()
- for pv in source[7]:
- if pointInIsland(pv + SourceOffset, target[0]):
- return 2 # SOURCE INSIDE TARGET
-
- # 2 test for a part of the target being totally inside the source.
- for pv in target[7]:
- if pointInIsland(pv - SourceOffset, source[0]):
- return 3 # PART OF TARGET INSIDE SOURCE.
-
- return 0 # NO INTERSECTION
-
-
-def rotate_uvs(uv_points, angle):
-
- if angle != 0.0:
- mat = Matrix.Rotation(angle, 2)
- for uv in uv_points:
- uv[:] = mat @ uv
-
-
-def optiRotateUvIsland(faces):
- uv_points = [uv for f in faces for uv in f.uv]
- angle = geometry.box_fit_2d(uv_points)
-
- if angle != 0.0:
- rotate_uvs(uv_points, angle)
-
- # orient them vertically (could be an option)
- minx, miny, maxx, maxy = boundsIsland(faces)
- w, h = maxx - minx, maxy - miny
- # use epsilon so we don't randomly rotate (almost) perfect squares.
- if h + 0.00001 < w:
- from math import pi
- angle = pi / 2.0
- rotate_uvs(uv_points, angle)
-
-
-# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
-def mergeUvIslands(islandList):
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
-
- # Pack islands to bottom LHS
- # Sync with island
-
- # islandTotFaceArea = [] # A list of floats, each island area
- # islandArea = [] # a list of tuples ( area, w,h)
-
- decoratedIslandList = []
-
- islandIdx = len(islandList)
- while islandIdx:
- islandIdx -= 1
- minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
- w, h = maxx - minx, maxy - miny
-
- totFaceArea = 0
- offset = Vector((minx, miny))
- for f in islandList[islandIdx]:
- for uv in f.uv:
- uv -= offset
-
- totFaceArea += f.area
-
- islandBoundsArea = w * h
- efficiency = abs(islandBoundsArea - totFaceArea)
-
- # UV Edge list used for intersections as well as unique points.
- edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
-
- decoratedIslandList.append([
- islandList[islandIdx],
- totFaceArea,
- efficiency,
- islandBoundsArea,
- w,
- h,
- edges,
- uniqueEdgePoints,
- ])
-
- # Sort by island bounding box area, smallest face area first.
- # no.. chance that to most simple edge loop first.
- decoratedIslandListAreaSort = decoratedIslandList[:]
-
- decoratedIslandListAreaSort.sort(key=lambda A: A[3])
-
- # sort by efficiency, Least Efficient first.
- decoratedIslandListEfficSort = decoratedIslandList[:]
- # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
-
- decoratedIslandListEfficSort.sort(key=lambda A: -A[2])
-
- # ================================================== THESE CAN BE TWEAKED.
- # This is a quality value for the number of tests.
- # from 1 to 4, generic quality value is from 1 to 100
- USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
-
- # If 100 will test as long as there is enough free space.
- # this is rarely enough, and testing takes a while, so lower quality speeds this up.
-
- # 1 means they have the same quality
- USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY) / 100.0) * 5)
-
- # print 'USER_STEP_QUALITY', USER_STEP_QUALITY
- # print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
-
- removedCount = 0
-
- areaIslandIdx = 0
- ctrl = Window.Qual.CTRL
- BREAK = False
- while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
- sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
- # Already packed?
- if not sourceIsland[0]:
- areaIslandIdx += 1
- else:
- efficIslandIdx = 0
- while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK:
-
- if Window.GetKeyQualifiers() & ctrl:
- BREAK = True
- break
-
- # Now we have 2 islands, if the efficiency of the islands lowers there's an
- # increasing likely hood that we can fit merge into the bigger UV island.
- # this ensures a tight fit.
-
- # Just use figures we have about user/unused area to see if they might fit.
-
- targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
-
- if sourceIsland[0] == targetIsland[0] or\
- not targetIsland[0] or\
- not sourceIsland[0]:
- pass
- else:
-
- #~ ([island, totFaceArea, efficiency, islandArea, w,h])
- # Wasted space on target is greater then UV bounding island area.
-
- #~ if targetIsland[3] > (sourceIsland[2]) and\ #
- # ~ print USER_FREE_SPACE_TO_TEST_QUALITY
- if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
- targetIsland[4] > sourceIsland[4] and\
- targetIsland[5] > sourceIsland[5]:
-
- # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1])
-
- # These enough spare space lets move the box until it fits
-
- # How many times does the source fit into the target x/y
- blockTestXUnit = targetIsland[4] / sourceIsland[4]
- blockTestYUnit = targetIsland[5] / sourceIsland[5]
-
- boxLeft = 0
-
- # Distance we can move between whilst staying inside the targets bounds.
- testWidth = targetIsland[4] - sourceIsland[4]
- testHeight = targetIsland[5] - sourceIsland[5]
-
- # Increment we move each test. x/y
- xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY / 50) + 0.1)))
- yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY / 50) + 0.1)))
-
- # Make sure were not moving less then a 3rg of our width/height
- if xIncrement < sourceIsland[4] / 3:
- xIncrement = sourceIsland[4]
- if yIncrement < sourceIsland[5] / 3:
- yIncrement = sourceIsland[5]
-
- boxLeft = 0 # Start 1 back so we can jump into the loop.
- boxBottom = 0 # -yIncrement
-
- # ~ testcount= 0
-
- while boxBottom <= testHeight:
- # Should we use this? - not needed for now.
- # ~ if Window.GetKeyQualifiers() & ctrl:
- # ~ BREAK= True
- # ~ break
-
- # testcount+=1
- # print 'Testing intersect'
- Intersect = islandIntersectUvIsland(
- sourceIsland, targetIsland, Vector((boxLeft, boxBottom)))
- # print 'Done', Intersect
- if Intersect == 1: # Line intersect, don't bother with this any more
- pass
-
- if Intersect == 2: # Source inside target
- """
- We have an intersection, if we are inside the target
- then move us 1 whole width across,
- Its possible this is a bad idea since 2 skinny Angular faces
- could join without 1 whole move, but its a lot more optimal to speed this up
- since we have already tested for it.
-
- It gives about 10% speedup with minimal errors.
- """
- # Move the test along its width + SMALL_NUM
- #boxLeft += sourceIsland[4] + SMALL_NUM
- boxLeft += sourceIsland[4]
- elif Intersect == 0: # No intersection?? Place it.
- # Progress
- removedCount += 1
-# XXX Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
-
- # Move faces into new island and offset
- targetIsland[0].extend(sourceIsland[0])
- offset = Vector((boxLeft, boxBottom))
-
- for f in sourceIsland[0]:
- for uv in f.uv:
- uv += offset
-
- del sourceIsland[0][:] # Empty
-
- # Move edge loop into new and offset.
- # targetIsland[6].extend(sourceIsland[6])
- # while sourceIsland[6]:
- targetIsland[6].extend([(
- (e[0] + offset, e[1] + offset, e[2])
- ) for e in sourceIsland[6]])
-
- del sourceIsland[6][:] # Empty
-
- # Sort by edge length, reverse so biggest are first.
-
- try:
- targetIsland[6].sort(key=lambda A: A[2])
- except:
- targetIsland[6].sort(lambda B, A: cmp(A[2], B[2]))
-
- targetIsland[7].extend(sourceIsland[7])
- offset = Vector((boxLeft, boxBottom, 0.0))
- for p in sourceIsland[7]:
- p += offset
-
- del sourceIsland[7][:]
-
- # Decrement the efficiency
- targetIsland[1] += sourceIsland[1] # Increment totFaceArea
- targetIsland[2] -= sourceIsland[1] # Decrement efficiency
- # IF we ever used these again, should set to 0, eg
- sourceIsland[2] = 0 # No area if anyone wants to know
-
- break
-
- # INCREMENT NEXT LOCATION
- if boxLeft > testWidth:
- boxBottom += yIncrement
- boxLeft = 0.0
- else:
- boxLeft += xIncrement
- # print testcount
-
- efficIslandIdx += 1
- areaIslandIdx += 1
-
- # Remove empty islands
- i = len(islandList)
- while i:
- i -= 1
- if not islandList[i]:
- del islandList[i] # Can increment islands removed here.
-
-# Takes groups of faces. assumes face groups are UV groups.
-
-
-def getUvIslands(faceGroups, me):
-
- # Get seams so we don't cross over seams
- edge_seams = {} # should be a set
- for ed in me.edges:
- if ed.use_seam:
- edge_seams[ed.key] = None # dummy var- use sets!
- # Done finding seams
-
- islandList = []
-
-# XXX Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups))
- # print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
- # Find grouped faces
-
- faceGroupIdx = len(faceGroups)
-
- while faceGroupIdx:
- faceGroupIdx -= 1
- faces = faceGroups[faceGroupIdx]
-
- if not faces:
- continue
-
- # Build edge dict
- edge_users = {}
-
- for i, f in enumerate(faces):
- for ed_key in f.edge_keys:
- if ed_key in edge_seams: # DELIMIT SEAMS! ;)
- edge_users[ed_key] = [] # so as not to raise an error
- else:
- try:
- edge_users[ed_key].append(i)
- except:
- edge_users[ed_key] = [i]
-
- # Modes
- # 0 - face not yet touched.
- # 1 - added to island list, and need to search
- # 2 - touched and searched - don't touch again.
- face_modes = [0] * len(faces) # initialize zero - untested.
-
- face_modes[0] = 1 # start the search with face 1
-
- newIsland = []
-
- newIsland.append(faces[0])
-
- ok = True
- while ok:
-
- ok = True
- while ok:
- ok = False
- for i in range(len(faces)):
- if face_modes[i] == 1: # search
- for ed_key in faces[i].edge_keys:
- for ii in edge_users[ed_key]:
- if i != ii and face_modes[ii] == 0:
- face_modes[ii] = ok = 1 # mark as searched
- newIsland.append(faces[ii])
-
- # mark as searched, don't look again.
- face_modes[i] = 2
-
- islandList.append(newIsland)
-
- ok = False
- for i in range(len(faces)):
- if face_modes[i] == 0:
- newIsland = []
- newIsland.append(faces[i])
-
- face_modes[i] = ok = 1
- break
- # if not ok will stop looping
-
-# XXX Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
-
- for island in islandList:
- optiRotateUvIsland(island)
-
- return islandList
-
-
-def packIslands(islandList):
- if USER_FILL_HOLES:
- # XXX Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
- mergeUvIslands(islandList) # Modify in place
-
- # Now we have UV islands, we need to pack them.
-
- # Make a synchronized list with the islands
- # so we can box pack the islands.
- packBoxes = []
-
- # Keep a list of X/Y offset so we can save time by writing the
- # uv's and packed data in one pass.
- islandOffsetList = []
-
- islandIdx = 0
-
- while islandIdx < len(islandList):
- minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
-
- w, h = maxx - minx, maxy - miny
-
- if USER_ISLAND_MARGIN:
- minx -= USER_ISLAND_MARGIN * w / 2
- miny -= USER_ISLAND_MARGIN * h / 2
- maxx += USER_ISLAND_MARGIN * w / 2
- maxy += USER_ISLAND_MARGIN * h / 2
-
- # recalc width and height
- w, h = maxx - minx, maxy - miny
-
- if w < SMALL_NUM:
- w = SMALL_NUM
- if h < SMALL_NUM:
- h = SMALL_NUM
-
- """Save the offset to be applied later,
- we could apply to the UVs now and align them to the bottom left hand area
- of the UV coords like the box packer imagines they are
- but, its quicker just to remember their offset and
- apply the packing and offset in 1 pass """
- islandOffsetList.append((minx, miny))
-
- # Add to boxList. use the island idx for the BOX id.
- packBoxes.append([0, 0, w, h])
- islandIdx += 1
-
- # Now we have a list of boxes to pack that syncs
- # with the islands.
-
- # print '\tPacking UV Islands...'
-# XXX Window.DrawProgressBar(0.7, "Packing %i UV Islands..." % len(packBoxes) )
-
- # time1 = time.time()
- packWidth, packHeight = geometry.box_pack_2d(packBoxes)
-
- # print 'Box Packing Time:', time.time() - time1
-
- # if len(packedLs) != len(islandList):
- # raise ValueError("Packed boxes differs from original length")
-
- # print '\tWriting Packed Data to faces'
-# XXX Window.DrawProgressBar(0.8, "Writing Packed Data to faces")
-
- # Sort by ID, so there in sync again
- islandIdx = len(islandList)
- # Having these here avoids divide by 0
- if islandIdx:
-
- if USER_STRETCH_ASPECT:
- # Maximize to uv area?? Will write a normalize function.
- xfactor = 1.0 / packWidth
- yfactor = 1.0 / packHeight
- else:
- # Keep proportions.
- xfactor = yfactor = 1.0 / max(packWidth, packHeight)
-
- while islandIdx:
- islandIdx -= 1
- # Write the packed values to the UV's
-
- xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0]
- yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1]
-
- for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
- for uv in f.uv:
- uv.x = (uv.x + xoffset) * xfactor
- uv.y = (uv.y + yoffset) * yfactor
-
-
-def VectoQuat(vec):
- vec = vec.normalized()
- return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted()
-
-
-class thickface:
- __slost__ = "v", "uv", "no", "area", "edge_keys"
-
- def __init__(self, face, uv_layer, mesh_verts):
- self.v = [mesh_verts[i] for i in face.vertices]
- self.uv = [uv_layer[i].uv for i in face.loop_indices]
-
- self.no = face.normal.copy()
- self.area = face.area
- self.edge_keys = face.edge_keys
-
-
-def main_consts():
- from math import radians
-
- global ROTMAT_2D_POS_90D
- global ROTMAT_2D_POS_45D
- global RotMatStepRotation
-
- ROTMAT_2D_POS_90D = Matrix.Rotation(radians(90.0), 2)
- ROTMAT_2D_POS_45D = Matrix.Rotation(radians(45.0), 2)
-
- RotMatStepRotation = []
- rot_angle = 22.5 # 45.0/2
- while rot_angle > 0.1:
- RotMatStepRotation.append([
- Matrix.Rotation(radians(+rot_angle), 2),
- Matrix.Rotation(radians(-rot_angle), 2),
- ])
-
- rot_angle = rot_angle / 2.0
-
-
-global ob
-ob = None
-
-
-def main(context,
- island_margin,
- projection_limit,
- user_area_weight,
- use_aspect,
- stretch_to_bounds,
- ):
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
- global USER_STRETCH_ASPECT
- global USER_ISLAND_MARGIN
-
- from math import cos
- import time
-
- global dict_matrix
- dict_matrix = {}
-
- # Constants:
- # Takes a list of faces that make up a UV island and rotate
- # until they optimally fit inside a square.
- global ROTMAT_2D_POS_90D
- global ROTMAT_2D_POS_45D
- global RotMatStepRotation
- main_consts()
-
- # Create the variables.
- USER_PROJECTION_LIMIT = projection_limit
- USER_ONLY_SELECTED_FACES = True
- USER_SHARE_SPACE = 1 # Only for hole filling.
- USER_STRETCH_ASPECT = stretch_to_bounds
- USER_ISLAND_MARGIN = island_margin # Only for hole filling.
- USER_FILL_HOLES = 0
- USER_FILL_HOLES_QUALITY = 50 # Only for hole filling.
- USER_VIEW_INIT = 0 # Only for hole filling.
-
- is_editmode = (context.mode == 'EDIT_MESH')
- if is_editmode:
- obList = context.objects_in_mode_unique_data
- else:
- obList = [
- ob for ob in context.selected_editable_objects
- if ob.type == 'MESH' and ob.data.library is None
- ]
-
- if not is_editmode:
- USER_ONLY_SELECTED_FACES = False
-
- if not obList:
- raise Exception("error, no selected mesh objects")
-
- # Convert from being button types
- USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
- USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD)
-
- # Toggle Edit mode
- if is_editmode:
- bpy.ops.object.mode_set(mode='OBJECT')
- # Assume face select mode! an annoying hack to toggle face select mode because Mesh doesn't like faceSelectMode.
-
- if USER_SHARE_SPACE:
- # Sort by data name so we get consistent results
- obList.sort(key=lambda ob: ob.data.name)
- collected_islandList = []
-
- time1 = time.time()
-
- # Tag as False so we don't operate on the same mesh twice.
- for me in bpy.data.meshes:
- me.tag = False
-
- for ob in obList:
- me = ob.data
-
- if me.tag or me.library:
- continue
-
- # Tag as used
- me.tag = True
-
- if not me.uv_layers: # Mesh has no UV Coords, don't bother.
- me.uv_layers.new()
-
- uv_layer = me.uv_layers.active.data
- me_verts = list(me.vertices)
-
- if USER_ONLY_SELECTED_FACES:
- meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons) if f.select]
- else:
- meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons)]
-
- # =======
- # Generate a projection list from face normals, this is meant to be smart :)
-
- # make a list of face props that are in sync with meshFaces
- # Make a Face List that is sorted by area.
- # meshFaces = []
-
- # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
- meshFaces.sort(key=lambda a: -a.area)
-
- # remove all zero area faces
- while meshFaces and meshFaces[-1].area <= SMALL_NUM:
- # Set their UV's to 0,0
- for uv in meshFaces[-1].uv:
- uv.zero()
- meshFaces.pop()
-
- if not meshFaces:
- continue
-
- # Smallest first is slightly more efficient,
- # but if the user cancels early then its better we work on the larger data.
-
- # Generate Projection Vecs
- # 0d is 1.0
- # 180 IS -0.59846
-
- # Initialize projectVecs
- if USER_VIEW_INIT:
- # Generate Projection
-
- # We add to this along the way
- projectVecs = [Vector(Window.GetViewVector()) @ ob.matrix_world.inverted().to_3x3()]
- else:
- projectVecs = []
-
- newProjectVec = meshFaces[0].no
- newProjectMeshFaces = [] # Popping stuffs it up.
-
- # Pretend that the most unique angle is ages away to start the loop off
- mostUniqueAngle = -1.0
-
- # This is popped
- tempMeshFaces = meshFaces[:]
-
- # This while only gathers projection vecs, faces are assigned later on.
- while 1:
- # If there's none there then start with the largest face
-
- # add all the faces that are close.
- for fIdx in range(len(tempMeshFaces) - 1, -1, -1):
- # Use half the angle limit so we don't overweight faces towards this
- # normal and hog all the faces.
- if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
- newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))
-
- # Add the average of all these faces normals as a projectionVec
- averageVec = Vector((0.0, 0.0, 0.0))
- if user_area_weight == 0.0:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no
- elif user_area_weight == 1.0:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no * fprop.area
- else:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight))
-
- if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
- projectVecs.append(averageVec.normalized())
-
- # Get the next vec!
- # Pick the face that's most different to all existing angles :)
- mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
- mostUniqueIndex = 0 # dummy
-
- for fIdx in range(len(tempMeshFaces) - 1, -1, -1):
- angleDifference = -1.0 # 180d difference.
-
- # Get the closest vec angle we are to.
- for p in projectVecs:
- temp_angle_diff = p.dot(tempMeshFaces[fIdx].no)
-
- if angleDifference < temp_angle_diff:
- angleDifference = temp_angle_diff
-
- if angleDifference < mostUniqueAngle:
- # We have a new most different angle
- mostUniqueIndex = fIdx
- mostUniqueAngle = angleDifference
-
- if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
- # print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
- # Now weight the vector to all its faces, will give a more direct projection
- # if the face its self was not representative of the normal from surrounding faces.
-
- newProjectVec = tempMeshFaces[mostUniqueIndex].no
- newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]
-
- else:
- if len(projectVecs) >= 1: # Must have at least 2 projections
- break
-
- # If there are only zero area faces then its possible
- # there are no projectionVecs
- if not len(projectVecs):
- Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
- return
-
- faceProjectionGroupList = [[] for i in range(len(projectVecs))]
-
- # MAP and Arrange # We know there are 3 or 4 faces here
-
- for fIdx in range(len(meshFaces) - 1, -1, -1):
- fvec = meshFaces[fIdx].no
- i = len(projectVecs)
-
- # Initialize first
- bestAng = fvec.dot(projectVecs[0])
- bestAngIdx = 0
-
- # Cycle through the remaining, first already done
- while i - 1:
- i -= 1
-
- newAng = fvec.dot(projectVecs[i])
- if newAng > bestAng: # Reverse logic for dotvecs
- bestAng = newAng
- bestAngIdx = i
-
- # Store the area for later use.
- faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])
-
- # Cull faceProjectionGroupList,
-
- # Now faceProjectionGroupList is full of faces that face match the project Vecs list
- for i in range(len(projectVecs)):
- # Account for projectVecs having no faces.
- if not faceProjectionGroupList[i]:
- continue
-
- # Make a projection matrix from a unit length vector.
- MatQuat = VectoQuat(projectVecs[i])
-
- # Get the faces UV's from the projected vertex.
- for f in faceProjectionGroupList[i]:
- f_uv = f.uv
- for j, v in enumerate(f.v):
- f_uv[j][:] = (MatQuat @ v.co).xy
-
- if USER_SHARE_SPACE:
- # Should we collect and pack later?
- islandList = getUvIslands(faceProjectionGroupList, me)
- collected_islandList.extend(islandList)
-
- else:
- # Should we pack the islands for this 1 object?
- islandList = getUvIslands(faceProjectionGroupList, me)
- packIslands(islandList)
-
- # update the mesh here if we need to.
-
- # We want to pack all in 1 go, so pack now
- if USER_SHARE_SPACE:
- packIslands(collected_islandList)
-
- print("Smart Projection time: %.2f" % (time.time() - time1))
-
- # aspect correction is only done in edit mode - and only smart unwrap supports currently
- if is_editmode:
- bpy.ops.object.mode_set(mode='EDIT')
-
- if use_aspect:
- import bmesh
- aspect = context.scene.uvedit_aspect(context.active_object)
- if aspect[0] > aspect[1]:
- aspect[0] = aspect[1] / aspect[0]
- aspect[1] = 1.0
- else:
- aspect[1] = aspect[0] / aspect[1]
- aspect[0] = 1.0
-
- bm = bmesh.from_edit_mesh(me)
-
- uv_act = bm.loops.layers.uv.active
-
- faces = [f for f in bm.faces if f.select]
-
- for f in faces:
- for l in f.loops:
- l[uv_act].uv[0] *= aspect[0]
- l[uv_act].uv[1] *= aspect[1]
-
- dict_matrix.clear()
-
-
-from bpy.props import FloatProperty, BoolProperty
-
-
-class SmartProject(Operator):
- """This script projection unwraps the selected faces of a mesh """ \
- """(it operates on all selected mesh objects, and can be used """ \
- """to unwrap selected faces, or all faces)"""
- bl_idname = "uv.smart_project"
- bl_label = "Smart UV Project"
- bl_options = {'REGISTER', 'UNDO'}
-
- angle_limit: FloatProperty(
- name="Angle Limit",
- description="Lower for more projection groups, higher for less distortion",
- min=1.0, max=89.0,
- default=66.0,
- )
- island_margin: FloatProperty(
- name="Island Margin",
- description="Margin to reduce bleed from adjacent islands",
- min=0.0, max=1.0,
- default=0.0,
- )
- user_area_weight: FloatProperty(
- name="Area Weight",
- description="Weight projections vector by faces with larger areas",
- min=0.0, max=1.0,
- default=0.0,
- )
- use_aspect: BoolProperty(
- name="Correct Aspect",
- description="Map UVs taking image aspect ratio into account",
- default=True,
- )
- stretch_to_bounds: BoolProperty(
- name="Stretch to UV Bounds",
- description="Stretch the final output to texture bounds",
- default=True,
- )
-
- @classmethod
- def poll(cls, context):
- return context.active_object is not None
-
- def execute(self, context):
- main(context,
- self.island_margin,
- self.angle_limit,
- self.user_area_weight,
- self.use_aspect,
- self.stretch_to_bounds,
- )
- return {'FINISHED'}
-
- def invoke(self, context, _event):
- wm = context.window_manager
- return wm.invoke_props_dialog(self)
-
-
-classes = (
- SmartProject,
-)
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..02bfebbdc0c 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,
@@ -159,6 +159,33 @@ class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator):
return self.execute(context)
+class VIEW3D_OT_edit_mesh_extrude_manifold_normal(Operator):
+ """Extrude manifold region along normals"""
+ bl_label = "Extrude Manifold Along Normals"
+ bl_idname = "view3d.edit_mesh_extrude_manifold_normal"
+
+ @classmethod
+ def poll(cls, context):
+ obj = context.active_object
+ return (obj is not None and obj.mode == 'EDIT')
+
+ def execute(self, context):
+ bpy.ops.mesh.extrude_manifold(
+ 'INVOKE_REGION_WIN',
+ MESH_OT_extrude_region={
+ "use_dissolve_ortho_edges": True,
+ },
+ TRANSFORM_OT_translate={
+ "orient_type": 'NORMAL',
+ "constraint_axis": (False, False, True),
+ },
+ )
+ return {'FINISHED'}
+
+ def invoke(self, context, _event):
+ return self.execute(context)
+
+
class VIEW3D_OT_transform_gizmo_set(Operator):
"""Set the current transform gizmo"""
bl_label = "Transform Gizmo Set"
@@ -208,5 +235,6 @@ classes = (
VIEW3D_OT_edit_mesh_extrude_individual_move,
VIEW3D_OT_edit_mesh_extrude_move,
VIEW3D_OT_edit_mesh_extrude_shrink_fatten,
+ VIEW3D_OT_edit_mesh_extrude_manifold_normal,
VIEW3D_OT_transform_gizmo_set,
)
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 4c4736cd669..570b4663f1d 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1450,6 +1450,10 @@ class WM_OT_properties_edit(Operator):
)
layout = self.layout
+
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
layout.prop(self, "property")
layout.prop(self, "value")
@@ -1457,22 +1461,21 @@ 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)
- row.prop(self, "min")
- row.prop(self, "max")
+ col = layout.column(align=True)
+ col.prop(self, "min")
+ col.prop(self, "max")
- row = layout.row()
- row.prop(self, "use_soft_limits")
- if bpy.app.use_override_library:
- row.prop(self, "is_overridable_library")
-
- row = layout.row(align=True)
- row.enabled = self.use_soft_limits
- row.prop(self, "soft_min", text="Soft Min")
- row.prop(self, "soft_max", text="Soft Max")
+ col = layout.column()
+ col.prop(self, "is_overridable_library")
+ col.prop(self, "use_soft_limits")
+
+ col = layout.column(align=True)
+ col.enabled = self.use_soft_limits
+ col.prop(self, "soft_min", text="Soft Min")
+ col.prop(self, "soft_max", text="Max")
layout.prop(self, "description")
if is_array and proptype == float:
@@ -1697,7 +1700,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 +2219,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 +2285,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 +2400,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..215c96a5975 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")
+
+ layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA')
- def LIMIT_ROTATION(self, _context, layout, con):
- split = layout.split()
+ self.draw_influence(layout, con)
- col = split.column(align=True)
- col.prop(con, "use_limit_x")
- sub = col.column(align=True)
+ 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")
+
+ 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, "mix_mode", text="Mix")
+
+ self.space_template(layout, con)
- 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")
+ self.draw_influence(layout, con)
- 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")
+ 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)
- 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")
+ 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, "owner_space")
- layout.prop(con, "mix_mode", text="Mix")
-
- 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)
-
- self.space_template(layout, con)
+ row.prop(con, "distance")
+ row.operator("constraint.limitdistance_reset", text="", icon="X")
- def RIGID_BODY_JOINT(self, _context, layout, con):
- self.target_template(layout, con, subtargets=False)
+ layout.prop(con, "limit_mode", text="Clamp Region")
- 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")
+ row.prop(con, "rest_length")
+ row.operator("constraint.stretchto_reset", text="", icon="X")
- def TRANSFORM(self, _context, layout, con):
- self.target_template(layout, con)
-
- 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)
+ 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")
- 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"
-
- 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")
+ row.active = not con.use_3d_position
+ row.prop(con, "use_undistorted_position")
- sub = row.column()
- sub.active = not con.use_3d_position
- sub.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,236 @@ 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.label(text="Constraint Properties:")
- box = layout.box()
+ layout.template_cache_file(con, "cache_file")
+
+ 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_from(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
+
+ from_axes = [con.map_to_x_from, con.map_to_y_from, con.map_to_z_from]
+
+ 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.active = "X" in from_axes
+ 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.active = "Y" in from_axes
+ 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.active = "Z" in from_axes
+ col.prop(con, "from_min_z" + ext, text="Z Min")
+ col.prop(con, "from_max_z" + ext, text="Max")
+
+ def draw_transform_to(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, "map_to_x_from", expand=False, text="X Source Axis")
+ col.prop(con, "to_min_x" + ext, text="Min")
+ col.prop(con, "to_max_x" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "map_to_y_from", expand=False, text="Y Source Axis")
+ col.prop(con, "to_min_y" + ext, text="Min")
+ col.prop(con, "to_max_y" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "map_to_z_from", expand=False, text="Z Source Axis")
+ col.prop(con, "to_min_z" + ext, text="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 +1056,597 @@ 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
+
+ row = box.row()
+ row.active = has_target and tgt.subtarget != ""
+ row.prop(tgt, "weight", slider=True, text="Weight")
- col = box.column()
- col.active = has_target and tgt.subtarget != ""
- col.prop(tgt, "weight", slider=True)
+ 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
- topcol.operator("constraint.normalize_target_weights")
- topcol.prop(con, "use_deform_preserve_volume")
- topcol.prop(con, "use_bone_envelopes")
+ col = layout.column()
+ col.prop(con, "chain_count")
+ col.prop(con, "use_even_divisions")
+ col.prop(con, "use_chain_offset")
- if context.pose_bone:
- topcol.prop(con, "use_current_location")
+ 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
+ layout.prop(con, "use_curve_radius")
-class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
- bl_label = "Object Constraints"
- bl_context = "constraint"
- bl_options = {'HIDE_HEADER'}
+ layout.prop(con, "y_scale_mode")
+ layout.prop(con, "xz_scale_mode")
- @classmethod
- def poll(cls, context):
- return (context.object)
+ if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}:
+ layout.prop(con, "use_original_scale")
- def draw(self, context):
+ 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")
- @classmethod
- def poll(cls, context):
- return (context.pose_bone)
+ col = layout.column(align=True)
+ col.prop(con, "frame_start", text="Frame Start")
+ col.prop(con, "frame_end", text="End")
+
+# 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 = "Map From"
+
+ def draw(self, context):
+ self.draw_transform_from(context)
+
+
+class BONE_PT_bTransformConstraint_from(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Map From"
+
+ def draw(self, context):
+ self.draw_transform_from(context)
+
+
+class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Map To"
+
+ def draw(self, context):
+ self.draw_transform_to(context)
+
+
+class BONE_PT_bTransformConstraint_to(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Map To"
+
+ def draw(self, context):
+ self.draw_transform_to(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_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_from,
+ BONE_PT_bTransformConstraint_to,
+ 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_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py
index ff4425fbb73..170d7910339 100644
--- a/release/scripts/startup/bl_ui/properties_data_bone.py
+++ b/release/scripts/startup/bl_ui/properties_data_bone.py
@@ -267,7 +267,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
if bone:
col = layout.column()
- col.prop(bone, "hide", text="Hide", toggle=0)
+ col.prop(bone, "hide", text="Hide", toggle=False)
class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel):
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..77308fed014 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -346,9 +346,11 @@ class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
enable_edit = ob.mode != 'EDIT'
enable_edit_value = False
+ enable_pin = False
- if ob.show_only_shape_key is False:
- if enable_edit or (ob.type == 'MESH' and ob.use_shape_key_edit_mode):
+ if enable_edit or (ob.use_shape_key_edit_mode and ob.type == 'MESH'):
+ enable_pin = True
+ if ob.show_only_shape_key:
enable_edit_value = True
row = layout.row()
@@ -386,7 +388,7 @@ class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
sub = row.row(align=True)
sub.label() # XXX, for alignment only
subsub = sub.row(align=True)
- subsub.active = enable_edit_value
+ subsub.active = enable_pin
subsub.prop(ob, "show_only_shape_key", text="")
sub.prop(ob, "use_shape_key_edit_mode", text="")
@@ -459,6 +461,35 @@ 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'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.preferences.experimental.use_sculpt_vertex_colors
+
+ 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 +514,9 @@ 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")
+ if context.preferences.experimental.use_sculpt_vertex_colors:
+ 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 +571,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_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py
index f70789ebeed..54b1ca3d910 100644
--- a/release/scripts/startup/bl_ui/properties_freestyle.py
+++ b/release/scripts/startup/bl_ui/properties_freestyle.py
@@ -115,6 +115,15 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel):
bl_label = "Freestyle"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
+ def draw_header(self, context):
+ view_layer = context.view_layer
+ rd = context.scene.render
+
+ layout = self.layout
+
+ layout.active = rd.use_freestyle
+ layout.prop(view_layer, "use_freestyle", text="")
+
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index 07c9fc363b5..f16528103ff 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -279,6 +279,31 @@ class GPENCIL_MT_snap(Menu):
layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
+class GPENCIL_MT_snap_pie(Menu):
+ bl_label = "Snap"
+
+ def draw(self, _context):
+ layout = self.layout
+ pie = layout.menu_pie()
+
+ pie.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid", icon='CURSOR')
+ pie.operator("gpencil.snap_to_grid", text="Selection to Grid", icon='RESTRICT_SELECT_OFF')
+ pie.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected", icon='CURSOR')
+ pie.operator(
+ "gpencil.snap_to_cursor",
+ text="Selection to Cursor",
+ icon='RESTRICT_SELECT_OFF'
+ ).use_offset = False
+ pie.operator(
+ "gpencil.snap_to_cursor",
+ text="Selection to Cursor (Keep Offset)",
+ icon='RESTRICT_SELECT_OFF'
+ ).use_offset = True
+ pie.separator()
+ pie.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin", icon='CURSOR')
+ pie.separator()
+
+
class GPENCIL_MT_move_to_layer(Menu):
bl_label = "Move to Layer"
@@ -380,6 +405,7 @@ class GPENCIL_MT_cleanup(Menu):
layout = self.layout
layout.operator("gpencil.frame_clean_loose", text="Delete Loose Points")
+ layout.operator("gpencil.frame_clean_duplicate", text="Delete Duplicated Frames")
if ob.mode != 'PAINT_GPENCIL':
layout.operator("gpencil.stroke_merge_by_distance", text="Merge by Distance")
@@ -901,6 +927,7 @@ class GreasePencilFlipTintColors(Operator):
classes = (
GPENCIL_MT_snap,
+ GPENCIL_MT_snap_pie,
GPENCIL_MT_cleanup,
GPENCIL_MT_move_to_layer,
GPENCIL_MT_layer_active,
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index 8ce53ed30eb..b142f6085fa 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -212,7 +212,6 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
-
obj = context.object
obj_type = obj.type
is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'})
@@ -237,10 +236,11 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel):
col.prop(obj, "show_in_front", text="In Front")
# if obj_type == 'MESH' or is_empty_image:
# col.prop(obj, "show_transparent", text="Transparency")
+ sub = layout.column()
if is_wire:
# wire objects only use the max. display type for duplis
- col.active = is_dupli
- col.prop(obj, "display_type", text="Display As")
+ sub.active = is_dupli
+ sub.prop(obj, "display_type", text="Display As")
if is_geometry or is_dupli or is_empty_image or is_gpencil:
# Only useful with object having faces/materials...
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..8cadad7e0d7 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -192,6 +192,10 @@ class ColorPalettePanel(BrushPanel):
elif context.vertex_paint_object:
capabilities = brush.vertex_paint_capabilities
return capabilities.has_color
+
+ elif context.sculpt_object:
+ capabilities = brush.sculpt_capabilities
+ return capabilities.has_color
return False
def draw(self, context):
@@ -537,10 +541,15 @@ def brush_settings(layout, context, brush, popover=False):
# Sculpt Mode #
if mode == 'SCULPT':
capabilities = brush.sculpt_capabilities
+ sculpt_tool = brush.sculpt_tool
# normal_radius_factor
layout.prop(brush, "normal_radius_factor", slider=True)
- layout.prop(brush, "hardness", slider=True)
+
+ row = layout.row(align=True)
+ row.prop(brush, "hardness", slider=True)
+ row.prop(brush, "invert_hardness_pressure", text = "")
+ row.prop(brush, "use_hardness_pressure", text = "")
# auto_smooth_factor and use_inverse_smooth_pressure
if capabilities.has_auto_smooth:
@@ -567,7 +576,7 @@ def brush_settings(layout, context, brush, popover=False):
# crease_pinch_factor
if capabilities.has_pinch_factor:
text = "Pinch"
- if brush.sculpt_tool in {'BLOB', 'SNAKE_HOOK'}:
+ if sculpt_tool in {'BLOB', 'SNAKE_HOOK'}:
text = "Magnify"
layout.prop(brush, "crease_pinch_factor", slider=True, text=text)
@@ -607,28 +616,43 @@ def brush_settings(layout, context, brush, popover=False):
layout.operator("sculpt.set_persistent_base")
layout.separator()
- if brush.sculpt_tool == 'CLAY_STRIPS':
+ 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")
+
+ # Per sculpt tool options.
+
+ if sculpt_tool == 'CLAY_STRIPS':
row = layout.row()
row.prop(brush, "tip_roundness")
- if brush.sculpt_tool == 'ELASTIC_DEFORM':
+ elif sculpt_tool == 'ELASTIC_DEFORM':
layout.separator()
layout.prop(brush, "elastic_deform_type")
layout.prop(brush, "elastic_deform_volume_preservation", slider=True)
layout.separator()
- if brush.sculpt_tool == 'POSE':
+ elif sculpt_tool == 'POSE':
layout.separator()
layout.prop(brush, "pose_deform_type")
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':
+ elif sculpt_tool == 'CLOTH':
layout.separator()
layout.prop(brush, "cloth_sim_limit")
layout.prop(brush, "cloth_sim_falloff")
@@ -638,30 +662,66 @@ def brush_settings(layout, context, brush, popover=False):
layout.separator()
layout.prop(brush, "cloth_mass")
layout.prop(brush, "cloth_damping")
+ layout.prop(brush, "cloth_constraint_softbody_strength")
layout.separator()
- if brush.sculpt_tool == 'SCRAPE':
+ elif sculpt_tool == 'SCRAPE':
row = layout.row()
row.prop(brush, "area_radius_factor", slider=True)
row = layout.row()
row.prop(brush, "invert_to_scrape_fill", text="Invert to Fill")
- if brush.sculpt_tool == 'FILL':
+ elif sculpt_tool == 'FILL':
row = layout.row()
row.prop(brush, "area_radius_factor", slider=True)
row = layout.row()
row.prop(brush, "invert_to_scrape_fill", text="Invert to Scrape")
- if brush.sculpt_tool == 'GRAB':
+ elif sculpt_tool == 'GRAB':
layout.prop(brush, "use_grab_active_vertex")
- if brush.sculpt_tool == 'MULTIPLANE_SCRAPE':
+ elif sculpt_tool == 'PAINT':
+ row = layout.row(align=True)
+ row.prop(brush, "flow")
+ row.prop(brush, "invert_flow_pressure", text = "")
+ row.prop(brush, "use_flow_pressure", text= "")
+
+ row = layout.row(align=True)
+ row.prop(brush, "wet_mix")
+ row.prop(brush, "invert_wet_mix_pressure", text = "")
+ row.prop(brush, "use_wet_mix_pressure", text = "")
+
+ row = layout.row(align=True)
+ row.prop(brush, "wet_persistence")
+ row.prop(brush, "invert_wet_persistence_pressure", text ="")
+ row.prop(brush, "use_wet_persistence_pressure", text= "")
+
+ row = layout.row(align=True)
+ row.prop(brush, "density")
+ row.prop(brush, "invert_density_pressure", text = "")
+ row.prop(brush, "use_density_pressure", text = "")
+
+ row = layout.row()
+ row.prop(brush, "tip_roundness")
+
+ row = layout.row()
+ row.prop(brush, "tip_scale_x")
+
+ elif sculpt_tool == 'SMEAR':
+ col = layout.column()
+ col.prop(brush, "smear_deform_type")
+
+ elif sculpt_tool == 'TOPOLOGY':
+ col = layout.column()
+ col.prop(brush, "slide_deform_type")
+
+ elif sculpt_tool == 'MULTIPLANE_SCRAPE':
col = layout.column()
col.prop(brush, "multiplane_scrape_angle")
col.prop(brush, "use_multiplane_scrape_dynamic")
col.prop(brush, "show_multiplane_scrape_planes_preview")
- if brush.sculpt_tool == 'SMOOTH':
+ elif sculpt_tool == 'SMOOTH':
col = layout.column()
col.prop(brush, "smooth_deform_type")
if brush.smooth_deform_type == 'SURFACE':
@@ -669,9 +729,11 @@ def brush_settings(layout, context, brush, popover=False):
col.prop(brush, "surface_smooth_current_vertex")
col.prop(brush, "surface_smooth_iterations")
- if brush.sculpt_tool == 'MASK':
+ elif sculpt_tool == 'MASK':
layout.row().prop(brush, "mask_tool", expand=True)
+ # End sculpt_tool interface.
+
# 3D and 2D Texture Paint Mode.
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
capabilities = brush.image_paint_capabilities
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index cc8fa511e42..f1dd6a1c890 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -136,13 +136,13 @@ class PARTICLE_UL_particle_systems(bpy.types.UIList):
if md:
row.prop(
md,
- "show_render",
+ "show_viewport",
emboss=False,
icon_only=True,
)
row.prop(
md,
- "show_viewport",
+ "show_render",
emboss=False,
icon_only=True,
)
diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py
index 0bf667482c4..79089b7cb89 100644
--- a/release/scripts/startup/bl_ui/properties_physics_cloth.py
+++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py
@@ -237,7 +237,10 @@ class PHYSICS_PT_cloth_pressure(PhysicButtonsPanel, Panel):
col.prop(cloth, "target_volume")
col = flow.column()
- col.prop(cloth, "pressure_factor", text="Factor")
+ col.prop(cloth, "pressure_factor")
+
+ col = flow.column()
+ col.prop(cloth, "fluid_density")
col = flow.column()
col.prop_search(cloth, "vertex_group_pressure", ob, "vertex_groups", text="Vertex Group")
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 0cd99efcca9..db33fda3b17 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -46,8 +46,8 @@ def physics_add(layout, md, name, type, typeicon, toggles):
icon='X',
)
if toggles:
- row.prop(md, "show_render", text="")
row.prop(md, "show_viewport", text="")
+ row.prop(md, "show_render", text="")
else:
row.operator(
"object.modifier_add",
@@ -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..8dd5b935922 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()
@@ -473,6 +473,7 @@ class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop(domain, "simulation_method", expand=False)
col.prop(domain, "flip_ratio", text="FLIP Ratio")
+ col.prop(domain, "sys_particle_maximum", text="System Maximum")
col = col.column(align=True)
col.prop(domain, "particle_radius", text="Particle Radius")
col.prop(domain, "particle_number", text="Sampling")
@@ -705,6 +706,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 +738,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 +779,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 +824,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 +858,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 +940,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 +1119,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="")
@@ -1132,49 +1136,47 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel):
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
- col.prop(domain, "cache_type", expand=False)
- col.enabled = not is_baking_any
-
- col.separator()
-
row = col.row()
- col = row.column(align=True)
- col.prop(domain, "cache_frame_start", text="Frame Start")
- col.prop(domain, "cache_frame_end", text="End")
- row.enabled = not is_baking_any
+ row = row.column(align=True)
+ row.prop(domain, "cache_frame_start", text="Frame Start")
+ row.prop(domain, "cache_frame_end", text="End")
+ row = col.row()
+ row.enabled = domain.cache_type in {'MODULAR', 'ALL'}
+ row.prop(domain, "cache_frame_offset", text="Offset")
col.separator()
col = flow.column()
+ col.prop(domain, "cache_type", expand=False)
+
row = col.row()
row.enabled = not is_baking_any and not has_baked_data
- row.prop(domain, "cache_data_format", text="Data File Format")
+ row.prop(domain, "cache_resumable", text="Is Resumable")
- 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 = col.row()
+ row.enabled = not is_baking_any and not has_baked_data
+ 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 +1191,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 +1205,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_physics_rigidbody.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
index b03f80bd600..a55bd89ca18 100644
--- a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
+++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
@@ -23,10 +23,10 @@ from bpy.types import (
)
-def rigid_body_warning(layout):
+def rigid_body_warning(layout, text):
row = layout.row(align=True)
row.alignment = 'RIGHT'
- row.label(text="Object does not have a Rigid Body")
+ row.label(text=text, icon='ERROR')
class PHYSICS_PT_rigidbody_panel:
@@ -49,13 +49,24 @@ class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel):
layout.use_property_split = True
ob = context.object
+ parent = ob.parent
rbo = ob.rigid_body
if rbo is None:
- rigid_body_warning(layout)
+ rigid_body_warning(layout, "Object does not have a Rigid Body")
return
- layout.prop(rbo, "type", text="Type")
+ if parent is not None and parent.rigid_body is not None:
+ if parent.rigid_body.collision_shape == 'COMPOUND':
+ row = layout.row(align=True)
+ row.alignment = 'RIGHT'
+ row.label(text="This object is part of a compound shape", icon='INFO')
+ else:
+ rigid_body_warning(layout, "Rigid Body can't be child of a non compound Rigid Body")
+ return
+
+ if parent is None or parent.rigid_body is None:
+ layout.prop(rbo, "type", text="Type")
class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel):
@@ -66,6 +77,8 @@ class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel):
@classmethod
def poll(cls, context):
obj = context.object
+ if obj.parent is not None and obj.parent.rigid_body is not None:
+ return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
@@ -76,7 +89,7 @@ class PHYSICS_PT_rigid_body_settings(PHYSICS_PT_rigidbody_panel, Panel):
rbo = ob.rigid_body
if rbo is None:
- rigid_body_warning(layout)
+ rigid_body_warning(layout, "Object does not have a Rigid Body")
return
col = layout.column()
@@ -96,17 +109,32 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel):
@classmethod
def poll(cls, context):
obj = context.object
+ if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND':
+ return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
ob = context.object
+ parent = ob.parent
rbo = ob.rigid_body
layout.use_property_split = True
layout.prop(rbo, "collision_shape", text="Shape")
+ if rbo.collision_shape == 'COMPOUND':
+ if parent is not None and parent.rigid_body is not None and parent.rigid_body.collision_shape == 'COMPOUND':
+ rigid_body_warning(layout, "Sub compound shapes are not allowed")
+ else:
+ found = False
+ for child in ob.children:
+ if child.rigid_body is not None:
+ found = True
+ break
+ if not found:
+ rigid_body_warning(layout, "There are no child rigid bodies")
+
if rbo.collision_shape in {'MESH', 'CONVEX_HULL'}:
layout.prop(rbo, "mesh_source", text="Source")
@@ -123,6 +151,8 @@ class PHYSICS_PT_rigid_body_collisions_surface(PHYSICS_PT_rigidbody_panel, Panel
@classmethod
def poll(cls, context):
obj = context.object
+ if obj.parent is not None and obj.parent.rigid_body is not None:
+ return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
@@ -149,6 +179,8 @@ class PHYSICS_PT_rigid_body_collisions_sensitivity(PHYSICS_PT_rigidbody_panel, P
@classmethod
def poll(cls, context):
obj = context.object
+ if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND':
+ return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
@@ -180,6 +212,8 @@ class PHYSICS_PT_rigid_body_collisions_collections(PHYSICS_PT_rigidbody_panel, P
@classmethod
def poll(cls, context):
obj = context.object
+ if obj.parent is not None and obj.parent.rigid_body is not None:
+ return False
return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES))
def draw(self, context):
@@ -200,6 +234,8 @@ class PHYSICS_PT_rigid_body_dynamics(PHYSICS_PT_rigidbody_panel, Panel):
@classmethod
def poll(cls, context):
obj = context.object
+ if obj.parent is not None and obj.parent.rigid_body is not None:
+ return False
return (obj and obj.rigid_body and obj.rigid_body.type == 'ACTIVE'
and (context.engine in cls.COMPAT_ENGINES))
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_clip.py b/release/scripts/startup/bl_ui/space_clip.py
index 5b6cc6609e0..43883ff0f3a 100644
--- a/release/scripts/startup/bl_ui/space_clip.py
+++ b/release/scripts/startup/bl_ui/space_clip.py
@@ -183,7 +183,7 @@ class CLIP_HT_header(Header):
r = active_object.reconstruction
if r.is_valid and sc.view == 'CLIP':
- layout.label(text="Solve error: %.4f" %
+ layout.label(text="Solve error: %.2f px" %
(r.average_error))
row = layout.row()
@@ -741,7 +741,7 @@ class CLIP_PT_track(CLIP_PT_tracking_panel, Panel):
layout.prop(act_track, "weight_stab")
if act_track.has_bundle:
- label_text = "Average Error: %.4f" % (act_track.average_error)
+ label_text = "Average Error: %.2f px" % (act_track.average_error)
layout.label(text=label_text)
layout.use_property_split = False
@@ -1238,7 +1238,7 @@ class CLIP_MT_view_zoom(Menu):
layout.operator(
"clip.view_zoom_ratio",
- text=iface_(f"Zoom {a:d}:{b:d}"),
+ text=iface_("Zoom %d:%d") % (a, b),
translate=False,
).ratio = a / b
@@ -1578,7 +1578,7 @@ class CLIP_MT_marker_pie(Menu):
layout = self.layout
pie = layout.menu_pie()
# Use Location Tracking
- prop = pie.operator("wm.context_set_enum", text="Loc")
+ prop = pie.operator("wm.context_set_enum", text="Location")
prop.data_path = "space_data.clip.tracking.tracks.active.motion_model"
prop.value = "Loc"
# Use Affine Tracking
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 9a39d840149..257ef420ef9 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -97,16 +97,14 @@ class FILEBROWSER_PT_filter(Panel):
params = space.params
is_lib_browser = params.use_library_browsing
- row = layout.row(align=True)
- row.prop(params, "use_filter", text="", toggle=0)
- row.label(text="Filter")
+ layout.prop(params, "use_filter", text="Filter", toggle=False)
col = layout.column()
col.active = params.use_filter
row = col.row()
row.label(icon='FILE_FOLDER')
- row.prop(params, "use_filter_folder", text="Folders", toggle=0)
+ row.prop(params, "use_filter_folder", text="Folders", toggle=False)
if params.filter_glob:
col.label(text=params.filter_glob)
@@ -114,33 +112,33 @@ class FILEBROWSER_PT_filter(Panel):
row = col.row()
row.label(icon='FILE_BLEND')
row.prop(params, "use_filter_blender",
- text=".blend Files", toggle=0)
+ text=".blend Files", toggle=False)
row = col.row()
row.label(icon='FILE_BACKUP')
row.prop(params, "use_filter_backup",
- text="Backup .blend Files", toggle=0)
+ text="Backup .blend Files", toggle=False)
row = col.row()
row.label(icon='FILE_IMAGE')
- row.prop(params, "use_filter_image", text="Image Files", toggle=0)
+ row.prop(params, "use_filter_image", text="Image Files", toggle=False)
row = col.row()
row.label(icon='FILE_MOVIE')
- row.prop(params, "use_filter_movie", text="Movie Files", toggle=0)
+ row.prop(params, "use_filter_movie", text="Movie Files", toggle=False)
row = col.row()
row.label(icon='FILE_SCRIPT')
row.prop(params, "use_filter_script",
- text="Script Files", toggle=0)
+ text="Script Files", toggle=False)
row = col.row()
row.label(icon='FILE_FONT')
- row.prop(params, "use_filter_font", text="Font Files", toggle=0)
+ row.prop(params, "use_filter_font", text="Font Files", toggle=False)
row = col.row()
row.label(icon='FILE_SOUND')
- row.prop(params, "use_filter_sound", text="Sound Files", toggle=0)
+ row.prop(params, "use_filter_sound", text="Sound Files", toggle=False)
row = col.row()
row.label(icon='FILE_TEXT')
- row.prop(params, "use_filter_text", text="Text Files", toggle=0)
+ row.prop(params, "use_filter_text", text="Text Files", toggle=False)
row = col.row()
row.label(icon='FILE_VOLUME')
- row.prop(params, "use_filter_volume", text="Volume Files", toggle=0)
+ row.prop(params, "use_filter_volume", text="Volume Files", toggle=False)
col.separator()
@@ -148,7 +146,7 @@ class FILEBROWSER_PT_filter(Panel):
row = col.row()
row.label(icon='BLANK1') # Indentation
row.prop(params, "use_filter_blendid",
- text="Blender IDs", toggle=0)
+ text="Blender IDs", toggle=False)
if params.use_filter_blendid:
row = col.row()
row.label(icon='BLANK1') # Indentation
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 76b7fc7f156..75c1bb5e3f9 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -176,7 +176,7 @@ class IMAGE_MT_select(Menu):
layout.separator()
layout.operator("uv.select_pinned")
- layout.operator("uv.select_linked")
+ layout.menu("IMAGE_MT_select_linked")
layout.separator()
@@ -184,6 +184,16 @@ class IMAGE_MT_select(Menu):
layout.operator("uv.select_overlap")
+class IMAGE_MT_select_linked(Menu):
+ bl_label = "Select Linked"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("uv.select_linked", text="Linked")
+ layout.operator("uv.shortest_path_select", text="Shortest Path")
+
+
class IMAGE_MT_image(Menu):
bl_label = "Image"
@@ -321,15 +331,37 @@ class IMAGE_MT_uvs_mirror(Menu):
layout.operator("transform.mirror", text="Y Axis").constraint_axis[1] = True
-class IMAGE_MT_uvs_weldalign(Menu):
- bl_label = "Weld/Align"
+class IMAGE_MT_uvs_align(Menu):
+ bl_label = "Align"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator_enum("uv.align", "axis")
+
+
+class IMAGE_MT_uvs_merge(Menu):
+ bl_label = "Merge"
def draw(self, _context):
layout = self.layout
- layout.operator("uv.weld") # W, 1.
- layout.operator("uv.remove_doubles")
- layout.operator_enum("uv.align", "axis") # W, 2/3/4.
+ layout.operator("uv.weld", text="At Center")
+ # Mainly to match the mesh menu.
+ layout.operator("uv.snap_selected", text="At Cursor").target = 'CURSOR'
+
+ layout.separator()
+
+ layout.operator("uv.remove_doubles", text="By Distance")
+
+
+class IMAGE_MT_uvs_split(Menu):
+ bl_label = "Split"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("uv.select_split", text="Selection")
class IMAGE_MT_uvs(Menu):
@@ -350,6 +382,11 @@ class IMAGE_MT_uvs(Menu):
layout.separator()
+ layout.menu("IMAGE_MT_uvs_merge")
+ layout.menu("IMAGE_MT_uvs_split")
+
+ layout.separator()
+
layout.prop(uv, "use_live_unwrap")
layout.operator("uv.unwrap")
@@ -373,7 +410,7 @@ class IMAGE_MT_uvs(Menu):
layout.operator("uv.minimize_stretch")
layout.operator("uv.stitch")
- layout.menu("IMAGE_MT_uvs_weldalign")
+ layout.menu("IMAGE_MT_uvs_align")
layout.separator()
@@ -462,7 +499,7 @@ class IMAGE_MT_uvs_context_menu(Menu):
layout.separator()
# Remove
- layout.operator("uv.remove_doubles", text="Remove Double UVs")
+ layout.operator("uv.remove_doubles", text="Merge By Distance")
layout.operator("uv.stitch")
layout.operator("uv.weld")
@@ -662,7 +699,12 @@ class IMAGE_HT_header(Header):
# Proportional Editing
row = layout.row(align=True)
- row.prop(tool_settings, "use_proportional_edit", icon_only=True)
+ row.prop(
+ tool_settings,
+ "use_proportional_edit",
+ icon_only=True,
+ icon='PROP_CON' if tool_settings.use_proportional_connected else 'PROP_ON',
+ )
sub = row.row(align=True)
sub.active = tool_settings.use_proportional_edit
sub.prop_with_popover(
@@ -1452,6 +1494,7 @@ classes = (
IMAGE_MT_view,
IMAGE_MT_view_zoom,
IMAGE_MT_select,
+ IMAGE_MT_select_linked,
IMAGE_MT_image,
IMAGE_MT_image_invert,
IMAGE_MT_uvs,
@@ -1459,7 +1502,9 @@ classes = (
IMAGE_MT_uvs_transform,
IMAGE_MT_uvs_snap,
IMAGE_MT_uvs_mirror,
- IMAGE_MT_uvs_weldalign,
+ IMAGE_MT_uvs_align,
+ IMAGE_MT_uvs_merge,
+ IMAGE_MT_uvs_split,
IMAGE_MT_uvs_select_mode,
IMAGE_MT_uvs_context_menu,
IMAGE_MT_mask_context_menu,
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index ee8015df273..5a54d4ca2d8 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -107,6 +107,14 @@ class OUTLINER_MT_editor_menus(Menu):
class OUTLINER_MT_context_menu(Menu):
bl_label = "Outliner Context Menu"
+ @staticmethod
+ def draw_common_operators(layout):
+ layout.menu("OUTLINER_MT_context_menu_view")
+
+ layout.separator()
+
+ layout.menu("INFO_MT_area")
+
def draw(self, context):
space = context.space_data
@@ -116,11 +124,7 @@ class OUTLINER_MT_context_menu(Menu):
OUTLINER_MT_collection_new.draw_without_context_menu(context, layout)
layout.separator()
- layout.menu("OUTLINER_MT_context_menu_view")
-
- layout.separator()
-
- layout.menu("INFO_MT_area")
+ OUTLINER_MT_context_menu.draw_common_operators(layout)
class OUTLINER_MT_context_menu_view(Menu):
@@ -213,7 +217,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()
@@ -242,7 +246,7 @@ class OUTLINER_MT_collection(Menu):
layout.separator()
- OUTLINER_MT_context_menu.draw(self, context)
+ OUTLINER_MT_context_menu.draw_common_operators(layout)
class OUTLINER_MT_collection_new(Menu):
@@ -250,7 +254,7 @@ class OUTLINER_MT_collection_new(Menu):
@staticmethod
def draw_without_context_menu(context, layout):
- layout.operator("outliner.collection_new", text="New Collection").nested = False
+ layout.operator("outliner.collection_new", text="New Collection").nested = True
layout.operator("outliner.id_paste", text="Paste Data-Blocks", icon='PASTEDOWN')
def draw(self, context):
@@ -260,7 +264,7 @@ class OUTLINER_MT_collection_new(Menu):
layout.separator()
- OUTLINER_MT_context_menu.draw(self, context)
+ OUTLINER_MT_context_menu.draw_common_operators(layout)
class OUTLINER_MT_object(Menu):
@@ -279,9 +283,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()
@@ -305,11 +307,15 @@ class OUTLINER_MT_object(Menu):
layout.operator("outliner.id_operation", text="Unlink").type = 'UNLINK'
layout.separator()
+ layout.operator("outliner.collection_new", text="New Collection").nested = True
+
+ layout.separator()
+
layout.operator_menu_enum("outliner.id_operation", "type", text="ID Data")
layout.separator()
- OUTLINER_MT_context_menu.draw(self, context)
+ OUTLINER_MT_context_menu.draw_common_operators(layout)
class OUTLINER_PT_filter(Panel):
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 26a150f83ef..8dfa182f52d 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -176,17 +176,6 @@ class SEQUENCER_MT_editor_menus(Menu):
layout.menu("SEQUENCER_MT_strip")
-class SEQUENCER_MT_view_toggle(Menu):
- bl_label = "View Type"
-
- def draw(self, _context):
- layout = self.layout
-
- layout.operator("sequencer.view_toggle").type = 'SEQUENCER'
- layout.operator("sequencer.view_toggle").type = 'PREVIEW'
- layout.operator("sequencer.view_toggle").type = 'SEQUENCER_PREVIEW'
-
-
class SEQUENCER_MT_view_cache(Menu):
bl_label = "Cache"
@@ -357,6 +346,20 @@ class SEQUENCER_MT_view(Menu):
layout.separator()
+ # Note that the context is needed for the shortcut to display properly.
+ layout.operator_context = 'INVOKE_REGION_PREVIEW' if is_preview else 'INVOKE_REGION_WIN'
+ props = layout.operator(
+ "wm.context_toggle_enum",
+ text="Toggle Sequencer/Preview",
+ icon='SEQ_SEQUENCER' if is_preview else 'SEQ_PREVIEW',
+ )
+ props.data_path = "space_data.view_type"
+ props.value_1 = 'SEQUENCER'
+ props.value_2 = 'PREVIEW'
+ layout.operator_context = 'INVOKE_DEFAULT'
+
+ layout.separator()
+
layout.menu("INFO_MT_area")
@@ -471,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()
@@ -632,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()
@@ -728,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)
@@ -1525,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) + " ")
@@ -1566,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) + " ")
@@ -2201,7 +2204,6 @@ classes = (
SEQUENCER_MT_range,
SEQUENCER_MT_view,
SEQUENCER_MT_view_cache,
- SEQUENCER_MT_view_toggle,
SEQUENCER_MT_preview_zoom,
SEQUENCER_MT_proxy,
SEQUENCER_MT_select_handle,
diff --git a/release/scripts/startup/bl_ui/space_statusbar.py b/release/scripts/startup/bl_ui/space_statusbar.py
index 4984372eed3..cbf72a7bc59 100644
--- a/release/scripts/startup/bl_ui/space_statusbar.py
+++ b/release/scripts/startup/bl_ui/space_statusbar.py
@@ -31,17 +31,20 @@ class STATUSBAR_HT_header(Header):
layout.separator_spacer()
- # messages
- layout.template_reports_banner()
- layout.template_running_jobs()
-
+ # Nothing in the center.
layout.separator_spacer()
- # stats
- scene = context.scene
- view_layer = context.view_layer
+ row = layout.row()
+ row.alignment = 'RIGHT'
+
+ # Stats & Info
+ row.label(text=context.screen.statusbar_info(), translate=False)
+
+ # Messages
+ row.template_reports_banner()
- layout.label(text=scene.statistics(view_layer), translate=False)
+ # Progress Bar
+ row.template_running_jobs()
classes = (
diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py
index f6f22ad464f..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()
@@ -400,7 +403,7 @@ class TEXT_MT_edit(Menu):
layout.separator()
layout.operator("text.start_find", text="Find & Replace...")
- layout.operator("text.find_set_selected", text="Find Next")
+ layout.operator("text.find_set_selected")
layout.operator("text.jump", text="Jump To...")
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..50316f50474 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
@@ -271,19 +271,24 @@ class ToolSelectPanelHelper:
yield item, i
i += 1
- # Special internal function, gives use items that contain keymaps.
@staticmethod
- def _tools_flatten_with_keymap(tools):
+ def _tools_flatten_with_dynamic(tools, *, context):
+ """
+ Expands dynamic items, indices aren't aligned with other flatten functions.
+ The context may be None, use as signal to return all items.
+ """
for item_parent in tools:
if item_parent is None:
- continue
+ yield None
for item in item_parent if (type(item_parent) is tuple) else (item_parent,):
- # skip None or generator function
- if item is None or _item_is_fn(item):
- continue
- if item.keymap is not None:
+ if item is None:
+ yield None
+ elif _item_is_fn(item):
+ yield from ToolSelectPanelHelper._tools_flatten_with_dynamic(item(context), context=context)
+ else:
yield item
+
@classmethod
def _tool_get_active(cls, context, space_type, mode, with_icon=False):
"""
@@ -450,7 +455,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:
@@ -484,8 +489,12 @@ class ToolSelectPanelHelper:
else:
context_descr = context_mode.replace("_", " ").title()
- for item in cls._tools_flatten_with_keymap(tools):
+ for item in cls._tools_flatten_with_dynamic(tools, context=None):
+ if item is None:
+ continue
keymap_data = item.keymap
+ if keymap_data is None:
+ continue
if callable(keymap_data[0]):
cls._km_action_simple(kc_default, kc_default, context_descr, item.label, keymap_data)
@@ -498,8 +507,13 @@ class ToolSelectPanelHelper:
for context_mode_test, tools in cls.tools_all():
if context_mode_test == context_mode:
- for item in cls._tools_flatten_with_keymap(tools):
- km_name = item.keymap[0]
+ for item in cls._tools_flatten(tools):
+ if item is None:
+ continue
+ keymap_data = item.keymap
+ if keymap_data is None:
+ continue
+ km_name = keymap_data[0]
# print((km.name, cls.bl_space_type, 'WINDOW', []))
if km_name in visited:
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 2c3108e0a11..83b3a27154a 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -50,11 +50,14 @@ def generate_from_enum_ex(
attr,
cursor='DEFAULT',
tooldef_keywords={},
+ exclude_filter = {}
):
tool_defs = []
for enum in type.bl_rna.properties[attr].enum_items_static:
name = enum.name
idname = enum.identifier
+ if idname in exclude_filter:
+ continue
tool_defs.append(
ToolDef.from_dict(
dict(
@@ -124,12 +127,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),
)
@@ -452,6 +455,11 @@ class _defs_view3d_add:
row = layout.row()
row.prop(props, "plane_axis", text="")
row = layout.row()
+ row.scale_x = 0.8
+ row.label(text="Orientation:")
+ row = layout.row()
+ row.prop(props, "plane_orientation", text="")
+ row = layout.row()
row.scale_x = 0.7
row.prop(props, "plane_origin")
@@ -482,7 +490,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,40 +746,66 @@ 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'
+
+ edge_bevel = props.affect == 'EDGES'
if not extra:
- if region_type == 'TOOL_HEADER':
+ if region_is_header:
layout.prop(props, "offset_type", text="")
else:
+ layout.row().prop(props, "affect", expand=True)
+ layout.separator()
layout.prop(props, "offset_type")
layout.prop(props, "segments")
- layout.prop(props, "profile", slider=True)
- if region_type == 'TOOL_HEADER':
+ if region_is_header:
+ layout.prop(props, "affect", text="")
+
+ 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
+
+ layout.prop(props, "material")
+
+ col = layout.column()
+ col.prop(props, "harden_normals")
+ col.prop(props, "clamp_overlap")
+ col.prop(props, "loop_slide")
+
col = layout.column(heading="Mark")
+ col.active = edge_bevel
col.prop(props, "mark_seam", text="Seam")
col.prop(props, "mark_sharp", text="Sharp")
- layout.prop(props, "material")
- layout.prop(props, "miter_outer", text="Outer Miter")
- layout.prop(props, "miter_inner", text="Inner Miter")
+ col = layout.column()
+ col.active = edge_bevel
+ col.prop(props, "miter_outer", text="Miter Outer")
+ col.prop(props, "miter_inner", text="Inner")
if props.miter_inner == 'ARC':
- layout.prop(props, "spread")
+ col.prop(props, "spread")
- layout.prop(props, "use_custom_profile")
- if props.use_custom_profile:
+ layout.separator()
+
+ col = layout.column()
+ col.active = edge_bevel
+ col.prop(props, "vmesh_method", text="Intersections")
+
+ layout.prop(props, "face_strength_mode", text="Face Strength")
+
+ layout.prop(props, "profile_type")
+
+ if props.profile_type == 'CUSTOM':
tool_settings = context.tool_settings
layout.template_curveprofile(tool_settings, "custom_bevel_profile_preset")
@@ -802,14 +836,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=(),
)
@@ -1166,12 +1200,18 @@ class _defs_sculpt:
@staticmethod
def generate_from_brushes(context):
+ if bpy.context.preferences.experimental.use_sculpt_vertex_colors:
+ exclude_filter = {}
+ else:
+ exclude_filter = {'PAINT', 'SMEAR'}
+
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.sculpt.",
type=bpy.types.Brush,
attr="sculpt_tool",
+ exclude_filter = exclude_filter,
)
@ToolDef.from_fn
@@ -1212,10 +1252,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 +1286,42 @@ 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,
+ )
+
+ @ToolDef.from_fn
+ def mask_by_color():
+ def draw_settings(_context, layout, tool):
+ props = tool.operator_properties("sculpt.mask_by_color")
+ layout.prop(props, "threshold")
+ layout.prop(props, "contiguous")
+ layout.prop(props, "invert")
+ layout.prop(props, "preserve_previous_mask")
+
+ return dict(
+ idname="builtin.mask_by_color",
+ label="Mask By Color",
+ icon="ops.sculpt.mask_by_color",
+ widget=None,
+ keymap=(),
+ draw_settings=draw_settings,
+ )
+
class _defs_vertex_paint:
@@ -1531,6 +1607,20 @@ class _defs_image_uv_select:
)
+class _defs_image_uv_edit:
+
+ @ToolDef.from_fn
+ def rip_region():
+ return dict(
+ idname="builtin.rip_region",
+ label="Rip Region",
+ icon="ops.mesh.rip",
+ # TODO: generic operator (UV version of `VIEW3D_GGT_tool_generic_handle_free`).
+ widget=None,
+ keymap=(),
+ )
+
+
class _defs_image_uv_sculpt:
@staticmethod
@@ -2125,6 +2215,8 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
*_tools_annotate,
None,
+ _defs_image_uv_edit.rip_region,
+ None,
lambda context: (
_defs_image_uv_sculpt.generate_from_brushes(context)
if _defs_image_generic.poll_uvedit(context)
@@ -2327,7 +2419,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 +2513,21 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_sculpt.mesh_filter,
_defs_sculpt.cloth_filter,
+ lambda context: (
+ (_defs_sculpt.color_filter,)
+ if context is None or (
+ context.preferences.view.show_developer_ui and
+ context.preferences.experimental.use_sculpt_vertex_colors)
+ else ()
+ ),
+ None,
+ lambda context: (
+ (_defs_sculpt.mask_by_color,)
+ if context is None or (
+ context.preferences.view.show_developer_ui and
+ context.preferences.experimental.use_sculpt_vertex_colors)
+ else ()
+ ),
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..03f85578b6e 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):
@@ -283,6 +283,22 @@ class USERPREF_PT_interface_temporary_windows(InterfacePanel, CenterAlignMixIn,
col.prop(view, "filebrowser_display_type", text="File Browser")
+class USERPREF_PT_interface_statusbar(InterfacePanel, CenterAlignMixIn, Panel):
+ bl_label = "Status Bar"
+ bl_parent_id = "USERPREF_PT_interface_editors"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_centered(self, context, layout):
+ prefs = context.preferences
+ view = prefs.view
+
+ col = layout.column(heading="Show")
+ col.prop(view, "show_statusbar_stats", text="Scene Statistics")
+ col.prop(view, "show_statusbar_memory", text="System Memory")
+ col.prop(view, "show_statusbar_vram", text="Video Memory")
+ col.prop(view, "show_statusbar_version", text="Blender Version")
+
+
class USERPREF_PT_interface_menus(InterfacePanel, Panel):
bl_label = "Menus"
bl_options = {'DEFAULT_CLOSED'}
@@ -994,7 +1010,7 @@ class USERPREF_PT_theme_bone_color_sets(ThemePanel, CenterAlignMixIn, Panel):
layout.use_property_split = True
for i, ui in enumerate(theme.bone_color_sets, 1):
- layout.label(text=iface_(f"Color Set {i:d}"), translate=False)
+ layout.label(text=iface_("Color Set %d") % i, translate=False)
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
@@ -1903,8 +1919,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 +2005,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 +2130,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 +2152,37 @@ 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"),
+ ({"property": "use_sculpt_vertex_colors"}, "T71947"),
+ ),
+ )
+
+
+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),
),
)
@@ -2161,6 +2205,7 @@ classes = (
USERPREF_PT_interface_display,
USERPREF_PT_interface_editors,
USERPREF_PT_interface_temporary_windows,
+ USERPREF_PT_interface_statusbar,
USERPREF_PT_interface_translation,
USERPREF_PT_interface_text,
USERPREF_PT_interface_menus,
@@ -2233,7 +2278,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..0a1be2dc698 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
@@ -1079,9 +1084,9 @@ class VIEW3D_MT_mirror(Menu):
for (space_name, space_id) in (("Global", 'GLOBAL'), ("Local", 'LOCAL')):
for axis_index, axis_name in enumerate("XYZ"):
- props = layout.operator("transform.mirror", text=f"{axis_name!s} {space_name!s}")
+ props = layout.operator("transform.mirror", text="%s %s" % (axis_name, space_name))
props.constraint_axis[axis_index] = True
- props.orient_type = 'GLOBAL'
+ props.orient_type = space_id
if space_id == 'GLOBAL':
layout.separator()
@@ -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')
@@ -2251,8 +2256,7 @@ class VIEW3D_MT_object_relations(Menu):
layout.operator("object.proxy_make", text="Make Proxy...")
- if bpy.app.use_override_library:
- layout.operator("object.make_override_library", text="Make Library Override...")
+ layout.operator("object.make_override_library", text="Make Library Override...")
layout.operator("object.make_dupli_face")
@@ -2348,6 +2352,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):
@@ -2746,6 +2751,8 @@ class VIEW3D_MT_object_quick_effects(Menu):
layout.operator("object.quick_explode")
layout.operator("object.quick_smoke")
layout.operator("object.quick_liquid")
+ if _context.preferences.experimental.use_new_particle_system:
+ layout.operator("object.quick_particles")
class VIEW3D_MT_object_showhide(Menu):
@@ -3692,7 +3699,7 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu):
col.separator()
col.operator("mesh.extrude_vertices_move", text="Extrude Vertices")
- col.operator("mesh.bevel", text="Bevel Vertices").vertex_only = True
+ col.operator("mesh.bevel", text="Bevel Vertices").affect = 'VERTICES'
if selected_verts_len > 1:
col.separator()
@@ -3741,7 +3748,7 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu):
col.separator()
col.operator("mesh.extrude_edges_move", text="Extrude Edges")
- col.operator("mesh.bevel", text="Bevel Edges").vertex_only = False
+ col.operator("mesh.bevel", text="Bevel Edges").affect = 'EDGES'
if selected_edges_len >= 2:
col.operator("mesh.bridge_edge_loops")
if selected_edges_len >= 1:
@@ -3871,6 +3878,8 @@ class VIEW3D_MT_edit_mesh_extrude(Menu):
layout.operator("view3d.edit_mesh_extrude_move_shrink_fatten", text="Extrude Faces Along Normals"),
'FACE': lambda layout:
layout.operator("mesh.extrude_faces_move", text="Extrude Individual Faces"),
+ 'MANIFOLD': lambda layout:
+ layout.operator("view3d.edit_mesh_extrude_manifold_normal", text="Extrude Manifold"),
}
@staticmethod
@@ -3881,7 +3890,7 @@ class VIEW3D_MT_edit_mesh_extrude(Menu):
menu = []
if mesh.total_face_sel:
- menu += ['REGION', 'REGION_VERT_NORMAL', 'FACE']
+ menu += ['REGION', 'REGION_VERT_NORMAL', 'FACE', 'MANIFOLD']
if mesh.total_edge_sel and (select_mode[0] or select_mode[1]):
menu += ['EDGE']
if mesh.total_vert_sel and select_mode[0]:
@@ -3913,7 +3922,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.extrude_vertices_move", text="Extrude Vertices")
- layout.operator("mesh.bevel", text="Bevel Vertices").vertex_only = True
+ layout.operator("mesh.bevel", text="Bevel Vertices").affect = 'VERTICES'
layout.separator()
@@ -3998,7 +4007,7 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.extrude_edges_move", text="Extrude Edges")
- layout.operator("mesh.bevel", text="Bevel Edges").vertex_only = False
+ layout.operator("mesh.bevel", text="Bevel Edges").affect = 'EDGES'
layout.operator("mesh.bridge_edge_loops")
layout.operator("mesh.screw")
@@ -4016,6 +4025,8 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
layout.separator()
layout.operator("transform.edge_slide")
+ props = layout.operator("mesh.loopcut_slide")
+ props.TRANSFORM_OT_edge_slide.release_confirm = False
layout.operator("mesh.offset_edge_loops_slide")
layout.separator()
@@ -5620,8 +5631,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'
@@ -6144,7 +6155,7 @@ class VIEW3D_PT_overlay_motion_tracking(Panel):
def draw_header(self, context):
view = context.space_data
- self.layout.prop(view, "show_reconstruction", text="")
+ self.layout.prop(view, "show_reconstruction", text=self.bl_label)
def draw(self, context):
layout = self.layout
@@ -6786,7 +6797,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):
@@ -6984,7 +6998,7 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu):
col.separator()
col.menu("VIEW3D_MT_mirror", text="Mirror Points")
- col.menu("VIEW3D_MT_snap", text="Snap Points")
+ col.menu("GPENCIL_MT_snap", text="Snap Points")
col.separator()
@@ -7329,6 +7343,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..25dc4663500 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,19 @@ 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(align=True)
+ row.active = tool_settings.use_transform_correct_face_attributes
+ row.prop(tool_settings, "use_transform_correct_keep_connected")
+
row = layout.row(heading="Mirror")
sub = row.row(align=True)
sub.prop(mesh, "use_mirror_x", text="X", toggle=True)
@@ -732,7 +741,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
@classmethod
def poll(cls, context):
- return (context.sculpt_object and context.tool_settings.sculpt)
+ paint_settings = cls.paint_settings(context)
+ return (context.sculpt_object and context.tool_settings.sculpt and paint_settings)
def draw_header(self, context):
is_popover = self.is_popover
@@ -806,6 +816,9 @@ 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")
+ if context.preferences.experimental.use_sculpt_vertex_colors:
+ col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
+
layout.operator("object.voxel_remesh", text="Remesh")
@@ -829,7 +842,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")
@@ -1381,6 +1393,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
col = layout.column(align=True)
if brush is not None:
+ col.prop(gp_settings, "brush_draw_mode")
+ col.separator()
+
if brush.gpencil_tool != 'FILL':
col.prop(gp_settings, "input_samples")
col.separator()
@@ -1694,7 +1709,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 +1783,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 +1858,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/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index bebd10edde3..19d565d10b6 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -85,7 +85,7 @@ class BUILTIN_KSI_Rotation(KeyingSetInfo):
class BUILTIN_KSI_Scaling(KeyingSetInfo):
"""Insert a keyframe on each of the scale channels"""
bl_idname = ANIM_KS_SCALING_ID
- bl_label = "Scaling"
+ bl_label = "Scale"
# poll - use predefined callback for selected bones/objects
poll = keyingsets_utils.RKS_POLL_selected_items
@@ -102,7 +102,7 @@ class BUILTIN_KSI_Scaling(KeyingSetInfo):
# LocRot
class BUILTIN_KSI_LocRot(KeyingSetInfo):
"""Insert a keyframe on each of the location and rotation channels"""
- bl_label = "LocRot"
+ bl_label = "Location & Rotation"
# poll - use predefined callback for selected bones/objects
poll = keyingsets_utils.RKS_POLL_selected_items
@@ -121,7 +121,7 @@ class BUILTIN_KSI_LocRot(KeyingSetInfo):
# LocScale
class BUILTIN_KSI_LocScale(KeyingSetInfo):
"""Insert a keyframe on each of the location and scale channels"""
- bl_label = "LocScale"
+ bl_label = "Location & Scale"
# poll - use predefined callback for selected bones/objects
poll = keyingsets_utils.RKS_POLL_selected_items
@@ -141,7 +141,7 @@ class BUILTIN_KSI_LocScale(KeyingSetInfo):
class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
"""Insert a keyframe on each of the location, rotation, and scale channels"""
bl_idname = ANIM_KS_LOC_ROT_SCALE_ID
- bl_label = "LocRotScale"
+ bl_label = "Location, Rotation & Scale"
# poll - use predefined callback for selected bones/objects
poll = keyingsets_utils.RKS_POLL_selected_items
@@ -162,7 +162,7 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
# RotScale
class BUILTIN_KSI_RotScale(KeyingSetInfo):
"""Insert a keyframe on each of the rotation and scale channels"""
- bl_label = "RotScale"
+ bl_label = "Rotation & Scale"
# poll - use predefined callback for selected bones/objects
poll = keyingsets_utils.RKS_POLL_selected_items
@@ -237,7 +237,7 @@ class BUILTIN_KSI_VisualRot(KeyingSetInfo):
class BUILTIN_KSI_VisualScaling(KeyingSetInfo):
"""Insert a keyframe on each of the scale channels, """ \
"""taking into account effects of constraints and relationships"""
- bl_label = "Visual Scaling"
+ bl_label = "Visual Scale"
bl_options = {'INSERTKEY_VISUAL'}
@@ -255,7 +255,7 @@ class BUILTIN_KSI_VisualScaling(KeyingSetInfo):
class BUILTIN_KSI_VisualLocRot(KeyingSetInfo):
"""Insert a keyframe on each of the location and rotation channels, """ \
"""taking into account effects of constraints and relationships"""
- bl_label = "Visual LocRot"
+ bl_label = "Visual Location & Rotation"
bl_options = {'INSERTKEY_VISUAL'}
@@ -275,9 +275,9 @@ class BUILTIN_KSI_VisualLocRot(KeyingSetInfo):
# VisualLocScale
class BUILTIN_KSI_VisualLocScale(KeyingSetInfo):
- """Insert a keyframe on each of the location and scaling channels, """ \
+ """Insert a keyframe on each of the location and scale channels, """ \
"""taking into account effects of constraints and relationships"""
- bl_label = "Visual LocScale"
+ bl_label = "Visual Location & Scale"
bl_options = {'INSERTKEY_VISUAL'}
@@ -298,8 +298,8 @@ class BUILTIN_KSI_VisualLocScale(KeyingSetInfo):
# VisualLocRotScale
class BUILTIN_KSI_VisualLocRotScale(KeyingSetInfo):
"""Insert a keyframe on each of the location, """ \
- """rotation and scaling channels, taking into account effects of constraints and relationships"""
- bl_label = "Visual LocRotScale"
+ """rotation and scale channels, taking into account effects of constraints and relationships"""
+ bl_label = "Visual Location, Rotation & Scale"
bl_options = {'INSERTKEY_VISUAL'}
@@ -321,9 +321,9 @@ class BUILTIN_KSI_VisualLocRotScale(KeyingSetInfo):
# VisualRotScale
class BUILTIN_KSI_VisualRotScale(KeyingSetInfo):
- """Insert a keyframe on each of the rotation and scaling channels, """ \
+ """Insert a keyframe on each of the rotation and scale channels, """ \
"""taking into account effects of constraints and relationships"""
- bl_label = "Visual RotScale"
+ bl_label = "Visual Rotation & Scale"
bl_options = {'INSERTKEY_VISUAL'}
@@ -628,7 +628,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
# Delta Scale
class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
- """Insert keyframes for additional scaling factor"""
+ """Insert keyframes for additional scale factor"""
bl_label = "Delta Scale"
# poll - selected objects only (and only if active object in object mode)
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 2dc6c6cd409..7321f4f6345 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -475,6 +475,12 @@ texture_node_categories = [
]),
]
+def not_implemented_node(idname):
+ NodeType = getattr(bpy.types, idname)
+ name = NodeType.bl_rna.name
+ label = f"{name} (mockup)"
+ return NodeItem(idname, label=label)
+
simulation_node_categories = [
# Simulation Nodes
SimulationNodeCategory("SIM_OUTPUT", "Output", items=[
@@ -484,15 +490,17 @@ simulation_node_categories = [
NodeItem("SimulationNodeTime"),
NodeItem("SimulationNodeParticleAttribute"),
NodeItem("FunctionNodeGroupInstanceID"),
+ NodeItem("ShaderNodeValue"),
+ NodeItem("FunctionNodeObjectTransforms"),
]),
SimulationNodeCategory("SIM_EMITTERS", "Emitters", items=[
NodeItem("SimulationNodeParticleMeshEmitter"),
- NodeItem("SimulationNodeEmitParticles"),
+ not_implemented_node("SimulationNodeEmitParticles"),
]),
SimulationNodeCategory("SIM_EVENTS", "Events", items=[
NodeItem("SimulationNodeParticleBirthEvent"),
NodeItem("SimulationNodeParticleTimeStepEvent"),
- NodeItem("SimulationNodeParticleMeshCollisionEvent"),
+ not_implemented_node("SimulationNodeParticleMeshCollisionEvent"),
]),
SimulationNodeCategory("SIM_FORCES", "Forces", items=[
NodeItem("SimulationNodeForce"),
@@ -500,18 +508,18 @@ simulation_node_categories = [
SimulationNodeCategory("SIM_EXECUTE", "Execute", items=[
NodeItem("SimulationNodeSetParticleAttribute"),
NodeItem("SimulationNodeExecuteCondition"),
- NodeItem("SimulationNodeMultiExecute"),
+ not_implemented_node("SimulationNodeMultiExecute"),
]),
SimulationNodeCategory("SIM_NOISE", "Noise", items=[
- NodeItem("ShaderNodeTexNoise"),
- NodeItem("ShaderNodeTexWhiteNoise"),
+ not_implemented_node("ShaderNodeTexNoise"),
+ not_implemented_node("ShaderNodeTexWhiteNoise"),
]),
SimulationNodeCategory("SIM_COLOR", "Color", items=[
- NodeItem("ShaderNodeMixRGB"),
- NodeItem("ShaderNodeInvert"),
- NodeItem("ShaderNodeHueSaturation"),
- NodeItem("ShaderNodeGamma"),
- NodeItem("ShaderNodeBrightContrast"),
+ not_implemented_node("ShaderNodeMixRGB"),
+ not_implemented_node("ShaderNodeInvert"),
+ not_implemented_node("ShaderNodeHueSaturation"),
+ not_implemented_node("ShaderNodeGamma"),
+ not_implemented_node("ShaderNodeBrightContrast"),
]),
SimulationNodeCategory("SIM_CONVERTER", "Converter", items=[
NodeItem("ShaderNodeMapRange"),
@@ -523,11 +531,11 @@ simulation_node_categories = [
NodeItem("ShaderNodeCombineRGB"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
- NodeItem("ShaderNodeSeparateHSV"),
- NodeItem("ShaderNodeCombineHSV"),
+ not_implemented_node("ShaderNodeSeparateHSV"),
+ not_implemented_node("ShaderNodeCombineHSV"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
- NodeItem("FunctionNodeSwitch"),
+ not_implemented_node("FunctionNodeSwitch"),
NodeItem("FunctionNodeCombineStrings"),
]),
SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items),
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 9b473812df2..88c19355960 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -22,4 +22,14 @@ if(WITH_LEGACY_OPENGL)
add_definitions(-DWITH_LEGACY_OPENGL)
endif()
+if(WITH_CLANG_TIDY)
+ if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang")
+ message(WARNING "Currently Clang-Tidy might fail with GCC toolchain, switch to Clang toolchain if that happens")
+ endif()
+
+ find_package(ClangTidy REQUIRED)
+ set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE})
+ set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE})
+endif()
+
add_subdirectory(blender)
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index 593d972b0af..e178b0f7935 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -45,6 +45,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_hair_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_ipo_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_key_types.h
@@ -71,12 +72,14 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_packedFile_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_particle_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_pointcache_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_pointcloud_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_rigidbody_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_scene_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_screen_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_sdna_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_sequence_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_shader_fx_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_simulation_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_sound_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_space_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_speaker_types.h
@@ -87,7 +90,9 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vec_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vfont_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_enums.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_volume_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_workspace_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h
@@ -107,7 +112,7 @@ add_subdirectory(blentranslation)
add_subdirectory(blenloader)
add_subdirectory(depsgraph)
add_subdirectory(ikplugin)
-add_subdirectory(physics)
+add_subdirectory(simulation)
add_subdirectory(gpu)
add_subdirectory(imbuf)
add_subdirectory(nodes)
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index da6224cff7f..ff31878a929 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -194,6 +194,8 @@ static GPUTexture *blf_batch_cache_texture_load(void)
int offset_x = bitmap_len_landed % tex_width;
int offset_y = bitmap_len_landed / tex_width;
+ GPU_texture_bind(gc->texture, 0);
+
/* TODO(germano): Update more than one row in a single call. */
while (remain) {
int remain_row = tex_width - offset_x;
@@ -297,7 +299,7 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
}
}
- err = FT_Set_Char_Size(font->face, 0, (FT_F26Dot6)(size * 64), dpi, dpi);
+ err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi);
if (err) {
/* FIXME: here we can go through the fixed size and choice a close one */
printf("The current font don't support the size, %u and dpi, %u\n", size, dpi);
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index 104582be932..b7904ce1879 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -32,6 +32,7 @@ extern "C" {
#endif
/* The following structures are defined in DNA_action_types.h, and DNA_anim_types.h */
+struct AnimationEvalContext;
struct FCurve;
struct Main;
struct Object;
@@ -202,14 +203,14 @@ void what_does_obaction(struct Object *ob,
struct bPose *pose,
struct bAction *act,
char groupname[],
- float cframe);
+ const struct AnimationEvalContext *anim_eval_context);
/* for proxy */
void BKE_pose_copy_pchan_result(struct bPoseChannel *pchanto,
const struct bPoseChannel *pchanfrom);
bool BKE_pose_copy_result(struct bPose *to, struct bPose *from);
-/* clear all transforms */
-void BKE_pose_rest(struct bPose *pose);
+/* Clear transforms. */
+void BKE_pose_rest(struct bPose *pose, bool selected_bones_only);
/* Tag pose for recalc. Also tag all related data to be recalc. */
void BKE_pose_tag_recalc(struct Main *bmain, struct bPose *pose);
diff --git a/source/blender/blenkernel/BKE_anim_data.h b/source/blender/blenkernel/BKE_anim_data.h
index 5aeaf4405f5..48e95740b9d 100644
--- a/source/blender/blenkernel/BKE_anim_data.h
+++ b/source/blender/blenkernel/BKE_anim_data.h
@@ -71,7 +71,11 @@ bool BKE_animdata_copy_id(struct Main *bmain,
const int flag);
/* Copy AnimData Actions */
-void BKE_animdata_copy_id_action(struct Main *bmain, struct ID *id, const bool set_newid);
+void BKE_animdata_copy_id_action(struct Main *bmain, struct ID *id);
+
+void BKE_animdata_duplicate_id_action(struct Main *bmain,
+ struct ID *id,
+ const uint duplicate_flags);
/* Merge copies of data from source AnimData block */
typedef enum eAnimData_MergeCopy_Modes {
diff --git a/source/blender/blenkernel/BKE_anim_path.h b/source/blender/blenkernel/BKE_anim_path.h
index 64bcedefa58..4de853303ad 100644
--- a/source/blender/blenkernel/BKE_anim_path.h
+++ b/source/blender/blenkernel/BKE_anim_path.h
@@ -36,13 +36,13 @@ struct Path;
void free_path(struct Path *path);
void calc_curvepath(struct Object *ob, struct ListBase *nurbs);
-int where_on_path(struct Object *ob,
- float ctime,
- float vec[4],
- float dir[3],
- float quat[4],
- float *radius,
- float *weight);
+bool where_on_path(const struct Object *ob,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius,
+ float *r_weight);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index 4a2ad28f90f..ddfe6b61cfb 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -48,6 +48,23 @@ struct bAction;
struct bActionGroup;
struct bContext;
+/* Container for data required to do FCurve and Driver evaluation. */
+typedef struct AnimationEvalContext {
+ /* For drivers, so that they have access to the dependency graph and the current view layer. See
+ * T77086. */
+ struct Depsgraph *depsgraph;
+
+ /* FCurves and Drivers can be evaluated at a different time than the current scene time, for
+ * example when evaluating NLA strips. This means that, even though the current time is stored in
+ * the dependency graph, we need an explicit evaluation time. */
+ float eval_time;
+} AnimationEvalContext;
+
+AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph,
+ float eval_time);
+AnimationEvalContext BKE_animsys_eval_context_construct_at(
+ const AnimationEvalContext *anim_eval_context, float eval_time);
+
/* ************************************* */
/* KeyingSets API */
@@ -172,11 +189,12 @@ void BKE_fcurves_id_cb(struct ID *id, ID_FCurve_Edit_Callback func, void *user_d
typedef struct NlaKeyframingContext NlaKeyframingContext;
-struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(struct ListBase *cache,
- struct PointerRNA *ptr,
- struct AnimData *adt,
- float ctime,
- const bool flush_to_original);
+struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
+ struct ListBase *cache,
+ struct PointerRNA *ptr,
+ struct AnimData *adt,
+ const struct AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original);
bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
@@ -209,7 +227,7 @@ bool BKE_animsys_write_rna_setting(struct PathResolvedRNA *anim_rna, const float
/* Evaluation loop for evaluating animation data */
void BKE_animsys_evaluate_animdata(struct ID *id,
struct AnimData *adt,
- float ctime,
+ const struct AnimationEvalContext *anim_eval_context,
eAnimData_Recalc recalc,
const bool flush_to_original);
@@ -229,14 +247,14 @@ void BKE_animsys_evaluate_all_animation(struct Main *main,
/* Evaluate Action (F-Curve Bag) */
void animsys_evaluate_action(struct PointerRNA *ptr,
struct bAction *act,
- float ctime,
+ const struct AnimationEvalContext *anim_eval_context,
const bool flush_to_original);
/* Evaluate Action Group */
void animsys_evaluate_action_group(struct PointerRNA *ptr,
struct bAction *act,
struct bActionGroup *agrp,
- float ctime);
+ const struct AnimationEvalContext *anim_eval_context);
/* ************************************* */
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index c22e7a24afe..6fb6675a05a 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -28,15 +28,18 @@
extern "C" {
#endif
+struct BMEditMesh;
struct Bone;
struct Depsgraph;
struct ListBase;
struct Main;
+struct Mesh;
struct Object;
struct PoseTree;
struct Scene;
struct bArmature;
struct bConstraint;
+struct bGPDstroke;
struct bPose;
struct bPoseChannel;
@@ -342,6 +345,45 @@ void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph,
struct Object *object,
int pchan_index);
+/* -------------------------------------------------------------------- */
+/** \name Deform 3D Coordinates by Armature (armature_deform.c)
+ * \{ */
+
+/* Note that we could have a 'BKE_armature_deform_coords' that doesn't take object data
+ * currently there are no callers for this though. */
+
+void BKE_armature_deform_coords_with_gpencil_stroke(const struct Object *ob_arm,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ int vert_coords_len,
+ int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ struct bGPDstroke *gps_target);
+
+void BKE_armature_deform_coords_with_mesh(const struct Object *ob_arm,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ int vert_coords_len,
+ int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ const struct Mesh *me_target);
+
+void BKE_armature_deform_coords_with_editmesh(const struct Object *ob_arm,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ int vert_coords_len,
+ int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ struct BMEditMesh *em_target);
+
+/** \} */
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 9d948dfd57b..800a3a426b7 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -32,7 +32,7 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 290
+#define BLENDER_VERSION 291
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
@@ -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 0
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_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_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index f5073ff7aa5..cd78746a3f0 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -93,9 +93,10 @@ typedef struct Cloth {
struct Implicit_Data *implicit; /* our implicit solver connects to this pointer */
struct EdgeSet *edgeset; /* used for selfcollisions */
int last_frame;
- float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */
- struct MEdge *edges; /* Used for hair collisions. */
- struct GHash *sew_edge_graph; /* Sewing edges represented using a GHash */
+ float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */
+ float average_acceleration[3]; /* Moving average of overall acceleration. */
+ struct MEdge *edges; /* Used for hair collisions. */
+ struct EdgeSet *sew_edge_graph; /* Sewing edges represented using a GHash */
} Cloth;
/**
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index f4b56aa152f..25bc2006ee3 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
@@ -54,19 +56,18 @@ void BKE_collection_add_from_object(struct Main *bmain,
struct Scene *scene,
const struct Object *ob_src,
struct Collection *collection_dst);
+void BKE_collection_add_from_collection(struct Main *bmain,
+ struct Scene *scene,
+ struct Collection *collection_src,
+ struct Collection *collection_dst);
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 */
@@ -152,7 +153,8 @@ bool BKE_collection_move(struct Main *bmain,
bool relative_after,
struct Collection *collection);
-bool BKE_collection_find_cycle(struct Collection *new_ancestor, struct Collection *collection);
+bool BKE_collection_cycle_find(struct Collection *new_ancestor, struct Collection *collection);
+bool BKE_collection_cycles_fix(struct Main *bmain, struct Collection *collection);
bool BKE_collection_has_collection(struct Collection *parent, struct Collection *collection);
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_context.h b/source/blender/blenkernel/BKE_context.h
index 70ca29d5795..7544789d864 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -312,6 +312,8 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
+bool CTX_wm_interface_locked(const bContext *C);
+
/* Gets pointer to the dependency graph.
* If it doesn't exist yet, it will be allocated.
*
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index 40f73ccfe84..ca41e51984b 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -29,11 +29,13 @@
extern "C" {
#endif
+struct BMEditMesh;
struct BezTriple;
struct Curve;
struct Depsgraph;
struct GHash;
struct ListBase;
+struct MDeformVert;
struct Main;
struct Nurb;
struct Object;
@@ -138,7 +140,7 @@ void BKE_curve_nurbs_vert_coords_apply(struct ListBase *lb,
float (*BKE_curve_nurbs_key_vert_coords_alloc(struct ListBase *lb,
float *key,
int *r_vert_len))[3];
-void BKE_curve_nurbs_key_vert_tilts_apply(struct ListBase *lb, float *key);
+void BKE_curve_nurbs_key_vert_tilts_apply(struct ListBase *lb, const float *key);
void BKE_curve_editNurb_keyIndex_delCV(struct GHash *keyindex, const void *cv);
void BKE_curve_editNurb_keyIndex_free(struct GHash **keyindex);
@@ -278,7 +280,15 @@ enum {
void BKE_curve_batch_cache_dirty_tag(struct Curve *cu, int mode);
void BKE_curve_batch_cache_free(struct Curve *cu);
-/* curve_decimate.c */
+extern void (*BKE_curve_batch_cache_dirty_tag_cb)(struct Curve *cu, int mode);
+extern void (*BKE_curve_batch_cache_free_cb)(struct Curve *cu);
+
+/* -------------------------------------------------------------------- */
+/** \name Decimate Curve (curve_decimate.c)
+ *
+ * Simplify curve data.
+ * \{ */
+
unsigned int BKE_curve_decimate_bezt_array(struct BezTriple *bezt_array,
const unsigned int bezt_array_len,
const unsigned int resolu,
@@ -293,8 +303,38 @@ void BKE_curve_decimate_nurb(struct Nurb *nu,
const float error_sq_max,
const unsigned int error_target_len);
-extern void (*BKE_curve_batch_cache_dirty_tag_cb)(struct Curve *cu, int mode);
-extern void (*BKE_curve_batch_cache_free_cb)(struct Curve *cu);
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Deform 3D Coordinates by Curve (curve_deform.c)
+ * \{ */
+
+void BKE_curve_deform_coords(const struct Object *ob_curve,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const struct MDeformVert *dvert,
+ const int defgrp_index,
+ const short flag,
+ const short defaxis);
+
+void BKE_curve_deform_coords_with_editmesh(const Object *ob_curve,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const int defgrp_index,
+ const short flag,
+ const short defaxis,
+ struct BMEditMesh *em_target);
+
+void BKE_curve_deform_co(const struct Object *ob_curve,
+ const struct Object *ob_target,
+ const float orco[3],
+ float vec[3],
+ const int no_rot_axis,
+ float r_mat[3][3]);
+
+/** \} */
#ifdef __cplusplus
}
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_deform.h b/source/blender/blenkernel/BKE_deform.h
index c86b877b575..f59e796c98c 100644
--- a/source/blender/blenkernel/BKE_deform.h
+++ b/source/blender/blenkernel/BKE_deform.h
@@ -40,14 +40,16 @@ struct bDeformGroup;
struct bDeformGroup *BKE_object_defgroup_new(struct Object *ob, const char *name);
void BKE_defgroup_copy_list(struct ListBase *lb1, const struct ListBase *lb2);
struct bDeformGroup *BKE_defgroup_duplicate(const struct bDeformGroup *ingroup);
-struct bDeformGroup *BKE_object_defgroup_find_name(struct Object *ob, const char *name);
-int *BKE_object_defgroup_flip_map(struct Object *ob, int *flip_map_len, const bool use_default);
-int *BKE_object_defgroup_flip_map_single(struct Object *ob,
+struct bDeformGroup *BKE_object_defgroup_find_name(const struct Object *ob, const char *name);
+int *BKE_object_defgroup_flip_map(const struct Object *ob,
+ int *flip_map_len,
+ const bool use_default);
+int *BKE_object_defgroup_flip_map_single(const struct Object *ob,
int *flip_map_len,
const bool use_default,
int defgroup);
-int BKE_object_defgroup_flip_index(struct Object *ob, int index, const bool use_default);
-int BKE_object_defgroup_name_index(struct Object *ob, const char *name);
+int BKE_object_defgroup_flip_index(const struct Object *ob, int index, const bool use_default);
+int BKE_object_defgroup_name_index(const struct Object *ob, const char *name);
void BKE_object_defgroup_unique_name(struct bDeformGroup *dg, struct Object *ob);
struct MDeformWeight *BKE_defvert_find_index(const struct MDeformVert *dv, const int defgroup);
@@ -109,7 +111,7 @@ void BKE_defvert_sync_mapped(struct MDeformVert *dvert_dst,
const int *flip_map,
const int flip_map_len,
const bool use_ensure);
-void BKE_defvert_remap(struct MDeformVert *dvert, int *map, const int map_len);
+void BKE_defvert_remap(struct MDeformVert *dvert, const int *map, const int map_len);
void BKE_defvert_flip(struct MDeformVert *dvert, const int *flip_map, const int flip_map_len);
void BKE_defvert_flip_merged(struct MDeformVert *dvert,
const int *flip_map,
diff --git a/source/blender/blenkernel/BKE_duplilist.h b/source/blender/blenkernel/BKE_duplilist.h
index 71b6d06b450..13918dd4fb1 100644
--- a/source/blender/blenkernel/BKE_duplilist.h
+++ b/source/blender/blenkernel/BKE_duplilist.h
@@ -52,7 +52,7 @@ typedef struct DupliObject {
/* Persistent identifier for a dupli object, for inter-frame matching of
* objects with motion blur, or inter-update matching for syncing. */
- int persistent_id[16]; /* 2*MAX_DUPLI_RECUR */
+ int persistent_id[8]; /* MAX_DUPLI_RECUR */
/* Particle this dupli was generated from. */
struct ParticleSystem *particle_system;
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_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index c3a597e29b9..b846e2e5b7b 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -36,6 +36,7 @@ struct FCurve;
struct FModifier;
struct AnimData;
+struct AnimationEvalContext;
struct BezTriple;
struct LibraryForeachIDData;
struct PathResolvedRNA;
@@ -271,7 +272,7 @@ void testhandles_fcurve(struct FCurve *fcu, eBezTriple_Flag sel_flag, const bool
void sort_time_fcurve(struct FCurve *fcu);
short test_time_fcurve(struct FCurve *fcu);
-void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]);
+void correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]);
/* -------- Evaluation -------- */
@@ -281,10 +282,12 @@ float evaluate_fcurve_only_curve(struct FCurve *fcu, float evaltime);
float evaluate_fcurve_driver(struct PathResolvedRNA *anim_rna,
struct FCurve *fcu,
struct ChannelDriver *driver_orig,
- float evaltime);
+ const struct AnimationEvalContext *anim_eval_context);
bool BKE_fcurve_is_empty(struct FCurve *fcu);
/* evaluate fcurve and store value */
-float calculate_fcurve(struct PathResolvedRNA *anim_rna, struct FCurve *fcu, float evaltime);
+float calculate_fcurve(struct PathResolvedRNA *anim_rna,
+ struct FCurve *fcu,
+ const struct AnimationEvalContext *anim_eval_context);
/* ************* F-Curve Samples API ******************** */
diff --git a/source/blender/blenkernel/BKE_fcurve_driver.h b/source/blender/blenkernel/BKE_fcurve_driver.h
index 563ed408ed7..6803485f843 100644
--- a/source/blender/blenkernel/BKE_fcurve_driver.h
+++ b/source/blender/blenkernel/BKE_fcurve_driver.h
@@ -30,6 +30,7 @@
extern "C" {
#endif
+struct AnimationEvalContext;
struct ChannelDriver;
struct DriverTarget;
struct DriverVar;
@@ -97,7 +98,7 @@ void BKE_driver_invalidate_expression(struct ChannelDriver *driver,
float evaluate_driver(struct PathResolvedRNA *anim_rna,
struct ChannelDriver *driver,
struct ChannelDriver *driver_orig,
- const float evaltime);
+ const struct AnimationEvalContext *anim_eval_context);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_fluid.h b/source/blender/blenkernel/BKE_fluid.h
index e06a1a9fb92..2392263181d 100644
--- a/source/blender/blenkernel/BKE_fluid.h
+++ b/source/blender/blenkernel/BKE_fluid.h
@@ -37,36 +37,36 @@ struct Main;
struct Scene;
typedef float (*BKE_Fluid_BresenhamFn)(
- float *result, float *input, int res[3], int *pixel, float *tRay, float correct);
+ float *result, const float *input, int res[3], int *pixel, float *tRay, float correct);
-struct Mesh *BKE_fluid_modifier_do(struct FluidModifierData *mmd,
+struct Mesh *BKE_fluid_modifier_do(struct FluidModifierData *fmd,
struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct Mesh *me);
-void BKE_fluid_modifier_free(struct FluidModifierData *mmd);
-void BKE_fluid_modifier_reset(struct FluidModifierData *mmd);
-void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd);
-void BKE_fluid_modifier_copy(const struct FluidModifierData *mmd,
- struct FluidModifierData *tmmd,
+void BKE_fluid_modifier_free(struct FluidModifierData *fmd);
+void BKE_fluid_modifier_reset(struct FluidModifierData *fmd);
+void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd);
+void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd,
+ struct FluidModifierData *tfmd,
const int flag);
-bool BKE_fluid_reallocate_fluid(struct FluidDomainSettings *mds, int res[3], int free_old);
-void BKE_fluid_reallocate_copy_fluid(struct FluidDomainSettings *mds,
+bool BKE_fluid_reallocate_fluid(struct FluidDomainSettings *fds, int res[3], int free_old);
+void BKE_fluid_reallocate_copy_fluid(struct FluidDomainSettings *fds,
int o_res[3],
int n_res[3],
- int o_min[3],
- int n_min[3],
- int o_max[3],
+ const int o_min[3],
+ const int n_min[3],
+ const int o_max[3],
int o_shift[3],
int n_shift[3]);
-void BKE_fluid_cache_free_all(struct FluidDomainSettings *mds, struct Object *ob);
-void BKE_fluid_cache_free(struct FluidDomainSettings *mds, struct Object *ob, int cache_map);
+void BKE_fluid_cache_free_all(struct FluidDomainSettings *fds, struct Object *ob);
+void BKE_fluid_cache_free(struct FluidDomainSettings *fds, struct Object *ob, int cache_map);
void BKE_fluid_cache_new_name_for_current_session(int maxlen, char *r_name);
float BKE_fluid_get_velocity_at(struct Object *ob, float position[3], float velocity[3]);
-int BKE_fluid_get_data_flags(struct FluidDomainSettings *mds);
+int BKE_fluid_get_data_flags(struct FluidDomainSettings *fds);
void BKE_fluid_particle_system_create(struct Main *bmain,
struct Object *ob,
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index f6cae6d8a9c..61c270202f1 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -169,7 +169,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 */
@@ -177,19 +177,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..964764d99e7 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -78,6 +78,20 @@ void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps);
void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]);
+typedef struct GPencilPointCoordinates {
+ /* This is used when doing "move only origin" in object_data_transform.c.
+ * pressure is needs to be stored here as it is tied to object scale. */
+ float co[3];
+ float pressure;
+} GPencilPointCoordinates;
+
+int BKE_gpencil_stroke_point_count(struct bGPdata *gpd);
+void BKE_gpencil_point_coords_get(struct bGPdata *gpd, GPencilPointCoordinates *elem_data);
+void BKE_gpencil_point_coords_apply(struct bGPdata *gpd, const GPencilPointCoordinates *elem_data);
+void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd,
+ const GPencilPointCoordinates *elem_data,
+ const float mat[4][4]);
+
bool BKE_gpencil_stroke_sample(struct bGPDstroke *gps, const float dist, const bool select);
bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf);
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence);
@@ -98,6 +112,22 @@ 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_stroke_set_random_color(struct bGPDstroke *gps);
+
+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,
+ const bool simple_material);
+
#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_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index b6dfadd3b2a..38322427374 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -46,7 +46,20 @@ enum {
IDTYPE_FLAGS_NO_MAKELOCAL = 1 << 2,
};
-/* ********** Prototypes for IDTypeInfo callbacks. ********** */
+typedef struct IDCacheKey {
+ /* The session UUID of the ID owning the cached data. */
+ unsigned int id_session_uuid;
+ /* Value uniquely identifying the cache within its ID.
+ * Typically the offset of its member in the data-block struct, but can be anything. */
+ size_t offset_in_ID;
+ /* Actual address of the cached data to save and restore. */
+ void *cache_v;
+} IDCacheKey;
+
+uint BKE_idtype_cache_key_hash(const void *key_v);
+bool BKE_idtype_cache_key_cmp(const void *key_a_v, const void *key_b_v);
+
+/* ********** Prototypes for #IDTypeInfo callbacks. ********** */
typedef void (*IDTypeInitDataFunction)(struct ID *id);
@@ -63,6 +76,20 @@ typedef void (*IDTypeMakeLocalFunction)(struct Main *bmain, struct ID *id, const
typedef void (*IDTypeForeachIDFunction)(struct ID *id, struct LibraryForeachIDData *data);
+typedef enum eIDTypeInfoCacheCallbackFlags {
+ /** Indicates to the callback that that cache may be stored in the .blend file, so its pointer
+ * should not be cleared at read-time. */
+ IDTYPE_CACHE_CB_FLAGS_PERSISTENT = 1 << 0,
+} eIDTypeInfoCacheCallbackFlags;
+typedef void (*IDTypeForeachCacheFunctionCallback)(struct ID *id,
+ const struct IDCacheKey *cache_key,
+ void **cache_p,
+ uint flags,
+ void *user_data);
+typedef void (*IDTypeForeachCacheFunction)(struct ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data);
+
typedef struct IDTypeInfo {
/* ********** General IDType data. ********** */
@@ -130,6 +157,11 @@ typedef struct IDTypeInfo {
* pointers) of given data-block.
*/
IDTypeForeachIDFunction foreach_id;
+
+ /**
+ * Iterator over all cache pointers of given ID.
+ */
+ IDTypeForeachCacheFunction foreach_cache;
} IDTypeInfo;
/* ********** Declaration of each IDTypeInfo. ********** */
@@ -203,6 +235,14 @@ short BKE_idtype_idcode_from_index(const int index);
short BKE_idtype_idcode_iter_step(int *index);
+/* Some helpers/wrappers around callbacks defined in #IDTypeInfo, dealing e.g. with embedded IDs.
+ * XXX Ideally those would rather belong to #BKE_lib_id, but using callback function pointers makes
+ * this hard to do properly if we want to avoid headers includes in headers. */
+
+void BKE_idtype_id_foreach_cache(struct ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 1e5573ab014..c5221baf7d7 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -55,6 +55,7 @@ void BKE_image_free_packedfiles(struct Image *image);
void BKE_image_free_views(struct Image *image);
void BKE_image_free_buffers(struct Image *image);
void BKE_image_free_buffers_ex(struct Image *image, bool do_lock);
+void BKE_image_free_gputextures(struct Image *ima);
/* call from library */
void BKE_image_free(struct Image *image);
@@ -274,6 +275,10 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame);
/* does all images with type MOVIE or SEQUENCE */
void BKE_image_all_free_anim_ibufs(struct Main *bmain, int except_frame);
+void BKE_image_free_all_gputextures(struct Main *bmain);
+void BKE_image_free_anim_gputextures(struct Main *bmain);
+void BKE_image_free_old_gputextures(struct Main *bmain);
+
bool BKE_image_memorypack(struct Image *ima);
void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath);
void BKE_image_packfiles_from_mem(struct ReportList *reports,
@@ -362,6 +367,30 @@ bool BKE_image_has_loaded_ibuf(struct Image *image);
struct ImBuf *BKE_image_get_ibuf_with_name(struct Image *image, const char *name);
struct ImBuf *BKE_image_get_first_ibuf(struct Image *image);
+/* Not to be use directly. */
+struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf);
+
+/* Get the GPUTexture for a given `Image`.
+ *
+ * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already
+ * available. It is also required when requesting the GPUTexture for a render result. */
+struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image,
+ struct ImageUser *iuser,
+ struct ImBuf *ibuf);
+struct GPUTexture *BKE_image_get_gpu_tiles(struct Image *image,
+ struct ImageUser *iuser,
+ struct ImBuf *ibuf);
+struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image,
+ struct ImageUser *iuser,
+ struct ImBuf *ibuf);
+
+void BKE_image_update_gputexture(
+ struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h);
+void BKE_image_paint_set_mipmap(struct Main *bmain, bool mipmap);
+
+/* Delayed free of OpenGL buffers by main thread */
+void BKE_image_free_unused_gpu_textures(void);
+
struct RenderSlot *BKE_image_add_renderslot(struct Image *ima, const char *name);
bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int slot);
struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int slot);
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h
index 6581891062c..b0eef02611b 100644
--- a/source/blender/blenkernel/BKE_key.h
+++ b/source/blender/blenkernel/BKE_key.h
@@ -51,6 +51,12 @@ void key_curve_normal_weights(float t, float data[4], int type);
float *BKE_key_evaluate_object_ex(struct Object *ob, int *r_totelem, float *arr, size_t arr_size);
float *BKE_key_evaluate_object(struct Object *ob, int *r_totelem);
+int BKE_keyblock_element_count_from_shape(const struct Key *key, const int shape_index);
+int BKE_keyblock_element_count(const struct Key *key);
+
+size_t BKE_keyblock_element_calc_size_from_shape(const struct Key *key, const int shape_index);
+size_t BKE_keyblock_element_calc_size(const struct Key *key);
+
bool BKE_key_idtype_support(const short id_type);
struct Key **BKE_key_from_id_p(struct ID *id);
@@ -74,6 +80,10 @@ void BKE_keyblock_convert_from_lattice(struct Lattice *lt, struct KeyBlock *kb);
void BKE_keyblock_convert_to_lattice(struct KeyBlock *kb, struct Lattice *lt);
int BKE_keyblock_curve_element_count(struct ListBase *nurb);
+void BKE_keyblock_curve_data_transform(const struct ListBase *nurb,
+ const float mat[4][4],
+ const void *src,
+ void *dst);
void BKE_keyblock_update_from_curve(struct Curve *cu, struct KeyBlock *kb, struct ListBase *nurb);
void BKE_keyblock_convert_from_curve(struct Curve *cu, struct KeyBlock *kb, struct ListBase *nurb);
void BKE_keyblock_convert_to_curve(struct KeyBlock *kb, struct Curve *cu, struct ListBase *nurb);
@@ -104,6 +114,28 @@ bool BKE_keyblock_move(struct Object *ob, int org_index, int new_index);
bool BKE_keyblock_is_basis(struct Key *key, const int index);
+/* -------------------------------------------------------------------- */
+/** \name Key-Block Data Access
+ * \{ */
+
+void BKE_keyblock_data_get_from_shape(const struct Key *key,
+ float (*arr)[3],
+ const int shape_index);
+void BKE_keyblock_data_get(const struct Key *key, float (*arr)[3]);
+
+void BKE_keyblock_data_set_with_mat4(struct Key *key,
+ const int shape_index,
+ const float (*vertices)[3],
+ const float mat[4][4]);
+void BKE_keyblock_curve_data_set_with_mat4(struct Key *key,
+ const struct ListBase *nurb,
+ const int shape_index,
+ const void *data,
+ const float mat[4][4]);
+void BKE_keyblock_data_set(struct Key *key, const int shape_index, const void *data);
+
+/** \} */
+
#ifdef __cplusplus
};
#endif
diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h
index 3cd8c69a263..bb23ad63020 100644
--- a/source/blender/blenkernel/BKE_lattice.h
+++ b/source/blender/blenkernel/BKE_lattice.h
@@ -30,6 +30,7 @@
extern "C" {
#endif
+struct BMEditMesh;
struct BPoint;
struct Depsgraph;
struct Lattice;
@@ -45,48 +46,9 @@ struct Lattice *BKE_lattice_add(struct Main *bmain, const char *name);
struct Lattice *BKE_lattice_copy(struct Main *bmain, const struct Lattice *lt);
void calc_lat_fudu(int flag, int res, float *r_fu, float *r_du);
-struct LatticeDeformData *init_latt_deform(struct Object *oblatt,
- struct Object *ob) ATTR_WARN_UNUSED_RESULT;
-void calc_latt_deform(struct LatticeDeformData *lattice_deform_data, float co[3], float weight);
-void end_latt_deform(struct LatticeDeformData *lattice_deform_data);
-
bool object_deform_mball(struct Object *ob, struct ListBase *dispbase);
void outside_lattice(struct Lattice *lt);
-void curve_deform_verts(struct Object *cuOb,
- struct Object *target,
- float (*vert_coords)[3],
- int numVerts,
- struct MDeformVert *dvert,
- const int defgrp_index,
- short flag,
- short defaxis);
-void curve_deform_vector(struct Object *cuOb,
- struct Object *target,
- float orco[3],
- float vec[3],
- float mat[3][3],
- int no_rot_axis);
-
-void lattice_deform_verts(struct Object *laOb,
- struct Object *target,
- struct Mesh *mesh,
- float (*vert_coords)[3],
- int numVerts,
- short flag,
- const char *vgroup,
- float influence);
-void armature_deform_verts(struct Object *armOb,
- struct Object *target,
- const struct Mesh *mesh,
- float (*vert_coords)[3],
- float (*defMats)[3][3],
- int numVerts,
- int deformflag,
- float (*prevCos)[3],
- const char *defgrp_name,
- struct bGPDstroke *gps);
-
float (*BKE_lattice_vert_coords_alloc(const struct Lattice *lt, int *r_vert_len))[3];
void BKE_lattice_vert_coords_get(const struct Lattice *lt, float (*vert_coords)[3]);
void BKE_lattice_vert_coords_apply_with_mat4(struct Lattice *lt,
@@ -97,7 +59,7 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob);
-struct MDeformVert *BKE_lattice_deform_verts_get(struct Object *lattice);
+struct MDeformVert *BKE_lattice_deform_verts_get(const struct Object *lattice);
struct BPoint *BKE_lattice_active_point_get(struct Lattice *lt);
struct BoundBox *BKE_lattice_boundbox_get(struct Object *ob);
@@ -137,6 +99,44 @@ void BKE_lattice_batch_cache_free(struct Lattice *lt);
extern void (*BKE_lattice_batch_cache_dirty_tag_cb)(struct Lattice *lt, int mode);
extern void (*BKE_lattice_batch_cache_free_cb)(struct Lattice *lt);
+/* -------------------------------------------------------------------- */
+/** \name Deform 3D Coordinates by Lattice (lattice_deform.c)
+ * \{ */
+
+struct LatticeDeformData *BKE_lattice_deform_data_create(
+ const struct Object *oblatt, const struct Object *ob) ATTR_WARN_UNUSED_RESULT;
+void BKE_lattice_deform_data_eval_co(struct LatticeDeformData *lattice_deform_data,
+ float co[3],
+ float weight);
+void BKE_lattice_deform_data_destroy(struct LatticeDeformData *lattice_deform_data);
+
+void BKE_lattice_deform_coords(const struct Object *ob_lattice,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ float influence);
+
+void BKE_lattice_deform_coords_with_mesh(const struct Object *ob_lattice,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ const float influence,
+ const struct Mesh *me_target);
+
+void BKE_lattice_deform_coords_with_editmesh(const struct Object *ob_lattice,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ const float influence,
+ struct BMEditMesh *em_target);
+/** \} */
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 7f5a6e3e36a..d46a03e90af 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,9 @@ 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 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,7 +266,9 @@ 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,
- char separator_char);
+ const bool add_lib_hint,
+ char separator_char,
+ int *r_prefix_len);
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..6a05f0c22b6 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -50,9 +50,8 @@ struct IDOverrideLibraryPropertyOperation;
struct Main;
struct PointerRNA;
struct PropertyRNA;
-
-void BKE_lib_override_library_enable(const bool do_enable);
-bool BKE_lib_override_library_is_enabled(void);
+struct Scene;
+struct ViewLayer;
struct IDOverrideLibrary *BKE_lib_override_library_init(struct ID *local_id,
struct ID *reference_id);
@@ -66,6 +65,15 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
bool BKE_lib_override_library_create_from_tag(struct Main *bmain);
+void BKE_lib_override_library_dependencies_tag(struct Main *bmain,
+ struct ID *id_root,
+ const uint tag,
+ const bool do_create_main_relashionships);
+bool BKE_lib_override_library_create(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct ID *id_root,
+ struct ID *id_reference);
struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find(
struct IDOverrideLibrary *override, const char *rna_path);
@@ -108,11 +116,12 @@ 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_id_reset(struct Main *bmain, struct ID *id_root);
+void BKE_lib_override_library_id_hierarchy_reset(struct Main *bmain, struct ID *id_root);
+
void BKE_lib_override_library_operations_tag(struct IDOverrideLibraryProperty *override_property,
const short tag,
const bool do_set);
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_material.h b/source/blender/blenkernel/BKE_material.h
index 225d966a51a..cae79326dda 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -84,7 +84,8 @@ void BKE_object_material_assign(
void BKE_object_material_array_assign(struct Main *bmain,
struct Object *ob,
struct Material ***matar,
- short totcol);
+ int totcol,
+ const bool to_object_only);
short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma);
bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 46c6f68384e..b2510be656e 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]);
@@ -512,13 +513,13 @@ void BKE_mesh_loops_to_mface_corners(struct CustomData *fdata,
void BKE_mesh_loops_to_tessdata(struct CustomData *fdata,
struct CustomData *ldata,
struct MFace *mface,
- int *polyindices,
+ const int *polyindices,
unsigned int (*loopindices)[4],
const int num_faces);
void BKE_mesh_tangent_loops_to_tessdata(struct CustomData *fdata,
struct CustomData *ldata,
struct MFace *mface,
- int *polyindices,
+ const int *polyindices,
unsigned int (*loopindices)[4],
const int num_faces,
const char *layer_name);
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index 9864cc4d28d..5ad5238bbb5 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -199,7 +199,7 @@ void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store);
void BKE_mesh_loop_islands_free(MeshIslandStore *island_store);
void BKE_mesh_loop_islands_add(MeshIslandStore *islands,
const int item_num,
- int *item_indices,
+ const int *item_indices,
const int num_island_items,
int *island_item_indices,
const int num_innercut_items,
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_mesh_wrapper.h b/source/blender/blenkernel/BKE_mesh_wrapper.h
index ec6a1e3457c..00e2dd08726 100644
--- a/source/blender/blenkernel/BKE_mesh_wrapper.h
+++ b/source/blender/blenkernel/BKE_mesh_wrapper.h
@@ -31,7 +31,7 @@ extern "C" {
struct Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(
struct BMEditMesh *em,
const struct CustomData_MeshMasks *cd_mask_extra,
- float (*vertexCos)[3],
+ const float (*vert_coords)[3],
const struct Mesh *me_settings);
struct Mesh *BKE_mesh_wrapper_from_editmesh(struct BMEditMesh *em,
const struct CustomData_MeshMasks *cd_mask_extra,
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index e4cf7d44f05..e16a9284425 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -41,30 +41,35 @@ struct ModifierData;
struct Object;
struct Scene;
struct bArmature;
+struct BlendWriter;
+struct BlendDataReader;
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,
@@ -76,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
@@ -84,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),
@@ -167,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).
@@ -176,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.
@@ -187,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,
@@ -204,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,
@@ -215,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
@@ -240,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.
@@ -264,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.
@@ -273,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
@@ -283,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.
*
@@ -316,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.
@@ -329,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.
*
@@ -341,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.
@@ -353,8 +378,25 @@ 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
+ * already.
+ *
+ * This method should write any additional arrays and referenced structs that should be
+ * stored in the file.
+ */
+ void (*blendWrite)(struct BlendWriter *writer, const struct ModifierData *md);
+
+ /**
+ * 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.
+ */
+ void (*blendRead)(struct BlendDataReader *reader, struct ModifierData *md);
} ModifierTypeInfo;
/* Used to find a modifier's panel type. */
@@ -430,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.
@@ -456,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_movieclip.h b/source/blender/blenkernel/BKE_movieclip.h
index dbd6eb15bf2..bba01dd84d2 100644
--- a/source/blender/blenkernel/BKE_movieclip.h
+++ b/source/blender/blenkernel/BKE_movieclip.h
@@ -113,6 +113,11 @@ bool BKE_movieclip_put_frame_if_possible(struct MovieClip *clip,
struct MovieClipUser *user,
struct ImBuf *ibuf);
+struct GPUTexture *BKE_movieclip_get_gpu_texture(struct MovieClip *clip,
+ struct MovieClipUser *cuser);
+
+void BKE_movieclip_free_gputexture(struct MovieClip *clip);
+
/* Dependency graph evaluation. */
void BKE_movieclip_eval_update(struct Depsgraph *depsgraph,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 536d04f8bd3..4c55488ecd5 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -101,6 +101,30 @@ typedef struct bNodeSocketTemplate {
char identifier[64]; /* generated from name */
} bNodeSocketTemplate;
+/* Use `void *` for callbacks that require C++. This is rather ugly, but works well for now. This
+ * would not be necessary if we would use bNodeSocketType and bNodeType only in C++ code.
+ * However, achieving this requires quite a few changes currently. */
+#ifdef __cplusplus
+namespace blender {
+namespace nodes {
+class SocketMFNetworkBuilder;
+class NodeMFNetworkBuilder;
+} // namespace nodes
+namespace fn {
+class MFDataType;
+}
+} // namespace blender
+
+using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
+using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)();
+using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder);
+
+#else
+typedef void *NodeExpandInMFNetworkFunction;
+typedef void *SocketGetMFDataTypeFunction;
+typedef void *SocketExpandInMFNetworkFunction;
+#endif
+
/**
* \brief Defines a socket type.
*
@@ -153,6 +177,11 @@ typedef struct bNodeSocketType {
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
+
+ /* Returns the multi-function data type of this socket type. */
+ SocketGetMFDataTypeFunction get_mf_data_type;
+ /* Expands the socket into a multi-function node that outputs the socket value. */
+ SocketExpandInMFNetworkFunction expand_in_mf_network;
} bNodeSocketType;
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
@@ -267,6 +296,9 @@ typedef struct bNodeType {
/* gpu */
NodeGPUExecFunction gpufunc;
+ /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
+ NodeExpandInMFNetworkFunction expand_in_mf_network;
+
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeType;
@@ -500,7 +532,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,
@@ -1311,6 +1343,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_FLOAT_COMPARE 1202
#define FN_NODE_GROUP_INSTANCE_ID 1203
#define FN_NODE_COMBINE_STRINGS 1204
+#define FN_NODE_OBJECT_TRANSFORMS 1205
/** \} */
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 3710ec810ce..f2a022c84a3 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,
+ 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_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h
index 410cb862aa7..e4813aa2288 100644
--- a/source/blender/blenkernel/BKE_object_deform.h
+++ b/source/blender/blenkernel/BKE_object_deform.h
@@ -33,7 +33,7 @@ struct Object;
struct bDeformGroup;
/* General vgroup operations */
-void BKE_object_defgroup_remap_update_users(struct Object *ob, int *map);
+void BKE_object_defgroup_remap_update_users(struct Object *ob, const int *map);
bool BKE_object_defgroup_array_get(struct ID *id, struct MDeformVert **dvert_arr, int *dvert_tot);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 4d30c5c7fce..382919147f8 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -24,6 +24,7 @@
* \ingroup bke
*/
+#include "BLI_bitmap.h"
#include "BLI_utildefines.h"
#include "DNA_object_enums.h"
@@ -37,6 +38,7 @@ struct Brush;
struct CurveMapping;
struct Depsgraph;
struct EnumPropertyItem;
+struct EdgeSet;
struct GHash;
struct GridPaintMask;
struct ImagePool;
@@ -56,7 +58,6 @@ struct ReportList;
struct Scene;
struct StrokeCache;
struct SubdivCCG;
-struct SubdivCCG;
struct Tex;
struct ToolSettings;
struct UnifiedPaintSettings;
@@ -259,18 +260,34 @@ typedef struct SculptPoseIKChain {
/* Cloth Brush */
typedef struct SculptClothLengthConstraint {
- int v1;
- int v2;
+ /* Elements that are affected by the constraint. */
+ /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always
+ * deformed. Element b could be another vertex of the same mesh or any other position (arbitrary
+ * point, position for a previous state). In that case, elem_index_a and elem_index_b should be
+ * the same to avoid affecting two different vertices when solving the constraints.
+ * *elem_position points to the position which is owned by the element. */
+ int elem_index_a;
+ float *elem_position_a;
+
+ int elem_index_b;
+ float *elem_position_b;
float length;
+ float strength;
} SculptClothLengthConstraint;
typedef struct SculptClothSimulation {
SculptClothLengthConstraint *length_constraints;
int tot_length_constraints;
+ struct EdgeSet *created_length_constraints;
int capacity_length_constraints;
float *length_constraint_tweak;
+ /* Position anchors for deformation brushes. These positions are modified by the brush and the
+ * final positions of the simulated vertices are updated with constraints that use these points
+ * as targets. */
+ float (*deformation_pos)[3];
+
float mass;
float damping;
@@ -281,11 +298,30 @@ 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;
+
+ /* Indexed by base mesh vertex index, stores if that vertex is a boundary. */
+ BLI_bitmap *boundary;
+} 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) */
@@ -306,6 +342,7 @@ typedef struct SculptSession {
int totvert, totpoly;
struct KeyBlock *shapekey_active;
+ struct MPropCol *vcol;
float *vmask;
/* Mesh connectivity */
@@ -336,10 +373,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;
@@ -363,6 +400,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;
@@ -372,9 +410,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];
@@ -425,10 +466,15 @@ void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss);
void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder);
void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
+/* Create new color layer on object if it doesn't have one and if experimental feature set has
+ * sculpt vertex color enabled. Returns truth if new layer has been added, false otherwise. */
+void BKE_sculpt_color_layer_create_if_needed(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_particle.h b/source/blender/blenkernel/BKE_particle.h
index 00d010cd6d9..7de588450d6 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -48,7 +48,6 @@ struct BVHTreeRay;
struct BVHTreeRayHit;
struct CustomData_MeshMasks;
struct Depsgraph;
-struct Depsgraph;
struct EdgeHash;
struct KDTree_3d;
struct LatticeDeformData;
@@ -366,6 +365,10 @@ struct ModifierData *object_add_particle_system(struct Main *bmain,
struct Scene *scene,
struct Object *ob,
const char *name);
+struct ModifierData *object_copy_particle_system(struct Main *bmain,
+ struct Scene *scene,
+ struct Object *ob,
+ const struct ParticleSystem *psys_orig);
void object_remove_particle_system(struct Main *bmain, struct Scene *scene, struct Object *ob);
struct ParticleSettings *BKE_particlesettings_add(struct Main *bmain, const char *name);
struct ParticleSettings *BKE_particlesettings_copy(struct Main *bmain,
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 8fb6f140822..da9280148fd 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 {
@@ -219,7 +224,7 @@ void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]);
unsigned int **BKE_pbvh_grid_hidden(const PBVH *pbvh);
int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden,
- int *grid_indices,
+ const int *grid_indices,
int totgrid,
int gridsize);
@@ -252,12 +257,14 @@ 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);
void BKE_pbvh_node_mark_normals_update(PBVHNode *node);
void BKE_pbvh_node_mark_topology_update(PBVHNode *node);
void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden);
+bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node);
void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked);
bool BKE_pbvh_node_fully_masked_get(PBVHNode *node);
void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked);
@@ -352,6 +359,7 @@ typedef struct PBVHVertexIter {
struct MVert *mverts;
int totvert;
const int *vert_indices;
+ struct MPropCol *vcol;
float *vmask;
/* bmesh */
@@ -368,6 +376,7 @@ typedef struct PBVHVertexIter {
short *no;
float *fno;
float *mask;
+ float *col;
bool visible;
} PBVHVertexIter;
@@ -419,7 +428,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 +483,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_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh
new file mode 100644
index 00000000000..884e4c00766
--- /dev/null
+++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh
@@ -0,0 +1,132 @@
+/*
+ * 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 __BKE_PERSISTENT_DATA_HANDLE_H__
+#define __BKE_PERSISTENT_DATA_HANDLE_H__
+
+/** \file
+ * \ingroup bke
+ *
+ * A PersistentDataHandle is a weak reference to some data in a Blender file. The handle itself is
+ * just a number. A PersistentDataHandleMap is used to convert between handles and the actual data.
+ */
+
+#include "BLI_map.hh"
+
+#include "DNA_ID.h"
+
+struct Object;
+
+namespace blender::bke {
+
+class PersistentDataHandleMap;
+
+class PersistentDataHandle {
+ private:
+ /* Negative values indicate that the handle is "empty". */
+ int32_t handle_;
+
+ friend PersistentDataHandleMap;
+
+ protected:
+ PersistentDataHandle(int handle) : handle_(handle)
+ {
+ }
+
+ public:
+ PersistentDataHandle() : handle_(-1)
+ {
+ }
+
+ friend bool operator==(const PersistentDataHandle &a, const PersistentDataHandle &b)
+ {
+ return a.handle_ == b.handle_;
+ }
+
+ friend bool operator!=(const PersistentDataHandle &a, const PersistentDataHandle &b)
+ {
+ return !(a == b);
+ }
+
+ friend std::ostream &operator<<(std::ostream &stream, const PersistentDataHandle &a)
+ {
+ stream << a.handle_;
+ return stream;
+ }
+
+ uint64_t hash() const
+ {
+ return (uint64_t)handle_;
+ }
+};
+
+class PersistentIDHandle : public PersistentDataHandle {
+ friend PersistentDataHandleMap;
+ using PersistentDataHandle::PersistentDataHandle;
+};
+
+class PersistentObjectHandle : public PersistentIDHandle {
+ friend PersistentDataHandleMap;
+ using PersistentIDHandle::PersistentIDHandle;
+};
+
+class PersistentDataHandleMap {
+ private:
+ Map<int32_t, ID *> id_by_handle_;
+ Map<ID *, int32_t> handle_by_id_;
+
+ public:
+ void add(int32_t handle, ID &id)
+ {
+ BLI_assert(handle >= 0);
+ handle_by_id_.add(&id, handle);
+ id_by_handle_.add(handle, &id);
+ }
+
+ PersistentIDHandle lookup(ID *id) const
+ {
+ const int handle = handle_by_id_.lookup_default(id, -1);
+ return PersistentIDHandle(handle);
+ }
+
+ PersistentObjectHandle lookup(Object *object) const
+ {
+ const int handle = handle_by_id_.lookup_default((ID *)object, -1);
+ return PersistentObjectHandle(handle);
+ }
+
+ ID *lookup(const PersistentIDHandle &handle) const
+ {
+ ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr);
+ return id;
+ }
+
+ Object *lookup(const PersistentObjectHandle &handle) const
+ {
+ ID *id = this->lookup((const PersistentIDHandle &)handle);
+ if (id == nullptr) {
+ return nullptr;
+ }
+ if (GS(id->name) != ID_OB) {
+ return nullptr;
+ }
+ return (Object *)id;
+ }
+};
+
+} // namespace blender::bke
+
+#endif /* __BKE_PERSISTENT_DATA_HANDLE_H__ */
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index a8c6fe4416f..dc99ec7f56f 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -86,12 +86,10 @@ struct ListBase;
struct Main;
struct Object;
struct ParticleKey;
-struct ParticleSimulationState;
struct ParticleSystem;
struct PointCache;
struct RigidBodyWorld;
struct Scene;
-struct Simulation;
struct SoftBody;
struct ViewLayer;
@@ -132,7 +130,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;
@@ -147,7 +145,7 @@ typedef struct PTCacheID {
/* copies point data to cache data */
int (*write_point)(int index, void *calldata, void **data, int cfra);
/* copies cache cata to point data */
- void (*read_point)(int index, void *calldata, void **data, float cfra, float *old_data);
+ void (*read_point)(int index, void *calldata, void **data, float cfra, const float *old_data);
/* interpolated between previously read point data and cache data */
void (*interpolate_point)(int index,
void *calldata,
@@ -155,7 +153,7 @@ typedef struct PTCacheID {
float cfra,
float cfra1,
float cfra2,
- float *old_data);
+ const float *old_data);
/* copies point data to cache data */
int (*write_stream)(PTCacheFile *pf, void *calldata);
@@ -291,12 +289,11 @@ void BKE_ptcache_make_particle_key(struct ParticleKey *key, int index, void **da
void BKE_ptcache_id_from_softbody(PTCacheID *pid, struct Object *ob, struct SoftBody *sb);
void BKE_ptcache_id_from_particles(PTCacheID *pid, struct Object *ob, struct ParticleSystem *psys);
void BKE_ptcache_id_from_cloth(PTCacheID *pid, struct Object *ob, struct ClothModifierData *clmd);
-void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidModifierData *mmd);
+void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidModifierData *fmd);
void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid,
struct Object *ob,
struct DynamicPaintSurface *surface);
void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct RigidBodyWorld *rbw);
-void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, struct ParticleSimulationState *state);
PTCacheID BKE_ptcache_id_find(struct Object *ob, struct Scene *scene, struct PointCache *cache);
void BKE_ptcache_ids_from_object(struct ListBase *lb,
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index fc7146b8cf4..13716ddb5c6 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -142,6 +142,14 @@ 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.
+ *
+ * \note Some editors should return early if the interface is locked
+ * (check with #CTX_wm_interface_locked) to avoid accessing scene data
+ * that another thread may be modifying
+ */
+ 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 +219,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_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index a50f9b24c61..2873a13d1f7 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -285,6 +285,11 @@ void BKE_sequence_reload_new_file(struct Main *bmain,
struct Scene *scene,
struct Sequence *seq,
const bool lock_range);
+void BKE_sequence_movie_reload_if_needed(struct Main *bmain,
+ struct Scene *scene,
+ struct Sequence *seq,
+ bool *r_was_reloaded,
+ bool *r_can_produce_frames);
int BKE_sequencer_evaluate_frame(struct Scene *scene, int cfra);
int BKE_sequencer_get_shown_sequences(struct ListBase *seqbasep,
int cfra,
@@ -619,6 +624,11 @@ void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb,
void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int cfra);
bool BKE_sequencer_check_scene_recursion(struct Scene *scene, struct ReportList *reports);
+bool BKE_sequencer_render_loop_check(struct Sequence *seq_main, struct Sequence *seq);
+void BKE_sequencer_flag_for_removal(struct Scene *scene,
+ struct ListBase *seqbase,
+ struct Sequence *seq);
+void BKE_sequencer_remove_flagged_sequences(struct Scene *scene, struct ListBase *seqbase);
#ifdef __cplusplus
}
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_simulation.h b/source/blender/blenkernel/BKE_simulation.h
index ff6aaa5e30e..6cbe77e8de3 100644
--- a/source/blender/blenkernel/BKE_simulation.h
+++ b/source/blender/blenkernel/BKE_simulation.h
@@ -17,22 +17,44 @@
#ifndef __BKE_SIMULATION_H__
#define __BKE_SIMULATION_H__
+#include "DNA_simulation_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
struct Depsgraph;
struct Main;
-struct Simulation;
+struct Scene;
void *BKE_simulation_add(struct Main *bmain, const char *name);
void BKE_simulation_data_update(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Simulation *simulation);
+void BKE_simulation_update_dependencies(struct Simulation *simulation, struct Main *bmain);
+
+SimulationState *BKE_simulation_state_add(Simulation *simulation,
+ const char *type,
+ const char *name);
+void BKE_simulation_state_remove(Simulation *simulation, SimulationState *state);
+void BKE_simulation_state_remove_all(Simulation *simulation);
+void BKE_simulation_state_reset(Simulation *simulation, SimulationState *state);
+void BKE_simulation_state_reset_all(Simulation *simulation);
+SimulationState *BKE_simulation_state_try_find_by_name(Simulation *simulation, const char *name);
+SimulationState *BKE_simulation_state_try_find_by_name_and_type(Simulation *simulation,
+ const char *name,
+ const char *type);
+void BKE_simulation_state_copy_data(const SimulationState *src_state, SimulationState *dst_state);
#ifdef __cplusplus
}
#endif
+#ifdef __cplusplus
+
+template<typename StateType> const char *BKE_simulation_get_state_type_name();
+
+#endif
+
#endif /* __BKE_SIMULATION_H__ */
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h
index 8d2565c31f7..5808f223f32 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,31 @@ 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);
+typedef enum SubdivCCGAdjacencyType {
+ SUBDIV_CCG_ADJACENT_NONE,
+ SUBDIV_CCG_ADJACENT_VERTEX,
+ SUBDIV_CCG_ADJACENT_EDGE,
+} SubdivCCGAdjacencyType;
+
+/* Returns if a grid coordinates is adjacent to a coarse mesh edge, vertex or nothing. If it is
+ * adjacent to an edge, r_v1 and r_v2 will be set to the two vertices of that edge. If it is
+ * adjacent to a vertex, r_v1 and r_v2 will be the index of that vertex. */
+SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg,
+ const SubdivCCGCoord *coord,
+ const MLoop *mloop,
+ const MPoly *mpoly,
+ int *r_v1,
+ int *r_v2);
+
+/* 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_subsurf.h b/source/blender/blenkernel/BKE_subsurf.h
index 2dee8de4dc7..0ed58180ffa 100644
--- a/source/blender/blenkernel/BKE_subsurf.h
+++ b/source/blender/blenkernel/BKE_subsurf.h
@@ -45,7 +45,6 @@ struct Mesh;
struct MeshElemMap;
struct Object;
struct PBVH;
-struct PBVH;
struct SubsurfModifierData;
/**************************** External *****************************/
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/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index c1a548360af..ada341ff570 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -27,6 +27,7 @@ set(INC
../bmesh
../depsgraph
../draw
+ ../functions
../gpencil_modifiers
../gpu
../ikplugin
@@ -35,7 +36,7 @@ set(INC
../makesrna
../modifiers
../nodes
- ../physics
+ ../simulation
../shader_fx
../render/extern/include
../../../intern/ghost
@@ -74,6 +75,7 @@ set(SRC
intern/anim_visualization.c
intern/appdir.c
intern/armature.c
+ intern/armature_deform.c
intern/armature_update.c
intern/autoexec.c
intern/blender.c
@@ -98,13 +100,14 @@ set(SRC
intern/context.c
intern/crazyspace.c
intern/curve.c
+ intern/curve_bevel.c
intern/curve_decimate.c
+ intern/curve_deform.c
intern/curveprofile.c
intern/customdata.c
intern/customdata_file.c
intern/data_transfer.c
intern/deform.c
- intern/derived_node_tree.cc
intern/displist.c
intern/displist_tangent.c
intern/dynamicpaint.c
@@ -132,12 +135,14 @@ set(SRC
intern/idtype.c
intern/image.c
intern/image_gen.c
+ intern/image_gpu.c
intern/image_save.c
intern/ipo.c
intern/kelvinlet.c
intern/key.c
intern/keyconfig.c
intern/lattice.c
+ intern/lattice_deform.c
intern/layer.c
intern/layer_utils.c
intern/lib_id.c
@@ -184,7 +189,6 @@ set(SRC
intern/multires_unsubdivide.c
intern/nla.c
intern/node.c
- intern/node_tree_ref.cc
intern/object.c
intern/object_deform.c
intern/object_dupli.c
@@ -293,7 +297,6 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
- BKE_derived_node_tree.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h
@@ -319,6 +322,7 @@ set(SRC
BKE_idprop.h
BKE_idtype.h
BKE_image.h
+ BKE_image_save.h
BKE_ipo.h
BKE_kelvinlet.h
BKE_key.h
@@ -353,7 +357,6 @@ set(SRC
BKE_multires.h
BKE_nla.h
BKE_node.h
- BKE_node_tree_ref.hh
BKE_object.h
BKE_object_deform.h
BKE_object_facemap.h
@@ -362,6 +365,7 @@ set(SRC
BKE_packedFile.h
BKE_paint.h
BKE_particle.h
+ BKE_persistent_data_handle.hh
BKE_pbvh.h
BKE_pointcache.h
BKE_pointcloud.h
@@ -370,6 +374,7 @@ set(SRC
BKE_scene.h
BKE_screen.h
BKE_sequencer.h
+ BKE_sequencer_offscreen.h
BKE_shader_fx.h
BKE_shrinkwrap.h
BKE_simulation.h
@@ -408,6 +413,7 @@ set(SRC
intern/multires_inline.h
intern/multires_reshape.h
intern/multires_unsubdivide.h
+ intern/ocean_intern.h
intern/pbvh_intern.h
intern/subdiv_converter.h
intern/subdiv_inline.h
@@ -421,6 +427,7 @@ set(LIB
bf_bmesh
bf_depsgraph
bf_draw
+ bf_functions
bf_gpencil_modifiers
bf_gpu
bf_ikplugin
@@ -433,7 +440,7 @@ set(LIB
bf_intern_opensubdiv # Uses stub when disabled.
bf_modifiers
bf_nodes
- bf_physics
+ bf_simulation
bf_rna
bf_shader_fx
)
@@ -691,3 +698,16 @@ blender_add_lib(bf_blenkernel "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# Needed so we can use dna_type_offsets.h for defaults initialization.
add_dependencies(bf_blenkernel bf_dna)
+
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ intern/armature_test.cc
+ intern/fcurve_test.cc
+ )
+ set(TEST_INC
+ ../editors/include
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_blenkernel_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB}")
+endif()
diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c
index 98deddb4316..8d8301362b3 100644
--- a/source/blender/blenkernel/intern/CCGSubSurf.c
+++ b/source/blender/blenkernel/intern/CCGSubSurf.c
@@ -32,8 +32,6 @@
#include "CCGSubSurf.h"
#include "CCGSubSurf_intern.h"
-#include "GPU_glew.h"
-
/***/
int BKE_ccg_gridsize(int level)
@@ -177,9 +175,7 @@ static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSiz
if (v == e->v0) {
return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
}
- else {
- return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
- }
+ return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
}
static void _edge_free(CCGEdge *e, CCGSubSurf *ss)
@@ -260,46 +256,45 @@ CCGSubSurf *ccgSubSurf_new(CCGMeshIFC *ifc,
if (subdivLevels < 1) {
return NULL;
}
- else {
- CCGSubSurf *ss = allocatorIFC->alloc(allocator, sizeof(*ss));
- ss->allocatorIFC = *allocatorIFC;
- ss->allocator = allocator;
+ CCGSubSurf *ss = allocatorIFC->alloc(allocator, sizeof(*ss));
- ss->vMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
- ss->eMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
- ss->fMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
+ ss->allocatorIFC = *allocatorIFC;
+ ss->allocator = allocator;
- ss->meshIFC = *ifc;
+ ss->vMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
+ ss->eMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
+ ss->fMap = ccg_ehash_new(0, &ss->allocatorIFC, ss->allocator);
- ss->subdivLevels = subdivLevels;
- ss->numGrids = 0;
- ss->allowEdgeCreation = 0;
- ss->defaultCreaseValue = 0;
- ss->defaultEdgeUserData = NULL;
+ ss->meshIFC = *ifc;
- ss->useAgeCounts = 0;
- ss->vertUserAgeOffset = ss->edgeUserAgeOffset = ss->faceUserAgeOffset = 0;
+ ss->subdivLevels = subdivLevels;
+ ss->numGrids = 0;
+ ss->allowEdgeCreation = 0;
+ ss->defaultCreaseValue = 0;
+ ss->defaultEdgeUserData = NULL;
- ss->calcVertNormals = 0;
- ss->normalDataOffset = 0;
+ ss->useAgeCounts = 0;
+ ss->vertUserAgeOffset = ss->edgeUserAgeOffset = ss->faceUserAgeOffset = 0;
- ss->allocMask = 0;
+ ss->calcVertNormals = 0;
+ ss->normalDataOffset = 0;
- ss->q = CCGSUBSURF_alloc(ss, ss->meshIFC.vertDataSize);
- ss->r = CCGSUBSURF_alloc(ss, ss->meshIFC.vertDataSize);
+ ss->allocMask = 0;
- ss->currentAge = 0;
+ ss->q = CCGSUBSURF_alloc(ss, ss->meshIFC.vertDataSize);
+ ss->r = CCGSUBSURF_alloc(ss, ss->meshIFC.vertDataSize);
- ss->syncState = eSyncState_None;
+ ss->currentAge = 0;
- ss->oldVMap = ss->oldEMap = ss->oldFMap = NULL;
- ss->lenTempArrays = 0;
- ss->tempVerts = NULL;
- ss->tempEdges = NULL;
+ ss->syncState = eSyncState_None;
- return ss;
- }
+ ss->oldVMap = ss->oldEMap = ss->oldFMap = NULL;
+ ss->lenTempArrays = 0;
+ ss->tempVerts = NULL;
+ ss->tempEdges = NULL;
+
+ return ss;
}
void ccgSubSurf_free(CCGSubSurf *ss)
@@ -378,7 +373,7 @@ CCGError ccgSubSurf_setSubdivisionLevels(CCGSubSurf *ss, int subdivisionLevels)
if (subdivisionLevels <= 0) {
return eCCGError_InvalidValue;
}
- else if (subdivisionLevels != ss->subdivLevels) {
+ if (subdivisionLevels != ss->subdivLevels) {
ss->numGrids = 0;
ss->subdivLevels = subdivisionLevels;
ccg_ehash_free(ss->vMap, (EHEntryFreeFP)_vert_free, ss);
@@ -420,12 +415,10 @@ CCGError ccgSubSurf_setUseAgeCounts(
(faceUserOffset + 4 > ss->meshIFC.faceUserSize)) {
return eCCGError_InvalidValue;
}
- else {
- ss->useAgeCounts = 1;
- ss->vertUserAgeOffset = vertUserOffset;
- ss->edgeUserAgeOffset = edgeUserOffset;
- ss->faceUserAgeOffset = faceUserOffset;
- }
+ ss->useAgeCounts = 1;
+ ss->vertUserAgeOffset = vertUserOffset;
+ ss->edgeUserAgeOffset = edgeUserOffset;
+ ss->faceUserAgeOffset = faceUserOffset;
}
else {
ss->useAgeCounts = 0;
@@ -441,10 +434,8 @@ CCGError ccgSubSurf_setCalcVertexNormals(CCGSubSurf *ss, int useVertNormals, int
if (normalDataOffset < 0 || normalDataOffset + 12 > ss->meshIFC.vertDataSize) {
return eCCGError_InvalidValue;
}
- else {
- ss->calcVertNormals = 1;
- ss->normalDataOffset = normalDataOffset;
- }
+ ss->calcVertNormals = 1;
+ ss->normalDataOffset = normalDataOffset;
}
else {
ss->calcVertNormals = 0;
@@ -512,19 +503,17 @@ CCGError ccgSubSurf_syncVertDel(CCGSubSurf *ss, CCGVertHDL vHDL)
if (ss->syncState != eSyncState_Partial) {
return eCCGError_InvalidSyncState;
}
- else {
- void **prevp;
- CCGVert *v = ccg_ehash_lookupWithPrev(ss->vMap, vHDL, &prevp);
- if (!v || v->numFaces || v->numEdges) {
- return eCCGError_InvalidValue;
- }
- else {
- *prevp = v->next;
- _vert_free(v, ss);
- }
+ void **prevp;
+ CCGVert *v = ccg_ehash_lookupWithPrev(ss->vMap, vHDL, &prevp);
+
+ if (!v || v->numFaces || v->numEdges) {
+ return eCCGError_InvalidValue;
}
+ *prevp = v->next;
+ _vert_free(v, ss);
+
return eCCGError_None;
}
@@ -533,19 +522,17 @@ CCGError ccgSubSurf_syncEdgeDel(CCGSubSurf *ss, CCGEdgeHDL eHDL)
if (ss->syncState != eSyncState_Partial) {
return eCCGError_InvalidSyncState;
}
- else {
- void **prevp;
- CCGEdge *e = ccg_ehash_lookupWithPrev(ss->eMap, eHDL, &prevp);
- if (!e || e->numFaces) {
- return eCCGError_InvalidValue;
- }
- else {
- *prevp = e->next;
- _edge_unlinkMarkAndFree(e, ss);
- }
+ void **prevp;
+ CCGEdge *e = ccg_ehash_lookupWithPrev(ss->eMap, eHDL, &prevp);
+
+ if (!e || e->numFaces) {
+ return eCCGError_InvalidValue;
}
+ *prevp = e->next;
+ _edge_unlinkMarkAndFree(e, ss);
+
return eCCGError_None;
}
@@ -554,19 +541,17 @@ CCGError ccgSubSurf_syncFaceDel(CCGSubSurf *ss, CCGFaceHDL fHDL)
if (ss->syncState != eSyncState_Partial) {
return eCCGError_InvalidSyncState;
}
- else {
- void **prevp;
- CCGFace *f = ccg_ehash_lookupWithPrev(ss->fMap, fHDL, &prevp);
- if (!f) {
- return eCCGError_InvalidValue;
- }
- else {
- *prevp = f->next;
- _face_unlinkMarkAndFree(f, ss);
- }
+ void **prevp;
+ CCGFace *f = ccg_ehash_lookupWithPrev(ss->fMap, fHDL, &prevp);
+
+ if (!f) {
+ return eCCGError_InvalidValue;
}
+ *prevp = f->next;
+ _face_unlinkMarkAndFree(f, ss);
+
return eCCGError_None;
}
@@ -1264,9 +1249,7 @@ int ccgSubSurf_getEdgeLevelSize(const CCGSubSurf *ss, int level)
if (level < 1 || level > ss->subdivLevels) {
return -1;
}
- else {
- return ccg_edgesize(level);
- }
+ return ccg_edgesize(level);
}
int ccgSubSurf_getGridSize(const CCGSubSurf *ss)
{
@@ -1277,9 +1260,7 @@ int ccgSubSurf_getGridLevelSize(const CCGSubSurf *ss, int level)
if (level < 1 || level > ss->subdivLevels) {
return -1;
}
- else {
- return ccg_gridsize(level);
- }
+ return ccg_gridsize(level);
}
int ccgSubSurf_getSimpleSubdiv(const CCGSubSurf *ss)
@@ -1299,9 +1280,7 @@ int ccgSubSurf_getVertAge(CCGSubSurf *ss, CCGVert *v)
byte *userData = ccgSubSurf_getVertUserData(ss, v);
return ss->currentAge - *((int *)&userData[ss->vertUserAgeOffset]);
}
- else {
- return 0;
- }
+ return 0;
}
void *ccgSubSurf_getVertUserData(CCGSubSurf *ss, CCGVert *v)
{
@@ -1316,9 +1295,7 @@ CCGFace *ccgSubSurf_getVertFace(CCGVert *v, int index)
if (index < 0 || index >= v->numFaces) {
return NULL;
}
- else {
- return v->faces[index];
- }
+ return v->faces[index];
}
int ccgSubSurf_getVertNumEdges(CCGVert *v)
{
@@ -1329,9 +1306,7 @@ CCGEdge *ccgSubSurf_getVertEdge(CCGVert *v, int index)
if (index < 0 || index >= v->numEdges) {
return NULL;
}
- else {
- return v->edges[index];
- }
+ return v->edges[index];
}
void *ccgSubSurf_getVertData(CCGSubSurf *ss, CCGVert *v)
{
@@ -1342,9 +1317,7 @@ void *ccgSubSurf_getVertLevelData(CCGSubSurf *ss, CCGVert *v, int level)
if (level < 0 || level > ss->subdivLevels) {
return NULL;
}
- else {
- return ccg_vert_getCo(v, level, ss->meshIFC.vertDataSize);
- }
+ return ccg_vert_getCo(v, level, ss->meshIFC.vertDataSize);
}
/* Edge accessors */
@@ -1359,9 +1332,7 @@ int ccgSubSurf_getEdgeAge(CCGSubSurf *ss, CCGEdge *e)
byte *userData = ccgSubSurf_getEdgeUserData(ss, e);
return ss->currentAge - *((int *)&userData[ss->edgeUserAgeOffset]);
}
- else {
- return 0;
- }
+ return 0;
}
void *ccgSubSurf_getEdgeUserData(CCGSubSurf *ss, CCGEdge *e)
{
@@ -1376,9 +1347,7 @@ CCGFace *ccgSubSurf_getEdgeFace(CCGEdge *e, int index)
if (index < 0 || index >= e->numFaces) {
return NULL;
}
- else {
- return e->faces[index];
- }
+ return e->faces[index];
}
CCGVert *ccgSubSurf_getEdgeVert0(CCGEdge *e)
{
@@ -1401,9 +1370,7 @@ void *ccgSubSurf_getEdgeLevelData(CCGSubSurf *ss, CCGEdge *e, int x, int level)
if (level < 0 || level > ss->subdivLevels) {
return NULL;
}
- else {
- return ccg_edge_getCo(e, level, x, ss->meshIFC.vertDataSize);
- }
+ return ccg_edge_getCo(e, level, x, ss->meshIFC.vertDataSize);
}
float ccgSubSurf_getEdgeCrease(CCGEdge *e)
{
@@ -1422,9 +1389,7 @@ int ccgSubSurf_getFaceAge(CCGSubSurf *ss, CCGFace *f)
byte *userData = ccgSubSurf_getFaceUserData(ss, f);
return ss->currentAge - *((int *)&userData[ss->faceUserAgeOffset]);
}
- else {
- return 0;
- }
+ return 0;
}
void *ccgSubSurf_getFaceUserData(CCGSubSurf *ss, CCGFace *f)
{
@@ -1442,18 +1407,14 @@ CCGVert *ccgSubSurf_getFaceVert(CCGFace *f, int index)
if (index < 0 || index >= f->numVerts) {
return NULL;
}
- else {
- return FACE_getVerts(f)[index];
- }
+ return FACE_getVerts(f)[index];
}
CCGEdge *ccgSubSurf_getFaceEdge(CCGFace *f, int index)
{
if (index < 0 || index >= f->numVerts) {
return NULL;
}
- else {
- return FACE_getEdges(f)[index];
- }
+ return FACE_getEdges(f)[index];
}
int ccgSubSurf_getFaceEdgeIndex(CCGFace *f, CCGEdge *e)
{
diff --git a/source/blender/blenkernel/intern/CCGSubSurf_legacy.c b/source/blender/blenkernel/intern/CCGSubSurf_legacy.c
index f3f681baa01..8fc9afd58f1 100644
--- a/source/blender/blenkernel/intern/CCGSubSurf_legacy.c
+++ b/source/blender/blenkernel/intern/CCGSubSurf_legacy.c
@@ -38,9 +38,7 @@ static void *_edge_getCoVert(CCGEdge *e, CCGVert *v, int lvl, int x, int dataSiz
if (v == e->v0) {
return &EDGE_getLevelData(e)[dataSize * (levelBase + x)];
}
- else {
- return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
- }
+ return &EDGE_getLevelData(e)[dataSize * (levelBase + (1 << lvl) - x)];
}
/* *************************************************** */
@@ -65,9 +63,8 @@ static CCGVert *_edge_getOtherVert(CCGEdge *e, CCGVert *vQ)
if (vQ == e->v0) {
return e->v1;
}
- else {
- return e->v0;
- }
+
+ return e->v0;
}
static float *_face_getIFNoEdge(CCGFace *f,
@@ -111,15 +108,13 @@ static float EDGE_getSharpness(CCGEdge *e, int lvl)
if (!lvl) {
return e->crease;
}
- else if (!e->crease) {
+ if (!e->crease) {
return 0.0f;
}
- else if (e->crease - lvl < 0.0f) {
+ if (e->crease - lvl < 0.0f) {
return 0.0f;
}
- else {
- return e->crease - lvl;
- }
+ return e->crease - lvl;
}
typedef struct CCGSubSurfCalcSubdivData {
diff --git a/source/blender/blenkernel/intern/CCGSubSurf_util.c b/source/blender/blenkernel/intern/CCGSubSurf_util.c
index 58d5f2e0495..bc63d8b97f7 100644
--- a/source/blender/blenkernel/intern/CCGSubSurf_util.c
+++ b/source/blender/blenkernel/intern/CCGSubSurf_util.c
@@ -304,7 +304,7 @@ void ccgSubSurf__dumpCoords(CCGSubSurf *ss)
}
for (x = 0; x < gridSize; x++) {
float *co = FACE_getIECo(f, subdivLevels, S, x);
- printf("face index=%d. cornder=%d, ie_index=%d, coord=(%f, %f, %f)\n",
+ printf("face index=%d. corner=%d, ie_index=%d, coord=(%f, %f, %f)\n",
index,
S,
x,
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index b4e2cd772c9..b40cc4e8b9f 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -424,15 +424,14 @@ int DM_release(DerivedMesh *dm)
return 1;
}
- else {
- CustomData_free_temporary(&dm->vertData, dm->numVertData);
- CustomData_free_temporary(&dm->edgeData, dm->numEdgeData);
- CustomData_free_temporary(&dm->faceData, dm->numTessFaceData);
- CustomData_free_temporary(&dm->loopData, dm->numLoopData);
- CustomData_free_temporary(&dm->polyData, dm->numPolyData);
- return 0;
- }
+ CustomData_free_temporary(&dm->vertData, dm->numVertData);
+ CustomData_free_temporary(&dm->edgeData, dm->numEdgeData);
+ CustomData_free_temporary(&dm->faceData, dm->numTessFaceData);
+ CustomData_free_temporary(&dm->loopData, dm->numLoopData);
+ CustomData_free_temporary(&dm->polyData, dm->numPolyData);
+
+ return 0;
}
void DM_DupPolys(DerivedMesh *source, DerivedMesh *target)
@@ -693,11 +692,9 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free)
if (em) {
return get_editbmesh_orco_verts(em);
}
- else {
- return BKE_mesh_orco_verts_get(ob);
- }
+ return BKE_mesh_orco_verts_get(ob);
}
- else if (layer == CD_CLOTH_ORCO) {
+ if (layer == CD_CLOTH_ORCO) {
/* apply shape key for cloth, this should really be solved
* by a more flexible customdata system, but not simple */
if (!em) {
@@ -1060,9 +1057,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
}
continue;
}
- else {
- BKE_modifier_set_error(md, "Sculpt: Hide, Mask and optimized display disabled");
- }
+ BKE_modifier_set_error(md, "Sculpt: Hide, Mask and optimized display disabled");
}
if (need_mapping && !BKE_modifier_supports_mapping(md)) {
@@ -1669,7 +1664,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..fa7eee83a68 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;
}
@@ -1512,8 +1512,10 @@ short action_get_item_transforms(bAction *act, Object *ob, bPoseChannel *pchan,
/* ************** Pose Management Tools ****************** */
-/* for do_all_pose_actions, clears the pose. Now also exported for proxy and tools */
-void BKE_pose_rest(bPose *pose)
+/**
+ * Zero the pose transforms for the entire pose or only for selected bones.
+ */
+void BKE_pose_rest(bPose *pose, bool selected_bones_only)
{
bPoseChannel *pchan;
@@ -1525,6 +1527,9 @@ void BKE_pose_rest(bPose *pose)
memset(pose->cyclic_offset, 0, sizeof(pose->cyclic_offset));
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
+ if (selected_bones_only && pchan->bone != NULL && (pchan->bone->flag & BONE_SELECTED) == 0) {
+ continue;
+ }
zero_v3(pchan->loc);
zero_v3(pchan->eul);
unit_qt(pchan->quat);
@@ -1612,8 +1617,12 @@ void BKE_pose_tag_recalc(Main *bmain, bPose *pose)
/* For the calculation of the effects of an Action at the given frame on an object
* This is currently only used for the Action Constraint
*/
-void what_does_obaction(
- Object *ob, Object *workob, bPose *pose, bAction *act, char groupname[], float cframe)
+void what_does_obaction(Object *ob,
+ Object *workob,
+ bPose *pose,
+ bAction *act,
+ char groupname[],
+ const AnimationEvalContext *anim_eval_context)
{
bActionGroup *agrp = BKE_action_group_find_name(act, groupname);
@@ -1669,7 +1678,7 @@ void what_does_obaction(
RNA_id_pointer_create(&workob->id, &id_ptr);
/* execute action for this group only */
- animsys_evaluate_action_group(&id_ptr, act, agrp, cframe);
+ animsys_evaluate_action_group(&id_ptr, act, agrp, anim_eval_context);
}
else {
AnimData adt = {NULL};
@@ -1680,6 +1689,6 @@ void what_does_obaction(
adt.action = act;
/* execute effects of Action on to workob (or it's PoseChannels) */
- BKE_animsys_evaluate_animdata(&workob->id, &adt, cframe, ADT_RECALC_ANIM, false);
+ BKE_animsys_evaluate_animdata(&workob->id, &adt, anim_eval_context, ADT_RECALC_ANIM, false);
}
}
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index cbb34cbee30..61181278c60 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -130,9 +130,7 @@ AnimData *BKE_animdata_from_id(ID *id)
IdAdtTemplate *iat = (IdAdtTemplate *)id;
return iat->adt;
}
- else {
- return NULL;
- }
+ return NULL;
}
/* Add AnimData to the given ID-block. In order for this to work, we assume that
@@ -161,9 +159,7 @@ AnimData *BKE_animdata_add_id(ID *id)
return iat->adt;
}
- else {
- return NULL;
- }
+ return NULL;
}
/* Action Setter --------------------------------------- */
@@ -379,17 +375,19 @@ bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag)
return true;
}
-void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid)
+static void animdata_copy_id_action(Main *bmain,
+ ID *id,
+ const bool set_newid,
+ const bool do_linked_id)
{
- const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id);
AnimData *adt = BKE_animdata_from_id(id);
if (adt) {
- if (adt->action && (!is_id_liboverride || !ID_IS_LINKED(adt->action))) {
+ if (adt->action && (do_linked_id || !ID_IS_LINKED(adt->action))) {
id_us_min((ID *)adt->action);
adt->action = set_newid ? ID_NEW_SET(adt->action, BKE_action_copy(bmain, adt->action)) :
BKE_action_copy(bmain, adt->action);
}
- if (adt->tmpact && (!is_id_liboverride || !ID_IS_LINKED(adt->tmpact))) {
+ if (adt->tmpact && (do_linked_id || !ID_IS_LINKED(adt->tmpact))) {
id_us_min((ID *)adt->tmpact);
adt->tmpact = set_newid ? ID_NEW_SET(adt->tmpact, BKE_action_copy(bmain, adt->tmpact)) :
BKE_action_copy(bmain, adt->tmpact);
@@ -397,7 +395,24 @@ void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid)
}
bNodeTree *ntree = ntreeFromID(id);
if (ntree) {
- BKE_animdata_copy_id_action(bmain, &ntree->id, set_newid);
+ animdata_copy_id_action(bmain, &ntree->id, set_newid, do_linked_id);
+ }
+ /* Note that collections are not animatable currently, so no need to handle scenes' master
+ * collection here. */
+}
+
+void BKE_animdata_copy_id_action(Main *bmain, ID *id)
+{
+ const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id);
+ animdata_copy_id_action(bmain, id, false, !is_id_liboverride);
+}
+
+void BKE_animdata_duplicate_id_action(struct Main *bmain,
+ struct ID *id,
+ const eDupli_ID_Flags duplicate_flags)
+{
+ if (duplicate_flags & USER_DUP_ACT) {
+ animdata_copy_id_action(bmain, id, true, (duplicate_flags & USER_DUP_LINKED_ID) != 0);
}
}
@@ -773,10 +788,9 @@ static char *rna_path_rename_fix(ID *owner_id,
MEM_freeN(oldpath);
return newPath;
}
- else {
- /* still couldn't resolve the path... so, might as well just leave it alone */
- MEM_freeN(newPath);
- }
+
+ /* still couldn't resolve the path... so, might as well just leave it alone */
+ MEM_freeN(newPath);
}
}
diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c
index e073bd6fc82..628e54971ce 100644
--- a/source/blender/blenkernel/intern/anim_path.c
+++ b/source/blender/blenkernel/intern/anim_path.c
@@ -42,9 +42,11 @@ static CLG_LogRef LOG = {"bke.anim"};
/* ******************************************************************** */
/* Curve Paths - for curve deforms and/or curve following */
-/* free curve path data
- * NOTE: frees the path itself!
- * NOTE: this is increasingly inaccurate with non-uniform BevPoint subdivisions [#24633]
+/**
+ * Free curve path data
+ *
+ * \note Frees the path itself!
+ * \note This is increasingly inaccurate with non-uniform #BevPoint subdivisions T24633.
*/
void free_path(Path *path)
{
@@ -54,8 +56,9 @@ void free_path(Path *path)
MEM_freeN(path);
}
-/* calculate a curve-deform path for a curve
- * - only called from displist.c -> do_makeDispListCurveTypes
+/**
+ * Calculate a curve-deform path for a curve
+ * - Only called from displist.c -> #do_makeDispListCurveTypes
*/
void calc_curvepath(Object *ob, ListBase *nurbs)
{
@@ -192,39 +195,40 @@ static int interval_test(const int min, const int max, int p1, const int cycl)
return p1;
}
-/* calculate the deformation implied by the curve path at a given parametric position,
+/**
+ * Calculate the deformation implied by the curve path at a given parametric position,
* and returns whether this operation succeeded.
*
- * note: ctime is normalized range <0-1>
+ * \param ctime: Time is normalized range <0-1>.
*
- * returns OK: 1/0
+ * \return success.
*/
-int where_on_path(Object *ob,
- float ctime,
- float vec[4],
- float dir[3],
- float quat[4],
- float *radius,
- float *weight)
+bool where_on_path(const Object *ob,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius,
+ float *r_weight)
{
Curve *cu;
- Nurb *nu;
- BevList *bl;
- Path *path;
- PathPoint *pp, *p0, *p1, *p2, *p3;
+ const Nurb *nu;
+ const BevList *bl;
+ const Path *path;
+ const PathPoint *pp, *p0, *p1, *p2, *p3;
float fac;
float data[4];
int cycl = 0, s0, s1, s2, s3;
- ListBase *nurbs;
+ const ListBase *nurbs;
if (ob == NULL || ob->type != OB_CURVE) {
- return 0;
+ return false;
}
cu = ob->data;
if (ob->runtime.curve_cache == NULL || ob->runtime.curve_cache->path == NULL ||
ob->runtime.curve_cache->path->data == NULL) {
CLOG_WARN(&LOG, "no path!");
- return 0;
+ return false;
}
path = ob->runtime.curve_cache->path;
pp = path->data;
@@ -232,10 +236,10 @@ int where_on_path(Object *ob,
/* test for cyclic */
bl = ob->runtime.curve_cache->bev.first;
if (!bl) {
- return 0;
+ return false;
}
if (!bl->nr) {
- return 0;
+ return false;
}
if (bl->poly > -1) {
cycl = 1;
@@ -262,7 +266,7 @@ int where_on_path(Object *ob,
/* NOTE: commented out for follow constraint
*
- * If it's ever be uncommented watch out for curve_deform_verts()
+ * If it's ever be uncommented watch out for BKE_curve_deform_coords()
* which used to temporary set CU_FOLLOW flag for the curve and no
* longer does it (because of threading issues of such a thing.
*/
@@ -270,10 +274,10 @@ int where_on_path(Object *ob,
key_curve_tangent_weights(1.0f - fac, data, KEY_BSPLINE);
- interp_v3_v3v3v3v3(dir, p0->vec, p1->vec, p2->vec, p3->vec, data);
+ interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, data);
- /* make compatible with vectoquat */
- negate_v3(dir);
+ /* Make compatible with #vec_to_quat. */
+ negate_v3(r_dir);
//}
nurbs = BKE_curve_editNurbs_get(cu);
@@ -296,16 +300,16 @@ int where_on_path(Object *ob,
key_curve_position_weights(1.0f - fac, data, KEY_BSPLINE);
}
- vec[0] = data[0] * p0->vec[0] + data[1] * p1->vec[0] + data[2] * p2->vec[0] +
- data[3] * p3->vec[0]; /* X */
- vec[1] = data[0] * p0->vec[1] + data[1] * p1->vec[1] + data[2] * p2->vec[1] +
- data[3] * p3->vec[1]; /* Y */
- vec[2] = data[0] * p0->vec[2] + data[1] * p1->vec[2] + data[2] * p2->vec[2] +
- data[3] * p3->vec[2]; /* Z */
- vec[3] = data[0] * p0->vec[3] + data[1] * p1->vec[3] + data[2] * p2->vec[3] +
- data[3] * p3->vec[3]; /* Tilt, should not be needed since we have quat still used */
+ r_vec[0] = /* X */
+ data[0] * p0->vec[0] + data[1] * p1->vec[0] + data[2] * p2->vec[0] + data[3] * p3->vec[0];
+ r_vec[1] = /* Y */
+ data[0] * p0->vec[1] + data[1] * p1->vec[1] + data[2] * p2->vec[1] + data[3] * p3->vec[1];
+ r_vec[2] = /* Z */
+ data[0] * p0->vec[2] + data[1] * p1->vec[2] + data[2] * p2->vec[2] + data[3] * p3->vec[2];
+ r_vec[3] = /* Tilt, should not be needed since we have quat still used */
+ data[0] * p0->vec[3] + data[1] * p1->vec[3] + data[2] * p2->vec[3] + data[3] * p3->vec[3];
- if (quat) {
+ if (r_quat) {
float totfac, q1[4], q2[4];
totfac = data[0] + data[3];
@@ -326,22 +330,22 @@ int where_on_path(Object *ob,
totfac = data[0] + data[1] + data[2] + data[3];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(quat, q1, q2, (data[1] + data[2]) / totfac);
+ interp_qt_qtqt(r_quat, q1, q2, (data[1] + data[2]) / totfac);
}
else {
- copy_qt_qt(quat, q2);
+ copy_qt_qt(r_quat, q2);
}
}
- if (radius) {
- *radius = data[0] * p0->radius + data[1] * p1->radius + data[2] * p2->radius +
- data[3] * p3->radius;
+ if (r_radius) {
+ *r_radius = data[0] * p0->radius + data[1] * p1->radius + data[2] * p2->radius +
+ data[3] * p3->radius;
}
- if (weight) {
- *weight = data[0] * p0->weight + data[1] * p1->weight + data[2] * p2->weight +
- data[3] * p3->weight;
+ if (r_weight) {
+ *r_weight = data[0] * p0->weight + data[1] * p1->weight + data[2] * p2->weight +
+ data[3] * p3->weight;
}
- return 1;
+ return true;
}
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 5e4b280d0d0..ea5a4bd99d1 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -538,7 +538,7 @@ static void animsys_write_orig_anim_rna(PointerRNA *ptr,
*/
static void animsys_evaluate_fcurves(PointerRNA *ptr,
ListBase *list,
- float ctime,
+ const AnimationEvalContext *anim_eval_context,
bool flush_to_original)
{
/* Calculate then execute each curve. */
@@ -557,7 +557,7 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr,
}
PathResolvedRNA anim_rna;
if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) {
- const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ const float curval = calculate_fcurve(&anim_rna, fcu, anim_eval_context);
BKE_animsys_write_rna_setting(&anim_rna, curval);
if (flush_to_original) {
animsys_write_orig_anim_rna(ptr, fcu->rna_path, fcu->array_index, curval);
@@ -569,8 +569,26 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr,
/* ***************************************** */
/* Driver Evaluation */
+AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph,
+ float eval_time)
+{
+ AnimationEvalContext ctx = {
+ .depsgraph = depsgraph,
+ .eval_time = eval_time,
+ };
+ return ctx;
+}
+
+AnimationEvalContext BKE_animsys_eval_context_construct_at(
+ const AnimationEvalContext *anim_eval_context, float eval_time)
+{
+ return BKE_animsys_eval_context_construct(anim_eval_context->depsgraph, eval_time);
+}
+
/* Evaluate Drivers */
-static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime)
+static void animsys_evaluate_drivers(PointerRNA *ptr,
+ AnimData *adt,
+ const AnimationEvalContext *anim_eval_context)
{
FCurve *fcu;
@@ -591,7 +609,7 @@ static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime
* before adding new to only be done when drivers only changed. */
PathResolvedRNA anim_rna;
if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) {
- const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ const float curval = calculate_fcurve(&anim_rna, fcu, anim_eval_context);
ok = BKE_animsys_write_rna_setting(&anim_rna, curval);
}
@@ -619,9 +637,8 @@ static void action_idcode_patch_check(ID *id, bAction *act)
if (ELEM(NULL, id, act)) {
return;
}
- else {
- idcode = GS(id->name);
- }
+
+ idcode = GS(id->name);
/* the actual checks... hopefully not too much of a performance hit in the long run... */
if (act->idroot == 0) {
@@ -648,7 +665,10 @@ static void action_idcode_patch_check(ID *id, bAction *act)
/* ----------------------------------------- */
/* Evaluate Action Group */
-void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup *agrp, float ctime)
+void animsys_evaluate_action_group(PointerRNA *ptr,
+ bAction *act,
+ bActionGroup *agrp,
+ const AnimationEvalContext *anim_eval_context)
{
FCurve *fcu;
@@ -670,7 +690,7 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup *
if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0 && !BKE_fcurve_is_empty(fcu)) {
PathResolvedRNA anim_rna;
if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) {
- const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ const float curval = calculate_fcurve(&anim_rna, fcu, anim_eval_context);
BKE_animsys_write_rna_setting(&anim_rna, curval);
}
}
@@ -680,7 +700,7 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup *
/* Evaluate Action (F-Curve Bag) */
static void animsys_evaluate_action_ex(PointerRNA *ptr,
bAction *act,
- float ctime,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
/* check if mapper is appropriate for use here (we set to NULL if it's inappropriate) */
@@ -691,15 +711,15 @@ static void animsys_evaluate_action_ex(PointerRNA *ptr,
action_idcode_patch_check(ptr->owner_id, act);
/* calculate then execute each curve */
- animsys_evaluate_fcurves(ptr, &act->curves, ctime, flush_to_original);
+ animsys_evaluate_fcurves(ptr, &act->curves, anim_eval_context, flush_to_original);
}
void animsys_evaluate_action(PointerRNA *ptr,
bAction *act,
- float ctime,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
- animsys_evaluate_action_ex(ptr, act, ctime, flush_to_original);
+ animsys_evaluate_action_ex(ptr, act, anim_eval_context, flush_to_original);
}
/* ***************************************** */
@@ -717,18 +737,19 @@ static float nlastrip_get_influence(NlaStrip *strip, float cframe)
/* there is some blend-in */
return fabsf(cframe - strip->start) / (strip->blendin);
}
- else if (IS_EQF(strip->blendout, 0.0f) == false && (cframe >= (strip->end - strip->blendout))) {
+ if (IS_EQF(strip->blendout, 0.0f) == false && (cframe >= (strip->end - strip->blendout))) {
/* there is some blend-out */
return fabsf(strip->end - cframe) / (strip->blendout);
}
- else {
- /* in the middle of the strip, we should be full strength */
- return 1.0f;
- }
+
+ /* in the middle of the strip, we should be full strength */
+ return 1.0f;
}
/* evaluate the evaluation time and influence for the strip, storing the results in the strip */
-static void nlastrip_evaluate_controls(NlaStrip *strip, float ctime, const bool flush_to_original)
+static void nlastrip_evaluate_controls(NlaStrip *strip,
+ const AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original)
{
/* now strip's evaluate F-Curves for these settings (if applicable) */
if (strip->fcurves.first) {
@@ -738,7 +759,7 @@ static void nlastrip_evaluate_controls(NlaStrip *strip, float ctime, const bool
RNA_pointer_create(NULL, &RNA_NlaStrip, strip, &strip_ptr);
/* execute these settings as per normal */
- animsys_evaluate_fcurves(&strip_ptr, &strip->fcurves, ctime, flush_to_original);
+ animsys_evaluate_fcurves(&strip_ptr, &strip->fcurves, anim_eval_context, flush_to_original);
}
/* analytically generate values for influence and time (if applicable)
@@ -746,17 +767,18 @@ static void nlastrip_evaluate_controls(NlaStrip *strip, float ctime, const bool
* in case the override has been turned off.
*/
if ((strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) == 0) {
- strip->influence = nlastrip_get_influence(strip, ctime);
+ strip->influence = nlastrip_get_influence(strip, anim_eval_context->eval_time);
}
/* Bypass evaluation time computation if time mapping is disabled. */
if ((strip->flag & NLASTRIP_FLAG_NO_TIME_MAP) != 0) {
- strip->strip_time = ctime;
+ strip->strip_time = anim_eval_context->eval_time;
return;
}
if ((strip->flag & NLASTRIP_FLAG_USR_TIME) == 0) {
- strip->strip_time = nlastrip_get_frame(strip, ctime, NLATIME_CONVERT_EVAL);
+ strip->strip_time = nlastrip_get_frame(
+ strip, anim_eval_context->eval_time, NLATIME_CONVERT_EVAL);
}
/* if user can control the evaluation time (using F-Curves), consider the option which allows
@@ -770,12 +792,16 @@ static void nlastrip_evaluate_controls(NlaStrip *strip, float ctime, const bool
}
/* gets the strip active at the current time for a list of strips for evaluation purposes */
-NlaEvalStrip *nlastrips_ctime_get_strip(
- ListBase *list, ListBase *strips, short index, float ctime, const bool flush_to_original)
+NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list,
+ ListBase *strips,
+ short index,
+ const AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original)
{
NlaStrip *strip, *estrip = NULL;
NlaEvalStrip *nes;
short side = 0;
+ float ctime = anim_eval_context->eval_time;
/* loop over strips, checking if they fall within the range */
for (strip = strips->first; strip; strip = strip->next) {
@@ -854,7 +880,9 @@ NlaEvalStrip *nlastrips_ctime_get_strip(
*/
/* TODO: this sounds a bit hacky having a few isolated F-Curves
* stuck on some data it operates on... */
- nlastrip_evaluate_controls(estrip, ctime, flush_to_original);
+ AnimationEvalContext clamped_eval_context = BKE_animsys_eval_context_construct_at(
+ anim_eval_context, ctime);
+ nlastrip_evaluate_controls(estrip, &clamped_eval_context, flush_to_original);
if (estrip->influence <= 0.0f) {
return NULL;
}
@@ -876,8 +904,12 @@ NlaEvalStrip *nlastrips_ctime_get_strip(
}
/* evaluate controls for the relevant extents of the bordering strips... */
- nlastrip_evaluate_controls(estrip->prev, estrip->start, flush_to_original);
- nlastrip_evaluate_controls(estrip->next, estrip->end, flush_to_original);
+ AnimationEvalContext start_eval_context = BKE_animsys_eval_context_construct_at(
+ anim_eval_context, estrip->start);
+ AnimationEvalContext end_eval_context = BKE_animsys_eval_context_construct_at(
+ anim_eval_context, estrip->end);
+ nlastrip_evaluate_controls(estrip->prev, &start_eval_context, flush_to_original);
+ nlastrip_evaluate_controls(estrip->next, &end_eval_context, flush_to_original);
break;
}
@@ -1115,9 +1147,7 @@ static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index)
return -1;
}
- else {
- return 0;
- }
+ return 0;
}
/* Initialise default values for NlaEvalChannel from the property data. */
@@ -1209,15 +1239,13 @@ static char nlaevalchan_detect_mix_mode(NlaEvalChannelKey *key, int length)
if (subtype == PROP_QUATERNION && length == 4) {
return NEC_MIX_QUATERNION;
}
- else if (subtype == PROP_AXISANGLE && length == 4) {
+ if (subtype == PROP_AXISANGLE && length == 4) {
return NEC_MIX_AXIS_ANGLE;
}
- else if (RNA_property_flag(key->prop) & PROP_PROPORTIONAL) {
+ if (RNA_property_flag(key->prop) & PROP_PROPORTIONAL) {
return NEC_MIX_MULTIPLY;
}
- else {
- return NEC_MIX_ADD;
- }
+ return NEC_MIX_ADD;
}
/* Verify that an appropriate NlaEvalChannel for this property exists. */
@@ -1454,10 +1482,9 @@ static bool nla_invert_combine_value(int mix_mode,
/* Division by zero. */
return false;
}
- else {
- *r_value = base_value * powf(target_value / old_value, 1.0f / influence);
- return true;
- }
+
+ *r_value = base_value * powf(target_value / old_value, 1.0f / influence);
+ return true;
case NEC_MIX_QUATERNION:
default:
@@ -1802,6 +1829,7 @@ static void nlastrip_evaluate_transition(PointerRNA *ptr,
ListBase *modifiers,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
ListBase tmp_modifiers = {NULL, NULL};
@@ -1843,13 +1871,15 @@ static void nlastrip_evaluate_transition(PointerRNA *ptr,
tmp_nes.strip_mode = NES_TIME_TRANSITION_START;
tmp_nes.strip = s1;
nlaeval_snapshot_init(&snapshot1, channels, snapshot);
- nlastrip_evaluate(ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, flush_to_original);
+ nlastrip_evaluate(
+ ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context, flush_to_original);
/* second strip */
tmp_nes.strip_mode = NES_TIME_TRANSITION_END;
tmp_nes.strip = s2;
nlaeval_snapshot_init(&snapshot2, channels, snapshot);
- nlastrip_evaluate(ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, flush_to_original);
+ nlastrip_evaluate(
+ ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context, flush_to_original);
/* accumulate temp-buffer and full-buffer, using the 'real' strip */
nlaeval_snapshot_mix_and_free(channels, snapshot, &snapshot1, &snapshot2, nes->strip_time);
@@ -1864,6 +1894,7 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr,
ListBase *modifiers,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
ListBase tmp_modifiers = {NULL, NULL};
@@ -1884,13 +1915,16 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr,
/* find the child-strip to evaluate */
evaltime = (nes->strip_time * (strip->end - strip->start)) + strip->start;
- tmp_nes = nlastrips_ctime_get_strip(NULL, &strip->strips, -1, evaltime, flush_to_original);
+ AnimationEvalContext child_context = BKE_animsys_eval_context_construct_at(anim_eval_context,
+ evaltime);
+ tmp_nes = nlastrips_ctime_get_strip(NULL, &strip->strips, -1, &child_context, flush_to_original);
/* directly evaluate child strip into accumulation buffer...
* - there's no need to use a temporary buffer (as it causes issues [T40082])
*/
if (tmp_nes) {
- nlastrip_evaluate(ptr, channels, &tmp_modifiers, tmp_nes, snapshot, flush_to_original);
+ nlastrip_evaluate(
+ ptr, channels, &tmp_modifiers, tmp_nes, snapshot, &child_context, flush_to_original);
/* free temp eval-strip */
MEM_freeN(tmp_nes);
@@ -1906,6 +1940,7 @@ void nlastrip_evaluate(PointerRNA *ptr,
ListBase *modifiers,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
NlaStrip *strip = nes->strip;
@@ -1928,10 +1963,12 @@ void nlastrip_evaluate(PointerRNA *ptr,
nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes, snapshot);
break;
case NLASTRIP_TYPE_TRANSITION: /* transition */
- nlastrip_evaluate_transition(ptr, channels, modifiers, nes, snapshot, flush_to_original);
+ nlastrip_evaluate_transition(
+ ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original);
break;
case NLASTRIP_TYPE_META: /* meta */
- nlastrip_evaluate_meta(ptr, channels, modifiers, nes, snapshot, flush_to_original);
+ nlastrip_evaluate_meta(
+ ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original);
break;
default: /* do nothing */
@@ -2079,7 +2116,7 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
static bool animsys_evaluate_nla(NlaEvalData *echannels,
PointerRNA *ptr,
AnimData *adt,
- float ctime,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original,
NlaKeyframingContext *r_context)
{
@@ -2127,7 +2164,8 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
}
/* otherwise, get strip to evaluate for this channel */
- nes = nlastrips_ctime_get_strip(&estrips, &nlt->strips, track_index, ctime, flush_to_original);
+ nes = nlastrips_ctime_get_strip(
+ &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original);
if (nes) {
nes->track = nlt;
}
@@ -2193,7 +2231,8 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
/* add this to our list of evaluation strips */
if (r_context == NULL) {
- nlastrips_ctime_get_strip(&estrips, &dummy_trackslist, -1, ctime, flush_to_original);
+ nlastrips_ctime_get_strip(
+ &estrips, &dummy_trackslist, -1, anim_eval_context, flush_to_original);
}
/* If computing the context for keyframing, store data there instead of the list. */
else {
@@ -2203,7 +2242,7 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
NLASTRIP_EXTEND_HOLD;
r_context->eval_strip = nes = nlastrips_ctime_get_strip(
- NULL, &dummy_trackslist, -1, ctime, flush_to_original);
+ NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original);
/* These setting combinations require no data from strips below, so exit immediately. */
if ((nes == NULL) ||
@@ -2228,7 +2267,13 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
/* 2. for each strip, evaluate then accumulate on top of existing channels,
* but don't set values yet. */
for (nes = estrips.first; nes; nes = nes->next) {
- nlastrip_evaluate(ptr, echannels, NULL, nes, &echannels->eval_snapshot, flush_to_original);
+ nlastrip_evaluate(ptr,
+ echannels,
+ NULL,
+ nes,
+ &echannels->eval_snapshot,
+ anim_eval_context,
+ flush_to_original);
}
/* 3. free temporary evaluation data that's not used elsewhere */
@@ -2242,7 +2287,7 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
*/
static void animsys_calculate_nla(PointerRNA *ptr,
AnimData *adt,
- float ctime,
+ const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
NlaEvalData echannels;
@@ -2250,7 +2295,7 @@ static void animsys_calculate_nla(PointerRNA *ptr,
nlaeval_init(&echannels);
/* evaluate the NLA stack, obtaining a set of values to flush */
- if (animsys_evaluate_nla(&echannels, ptr, adt, ctime, flush_to_original, NULL)) {
+ if (animsys_evaluate_nla(&echannels, ptr, adt, anim_eval_context, flush_to_original, NULL)) {
/* reset any channels touched by currently inactive actions to default value */
animsys_evaluate_nla_domain(ptr, &echannels, adt);
@@ -2264,7 +2309,7 @@ static void animsys_calculate_nla(PointerRNA *ptr,
CLOG_WARN(&LOG, "NLA Eval: Stopgap for active action on NLA Stack - no strips case");
}
- animsys_evaluate_action(ptr, adt->action, ctime, flush_to_original);
+ animsys_evaluate_action(ptr, adt->action, anim_eval_context, flush_to_original);
}
/* free temp data */
@@ -2282,11 +2327,12 @@ static void animsys_calculate_nla(PointerRNA *ptr,
* \param ptr: RNA pointer to the Object with the animation.
* \return Keyframing context, or NULL if not necessary.
*/
-NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(struct ListBase *cache,
- struct PointerRNA *ptr,
- struct AnimData *adt,
- float ctime,
- const bool flush_to_original)
+NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
+ struct ListBase *cache,
+ struct PointerRNA *ptr,
+ struct AnimData *adt,
+ const AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original)
{
/* No remapping needed if NLA is off or no action. */
if ((adt == NULL) || (adt->action == NULL) || (adt->nla_tracks.first == NULL) ||
@@ -2309,7 +2355,7 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(struct ListBase *ca
ctx->adt = adt;
nlaeval_init(&ctx->nla_channels);
- animsys_evaluate_nla(&ctx->nla_channels, ptr, adt, ctime, flush_to_original, ctx);
+ animsys_evaluate_nla(&ctx->nla_channels, ptr, adt, anim_eval_context, flush_to_original, ctx);
BLI_assert(ELEM(ctx->strip.act, NULL, adt->action));
BLI_addtail(cache, ctx);
@@ -2499,8 +2545,11 @@ static void animsys_evaluate_overrides(PointerRNA *ptr, AnimData *adt)
* and that the flags for which parts of the anim-data settings need to be recalculated
* have been set already by the depsgraph. Now, we use the recalc
*/
-void BKE_animsys_evaluate_animdata(
- ID *id, AnimData *adt, float ctime, eAnimData_Recalc recalc, const bool flush_to_original)
+void BKE_animsys_evaluate_animdata(ID *id,
+ AnimData *adt,
+ const AnimationEvalContext *anim_eval_context,
+ eAnimData_Recalc recalc,
+ const bool flush_to_original)
{
PointerRNA id_ptr;
@@ -2523,11 +2572,11 @@ void BKE_animsys_evaluate_animdata(
/* evaluate NLA-stack
* - active action is evaluated as part of the NLA stack as the last item
*/
- animsys_calculate_nla(&id_ptr, adt, ctime, flush_to_original);
+ animsys_calculate_nla(&id_ptr, adt, anim_eval_context, flush_to_original);
}
/* evaluate Active Action only */
else if (adt->action) {
- animsys_evaluate_action_ex(&id_ptr, adt->action, ctime, flush_to_original);
+ animsys_evaluate_action_ex(&id_ptr, adt->action, anim_eval_context, flush_to_original);
}
}
@@ -2537,7 +2586,7 @@ void BKE_animsys_evaluate_animdata(
* - Drivers should be in the appropriate order to be evaluated without problems...
*/
if (recalc & ADT_RECALC_DRIVERS) {
- animsys_evaluate_drivers(&id_ptr, adt, ctime);
+ animsys_evaluate_drivers(&id_ptr, adt, anim_eval_context);
}
/* always execute 'overrides'
@@ -2565,6 +2614,8 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float
}
const bool flush_to_original = DEG_is_active(depsgraph);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ ctime);
/* macros for less typing
* - only evaluate animation data for id if it has users (and not just fake ones)
@@ -2575,7 +2626,7 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float
for (id = first; id; id = id->next) { \
if (ID_REAL_USERS(id) > 0) { \
AnimData *adt = BKE_animdata_from_id(id); \
- BKE_animsys_evaluate_animdata(id, adt, ctime, aflag, flush_to_original); \
+ BKE_animsys_evaluate_animdata(id, adt, &anim_eval_context, aflag, flush_to_original); \
} \
} \
(void)0
@@ -2594,9 +2645,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, float
if (ntp->nodetree) { \
AnimData *adt2 = BKE_animdata_from_id((ID *)ntp->nodetree); \
BKE_animsys_evaluate_animdata( \
- &ntp->nodetree->id, adt2, ctime, ADT_RECALC_ANIM, flush_to_original); \
+ &ntp->nodetree->id, adt2, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); \
} \
- BKE_animsys_evaluate_animdata(id, adt, ctime, aflag, flush_to_original); \
+ BKE_animsys_evaluate_animdata(id, adt, &anim_eval_context, aflag, flush_to_original); \
} \
} \
(void)0
@@ -2713,7 +2764,10 @@ void BKE_animsys_eval_animdata(Depsgraph *depsgraph, ID *id)
* which should get handled as part of the dependency graph instead. */
DEG_debug_print_eval_time(depsgraph, __func__, id->name, id, ctime);
const bool flush_to_original = DEG_is_active(depsgraph);
- BKE_animsys_evaluate_animdata(id, adt, ctime, ADT_RECALC_ANIM, flush_to_original);
+
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ ctime);
+ BKE_animsys_evaluate_animdata(id, adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original);
}
void BKE_animsys_update_driver_array(ID *id)
@@ -2775,7 +2829,9 @@ void BKE_animsys_eval_driver(Depsgraph *depsgraph, ID *id, int driver_index, FCu
if (BKE_animsys_store_rna_setting(&id_ptr, fcu->rna_path, fcu->array_index, &anim_rna)) {
/* Evaluate driver, and write results to COW-domain destination */
const float ctime = DEG_get_ctime(depsgraph);
- const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, ctime);
+ const float curval = calculate_fcurve(&anim_rna, fcu, &anim_eval_context);
ok = BKE_animsys_write_rna_setting(&anim_rna, curval);
/* Flush results & status codes to original data for UI (T59984) */
diff --git a/source/blender/blenkernel/intern/anim_visualization.c b/source/blender/blenkernel/intern/anim_visualization.c
index 04dbe4102cc..a6f63c2ba95 100644
--- a/source/blender/blenkernel/intern/anim_visualization.c
+++ b/source/blender/blenkernel/intern/anim_visualization.c
@@ -184,10 +184,8 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports,
/* return/use this as it is already valid length */
return mpath;
}
- else {
- /* clear the existing path (as the range has changed), and reallocate below */
- animviz_free_motionpath_cache(mpath);
- }
+ /* clear the existing path (as the range has changed), and reallocate below */
+ animviz_free_motionpath_cache(mpath);
}
}
else {
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 8189385a69d..2cc715464ad 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -56,7 +56,7 @@
# ifdef WITH_BINRELOC
# include "binreloc.h"
# endif
-/* mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
+/* #mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
# include <unistd.h>
#endif /* WIN32 */
@@ -120,7 +120,7 @@ static char *blender_version_decimal(const int ver)
}
/**
- * Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath,
+ * Concatenates path_base, (optional) path_sep and (optional) folder_name into \a targetpath,
* returning true if result points to a directory.
*/
static bool test_path(char *targetpath,
@@ -138,14 +138,14 @@ static bool test_path(char *targetpath,
BLI_strncpy(tmppath, path_base, sizeof(tmppath));
}
- /* rare cases folder_name is omitted (when looking for ~/.config/blender/2.xx dir only) */
+ /* Rare cases folder_name is omitted (when looking for `~/.config/blender/2.xx` dir only). */
if (folder_name) {
BLI_join_dirfile(targetpath, targetpath_len, tmppath, folder_name);
}
else {
BLI_strncpy(targetpath, tmppath, targetpath_len);
}
- /* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile)
+ /* FIXME: why is "//" on front of \a tmppath expanded to "/" (by BLI_join_dirfile)
* if folder_name is specified but not otherwise? */
if (BLI_is_dir(targetpath)) {
@@ -154,13 +154,12 @@ static bool test_path(char *targetpath,
#endif
return true;
}
- else {
+
#ifdef PATH_DEBUG
- printf("\t%s missing: %s\n", __func__, targetpath);
+ printf("\t%s missing: %s\n", __func__, targetpath);
#endif
- // targetpath[0] = '\0';
- return false;
- }
+ // targetpath[0] = '\0';
+ return false;
}
/**
@@ -181,18 +180,17 @@ static bool test_env_path(char *path, const char *envvar)
#endif
return true;
}
- else {
- path[0] = '\0';
+
+ path[0] = '\0';
#ifdef PATH_DEBUG
- printf("\t%s env %s missing: %s\n", __func__, envvar, env);
+ printf("\t%s env %s missing: %s\n", __func__, envvar, env);
#endif
- return false;
- }
+ return false;
}
/**
* Constructs in \a targetpath the name of a directory relative to a version-specific
- * subdirectory in the parent directory of the Blender executable.
+ * sub-directory in the parent directory of the Blender executable.
*
* \param targetpath: String to return path
* \param folder_name: Optional folder name within version-specific directory
@@ -272,10 +270,8 @@ static bool get_path_environment(char *targetpath,
if (subfolder_name) {
return test_path(targetpath, targetpath_len, user_path, NULL, subfolder_name);
}
- else {
- BLI_strncpy(targetpath, user_path, FILE_MAX);
- return true;
- }
+ BLI_strncpy(targetpath, user_path, FILE_MAX);
+ return true;
}
return false;
}
@@ -300,10 +296,8 @@ static bool get_path_environment_notest(char *targetpath,
BLI_join_dirfile(targetpath, targetpath_len, user_path, subfolder_name);
return true;
}
- else {
- BLI_strncpy(targetpath, user_path, FILE_MAX);
- return true;
- }
+ BLI_strncpy(targetpath, user_path, FILE_MAX);
+ return true;
}
return false;
}
@@ -313,7 +307,7 @@ static bool get_path_environment_notest(char *targetpath,
* \param targetpath: String to return path
* \param folder_name: default name of folder within user area
* \param subfolder_name: optional name of subfolder within folder
- * \param ver: Blender version, used to construct a subdirectory name
+ * \param ver: Blender version, used to construct a sub-directory name
* \return true if it was able to construct such a path.
*/
static bool get_path_user(char *targetpath,
@@ -347,9 +341,8 @@ static bool get_path_user(char *targetpath,
if (subfolder_name) {
return test_path(targetpath, targetpath_len, user_path, folder_name, subfolder_name);
}
- else {
- return test_path(targetpath, targetpath_len, user_path, NULL, folder_name);
- }
+
+ return test_path(targetpath, targetpath_len, user_path, NULL, folder_name);
}
/**
@@ -357,8 +350,8 @@ static bool get_path_user(char *targetpath,
*
* \param targetpath: String to return path
* \param folder_name: default name of folder within installation area
- * \param subfolder_name: optional name of subfolder within folder
- * \param ver: Blender version, used to construct a subdirectory name
+ * \param subfolder_name: optional name of sub-folder within folder
+ * \param ver: Blender version, used to construct a sub-directory name
* \return true if it was able to construct such a path.
*/
static bool get_path_system(char *targetpath,
@@ -401,10 +394,9 @@ static bool get_path_system(char *targetpath,
/* try $BLENDERPATH/folder_name/subfolder_name */
return test_path(targetpath, targetpath_len, system_path, folder_name, subfolder_name);
}
- else {
- /* try $BLENDERPATH/folder_name */
- return test_path(targetpath, targetpath_len, system_path, NULL, folder_name);
- }
+
+ /* try $BLENDERPATH/folder_name */
+ return test_path(targetpath, targetpath_len, system_path, NULL, folder_name);
}
/**
@@ -643,7 +635,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, con
* adds the correct extension (.com .exe etc) from
* $PATHEXT if necessary. Also on Windows it translates
* the name to its 8.3 version to prevent problems with
- * spaces and stuff. Final result is returned in fullname.
+ * spaces and stuff. Final result is returned in \a fullname.
*
* \param fullname: The full path and full name of the executable
* (must be FILE_MAX minimum)
@@ -983,7 +975,7 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c
/**
* Sets btempdir_base to userdir if specified and is a valid directory, otherwise
* chooses a suitable OS-specific temporary directory.
- * Sets btempdir_session to a mkdtemp-generated sub-dir of btempdir_base.
+ * Sets btempdir_session to a #mkdtemp generated sub-dir of btempdir_base.
*
* \note On Window userdir will be set to the temporary directory!
*/
@@ -1027,7 +1019,11 @@ void BKE_tempdir_session_purge(void)
}
/* Gets a good default directory for fonts */
-bool BKE_appdir_font_folder_default(char *dir)
+
+bool BKE_appdir_font_folder_default(
+ /* This parameter can only be `const` on non-windows platforms.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ char *dir)
{
bool success = false;
#ifdef WIN32
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 36921bd2662..bf04603aa46 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -35,18 +35,12 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
-#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
-#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
-#include "DNA_gpencil_types.h"
-#include "DNA_lattice_types.h"
#include "DNA_listBase.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -55,11 +49,8 @@
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
-#include "BKE_deform.h"
-#include "BKE_displist.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
-#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -71,13 +62,13 @@
#include "BIK_api.h"
-#include "atomic_ops.h"
-
#include "CLG_log.h"
static CLG_LogRef LOG = {"bke.armature"};
-/*************************** Prototypes ***************************/
+/* -------------------------------------------------------------------- */
+/** \name Prototypes
+ * \{ */
static void copy_bonechildren(Bone *bone_dst,
const Bone *bone_src,
@@ -87,7 +78,11 @@ static void copy_bonechildren(Bone *bone_dst,
static void copy_bonechildren_custom_handles(Bone *bone_dst, bArmature *arm_dst);
-/*********************** Armature Datablock ***********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Data-block
+ * \{ */
/**
* Only copy internal data of Armature ID from source
@@ -187,7 +182,11 @@ IDTypeInfo IDType_ID_AR = {
.foreach_id = armature_foreach_id,
};
-/* **************** Generic Functions, data level *************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Data-Level Functions
+ * \{ */
bArmature *BKE_armature_add(Main *bmain, const char *name)
{
@@ -284,6 +283,12 @@ bArmature *BKE_armature_copy(Main *bmain, const bArmature *arm)
return arm_copy;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Transform Copy
+ * \{ */
+
static void copy_bone_transform(Bone *bone_dst, const Bone *bone_src)
{
bone_dst->roll = bone_src->roll;
@@ -313,6 +318,14 @@ void BKE_armature_copy_bone_transforms(bArmature *armature_dst, const bArmature
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Transform by 4x4 Matrix
+ *
+ * \see #ED_armature_edit_transform for the edit-mode version of this function.
+ * \{ */
+
/** Helper for #ED_armature_transform */
static void armature_transform_recurse(ListBase *bonebase,
const float mat[4][4],
@@ -408,6 +421,14 @@ void BKE_armature_transform(bArmature *arm, const float mat[4][4], const bool do
armature_transform_recurse(&arm->bonebase, mat, do_props, mat3, scale, NULL, NULL);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Bone Find by Name
+ *
+ * Using fast #GHash look-ups when available.
+ * \{ */
+
static Bone *get_named_bone_bonechildren(ListBase *lb, const char *name)
{
Bone *curBone, *rbone;
@@ -480,19 +501,29 @@ void BKE_armature_bone_hash_free(bArmature *arm)
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Bone Flags
+ * \{ */
+
bool BKE_armature_bone_flag_test_recursive(const Bone *bone, int flag)
{
if (bone->flag & flag) {
return true;
}
- else if (bone->parent) {
+ if (bone->parent) {
return BKE_armature_bone_flag_test_recursive(bone->parent, flag);
}
- else {
- return false;
- }
+ return false;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Layer Refresh Used
+ * \{ */
+
static void armature_refresh_layer_used_recursive(bArmature *arm, ListBase *bones)
{
LISTBASE_FOREACH (Bone *, bone, bones) {
@@ -518,6 +549,12 @@ void BKE_armature_refresh_layer_used(struct Depsgraph *depsgraph, struct bArmatu
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Layer Refresh Used
+ * \{ */
+
/* Finds the best possible extension to the name on a particular axis. (For renaming, check for
* unique names afterwards) strip_number: removes number extensions (TODO: not used)
* axis: the axis to name on
@@ -648,13 +685,14 @@ int bone_autoside_name(
return 1;
}
-
- else {
- return 0;
- }
+ return 0;
}
-/* ************* B-Bone support ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature B-Bone Support
+ * \{ */
/* Compute a set of bezier parameter values that produce approximately equally spaced points. */
static void equalize_cubic_bezier(const float control[4][3],
@@ -1209,8 +1247,6 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param,
return param->segments;
}
-/* ************ Armature Deform ******************* */
-
static void allocate_bbone_cache(bPoseChannel *pchan, int segments)
{
bPoseChannel_Runtime *runtime = &pchan->runtime;
@@ -1331,498 +1367,11 @@ void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan,
*r_blend_next = blend;
}
-/* Add the effect of one bone or B-Bone segment to the accumulated result. */
-static void pchan_deform_accumulate(const DualQuat *deform_dq,
- const float deform_mat[4][4],
- const float co_in[3],
- float weight,
- float co_accum[3],
- DualQuat *dq_accum,
- float mat_accum[3][3])
-{
- if (weight == 0.0f) {
- return;
- }
+/** \} */
- if (dq_accum) {
- BLI_assert(!co_accum);
-
- add_weighted_dq_dq(dq_accum, deform_dq, weight);
- }
- else {
- float tmp[3];
- mul_v3_m4v3(tmp, deform_mat, co_in);
-
- sub_v3_v3(tmp, co_in);
- madd_v3_v3fl(co_accum, tmp, weight);
-
- if (mat_accum) {
- float tmpmat[3][3];
- copy_m3_m4(tmpmat, deform_mat);
-
- madd_m3_m3m3fl(mat_accum, mat_accum, tmpmat, weight);
- }
- }
-}
-
-static void b_bone_deform(const bPoseChannel *pchan,
- const float co[3],
- float weight,
- float vec[3],
- DualQuat *dq,
- float defmat[3][3])
-{
- const DualQuat *quats = pchan->runtime.bbone_dual_quats;
- const Mat4 *mats = pchan->runtime.bbone_deform_mats;
- const float(*mat)[4] = mats[0].mat;
- float blend, y;
- int index;
-
- /* Transform co to bone space and get its y component. */
- y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
-
- /* Calculate the indices of the 2 affecting b_bone segments. */
- BKE_pchan_bbone_deform_segment_index(pchan, y / pchan->bone->length, &index, &blend);
-
- pchan_deform_accumulate(
- &quats[index], mats[index + 1].mat, co, weight * (1.0f - blend), vec, dq, defmat);
- pchan_deform_accumulate(
- &quats[index + 1], mats[index + 2].mat, co, weight * blend, vec, dq, defmat);
-}
-
-/* using vec with dist to bone b1 - b2 */
-float distfactor_to_bone(
- const float vec[3], const float b1[3], const float b2[3], float rad1, float rad2, float rdist)
-{
- float dist_sq;
- float bdelta[3];
- float pdelta[3];
- float hsqr, a, l, rad;
-
- sub_v3_v3v3(bdelta, b2, b1);
- l = normalize_v3(bdelta);
-
- sub_v3_v3v3(pdelta, vec, b1);
-
- a = dot_v3v3(bdelta, pdelta);
- hsqr = len_squared_v3(pdelta);
-
- if (a < 0.0f) {
- /* If we're past the end of the bone, do a spherical field attenuation thing */
- dist_sq = len_squared_v3v3(b1, vec);
- rad = rad1;
- }
- else if (a > l) {
- /* If we're past the end of the bone, do a spherical field attenuation thing */
- dist_sq = len_squared_v3v3(b2, vec);
- rad = rad2;
- }
- else {
- dist_sq = (hsqr - (a * a));
-
- if (l != 0.0f) {
- rad = a / l;
- rad = rad * rad2 + (1.0f - rad) * rad1;
- }
- else {
- rad = rad1;
- }
- }
-
- a = rad * rad;
- if (dist_sq < a) {
- return 1.0f;
- }
- else {
- l = rad + rdist;
- l *= l;
- if (rdist == 0.0f || dist_sq >= l) {
- return 0.0f;
- }
- else {
- a = sqrtf(dist_sq) - rad;
- return 1.0f - (a * a) / (rdist * rdist);
- }
- }
-}
-
-static float dist_bone_deform(
- bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
-{
- Bone *bone = pchan->bone;
- float fac, contrib = 0.0;
-
- if (bone == NULL) {
- return 0.0f;
- }
-
- fac = distfactor_to_bone(
- co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
-
- if (fac > 0.0f) {
- fac *= bone->weight;
- contrib = fac;
- if (contrib > 0.0f) {
- if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
- b_bone_deform(pchan, co, fac, vec, dq, mat);
- }
- else {
- pchan_deform_accumulate(
- &pchan->runtime.deform_dual_quat, pchan->chan_mat, co, fac, vec, dq, mat);
- }
- }
- }
-
- return contrib;
-}
-
-static void pchan_bone_deform(bPoseChannel *pchan,
- float weight,
- float vec[3],
- DualQuat *dq,
- float mat[3][3],
- const float co[3],
- float *contrib)
-{
- Bone *bone = pchan->bone;
-
- if (!weight) {
- return;
- }
-
- if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
- b_bone_deform(pchan, co, weight, vec, dq, mat);
- }
- else {
- pchan_deform_accumulate(
- &pchan->runtime.deform_dual_quat, pchan->chan_mat, co, weight, vec, dq, mat);
- }
-
- (*contrib) += weight;
-}
-
-typedef struct ArmatureUserdata {
- Object *armOb;
- Object *target;
- const Mesh *mesh;
- float (*vertexCos)[3];
- float (*defMats)[3][3];
- float (*prevCos)[3];
-
- bool use_envelope;
- bool use_quaternion;
- bool invert_vgroup;
- bool use_dverts;
-
- int armature_def_nr;
-
- int target_totvert;
- MDeformVert *dverts;
-
- int defbase_tot;
- bPoseChannel **defnrToPC;
-
- float premat[4][4];
- float postmat[4][4];
-} ArmatureUserdata;
-
-static void armature_vert_task(void *__restrict userdata,
- const int i,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- const ArmatureUserdata *data = userdata;
- float(*const vertexCos)[3] = data->vertexCos;
- float(*const defMats)[3][3] = data->defMats;
- float(*const prevCos)[3] = data->prevCos;
- const bool use_envelope = data->use_envelope;
- const bool use_quaternion = data->use_quaternion;
- const bool use_dverts = data->use_dverts;
- const int armature_def_nr = data->armature_def_nr;
-
- MDeformVert *dvert;
- DualQuat sumdq, *dq = NULL;
- bPoseChannel *pchan;
- float *co, dco[3];
- float sumvec[3], summat[3][3];
- float *vec = NULL, (*smat)[3] = NULL;
- float contrib = 0.0f;
- float armature_weight = 1.0f; /* default to 1 if no overall def group */
- float prevco_weight = 1.0f; /* weight for optional cached vertexcos */
-
- if (use_quaternion) {
- memset(&sumdq, 0, sizeof(DualQuat));
- dq = &sumdq;
- }
- else {
- sumvec[0] = sumvec[1] = sumvec[2] = 0.0f;
- vec = sumvec;
-
- if (defMats) {
- zero_m3(summat);
- smat = summat;
- }
- }
-
- if (use_dverts || armature_def_nr != -1) {
- if (data->mesh) {
- BLI_assert(i < data->mesh->totvert);
- if (data->mesh->dvert != NULL) {
- dvert = data->mesh->dvert + i;
- }
- else {
- dvert = NULL;
- }
- }
- else if (data->dverts && i < data->target_totvert) {
- dvert = data->dverts + i;
- }
- else {
- dvert = NULL;
- }
- }
- else {
- dvert = NULL;
- }
-
- if (armature_def_nr != -1 && dvert) {
- armature_weight = BKE_defvert_find_weight(dvert, armature_def_nr);
-
- if (data->invert_vgroup) {
- armature_weight = 1.0f - armature_weight;
- }
-
- /* hackish: the blending factor can be used for blending with prevCos too */
- if (prevCos) {
- prevco_weight = armature_weight;
- armature_weight = 1.0f;
- }
- }
-
- /* check if there's any point in calculating for this vert */
- if (armature_weight == 0.0f) {
- return;
- }
-
- /* get the coord we work on */
- co = prevCos ? prevCos[i] : vertexCos[i];
-
- /* Apply the object's matrix */
- mul_m4_v3(data->premat, co);
-
- if (use_dverts && dvert && dvert->totweight) { /* use weight groups ? */
- MDeformWeight *dw = dvert->dw;
- int deformed = 0;
- unsigned int j;
- for (j = dvert->totweight; j != 0; j--, dw++) {
- const uint index = dw->def_nr;
- if (index < data->defbase_tot && (pchan = data->defnrToPC[index])) {
- float weight = dw->weight;
- Bone *bone = pchan->bone;
-
- deformed = 1;
-
- if (bone && bone->flag & BONE_MULT_VG_ENV) {
- weight *= distfactor_to_bone(
- co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
- }
-
- pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib);
- }
- }
- /* if there are vertexgroups but not groups with bones
- * (like for softbody groups) */
- if (deformed == 0 && use_envelope) {
- for (pchan = data->armOb->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
- contrib += dist_bone_deform(pchan, vec, dq, smat, co);
- }
- }
- }
- }
- else if (use_envelope) {
- for (pchan = data->armOb->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
- contrib += dist_bone_deform(pchan, vec, dq, smat, co);
- }
- }
- }
-
- /* actually should be EPSILON? weight values and contrib can be like 10e-39 small */
- if (contrib > 0.0001f) {
- if (use_quaternion) {
- normalize_dq(dq, contrib);
-
- if (armature_weight != 1.0f) {
- copy_v3_v3(dco, co);
- mul_v3m3_dq(dco, (defMats) ? summat : NULL, dq);
- sub_v3_v3(dco, co);
- mul_v3_fl(dco, armature_weight);
- add_v3_v3(co, dco);
- }
- else {
- mul_v3m3_dq(co, (defMats) ? summat : NULL, dq);
- }
-
- smat = summat;
- }
- else {
- mul_v3_fl(vec, armature_weight / contrib);
- add_v3_v3v3(co, vec, co);
- }
-
- if (defMats) {
- float pre[3][3], post[3][3], tmpmat[3][3];
-
- copy_m3_m4(pre, data->premat);
- copy_m3_m4(post, data->postmat);
- copy_m3_m3(tmpmat, defMats[i]);
-
- if (!use_quaternion) { /* quaternion already is scale corrected */
- mul_m3_fl(smat, armature_weight / contrib);
- }
-
- mul_m3_series(defMats[i], post, smat, pre, tmpmat);
- }
- }
-
- /* always, check above code */
- mul_m4_v3(data->postmat, co);
-
- /* interpolate with previous modifier position using weight group */
- if (prevCos) {
- float mw = 1.0f - prevco_weight;
- vertexCos[i][0] = prevco_weight * vertexCos[i][0] + mw * co[0];
- vertexCos[i][1] = prevco_weight * vertexCos[i][1] + mw * co[1];
- vertexCos[i][2] = prevco_weight * vertexCos[i][2] + mw * co[2];
- }
-}
-
-void armature_deform_verts(Object *armOb,
- Object *target,
- const Mesh *mesh,
- float (*vertexCos)[3],
- float (*defMats)[3][3],
- int numVerts,
- int deformflag,
- float (*prevCos)[3],
- const char *defgrp_name,
- bGPDstroke *gps)
-{
- bArmature *arm = armOb->data;
- bPoseChannel **defnrToPC = NULL;
- MDeformVert *dverts = NULL;
- bDeformGroup *dg;
- const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0;
- const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0;
- const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0;
- int defbase_tot = 0; /* safety for vertexgroup index overflow */
- int i, target_totvert = 0; /* safety for vertexgroup overflow */
- bool use_dverts = false;
- int armature_def_nr;
-
- /* in editmode, or not an armature */
- if (arm->edbo || (armOb->pose == NULL)) {
- return;
- }
-
- if ((armOb->pose->flag & POSE_RECALC) != 0) {
- CLOG_ERROR(&LOG,
- "Trying to evaluate influence of armature '%s' which needs Pose recalc!",
- armOb->id.name);
- BLI_assert(0);
- }
-
- /* get the def_nr for the overall armature vertex group if present */
- armature_def_nr = BKE_object_defgroup_name_index(target, defgrp_name);
-
- if (ELEM(target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
- defbase_tot = BLI_listbase_count(&target->defbase);
-
- if (target->type == OB_MESH) {
- Mesh *me = target->data;
- dverts = me->dvert;
- if (dverts) {
- target_totvert = me->totvert;
- }
- }
- else if (target->type == OB_LATTICE) {
- Lattice *lt = target->data;
- dverts = lt->dvert;
- if (dverts) {
- target_totvert = lt->pntsu * lt->pntsv * lt->pntsw;
- }
- }
- else if (target->type == OB_GPENCIL) {
- dverts = gps->dvert;
- if (dverts) {
- target_totvert = gps->totpoints;
- }
- }
- }
-
- /* get a vertex-deform-index to posechannel array */
- if (deformflag & ARM_DEF_VGROUP) {
- if (ELEM(target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
- /* if we have a Mesh, only use dverts if it has them */
- if (mesh) {
- use_dverts = (mesh->dvert != NULL);
- }
- else if (dverts) {
- use_dverts = true;
- }
-
- if (use_dverts) {
- defnrToPC = MEM_callocN(sizeof(*defnrToPC) * defbase_tot, "defnrToBone");
- /* TODO(sergey): Some considerations here:
- *
- * - Check whether keeping this consistent across frames gives speedup.
- */
- for (i = 0, dg = target->defbase.first; dg; i++, dg = dg->next) {
- defnrToPC[i] = BKE_pose_channel_find_name(armOb->pose, dg->name);
- /* exclude non-deforming bones */
- if (defnrToPC[i]) {
- if (defnrToPC[i]->bone->flag & BONE_NO_DEFORM) {
- defnrToPC[i] = NULL;
- }
- }
- }
- }
- }
- }
-
- ArmatureUserdata data = {.armOb = armOb,
- .target = target,
- .mesh = mesh,
- .vertexCos = vertexCos,
- .defMats = defMats,
- .prevCos = prevCos,
- .use_envelope = use_envelope,
- .use_quaternion = use_quaternion,
- .invert_vgroup = invert_vgroup,
- .use_dverts = use_dverts,
- .armature_def_nr = armature_def_nr,
- .target_totvert = target_totvert,
- .dverts = dverts,
- .defbase_tot = defbase_tot,
- .defnrToPC = defnrToPC};
-
- float obinv[4][4];
- invert_m4_m4(obinv, target->obmat);
-
- mul_m4_m4m4(data.postmat, obinv, armOb->obmat);
- invert_m4_m4(data.premat, data.postmat);
-
- TaskParallelSettings settings;
- BLI_parallel_range_settings_defaults(&settings);
- settings.min_iter_per_thread = 32;
- BLI_task_parallel_range(0, numVerts, &data, armature_vert_task, &settings);
-
- if (defnrToPC) {
- MEM_freeN(defnrToPC);
- }
-}
-
-/* ************ END Armature Deform ******************* */
+/* -------------------------------------------------------------------- */
+/** \name Bone Space to Space Conversion API
+ * \{ */
void get_objectspace_bone_matrix(struct Bone *bone,
float M_accumulatedMatrix[4][4],
@@ -1832,8 +1381,6 @@ void get_objectspace_bone_matrix(struct Bone *bone,
copy_m4_m4(M_accumulatedMatrix, bone->arm_mat);
}
-/* **************** Space to Space API ****************** */
-
/* Convert World-Space Matrix to Pose-Space Matrix */
void BKE_armature_mat_world_to_pose(Object *ob, float inmat[4][4], float outmat[4][4])
{
@@ -1868,6 +1415,12 @@ void BKE_armature_loc_world_to_pose(Object *ob, const float inloc[3], float outl
copy_v3_v3(outloc, nLocMat[3]);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Bone Matrix Calculation API
+ * \{ */
+
/* Simple helper, computes the offset bone matrix.
* offs_bone = yoffs(b-1) + root(b) + bonemat(b). */
void BKE_bone_offset_matrix_get(const Bone *bone, float offs_bone[4][4])
@@ -2156,6 +1709,14 @@ void BKE_armature_loc_pose_to_bone(bPoseChannel *pchan, const float inloc[3], fl
copy_v3_v3(outloc, nLocMat[3]);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Bone Matrix Read/Write API
+ *
+ * High level functions for transforming bones and reading the transform values.
+ * \{ */
+
void BKE_armature_mat_pose_to_bone_ex(struct Depsgraph *depsgraph,
Object *ob,
bPoseChannel *pchan,
@@ -2255,8 +1816,13 @@ void BKE_armature_mat_pose_to_delta(float delta_mat[4][4],
mul_m4_m4m4(delta_mat, imat, pose_mat);
}
-/* **************** Rotation Mode Conversions ****************************** */
-/* Used for Objects and Pose Channels, since both can have multiple rotation representations */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Rotation Mode Conversions
+ *
+ * Used for Objects and Pose Channels, since both can have multiple rotation representations.
+ * \{ */
/* Called from RNA when rotation mode changes
* - the result should be that the rotations given in the provided pointers have had conversions
@@ -2308,9 +1874,15 @@ void BKE_rotMode_change_values(
}
}
-/* **************** The new & simple (but OK!) armature evaluation ********* */
+/** \} */
-/* ****************** And how it works! ****************************************
+/* -------------------------------------------------------------------- */
+/** \name Bone Vector, Roll Conversion
+ *
+ * Used for Objects and Pose Channels, since both can have multiple rotation representations.
+ *
+ * How it Works
+ * ============
*
* This is the bone transformation trick; they're hierarchical so each bone(b)
* is in the coord system of bone(b-1):
@@ -2326,7 +1898,7 @@ void BKE_rotMode_change_values(
*
* pose_mat(b)= arm_mat(b) * chan_mat(b)
*
- * *************************************************************************** */
+ * \{ */
/* Computes vector and roll based on a rotation.
* "mat" must contain only a rotation, and no scaling. */
@@ -2494,6 +2066,12 @@ void vec_roll_to_mat3(const float vec[3], const float roll, float mat[3][3])
vec_roll_to_mat3_normalized(nor, roll, mat);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Bone Matrix Calculation (Recursive)
+ * \{ */
+
/**
* Recursive part, calculates rest-position of entire tree of children.
* \note Used when exiting edit-mode too.
@@ -2548,6 +2126,12 @@ void BKE_armature_where_is(bArmature *arm)
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pose Rebuild
+ * \{ */
+
/* if bone layer is protected, copy the data from from->pose
* when used with linked libraries this copies from the linked pose into the local pose */
static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
@@ -2580,7 +2164,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
}
/* clear all transformation values from library */
- BKE_pose_rest(frompose);
+ BKE_pose_rest(frompose, false);
/* copy over all of the proxy's bone groups */
/* TODO for later
@@ -2828,7 +2412,11 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
}
}
-/* ********************** THE POSE SOLVER ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pose Solver
+ * \{ */
/* loc/rot/size to given mat4 */
void BKE_pchan_to_mat4(const bPoseChannel *pchan, float chan_mat[4][4])
@@ -3024,7 +2612,12 @@ void BKE_pose_where_is(struct Depsgraph *depsgraph, Scene *scene, Object *ob)
}
}
-/************** Bounding box ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Calculate Bounding Box (Armature & Pose)
+ * \{ */
+
static int minmax_armature(Object *ob, float r_min[3], float r_max[3])
{
bPoseChannel *pchan;
@@ -3106,7 +2699,11 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
return changed;
}
-/************** Graph evaluation ********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Graph Evaluation
+ * \{ */
bPoseChannel *BKE_armature_ik_solver_find_root(bPoseChannel *pchan, bKinematicConstraint *data)
{
@@ -3145,3 +2742,5 @@ bPoseChannel *BKE_armature_splineik_solver_find_root(bPoseChannel *pchan,
}
return rootchan;
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
new file mode 100644
index 00000000000..8711a001e32
--- /dev/null
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -0,0 +1,685 @@
+/*
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * Deform coordinates by a armature object (used by modifier).
+ */
+
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_armature_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_lattice_types.h"
+#include "DNA_listBase.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_deform.h"
+#include "BKE_editmesh.h"
+#include "BKE_lattice.h"
+
+#include "DEG_depsgraph_build.h"
+
+#include "CLG_log.h"
+
+static CLG_LogRef LOG = {"bke.armature_deform"};
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Deform Internal Utilities
+ * \{ */
+
+/* Add the effect of one bone or B-Bone segment to the accumulated result. */
+static void pchan_deform_accumulate(const DualQuat *deform_dq,
+ const float deform_mat[4][4],
+ const float co_in[3],
+ float weight,
+ float co_accum[3],
+ DualQuat *dq_accum,
+ float mat_accum[3][3])
+{
+ if (weight == 0.0f) {
+ return;
+ }
+
+ if (dq_accum) {
+ BLI_assert(!co_accum);
+
+ add_weighted_dq_dq(dq_accum, deform_dq, weight);
+ }
+ else {
+ float tmp[3];
+ mul_v3_m4v3(tmp, deform_mat, co_in);
+
+ sub_v3_v3(tmp, co_in);
+ madd_v3_v3fl(co_accum, tmp, weight);
+
+ if (mat_accum) {
+ float tmpmat[3][3];
+ copy_m3_m4(tmpmat, deform_mat);
+
+ madd_m3_m3m3fl(mat_accum, mat_accum, tmpmat, weight);
+ }
+ }
+}
+
+static void b_bone_deform(const bPoseChannel *pchan,
+ const float co[3],
+ float weight,
+ float vec[3],
+ DualQuat *dq,
+ float defmat[3][3])
+{
+ const DualQuat *quats = pchan->runtime.bbone_dual_quats;
+ const Mat4 *mats = pchan->runtime.bbone_deform_mats;
+ const float(*mat)[4] = mats[0].mat;
+ float blend, y;
+ int index;
+
+ /* Transform co to bone space and get its y component. */
+ y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
+
+ /* Calculate the indices of the 2 affecting b_bone segments. */
+ BKE_pchan_bbone_deform_segment_index(pchan, y / pchan->bone->length, &index, &blend);
+
+ pchan_deform_accumulate(
+ &quats[index], mats[index + 1].mat, co, weight * (1.0f - blend), vec, dq, defmat);
+ pchan_deform_accumulate(
+ &quats[index + 1], mats[index + 2].mat, co, weight * blend, vec, dq, defmat);
+}
+
+/* using vec with dist to bone b1 - b2 */
+float distfactor_to_bone(
+ const float vec[3], const float b1[3], const float b2[3], float rad1, float rad2, float rdist)
+{
+ float dist_sq;
+ float bdelta[3];
+ float pdelta[3];
+ float hsqr, a, l, rad;
+
+ sub_v3_v3v3(bdelta, b2, b1);
+ l = normalize_v3(bdelta);
+
+ sub_v3_v3v3(pdelta, vec, b1);
+
+ a = dot_v3v3(bdelta, pdelta);
+ hsqr = len_squared_v3(pdelta);
+
+ if (a < 0.0f) {
+ /* If we're past the end of the bone, do a spherical field attenuation thing */
+ dist_sq = len_squared_v3v3(b1, vec);
+ rad = rad1;
+ }
+ else if (a > l) {
+ /* If we're past the end of the bone, do a spherical field attenuation thing */
+ dist_sq = len_squared_v3v3(b2, vec);
+ rad = rad2;
+ }
+ else {
+ dist_sq = (hsqr - (a * a));
+
+ if (l != 0.0f) {
+ rad = a / l;
+ rad = rad * rad2 + (1.0f - rad) * rad1;
+ }
+ else {
+ rad = rad1;
+ }
+ }
+
+ a = rad * rad;
+ if (dist_sq < a) {
+ return 1.0f;
+ }
+
+ l = rad + rdist;
+ l *= l;
+ if (rdist == 0.0f || dist_sq >= l) {
+ return 0.0f;
+ }
+
+ a = sqrtf(dist_sq) - rad;
+ return 1.0f - (a * a) / (rdist * rdist);
+}
+
+static float dist_bone_deform(
+ bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
+{
+ Bone *bone = pchan->bone;
+ float fac, contrib = 0.0;
+
+ if (bone == NULL) {
+ return 0.0f;
+ }
+
+ fac = distfactor_to_bone(
+ co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
+
+ if (fac > 0.0f) {
+ fac *= bone->weight;
+ contrib = fac;
+ if (contrib > 0.0f) {
+ if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
+ b_bone_deform(pchan, co, fac, vec, dq, mat);
+ }
+ else {
+ pchan_deform_accumulate(
+ &pchan->runtime.deform_dual_quat, pchan->chan_mat, co, fac, vec, dq, mat);
+ }
+ }
+ }
+
+ return contrib;
+}
+
+static void pchan_bone_deform(bPoseChannel *pchan,
+ float weight,
+ float vec[3],
+ DualQuat *dq,
+ float mat[3][3],
+ const float co[3],
+ float *contrib)
+{
+ Bone *bone = pchan->bone;
+
+ if (!weight) {
+ return;
+ }
+
+ if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
+ b_bone_deform(pchan, co, weight, vec, dq, mat);
+ }
+ else {
+ pchan_deform_accumulate(
+ &pchan->runtime.deform_dual_quat, pchan->chan_mat, co, weight, vec, dq, mat);
+ }
+
+ (*contrib) += weight;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Armature Deform #BKE_armature_deform_coords API
+ *
+ * #BKE_armature_deform_coords and related functions.
+ * \{ */
+
+typedef struct ArmatureUserdata {
+ const Object *ob_arm;
+ const Object *ob_target;
+ const Mesh *me_target;
+ float (*vert_coords)[3];
+ float (*vert_deform_mats)[3][3];
+ float (*vert_coords_prev)[3];
+
+ bool use_envelope;
+ bool use_quaternion;
+ bool invert_vgroup;
+ bool use_dverts;
+
+ int armature_def_nr;
+
+ const MDeformVert *dverts;
+ int dverts_len;
+
+ bPoseChannel **pchan_from_defbase;
+ int defbase_len;
+
+ float premat[4][4];
+ float postmat[4][4];
+
+ /** Specific data types. */
+ struct {
+ int cd_dvert_offset;
+ } bmesh;
+} ArmatureUserdata;
+
+static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
+ const int i,
+ const MDeformVert *dvert)
+{
+ float(*const vert_coords)[3] = data->vert_coords;
+ float(*const vert_deform_mats)[3][3] = data->vert_deform_mats;
+ float(*const vert_coords_prev)[3] = data->vert_coords_prev;
+ const bool use_envelope = data->use_envelope;
+ const bool use_quaternion = data->use_quaternion;
+ const bool use_dverts = data->use_dverts;
+ const int armature_def_nr = data->armature_def_nr;
+
+ DualQuat sumdq, *dq = NULL;
+ bPoseChannel *pchan;
+ float *co, dco[3];
+ float sumvec[3], summat[3][3];
+ float *vec = NULL, (*smat)[3] = NULL;
+ float contrib = 0.0f;
+ float armature_weight = 1.0f; /* default to 1 if no overall def group */
+ float prevco_weight = 1.0f; /* weight for optional cached vertexcos */
+
+ if (use_quaternion) {
+ memset(&sumdq, 0, sizeof(DualQuat));
+ dq = &sumdq;
+ }
+ else {
+ zero_v3(sumvec);
+ vec = sumvec;
+
+ if (vert_deform_mats) {
+ zero_m3(summat);
+ smat = summat;
+ }
+ }
+
+ if (armature_def_nr != -1 && dvert) {
+ armature_weight = BKE_defvert_find_weight(dvert, armature_def_nr);
+
+ if (data->invert_vgroup) {
+ armature_weight = 1.0f - armature_weight;
+ }
+
+ /* hackish: the blending factor can be used for blending with vert_coords_prev too */
+ if (vert_coords_prev) {
+ prevco_weight = armature_weight;
+ armature_weight = 1.0f;
+ }
+ }
+
+ /* check if there's any point in calculating for this vert */
+ if (armature_weight == 0.0f) {
+ return;
+ }
+
+ /* get the coord we work on */
+ co = vert_coords_prev ? vert_coords_prev[i] : vert_coords[i];
+
+ /* Apply the object's matrix */
+ mul_m4_v3(data->premat, co);
+
+ if (use_dverts && dvert && dvert->totweight) { /* use weight groups ? */
+ const MDeformWeight *dw = dvert->dw;
+ int deformed = 0;
+ unsigned int j;
+ for (j = dvert->totweight; j != 0; j--, dw++) {
+ const uint index = dw->def_nr;
+ if (index < data->defbase_len && (pchan = data->pchan_from_defbase[index])) {
+ float weight = dw->weight;
+ Bone *bone = pchan->bone;
+
+ deformed = 1;
+
+ if (bone && bone->flag & BONE_MULT_VG_ENV) {
+ weight *= distfactor_to_bone(
+ co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
+ }
+
+ pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib);
+ }
+ }
+ /* 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)) {
+ contrib += dist_bone_deform(pchan, vec, dq, smat, co);
+ }
+ }
+ }
+ }
+ else if (use_envelope) {
+ for (pchan = data->ob_arm->pose->chanbase.first; pchan; pchan = pchan->next) {
+ if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
+ contrib += dist_bone_deform(pchan, vec, dq, smat, co);
+ }
+ }
+ }
+
+ /* actually should be EPSILON? weight values and contrib can be like 10e-39 small */
+ if (contrib > 0.0001f) {
+ if (use_quaternion) {
+ normalize_dq(dq, contrib);
+
+ if (armature_weight != 1.0f) {
+ copy_v3_v3(dco, co);
+ mul_v3m3_dq(dco, (vert_deform_mats) ? summat : NULL, dq);
+ sub_v3_v3(dco, co);
+ mul_v3_fl(dco, armature_weight);
+ add_v3_v3(co, dco);
+ }
+ else {
+ mul_v3m3_dq(co, (vert_deform_mats) ? summat : NULL, dq);
+ }
+
+ smat = summat;
+ }
+ else {
+ mul_v3_fl(vec, armature_weight / contrib);
+ add_v3_v3v3(co, vec, co);
+ }
+
+ if (vert_deform_mats) {
+ float pre[3][3], post[3][3], tmpmat[3][3];
+
+ copy_m3_m4(pre, data->premat);
+ copy_m3_m4(post, data->postmat);
+ copy_m3_m3(tmpmat, vert_deform_mats[i]);
+
+ if (!use_quaternion) { /* quaternion already is scale corrected */
+ mul_m3_fl(smat, armature_weight / contrib);
+ }
+
+ mul_m3_series(vert_deform_mats[i], post, smat, pre, tmpmat);
+ }
+ }
+
+ /* always, check above code */
+ mul_m4_v3(data->postmat, co);
+
+ /* interpolate with previous modifier position using weight group */
+ if (vert_coords_prev) {
+ float mw = 1.0f - prevco_weight;
+ vert_coords[i][0] = prevco_weight * vert_coords[i][0] + mw * co[0];
+ vert_coords[i][1] = prevco_weight * vert_coords[i][1] + mw * co[1];
+ vert_coords[i][2] = prevco_weight * vert_coords[i][2] + mw * co[2];
+ }
+}
+
+static void armature_vert_task(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const ArmatureUserdata *data = userdata;
+ const MDeformVert *dvert;
+ if (data->use_dverts || data->armature_def_nr != -1) {
+ if (data->me_target) {
+ BLI_assert(i < data->me_target->totvert);
+ if (data->me_target->dvert != NULL) {
+ dvert = data->me_target->dvert + i;
+ }
+ else {
+ dvert = NULL;
+ }
+ }
+ else if (data->dverts && i < data->dverts_len) {
+ dvert = data->dverts + i;
+ }
+ else {
+ dvert = NULL;
+ }
+ }
+ else {
+ dvert = NULL;
+ }
+
+ armature_vert_task_with_dvert(data, i, dvert);
+}
+
+static void armature_vert_task_editmesh(void *__restrict userdata, MempoolIterData *iter)
+{
+ const ArmatureUserdata *data = userdata;
+ BMVert *v = (BMVert *)iter;
+ MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
+ armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert);
+}
+
+static void armature_vert_task_editmesh_no_dvert(void *__restrict userdata, MempoolIterData *iter)
+{
+ const ArmatureUserdata *data = userdata;
+ BMVert *v = (BMVert *)iter;
+ armature_vert_task_with_dvert(data, BM_elem_index_get(v), NULL);
+}
+
+static void armature_deform_coords_impl(const Object *ob_arm,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ const int vert_coords_len,
+ const int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ const Mesh *me_target,
+ BMEditMesh *em_target,
+ bGPDstroke *gps_target)
+{
+ bArmature *arm = ob_arm->data;
+ bPoseChannel **pchan_from_defbase = NULL;
+ const MDeformVert *dverts = NULL;
+ bDeformGroup *dg;
+ const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0;
+ const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0;
+ const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0;
+ int defbase_len = 0; /* safety for vertexgroup index overflow */
+ int i, dverts_len = 0; /* safety for vertexgroup overflow */
+ bool use_dverts = false;
+ int armature_def_nr;
+ int cd_dvert_offset = -1;
+
+ /* in editmode, or not an armature */
+ if (arm->edbo || (ob_arm->pose == NULL)) {
+ return;
+ }
+
+ if ((ob_arm->pose->flag & POSE_RECALC) != 0) {
+ CLOG_ERROR(&LOG,
+ "Trying to evaluate influence of armature '%s' which needs Pose recalc!",
+ ob_arm->id.name);
+ BLI_assert(0);
+ }
+
+ /* get the def_nr for the overall armature vertex group if present */
+ armature_def_nr = BKE_object_defgroup_name_index(ob_target, defgrp_name);
+
+ if (ELEM(ob_target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
+ defbase_len = BLI_listbase_count(&ob_target->defbase);
+
+ if (ob_target->type == OB_MESH) {
+ if (em_target == NULL) {
+ Mesh *me = ob_target->data;
+ dverts = me->dvert;
+ if (dverts) {
+ dverts_len = me->totvert;
+ }
+ }
+ }
+ else if (ob_target->type == OB_LATTICE) {
+ Lattice *lt = ob_target->data;
+ dverts = lt->dvert;
+ if (dverts) {
+ dverts_len = lt->pntsu * lt->pntsv * lt->pntsw;
+ }
+ }
+ else if (ob_target->type == OB_GPENCIL) {
+ dverts = gps_target->dvert;
+ if (dverts) {
+ dverts_len = gps_target->totpoints;
+ }
+ }
+ }
+
+ /* get a vertex-deform-index to posechannel array */
+ if (deformflag & ARM_DEF_VGROUP) {
+ if (ELEM(ob_target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
+ /* if we have a Mesh, only use dverts if it has them */
+ if (em_target) {
+ cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT);
+ use_dverts = (cd_dvert_offset != -1);
+ }
+ else if (me_target) {
+ use_dverts = (me_target->dvert != NULL);
+ }
+ else if (dverts) {
+ use_dverts = true;
+ }
+
+ if (use_dverts) {
+ pchan_from_defbase = MEM_callocN(sizeof(*pchan_from_defbase) * defbase_len, "defnrToBone");
+ /* TODO(sergey): Some considerations here:
+ *
+ * - Check whether keeping this consistent across frames gives speedup.
+ */
+ for (i = 0, dg = ob_target->defbase.first; dg; i++, dg = dg->next) {
+ pchan_from_defbase[i] = BKE_pose_channel_find_name(ob_arm->pose, dg->name);
+ /* exclude non-deforming bones */
+ if (pchan_from_defbase[i]) {
+ if (pchan_from_defbase[i]->bone->flag & BONE_NO_DEFORM) {
+ pchan_from_defbase[i] = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ArmatureUserdata data = {
+ .ob_arm = ob_arm,
+ .ob_target = ob_target,
+ .me_target = me_target,
+ .vert_coords = vert_coords,
+ .vert_deform_mats = vert_deform_mats,
+ .vert_coords_prev = vert_coords_prev,
+ .use_envelope = use_envelope,
+ .use_quaternion = use_quaternion,
+ .invert_vgroup = invert_vgroup,
+ .use_dverts = use_dverts,
+ .armature_def_nr = armature_def_nr,
+ .dverts = dverts,
+ .dverts_len = dverts_len,
+ .pchan_from_defbase = pchan_from_defbase,
+ .defbase_len = defbase_len,
+ .bmesh =
+ {
+ .cd_dvert_offset = cd_dvert_offset,
+ },
+ };
+
+ float obinv[4][4];
+ invert_m4_m4(obinv, ob_target->obmat);
+
+ mul_m4_m4m4(data.postmat, obinv, ob_arm->obmat);
+ invert_m4_m4(data.premat, data.postmat);
+
+ if (em_target != NULL) {
+ /* While this could cause an extra loop over mesh data, in most cases this will
+ * have already been properly set. */
+ BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
+
+ if (use_dverts) {
+ BLI_task_parallel_mempool(em_target->bm->vpool, &data, armature_vert_task_editmesh, true);
+ }
+ else {
+ BLI_task_parallel_mempool(
+ em_target->bm->vpool, &data, armature_vert_task_editmesh_no_dvert, true);
+ }
+ }
+ else {
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 32;
+ BLI_task_parallel_range(0, vert_coords_len, &data, armature_vert_task, &settings);
+ }
+
+ if (pchan_from_defbase) {
+ MEM_freeN(pchan_from_defbase);
+ }
+}
+
+void BKE_armature_deform_coords_with_gpencil_stroke(const Object *ob_arm,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ int vert_coords_len,
+ int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ bGPDstroke *gps_target)
+{
+ armature_deform_coords_impl(ob_arm,
+ ob_target,
+ vert_coords,
+ vert_deform_mats,
+ vert_coords_len,
+ deformflag,
+ vert_coords_prev,
+ defgrp_name,
+ NULL,
+ NULL,
+ gps_target);
+}
+
+void BKE_armature_deform_coords_with_mesh(const Object *ob_arm,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ int vert_coords_len,
+ int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ const Mesh *me_target)
+{
+ armature_deform_coords_impl(ob_arm,
+ ob_target,
+ vert_coords,
+ vert_deform_mats,
+ vert_coords_len,
+ deformflag,
+ vert_coords_prev,
+ defgrp_name,
+ me_target,
+ NULL,
+ NULL);
+}
+
+void BKE_armature_deform_coords_with_editmesh(const Object *ob_arm,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ float (*vert_deform_mats)[3][3],
+ int vert_coords_len,
+ int deformflag,
+ float (*vert_coords_prev)[3],
+ const char *defgrp_name,
+ BMEditMesh *em_target)
+{
+ armature_deform_coords_impl(ob_arm,
+ ob_target,
+ vert_coords,
+ vert_deform_mats,
+ vert_coords_len,
+ deformflag,
+ vert_coords_prev,
+ defgrp_name,
+ NULL,
+ em_target,
+ NULL);
+}
+
+/** \} */
diff --git a/tests/gtests/blenkernel/BKE_armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc
index ed6045081d4..cf17c37bc69 100644
--- a/tests/gtests/blenkernel/BKE_armature_test.cc
+++ b/source/blender/blenkernel/intern/armature_test.cc
@@ -23,6 +23,8 @@
#include "testing/testing.h"
+namespace blender::bke::tests {
+
static const float FLOAT_EPSILON = 1.2e-7;
TEST(mat3_vec_to_roll, UnitMatrix)
@@ -87,3 +89,5 @@ TEST(mat3_vec_to_roll, Rotationmatrix)
EXPECT_NEAR(0.57158958f, roll, FLOAT_EPSILON);
}
}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index d0a5e4348b9..d66991aed70 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -119,9 +119,8 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
if (segcount == 0) {
return;
}
- else {
- pchanRoot = pchanChain[segcount - 1];
- }
+
+ pchanRoot = pchanChain[segcount - 1];
/* perform binding step if required */
if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
@@ -717,7 +716,7 @@ void BKE_pose_constraints_evaluate(struct Depsgraph *depsgraph,
if (armature->flag & ARM_RESTPOS) {
return;
}
- else if (pchan->flag & POSE_IKTREE || pchan->flag & POSE_IKSPLINE) {
+ if (pchan->flag & POSE_IKTREE || pchan->flag & POSE_IKSPLINE) {
/* IK are being solved separately/ */
}
else {
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..a0da1b1677d 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) {
@@ -872,91 +872,90 @@ static Object *boid_find_ground(BoidBrainData *bbd,
return bpa->ground;
}
- else {
- float zvec[3] = {0.0f, 0.0f, 2000.0f};
- ParticleCollision col;
- ColliderCache *coll;
- BVHTreeRayHit hit;
- float radius = 0.0f, t, ray_dir[3];
-
- if (!bbd->sim->colliders) {
- return NULL;
- }
-
- memset(&col, 0, sizeof(ParticleCollision));
- /* first try to find below boid */
- copy_v3_v3(col.co1, pa->state.co);
- sub_v3_v3v3(col.co2, pa->state.co, zvec);
- sub_v3_v3v3(ray_dir, col.co2, col.co1);
- col.f = 0.0f;
- hit.index = -1;
- hit.dist = col.original_ray_length = normalize_v3(ray_dir);
- col.pce.inside = 0;
+ float zvec[3] = {0.0f, 0.0f, 2000.0f};
+ ParticleCollision col;
+ ColliderCache *coll;
+ BVHTreeRayHit hit;
+ float radius = 0.0f, t, ray_dir[3];
- for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
- col.current = coll->ob;
- col.md = coll->collmd;
- col.fac1 = col.fac2 = 0.f;
+ if (!bbd->sim->colliders) {
+ return NULL;
+ }
- if (col.md && col.md->bvhtree) {
- BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
- col.co1,
- ray_dir,
- radius,
- &hit,
- BKE_psys_collision_neartest_cb,
- &col,
- raycast_flag);
- }
+ memset(&col, 0, sizeof(ParticleCollision));
+
+ /* first try to find below boid */
+ copy_v3_v3(col.co1, pa->state.co);
+ sub_v3_v3v3(col.co2, pa->state.co, zvec);
+ sub_v3_v3v3(ray_dir, col.co2, col.co1);
+ col.f = 0.0f;
+ hit.index = -1;
+ hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+ col.pce.inside = 0;
+
+ for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
+ col.current = coll->ob;
+ col.md = coll->collmd;
+ col.fac1 = col.fac2 = 0.f;
+
+ if (col.md && col.md->bvhtree) {
+ BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
+ col.co1,
+ ray_dir,
+ radius,
+ &hit,
+ BKE_psys_collision_neartest_cb,
+ &col,
+ raycast_flag);
}
- /* then use that object */
- if (hit.index >= 0) {
- t = hit.dist / col.original_ray_length;
- interp_v3_v3v3(ground_co, col.co1, col.co2, t);
- normalize_v3_v3(ground_nor, col.pce.nor);
- return col.hit;
- }
-
- /* couldn't find below, so find upmost deflector object */
- add_v3_v3v3(col.co1, pa->state.co, zvec);
- sub_v3_v3v3(col.co2, pa->state.co, zvec);
- sub_v3_v3(col.co2, zvec);
- sub_v3_v3v3(ray_dir, col.co2, col.co1);
- col.f = 0.0f;
- hit.index = -1;
- hit.dist = col.original_ray_length = normalize_v3(ray_dir);
-
- for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
- col.current = coll->ob;
- col.md = coll->collmd;
+ }
+ /* then use that object */
+ if (hit.index >= 0) {
+ t = hit.dist / col.original_ray_length;
+ interp_v3_v3v3(ground_co, col.co1, col.co2, t);
+ normalize_v3_v3(ground_nor, col.pce.nor);
+ return col.hit;
+ }
- if (col.md && col.md->bvhtree) {
- BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
- col.co1,
- ray_dir,
- radius,
- &hit,
- BKE_psys_collision_neartest_cb,
- &col,
- raycast_flag);
- }
+ /* couldn't find below, so find upmost deflector object */
+ add_v3_v3v3(col.co1, pa->state.co, zvec);
+ sub_v3_v3v3(col.co2, pa->state.co, zvec);
+ sub_v3_v3(col.co2, zvec);
+ sub_v3_v3v3(ray_dir, col.co2, col.co1);
+ col.f = 0.0f;
+ hit.index = -1;
+ hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+
+ for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
+ col.current = coll->ob;
+ col.md = coll->collmd;
+
+ if (col.md && col.md->bvhtree) {
+ BLI_bvhtree_ray_cast_ex(col.md->bvhtree,
+ col.co1,
+ ray_dir,
+ radius,
+ &hit,
+ BKE_psys_collision_neartest_cb,
+ &col,
+ raycast_flag);
}
- /* then use that object */
- if (hit.index >= 0) {
- t = hit.dist / col.original_ray_length;
- interp_v3_v3v3(ground_co, col.co1, col.co2, t);
- normalize_v3_v3(ground_nor, col.pce.nor);
- return col.hit;
- }
-
- /* default to z=0 */
- copy_v3_v3(ground_co, pa->state.co);
- ground_co[2] = 0;
- ground_nor[0] = ground_nor[1] = 0.0f;
- ground_nor[2] = 1.0f;
- return NULL;
}
+ /* then use that object */
+ if (hit.index >= 0) {
+ t = hit.dist / col.original_ray_length;
+ interp_v3_v3v3(ground_co, col.co1, col.co2, t);
+ normalize_v3_v3(ground_nor, col.pce.nor);
+ return col.hit;
+ }
+
+ /* default to z=0 */
+ copy_v3_v3(ground_co, pa->state.co);
+ ground_co[2] = 0;
+ ground_nor[0] = ground_nor[1] = 0.0f;
+ ground_nor[2] = 1.0f;
+ return NULL;
}
static int boid_rule_applies(ParticleData *pa, BoidSettings *UNUSED(boids), BoidRule *rule)
{
@@ -1046,9 +1045,7 @@ static int apply_boid_rule(
fuzziness * len_v3(pa->prev_state.vel)) == 0) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa)
{
@@ -1388,6 +1385,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..e2d17f34992 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -147,19 +147,16 @@ static bool bpath_relative_rebase_visit_cb(void *userdata, char *path_dst, const
data->count_changed++;
return true;
}
- else {
- /* Failed to make relative path absolute. */
- BLI_assert(0);
- BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
- data->count_failed++;
- return false;
- }
- return false;
- }
- else {
- /* Absolute, leave this as-is. */
+
+ /* Failed to make relative path absolute. */
+ BLI_assert(0);
+ BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
+ data->count_failed++;
return false;
}
+
+ /* Absolute, leave this as-is. */
+ return false;
}
void BKE_bpath_relative_rebase(Main *bmain,
@@ -211,18 +208,17 @@ static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, cons
if (BLI_path_is_rel(path_src)) {
return false; /* already relative */
}
+
+ strcpy(path_dst, path_src);
+ BLI_path_rel(path_dst, data->basedir);
+ if (BLI_path_is_rel(path_dst)) {
+ data->count_changed++;
+ }
else {
- strcpy(path_dst, path_src);
- BLI_path_rel(path_dst, data->basedir);
- if (BLI_path_is_rel(path_dst)) {
- data->count_changed++;
- }
- else {
- BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made relative", path_src);
- data->count_failed++;
- }
- return true;
+ BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made relative", path_src);
+ data->count_failed++;
}
+ return true;
}
void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports)
@@ -263,18 +259,17 @@ static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, cons
if (BLI_path_is_rel(path_src) == false) {
return false; /* already absolute */
}
+
+ strcpy(path_dst, path_src);
+ BLI_path_abs(path_dst, data->basedir);
+ if (BLI_path_is_rel(path_dst) == false) {
+ data->count_changed++;
+ }
else {
- strcpy(path_dst, path_src);
- BLI_path_abs(path_dst, data->basedir);
- if (BLI_path_is_rel(path_dst) == false) {
- data->count_changed++;
- }
- else {
- BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
- data->count_failed++;
- }
- return true;
+ BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
+ data->count_failed++;
}
+ return true;
}
/* similar to BKE_bpath_relative_convert - keep in sync! */
@@ -411,7 +406,7 @@ static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const c
BLI_path_basename(data->searchdir));
return false;
}
- else if (found == false) {
+ if (found == false) {
BKE_reportf(data->reports,
RPT_WARNING,
"Could not find '%s' in '%s'",
@@ -419,18 +414,17 @@ static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const c
data->searchdir);
return false;
}
- else {
- bool was_relative = BLI_path_is_rel(path_dst);
- BLI_strncpy(path_dst, filename_new, FILE_MAX);
+ bool was_relative = BLI_path_is_rel(path_dst);
- /* keep path relative if the previous one was relative */
- if (was_relative) {
- BLI_path_rel(path_dst, data->basedir);
- }
+ BLI_strncpy(path_dst, filename_new, FILE_MAX);
- return true;
+ /* keep path relative if the previous one was relative */
+ if (was_relative) {
+ BLI_path_rel(path_dst, data->basedir);
}
+
+ return true;
}
void BKE_bpath_missing_files_find(Main *bmain,
@@ -483,9 +477,8 @@ static bool rewrite_path_fixed(char *path,
BLI_strncpy(path, path_dst, FILE_MAX);
return true;
}
- else {
- return false;
- }
+
+ return false;
}
static bool rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR],
@@ -510,9 +503,8 @@ static bool rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR],
BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE);
return true;
}
- else {
- return false;
- }
+
+ return false;
}
static bool rewrite_path_alloc(char **path,
@@ -538,9 +530,8 @@ static bool rewrite_path_alloc(char **path,
(*path) = BLI_strdup(path_dst);
return true;
}
- else {
- return false;
- }
+
+ return false;
}
/**
@@ -564,8 +555,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) */
@@ -609,9 +600,9 @@ void BKE_bpath_traverse_id(
}
}
else if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) {
- rewrite_path_fixed(mmd->domain->cache_directory, visit_cb, absbase, bpath_user_data);
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
+ rewrite_path_fixed(fmd->domain->cache_directory, visit_cb, absbase, bpath_user_data);
}
}
else if (md->type == eModifierType_Cloth) {
@@ -643,7 +634,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 +646,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 +747,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: {
@@ -827,10 +818,9 @@ bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *pa
BLI_strncpy(path_dst, filepath, FILE_MAX);
return true;
}
- else {
- /* Path was not relative to begin with. */
- return false;
- }
+
+ /* Path was not relative to begin with. */
+ return false;
}
/** \} */
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 3241518cae5..355bdfd10fd 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);
@@ -371,8 +372,8 @@ bool BKE_brush_delete(Main *bmain, Brush *brush)
if (brush->id.tag & LIB_TAG_INDIRECT) {
return false;
}
- else if (BKE_library_ID_is_indirectly_used(bmain, brush) && ID_REAL_USERS(brush) <= 1 &&
- ID_EXTRA_USERS(brush) == 0) {
+ if (BKE_library_ID_is_indirectly_used(bmain, brush) && ID_REAL_USERS(brush) <= 1 &&
+ ID_EXTRA_USERS(brush) == 0) {
return false;
}
@@ -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)
@@ -1463,6 +1484,7 @@ void BKE_brush_sculpt_reset(Brush *br)
case SCULPT_TOOL_SLIDE_RELAX:
br->spacing = 10;
br->alpha = 1.0f;
+ br->slide_deform_type = BRUSH_SLIDE_DEFORM_DRAG;
break;
case SCULPT_TOOL_CLAY:
br->flag |= BRUSH_SIZE_PRESSURE;
@@ -1512,6 +1534,7 @@ void BKE_brush_sculpt_reset(Brush *br)
break;
case SCULPT_TOOL_SMOOTH:
br->flag &= ~BRUSH_SPACE_ATTEN;
+ br->automasking_flags |= BRUSH_AUTOMASKING_BOUNDARY_EDGES;
br->spacing = 5;
br->alpha = 0.7f;
br->surface_smooth_shape_preservation = 0.5f;
@@ -1538,7 +1561,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 +1593,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 +1670,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:
@@ -2140,10 +2182,9 @@ float BKE_brush_curve_strength(const Brush *br, float p, const float len)
if (p >= len) {
return 0;
}
- else {
- p = p / len;
- p = 1.0f - p;
- }
+
+ p = p / len;
+ p = 1.0f - p;
switch (br->curve_preset) {
case BRUSH_CURVE_CUSTOM:
diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c
index 93794eb9709..bea8fdd5719 100644
--- a/source/blender/blenkernel/intern/bvhutils.c
+++ b/source/blender/blenkernel/intern/bvhutils.c
@@ -717,11 +717,14 @@ BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data,
/* printf("BVHTree built and saved on cache\n"); */
BVHCache *bvh_cache = *bvh_cache_p;
bvhcache_insert(bvh_cache, tree, bvh_cache_type);
- bvhcache_unlock(bvh_cache, lock_started);
in_cache = true;
}
}
+ if (bvh_cache_p) {
+ bvhcache_unlock(*bvh_cache_p, lock_started);
+ }
+
/* Setup BVHTreeFromMesh */
bvhtree_from_mesh_verts_setup_data(data, tree, in_cache, vert, vert_allocated);
@@ -929,11 +932,14 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
/* Save on cache for later use */
/* printf("BVHTree built and saved on cache\n"); */
bvhcache_insert(bvh_cache, tree, bvh_cache_type);
- bvhcache_unlock(bvh_cache, lock_started);
in_cache = true;
}
}
+ if (bvh_cache_p) {
+ bvhcache_unlock(*bvh_cache_p, lock_started);
+ }
+
/* Setup BVHTreeFromMesh */
bvhtree_from_mesh_edges_setup_data(
data, tree, in_cache, vert, vert_allocated, edge, edge_allocated);
@@ -1058,11 +1064,14 @@ BVHTree *bvhtree_from_mesh_faces_ex(BVHTreeFromMesh *data,
/* printf("BVHTree built and saved on cache\n"); */
BVHCache *bvh_cache = *bvh_cache_p;
bvhcache_insert(bvh_cache, tree, bvh_cache_type);
- bvhcache_unlock(bvh_cache, lock_started);
in_cache = true;
}
}
+ if (bvh_cache_p) {
+ bvhcache_unlock(*bvh_cache_p, lock_started);
+ }
+
/* Setup BVHTreeFromMesh */
bvhtree_from_mesh_faces_setup_data(
data, tree, in_cache, vert, vert_allocated, face, face_allocated);
@@ -1298,11 +1307,14 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data,
if (bvh_cache_p) {
BVHCache *bvh_cache = *bvh_cache_p;
bvhcache_insert(bvh_cache, tree, bvh_cache_type);
- bvhcache_unlock(bvh_cache, lock_started);
in_cache = true;
}
}
+ if (bvh_cache_p) {
+ bvhcache_unlock(*bvh_cache_p, lock_started);
+ }
+
/* Setup BVHTreeFromMesh */
bvhtree_from_mesh_looptri_setup_data(data,
tree,
@@ -1428,8 +1440,6 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
mesh->medge, mesh->totedge, mesh->mvert, verts_len, &loose_vert_len);
}
- /* TODO: a global mutex lock held during the expensive operation of
- * building the BVH tree is really bad for performance. */
tree = bvhtree_from_mesh_verts_ex(data,
mesh->mvert,
verts_len,
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 5ec4c84c013..7c0e4064cdb 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -54,7 +54,9 @@
#include "MEM_guardedalloc.h"
-/****************************** Camera Datablock *****************************/
+/* -------------------------------------------------------------------- */
+/** \name Camera Data-Block
+ * \{ */
static void camera_init_data(ID *id)
{
@@ -128,7 +130,11 @@ IDTypeInfo IDType_ID_CA = {
.foreach_id = camera_foreach_id,
};
-/******************************** Camera Usage *******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera Usage
+ * \{ */
void *BKE_camera_add(Main *bmain, const char *name)
{
@@ -188,7 +194,11 @@ int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey)
return sensor_fit;
}
-/******************************** Camera Params *******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera Parameter Access
+ * \{ */
void BKE_camera_params_init(CameraParams *params)
{
@@ -380,7 +390,11 @@ void BKE_camera_params_compute_matrix(CameraParams *params)
}
}
-/***************************** Camera View Frame *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera View Frame
+ * \{ */
void BKE_camera_view_frame_ex(const Scene *scene,
const Camera *camera,
@@ -482,6 +496,12 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec
scene, camera, 1.0, false, dummy_scale, dummy_asp, dummy_shift, &dummy_drawsize, r_vec);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera View Frame Fit to Points
+ * \{ */
+
#define CAMERA_VIEWFRAME_NUM_PLANES 4
typedef struct CameraViewFrameData {
@@ -723,7 +743,11 @@ bool BKE_camera_view_frame_fit_to_coords(const Depsgraph *depsgraph,
return camera_frame_fit_calc_from_data(&params, &data_cb, r_co, r_scale);
}
-/******************* multiview matrix functions ***********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera Multi-View Matrix
+ * \{ */
static void camera_model_matrix(const Object *camera, float r_modelmat[4][4])
{
@@ -1038,6 +1062,12 @@ void BKE_camera_multiview_params(const RenderData *rd,
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera Background Image
+ * \{ */
+
CameraBGImage *BKE_camera_background_image_new(Camera *cam)
{
CameraBGImage *bgpic = MEM_callocN(sizeof(CameraBGImage), "Background Image");
@@ -1072,3 +1102,5 @@ void BKE_camera_background_image_clear(Camera *cam)
bgpic = next_bgpic;
}
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 0b9780ac81c..467bd68c631 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -30,7 +30,6 @@
#include "DNA_scene_types.h"
#include "BLI_edgehash.h"
-#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_rand.h"
@@ -43,11 +42,12 @@
#include "BKE_cloth.h"
#include "BKE_effect.h"
#include "BKE_global.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_pointcache.h"
-#include "BPH_mass_spring.h"
+#include "SIM_mass_spring.h"
// #include "PIL_time.h" /* timing for debug prints */
@@ -146,6 +146,7 @@ void cloth_init(ClothModifierData *clmd)
clmd->sim_parms->uniform_pressure_force = 0.0f;
clmd->sim_parms->target_volume = 0.0f;
clmd->sim_parms->pressure_factor = 1.0f;
+ clmd->sim_parms->fluid_density = 0.0f;
clmd->sim_parms->vgroup_pressure = 0;
// also from softbodies
@@ -343,12 +344,12 @@ static int do_init_cloth(Object *ob, ClothModifierData *clmd, Mesh *result, int
return 0;
}
- BKE_cloth_solver_set_positions(clmd);
+ SIM_cloth_solver_set_positions(clmd);
ClothSimSettings *parms = clmd->sim_parms;
if (parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE &&
!(parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE_VOL)) {
- BKE_cloth_solver_set_volume(clmd);
+ SIM_cloth_solver_set_volume(clmd);
}
clmd->clothObject->last_frame = MINFRAME - 1;
@@ -403,7 +404,7 @@ static int do_step_cloth(
// TIMEIT_START(cloth_step)
/* call the solver. */
- ret = BPH_cloth_solve(depsgraph, ob, framenr, clmd, effectors);
+ ret = SIM_cloth_solve(depsgraph, ob, framenr, clmd, effectors);
// TIMEIT_END(cloth_step)
@@ -452,7 +453,7 @@ void clothModifier_do(ClothModifierData *clmd,
BKE_ptcache_invalidate(cache);
return;
}
- else if (framenr > endframe) {
+ if (framenr > endframe) {
framenr = endframe;
}
@@ -478,7 +479,7 @@ void clothModifier_do(ClothModifierData *clmd,
if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED ||
(!can_simulate && cache_result == PTCACHE_READ_OLD)) {
- BKE_cloth_solver_set_positions(clmd);
+ SIM_cloth_solver_set_positions(clmd);
cloth_to_object(ob, clmd, vertexCos);
BKE_ptcache_validate(cache, framenr);
@@ -491,8 +492,8 @@ void clothModifier_do(ClothModifierData *clmd,
return;
}
- else if (cache_result == PTCACHE_READ_OLD) {
- BKE_cloth_solver_set_positions(clmd);
+ if (cache_result == PTCACHE_READ_OLD) {
+ SIM_cloth_solver_set_positions(clmd);
}
else if (
/* 2.4x disabled lib, but this can be used in some cases, testing further - campbell */
@@ -536,7 +537,7 @@ void cloth_free_modifier(ClothModifierData *clmd)
cloth = clmd->clothObject;
if (cloth) {
- BPH_cloth_solver_free(clmd);
+ SIM_cloth_solver_free(clmd);
// Free the verts.
if (cloth->verts != NULL) {
@@ -585,7 +586,7 @@ void cloth_free_modifier(ClothModifierData *clmd)
}
if (cloth->sew_edge_graph) {
- BLI_ghash_free(cloth->sew_edge_graph, MEM_freeN, NULL);
+ BLI_edgeset_free(cloth->sew_edge_graph);
cloth->sew_edge_graph = NULL;
}
@@ -618,7 +619,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd)
printf("cloth_free_modifier_extern in\n");
}
- BPH_cloth_solver_free(clmd);
+ SIM_cloth_solver_free(clmd);
// Free the verts.
if (cloth->verts != NULL) {
@@ -667,7 +668,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd)
}
if (cloth->sew_edge_graph) {
- BLI_ghash_free(cloth->sew_edge_graph, MEM_freeN, NULL);
+ BLI_edgeset_free(cloth->sew_edge_graph);
cloth->sew_edge_graph = NULL;
}
@@ -918,10 +919,10 @@ static int cloth_from_object(
}
// init our solver
- BPH_cloth_solver_init(ob, clmd);
+ SIM_cloth_solver_init(ob, clmd);
if (!first) {
- BKE_cloth_solver_set_positions(clmd);
+ SIM_cloth_solver_set_positions(clmd);
}
clmd->clothObject->bvhtree = bvhtree_build_from_cloth(clmd, clmd->coll_parms->epsilon);
@@ -1036,7 +1037,7 @@ static void cloth_free_errorsprings(Cloth *cloth,
}
BLI_INLINE void cloth_bend_poly_dir(
- ClothVertex *verts, int i, int j, int *inds, int len, float r_dir[3])
+ ClothVertex *verts, int i, int j, const int *inds, int len, float r_dir[3])
{
float cent[3] = {0};
float fact = 1.0f / len;
@@ -1269,6 +1270,21 @@ static void cloth_update_verts(Object *ob, ClothModifierData *clmd, Mesh *mesh)
}
}
+/* Write rest vert locations to a copy of the mesh. */
+static Mesh *cloth_make_rest_mesh(ClothModifierData *clmd, Mesh *mesh)
+{
+ Mesh *new_mesh = BKE_mesh_copy_for_eval(mesh, false);
+ ClothVertex *verts = clmd->clothObject->verts;
+ MVert *mvert = new_mesh->mvert;
+
+ /* vertex count is already ensured to match */
+ for (unsigned i = 0; i < mesh->totvert; i++, verts++) {
+ copy_v3_v3(mvert[i].co, verts->xrest);
+ }
+
+ return new_mesh;
+}
+
/* Update spring rest length, for dynamically deformable cloth */
static void cloth_update_spring_lengths(ClothModifierData *clmd, Mesh *mesh)
{
@@ -1540,9 +1556,8 @@ static bool find_internal_spring_target_vertex(BVHTreeFromMesh *treedata,
*r_tar_v_idx = vert_idx;
return true;
}
- else {
- return false;
- }
+
+ return false;
}
static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
@@ -1597,10 +1612,18 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
BVHTreeFromMesh treedata = {NULL};
unsigned int tar_v_idx;
BLI_bitmap *verts_used = NULL;
+ Mesh *tmp_mesh = NULL;
RNG *rng;
+ /* If using the rest shape key, it's necessary to make a copy of the mesh. */
+ if (clmd->sim_parms->shapekey_rest &&
+ !(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH)) {
+ tmp_mesh = cloth_make_rest_mesh(clmd, mesh);
+ BKE_mesh_calc_normals(tmp_mesh);
+ }
+
verts_used = BLI_BITMAP_NEW(mvert_num * mvert_num, __func__);
- BKE_bvhtree_from_mesh_get(&treedata, mesh, BVHTREE_FROM_LOOPTRI, 2);
+ BKE_bvhtree_from_mesh_get(&treedata, tmp_mesh ? tmp_mesh : mesh, BVHTREE_FROM_LOOPTRI, 2);
rng = BLI_rng_new_srandom(0);
for (int i = 0; i < mvert_num; i++) {
@@ -1645,12 +1668,18 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
cloth_free_errorsprings(cloth, edgelist, spring_ref);
MEM_freeN(verts_used);
free_bvhtree_from_mesh(&treedata);
+ if (tmp_mesh) {
+ BKE_mesh_free(tmp_mesh);
+ }
return 0;
}
}
}
MEM_freeN(verts_used);
free_bvhtree_from_mesh(&treedata);
+ if (tmp_mesh) {
+ BKE_mesh_free(tmp_mesh);
+ }
BLI_rng_free(rng);
}
@@ -1662,8 +1691,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW) {
/* cloth->sew_edge_graph should not exist before this */
BLI_assert(cloth->sew_edge_graph == NULL);
- cloth->sew_edge_graph = BLI_ghash_new(
- BLI_ghashutil_inthash_v2_p, BLI_ghashutil_inthash_v2_cmp, "cloth_sewing_edges_graph");
+ cloth->sew_edge_graph = BLI_edgeset_new("cloth_sewing_edges_graph");
}
/* Structural springs. */
@@ -1678,18 +1706,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
spring->lin_stiffness = 1.0f;
spring->type = CLOTH_SPRING_TYPE_SEWING;
- /* set indices of verts of the sewing edge symmetrically in sew_edge_graph */
- unsigned int *vertex_index_pair = MEM_mallocN(sizeof(unsigned int) * 2,
- "sewing_edge_index_pair_01");
- if (medge[i].v1 < medge[i].v2) {
- vertex_index_pair[0] = medge[i].v1;
- vertex_index_pair[1] = medge[i].v2;
- }
- else {
- vertex_index_pair[0] = medge[i].v2;
- vertex_index_pair[1] = medge[i].v1;
- }
- BLI_ghash_insert(cloth->sew_edge_graph, vertex_index_pair, NULL);
+ BLI_edgeset_insert(cloth->sew_edge_graph, medge[i].v1, medge[i].v2);
}
else {
shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 290b181f172..6118325c231 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"
@@ -53,7 +54,9 @@
#include "MEM_guardedalloc.h"
-/******************************** Prototypes ********************************/
+/* -------------------------------------------------------------------- */
+/** \name Prototypes
+ * \{ */
static bool collection_child_add(Collection *parent,
Collection *collection,
@@ -72,7 +75,11 @@ static CollectionParent *collection_find_parent(Collection *child, Collection *c
static bool collection_find_child_recursive(Collection *parent, Collection *collection);
-/****************************** Collection Datablock ************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Data-Block
+ * \{ */
/**
* Only copy internal data of Collection ID from source
@@ -168,7 +175,11 @@ IDTypeInfo IDType_ID_GR = {
.foreach_id = collection_foreach_id,
};
-/***************************** Add Collection *******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Collection
+ * \{ */
/* Add new collection, without view layer syncing. */
static Collection *collection_add(Main *bmain,
@@ -238,7 +249,39 @@ void BKE_collection_add_from_object(Main *bmain,
BKE_main_collection_sync(bmain);
}
-/*********************** Free and Delete Collection ****************************/
+/**
+ * Add \a collection_dst to all scene collections that reference collection \a collection_src is
+ * in.
+ *
+ * Logic is very similar to #BKE_collection_object_add_from().
+ */
+void BKE_collection_add_from_collection(Main *bmain,
+ Scene *scene,
+ Collection *collection_src,
+ Collection *collection_dst)
+{
+ bool is_instantiated = false;
+
+ FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
+ if (!ID_IS_LINKED(collection) && BKE_collection_has_collection(collection, collection_src)) {
+ collection_child_add(collection, collection_dst, 0, true);
+ is_instantiated = true;
+ }
+ }
+ FOREACH_SCENE_COLLECTION_END;
+
+ if (!is_instantiated) {
+ collection_child_add(scene->master_collection, collection_dst, 0, true);
+ }
+
+ BKE_main_collection_sync(bmain);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Free and Delete Collection
+ * \{ */
/** Free (or release) any data used by this collection (does not free the collection itself). */
void BKE_collection_free(Collection *collection)
@@ -303,19 +346,23 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy)
return true;
}
-/***************************** Collection Copy *******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Copy
+ * \{ */
+
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
@@ -324,15 +371,14 @@ 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);
+ else if (collection_old->id.newid == NULL) {
+ collection_new = (Collection *)BKE_id_copy_for_duplicate(
+ bmain, (ID *)collection_old, duplicate_flags);
- /* 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);
+ if (collection_new == collection_old) {
+ return collection_new;
}
+
do_full_process = true;
}
else {
@@ -357,7 +403,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;
}
@@ -368,15 +414,13 @@ static Collection *collection_duplicate_recursive(Main *bmain,
Object *ob_old = cob->ob;
Object *ob_new = (Object *)ob_old->id.newid;
- /* If collection is an override, we do not want to duplicate any linked data-block, as that
- * would generate a purely local data. */
- if (is_collection_liboverride && ID_IS_LINKED(ob_old)) {
- continue;
+ if (ob_new == NULL) {
+ ob_new = BKE_object_duplicate(
+ bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS);
}
- if (ob_new == NULL) {
- ob_new = BKE_object_duplicate(bmain, ob_old, object_dupflag);
- ID_NEW_SET(ob_old, ob_new);
+ if (ob_new == ob_old) {
+ continue;
}
collection_object_add(bmain, collection_new, ob_new, 0, true);
@@ -389,74 +433,79 @@ static Collection *collection_duplicate_recursive(Main *bmain,
LISTBASE_FOREACH_MUTABLE (CollectionChild *, child, &collection_old->children) {
Collection *child_collection_old = child->collection;
- if (is_collection_liboverride && ID_IS_LINKED(child_collection_old)) {
- continue;
+ Collection *child_collection_new = collection_duplicate_recursive(
+ bmain, collection_new, child_collection_old, duplicate_flags, duplicate_options);
+ if (child_collection_new != child_collection_old) {
+ collection_child_remove(collection_new, child_collection_old);
}
-
- collection_duplicate_recursive(
- bmain, collection_new, child_collection_old, do_hierarchy, do_objects, do_obdata);
- collection_child_remove(collection_new, child_collection_old);
}
return collection_new;
}
/**
- * Makes a standard (aka shallow) ID copy of a Collection.
+ * Make a deep copy (aka duplicate) of the given collection and all of its children, recursively.
*
- * 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.
- *
- * 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);
+ /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
+ * all expected linked data. */
+ if (ID_IS_LINKED(collection)) {
+ duplicate_flags |= USER_DUP_LINKED_ID;
+ }
}
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;
}
-/********************************* Naming *******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Naming
+ * \{ */
/**
* The automatic/fallback name of a new collection.
@@ -496,7 +545,11 @@ const char *BKE_collection_ui_name_get(struct Collection *collection)
}
}
-/* **************** Object List Cache *******************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object List Cache
+ * \{ */
static void collection_object_cache_fill(ListBase *lb, Collection *collection, int parent_restrict)
{
@@ -569,7 +622,11 @@ Base *BKE_collection_or_layer_objects(const ViewLayer *view_layer, Collection *c
}
}
-/*********************** Scene Master Collection ***************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Scene Master Collection
+ * \{ */
Collection *BKE_collection_master_add()
{
@@ -594,7 +651,11 @@ Scene *BKE_collection_master_scene_search(const Main *bmain, const Collection *m
return NULL;
}
-/*********************** Cyclic Checks ************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cyclic Checks
+ * \{ */
static bool collection_object_cyclic_check_internal(Object *object, Collection *collection)
{
@@ -634,7 +695,11 @@ bool BKE_collection_object_cyclic_check(Main *bmain, Object *object, Collection
return collection_object_cyclic_check_internal(object, collection);
}
-/******************* Collection Object Membership *******************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Object Membership
+ * \{ */
bool BKE_collection_has_object(Collection *collection, const Object *ob)
{
@@ -695,7 +760,11 @@ bool BKE_collection_is_empty(Collection *collection)
BLI_listbase_is_empty(&collection->children);
}
-/********************** Collection Objects *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Objects
+ * \{ */
static void collection_tag_update_parent_recursive(Main *bmain,
Collection *collection,
@@ -716,6 +785,31 @@ static void collection_tag_update_parent_recursive(Main *bmain,
}
}
+static Collection *collection_parent_editable_find_recursive(Collection *collection)
+{
+ if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
+ return collection;
+ }
+
+ if (collection->flag & COLLECTION_IS_MASTER) {
+ return NULL;
+ }
+
+ LISTBASE_FOREACH (CollectionParent *, collection_parent, &collection->parents) {
+ if (!ID_IS_LINKED(collection_parent->collection) &&
+ !ID_IS_OVERRIDE_LIBRARY(collection_parent->collection)) {
+ return collection_parent->collection;
+ }
+ Collection *editable_collection = collection_parent_editable_find_recursive(
+ collection_parent->collection);
+ if (editable_collection != NULL) {
+ return editable_collection;
+ }
+ }
+
+ return NULL;
+}
+
static bool collection_object_add(
Main *bmain, Collection *collection, Object *ob, int flag, const bool add_us)
{
@@ -786,6 +880,15 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
return false;
}
+ collection = collection_parent_editable_find_recursive(collection);
+
+ /* Only case where this pointer can be NULL is when scene itself is linked, this case should
+ * never be reached. */
+ BLI_assert(collection != NULL);
+ if (collection == NULL) {
+ return false;
+ }
+
if (!collection_object_add(bmain, collection, ob, 0, true)) {
return false;
}
@@ -808,7 +911,8 @@ void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, O
bool is_instantiated = false;
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
- if (!ID_IS_LINKED(collection) && BKE_collection_has_object(collection, ob_src)) {
+ if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection) &&
+ BKE_collection_has_object(collection, ob_src)) {
collection_object_add(bmain, collection, ob_dst, 0, true);
is_instantiated = true;
}
@@ -849,7 +953,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)
@@ -1008,7 +1112,11 @@ void BKE_collection_object_move(
}
}
-/***************** Collection Scene Membership ****************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Scene Membership
+ * \{ */
bool BKE_collection_is_in_scene(Collection *collection)
{
@@ -1033,21 +1141,114 @@ void BKE_collections_after_lib_link(Main *bmain)
BKE_main_collection_sync(bmain);
}
-/********************** Collection Children *******************/
+/** \} */
-bool BKE_collection_find_cycle(Collection *new_ancestor, Collection *collection)
+/* -------------------------------------------------------------------- */
+/** \name Collection Children
+ * \{ */
+
+static bool collection_instance_find_recursive(Collection *collection,
+ Collection *instance_collection)
+{
+ LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
+ if (collection_object->ob != NULL &&
+ /* Object from a given collection should never instantiate that collection either. */
+ ELEM(collection_object->ob->instance_collection, instance_collection, collection)) {
+ return true;
+ }
+ }
+
+ LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
+ if (collection_instance_find_recursive(collection_child->collection, instance_collection)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Find potential cycles in collections.
+ *
+ * \param new_ancestor the potential new owner of given \a collection, or the collection to check
+ * if the later is NULL.
+ * \param collection the collection we want to add to \a new_ancestor, may be NULL if we just want
+ * to ensure \a new_ancestor does not already have cycles.
+ * \return true if a cycle is found.
+ */
+bool BKE_collection_cycle_find(Collection *new_ancestor, Collection *collection)
{
if (collection == new_ancestor) {
return true;
}
+ if (collection == NULL) {
+ collection = new_ancestor;
+ }
+
LISTBASE_FOREACH (CollectionParent *, parent, &new_ancestor->parents) {
- if (BKE_collection_find_cycle(parent->collection, collection)) {
+ if (BKE_collection_cycle_find(parent->collection, collection)) {
return true;
}
}
- return false;
+ /* 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_instance_find_recursive(collection, new_ancestor);
+}
+
+static bool collection_instance_fix_recursive(Collection *parent_collection,
+ Collection *collection)
+{
+ bool cycles_found = false;
+
+ LISTBASE_FOREACH (CollectionObject *, collection_object, &parent_collection->gobject) {
+ if (collection_object->ob != NULL &&
+ collection_object->ob->instance_collection == collection) {
+ id_us_min(&collection->id);
+ collection_object->ob->instance_collection = NULL;
+ cycles_found = true;
+ }
+ }
+
+ LISTBASE_FOREACH (CollectionChild *, collection_child, &parent_collection->children) {
+ if (collection_instance_fix_recursive(collection_child->collection, collection)) {
+ cycles_found = true;
+ }
+ }
+
+ return cycles_found;
+}
+
+static bool collection_cycle_fix_recursive(Main *bmain,
+ Collection *parent_collection,
+ Collection *collection)
+{
+ bool cycles_found = false;
+
+ LISTBASE_FOREACH_MUTABLE (CollectionParent *, parent, &parent_collection->parents) {
+ if (BKE_collection_cycle_find(parent->collection, collection)) {
+ BKE_collection_child_remove(bmain, parent->collection, parent_collection);
+ cycles_found = true;
+ }
+ else if (collection_cycle_fix_recursive(bmain, parent->collection, collection)) {
+ cycles_found = true;
+ }
+ }
+
+ return cycles_found;
+}
+
+/**
+ * Find and fix potential cycles in collections.
+ *
+ * \param collection the collection to check for existing cycles.
+ * \return true if cycles are found and fixed.
+ */
+bool BKE_collection_cycles_fix(Main *bmain, Collection *collection)
+{
+ return collection_cycle_fix_recursive(bmain, collection, collection) ||
+ collection_instance_fix_recursive(collection, collection);
}
static CollectionChild *collection_find_child(Collection *parent, Collection *collection)
@@ -1089,7 +1290,7 @@ static bool collection_child_add(Collection *parent,
if (child) {
return false;
}
- if (BKE_collection_find_cycle(parent, collection)) {
+ if (BKE_collection_cycle_find(parent, collection)) {
return false;
}
@@ -1167,7 +1368,7 @@ void BKE_collection_parent_relations_rebuild(Collection *collection)
child = child_next) {
child_next = child->next;
- if (child->collection == NULL || BKE_collection_find_cycle(collection, child->collection)) {
+ if (child->collection == NULL || BKE_collection_cycle_find(collection, child->collection)) {
BLI_freelinkN(&collection->children, child);
}
else {
@@ -1204,7 +1405,11 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain)
/* Scene's master collections will be 'root' parent of most of our collections, so start with
* them. */
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
- collection_parents_rebuild_recursive(scene->master_collection);
+ /* This function can be called from readfile.c, when this pointer is not guaranteed to be NULL.
+ */
+ if (scene->master_collection != NULL) {
+ collection_parents_rebuild_recursive(scene->master_collection);
+ }
}
/* We may have parent chains outside of scene's master_collection context? At least, readfile's
@@ -1220,7 +1425,11 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain)
}
}
-/********************** Collection index *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection Index
+ * \{ */
static Collection *collection_from_index_recursive(Collection *collection,
const int index,
@@ -1306,7 +1515,11 @@ bool BKE_collection_objects_select(ViewLayer *view_layer, Collection *collection
}
}
-/***************** Collection move (outliner drag & drop) *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Collection move (outliner drag & drop)
+ * \{ */
bool BKE_collection_move(Main *bmain,
Collection *to_parent,
@@ -1318,7 +1531,7 @@ bool BKE_collection_move(Main *bmain,
if (collection->flag & COLLECTION_IS_MASTER) {
return false;
}
- if (BKE_collection_find_cycle(to_parent, collection)) {
+ if (BKE_collection_cycle_find(to_parent, collection)) {
return false;
}
@@ -1399,7 +1612,11 @@ bool BKE_collection_move(Main *bmain,
return true;
}
-/**************************** Iterators ******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Iterators
+ * \{ */
/* scene collection iteractor */
@@ -1585,3 +1802,5 @@ void BKE_scene_objects_iterator_end(BLI_Iterator *iter)
MEM_freeN(data);
}
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c
index daf1602319f..31d49dd4508 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -32,7 +32,7 @@
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
-#include "BLI_ghash.h"
+#include "BLI_edgehash.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_task.h"
@@ -1153,17 +1153,7 @@ static bool cloth_bvh_selfcollision_is_active(const Cloth *cloth,
}
if (sewing_active) {
- unsigned int vertex_index_pair[2];
- /* The indices pair are ordered, thus must ensure the same here as well */
- if (tri_a->tri[i] < tri_b->tri[j]) {
- vertex_index_pair[0] = tri_a->tri[i];
- vertex_index_pair[1] = tri_b->tri[j];
- }
- else {
- vertex_index_pair[0] = tri_b->tri[j];
- vertex_index_pair[1] = tri_a->tri[i];
- }
- if (BLI_ghash_haskey(cloth->sew_edge_graph, vertex_index_pair)) {
+ if (BLI_edgeset_haskey(cloth->sew_edge_graph, tri_a->tri[i], tri_b->tri[j])) {
return false;
}
}
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..2ef32895db9 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -53,6 +53,7 @@
#include "BKE_action.h"
#include "BKE_anim_path.h"
+#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_bvhutils.h"
#include "BKE_cachefile.h"
@@ -2625,7 +2626,7 @@ static void actcon_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
}
}
-static void actcon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
+static void actcon_get_tarmat(struct Depsgraph *depsgraph,
bConstraint *con,
bConstraintOb *cob,
bConstraintTarget *ct,
@@ -2679,6 +2680,8 @@ static void actcon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
s = (vec[axis] - data->min) / (data->max - data->min);
CLAMP(s, 0, 1);
t = (s * (data->end - data->start)) + data->start;
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ t);
if (G.debug & G_DEBUG) {
printf("do Action Constraint %s - Ob %s Pchan %s\n",
@@ -2693,7 +2696,7 @@ static void actcon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
/* evaluate using workob */
/* FIXME: we don't have any consistent standards on limiting effects on object... */
- what_does_obaction(cob->ob, &workob, NULL, data->act, NULL, t);
+ what_does_obaction(cob->ob, &workob, NULL, data->act, NULL, &anim_eval_context);
BKE_object_to_mat4(&workob, ct->matrix);
}
else if (cob->type == CONSTRAINT_OBTYPE_BONE) {
@@ -2710,7 +2713,7 @@ static void actcon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
tchan->rotmode = pchan->rotmode;
/* evaluate action using workob (it will only set the PoseChannel in question) */
- what_does_obaction(cob->ob, &workob, &pose, data->act, pchan->name, t);
+ what_does_obaction(cob->ob, &workob, &pose, data->act, pchan->name, &anim_eval_context);
/* convert animation to matrices for use here */
BKE_pchan_calc_mat(tchan);
@@ -5412,9 +5415,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 +5451,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 +5477,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 +5624,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/context.c b/source/blender/blenkernel/intern/context.c
index 8de12139306..30f021b0e81 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -694,6 +694,11 @@ wmWindowManager *CTX_wm_manager(const bContext *C)
return C->wm.manager;
}
+bool CTX_wm_interface_locked(const bContext *C)
+{
+ return (bool)C->wm.manager->is_interface_locked;
+}
+
wmWindow *CTX_wm_window(const bContext *C)
{
return ctx_wm_python_context_get(C, "window", &RNA_Window, C->wm.window);
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 e67cf8573f3..acc3c52c08c 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -29,7 +29,6 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
-#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -43,7 +42,6 @@
/* for dereferencing pointers */
#include "DNA_key_types.h"
#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
#include "BKE_curve.h"
@@ -54,7 +52,6 @@
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
-#include "BKE_material.h"
#include "BKE_object.h"
#include "DEG_depsgraph.h"
@@ -226,7 +223,7 @@ void BKE_curve_init(Curve *cu, const short curve_type)
cu->vfont->id.us += 4;
cu->str = MEM_malloc_arrayN(12, sizeof(unsigned char), "str");
BLI_strncpy(cu->str, "Text", 12);
- cu->len = cu->len_wchar = cu->pos = 4;
+ cu->len = cu->len_char32 = cu->pos = 4;
cu->strinfo = MEM_calloc_arrayN(12, sizeof(CharInfo), "strinfo new");
cu->totbox = cu->actbox = 1;
cu->tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "textbox");
@@ -1160,7 +1157,7 @@ void BKE_nurb_knot_calc_v(Nurb *nu)
}
static void basisNurb(
- float t, short order, int pnts, float *knots, float *basis, int *start, int *end)
+ float t, short order, int pnts, const float *knots, float *basis, int *start, int *end)
{
float d, e;
int i, i1 = 0, i2 = 0, j, orderpluspnts, opp2, o2;
@@ -1193,9 +1190,8 @@ static void basisNurb(
}
break;
}
- else {
- basis[i] = 0.0;
- }
+
+ basis[i] = 0.0;
}
basis[i] = 0.0;
@@ -1730,205 +1726,6 @@ static void forward_diff_bezier_cotangent(const float p0[3],
}
}
-/* ***************** BEVEL ****************** */
-
-void BKE_curve_bevel_make(Object *ob, ListBase *disp)
-{
- DispList *dl, *dlnew;
- Curve *bevcu, *cu;
- float *fp, facx, facy, angle, dangle;
- int nr, a;
-
- cu = ob->data;
- BLI_listbase_clear(disp);
-
- /* if a font object is being edited, then do nothing */
- // XXX if ( ob == obedit && ob->type == OB_FONT ) return;
-
- if (cu->bevobj) {
- if (cu->bevobj->type != OB_CURVE) {
- return;
- }
-
- bevcu = cu->bevobj->data;
- if (bevcu->ext1 == 0.0f && bevcu->ext2 == 0.0f) {
- ListBase bevdisp = {NULL, NULL};
- facx = cu->bevobj->scale[0];
- facy = cu->bevobj->scale[1];
-
- if (cu->bevobj->runtime.curve_cache) {
- dl = cu->bevobj->runtime.curve_cache->disp.first;
- }
- else {
- BLI_assert(cu->bevobj->runtime.curve_cache != NULL);
- dl = NULL;
- }
-
- while (dl) {
- if (ELEM(dl->type, DL_POLY, DL_SEGM)) {
- dlnew = MEM_mallocN(sizeof(DispList), "makebevelcurve1");
- *dlnew = *dl;
- dlnew->verts = MEM_malloc_arrayN(
- dl->parts * dl->nr, 3 * sizeof(float), "makebevelcurve1");
- memcpy(dlnew->verts, dl->verts, 3 * sizeof(float) * dl->parts * dl->nr);
-
- if (dlnew->type == DL_SEGM) {
- dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE);
- }
-
- BLI_addtail(disp, dlnew);
- fp = dlnew->verts;
- nr = dlnew->parts * dlnew->nr;
- while (nr--) {
- fp[2] = fp[1] * facy;
- fp[1] = -fp[0] * facx;
- fp[0] = 0.0;
- fp += 3;
- }
- }
- dl = dl->next;
- }
-
- BKE_displist_free(&bevdisp);
- }
- }
- else if (cu->ext1 == 0.0f && cu->ext2 == 0.0f) {
- /* pass */
- }
- else if (cu->ext2 == 0.0f) {
- dl = MEM_callocN(sizeof(DispList), "makebevelcurve2");
- dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), "makebevelcurve2");
- BLI_addtail(disp, dl);
- dl->type = DL_SEGM;
- dl->parts = 1;
- dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
- dl->nr = 2;
-
- fp = dl->verts;
- fp[0] = fp[1] = 0.0;
- fp[2] = -cu->ext1;
- fp[3] = fp[4] = 0.0;
- fp[5] = cu->ext1;
- }
- else if ((cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) {
- /* We make a full round bevel in that case. */
-
- nr = 4 + 2 * cu->bevresol;
-
- dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1");
- dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p1");
- BLI_addtail(disp, dl);
- dl->type = DL_POLY;
- dl->parts = 1;
- dl->flag = DL_BACK_CURVE;
- dl->nr = nr;
-
- /* a circle */
- fp = dl->verts;
- dangle = (2.0f * (float)M_PI / (nr));
- angle = -(nr - 1) * dangle;
-
- for (a = 0; a < nr; a++) {
- fp[0] = 0.0;
- fp[1] = (cosf(angle) * (cu->ext2));
- fp[2] = (sinf(angle) * (cu->ext2)) - cu->ext1;
- angle += dangle;
- fp += 3;
- }
- }
- else {
- /* The general case for nonzero extrusion or an incomplete loop. */
- dl = MEM_callocN(sizeof(DispList), "makebevelcurve");
- if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) {
- /* The full loop. */
- nr = 4 * cu->bevresol + 6;
- dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
- }
- else if ((cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) {
- /* Half the loop. */
- nr = 2 * (cu->bevresol + 1) + ((cu->ext1 == 0.0f) ? 1 : 2);
- dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
- }
- else {
- /* One quarter of the loop (just front or back). */
- nr = (cu->ext1 == 0.0f) ? cu->bevresol + 2 : cu->bevresol + 3;
- dl->flag = (cu->flag & CU_FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE;
- }
-
- dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve");
- BLI_addtail(disp, dl);
- /* Use a different type depending on whether the loop is complete or not. */
- dl->type = ((cu->flag & (CU_FRONT | CU_BACK)) == 0) ? DL_POLY : DL_SEGM;
- dl->parts = 1;
- dl->nr = nr;
-
- fp = dl->verts;
- dangle = (float)M_PI_2 / (cu->bevresol + 1);
- angle = 0.0;
-
- /* Build the back section. */
- if (cu->flag & CU_BACK || !(cu->flag & CU_FRONT)) {
- angle = (float)M_PI_2 * 3.0f;
- for (a = 0; a < cu->bevresol + 2; a++) {
- fp[0] = 0.0;
- fp[1] = (float)(cosf(angle) * (cu->ext2));
- fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1;
- angle += dangle;
- fp += 3;
- }
- if ((cu->ext1 != 0.0f) && !(cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) {
- /* Add the extrusion if we're only building the back. */
- fp[0] = 0.0;
- fp[1] = cu->ext2;
- fp[2] = cu->ext1;
- }
- }
-
- /* Build the front section. */
- if (cu->flag & CU_FRONT || !(cu->flag & CU_BACK)) {
- if ((cu->ext1 != 0.0f) && !(cu->flag & CU_BACK) && (cu->flag & CU_FRONT)) {
- /* Add the extrusion if we're only building the back. */
- fp[0] = 0.0;
- fp[1] = cu->ext2;
- fp[2] = -cu->ext1;
- fp += 3;
- }
- /* Don't duplicate the last back vertex. */
- angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0;
- int front_len = (cu->ext1 == 0.0f && ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT))) ?
- cu->bevresol + 1 :
- cu->bevresol + 2;
- for (a = 0; a < front_len; a++) {
- fp[0] = 0.0;
- fp[1] = (float)(cosf(angle) * (cu->ext2));
- fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1;
- angle += dangle;
- fp += 3;
- }
- }
-
- /* Build the other half only if we're building the full loop. */
- if (!(cu->flag & (CU_FRONT | CU_BACK))) {
- for (a = 0; a < cu->bevresol + 1; a++) {
- fp[0] = 0.0;
- fp[1] = (float)(cosf(angle) * (cu->ext2));
- fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1;
- angle += dangle;
- fp += 3;
- }
-
- angle = (float)M_PI;
- for (a = 0; a < cu->bevresol + 1; a++) {
- fp[0] = 0.0;
- fp[1] = (float)(cosf(angle) * (cu->ext2));
- fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1;
- angle += dangle;
- fp += 3;
- }
- }
- }
-}
-
static int cu_isectLL(const float v1[3],
const float v2[3],
const float v3[3],
@@ -2043,7 +1840,7 @@ static int vergxcobev(const void *a1, const void *a2)
if (x1->left > x2->left) {
return 1;
}
- else if (x1->left < x2->left) {
+ if (x1->left < x2->left) {
return -1;
}
return 0;
@@ -3116,8 +2913,9 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
if (bl->poly > 0) {
BevPoint *bevp;
- min = 300000.0;
bevp = bl->bevpoints;
+ bevp1 = bl->bevpoints;
+ min = bevp1->vec[0];
nr = bl->nr;
while (nr--) {
if (min > bevp->vec[0]) {
@@ -3564,8 +3362,13 @@ static void free_arrays(void *buffer)
}
/* computes in which direction to change h[i] to satisfy conditions better */
-static float bezier_relax_direction(
- float *a, float *b, float *c, float *d, float *h, int i, int count)
+static float bezier_relax_direction(const float *a,
+ const float *b,
+ const float *c,
+ const float *d,
+ const float *h,
+ int i,
+ int count)
{
/* current deviation between sides of the equation */
float state = a[i] * h[(i + count - 1) % count] + b[i] * h[i] + c[i] * h[(i + 1) % count] - d[i];
@@ -3581,8 +3384,15 @@ static void bezier_lock_unknown(float *a, float *b, float *c, float *d, int i, f
d[i] = value;
}
-static void bezier_restore_equation(
- float *a, float *b, float *c, float *d, float *a0, float *b0, float *c0, float *d0, int i)
+static void bezier_restore_equation(float *a,
+ float *b,
+ float *c,
+ float *d,
+ const float *a0,
+ const float *b0,
+ const float *c0,
+ const float *d0,
+ int i)
{
a[i] = a0[i];
b[i] = b0[i];
@@ -3590,8 +3400,14 @@ static void bezier_restore_equation(
d[i] = d0[i];
}
-static bool tridiagonal_solve_with_limits(
- float *a, float *b, float *c, float *d, float *h, float *hmin, float *hmax, int solve_count)
+static bool tridiagonal_solve_with_limits(float *a,
+ float *b,
+ float *c,
+ float *d,
+ float *h,
+ const float *hmin,
+ const float *hmax,
+ int solve_count)
{
float *a0, *b0, *c0, *d0;
float **arrays[] = {&a0, &b0, &c0, &d0, NULL};
@@ -3730,7 +3546,7 @@ static bool tridiagonal_solve_with_limits(
/* clang-format on */
static void bezier_eq_continuous(
- float *a, float *b, float *c, float *d, float *dy, float *l, int i)
+ float *a, float *b, float *c, float *d, const float *dy, const float *l, int i)
{
a[i] = l[i] * l[i];
b[i] = 2.0f * (l[i] + 1);
@@ -3739,7 +3555,7 @@ static void bezier_eq_continuous(
}
static void bezier_eq_noaccel_right(
- float *a, float *b, float *c, float *d, float *dy, float *l, int i)
+ float *a, float *b, float *c, float *d, const float *dy, const float *l, int i)
{
a[i] = 0.0f;
b[i] = 2.0f;
@@ -3748,7 +3564,7 @@ static void bezier_eq_noaccel_right(
}
static void bezier_eq_noaccel_left(
- float *a, float *b, float *c, float *d, float *dy, float *l, int i)
+ float *a, float *b, float *c, float *d, const float *dy, const float *l, int i)
{
a[i] = l[i] * l[i];
b[i] = 2.0f * l[i];
@@ -4084,9 +3900,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,
@@ -4615,7 +4431,7 @@ void BKE_nurb_direction_switch(Nurb *nu)
bp1++;
bp2--;
}
- /* If there're odd number of points no need to touch coord of middle one,
+ /* If there are odd number of points no need to touch coord of middle one,
* but still need to change it's tilt.
*/
if (nu->pntsu & 1) {
@@ -4827,7 +4643,7 @@ float (*BKE_curve_nurbs_key_vert_coords_alloc(ListBase *lb, float *key, int *r_v
return cos;
}
-void BKE_curve_nurbs_key_vert_tilts_apply(ListBase *lb, float *key)
+void BKE_curve_nurbs_key_vert_tilts_apply(ListBase *lb, const float *key)
{
Nurb *nu;
int i;
@@ -5067,32 +4883,31 @@ bool BKE_nurb_type_convert(Nurb *nu,
}
return false; /* conversion impossible */
}
- else {
- bezt = MEM_calloc_arrayN(nr, sizeof(BezTriple), "setsplinetype2");
- nu->bezt = bezt;
- a = nr;
- bp = nu->bp;
- while (a--) {
- copy_v3_v3(bezt->vec[0], bp->vec);
- bezt->f1 = bp->f1;
- bp++;
- copy_v3_v3(bezt->vec[1], bp->vec);
- bezt->f2 = bp->f1;
- bp++;
- copy_v3_v3(bezt->vec[2], bp->vec);
- bezt->f3 = bp->f1;
- bezt->radius = bp->radius;
- bezt->weight = bp->weight;
- bp++;
- bezt++;
- }
- MEM_freeN(nu->bp);
- nu->bp = NULL;
- MEM_freeN(nu->knotsu);
- nu->knotsu = NULL;
- nu->pntsu = nr;
- nu->type = CU_BEZIER;
+
+ bezt = MEM_calloc_arrayN(nr, sizeof(BezTriple), "setsplinetype2");
+ nu->bezt = bezt;
+ a = nr;
+ bp = nu->bp;
+ while (a--) {
+ copy_v3_v3(bezt->vec[0], bp->vec);
+ bezt->f1 = bp->f1;
+ bp++;
+ copy_v3_v3(bezt->vec[1], bp->vec);
+ bezt->f2 = bp->f1;
+ bp++;
+ copy_v3_v3(bezt->vec[2], bp->vec);
+ bezt->f3 = bp->f1;
+ bezt->radius = bp->radius;
+ bezt->weight = bp->weight;
+ bp++;
+ bezt++;
}
+ MEM_freeN(nu->bp);
+ nu->bp = NULL;
+ MEM_freeN(nu->knotsu);
+ nu->knotsu = NULL;
+ nu->pntsu = nr;
+ nu->type = CU_BEZIER;
}
}
@@ -5143,10 +4958,9 @@ int BKE_curve_nurb_vert_index_get(const Nurb *nu, const void *vert)
BLI_assert(ARRAY_HAS_ITEM((BezTriple *)vert, nu->bezt, nu->pntsu));
return (BezTriple *)vert - nu->bezt;
}
- else {
- BLI_assert(ARRAY_HAS_ITEM((BPoint *)vert, nu->bp, nu->pntsu * nu->pntsv));
- return (BPoint *)vert - nu->bp;
- }
+
+ BLI_assert(ARRAY_HAS_ITEM((BPoint *)vert, nu->bp, nu->pntsu * nu->pntsv));
+ return (BPoint *)vert - nu->bp;
}
/* Set active nurb and active vert for curve */
@@ -5425,7 +5239,7 @@ void BKE_curve_material_index_remove(Curve *cu, int index)
if (curvetype == OB_FONT) {
struct CharInfo *info = cu->strinfo;
int i;
- for (i = cu->len_wchar - 1; i >= 0; i--, info++) {
+ for (i = cu->len_char32 - 1; i >= 0; i--, info++) {
if (info->mat_nr && info->mat_nr >= index) {
info->mat_nr--;
}
@@ -5449,7 +5263,7 @@ bool BKE_curve_material_index_used(Curve *cu, int index)
if (curvetype == OB_FONT) {
struct CharInfo *info = cu->strinfo;
int i;
- for (i = cu->len_wchar - 1; i >= 0; i--, info++) {
+ for (i = cu->len_char32 - 1; i >= 0; i--, info++) {
if (info->mat_nr == index) {
return true;
}
@@ -5475,7 +5289,7 @@ void BKE_curve_material_index_clear(Curve *cu)
if (curvetype == OB_FONT) {
struct CharInfo *info = cu->strinfo;
int i;
- for (i = cu->len_wchar - 1; i >= 0; i--, info++) {
+ for (i = cu->len_char32 - 1; i >= 0; i--, info++) {
info->mat_nr = 0;
}
}
@@ -5497,7 +5311,7 @@ bool BKE_curve_material_index_validate(Curve *cu)
CharInfo *info = cu->strinfo;
const int max_idx = max_ii(0, cu->totcol); /* OB_FONT use 1 as first mat index, not 0!!! */
int i;
- for (i = cu->len_wchar - 1; i >= 0; i--, info++) {
+ for (i = cu->len_char32 - 1; i >= 0; i--, info++) {
if (info->mat_nr > max_idx) {
info->mat_nr = 0;
is_valid = false;
@@ -5519,9 +5333,7 @@ bool BKE_curve_material_index_validate(Curve *cu)
DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY);
return true;
}
- else {
- return false;
- }
+ return false;
}
void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int remap_len)
@@ -5547,7 +5359,7 @@ void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int
}
else {
strinfo = cu->strinfo;
- charinfo_len = cu->len_wchar;
+ charinfo_len = cu->len_char32;
}
for (i = 0; i <= charinfo_len; i++) {
diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c
new file mode 100644
index 00000000000..7f23f0215cc
--- /dev/null
+++ b/source/blender/blenkernel/intern/curve_bevel.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 bke
+ *
+ * Handle curve object data bevel options,
+ * both extruding
+ */
+
+#include <string.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math_base.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_displist.h"
+
+typedef enum CurveBevelFillType {
+ BACK = 0,
+ FRONT,
+ HALF,
+ FULL,
+} CurveBevelFillType;
+
+static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve)
+{
+ if (!(curve->flag & (CU_FRONT | CU_BACK))) {
+ return FULL;
+ }
+ if ((curve->flag & CU_FRONT) && (curve->flag & CU_BACK)) {
+ return HALF;
+ }
+
+ return (curve->flag & CU_FRONT) ? FRONT : BACK;
+}
+
+static void curve_bevel_make_extrude_and_fill(Curve *cu,
+ ListBase *disp,
+ const bool use_extrude,
+ const CurveBevelFillType fill_type)
+{
+ DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+
+ int nr;
+ if (fill_type == FULL) {
+ /* The full loop. */
+ nr = 4 * cu->bevresol + 6;
+ dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
+ }
+ else if (fill_type == HALF) {
+ /* Half the loop. */
+ nr = 2 * (cu->bevresol + 1) + (use_extrude ? 2 : 1);
+ dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
+ }
+ else {
+ /* One quarter of the loop (just front or back). */
+ nr = use_extrude ? cu->bevresol + 3 : cu->bevresol + 2;
+ dl->flag = (fill_type == FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE;
+ }
+
+ dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), __func__);
+ BLI_addtail(disp, dl);
+ /* Use a different type depending on whether the loop is complete or not. */
+ dl->type = (fill_type == FULL) ? DL_POLY : DL_SEGM;
+ dl->parts = 1;
+ dl->nr = nr;
+
+ float *fp = dl->verts;
+ const float dangle = (float)M_PI_2 / (cu->bevresol + 1);
+ float angle = 0.0f;
+
+ /* Build the back section. */
+ if (ELEM(fill_type, BACK, HALF, FULL)) {
+ angle = (float)M_PI_2 * 3.0f;
+ for (int i = 0; i < cu->bevresol + 2; i++) {
+ fp[0] = 0.0f;
+ fp[1] = (float)(cosf(angle) * (cu->ext2));
+ fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1;
+ angle += dangle;
+ fp += 3;
+ }
+ if (use_extrude && fill_type == BACK) {
+ /* Add the extrusion if we're only building the back. */
+ fp[0] = 0.0f;
+ fp[1] = cu->ext2;
+ fp[2] = cu->ext1;
+ }
+ }
+
+ /* Build the front section. */
+ if (ELEM(fill_type, FRONT, HALF, FULL)) {
+ if (use_extrude && fill_type == FRONT) {
+ /* Add the extrusion if we're only building the front. */
+ fp[0] = 0.0f;
+ fp[1] = cu->ext2;
+ fp[2] = -cu->ext1;
+ fp += 3;
+ }
+ /* Don't duplicate the last back vertex. */
+ angle = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? dangle : 0;
+ int front_len = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? cu->bevresol + 1 :
+ cu->bevresol + 2;
+ for (int i = 0; i < front_len; i++) {
+ fp[0] = 0.0f;
+ fp[1] = (float)(cosf(angle) * (cu->ext2));
+ fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1;
+ angle += dangle;
+ fp += 3;
+ }
+ }
+
+ /* Build the other half only if we're building the full loop. */
+ if (fill_type == FULL) {
+ for (int i = 0; i < cu->bevresol + 1; i++) {
+ fp[0] = 0.0f;
+ fp[1] = (float)(cosf(angle) * (cu->ext2));
+ fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1;
+ angle += dangle;
+ fp += 3;
+ }
+
+ angle = (float)M_PI;
+ for (int i = 0; i < cu->bevresol + 1; i++) {
+ fp[0] = 0.0f;
+ fp[1] = (float)(cosf(angle) * (cu->ext2));
+ fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1;
+ angle += dangle;
+ fp += 3;
+ }
+ }
+}
+
+static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp)
+{
+ const int nr = 4 + 2 * cu->bevresol;
+
+ DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), __func__);
+ BLI_addtail(disp, dl);
+ dl->type = DL_POLY;
+ dl->parts = 1;
+ dl->flag = DL_BACK_CURVE;
+ dl->nr = nr;
+
+ float *fp = dl->verts;
+ const float dangle = (2.0f * (float)M_PI / (nr));
+ float angle = -(nr - 1) * dangle;
+
+ for (int i = 0; i < nr; i++) {
+ fp[0] = 0.0;
+ fp[1] = (cosf(angle) * (cu->ext2));
+ fp[2] = (sinf(angle) * (cu->ext2)) - cu->ext1;
+ angle += dangle;
+ fp += 3;
+ }
+}
+
+static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp)
+{
+ DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), __func__);
+ BLI_addtail(disp, dl);
+ dl->type = DL_SEGM;
+ dl->parts = 1;
+ dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
+ dl->nr = 2;
+
+ float *fp = dl->verts;
+ fp[0] = fp[1] = 0.0;
+ fp[2] = -cu->ext1;
+ fp[3] = fp[4] = 0.0;
+ fp[5] = cu->ext1;
+}
+
+static void curve_bevel_make_from_object(Curve *cu, ListBase *disp)
+{
+ if (cu->bevobj->type != OB_CURVE) {
+ return;
+ }
+
+ Curve *bevcu = cu->bevobj->data;
+ if (bevcu->ext1 == 0.0f && bevcu->ext2 == 0.0f) {
+ ListBase bevdisp = {NULL, NULL};
+ float facx = cu->bevobj->scale[0];
+ float facy = cu->bevobj->scale[1];
+
+ DispList *dl;
+ if (cu->bevobj->runtime.curve_cache) {
+ dl = cu->bevobj->runtime.curve_cache->disp.first;
+ }
+ else {
+ BLI_assert(cu->bevobj->runtime.curve_cache != NULL);
+ dl = NULL;
+ }
+
+ while (dl) {
+ if (ELEM(dl->type, DL_POLY, DL_SEGM)) {
+ DispList *dlnew = MEM_mallocN(sizeof(DispList), __func__);
+ *dlnew = *dl;
+ dlnew->verts = MEM_malloc_arrayN(dl->parts * dl->nr, 3 * sizeof(float), __func__);
+ memcpy(dlnew->verts, dl->verts, 3 * sizeof(float) * dl->parts * dl->nr);
+
+ if (dlnew->type == DL_SEGM) {
+ dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE);
+ }
+
+ BLI_addtail(disp, dlnew);
+ float *fp = dlnew->verts;
+ int nr = dlnew->parts * dlnew->nr;
+ while (nr--) {
+ fp[2] = fp[1] * facy;
+ fp[1] = -fp[0] * facx;
+ fp[0] = 0.0;
+ fp += 3;
+ }
+ }
+ dl = dl->next;
+ }
+
+ BKE_displist_free(&bevdisp);
+ }
+}
+
+void BKE_curve_bevel_make(Object *ob, ListBase *disp)
+{
+ Curve *curve = ob->data;
+
+ const bool use_extrude = curve->ext1 != 0.0f;
+ const bool use_bevel = curve->ext2 != 0.0f;
+
+ BLI_listbase_clear(disp);
+
+ if (curve->bevobj) {
+ curve_bevel_make_from_object(curve, disp);
+ }
+ else if (!(use_extrude || use_bevel)) {
+ /* Pass. */
+ }
+ else if (use_extrude && !use_bevel) {
+ curve_bevel_make_only_extrude(curve, disp);
+ }
+ else {
+ CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve);
+
+ if (!use_extrude && fill_type == FULL) {
+ curve_bevel_make_full_circle(curve, disp);
+ }
+ else {
+ /* The general case for nonzero extrusion or an incomplete loop. */
+ curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type);
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c
new file mode 100644
index 00000000000..d4f197521a1
--- /dev/null
+++ b/source/blender/blenkernel/intern/curve_deform.c
@@ -0,0 +1,504 @@
+/*
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * Deform coordinates by a curve object (used by modifier).
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_anim_path.h"
+#include "BKE_curve.h"
+#include "BKE_editmesh.h"
+#include "BKE_lattice.h"
+#include "BKE_modifier.h"
+
+#include "BKE_deform.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Curve Deform Internal Utilities
+ * \{ */
+
+/**
+ * Calculations is in local space of deformed object
+ * so we store matrices to transform points to/from local-space.
+ */
+typedef struct {
+ float dmin[3], dmax[3];
+ float curvespace[4][4], objectspace[4][4], objectspace3[3][3];
+ int no_rot_axis;
+} CurveDeform;
+
+static void init_curve_deform(const Object *ob_curve, const Object *ob_target, CurveDeform *cd)
+{
+ float imat[4][4];
+ invert_m4_m4(imat, ob_target->obmat);
+ mul_m4_m4m4(cd->objectspace, imat, ob_curve->obmat);
+ invert_m4_m4(cd->curvespace, cd->objectspace);
+ copy_m3_m4(cd->objectspace3, cd->objectspace);
+ cd->no_rot_axis = 0;
+}
+
+/**
+ * This makes sure we can extend for non-cyclic.
+ *
+ * \return Success.
+ */
+static bool where_on_path_deform(const Object *ob_curve,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius)
+{
+ BevList *bl;
+ float ctime1;
+ int cycl = 0;
+
+ /* test for cyclic */
+ bl = ob_curve->runtime.curve_cache->bev.first;
+ if (!bl->nr) {
+ return false;
+ }
+ if (bl->poly > -1) {
+ cycl = 1;
+ }
+
+ if (cycl == 0) {
+ ctime1 = CLAMPIS(ctime, 0.0f, 1.0f);
+ }
+ else {
+ ctime1 = ctime;
+ }
+
+ /* vec needs 4 items */
+ if (where_on_path(ob_curve, ctime1, r_vec, r_dir, r_quat, r_radius, NULL)) {
+
+ if (cycl == 0) {
+ Path *path = ob_curve->runtime.curve_cache->path;
+ float dvec[3];
+
+ if (ctime < 0.0f) {
+ sub_v3_v3v3(dvec, path->data[1].vec, path->data[0].vec);
+ mul_v3_fl(dvec, ctime * (float)path->len);
+ add_v3_v3(r_vec, dvec);
+ if (r_quat) {
+ copy_qt_qt(r_quat, path->data[0].quat);
+ }
+ if (r_radius) {
+ *r_radius = path->data[0].radius;
+ }
+ }
+ else if (ctime > 1.0f) {
+ sub_v3_v3v3(dvec, path->data[path->len - 1].vec, path->data[path->len - 2].vec);
+ mul_v3_fl(dvec, (ctime - 1.0f) * (float)path->len);
+ add_v3_v3(r_vec, dvec);
+ if (r_quat) {
+ copy_qt_qt(r_quat, path->data[path->len - 1].quat);
+ }
+ if (r_radius) {
+ *r_radius = path->data[path->len - 1].radius;
+ }
+ /* weight - not used but could be added */
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * For each point, rotate & translate to curve use path, since it has constant distances.
+ *
+ * \param co: local coord, result local too.
+ * \param r_quat: returns quaternion for rotation,
+ * using #CurveDeform.no_rot_axis axis is using another define.
+ */
+static bool calc_curve_deform(
+ const Object *ob_curve, float co[3], const short axis, const CurveDeform *cd, float r_quat[4])
+{
+ Curve *cu = ob_curve->data;
+ float fac, loc[4], dir[3], new_quat[4], radius;
+ short index;
+ const bool is_neg_axis = (axis > 2);
+
+ if (ob_curve->runtime.curve_cache == NULL) {
+ /* Happens with a cyclic dependencies. */
+ return false;
+ }
+
+ if (ob_curve->runtime.curve_cache->path == NULL) {
+ return false; /* happens on append, cyclic dependencies and empty curves */
+ }
+
+ /* options */
+ if (is_neg_axis) {
+ index = axis - 3;
+ if (cu->flag & CU_STRETCH) {
+ fac = -(co[index] - cd->dmax[index]) / (cd->dmax[index] - cd->dmin[index]);
+ }
+ else {
+ fac = -(co[index] - cd->dmax[index]) / (ob_curve->runtime.curve_cache->path->totdist);
+ }
+ }
+ else {
+ index = axis;
+ if (cu->flag & CU_STRETCH) {
+ fac = (co[index] - cd->dmin[index]) / (cd->dmax[index] - cd->dmin[index]);
+ }
+ else {
+ if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
+ fac = +(co[index] - cd->dmin[index]) / (ob_curve->runtime.curve_cache->path->totdist);
+ }
+ else {
+ fac = 0.0f;
+ }
+ }
+ }
+
+ if (where_on_path_deform(ob_curve, fac, loc, dir, new_quat, &radius)) { /* returns OK */
+ float quat[4], cent[3];
+
+ if (cd->no_rot_axis) { /* set by caller */
+
+ /* This is not exactly the same as 2.4x, since the axis is having rotation removed rather
+ * than changing the axis before calculating the tilt but serves much the same purpose. */
+ float dir_flat[3] = {0, 0, 0}, q[4];
+ copy_v3_v3(dir_flat, dir);
+ dir_flat[cd->no_rot_axis - 1] = 0.0f;
+
+ normalize_v3(dir);
+ normalize_v3(dir_flat);
+
+ rotation_between_vecs_to_quat(q, dir, dir_flat); /* Could this be done faster? */
+
+ mul_qt_qtqt(new_quat, q, new_quat);
+ }
+
+ /* Logic for 'cent' orientation *
+ *
+ * The way 'co' is copied to 'cent' may seem to have no meaning, but it does.
+ *
+ * Use a curve modifier to stretch a cube out, color each side RGB,
+ * positive side light, negative dark.
+ * view with X up (default), from the angle that you can see 3 faces RGB colors (light),
+ * anti-clockwise
+ * Notice X,Y,Z Up all have light colors and each ordered CCW.
+ *
+ * Now for Neg Up XYZ, the colors are all dark, and ordered clockwise - Campbell
+ *
+ * note: moved functions into quat_apply_track/vec_apply_track
+ * */
+ copy_qt_qt(quat, new_quat);
+ copy_v3_v3(cent, co);
+
+ /* zero the axis which is not used,
+ * the big block of text above now applies to these 3 lines */
+ quat_apply_track(
+ quat,
+ axis,
+ (axis == 0 || axis == 2) ? 1 : 0); /* up flag is a dummy, set so no rotation is done */
+ vec_apply_track(cent, axis);
+ cent[index] = 0.0f;
+
+ /* scale if enabled */
+ if (cu->flag & CU_PATH_RADIUS) {
+ mul_v3_fl(cent, radius);
+ }
+
+ /* local rotation */
+ normalize_qt(quat);
+ mul_qt_v3(quat, cent);
+
+ /* translation */
+ add_v3_v3v3(co, cent, loc);
+
+ if (r_quat) {
+ copy_qt_qt(r_quat, quat);
+ }
+
+ return true;
+ }
+ return false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Curve Deform #BKE_curve_deform_coords API
+ *
+ * #BKE_curve_deform and related functions.
+ * \{ */
+
+static void curve_deform_coords_impl(const Object *ob_curve,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const MDeformVert *dvert,
+ const int defgrp_index,
+ const short flag,
+ const short defaxis,
+ BMEditMesh *em_target)
+{
+ Curve *cu;
+ int a;
+ CurveDeform cd;
+ const bool is_neg_axis = (defaxis > 2);
+ const bool invert_vgroup = (flag & MOD_CURVE_INVERT_VGROUP) != 0;
+ bool use_dverts = false;
+ int cd_dvert_offset;
+
+ if (ob_curve->type != OB_CURVE) {
+ return;
+ }
+
+ cu = ob_curve->data;
+
+ init_curve_deform(ob_curve, ob_target, &cd);
+
+ if (cu->flag & CU_DEFORM_BOUNDS_OFF) {
+ /* Dummy bounds. */
+ if (is_neg_axis == false) {
+ cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = 0.0f;
+ cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 1.0f;
+ }
+ else {
+ /* Negative, these bounds give a good rest position. */
+ cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = -1.0f;
+ cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 0.0f;
+ }
+ }
+ else {
+ /* Set mesh min/max bounds. */
+ INIT_MINMAX(cd.dmin, cd.dmax);
+ }
+
+ if (em_target != NULL) {
+ cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT);
+ if (cd_dvert_offset != -1) {
+ use_dverts = true;
+ }
+ }
+ else {
+ if (dvert != NULL) {
+ use_dverts = true;
+ }
+ }
+
+ if (use_dverts) {
+ if (cu->flag & CU_DEFORM_BOUNDS_OFF) {
+
+#define DEFORM_OP(dvert) \
+ { \
+ const float weight = invert_vgroup ? 1.0f - BKE_defvert_find_weight(dvert, defgrp_index) : \
+ BKE_defvert_find_weight(dvert, defgrp_index); \
+ if (weight > 0.0f) { \
+ float vec[3]; \
+ mul_m4_v3(cd.curvespace, vert_coords[a]); \
+ copy_v3_v3(vec, vert_coords[a]); \
+ calc_curve_deform(ob_curve, vec, defaxis, &cd, NULL); \
+ interp_v3_v3v3(vert_coords[a], vert_coords[a], vec, weight); \
+ mul_m4_v3(cd.objectspace, vert_coords[a]); \
+ } \
+ } \
+ ((void)0)
+
+ if (em_target != NULL) {
+ BMIter iter;
+ BMVert *v;
+ BM_ITER_MESH_INDEX (v, &iter, em_target->bm, BM_VERTS_OF_MESH, a) {
+ dvert = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset);
+ DEFORM_OP(dvert);
+ }
+ }
+ else {
+ for (a = 0; a < vert_coords_len; a++) {
+ DEFORM_OP(&dvert[a]);
+ }
+ }
+
+#undef DEFORM_OP
+ }
+ else {
+
+#define DEFORM_OP_MINMAX(dvert) \
+ { \
+ const float weight = invert_vgroup ? 1.0f - BKE_defvert_find_weight(dvert, defgrp_index) : \
+ BKE_defvert_find_weight(dvert, defgrp_index); \
+ if (weight > 0.0f) { \
+ mul_m4_v3(cd.curvespace, vert_coords[a]); \
+ minmax_v3v3_v3(cd.dmin, cd.dmax, vert_coords[a]); \
+ } \
+ } \
+ ((void)0)
+
+ /* already in 'cd.curvespace', prev for loop */
+#define DEFORM_OP_CLAMPED(dvert) \
+ { \
+ const float weight = invert_vgroup ? 1.0f - BKE_defvert_find_weight(dvert, defgrp_index) : \
+ BKE_defvert_find_weight(dvert, defgrp_index); \
+ if (weight > 0.0f) { \
+ float vec[3]; \
+ copy_v3_v3(vec, vert_coords[a]); \
+ calc_curve_deform(ob_curve, vec, defaxis, &cd, NULL); \
+ interp_v3_v3v3(vert_coords[a], vert_coords[a], vec, weight); \
+ mul_m4_v3(cd.objectspace, vert_coords[a]); \
+ } \
+ } \
+ ((void)0)
+
+ if (em_target != NULL) {
+ BMIter iter;
+ BMVert *v;
+ BM_ITER_MESH_INDEX (v, &iter, em_target->bm, BM_VERTS_OF_MESH, a) {
+ dvert = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset);
+ DEFORM_OP_MINMAX(dvert);
+ }
+
+ BM_ITER_MESH_INDEX (v, &iter, em_target->bm, BM_VERTS_OF_MESH, a) {
+ dvert = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset);
+ DEFORM_OP_CLAMPED(dvert);
+ }
+ }
+ else {
+
+ for (a = 0; a < vert_coords_len; a++) {
+ DEFORM_OP_MINMAX(&dvert[a]);
+ }
+
+ for (a = 0; a < vert_coords_len; a++) {
+ DEFORM_OP_CLAMPED(&dvert[a]);
+ }
+ }
+ }
+
+#undef DEFORM_OP_MINMAX
+#undef DEFORM_OP_CLAMPED
+ }
+ else {
+ if (cu->flag & CU_DEFORM_BOUNDS_OFF) {
+ for (a = 0; a < vert_coords_len; a++) {
+ mul_m4_v3(cd.curvespace, vert_coords[a]);
+ calc_curve_deform(ob_curve, vert_coords[a], defaxis, &cd, NULL);
+ mul_m4_v3(cd.objectspace, vert_coords[a]);
+ }
+ }
+ else {
+ for (a = 0; a < vert_coords_len; a++) {
+ mul_m4_v3(cd.curvespace, vert_coords[a]);
+ minmax_v3v3_v3(cd.dmin, cd.dmax, vert_coords[a]);
+ }
+
+ for (a = 0; a < vert_coords_len; a++) {
+ /* already in 'cd.curvespace', prev for loop */
+ calc_curve_deform(ob_curve, vert_coords[a], defaxis, &cd, NULL);
+ mul_m4_v3(cd.objectspace, vert_coords[a]);
+ }
+ }
+ }
+}
+
+void BKE_curve_deform_coords(const Object *ob_curve,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const MDeformVert *dvert,
+ const int defgrp_index,
+ const short flag,
+ const short defaxis)
+{
+ curve_deform_coords_impl(
+ ob_curve, ob_target, vert_coords, vert_coords_len, dvert, defgrp_index, flag, defaxis, NULL);
+}
+
+void BKE_curve_deform_coords_with_editmesh(const Object *ob_curve,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const int defgrp_index,
+ const short flag,
+ const short defaxis,
+ BMEditMesh *em_target)
+{
+ curve_deform_coords_impl(ob_curve,
+ ob_target,
+ vert_coords,
+ vert_coords_len,
+ NULL,
+ defgrp_index,
+ flag,
+ defaxis,
+ em_target);
+}
+
+/**
+ * \param orco: Input vec and orco = local coord in curve space
+ * orco is original not-animated or deformed reference point.
+ *
+ * The result written in vec and r_mat.
+ */
+void BKE_curve_deform_co(const Object *ob_curve,
+ const Object *ob_target,
+ const float orco[3],
+ float vec[3],
+ const int no_rot_axis,
+ float r_mat[3][3])
+{
+ CurveDeform cd;
+ float quat[4];
+
+ if (ob_curve->type != OB_CURVE) {
+ unit_m3(r_mat);
+ return;
+ }
+
+ init_curve_deform(ob_curve, ob_target, &cd);
+ cd.no_rot_axis = no_rot_axis; /* option to only rotate for XY, for example */
+
+ copy_v3_v3(cd.dmin, orco);
+ copy_v3_v3(cd.dmax, orco);
+
+ mul_m4_v3(cd.curvespace, vec);
+
+ if (calc_curve_deform(ob_curve, vec, ob_target->trackflag, &cd, quat)) {
+ float qmat[3][3];
+
+ quat_to_mat3(qmat, quat);
+ mul_m3_m3m3(r_mat, qmat, cd.objectspace3);
+ }
+ else {
+ unit_m3(r_mat);
+ }
+
+ mul_m4_v3(cd.objectspace, vec);
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index a748dfbab3e..8577990dc03 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);
@@ -766,7 +886,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
*/
static void curveprofile_make_table(CurveProfile *profile)
{
- int n_samples = PROF_N_TABLE(profile->path_len);
+ int n_samples = PROF_TABLE_LEN(profile->path_len);
CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1),
"high-res table");
@@ -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);
}
/**
@@ -939,7 +1059,7 @@ void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len)
*/
static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i)
{
- BLI_assert(i < PROF_N_TABLE(profile->path_len));
+ BLI_assert(i < PROF_TABLE_LEN(profile->path_len));
return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
}
@@ -952,7 +1072,7 @@ static float curveprofile_distance_to_next_table_point(const CurveProfile *profi
float BKE_curveprofile_total_length(const CurveProfile *profile)
{
float total_length = 0;
- for (int i = 0; i < PROF_N_TABLE(profile->path_len) - 1; i++) {
+ for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) {
total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
}
return total_length;
@@ -1040,7 +1160,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile,
float length_travelled = 0.0f;
while (length_travelled < requested_length) {
/* Check if we reached the last point before the final one. */
- if (i == PROF_N_TABLE(profile->path_len) - 2) {
+ if (i == PROF_TABLE_LEN(profile->path_len) - 2) {
break;
}
float new_length = curveprofile_distance_to_next_table_point(profile, i);
@@ -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..7bf11d86a63 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -300,7 +300,7 @@ static void layerInterp_mdeformvert(const void **sources,
/* now we know how many unique deform weights there are, so realloc */
if (dvert->dw && (dvert->totweight == totweight)) {
- /* pass (fastpath if we don't need to realloc) */
+ /* pass (fast-path if we don't need to realloc). */
}
else {
if (dvert->dw) {
@@ -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)
@@ -1465,6 +1464,102 @@ static int layerMaxNum_propcol(void)
return MAX_MCOL;
}
+static void layerInterp_propfloat3(
+ const void **sources, const float *weights, const float *sub_weights, int count, void *dest)
+{
+ vec3f result = {0.0f, 0.0f, 0.0f};
+ for (int i = 0; i < count; i++) {
+ float weight = weights ? weights[i] : 1.0f;
+ const vec3f *src = sources[i];
+ if (sub_weights) {
+ madd_v3_v3fl(&result.x, &src->x, sub_weights[i] * weight);
+ }
+ else {
+ madd_v3_v3fl(&result.x, &src->x, weight);
+ }
+ }
+ copy_v3_v3((float *)dest, &result.x);
+}
+
+static void layerMultiply_propfloat3(void *data, float fac)
+{
+ vec3f *vec = data;
+ vec->x *= fac;
+ vec->y *= fac;
+ vec->z *= fac;
+}
+
+static void layerAdd_propfloat3(void *data1, const void *data2)
+{
+ vec3f *vec1 = data1;
+ const vec3f *vec2 = data2;
+ vec1->x += vec2->x;
+ vec1->y += vec2->y;
+ vec1->z += vec2->z;
+}
+
+static bool layerValidate_propfloat3(void *data, const uint totitems, const bool do_fixes)
+{
+ float *values = data;
+ bool has_errors = false;
+ for (int i = 0; i < totitems * 3; i++) {
+ if (!isfinite(values[i])) {
+ if (do_fixes) {
+ values[i] = 0.0f;
+ }
+ has_errors = true;
+ }
+ }
+ return has_errors;
+}
+
+static void layerInterp_propfloat2(
+ const void **sources, const float *weights, const float *sub_weights, int count, void *dest)
+{
+ vec2f result = {0.0f, 0.0f};
+ for (int i = 0; i < count; i++) {
+ float weight = weights ? weights[i] : 1.0f;
+ const vec2f *src = sources[i];
+ if (sub_weights) {
+ madd_v2_v2fl(&result.x, &src->x, sub_weights[i] * weight);
+ }
+ else {
+ madd_v2_v2fl(&result.x, &src->x, weight);
+ }
+ }
+ copy_v2_v2((float *)dest, &result.x);
+}
+
+static void layerMultiply_propfloat2(void *data, float fac)
+{
+ vec2f *vec = data;
+ vec->x *= fac;
+ vec->y *= fac;
+}
+
+static void layerAdd_propfloat2(void *data1, const void *data2)
+{
+ vec2f *vec1 = data1;
+ const vec2f *vec2 = data2;
+ vec1->x += vec2->x;
+ vec1->y += vec2->y;
+}
+
+static bool layerValidate_propfloat2(void *data, const uint totitems, const bool do_fixes)
+{
+ float *values = data;
+ bool has_errors = false;
+ for (int i = 0; i < totitems * 2; i++) {
+ if (!isfinite(values[i])) {
+ if (do_fixes) {
+ values[i] = 0.0f;
+ }
+ has_errors = true;
+ }
+ }
+ return has_errors;
+}
+
static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0: CD_MVERT */
{sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL},
@@ -1784,7 +1879,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(MPropCol),
"MPropCol",
1,
- N_("Col"),
+ N_("Color"),
NULL,
NULL,
layerInterp_propcol,
@@ -1800,7 +1895,38 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
NULL,
NULL,
NULL,
- layerMaxNum_propcol}};
+ layerMaxNum_propcol},
+ /* 48: CD_PROP_FLOAT3 */
+ {sizeof(float[3]),
+ "vec3f",
+ 1,
+ N_("Float3"),
+ NULL,
+ NULL,
+ layerInterp_propfloat3,
+ NULL,
+ NULL,
+ layerValidate_propfloat3,
+ NULL,
+ layerMultiply_propfloat3,
+ NULL,
+ layerAdd_propfloat3},
+ /* 49: CD_PROP_FLOAT2 */
+ {sizeof(float[2]),
+ "vec2f",
+ 1,
+ N_("Float2"),
+ NULL,
+ NULL,
+ layerInterp_propfloat2,
+ NULL,
+ NULL,
+ layerValidate_propfloat2,
+ NULL,
+ layerMultiply_propfloat2,
+ NULL,
+ layerAdd_propfloat2},
+};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 0-4 */ "CDMVert",
@@ -1853,6 +1979,8 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDHairMapping",
"CDPoint",
"CDPropCol",
+ "CDPropFloat3",
+ "CDPropFloat2",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
@@ -1871,7 +1999,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 +2009,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 +2018,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 +2029,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 +2054,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 |
@@ -2514,11 +2643,13 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
}
if (alloctype == CD_DUPLICATE && layerdata) {
- if (typeInfo->copy) {
- typeInfo->copy(layerdata, newlayerdata, totelem);
- }
- else {
- memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size);
+ if (totelem > 0) {
+ if (typeInfo->copy) {
+ typeInfo->copy(layerdata, newlayerdata, totelem);
+ }
+ else {
+ memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size);
+ }
}
}
else if (alloctype == CD_DEFAULT) {
diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c
index 48e0ee50d43..9bdd80fd668 100644
--- a/source/blender/blenkernel/intern/data_transfer.c
+++ b/source/blender/blenkernel/intern/data_transfer.c
@@ -562,7 +562,7 @@ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map
CustomData *cd_dst,
const bool use_dupref_dst,
const int tolayers,
- bool *use_layers_src,
+ const bool *use_layers_src,
const int num_layers_src,
cd_datatransfer_interp interp,
void *interp_data)
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index 9a3965a86f6..98fc5f9a23a 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -249,7 +249,7 @@ void BKE_defvert_sync_mapped(MDeformVert *dvert_dst,
/**
* be sure all flip_map values are valid
*/
-void BKE_defvert_remap(MDeformVert *dvert, int *map, const int map_len)
+void BKE_defvert_remap(MDeformVert *dvert, const int *map, const int map_len)
{
MDeformWeight *dw = dvert->dw;
unsigned int i;
@@ -486,14 +486,14 @@ void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int
}
}
-bDeformGroup *BKE_object_defgroup_find_name(Object *ob, const char *name)
+bDeformGroup *BKE_object_defgroup_find_name(const Object *ob, const char *name)
{
return (name && name[0] != '\0') ?
BLI_findstring(&ob->defbase, name, offsetof(bDeformGroup, name)) :
NULL;
}
-int BKE_object_defgroup_name_index(Object *ob, const char *name)
+int BKE_object_defgroup_name_index(const Object *ob, const char *name)
{
return (name && name[0] != '\0') ?
BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)) :
@@ -503,7 +503,7 @@ int BKE_object_defgroup_name_index(Object *ob, const char *name)
/**
* \note caller must free.
*/
-int *BKE_object_defgroup_flip_map(Object *ob, int *flip_map_len, const bool use_default)
+int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const bool use_default)
{
int defbase_tot = *flip_map_len = BLI_listbase_count(&ob->defbase);
@@ -545,7 +545,7 @@ int *BKE_object_defgroup_flip_map(Object *ob, int *flip_map_len, const bool use_
/**
* \note caller must free.
*/
-int *BKE_object_defgroup_flip_map_single(Object *ob,
+int *BKE_object_defgroup_flip_map_single(const Object *ob,
int *flip_map_len,
const bool use_default,
int defgroup)
@@ -580,7 +580,7 @@ int *BKE_object_defgroup_flip_map_single(Object *ob,
}
}
-int BKE_object_defgroup_flip_index(Object *ob, int index, const bool use_default)
+int BKE_object_defgroup_flip_index(const Object *ob, int index, const bool use_default)
{
bDeformGroup *dg = BLI_findlink(&ob->defbase, index);
int flip_index = -1;
@@ -1184,7 +1184,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map,
CustomData *cd_dst,
const bool UNUSED(use_dupref_dst),
const int tolayers,
- bool *use_layers_src,
+ const bool *use_layers_src,
const int num_layers_src)
{
int idx_src;
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index dae8a59fe43..b9279ace39f 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -588,7 +588,7 @@ static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist)
}
/* check whether bounds intersects a point with given radius */
-static bool boundIntersectPoint(Bounds3D *b, float point[3], const float radius)
+static bool boundIntersectPoint(Bounds3D *b, const float point[3], const float radius)
{
if (!b->valid) {
return false;
@@ -4780,13 +4780,16 @@ static void dynamic_paint_paint_single_point_cb_ex(void *__restrict userdata,
}
}
-static int dynamicPaint_paintSinglePoint(Depsgraph *depsgraph,
- DynamicPaintSurface *surface,
- float *pointCoord,
- DynamicPaintBrushSettings *brush,
- Object *brushOb,
- Scene *scene,
- float timescale)
+static int dynamicPaint_paintSinglePoint(
+ Depsgraph *depsgraph,
+ DynamicPaintSurface *surface,
+ /* Cannot be const, because it is assigned to non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ float *pointCoord,
+ DynamicPaintBrushSettings *brush,
+ Object *brushOb,
+ Scene *scene,
+ float timescale)
{
PaintSurfaceData *sData = surface->data;
float brush_radius = brush->paint_distance * surface->radius_scale;
@@ -5112,7 +5115,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 */
@@ -5456,11 +5459,14 @@ static void dynamic_paint_effect_drip_cb(void *__restrict userdata,
}
}
-static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface,
- float *force,
- PaintPoint *prevPoint,
- float timescale,
- float steps)
+static void dynamicPaint_doEffectStep(
+ DynamicPaintSurface *surface,
+ /* Cannot be const, because it is assigned to non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ float *force,
+ PaintPoint *prevPoint,
+ float timescale,
+ float steps)
{
PaintSurfaceData *sData = surface->data;
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..a43553ee89f 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,17 +1087,18 @@ void BKE_effectors_apply(ListBase *effectors,
EffectorWeights *weights,
EffectedPoint *point,
float *force,
+ float *wind_force,
float *impulse)
{
/*
* Modifies the force on a particle according to its
* relation with the effector object
* Different kind of effectors include:
- * Forcefields: Gravity-like attractor
+ * Force-fields: Gravity-like attractor
* (force power is related to the inverse of distance to the power of a falloff value)
* Vortex fields: swirling effectors
* (particles rotate around Z-axis of the object. otherwise, same relation as)
- * (Forcefields, but this is not done through a force/acceleration)
+ * (Force-fields, but this is not done through a force/acceleration)
* Guide: particles on a path
* (particles are guided along a curve bezier or old nurbs)
* (is independent of other effectors)
@@ -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..acbbf50701a 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)
{
@@ -1277,7 +1277,7 @@ short test_time_fcurve(FCurve *fcu)
* than the horizontal distance between (v1-v4).
* This is to prevent curve loops.
*/
-void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2])
+void correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2])
{
float h1[2], h2[2], len1, len2, len, fac;
@@ -1872,17 +1872,18 @@ float evaluate_fcurve_only_curve(FCurve *fcu, float evaltime)
float evaluate_fcurve_driver(PathResolvedRNA *anim_rna,
FCurve *fcu,
ChannelDriver *driver_orig,
- float evaltime)
+ const AnimationEvalContext *anim_eval_context)
{
BLI_assert(fcu->driver != NULL);
float cvalue = 0.0f;
+ float evaltime = anim_eval_context->eval_time;
/* If there is a driver (only if this F-Curve is acting as 'driver'),
* evaluate it to find value to use as "evaltime" since drivers essentially act as alternative
* input (i.e. in place of 'time') for F-Curves. */
if (fcu->driver) {
/* evaltime now serves as input for the curve */
- evaltime = evaluate_driver(anim_rna, fcu->driver, driver_orig, evaltime);
+ evaltime = evaluate_driver(anim_rna, fcu->driver, driver_orig, anim_eval_context);
/* only do a default 1-1 mapping if it's unlikely that anything else will set a value... */
if (fcu->totvert == 0) {
@@ -1924,7 +1925,9 @@ bool BKE_fcurve_is_empty(FCurve *fcu)
}
/* Calculate the value of the given F-Curve at the given frame, and set its curval */
-float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime)
+float calculate_fcurve(PathResolvedRNA *anim_rna,
+ FCurve *fcu,
+ const AnimationEvalContext *anim_eval_context)
{
/* only calculate + set curval (overriding the existing value) if curve has
* any data which warrants this...
@@ -1936,10 +1939,10 @@ float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime)
/* calculate and set curval (evaluates driver too if necessary) */
float curval;
if (fcu->driver) {
- curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, evaltime);
+ curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, anim_eval_context);
}
else {
- curval = evaluate_fcurve(fcu, evaltime);
+ curval = evaluate_fcurve(fcu, anim_eval_context->eval_time);
}
fcu->curval = curval; /* debug display only, not thread safe! */
return curval;
diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c
index 78a6cf28824..87cb77930f5 100644
--- a/source/blender/blenkernel/intern/fcurve_driver.c
+++ b/source/blender/blenkernel/intern/fcurve_driver.c
@@ -21,12 +21,6 @@
* \ingroup bke
*/
-// #include <float.h>
-// #include <math.h>
-// #include <stddef.h>
-// #include <stdio.h>
-// #include <string.h>
-
#include "MEM_guardedalloc.h"
#include "DNA_anim_types.h"
@@ -43,6 +37,7 @@
#include "BLT_translation.h"
#include "BKE_action.h"
+#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_fcurve_driver.h"
@@ -65,17 +60,19 @@ static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER;
static CLG_LogRef LOG = {"bke.fcurve"};
-/* Driver Variables --------------------------- */
+/* -------------------------------------------------------------------- */
+/** \name Driver Variables
+ * \{ */
/* TypeInfo for Driver Variables (dvti) */
typedef struct DriverVarTypeInfo {
- /* evaluation callback */
+ /* Evaluation callback. */
float (*get_value)(ChannelDriver *driver, DriverVar *dvar);
- /* allocation of target slots */
- int num_targets; /* number of target slots required */
- const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */
- short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */
+ /* Allocation of target slots. */
+ int num_targets; /* Number of target slots required. */
+ const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots. */
+ short target_flags[MAX_DRIVER_TARGETS]; /* Flags defining the requirements for each slot. */
} DriverVarTypeInfo;
/* Macro to begin definitions */
@@ -84,7 +81,11 @@ typedef struct DriverVarTypeInfo {
/* Macro to end definitions */
#define END_DVAR_TYPEDEF }
-/* ......... */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Driver Target Utilities
+ * \{ */
static ID *dtar_id_ensure_proxy_from(ID *id)
{
@@ -106,14 +107,14 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
int index = -1;
float value = 0.0f;
- /* sanity check */
+ /* Sanity check. */
if (ELEM(NULL, driver, dtar)) {
return 0.0f;
}
id = dtar_id_ensure_proxy_from(dtar->id);
- /* error check for missing pointer... */
+ /* Error check for missing pointer. */
if (id == NULL) {
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
@@ -124,12 +125,12 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
- /* get RNA-pointer for the ID-block given in target */
+ /* Get RNA-pointer for the ID-block given in target. */
RNA_id_pointer_create(id, &id_ptr);
- /* get property to read from, and get value as appropriate */
+ /* Get property to read from, and get value as appropriate. */
if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
- /* path couldn't be resolved */
+ /* Path couldn't be resolved. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: cannot resolve target for %s -> %s",
@@ -143,9 +144,9 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
}
if (RNA_property_array_check(prop)) {
- /* array */
+ /* Array. */
if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) {
- /* out of bounds */
+ /* Out of bounds. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)",
@@ -174,7 +175,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
}
}
else {
- /* not an array */
+ /* Not an array. */
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
value = (float)RNA_property_boolean_get(&ptr, prop);
@@ -193,7 +194,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
}
}
- /* if we're still here, we should be ok... */
+ /* If we're still here, we should be ok. */
dtar->flag &= ~DTAR_FLAG_INVALID;
return value;
}
@@ -213,14 +214,14 @@ bool driver_get_variable_property(ChannelDriver *driver,
ID *id;
int index = -1;
- /* sanity check */
+ /* Sanity check. */
if (ELEM(NULL, driver, dtar)) {
return false;
}
id = dtar_id_ensure_proxy_from(dtar->id);
- /* error check for missing pointer... */
+ /* Error check for missing pointer. */
if (id == NULL) {
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
@@ -231,19 +232,19 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
- /* get RNA-pointer for the ID-block given in target */
+ /* Get RNA-pointer for the ID-block given in target. */
RNA_id_pointer_create(id, &id_ptr);
- /* get property to read from, and get value as appropriate */
+ /* Get property to read from, and get value as appropriate. */
if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') {
ptr = PointerRNA_NULL;
- prop = NULL; /* ok */
+ prop = NULL; /* OK. */
}
else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
- /* ok */
+ /* OK. */
}
else {
- /* path couldn't be resolved */
+ /* Path couldn't be resolved. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: cannot resolve target for %s -> %s",
@@ -264,7 +265,7 @@ bool driver_get_variable_property(ChannelDriver *driver,
*r_prop = prop;
*r_index = index;
- /* if we're still here, we should be ok... */
+ /* If we're still here, we should be ok. */
dtar->flag &= ~DTAR_FLAG_INVALID;
return true;
}
@@ -276,14 +277,14 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
- /* check if this target has valid data */
+ /* Check if this target has valid data. */
if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
- /* invalid target, so will not have enough targets */
+ /* Invalid target, so will not have enough targets. */
driver->flag |= DRIVER_FLAG_INVALID;
dtar->flag |= DTAR_FLAG_INVALID;
}
else {
- /* target seems to be OK now... */
+ /* Target seems to be OK now. */
dtar->flag &= ~DTAR_FLAG_INVALID;
valid_targets++;
}
@@ -293,21 +294,25 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
return valid_targets;
}
-/* ......... */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Driver Variable Utilities
+ * \{ */
-/* evaluate 'single prop' driver variable */
+/* Evaluate 'single prop' driver variable. */
static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
{
- /* just evaluate the first target slot */
+ /* Just evaluate the first target slot. */
return dtar_get_prop_val(driver, &dvar->targets[0]);
}
-/* evaluate 'rotation difference' driver variable */
+/* Evaluate 'rotation difference' driver variable. */
static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
{
short valid_targets = driver_check_valid_targets(driver, dvar);
- /* make sure we have enough valid targets to use - all or nothing for now... */
+ /* Make sure we have enough valid targets to use - all or nothing for now. */
if (driver_check_valid_targets(driver, dvar) != 2) {
if (G.debug & G_DEBUG) {
CLOG_WARN(&LOG,
@@ -323,31 +328,31 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
/* NOTE: for now, these are all just worldspace */
for (int i = 0; i < 2; i++) {
- /* get pointer to loc values to store in */
+ /* Get pointer to loc values to store in. */
DriverTarget *dtar = &dvar->targets[i];
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
bPoseChannel *pchan;
- /* after the checks above, the targets should be valid here... */
+ /* After the checks above, the targets should be valid here. */
BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
- /* 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 */
+ /* Check if object or bone. */
if (pchan) {
- /* bone */
+ /* Bone. */
mat[i] = pchan->pose_mat;
}
else {
- /* object */
+ /* Object. */
mat[i] = ob->obmat;
}
}
float q1[4], q2[4], quat[4], angle;
- /* use the final posed locations */
+ /* Use the final posed locations. */
mat4_to_quat(q1, mat[0]);
mat4_to_quat(q2, mat[1]);
@@ -359,15 +364,18 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle);
}
-/* evaluate 'location difference' driver variable */
-/* TODO: this needs to take into account space conversions... */
+/**
+ * Evaluate 'location difference' driver variable.
+ *
+ * TODO: this needs to take into account space conversions.
+ */
static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
{
float loc1[3] = {0.0f, 0.0f, 0.0f};
float loc2[3] = {0.0f, 0.0f, 0.0f};
short valid_targets = driver_check_valid_targets(driver, dvar);
- /* make sure we have enough valid targets to use - all or nothing for now... */
+ /* Make sure we have enough valid targets to use - all or nothing for now. */
if (valid_targets < dvar->num_targets) {
if (G.debug & G_DEBUG) {
CLOG_WARN(&LOG,
@@ -380,72 +388,72 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
}
/* SECOND PASS: get two location values */
- /* NOTE: for now, these are all just worldspace */
+ /* NOTE: for now, these are all just world-space */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
- /* get pointer to loc values to store in */
+ /* Get pointer to loc values to store in. */
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
bPoseChannel *pchan;
float tmp_loc[3];
- /* after the checks above, the targets should be valid here... */
+ /* After the checks above, the targets should be valid here. */
BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB));
- /* 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 */
+ /* Check if object or bone. */
if (pchan) {
- /* bone */
+ /* Bone. */
if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
float mat[4][4];
- /* extract transform just like how the constraints do it! */
+ /* Extract transform just like how the constraints do it! */
copy_m4_m4(mat, pchan->pose_mat);
BKE_constraint_mat_convertspace(
ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
- /* ... and from that, we get our transform */
+ /* ... and from that, we get our transform. */
copy_v3_v3(tmp_loc, mat[3]);
}
else {
- /* transform space (use transform values directly) */
+ /* Transform space (use transform values directly). */
copy_v3_v3(tmp_loc, pchan->loc);
}
}
else {
- /* convert to worldspace */
+ /* Convert to worldspace. */
copy_v3_v3(tmp_loc, pchan->pose_head);
mul_m4_v3(ob->obmat, tmp_loc);
}
}
else {
- /* object */
+ /* Object. */
if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- /* XXX: this should practically be the same as transform space... */
+ /* XXX: this should practically be the same as transform space. */
float mat[4][4];
- /* extract transform just like how the constraints do it! */
+ /* Extract transform just like how the constraints do it! */
copy_m4_m4(mat, ob->obmat);
BKE_constraint_mat_convertspace(
ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
- /* ... and from that, we get our transform */
+ /* ... and from that, we get our transform. */
copy_v3_v3(tmp_loc, mat[3]);
}
else {
- /* transform space (use transform values directly) */
+ /* Transform space (use transform values directly). */
copy_v3_v3(tmp_loc, ob->loc);
}
}
else {
- /* worldspace */
+ /* World-space. */
copy_v3_v3(tmp_loc, ob->obmat[3]);
}
}
- /* copy the location to the right place */
+ /* Copy the location to the right place. */
if (tarIndex) {
copy_v3_v3(loc2, tmp_loc);
}
@@ -455,13 +463,14 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
}
DRIVER_TARGETS_LOOPER_END;
- /* if we're still here, there should now be two targets to use,
- * so just take the length of the vector between these points
- */
+ /* If we're still here, there should now be two targets to use,
+ * so just take the length of the vector between these points. */
return len_v3v3(loc1, loc2);
}
-/* evaluate 'transform channel' driver variable */
+/**
+ * Evaluate 'transform channel' driver variable.
+ */
static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
@@ -472,29 +481,29 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
bool use_eulers = false;
short rot_order = ROT_MODE_EUL;
- /* check if this target has valid data */
+ /* Check if this target has valid data. */
if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
- /* invalid target, so will not have enough targets */
+ /* Invalid target, so will not have enough targets. */
driver->flag |= DRIVER_FLAG_INVALID;
dtar->flag |= DTAR_FLAG_INVALID;
return 0.0f;
}
else {
- /* target should be valid now */
+ /* Target should be valid now. */
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 */
+ /* Bone. */
if (pchan->rotmode > 0) {
copy_v3_v3(oldEul, pchan->eul);
rot_order = pchan->rotmode;
@@ -503,26 +512,25 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- /* just like how the constraints do it! */
+ /* Just like how the constraints do it! */
copy_m4_m4(mat, pchan->pose_mat);
BKE_constraint_mat_convertspace(
ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
}
else {
- /* specially calculate local matrix, since chan_mat is not valid
+ /* Specially calculate local matrix, since chan_mat is not valid
* since it stores delta transform of pose_mat so that deforms work
- * so it cannot be used here for "transform" space
- */
+ * so it cannot be used here for "transform" space. */
BKE_pchan_to_mat4(pchan, mat);
}
}
else {
- /* worldspace matrix */
+ /* World-space matrix. */
mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat);
}
}
else {
- /* object */
+ /* Object. */
if (ob->rotmode > 0) {
copy_v3_v3(oldEul, ob->rot);
rot_order = ob->rotmode;
@@ -531,25 +539,25 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
if (dtar->flag & DTAR_FLAG_LOCALSPACE) {
if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) {
- /* just like how the constraints do it! */
+ /* Just like how the constraints do it! */
copy_m4_m4(mat, ob->obmat);
BKE_constraint_mat_convertspace(
ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
}
else {
- /* transforms to matrix */
+ /* Transforms to matrix. */
BKE_object_to_mat4(ob, mat);
}
}
else {
- /* worldspace matrix - just the good-old one */
+ /* World-space matrix - just the good-old one. */
copy_m4_m4(mat, ob->obmat);
}
}
- /* check which transform */
+ /* Check which transform. */
if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) {
- /* not valid channel */
+ /* Not valid channel. */
return 0.0f;
}
else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) {
@@ -566,7 +574,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]);
}
else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) {
- /* extract rotation as eulers (if needed)
+ /* Extract rotation as eulers (if needed)
* - definitely if rotation order isn't eulers already
* - if eulers, then we have 2 options:
* a) decompose transform matrix as required, then try to make eulers from
@@ -595,7 +603,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
return quat[channel];
}
else {
- /* extract location and choose right axis */
+ /* Extract location and choose right axis. */
return mat[3][dtar->transChan];
}
}
@@ -665,41 +673,45 @@ void BKE_driver_target_matrix_to_rot_channels(
}
}
-/* ......... */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Driver Variable Type Info
+ * \{ */
/* Table of Driver Variable Type Info Data */
static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */
- 1, /* number of targets used */
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* Eval callback. */
+ 1, /* Number of targets used. */
{"Property"}, /* UI names for targets */
- {0} /* flags */
+ {0} /* Flags. */
END_DVAR_TYPEDEF,
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */
- 2, /* number of targets used */
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* Eval callback. */
+ 2, /* Number of targets used. */
{"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
- DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
+ DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
END_DVAR_TYPEDEF,
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */
- 2, /* number of targets used */
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* Eval callback. */
+ 2, /* Number of targets used. */
{"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY,
- DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
+ DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
END_DVAR_TYPEDEF,
- BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */
- 1, /* number of targets used */
+ BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* Eval callback. */
+ 1, /* Number of targets used. */
{"Object/Bone"}, /* UI names for targets */
- {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */
+ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
END_DVAR_TYPEDEF,
};
/* Get driver variable typeinfo */
static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
{
- /* check if valid type */
+ /* Check if valid type. */
if ((type >= 0) && (type < MAX_DVAR_TYPES)) {
return &dvar_types[type];
}
@@ -708,40 +720,44 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type)
}
}
-/* Driver API --------------------------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Driver API
+ * \{ */
/* Perform actual freeing driver variable and remove it from the given list */
void driver_free_variable(ListBase *variables, DriverVar *dvar)
{
- /* sanity checks */
+ /* Sanity checks. */
if (dvar == NULL) {
return;
}
- /* free target vars
+ /* Free target vars:
* - need to go over all of them, not just up to the ones that are used
* currently, since there may be some lingering RNA paths from
* previous users needing freeing
*/
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- /* free RNA path if applicable */
+ /* Free RNA path if applicable. */
if (dtar->rna_path) {
MEM_freeN(dtar->rna_path);
}
}
DRIVER_TARGETS_LOOPER_END;
- /* remove the variable from the driver */
+ /* Remove the variable from the driver. */
BLI_freelinkN(variables, dvar);
}
/* Free the driver variable and do extra updates */
void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar)
{
- /* remove and free the driver variable */
+ /* Remove and free the driver variable. */
driver_free_variable(&driver->variables, dvar);
- /* since driver variables are cached, the expression needs re-compiling too */
+ /* Since driver variables are cached, the expression needs re-compiling too. */
BKE_driver_invalidate_expression(driver, false, true);
}
@@ -752,9 +768,9 @@ void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars)
BLI_duplicatelist(dst_vars, src_vars);
LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) {
- /* need to go over all targets so that we don't leave any dangling paths */
+ /* Need to go over all targets so that we don't leave any dangling paths. */
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- /* make a copy of target's rna path if available */
+ /* Make a copy of target's rna path if available. */
if (dtar->rna_path) {
dtar->rna_path = MEM_dupallocN(dtar->rna_path);
}
@@ -768,25 +784,24 @@ void driver_change_variable_type(DriverVar *dvar, int type)
{
const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type);
- /* sanity check */
+ /* Sanity check. */
if (ELEM(NULL, dvar, dvti)) {
return;
}
- /* set the new settings */
+ /* Set the new settings. */
dvar->type = type;
dvar->num_targets = dvti->num_targets;
- /* make changes to the targets based on the defines for these types
- * NOTE: only need to make sure the ones we're using here are valid...
- */
+ /* Make changes to the targets based on the defines for these types.
+ * NOTE: only need to make sure the ones we're using here are valid. */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
short flags = dvti->target_flags[tarIndex];
- /* store the flags */
+ /* Store the flags. */
dtar->flag = flags;
- /* object ID types only, or idtype not yet initialized */
+ /* Object ID types only, or idtype not yet initialized. */
if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) {
dtar->idtype = ID_OB;
}
@@ -803,12 +818,12 @@ void driver_variable_name_validate(DriverVar *dvar)
'?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r',
};
- /* sanity checks */
+ /* Sanity checks. */
if (dvar == NULL) {
return;
}
- /* clear all invalid-name flags */
+ /* Clear all invalid-name flags. */
dvar->flag &= ~DVAR_ALL_INVALID_FLAGS;
/* 0) Zero-length identifiers are not allowed */
@@ -869,16 +884,16 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver)
{
DriverVar *dvar;
- /* sanity checks */
+ /* Sanity checks. */
if (driver == NULL) {
return NULL;
}
- /* make a new variable */
+ /* Make a new variable. */
dvar = MEM_callocN(sizeof(DriverVar), "DriverVar");
BLI_addtail(&driver->variables, dvar);
- /* give the variable a 'unique' name */
+ /* Give the variable a 'unique' name. */
strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"));
BLI_uniquename(&driver->variables,
dvar,
@@ -887,13 +902,13 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver)
offsetof(DriverVar, name),
sizeof(dvar->name));
- /* set the default type to 'single prop' */
+ /* Set the default type to 'single prop'. */
driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP);
- /* since driver variables are cached, the expression needs re-compiling too */
+ /* Since driver variables are cached, the expression needs re-compiling too. */
BKE_driver_invalidate_expression(driver, false, true);
- /* return the target */
+ /* Return the target. */
return dvar;
}
@@ -903,20 +918,20 @@ void fcurve_free_driver(FCurve *fcu)
ChannelDriver *driver;
DriverVar *dvar, *dvarn;
- /* sanity checks */
+ /* Sanity checks. */
if (ELEM(NULL, fcu, fcu->driver)) {
return;
}
driver = fcu->driver;
- /* free driver targets */
+ /* Free driver targets. */
for (dvar = driver->variables.first; dvar; dvar = dvarn) {
dvarn = dvar->next;
driver_free_variable_ex(driver, dvar);
}
#ifdef WITH_PYTHON
- /* free compiled driver expression */
+ /* Free compiled driver expression. */
if (driver->expr_comp) {
BPY_DECREF(driver->expr_comp);
}
@@ -935,27 +950,31 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver)
{
ChannelDriver *ndriver;
- /* sanity checks */
+ /* Sanity checks. */
if (driver == NULL) {
return NULL;
}
- /* copy all data */
+ /* Copy all data. */
ndriver = MEM_dupallocN(driver);
ndriver->expr_comp = NULL;
ndriver->expr_simple = NULL;
- /* copy variables */
+ /* Copy variables. */
- /* to get rid of refs to non-copied data (that's still used on original) */
+ /* To get rid of refs to non-copied data (that's still used on original). */
BLI_listbase_clear(&ndriver->variables);
driver_variables_copy(&ndriver->variables, &driver->variables);
- /* return the new driver */
+ /* Return the new driver. */
return ndriver;
}
-/* Driver Expression Evaluation --------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Driver Expression Evaluation
+ * \{ */
/* Index constants for the expression parameter array. */
enum {
@@ -1025,7 +1044,7 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver,
return true;
default:
- /* arriving here means a bug, not user error */
+ /* Arriving here means a bug, not user error. */
CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression);
return false;
}
@@ -1134,22 +1153,25 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver,
#endif
}
-/* Driver Evaluation -------------------------- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Driver Evaluation
+ * \{ */
/* Evaluate a Driver Variable to get a value that contributes to the final */
float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
{
const DriverVarTypeInfo *dvti;
- /* sanity check */
+ /* Sanity check. */
if (ELEM(NULL, driver, dvar)) {
return 0.0f;
}
- /* call the relevant callbacks to get the variable value
+ /* Call the relevant callbacks to get the variable value
* using the variable type info, storing the obtained value
- * in dvar->curval so that drivers can be debugged
- */
+ * in `dvar->curval` so that drivers can be debugged. */
dvti = get_dvar_typeinfo(dvar->type);
if (dvti && dvti->get_value) {
@@ -1166,25 +1188,25 @@ static void evaluate_driver_sum(ChannelDriver *driver)
{
DriverVar *dvar;
- /* check how many variables there are first (i.e. just one?) */
+ /* Check how many variables there are first (i.e. just one?). */
if (BLI_listbase_is_single(&driver->variables)) {
- /* just one target, so just use that */
+ /* Just one target, so just use that. */
dvar = driver->variables.first;
driver->curval = driver_get_variable_value(driver, dvar);
return;
}
- /* more than one target, so average the values of the targets */
+ /* More than one target, so average the values of the targets. */
float value = 0.0f;
int tot = 0;
- /* loop through targets, adding (hopefully we don't get any overflow!) */
+ /* Loop through targets, adding (hopefully we don't get any overflow!). */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
value += driver_get_variable_value(driver, dvar);
tot++;
}
- /* perform operations on the total if appropriate */
+ /* Perform operations on the total if appropriate. */
if (driver->type == DRIVER_TYPE_AVERAGE) {
driver->curval = tot ? (value / (float)tot) : 0.0f;
}
@@ -1198,97 +1220,99 @@ static void evaluate_driver_min_max(ChannelDriver *driver)
DriverVar *dvar;
float value = 0.0f;
- /* loop through the variables, getting the values and comparing them to existing ones */
+ /* Loop through the variables, getting the values and comparing them to existing ones. */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- /* get value */
+ /* Get value. */
float tmp_val = driver_get_variable_value(driver, dvar);
- /* store this value if appropriate */
+ /* Store this value if appropriate. */
if (dvar->prev) {
- /* check if greater/smaller than the baseline */
+ /* Check if greater/smaller than the baseline. */
if (driver->type == DRIVER_TYPE_MAX) {
- /* max? */
+ /* Max? */
if (tmp_val > value) {
value = tmp_val;
}
}
else {
- /* min? */
+ /* Min? */
if (tmp_val < value) {
value = tmp_val;
}
}
}
else {
- /* first item - make this the baseline for comparisons */
+ /* First item - make this the baseline for comparisons. */
value = tmp_val;
}
}
- /* store value in driver */
+ /* Store value in driver. */
driver->curval = value;
}
static void evaluate_driver_python(PathResolvedRNA *anim_rna,
ChannelDriver *driver,
ChannelDriver *driver_orig,
- const float evaltime)
+ const AnimationEvalContext *anim_eval_context)
{
- /* check for empty or invalid expression */
+ /* Check for empty or invalid expression. */
if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) {
driver->curval = 0.0f;
}
- else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) {
+ else if (!driver_try_evaluate_simple_expr(
+ driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) {
#ifdef WITH_PYTHON
- /* this evaluates the expression using Python, and returns its result:
- * - on errors it reports, then returns 0.0f
- */
+ /* This evaluates the expression using Python, and returns its result:
+ * - on errors it reports, then returns 0.0f. */
BLI_mutex_lock(&python_driver_lock);
- driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime);
+ driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, anim_eval_context);
BLI_mutex_unlock(&python_driver_lock);
-#else /* WITH_PYTHON*/
- UNUSED_VARS(anim_rna, evaltime);
-#endif /* WITH_PYTHON*/
+#else /* WITH_PYTHON */
+ UNUSED_VARS(anim_rna, anim_eval_context);
+#endif /* WITH_PYTHON */
}
}
-/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime"
- * - "evaltime" is the frame at which F-Curve is being evaluated
- * - has to return a float value
- * - driver_orig is where we cache Python expressions, in case of COW
+/**
+ * Evaluate an Channel-Driver to get a 'time' value to use
+ * instead of `anim_eval_context->eval_time`.
+ *
+ * - `anim_eval_context->eval_time` is the frame at which F-Curve is being evaluated.
+ * - Has to return a float value.
+ * - \a driver_orig is where we cache Python expressions, in case of COW
*/
float evaluate_driver(PathResolvedRNA *anim_rna,
ChannelDriver *driver,
ChannelDriver *driver_orig,
- const float evaltime)
+ const AnimationEvalContext *anim_eval_context)
{
- /* check if driver can be evaluated */
+ /* Check if driver can be evaluated. */
if (driver_orig->flag & DRIVER_FLAG_INVALID) {
return 0.0f;
}
switch (driver->type) {
- case DRIVER_TYPE_AVERAGE: /* average values of driver targets */
- case DRIVER_TYPE_SUM: /* sum values of driver targets */
+ case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */
+ case DRIVER_TYPE_SUM: /* Sum values of driver targets. */
evaluate_driver_sum(driver);
break;
- case DRIVER_TYPE_MIN: /* smallest value */
- case DRIVER_TYPE_MAX: /* largest value */
+ case DRIVER_TYPE_MIN: /* Smallest value. */
+ case DRIVER_TYPE_MAX: /* Largest value. */
evaluate_driver_min_max(driver);
break;
- case DRIVER_TYPE_PYTHON: /* expression */
- evaluate_driver_python(anim_rna, driver, driver_orig, evaltime);
+ case DRIVER_TYPE_PYTHON: /* Expression. */
+ evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context);
break;
default:
- /* special 'hack' - just use stored value
+ /* Special 'hack' - just use stored value
* This is currently used as the mechanism which allows animated settings to be able
- * to be changed via the UI.
- */
+ * to be changed via the UI. */
break;
}
- /* return value for driver */
+ /* Return value for driver. */
return driver->curval;
}
diff --git a/tests/gtests/blenkernel/BKE_fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc
index e994dd43af9..a6f65a7c9b3 100644
--- a/tests/gtests/blenkernel/BKE_fcurve_test.cc
+++ b/source/blender/blenkernel/intern/fcurve_test.cc
@@ -19,13 +19,13 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BKE_fcurve.h"
#include "ED_keyframing.h"
#include "DNA_anim_types.h"
-}
+
+namespace blender::bke::tests {
// Epsilon for floating point comparisons.
static const float EPSILON = 1e-7f;
@@ -209,3 +209,5 @@ TEST(evaluate_fcurve, ExtrapolationBezierKeys)
BKE_fcurve_free(fcu);
}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index b75592836e0..079b436a3ea 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -93,7 +93,7 @@
/** Max value for phi initialization */
#define PHI_MAX 9999.0f
-static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *mmd, bool need_lock);
+static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock);
#ifdef WITH_FLUID
// #define DEBUG_PRINT
@@ -115,131 +115,106 @@ struct Scene;
# define ADD_IF_LOWER_NEG(a, b) (max_ff((a) + (b), min_ff((a), (b))))
# define ADD_IF_LOWER(a, b) (((b) > 0) ? ADD_IF_LOWER_POS((a), (b)) : ADD_IF_LOWER_NEG((a), (b)))
-bool BKE_fluid_reallocate_fluid(FluidDomainSettings *mds, int res[3], int free_old)
+bool BKE_fluid_reallocate_fluid(FluidDomainSettings *fds, int res[3], int free_old)
{
- if (free_old && mds->fluid) {
- manta_free(mds->fluid);
+ if (free_old && fds->fluid) {
+ manta_free(fds->fluid);
}
if (!min_iii(res[0], res[1], res[2])) {
- mds->fluid = NULL;
+ fds->fluid = NULL;
}
else {
- mds->fluid = manta_init(res, mds->mmd);
+ fds->fluid = manta_init(res, fds->fmd);
- mds->res_noise[0] = res[0] * mds->noise_scale;
- mds->res_noise[1] = res[1] * mds->noise_scale;
- mds->res_noise[2] = res[2] * mds->noise_scale;
+ fds->res_noise[0] = res[0] * fds->noise_scale;
+ fds->res_noise[1] = res[1] * fds->noise_scale;
+ fds->res_noise[2] = res[2] * fds->noise_scale;
}
- return (mds->fluid != NULL);
+ return (fds->fluid != NULL);
}
-void BKE_fluid_reallocate_copy_fluid(FluidDomainSettings *mds,
+void BKE_fluid_reallocate_copy_fluid(FluidDomainSettings *fds,
int o_res[3],
int n_res[3],
- int o_min[3],
- int n_min[3],
- int o_max[3],
+ const int o_min[3],
+ const int n_min[3],
+ const int o_max[3],
int o_shift[3],
int n_shift[3])
{
- int x, y, z;
- struct MANTA *fluid_old = mds->fluid;
- const int block_size = mds->noise_scale;
+ struct MANTA *fluid_old = fds->fluid;
+ const int block_size = fds->noise_scale;
int new_shift[3] = {0};
sub_v3_v3v3_int(new_shift, n_shift, o_shift);
- /* allocate new fluid data */
- BKE_fluid_reallocate_fluid(mds, n_res, 0);
+ /* Allocate new fluid data. */
+ BKE_fluid_reallocate_fluid(fds, n_res, 0);
int o_total_cells = o_res[0] * o_res[1] * o_res[2];
int n_total_cells = n_res[0] * n_res[1] * n_res[2];
- /* boundary cells will be skipped when copying data */
- int bwidth = mds->boundary_width;
-
- /* copy values from old fluid to new */
+ /* Copy values from old fluid to new fluid object. */
if (o_total_cells > 1 && n_total_cells > 1) {
- /* base smoke */
- float *o_dens, *o_react, *o_flame, *o_fuel, *o_heat, *o_vx, *o_vy, *o_vz, *o_r, *o_g, *o_b;
- float *n_dens, *n_react, *n_flame, *n_fuel, *n_heat, *n_vx, *n_vy, *n_vz, *n_r, *n_g, *n_b;
- float dummy, *dummy_s;
- int *dummy_p;
- /* noise smoke */
+ float *o_dens = manta_smoke_get_density(fluid_old);
+ float *o_react = manta_smoke_get_react(fluid_old);
+ float *o_flame = manta_smoke_get_flame(fluid_old);
+ float *o_fuel = manta_smoke_get_fuel(fluid_old);
+ float *o_heat = manta_smoke_get_heat(fluid_old);
+ float *o_vx = manta_get_velocity_x(fluid_old);
+ float *o_vy = manta_get_velocity_y(fluid_old);
+ float *o_vz = manta_get_velocity_z(fluid_old);
+ float *o_r = manta_smoke_get_color_r(fluid_old);
+ float *o_g = manta_smoke_get_color_g(fluid_old);
+ float *o_b = manta_smoke_get_color_b(fluid_old);
+
+ float *n_dens = manta_smoke_get_density(fds->fluid);
+ float *n_react = manta_smoke_get_react(fds->fluid);
+ float *n_flame = manta_smoke_get_flame(fds->fluid);
+ float *n_fuel = manta_smoke_get_fuel(fds->fluid);
+ float *n_heat = manta_smoke_get_heat(fds->fluid);
+ float *n_vx = manta_get_velocity_x(fds->fluid);
+ float *n_vy = manta_get_velocity_y(fds->fluid);
+ float *n_vz = manta_get_velocity_z(fds->fluid);
+ float *n_r = manta_smoke_get_color_r(fds->fluid);
+ float *n_g = manta_smoke_get_color_g(fds->fluid);
+ float *n_b = manta_smoke_get_color_b(fds->fluid);
+
+ /* Noise smoke fields. */
int wt_res_old[3];
- float *o_wt_dens, *o_wt_react, *o_wt_flame, *o_wt_fuel, *o_wt_tcu, *o_wt_tcv, *o_wt_tcw,
- *o_wt_tcu2, *o_wt_tcv2, *o_wt_tcw2, *o_wt_r, *o_wt_g, *o_wt_b;
- float *n_wt_dens, *n_wt_react, *n_wt_flame, *n_wt_fuel, *n_wt_tcu, *n_wt_tcv, *n_wt_tcw,
- *n_wt_tcu2, *n_wt_tcv2, *n_wt_tcw2, *n_wt_r, *n_wt_g, *n_wt_b;
-
- if (mds->flags & FLUID_DOMAIN_USE_NOISE) {
- manta_smoke_turbulence_export(fluid_old,
- &o_wt_dens,
- &o_wt_react,
- &o_wt_flame,
- &o_wt_fuel,
- &o_wt_r,
- &o_wt_g,
- &o_wt_b,
- &o_wt_tcu,
- &o_wt_tcv,
- &o_wt_tcw,
- &o_wt_tcu2,
- &o_wt_tcv2,
- &o_wt_tcw2);
- manta_smoke_turbulence_get_res(fluid_old, wt_res_old);
- manta_smoke_turbulence_export(mds->fluid,
- &n_wt_dens,
- &n_wt_react,
- &n_wt_flame,
- &n_wt_fuel,
- &n_wt_r,
- &n_wt_g,
- &n_wt_b,
- &n_wt_tcu,
- &n_wt_tcv,
- &n_wt_tcw,
- &n_wt_tcu2,
- &n_wt_tcv2,
- &n_wt_tcw2);
- }
-
- manta_smoke_export(fluid_old,
- &dummy,
- &dummy,
- &o_dens,
- &o_react,
- &o_flame,
- &o_fuel,
- &o_heat,
- &o_vx,
- &o_vy,
- &o_vz,
- &o_r,
- &o_g,
- &o_b,
- &dummy_p,
- &dummy_s);
- manta_smoke_export(mds->fluid,
- &dummy,
- &dummy,
- &n_dens,
- &n_react,
- &n_flame,
- &n_fuel,
- &n_heat,
- &n_vx,
- &n_vy,
- &n_vz,
- &n_r,
- &n_g,
- &n_b,
- &dummy_p,
- &dummy_s);
-
- for (x = o_min[0]; x < o_max[0]; x++) {
- for (y = o_min[1]; y < o_max[1]; y++) {
- for (z = o_min[2]; z < o_max[2]; z++) {
+ float *o_wt_dens = manta_noise_get_density(fluid_old);
+ float *o_wt_react = manta_noise_get_react(fluid_old);
+ float *o_wt_flame = manta_noise_get_flame(fluid_old);
+ float *o_wt_fuel = manta_noise_get_fuel(fluid_old);
+ float *o_wt_r = manta_noise_get_color_r(fluid_old);
+ float *o_wt_g = manta_noise_get_color_g(fluid_old);
+ float *o_wt_b = manta_noise_get_color_b(fluid_old);
+ float *o_wt_tcu = manta_noise_get_texture_u(fluid_old);
+ float *o_wt_tcv = manta_noise_get_texture_v(fluid_old);
+ float *o_wt_tcw = manta_noise_get_texture_w(fluid_old);
+ float *o_wt_tcu2 = manta_noise_get_texture_u2(fluid_old);
+ float *o_wt_tcv2 = manta_noise_get_texture_v2(fluid_old);
+ float *o_wt_tcw2 = manta_noise_get_texture_w2(fluid_old);
+
+ float *n_wt_dens = manta_noise_get_density(fds->fluid);
+ float *n_wt_react = manta_noise_get_react(fds->fluid);
+ float *n_wt_flame = manta_noise_get_flame(fds->fluid);
+ float *n_wt_fuel = manta_noise_get_fuel(fds->fluid);
+ float *n_wt_r = manta_noise_get_color_r(fds->fluid);
+ float *n_wt_g = manta_noise_get_color_g(fds->fluid);
+ float *n_wt_b = manta_noise_get_color_b(fds->fluid);
+ float *n_wt_tcu = manta_noise_get_texture_u(fds->fluid);
+ float *n_wt_tcv = manta_noise_get_texture_v(fds->fluid);
+ float *n_wt_tcw = manta_noise_get_texture_w(fds->fluid);
+ float *n_wt_tcu2 = manta_noise_get_texture_u2(fds->fluid);
+ float *n_wt_tcv2 = manta_noise_get_texture_v2(fds->fluid);
+ float *n_wt_tcw2 = manta_noise_get_texture_w2(fds->fluid);
+
+ manta_noise_get_res(fluid_old, wt_res_old);
+
+ for (int z = o_min[2]; z < o_max[2]; z++) {
+ for (int y = o_min[1]; y < o_max[1]; y++) {
+ for (int x = o_min[0]; x < o_max[0]; x++) {
/* old grid index */
int xo = x - o_min[0];
int yo = y - o_min[1];
@@ -251,23 +226,34 @@ void BKE_fluid_reallocate_copy_fluid(FluidDomainSettings *mds,
int zn = z - n_min[2] - new_shift[2];
int index_new = manta_get_index(xn, n_res[0], yn, n_res[1], zn);
- /* skip if outside new domain */
+ /* Skip if outside new domain. */
if (xn < 0 || xn >= n_res[0] || yn < 0 || yn >= n_res[1] || zn < 0 || zn >= n_res[2]) {
continue;
}
- /* skip if trying to copy from old boundary cell */
+# if 0
+ /* Note (sebbas):
+ * Disabling this "skip section" as not copying borders results in weird cut-off effects.
+ * It is possible that this cutting off is the reason for line effects as seen in T74559.
+ * Since domain borders will be handled on the simulation side anyways,
+ * copying border values should not be an issue. */
+
+ /* boundary cells will be skipped when copying data */
+ int bwidth = fds->boundary_width;
+
+ /* Skip if trying to copy from old boundary cell. */
if (xo < bwidth || yo < bwidth || zo < bwidth || xo >= o_res[0] - bwidth ||
yo >= o_res[1] - bwidth || zo >= o_res[2] - bwidth) {
continue;
}
- /* skip if trying to copy into new boundary cell */
+ /* Skip if trying to copy into new boundary cell. */
if (xn < bwidth || yn < bwidth || zn < bwidth || xn >= n_res[0] - bwidth ||
yn >= n_res[1] - bwidth || zn >= n_res[2] - bwidth) {
continue;
}
+# endif
/* copy data */
- if (mds->flags & FLUID_DOMAIN_USE_NOISE) {
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE) {
int i, j, k;
/* old grid index */
int xx_o = xo * block_size;
@@ -293,7 +279,7 @@ void BKE_fluid_reallocate_copy_fluid(FluidDomainSettings *mds,
int big_index_old = manta_get_index(
xx_o + i, wt_res_old[0], yy_o + j, wt_res_old[1], zz_o + k);
int big_index_new = manta_get_index(
- xx_n + i, mds->res_noise[0], yy_n + j, mds->res_noise[1], zz_n + k);
+ xx_n + i, fds->res_noise[0], yy_n + j, fds->res_noise[1], zz_n + k);
/* copy data */
n_wt_dens[big_index_new] = o_wt_dens[big_index_old];
if (n_wt_flame && o_wt_flame) {
@@ -338,86 +324,86 @@ void BKE_fluid_reallocate_copy_fluid(FluidDomainSettings *mds,
manta_free(fluid_old);
}
-void BKE_fluid_cache_free_all(FluidDomainSettings *mds, Object *ob)
+void BKE_fluid_cache_free_all(FluidDomainSettings *fds, Object *ob)
{
int cache_map = (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE |
FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES |
FLUID_DOMAIN_OUTDATED_GUIDE);
- BKE_fluid_cache_free(mds, ob, cache_map);
+ BKE_fluid_cache_free(fds, ob, cache_map);
}
-void BKE_fluid_cache_free(FluidDomainSettings *mds, Object *ob, int cache_map)
+void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map)
{
char temp_dir[FILE_MAX];
- int flags = mds->cache_flag;
+ int flags = fds->cache_flag;
const char *relbase = BKE_modifier_path_relbase_from_global(ob);
if (cache_map & FLUID_DOMAIN_OUTDATED_DATA) {
flags &= ~(FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA);
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- mds->cache_frame_pause_data = 0;
+ fds->cache_frame_pause_data = 0;
}
if (cache_map & FLUID_DOMAIN_OUTDATED_NOISE) {
flags &= ~(FLUID_DOMAIN_BAKING_NOISE | FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE);
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- mds->cache_frame_pause_noise = 0;
+ fds->cache_frame_pause_noise = 0;
}
if (cache_map & FLUID_DOMAIN_OUTDATED_MESH) {
flags &= ~(FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH);
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- mds->cache_frame_pause_mesh = 0;
+ fds->cache_frame_pause_mesh = 0;
}
if (cache_map & FLUID_DOMAIN_OUTDATED_PARTICLES) {
flags &= ~(FLUID_DOMAIN_BAKING_PARTICLES | FLUID_DOMAIN_BAKED_PARTICLES |
FLUID_DOMAIN_OUTDATED_PARTICLES);
BLI_path_join(
- temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL);
+ temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- mds->cache_frame_pause_particles = 0;
+ fds->cache_frame_pause_particles = 0;
}
if (cache_map & FLUID_DOMAIN_OUTDATED_GUIDE) {
flags &= ~(FLUID_DOMAIN_BAKING_GUIDE | FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE);
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_delete(temp_dir, true, true); /* BLI_exists(filepath) is implicit */
- mds->cache_frame_pause_guide = 0;
+ fds->cache_frame_pause_guide = 0;
}
- mds->cache_flag = flags;
+ fds->cache_flag = flags;
}
/* convert global position to domain cell space */
-static void manta_pos_to_cell(FluidDomainSettings *mds, float pos[3])
+static void manta_pos_to_cell(FluidDomainSettings *fds, float pos[3])
{
- mul_m4_v3(mds->imat, pos);
- sub_v3_v3(pos, mds->p0);
- pos[0] *= 1.0f / mds->cell_size[0];
- pos[1] *= 1.0f / mds->cell_size[1];
- pos[2] *= 1.0f / mds->cell_size[2];
+ mul_m4_v3(fds->imat, pos);
+ sub_v3_v3(pos, fds->p0);
+ pos[0] *= 1.0f / fds->cell_size[0];
+ pos[1] *= 1.0f / fds->cell_size[1];
+ pos[2] *= 1.0f / fds->cell_size[2];
}
/* Set domain transformations and base resolution from object mesh. */
-static void manta_set_domain_from_mesh(FluidDomainSettings *mds,
+static void manta_set_domain_from_mesh(FluidDomainSettings *fds,
Object *ob,
Mesh *me,
bool init_resolution)
@@ -429,7 +415,7 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *mds,
float scale = 0.0;
int res;
- res = mds->maxres;
+ res = fds->maxres;
/* Set minimum and maximum coordinates of BB. */
for (i = 0; i < me->totvert; i++) {
@@ -437,24 +423,24 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *mds,
}
/* Set domain bounds. */
- copy_v3_v3(mds->p0, min);
- copy_v3_v3(mds->p1, max);
- mds->dx = 1.0f / res;
+ copy_v3_v3(fds->p0, min);
+ copy_v3_v3(fds->p1, max);
+ fds->dx = 1.0f / res;
/* Calculate domain dimensions. */
sub_v3_v3v3(size, max, min);
if (init_resolution) {
- zero_v3_int(mds->base_res);
- copy_v3_v3(mds->cell_size, size);
+ zero_v3_int(fds->base_res);
+ copy_v3_v3(fds->cell_size, size);
}
/* Apply object scale. */
for (i = 0; i < 3; i++) {
size[i] = fabsf(size[i] * ob->scale[i]);
}
- copy_v3_v3(mds->global_size, size);
- copy_v3_v3(mds->dp0, min);
+ copy_v3_v3(fds->global_size, size);
+ copy_v3_v3(fds->dp0, min);
- invert_m4_m4(mds->imat, ob->obmat);
+ invert_m4_m4(fds->imat, ob->obmat);
/* Prevent crash when initializing a plane as domain. */
if (!init_resolution || (size[0] < FLT_EPSILON) || (size[1] < FLT_EPSILON) ||
@@ -465,103 +451,111 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *mds,
/* Define grid resolutions from longest domain side. */
if (size[0] >= MAX2(size[1], size[2])) {
scale = res / size[0];
- mds->scale = size[0] / fabsf(ob->scale[0]);
- mds->base_res[0] = res;
- mds->base_res[1] = max_ii((int)(size[1] * scale + 0.5f), 4);
- mds->base_res[2] = max_ii((int)(size[2] * scale + 0.5f), 4);
+ fds->scale = size[0] / fabsf(ob->scale[0]);
+ fds->base_res[0] = res;
+ fds->base_res[1] = max_ii((int)(size[1] * scale + 0.5f), 4);
+ fds->base_res[2] = max_ii((int)(size[2] * scale + 0.5f), 4);
}
else if (size[1] >= MAX2(size[0], size[2])) {
scale = res / size[1];
- mds->scale = size[1] / fabsf(ob->scale[1]);
- mds->base_res[0] = max_ii((int)(size[0] * scale + 0.5f), 4);
- mds->base_res[1] = res;
- mds->base_res[2] = max_ii((int)(size[2] * scale + 0.5f), 4);
+ fds->scale = size[1] / fabsf(ob->scale[1]);
+ fds->base_res[0] = max_ii((int)(size[0] * scale + 0.5f), 4);
+ fds->base_res[1] = res;
+ fds->base_res[2] = max_ii((int)(size[2] * scale + 0.5f), 4);
}
else {
scale = res / size[2];
- mds->scale = size[2] / fabsf(ob->scale[2]);
- mds->base_res[0] = max_ii((int)(size[0] * scale + 0.5f), 4);
- mds->base_res[1] = max_ii((int)(size[1] * scale + 0.5f), 4);
- mds->base_res[2] = res;
+ fds->scale = size[2] / fabsf(ob->scale[2]);
+ fds->base_res[0] = max_ii((int)(size[0] * scale + 0.5f), 4);
+ fds->base_res[1] = max_ii((int)(size[1] * scale + 0.5f), 4);
+ fds->base_res[2] = res;
}
/* Set cell size. */
- mds->cell_size[0] /= (float)mds->base_res[0];
- mds->cell_size[1] /= (float)mds->base_res[1];
- mds->cell_size[2] /= (float)mds->base_res[2];
+ fds->cell_size[0] /= (float)fds->base_res[0];
+ fds->cell_size[1] /= (float)fds->base_res[1];
+ fds->cell_size[2] /= (float)fds->base_res[2];
+}
+
+static void update_final_gravity(FluidDomainSettings *fds, Scene *scene)
+{
+ if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
+ copy_v3_v3(fds->gravity_final, scene->physics_settings.gravity);
+ }
+ else {
+ copy_v3_v3(fds->gravity_final, fds->gravity);
+ }
+ mul_v3_fl(fds->gravity_final, fds->effector_weights->global_gravity);
}
static bool BKE_fluid_modifier_init(
- FluidModifierData *mmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me)
+ FluidModifierData *fmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me)
{
int scene_framenr = (int)DEG_get_ctime(depsgraph);
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain && !mmd->domain->fluid) {
- FluidDomainSettings *mds = mmd->domain;
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain && !fmd->domain->fluid) {
+ FluidDomainSettings *fds = fmd->domain;
int res[3];
/* Set domain dimensions from mesh. */
- manta_set_domain_from_mesh(mds, ob, me, true);
+ manta_set_domain_from_mesh(fds, ob, me, true);
/* Set domain gravity, use global gravity if enabled. */
- if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
- copy_v3_v3(mds->gravity, scene->physics_settings.gravity);
- }
- mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity);
+ update_final_gravity(fds, scene);
/* Reset domain values. */
- zero_v3_int(mds->shift);
- zero_v3(mds->shift_f);
- add_v3_fl(mds->shift_f, 0.5f);
- zero_v3(mds->prev_loc);
- mul_m4_v3(ob->obmat, mds->prev_loc);
- copy_m4_m4(mds->obmat, ob->obmat);
+ zero_v3_int(fds->shift);
+ zero_v3(fds->shift_f);
+ add_v3_fl(fds->shift_f, 0.5f);
+ zero_v3(fds->prev_loc);
+ mul_m4_v3(ob->obmat, fds->prev_loc);
+ copy_m4_m4(fds->obmat, ob->obmat);
/* Set resolutions. */
- if (mmd->domain->type == FLUID_DOMAIN_TYPE_GAS &&
- mmd->domain->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
+ if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS &&
+ fmd->domain->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
res[0] = res[1] = res[2] = 1; /* Use minimum res for adaptive init. */
}
else {
- copy_v3_v3_int(res, mds->base_res);
+ copy_v3_v3_int(res, fds->base_res);
}
- copy_v3_v3_int(mds->res, res);
- mds->total_cells = mds->res[0] * mds->res[1] * mds->res[2];
- mds->res_min[0] = mds->res_min[1] = mds->res_min[2] = 0;
- copy_v3_v3_int(mds->res_max, res);
+ copy_v3_v3_int(fds->res, res);
+ fds->total_cells = fds->res[0] * fds->res[1] * fds->res[2];
+ fds->res_min[0] = fds->res_min[1] = fds->res_min[2] = 0;
+ copy_v3_v3_int(fds->res_max, res);
/* Set time, frame length = 0.1 is at 25fps. */
float fps = scene->r.frs_sec / scene->r.frs_sec_base;
- mds->frame_length = DT_DEFAULT * (25.0f / fps) * mds->time_scale;
+ fds->frame_length = DT_DEFAULT * (25.0f / fps) * fds->time_scale;
/* Initially dt is equal to frame length (dt can change with adaptive-time stepping though). */
- mds->dt = mds->frame_length;
- mds->time_per_frame = 0;
+ fds->dt = fds->frame_length;
+ fds->time_per_frame = 0;
- mmd->time = scene_framenr;
+ fmd->time = scene_framenr;
/* Allocate fluid. */
- return BKE_fluid_reallocate_fluid(mds, mds->res, 0);
+ return BKE_fluid_reallocate_fluid(fds, fds->res, 0);
}
- else if (mmd->type & MOD_FLUID_TYPE_FLOW) {
- if (!mmd->flow) {
- BKE_fluid_modifier_create_type_data(mmd);
+ else if (fmd->type & MOD_FLUID_TYPE_FLOW) {
+ if (!fmd->flow) {
+ BKE_fluid_modifier_create_type_data(fmd);
}
- mmd->time = scene_framenr;
+ fmd->time = scene_framenr;
return true;
}
- else if (mmd->type & MOD_FLUID_TYPE_EFFEC) {
- if (!mmd->effector) {
- BKE_fluid_modifier_create_type_data(mmd);
+ else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
+ if (!fmd->effector) {
+ BKE_fluid_modifier_create_type_data(fmd);
}
- mmd->time = scene_framenr;
+ fmd->time = scene_framenr;
return true;
}
return false;
}
// forward declaration
-static void manta_smoke_calc_transparency(FluidDomainSettings *mds, ViewLayer *view_layer);
+static void manta_smoke_calc_transparency(FluidDomainSettings *fds, ViewLayer *view_layer);
static float calc_voxel_transp(
- float *result, float *input, int res[3], int *pixel, float *t_ray, float correct);
+ float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct);
static void update_distances(int index,
- float *mesh_distances,
+ float *fesh_distances,
BVHTreeFromMesh *tree_data,
const float ray_start[3],
float surface_thickness,
@@ -591,17 +585,17 @@ static int get_light(ViewLayer *view_layer, float *light)
return found_light;
}
-static void clamp_bounds_in_domain(FluidDomainSettings *mds,
+static void clamp_bounds_in_domain(FluidDomainSettings *fds,
int min[3],
int max[3],
- float *min_vel,
- float *max_vel,
+ const float *min_vel,
+ const float *max_vel,
int margin,
float dt)
{
int i;
for (i = 0; i < 3; i++) {
- int adapt = (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) ? mds->adapt_res : 0;
+ int adapt = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) ? fds->adapt_res : 0;
/* Add some margin. */
min[i] -= margin;
max[i] += margin;
@@ -615,8 +609,8 @@ static void clamp_bounds_in_domain(FluidDomainSettings *mds,
}
/* Clamp within domain max size. */
- CLAMP(min[i], -adapt, mds->base_res[i] + adapt);
- CLAMP(max[i], -adapt, mds->base_res[i] + adapt);
+ CLAMP(min[i], -adapt, fds->base_res[i] + adapt);
+ CLAMP(max[i], -adapt, fds->base_res[i] + adapt);
}
}
@@ -821,7 +815,7 @@ static void bb_combineMaps(FluidObjectBB *output,
/** \name Effectors
* \{ */
-BLI_INLINE void apply_effector_fields(FluidEffectorSettings *UNUSED(mes),
+BLI_INLINE void apply_effector_fields(FluidEffectorSettings *UNUSED(fes),
int index,
float distance_value,
float *phi_in,
@@ -851,7 +845,7 @@ BLI_INLINE void apply_effector_fields(FluidEffectorSettings *UNUSED(mes),
}
}
-static void sample_effector(FluidEffectorSettings *mes,
+static void sample_effector(FluidEffectorSettings *fes,
const MVert *mvert,
const MLoop *mloop,
const MLoopTri *mlooptri,
@@ -890,10 +884,10 @@ static void sample_effector(FluidEffectorSettings *mes,
interp_v3_v3v3v3(hit_vel, &vert_vel[v1 * 3], &vert_vel[v2 * 3], &vert_vel[v3 * 3], weights);
/* Guiding has additional velocity multiplier */
- if (mes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
- mul_v3_fl(hit_vel, mes->vel_multi);
+ if (fes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
+ mul_v3_fl(hit_vel, fes->vel_multi);
- switch (mes->guide_mode) {
+ switch (fes->guide_mode) {
case FLUID_EFFECTOR_GUIDE_AVERAGED:
velocity_map[index * 3] = (velocity_map[index * 3] + hit_vel[0]) * 0.5f;
velocity_map[index * 3 + 1] = (velocity_map[index * 3 + 1] + hit_vel[1]) * 0.5f;
@@ -936,7 +930,7 @@ static void sample_effector(FluidEffectorSettings *mes,
}
typedef struct ObstaclesFromDMData {
- FluidEffectorSettings *mes;
+ FluidEffectorSettings *fes;
const MVert *mvert;
const MLoop *mloop;
@@ -964,7 +958,7 @@ static void obstacles_from_mesh_task_cb(void *__restrict userdata,
float ray_start[3] = {(float)x + 0.5f, (float)y + 0.5f, (float)z + 0.5f};
/* Calculate object velocities. Result in bb->velocity. */
- sample_effector(data->mes,
+ sample_effector(data->fes,
data->mvert,
data->mloop,
data->mlooptri,
@@ -980,8 +974,8 @@ static void obstacles_from_mesh_task_cb(void *__restrict userdata,
bb->distances,
data->tree,
ray_start,
- data->mes->surface_distance,
- data->mes->flags & FLUID_EFFECTOR_USE_PLANE_INIT);
+ data->fes->surface_distance,
+ data->fes->flags & FLUID_EFFECTOR_USE_PLANE_INIT);
/* Ensure that num objects are also counted inside object.
* But don't count twice (see object inc for nearest point). */
@@ -993,12 +987,12 @@ static void obstacles_from_mesh_task_cb(void *__restrict userdata,
}
static void obstacles_from_mesh(Object *coll_ob,
- FluidDomainSettings *mds,
- FluidEffectorSettings *mes,
+ FluidDomainSettings *fds,
+ FluidEffectorSettings *fes,
FluidObjectBB *bb,
float dt)
{
- if (mes->mesh) {
+ if (fes->mesh) {
Mesh *me = NULL;
MVert *mvert = NULL;
const MLoopTri *looptri;
@@ -1009,7 +1003,7 @@ static void obstacles_from_mesh(Object *coll_ob,
float *vert_vel = NULL;
bool has_velocity = false;
- me = BKE_mesh_copy_for_eval(mes->mesh, true);
+ me = BKE_mesh_copy_for_eval(fes->mesh, true);
int min[3], max[3], res[3];
@@ -1029,13 +1023,13 @@ static void obstacles_from_mesh(Object *coll_ob,
{
vert_vel = MEM_callocN(sizeof(float) * numverts * 3, "manta_obs_velocity");
- if (mes->numverts != numverts || !mes->verts_old) {
- if (mes->verts_old) {
- MEM_freeN(mes->verts_old);
+ if (fes->numverts != numverts || !fes->verts_old) {
+ if (fes->verts_old) {
+ MEM_freeN(fes->verts_old);
}
- mes->verts_old = MEM_callocN(sizeof(float) * numverts * 3, "manta_obs_verts_old");
- mes->numverts = numverts;
+ fes->verts_old = MEM_callocN(sizeof(float) * numverts * 3, "manta_obs_verts_old");
+ fes->numverts = numverts;
}
else {
has_velocity = true;
@@ -1049,22 +1043,22 @@ static void obstacles_from_mesh(Object *coll_ob,
/* Vertex position. */
mul_m4_v3(coll_ob->obmat, mvert[i].co);
- manta_pos_to_cell(mds, mvert[i].co);
+ manta_pos_to_cell(fds, mvert[i].co);
/* Vertex normal. */
normal_short_to_float_v3(n, mvert[i].no);
mul_mat3_m4_v3(coll_ob->obmat, n);
- mul_mat3_m4_v3(mds->imat, n);
+ mul_mat3_m4_v3(fds->imat, n);
normalize_v3(n);
normal_float_to_short_v3(mvert[i].no, n);
/* Vertex velocity. */
- add_v3fl_v3fl_v3i(co, mvert[i].co, mds->shift);
+ add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift);
if (has_velocity) {
- sub_v3_v3v3(&vert_vel[i * 3], co, &mes->verts_old[i * 3]);
- mul_v3_fl(&vert_vel[i * 3], mds->dx / dt);
+ sub_v3_v3v3(&vert_vel[i * 3], co, &fes->verts_old[i * 3]);
+ mul_v3_fl(&vert_vel[i * 3], fds->dx / dt);
}
- copy_v3_v3(&mes->verts_old[i * 3], co);
+ copy_v3_v3(&fes->verts_old[i * 3], co);
/* Calculate emission map bounds. */
bb_boundInsert(bb, mvert[i].co);
@@ -1073,7 +1067,7 @@ static void obstacles_from_mesh(Object *coll_ob,
/* Set emission map.
* Use 3 cell diagonals as margin (3 * 1.732 = 5.196). */
int bounds_margin = (int)ceil(5.196);
- clamp_bounds_in_domain(mds, bb->min, bb->max, NULL, NULL, bounds_margin, dt);
+ clamp_bounds_in_domain(fds, bb->min, bb->max, NULL, NULL, bounds_margin, dt);
bb_allocateData(bb, true, false);
/* Setup loop bounds. */
@@ -1086,7 +1080,7 @@ static void obstacles_from_mesh(Object *coll_ob,
if (BKE_bvhtree_from_mesh_get(&tree_data, me, BVHTREE_FROM_LOOPTRI, 4)) {
ObstaclesFromDMData data = {
- .mes = mes,
+ .fes = fes,
.mvert = mvert,
.mloop = mloop,
.mlooptri = looptri,
@@ -1117,21 +1111,21 @@ static void obstacles_from_mesh(Object *coll_ob,
}
}
-static void ensure_obstaclefields(FluidDomainSettings *mds)
+static void ensure_obstaclefields(FluidDomainSettings *fds)
{
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE) {
- manta_ensure_obstacle(mds->fluid, mds->mmd);
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE) {
+ manta_ensure_obstacle(fds->fluid, fds->fmd);
}
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE) {
- manta_ensure_guiding(mds->fluid, mds->mmd);
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE) {
+ manta_ensure_guiding(fds->fluid, fds->fmd);
}
}
-static void update_obstacleflags(FluidDomainSettings *mds,
+static void update_obstacleflags(FluidDomainSettings *fds,
Object **coll_ob_array,
int coll_ob_array_len)
{
- int active_fields = mds->active_fields;
+ int active_fields = fds->active_fields;
uint coll_index;
/* First, remove all flags that we want to update. */
@@ -1141,46 +1135,46 @@ static void update_obstacleflags(FluidDomainSettings *mds,
/* Monitor active fields based on flow settings */
for (coll_index = 0; coll_index < coll_ob_array_len; coll_index++) {
Object *coll_ob = coll_ob_array[coll_index];
- FluidModifierData *mmd2 = (FluidModifierData *)BKE_modifiers_findby_type(coll_ob,
+ FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(coll_ob,
eModifierType_Fluid);
/* Sanity check. */
- if (!mmd2) {
+ if (!fmd2) {
continue;
}
- if ((mmd2->type & MOD_FLUID_TYPE_EFFEC) && mmd2->effector) {
- FluidEffectorSettings *mes = mmd2->effector;
- if (!mes) {
+ if ((fmd2->type & MOD_FLUID_TYPE_EFFEC) && fmd2->effector) {
+ FluidEffectorSettings *fes = fmd2->effector;
+ if (!fes) {
break;
}
- if (mes->flags & FLUID_EFFECTOR_NEEDS_UPDATE) {
- mes->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
- mds->cache_flag |= FLUID_DOMAIN_OUTDATED_DATA;
+ if (fes->flags & FLUID_EFFECTOR_NEEDS_UPDATE) {
+ fes->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
+ fds->cache_flag |= FLUID_DOMAIN_OUTDATED_DATA;
}
- if (mes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
+ if (fes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
active_fields |= FLUID_DOMAIN_ACTIVE_OBSTACLE;
}
- if (mes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
+ if (fes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
active_fields |= FLUID_DOMAIN_ACTIVE_GUIDE;
}
}
}
- mds->active_fields = active_fields;
+ fds->active_fields = active_fields;
}
static bool escape_effectorobject(Object *flowobj,
- FluidDomainSettings *mds,
- FluidEffectorSettings *mes,
+ FluidDomainSettings *fds,
+ FluidEffectorSettings *fes,
int frame)
{
bool is_static = is_static_object(flowobj);
- bool use_effector = (mes->flags & FLUID_EFFECTOR_USE_EFFEC);
+ bool use_effector = (fes->flags & FLUID_EFFECTOR_USE_EFFEC);
- bool is_resume = (mds->cache_frame_pause_data == frame);
- bool is_adaptive = (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
- bool is_first_frame = (frame == mds->cache_frame_start);
+ bool is_resume = (fds->cache_frame_pause_data == frame);
+ bool is_adaptive = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
+ bool is_first_frame = (frame == fds->cache_frame_start);
/* Cannot use static mode with adaptive domain.
* The adaptive domain might expand and only later discover the static object. */
@@ -1205,31 +1199,31 @@ static void compute_obstaclesemission(Scene *scene,
Object **effecobjs,
int frame,
float frame_length,
- FluidDomainSettings *mds,
+ FluidDomainSettings *fds,
uint numeffecobjs,
float time_per_frame)
{
- bool is_first_frame = (frame == mds->cache_frame_start);
+ bool is_first_frame = (frame == fds->cache_frame_start);
/* Prepare effector maps. */
for (int effec_index = 0; effec_index < numeffecobjs; effec_index++) {
Object *effecobj = effecobjs[effec_index];
- FluidModifierData *mmd2 = (FluidModifierData *)BKE_modifiers_findby_type(effecobj,
+ FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(effecobj,
eModifierType_Fluid);
/* Sanity check. */
- if (!mmd2) {
+ if (!fmd2) {
continue;
}
/* Check for initialized effector object. */
- if ((mmd2->type & MOD_FLUID_TYPE_EFFEC) && mmd2->effector) {
- FluidEffectorSettings *mes = mmd2->effector;
- int subframes = mes->subframes;
+ if ((fmd2->type & MOD_FLUID_TYPE_EFFEC) && fmd2->effector) {
+ FluidEffectorSettings *fes = fmd2->effector;
+ int subframes = fes->subframes;
FluidObjectBB *bb = &bb_maps[effec_index];
/* Optimization: Skip this object under certain conditions. */
- if (escape_effectorobject(effecobj, mds, mes, frame)) {
+ if (escape_effectorobject(effecobj, fds, fes, frame)) {
continue;
}
@@ -1281,10 +1275,10 @@ static void compute_obstaclesemission(Scene *scene,
depsgraph, scene, effecobj, true, 5, BKE_scene_frame_get(scene), eModifierType_Fluid);
if (subframes) {
- obstacles_from_mesh(effecobj, mds, mes, &bb_temp, subframe_dt);
+ obstacles_from_mesh(effecobj, fds, fes, &bb_temp, subframe_dt);
}
else {
- obstacles_from_mesh(effecobj, mds, mes, bb, subframe_dt);
+ obstacles_from_mesh(effecobj, fds, fes, bb, subframe_dt);
}
/* If this we emitted with temp emission map in this loop (subframe emission), we combine
@@ -1302,7 +1296,7 @@ static void compute_obstaclesemission(Scene *scene,
static void update_obstacles(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
- FluidDomainSettings *mds,
+ FluidDomainSettings *fds,
float time_per_frame,
float frame_length,
int frame,
@@ -1311,15 +1305,15 @@ static void update_obstacles(Depsgraph *depsgraph,
FluidObjectBB *bb_maps = NULL;
Object **effecobjs = NULL;
uint numeffecobjs = 0;
- bool is_resume = (mds->cache_frame_pause_data == frame);
- bool is_first_frame = (frame == mds->cache_frame_start);
+ bool is_resume = (fds->cache_frame_pause_data == frame);
+ bool is_first_frame = (frame == fds->cache_frame_start);
effecobjs = BKE_collision_objects_create(
- depsgraph, ob, mds->effector_group, &numeffecobjs, eModifierType_Fluid);
+ depsgraph, ob, fds->effector_group, &numeffecobjs, eModifierType_Fluid);
/* Update all effector related flags and ensure that corresponding grids get initialized. */
- update_obstacleflags(mds, effecobjs, numeffecobjs);
- ensure_obstaclefields(mds);
+ update_obstacleflags(fds, effecobjs, numeffecobjs);
+ ensure_obstaclefields(fds);
/* Allocate effector map for each effector object. */
bb_maps = MEM_callocN(sizeof(struct FluidObjectBB) * numeffecobjs, "fluid_effector_bb_maps");
@@ -1332,27 +1326,27 @@ static void update_obstacles(Depsgraph *depsgraph,
effecobjs,
frame,
frame_length,
- mds,
+ fds,
numeffecobjs,
time_per_frame);
- float *vel_x = manta_get_ob_velocity_x(mds->fluid);
- float *vel_y = manta_get_ob_velocity_y(mds->fluid);
- float *vel_z = manta_get_ob_velocity_z(mds->fluid);
- float *vel_x_guide = manta_get_guide_velocity_x(mds->fluid);
- float *vel_y_guide = manta_get_guide_velocity_y(mds->fluid);
- float *vel_z_guide = manta_get_guide_velocity_z(mds->fluid);
- float *phi_obs_in = manta_get_phiobs_in(mds->fluid);
- float *phi_obsstatic_in = manta_get_phiobsstatic_in(mds->fluid);
- float *phi_guide_in = manta_get_phiguide_in(mds->fluid);
- float *num_obstacles = manta_get_num_obstacle(mds->fluid);
- float *num_guides = manta_get_num_guide(mds->fluid);
+ float *vel_x = manta_get_ob_velocity_x(fds->fluid);
+ float *vel_y = manta_get_ob_velocity_y(fds->fluid);
+ float *vel_z = manta_get_ob_velocity_z(fds->fluid);
+ float *vel_x_guide = manta_get_guide_velocity_x(fds->fluid);
+ float *vel_y_guide = manta_get_guide_velocity_y(fds->fluid);
+ float *vel_z_guide = manta_get_guide_velocity_z(fds->fluid);
+ float *phi_obs_in = manta_get_phiobs_in(fds->fluid);
+ float *phi_obsstatic_in = manta_get_phiobsstatic_in(fds->fluid);
+ float *phi_guide_in = manta_get_phiguide_in(fds->fluid);
+ float *num_obstacles = manta_get_num_obstacle(fds->fluid);
+ float *num_guides = manta_get_num_guide(fds->fluid);
uint z;
- bool use_adaptivedomain = (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
+ bool use_adaptivedomain = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
/* Grid reset before writing again. */
- for (z = 0; z < mds->res[0] * mds->res[1] * mds->res[2]; z++) {
+ for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) {
/* Use big value that's not inf to initialize levelset grids. */
if (phi_obs_in) {
@@ -1387,26 +1381,25 @@ static void update_obstacles(Depsgraph *depsgraph,
/* Prepare grids from effector objects. */
for (int effec_index = 0; effec_index < numeffecobjs; effec_index++) {
Object *effecobj = effecobjs[effec_index];
- FluidModifierData *mmd2 = (FluidModifierData *)BKE_modifiers_findby_type(effecobj,
+ FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(effecobj,
eModifierType_Fluid);
/* Sanity check. */
- if (!mmd2) {
+ if (!fmd2) {
continue;
}
/* Cannot use static mode with adaptive domain.
* The adaptive domain might expand and only later in the simulations discover the static
* object. */
- bool is_static = is_static_object(effecobj) &&
- ((mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0);
+ bool is_static = is_static_object(effecobj) && !use_adaptivedomain;
/* Check for initialized effector object. */
- if ((mmd2->type & MOD_FLUID_TYPE_EFFEC) && mmd2->effector) {
- FluidEffectorSettings *mes = mmd2->effector;
+ if ((fmd2->type & MOD_FLUID_TYPE_EFFEC) && fmd2->effector) {
+ FluidEffectorSettings *fes = fmd2->effector;
/* Optimization: Skip effector objects with disabled effec flag. */
- if ((mes->flags & FLUID_EFFECTOR_USE_EFFEC) == 0) {
+ if ((fes->flags & FLUID_EFFECTOR_USE_EFFEC) == 0) {
continue;
}
@@ -1429,20 +1422,20 @@ static void update_obstacles(Depsgraph *depsgraph,
e_index = manta_get_index(ex, bb->res[0], ey, bb->res[1], ez);
/* Get domain index. */
- dx = gx - mds->res_min[0];
- dy = gy - mds->res_min[1];
- dz = gz - mds->res_min[2];
- d_index = manta_get_index(dx, mds->res[0], dy, mds->res[1], dz);
+ dx = gx - fds->res_min[0];
+ dy = gy - fds->res_min[1];
+ dz = gz - fds->res_min[2];
+ d_index = manta_get_index(dx, fds->res[0], dy, fds->res[1], dz);
/* Make sure emission cell is inside the new domain boundary. */
- if (dx < 0 || dy < 0 || dz < 0 || dx >= mds->res[0] || dy >= mds->res[1] ||
- dz >= mds->res[2]) {
+ if (dx < 0 || dy < 0 || dz < 0 || dx >= fds->res[0] || dy >= fds->res[1] ||
+ dz >= fds->res[2]) {
continue;
}
- if (mes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
+ if (fes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
float *levelset = ((is_first_frame || is_resume) && is_static) ? phi_obsstatic_in :
phi_obs_in;
- apply_effector_fields(mes,
+ apply_effector_fields(fes,
d_index,
distance_map[e_index],
levelset,
@@ -1455,8 +1448,8 @@ static void update_obstacles(Depsgraph *depsgraph,
velocity_map[e_index * 3 + 2],
vel_z);
}
- if (mes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
- apply_effector_fields(mes,
+ if (fes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
+ apply_effector_fields(fes,
d_index,
distance_map[e_index],
phi_guide_in,
@@ -1489,7 +1482,7 @@ static void update_obstacles(Depsgraph *depsgraph,
* \{ */
typedef struct EmitFromParticlesData {
- FluidFlowSettings *mfs;
+ FluidFlowSettings *ffs;
KDTree_3d *tree;
FluidObjectBB *bb;
@@ -1505,7 +1498,7 @@ static void emit_from_particles_task_cb(void *__restrict userdata,
const TaskParallelTLS *__restrict UNUSED(tls))
{
EmitFromParticlesData *data = userdata;
- FluidFlowSettings *mfs = data->mfs;
+ FluidFlowSettings *ffs = data->ffs;
FluidObjectBB *bb = data->bb;
for (int x = data->min[0]; x < data->max[0]; x++) {
@@ -1524,9 +1517,9 @@ static void emit_from_particles_task_cb(void *__restrict userdata,
1.0f :
(1.0f - (nearest.dist - data->solid) / data->smooth);
/* Uses particle velocity as initial velocity for smoke. */
- if (mfs->flags & FLUID_FLOW_INITVELOCITY && (mfs->psys->part->phystype != PART_PHYS_NO)) {
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY && (ffs->psys->part->phystype != PART_PHYS_NO)) {
madd_v3_v3fl(
- &bb->velocity[index * 3], &data->particle_vel[nearest.index * 3], mfs->vel_multi);
+ &bb->velocity[index * 3], &data->particle_vel[nearest.index * 3], ffs->vel_multi);
}
}
}
@@ -1534,18 +1527,18 @@ static void emit_from_particles_task_cb(void *__restrict userdata,
}
static void emit_from_particles(Object *flow_ob,
- FluidDomainSettings *mds,
- FluidFlowSettings *mfs,
+ FluidDomainSettings *fds,
+ FluidFlowSettings *ffs,
FluidObjectBB *bb,
Depsgraph *depsgraph,
Scene *scene,
float dt)
{
- if (mfs && mfs->psys && mfs->psys->part &&
- ELEM(mfs->psys->part->type, PART_EMITTER, PART_FLUID)) // is particle system selected
+ if (ffs && ffs->psys && ffs->psys->part &&
+ ELEM(ffs->psys->part->type, PART_EMITTER, PART_FLUID)) // is particle system selected
{
ParticleSimulationData sim;
- ParticleSystem *psys = mfs->psys;
+ ParticleSystem *psys = ffs->psys;
float *particle_pos;
float *particle_vel;
int totpart = psys->totpart, totchild;
@@ -1554,7 +1547,7 @@ static void emit_from_particles(Object *flow_ob,
int bounds_margin = 1;
/* radius based flow */
- const float solid = mfs->particle_size * 0.5f;
+ const float solid = ffs->particle_size * 0.5f;
const float smooth = 0.5f; /* add 0.5 cells of linear falloff to reduce aliasing */
KDTree_3d *tree = NULL;
@@ -1590,7 +1583,7 @@ static void emit_from_particles(Object *flow_ob,
"manta_flow_particles_vel");
/* setup particle radius emission if enabled */
- if (mfs->flags & FLUID_FLOW_USE_PART_SIZE) {
+ if (ffs->flags & FLUID_FLOW_USE_PART_SIZE) {
tree = BLI_kdtree_3d_new(psys->totpart + psys->totchild);
bounds_margin = (int)ceil(solid + smooth);
}
@@ -1621,14 +1614,14 @@ static void emit_from_particles(Object *flow_ob,
/* location */
pos = &particle_pos[valid_particles * 3];
copy_v3_v3(pos, state.co);
- manta_pos_to_cell(mds, pos);
+ manta_pos_to_cell(fds, pos);
/* velocity */
vel = &particle_vel[valid_particles * 3];
copy_v3_v3(vel, state.vel);
- mul_mat3_m4_v3(mds->imat, &particle_vel[valid_particles * 3]);
+ mul_mat3_m4_v3(fds->imat, &particle_vel[valid_particles * 3]);
- if (mfs->flags & FLUID_FLOW_USE_PART_SIZE) {
+ if (ffs->flags & FLUID_FLOW_USE_PART_SIZE) {
BLI_kdtree_3d_insert(tree, valid_particles, pos);
}
@@ -1638,10 +1631,10 @@ static void emit_from_particles(Object *flow_ob,
}
/* set emission map */
- clamp_bounds_in_domain(mds, bb->min, bb->max, NULL, NULL, bounds_margin, dt);
- bb_allocateData(bb, mfs->flags & FLUID_FLOW_INITVELOCITY, true);
+ clamp_bounds_in_domain(fds, bb->min, bb->max, NULL, NULL, bounds_margin, dt);
+ bb_allocateData(bb, ffs->flags & FLUID_FLOW_INITVELOCITY, true);
- if (!(mfs->flags & FLUID_FLOW_USE_PART_SIZE)) {
+ if (!(ffs->flags & FLUID_FLOW_USE_PART_SIZE)) {
for (p = 0; p < valid_particles; p++) {
int cell[3];
size_t i = 0;
@@ -1667,8 +1660,8 @@ static void emit_from_particles(Object *flow_ob,
/* Add influence to emission map */
bb->influence[index] = 1.0f;
/* Uses particle velocity as initial velocity for smoke */
- if (mfs->flags & FLUID_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO)) {
- madd_v3_v3fl(&bb->velocity[index * 3], &particle_vel[p * 3], mfs->vel_multi);
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO)) {
+ madd_v3_v3fl(&bb->velocity[index * 3], &particle_vel[p * 3], ffs->vel_multi);
}
} // particles loop
}
@@ -1685,7 +1678,7 @@ static void emit_from_particles(Object *flow_ob,
BLI_kdtree_3d_balance(tree);
EmitFromParticlesData data = {
- .mfs = mfs,
+ .ffs = ffs,
.tree = tree,
.bb = bb,
.particle_vel = particle_vel,
@@ -1702,7 +1695,7 @@ static void emit_from_particles(Object *flow_ob,
BLI_task_parallel_range(min[2], max[2], &data, emit_from_particles_task_cb, &settings);
}
- if (mfs->flags & FLUID_FLOW_USE_PART_SIZE) {
+ if (ffs->flags & FLUID_FLOW_USE_PART_SIZE) {
BLI_kdtree_3d_free(tree);
}
@@ -1821,7 +1814,7 @@ static void update_distances(int index,
CLAMP(distance_map[index], -PHI_MAX, PHI_MAX);
}
-static void sample_mesh(FluidFlowSettings *mfs,
+static void sample_mesh(FluidFlowSettings *ffs,
const MVert *mvert,
const MLoop *mloop,
const MLoopTri *mlooptri,
@@ -1830,7 +1823,7 @@ static void sample_mesh(FluidFlowSettings *mfs,
float *velocity_map,
int index,
const int base_res[3],
- float flow_center[3],
+ const float flow_center[3],
BVHTreeFromMesh *tree_data,
const float ray_start[3],
const float *vert_vel,
@@ -1857,8 +1850,8 @@ static void sample_mesh(FluidFlowSettings *mfs,
const float surface_distance = 1.732;
nearest.dist_sq = surface_distance * surface_distance; /* find_nearest uses squared distance. */
- bool is_gas_flow = (mfs->type == FLUID_FLOW_TYPE_SMOKE || mfs->type == FLUID_FLOW_TYPE_FIRE ||
- mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE);
+ bool is_gas_flow = (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_FIRE ||
+ ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE);
/* Emission strength for gases will be computed below.
* For liquids it's not needed. Just set to non zero value
@@ -1866,7 +1859,7 @@ static void sample_mesh(FluidFlowSettings *mfs,
float emission_strength = (is_gas_flow) ? 0.0f : 1.0f;
/* Emission inside the flow object. */
- if (is_gas_flow && mfs->volume_density) {
+ if (is_gas_flow && ffs->volume_density) {
if (BLI_bvhtree_ray_cast(tree_data->tree,
ray_start,
ray_dir,
@@ -1891,7 +1884,7 @@ static void sample_mesh(FluidFlowSettings *mfs,
tree_data->raycast_callback,
tree_data);
if (hit.index != -1) {
- volume_factor = mfs->volume_density;
+ volume_factor = ffs->volume_density;
}
}
}
@@ -1913,8 +1906,8 @@ static void sample_mesh(FluidFlowSettings *mfs,
/* Compute emission strength for smoke flow. */
if (is_gas_flow) {
/* Emission from surface is based on UI configurable distance value. */
- if (mfs->surface_distance) {
- emission_strength = sqrtf(nearest.dist_sq) / mfs->surface_distance;
+ if (ffs->surface_distance) {
+ emission_strength = sqrtf(nearest.dist_sq) / ffs->surface_distance;
CLAMP(emission_strength, 0.0f, 1.0f);
emission_strength = pow(1.0f - emission_strength, 0.5f);
}
@@ -1931,15 +1924,15 @@ static void sample_mesh(FluidFlowSettings *mfs,
}
/* Apply emission texture. */
- if ((mfs->flags & FLUID_FLOW_TEXTUREEMIT) && mfs->noise_texture) {
+ if ((ffs->flags & FLUID_FLOW_TEXTUREEMIT) && ffs->noise_texture) {
float tex_co[3] = {0};
TexResult texres;
- if (mfs->texture_type == FLUID_FLOW_TEXTURE_MAP_AUTO) {
- tex_co[0] = ((x - flow_center[0]) / base_res[0]) / mfs->texture_size;
- tex_co[1] = ((y - flow_center[1]) / base_res[1]) / mfs->texture_size;
- tex_co[2] = ((z - flow_center[2]) / base_res[2] - mfs->texture_offset) /
- mfs->texture_size;
+ if (ffs->texture_type == FLUID_FLOW_TEXTURE_MAP_AUTO) {
+ tex_co[0] = ((x - flow_center[0]) / base_res[0]) / ffs->texture_size;
+ tex_co[1] = ((y - flow_center[1]) / base_res[1]) / ffs->texture_size;
+ tex_co[2] = ((z - flow_center[2]) / base_res[2] - ffs->texture_offset) /
+ ffs->texture_size;
}
else if (mloopuv) {
const float *uv[3];
@@ -1952,18 +1945,18 @@ static void sample_mesh(FluidFlowSettings *mfs,
/* Map texure coord between -1.0f and 1.0f. */
tex_co[0] = tex_co[0] * 2.0f - 1.0f;
tex_co[1] = tex_co[1] * 2.0f - 1.0f;
- tex_co[2] = mfs->texture_offset;
+ tex_co[2] = ffs->texture_offset;
}
texres.nor = NULL;
- BKE_texture_get_value(NULL, mfs->noise_texture, tex_co, &texres, false);
+ BKE_texture_get_value(NULL, ffs->noise_texture, tex_co, &texres, false);
emission_strength *= texres.tin;
}
}
/* Initial velocity of flow object. Only compute velocity if emission is present. */
- if (mfs->flags & FLUID_FLOW_INITVELOCITY && velocity_map && emission_strength != 0.0) {
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY && velocity_map && emission_strength != 0.0) {
/* Apply normal directional velocity. */
- if (mfs->vel_normal) {
+ if (ffs->vel_normal) {
/* Interpolate vertex normal vectors to get nearest point normal. */
normal_short_to_float_v3(n1, mvert[v1].no);
normal_short_to_float_v3(n2, mvert[v2].no);
@@ -1972,26 +1965,26 @@ static void sample_mesh(FluidFlowSettings *mfs,
normalize_v3(hit_normal);
/* Apply normal directional velocity. */
- velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal;
- velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal;
- velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal;
+ velocity_map[index * 3] += hit_normal[0] * ffs->vel_normal;
+ velocity_map[index * 3 + 1] += hit_normal[1] * ffs->vel_normal;
+ velocity_map[index * 3 + 2] += hit_normal[2] * ffs->vel_normal;
}
/* Apply object velocity. */
- if (has_velocity && mfs->vel_multi) {
+ if (has_velocity && ffs->vel_multi) {
float hit_vel[3];
interp_v3_v3v3v3(
hit_vel, &vert_vel[v1 * 3], &vert_vel[v2 * 3], &vert_vel[v3 * 3], weights);
- velocity_map[index * 3] += hit_vel[0] * mfs->vel_multi;
- velocity_map[index * 3 + 1] += hit_vel[1] * mfs->vel_multi;
- velocity_map[index * 3 + 2] += hit_vel[2] * mfs->vel_multi;
+ velocity_map[index * 3] += hit_vel[0] * ffs->vel_multi;
+ velocity_map[index * 3 + 1] += hit_vel[1] * ffs->vel_multi;
+ velocity_map[index * 3 + 2] += hit_vel[2] * ffs->vel_multi;
# ifdef DEBUG_PRINT
/* Debugging: Print flow object velocities. */
printf("adding flow object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]);
# endif
}
- velocity_map[index * 3] += mfs->vel_coord[0];
- velocity_map[index * 3 + 1] += mfs->vel_coord[1];
- velocity_map[index * 3 + 2] += mfs->vel_coord[2];
+ velocity_map[index * 3] += ffs->vel_coord[0];
+ velocity_map[index * 3 + 1] += ffs->vel_coord[1];
+ velocity_map[index * 3 + 2] += ffs->vel_coord[2];
}
}
@@ -2000,8 +1993,8 @@ static void sample_mesh(FluidFlowSettings *mfs,
}
typedef struct EmitFromDMData {
- FluidDomainSettings *mds;
- FluidFlowSettings *mfs;
+ FluidDomainSettings *fds;
+ FluidFlowSettings *ffs;
const MVert *mvert;
const MLoop *mloop;
@@ -2034,9 +2027,9 @@ static void emit_from_mesh_task_cb(void *__restrict userdata,
/* Compute emission only for flow objects that produce fluid (i.e. skip outflow objects).
* Result in bb->influence. Also computes initial velocities. Result in bb->velocity. */
- if ((data->mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY) ||
- (data->mfs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW)) {
- sample_mesh(data->mfs,
+ if ((data->ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY) ||
+ (data->ffs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW)) {
+ sample_mesh(data->ffs,
data->mvert,
data->mloop,
data->mlooptri,
@@ -2044,7 +2037,7 @@ static void emit_from_mesh_task_cb(void *__restrict userdata,
bb->influence,
bb->velocity,
index,
- data->mds->base_res,
+ data->fds->base_res,
data->flow_center,
data->tree,
ray_start,
@@ -2062,16 +2055,16 @@ static void emit_from_mesh_task_cb(void *__restrict userdata,
bb->distances,
data->tree,
ray_start,
- data->mfs->surface_distance,
- data->mfs->flags & FLUID_FLOW_USE_PLANE_INIT);
+ data->ffs->surface_distance,
+ data->ffs->flags & FLUID_FLOW_USE_PLANE_INIT);
}
}
}
static void emit_from_mesh(
- Object *flow_ob, FluidDomainSettings *mds, FluidFlowSettings *mfs, FluidObjectBB *bb, float dt)
+ Object *flow_ob, FluidDomainSettings *fds, FluidFlowSettings *ffs, FluidObjectBB *bb, float dt)
{
- if (mfs->mesh) {
+ if (ffs->mesh) {
Mesh *me = NULL;
MVert *mvert = NULL;
const MLoopTri *mlooptri = NULL;
@@ -2084,13 +2077,13 @@ static void emit_from_mesh(
float *vert_vel = NULL;
bool has_velocity = false;
- int defgrp_index = mfs->vgroup_density - 1;
+ int defgrp_index = ffs->vgroup_density - 1;
float flow_center[3] = {0};
int min[3], max[3], res[3];
/* Copy mesh for thread safety as we modify it.
* Main issue is its VertArray being modified, then replaced and freed. */
- me = BKE_mesh_copy_for_eval(mfs->mesh, true);
+ me = BKE_mesh_copy_for_eval(ffs->mesh, true);
/* Duplicate vertices to modify. */
if (me->mvert) {
@@ -2104,17 +2097,17 @@ static void emit_from_mesh(
mlooptri = BKE_mesh_runtime_looptri_ensure(me);
numverts = me->totvert;
dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT);
- mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, mfs->uvlayer_name);
+ mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ffs->uvlayer_name);
- if (mfs->flags & FLUID_FLOW_INITVELOCITY) {
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
vert_vel = MEM_callocN(sizeof(float) * numverts * 3, "manta_flow_velocity");
- if (mfs->numverts != numverts || !mfs->verts_old) {
- if (mfs->verts_old) {
- MEM_freeN(mfs->verts_old);
+ if (ffs->numverts != numverts || !ffs->verts_old) {
+ if (ffs->verts_old) {
+ MEM_freeN(ffs->verts_old);
}
- mfs->verts_old = MEM_callocN(sizeof(float) * numverts * 3, "manta_flow_verts_old");
- mfs->numverts = numverts;
+ ffs->verts_old = MEM_callocN(sizeof(float) * numverts * 3, "manta_flow_verts_old");
+ ffs->numverts = numverts;
}
else {
has_velocity = true;
@@ -2127,37 +2120,37 @@ static void emit_from_mesh(
/* Vertex position. */
mul_m4_v3(flow_ob->obmat, mvert[i].co);
- manta_pos_to_cell(mds, mvert[i].co);
+ manta_pos_to_cell(fds, mvert[i].co);
/* Vertex normal. */
normal_short_to_float_v3(n, mvert[i].no);
mul_mat3_m4_v3(flow_ob->obmat, n);
- mul_mat3_m4_v3(mds->imat, n);
+ mul_mat3_m4_v3(fds->imat, n);
normalize_v3(n);
normal_float_to_short_v3(mvert[i].no, n);
/* Vertex velocity. */
- if (mfs->flags & FLUID_FLOW_INITVELOCITY) {
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
float co[3];
- add_v3fl_v3fl_v3i(co, mvert[i].co, mds->shift);
+ add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift);
if (has_velocity) {
- sub_v3_v3v3(&vert_vel[i * 3], co, &mfs->verts_old[i * 3]);
- mul_v3_fl(&vert_vel[i * 3], mds->dx / dt);
+ sub_v3_v3v3(&vert_vel[i * 3], co, &ffs->verts_old[i * 3]);
+ mul_v3_fl(&vert_vel[i * 3], fds->dx / dt);
}
- copy_v3_v3(&mfs->verts_old[i * 3], co);
+ copy_v3_v3(&ffs->verts_old[i * 3], co);
}
/* Calculate emission map bounds. */
bb_boundInsert(bb, mvert[i].co);
}
mul_m4_v3(flow_ob->obmat, flow_center);
- manta_pos_to_cell(mds, flow_center);
+ manta_pos_to_cell(fds, flow_center);
/* Set emission map.
* Use 3 cell diagonals as margin (3 * 1.732 = 5.196). */
int bounds_margin = (int)ceil(5.196);
- clamp_bounds_in_domain(mds, bb->min, bb->max, NULL, NULL, bounds_margin, dt);
- bb_allocateData(bb, mfs->flags & FLUID_FLOW_INITVELOCITY, true);
+ clamp_bounds_in_domain(fds, bb->min, bb->max, NULL, NULL, bounds_margin, dt);
+ bb_allocateData(bb, ffs->flags & FLUID_FLOW_INITVELOCITY, true);
/* Setup loop bounds. */
for (i = 0; i < 3; i++) {
@@ -2169,8 +2162,8 @@ static void emit_from_mesh(
if (BKE_bvhtree_from_mesh_get(&tree_data, me, BVHTREE_FROM_LOOPTRI, 4)) {
EmitFromDMData data = {
- .mds = mds,
- .mfs = mfs,
+ .fds = fds,
+ .ffs = ffs,
.mvert = mvert,
.mloop = mloop,
.mlooptri = mlooptri,
@@ -2212,7 +2205,7 @@ static void emit_from_mesh(
* \{ */
static void adaptive_domain_adjust(
- FluidDomainSettings *mds, Object *ob, FluidObjectBB *bb_maps, uint numflowobj, float dt)
+ FluidDomainSettings *fds, Object *ob, FluidObjectBB *bb_maps, uint numflowobj, float dt)
{
/* calculate domain shift for current frame */
int new_shift[3] = {0};
@@ -2222,57 +2215,57 @@ static void adaptive_domain_adjust(
mul_m4_v3(ob->obmat, ob_loc);
- sub_v3_v3v3(frame_shift_f, ob_loc, mds->prev_loc);
- copy_v3_v3(mds->prev_loc, ob_loc);
+ sub_v3_v3v3(frame_shift_f, ob_loc, fds->prev_loc);
+ copy_v3_v3(fds->prev_loc, ob_loc);
/* convert global space shift to local "cell" space */
- mul_mat3_m4_v3(mds->imat, frame_shift_f);
- frame_shift_f[0] = frame_shift_f[0] / mds->cell_size[0];
- frame_shift_f[1] = frame_shift_f[1] / mds->cell_size[1];
- frame_shift_f[2] = frame_shift_f[2] / mds->cell_size[2];
+ mul_mat3_m4_v3(fds->imat, frame_shift_f);
+ frame_shift_f[0] = frame_shift_f[0] / fds->cell_size[0];
+ frame_shift_f[1] = frame_shift_f[1] / fds->cell_size[1];
+ frame_shift_f[2] = frame_shift_f[2] / fds->cell_size[2];
/* add to total shift */
- add_v3_v3(mds->shift_f, frame_shift_f);
+ add_v3_v3(fds->shift_f, frame_shift_f);
/* convert to integer */
- total_shift[0] = (int)(floorf(mds->shift_f[0]));
- total_shift[1] = (int)(floorf(mds->shift_f[1]));
- total_shift[2] = (int)(floorf(mds->shift_f[2]));
+ total_shift[0] = (int)(floorf(fds->shift_f[0]));
+ total_shift[1] = (int)(floorf(fds->shift_f[1]));
+ total_shift[2] = (int)(floorf(fds->shift_f[2]));
int temp_shift[3];
- copy_v3_v3_int(temp_shift, mds->shift);
- sub_v3_v3v3_int(new_shift, total_shift, mds->shift);
- copy_v3_v3_int(mds->shift, total_shift);
+ copy_v3_v3_int(temp_shift, fds->shift);
+ sub_v3_v3v3_int(new_shift, total_shift, fds->shift);
+ copy_v3_v3_int(fds->shift, total_shift);
/* calculate new domain boundary points so that smoke doesn't slide on sub-cell movement */
- mds->p0[0] = mds->dp0[0] - mds->cell_size[0] * (mds->shift_f[0] - total_shift[0] - 0.5f);
- mds->p0[1] = mds->dp0[1] - mds->cell_size[1] * (mds->shift_f[1] - total_shift[1] - 0.5f);
- mds->p0[2] = mds->dp0[2] - mds->cell_size[2] * (mds->shift_f[2] - total_shift[2] - 0.5f);
- mds->p1[0] = mds->p0[0] + mds->cell_size[0] * mds->base_res[0];
- mds->p1[1] = mds->p0[1] + mds->cell_size[1] * mds->base_res[1];
- mds->p1[2] = mds->p0[2] + mds->cell_size[2] * mds->base_res[2];
+ fds->p0[0] = fds->dp0[0] - fds->cell_size[0] * (fds->shift_f[0] - total_shift[0] - 0.5f);
+ fds->p0[1] = fds->dp0[1] - fds->cell_size[1] * (fds->shift_f[1] - total_shift[1] - 0.5f);
+ fds->p0[2] = fds->dp0[2] - fds->cell_size[2] * (fds->shift_f[2] - total_shift[2] - 0.5f);
+ fds->p1[0] = fds->p0[0] + fds->cell_size[0] * fds->base_res[0];
+ fds->p1[1] = fds->p0[1] + fds->cell_size[1] * fds->base_res[1];
+ fds->p1[2] = fds->p0[2] + fds->cell_size[2] * fds->base_res[2];
/* adjust domain resolution */
- const int block_size = mds->noise_scale;
+ const int block_size = fds->noise_scale;
int min[3] = {32767, 32767, 32767}, max[3] = {-32767, -32767, -32767}, res[3];
int total_cells = 1, res_changed = 0, shift_changed = 0;
float min_vel[3], max_vel[3];
int x, y, z;
- float *density = manta_smoke_get_density(mds->fluid);
- float *fuel = manta_smoke_get_fuel(mds->fluid);
- float *bigdensity = manta_smoke_turbulence_get_density(mds->fluid);
- float *bigfuel = manta_smoke_turbulence_get_fuel(mds->fluid);
- float *vx = manta_get_velocity_x(mds->fluid);
- float *vy = manta_get_velocity_y(mds->fluid);
- float *vz = manta_get_velocity_z(mds->fluid);
+ float *density = manta_smoke_get_density(fds->fluid);
+ float *fuel = manta_smoke_get_fuel(fds->fluid);
+ float *bigdensity = manta_noise_get_density(fds->fluid);
+ float *bigfuel = manta_noise_get_fuel(fds->fluid);
+ float *vx = manta_get_velocity_x(fds->fluid);
+ float *vy = manta_get_velocity_y(fds->fluid);
+ float *vz = manta_get_velocity_z(fds->fluid);
int wt_res[3];
- if (mds->flags & FLUID_DOMAIN_USE_NOISE && mds->fluid) {
- manta_smoke_turbulence_get_res(mds->fluid, wt_res);
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
+ manta_noise_get_res(fds->fluid, wt_res);
}
INIT_MINMAX(min_vel, max_vel);
/* Calculate bounds for current domain content */
- for (x = mds->res_min[0]; x < mds->res_max[0]; x++) {
- for (y = mds->res_min[1]; y < mds->res_max[1]; y++) {
- for (z = mds->res_min[2]; z < mds->res_max[2]; z++) {
+ for (x = fds->res_min[0]; x < fds->res_max[0]; x++) {
+ for (y = fds->res_min[1]; y < fds->res_max[1]; y++) {
+ for (z = fds->res_min[2]; z < fds->res_max[2]; z++) {
int xn = x - new_shift[0];
int yn = y - new_shift[1];
int zn = z - new_shift[2];
@@ -2285,20 +2278,20 @@ static void adaptive_domain_adjust(
continue;
}
- index = manta_get_index(x - mds->res_min[0],
- mds->res[0],
- y - mds->res_min[1],
- mds->res[1],
- z - mds->res_min[2]);
+ index = manta_get_index(x - fds->res_min[0],
+ fds->res[0],
+ y - fds->res_min[1],
+ fds->res[1],
+ z - fds->res_min[2]);
max_den = (fuel) ? MAX2(density[index], fuel[index]) : density[index];
/* check high resolution bounds if max density isnt already high enough */
- if (max_den < mds->adapt_threshold && mds->flags & FLUID_DOMAIN_USE_NOISE && mds->fluid) {
+ if (max_den < fds->adapt_threshold && fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
int i, j, k;
/* high res grid index */
- int xx = (x - mds->res_min[0]) * block_size;
- int yy = (y - mds->res_min[1]) * block_size;
- int zz = (z - mds->res_min[2]) * block_size;
+ int xx = (x - fds->res_min[0]) * block_size;
+ int yy = (y - fds->res_min[1]) * block_size;
+ int zz = (z - fds->res_min[2]) * block_size;
for (i = 0; i < block_size; i++) {
for (j = 0; j < block_size; j++) {
@@ -2315,7 +2308,7 @@ static void adaptive_domain_adjust(
}
/* content bounds (use shifted coordinates) */
- if (max_den >= mds->adapt_threshold) {
+ if (max_den >= fds->adapt_threshold) {
if (min[0] > xn) {
min[0] = xn;
}
@@ -2371,7 +2364,7 @@ static void adaptive_domain_adjust(
float max_den = bb->influence[index];
/* density bounds */
- if (max_den >= mds->adapt_threshold) {
+ if (max_den >= fds->adapt_threshold) {
if (min[0] > x) {
min[0] = x;
}
@@ -2397,7 +2390,7 @@ static void adaptive_domain_adjust(
}
/* calculate new bounds based on these values */
- clamp_bounds_in_domain(mds, min, max, min_vel, max_vel, mds->adapt_margin + 1, dt);
+ clamp_bounds_in_domain(fds, min, max, min_vel, max_vel, fds->adapt_margin + 1, dt);
for (int i = 0; i < 3; i++) {
/* calculate new resolution */
@@ -2420,37 +2413,37 @@ static void adaptive_domain_adjust(
total_cells = 1;
break;
}
- if (min[i] != mds->res_min[i] || max[i] != mds->res_max[i]) {
+ if (min[i] != fds->res_min[i] || max[i] != fds->res_max[i]) {
res_changed = 1;
}
}
if (res_changed || shift_changed) {
BKE_fluid_reallocate_copy_fluid(
- mds, mds->res, res, mds->res_min, min, mds->res_max, temp_shift, total_shift);
+ fds, fds->res, res, fds->res_min, min, fds->res_max, temp_shift, total_shift);
/* set new domain dimensions */
- copy_v3_v3_int(mds->res_min, min);
- copy_v3_v3_int(mds->res_max, max);
- copy_v3_v3_int(mds->res, res);
- mds->total_cells = total_cells;
+ copy_v3_v3_int(fds->res_min, min);
+ copy_v3_v3_int(fds->res_max, max);
+ copy_v3_v3_int(fds->res, res);
+ fds->total_cells = total_cells;
/* Redo adapt time step in manta to refresh solver state (ie time variables) */
- manta_adapt_timestep(mds->fluid);
+ manta_adapt_timestep(fds->fluid);
}
/* update global size field with new bbox size */
/* volume bounds */
float minf[3], maxf[3], size[3];
- madd_v3fl_v3fl_v3fl_v3i(minf, mds->p0, mds->cell_size, mds->res_min);
- madd_v3fl_v3fl_v3fl_v3i(maxf, mds->p0, mds->cell_size, mds->res_max);
+ madd_v3fl_v3fl_v3fl_v3i(minf, fds->p0, fds->cell_size, fds->res_min);
+ madd_v3fl_v3fl_v3fl_v3i(maxf, fds->p0, fds->cell_size, fds->res_max);
/* calculate domain dimensions */
sub_v3_v3v3(size, maxf, minf);
/* apply object scale */
for (int i = 0; i < 3; i++) {
size[i] = fabsf(size[i] * ob->scale[i]);
}
- copy_v3_v3(mds->global_size, size);
+ copy_v3_v3(fds->global_size, size);
}
BLI_INLINE void apply_outflow_fields(int index,
@@ -2488,7 +2481,7 @@ BLI_INLINE void apply_outflow_fields(int index,
}
}
-BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs,
+BLI_INLINE void apply_inflow_fields(FluidFlowSettings *ffs,
float emission_value,
float distance_value,
int index,
@@ -2522,28 +2515,28 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs,
}
/* Set inflow for smoke from here on. */
- int absolute_flow = (mfs->flags & FLUID_FLOW_ABSOLUTE);
+ int absolute_flow = (ffs->flags & FLUID_FLOW_ABSOLUTE);
float dens_old = (density) ? density[index] : 0.0;
// float fuel_old = (fuel) ? fuel[index] : 0.0f; /* UNUSED */
- float dens_flow = (mfs->type == FLUID_FLOW_TYPE_FIRE) ? 0.0f : emission_value * mfs->density;
- float fuel_flow = (fuel) ? emission_value * mfs->fuel_amount : 0.0f;
+ float dens_flow = (ffs->type == FLUID_FLOW_TYPE_FIRE) ? 0.0f : emission_value * ffs->density;
+ float fuel_flow = (fuel) ? emission_value * ffs->fuel_amount : 0.0f;
/* Set heat inflow. */
if (heat && heat_in) {
if (emission_value > 0.0f) {
- heat_in[index] = ADD_IF_LOWER(heat[index], mfs->temperature);
+ heat_in[index] = ADD_IF_LOWER(heat[index], ffs->temperature);
}
}
/* Set density and fuel - absolute mode. */
if (absolute_flow) {
if (density && density_in) {
- if (mfs->type != FLUID_FLOW_TYPE_FIRE && dens_flow > density[index]) {
+ if (ffs->type != FLUID_FLOW_TYPE_FIRE && dens_flow > density[index]) {
/* Use MAX2 to preserve values from other emitters at this cell. */
density_in[index] = MAX2(dens_flow, density_in[index]);
}
}
if (fuel && fuel_in) {
- if (mfs->type != FLUID_FLOW_TYPE_SMOKE && fuel_flow && fuel_flow > fuel[index]) {
+ if (ffs->type != FLUID_FLOW_TYPE_SMOKE && fuel_flow && fuel_flow > fuel[index]) {
/* Use MAX2 to preserve values from other emitters at this cell. */
fuel_in[index] = MAX2(fuel_flow, fuel_in[index]);
}
@@ -2552,13 +2545,13 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs,
/* Set density and fuel - additive mode. */
else {
if (density && density_in) {
- if (mfs->type != FLUID_FLOW_TYPE_FIRE) {
+ if (ffs->type != FLUID_FLOW_TYPE_FIRE) {
density_in[index] += dens_flow;
CLAMP(density_in[index], 0.0f, 1.0f);
}
}
if (fuel && fuel_in) {
- if (mfs->type != FLUID_FLOW_TYPE_SMOKE && mfs->fuel_amount) {
+ if (ffs->type != FLUID_FLOW_TYPE_SMOKE && ffs->fuel_amount) {
fuel_in[index] += fuel_flow;
CLAMP(fuel_in[index], 0.0f, 10.0f);
}
@@ -2569,9 +2562,9 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs,
if (color_r && color_r_in) {
if (dens_flow) {
float total_dens = density[index] / (dens_old + dens_flow);
- color_r_in[index] = (color_r[index] + mfs->color[0] * dens_flow) * total_dens;
- color_g_in[index] = (color_g[index] + mfs->color[1] * dens_flow) * total_dens;
- color_b_in[index] = (color_b[index] + mfs->color[2] * dens_flow) * total_dens;
+ color_r_in[index] = (color_r[index] + ffs->color[0] * dens_flow) * total_dens;
+ color_g_in[index] = (color_g[index] + ffs->color[1] * dens_flow) * total_dens;
+ color_b_in[index] = (color_b[index] + ffs->color[2] * dens_flow) * total_dens;
}
}
@@ -2588,35 +2581,35 @@ BLI_INLINE void apply_inflow_fields(FluidFlowSettings *mfs,
}
}
-static void ensure_flowsfields(FluidDomainSettings *mds)
+static void ensure_flowsfields(FluidDomainSettings *fds)
{
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL) {
- manta_ensure_invelocity(mds->fluid, mds->mmd);
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL) {
+ manta_ensure_invelocity(fds->fluid, fds->fmd);
}
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW) {
- manta_ensure_outflow(mds->fluid, mds->mmd);
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW) {
+ manta_ensure_outflow(fds->fluid, fds->fmd);
}
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) {
- manta_smoke_ensure_heat(mds->fluid, mds->mmd);
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) {
+ manta_smoke_ensure_heat(fds->fluid, fds->fmd);
}
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
- manta_smoke_ensure_fire(mds->fluid, mds->mmd);
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
+ manta_smoke_ensure_fire(fds->fluid, fds->fmd);
}
- if (mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) {
+ if (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) {
/* initialize all smoke with "active_color" */
- manta_smoke_ensure_colors(mds->fluid, mds->mmd);
+ manta_smoke_ensure_colors(fds->fluid, fds->fmd);
}
- if (mds->type == FLUID_DOMAIN_TYPE_LIQUID &&
- (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY ||
- mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM ||
- mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER)) {
- manta_liquid_ensure_sndparts(mds->fluid, mds->mmd);
+ if (fds->type == FLUID_DOMAIN_TYPE_LIQUID &&
+ (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY ||
+ fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM ||
+ fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER)) {
+ manta_liquid_ensure_sndparts(fds->fluid, fds->fmd);
}
}
-static void update_flowsflags(FluidDomainSettings *mds, Object **flowobjs, int numflowobj)
+static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int numflowobj)
{
- int active_fields = mds->active_fields;
+ int active_fields = fds->active_fields;
uint flow_index;
/* First, remove all flags that we want to update. */
@@ -2627,95 +2620,95 @@ static void update_flowsflags(FluidDomainSettings *mds, Object **flowobjs, int n
/* Monitor active fields based on flow settings */
for (flow_index = 0; flow_index < numflowobj; flow_index++) {
Object *flow_ob = flowobjs[flow_index];
- FluidModifierData *mmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flow_ob,
+ FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flow_ob,
eModifierType_Fluid);
/* Sanity check. */
- if (!mmd2) {
+ if (!fmd2) {
continue;
}
- if ((mmd2->type & MOD_FLUID_TYPE_FLOW) && mmd2->flow) {
- FluidFlowSettings *mfs = mmd2->flow;
- if (!mfs) {
+ if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
+ FluidFlowSettings *ffs = fmd2->flow;
+ if (!ffs) {
break;
}
- if (mfs->flags & FLUID_FLOW_NEEDS_UPDATE) {
- mfs->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
- mds->cache_flag |= FLUID_DOMAIN_OUTDATED_DATA;
+ if (ffs->flags & FLUID_FLOW_NEEDS_UPDATE) {
+ ffs->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
+ fds->cache_flag |= FLUID_DOMAIN_OUTDATED_DATA;
}
- if (mfs->flags & FLUID_FLOW_INITVELOCITY) {
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
active_fields |= FLUID_DOMAIN_ACTIVE_INVEL;
}
- if (mfs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW) {
+ if (ffs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW) {
active_fields |= FLUID_DOMAIN_ACTIVE_OUTFLOW;
}
/* liquids done from here */
- if (mds->type == FLUID_DOMAIN_TYPE_LIQUID) {
+ if (fds->type == FLUID_DOMAIN_TYPE_LIQUID) {
continue;
}
/* activate heat field if flow produces any heat */
- if (mfs->temperature) {
+ if (ffs->temperature) {
active_fields |= FLUID_DOMAIN_ACTIVE_HEAT;
}
/* activate fuel field if flow adds any fuel */
- if (mfs->fuel_amount &&
- (mfs->type == FLUID_FLOW_TYPE_FIRE || mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) {
+ if (ffs->fuel_amount &&
+ (ffs->type == FLUID_FLOW_TYPE_FIRE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) {
active_fields |= FLUID_DOMAIN_ACTIVE_FIRE;
}
/* activate color field if flows add smoke with varying colors */
- if (mfs->density &&
- (mfs->type == FLUID_FLOW_TYPE_SMOKE || mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) {
+ if (ffs->density &&
+ (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) {
if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) {
- copy_v3_v3(mds->active_color, mfs->color);
+ copy_v3_v3(fds->active_color, ffs->color);
active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET;
}
- else if (!equals_v3v3(mds->active_color, mfs->color)) {
- copy_v3_v3(mds->active_color, mfs->color);
+ else if (!equals_v3v3(fds->active_color, ffs->color)) {
+ copy_v3_v3(fds->active_color, ffs->color);
active_fields |= FLUID_DOMAIN_ACTIVE_COLORS;
}
}
}
}
/* Monitor active fields based on domain settings */
- if (mds->type == FLUID_DOMAIN_TYPE_GAS && active_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
+ if (fds->type == FLUID_DOMAIN_TYPE_GAS && active_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
/* heat is always needed for fire */
active_fields |= FLUID_DOMAIN_ACTIVE_HEAT;
/* also activate colors if domain smoke color differs from active color */
if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) {
- copy_v3_v3(mds->active_color, mds->flame_smoke_color);
+ copy_v3_v3(fds->active_color, fds->flame_smoke_color);
active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET;
}
- else if (!equals_v3v3(mds->active_color, mds->flame_smoke_color)) {
- copy_v3_v3(mds->active_color, mds->flame_smoke_color);
+ else if (!equals_v3v3(fds->active_color, fds->flame_smoke_color)) {
+ copy_v3_v3(fds->active_color, fds->flame_smoke_color);
active_fields |= FLUID_DOMAIN_ACTIVE_COLORS;
}
}
- mds->active_fields = active_fields;
+ fds->active_fields = active_fields;
}
static bool escape_flowsobject(Object *flowobj,
- FluidDomainSettings *mds,
- FluidFlowSettings *mfs,
+ FluidDomainSettings *fds,
+ FluidFlowSettings *ffs,
int frame)
{
- bool use_velocity = (mfs->flags & FLUID_FLOW_INITVELOCITY);
+ bool use_velocity = (ffs->flags & FLUID_FLOW_INITVELOCITY);
bool is_static = is_static_object(flowobj);
- bool liquid_flow = mfs->type == FLUID_FLOW_TYPE_LIQUID;
- bool gas_flow = (mfs->type == FLUID_FLOW_TYPE_SMOKE || mfs->type == FLUID_FLOW_TYPE_FIRE ||
- mfs->type == FLUID_FLOW_TYPE_SMOKEFIRE);
- bool is_geometry = (mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
- bool is_inflow = (mfs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW);
- bool is_outflow = (mfs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW);
- bool use_flow = (mfs->flags & FLUID_FLOW_USE_INFLOW);
+ bool liquid_flow = ffs->type == FLUID_FLOW_TYPE_LIQUID;
+ bool gas_flow = (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_FIRE ||
+ ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE);
+ bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
+ bool is_inflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW);
+ bool is_outflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW);
+ bool use_flow = (ffs->flags & FLUID_FLOW_USE_INFLOW);
- bool liquid_domain = mds->type == FLUID_DOMAIN_TYPE_LIQUID;
- bool gas_domain = mds->type == FLUID_DOMAIN_TYPE_GAS;
- bool is_adaptive = (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
- bool is_resume = (mds->cache_frame_pause_data == frame);
- bool is_first_frame = (mds->cache_frame_start == frame);
+ bool liquid_domain = fds->type == FLUID_DOMAIN_TYPE_LIQUID;
+ bool gas_domain = fds->type == FLUID_DOMAIN_TYPE_GAS;
+ bool is_adaptive = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
+ bool is_resume = (fds->cache_frame_pause_data == frame);
+ bool is_first_frame = (fds->cache_frame_start == frame);
/* Cannot use static mode with adaptive domain.
* The adaptive domain might expand and only later discover the static object. */
@@ -2749,31 +2742,31 @@ static void compute_flowsemission(Scene *scene,
Object **flowobjs,
int frame,
float frame_length,
- FluidDomainSettings *mds,
+ FluidDomainSettings *fds,
uint numflowobjs,
float time_per_frame)
{
- bool is_first_frame = (frame == mds->cache_frame_start);
+ bool is_first_frame = (frame == fds->cache_frame_start);
/* Prepare flow emission maps. */
for (int flow_index = 0; flow_index < numflowobjs; flow_index++) {
Object *flowobj = flowobjs[flow_index];
- FluidModifierData *mmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flowobj,
+ FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flowobj,
eModifierType_Fluid);
/* Sanity check. */
- if (!mmd2) {
+ if (!fmd2) {
continue;
}
/* Check for initialized flow object. */
- if ((mmd2->type & MOD_FLUID_TYPE_FLOW) && mmd2->flow) {
- FluidFlowSettings *mfs = mmd2->flow;
- int subframes = mfs->subframes;
+ if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
+ FluidFlowSettings *ffs = fmd2->flow;
+ int subframes = ffs->subframes;
FluidObjectBB *bb = &bb_maps[flow_index];
/* Optimization: Skip this object under certain conditions. */
- if (escape_flowsobject(flowobj, mds, mfs, frame)) {
+ if (escape_flowsobject(flowobj, fds, ffs, frame)) {
continue;
}
@@ -2823,21 +2816,21 @@ static void compute_flowsemission(Scene *scene,
depsgraph, scene, flowobj, true, 5, BKE_scene_frame_get(scene), eModifierType_Fluid);
/* Emission from particles. */
- if (mfs->source == FLUID_FLOW_SOURCE_PARTICLES) {
+ if (ffs->source == FLUID_FLOW_SOURCE_PARTICLES) {
if (subframes) {
- emit_from_particles(flowobj, mds, mfs, &bb_temp, depsgraph, scene, subframe_dt);
+ emit_from_particles(flowobj, fds, ffs, &bb_temp, depsgraph, scene, subframe_dt);
}
else {
- emit_from_particles(flowobj, mds, mfs, bb, depsgraph, scene, subframe_dt);
+ emit_from_particles(flowobj, fds, ffs, bb, depsgraph, scene, subframe_dt);
}
}
/* Emission from mesh. */
- else if (mfs->source == FLUID_FLOW_SOURCE_MESH) {
+ else if (ffs->source == FLUID_FLOW_SOURCE_MESH) {
if (subframes) {
- emit_from_mesh(flowobj, mds, mfs, &bb_temp, subframe_dt);
+ emit_from_mesh(flowobj, fds, ffs, &bb_temp, subframe_dt);
}
else {
- emit_from_mesh(flowobj, mds, mfs, bb, subframe_dt);
+ emit_from_mesh(flowobj, fds, ffs, bb, subframe_dt);
}
}
else {
@@ -2848,7 +2841,7 @@ static void compute_flowsemission(Scene *scene,
* the temp map with the original emission map. */
if (subframes) {
/* Combine emission maps. */
- bb_combineMaps(bb, &bb_temp, !(mfs->flags & FLUID_FLOW_ABSOLUTE), sample_size);
+ bb_combineMaps(bb, &bb_temp, !(ffs->flags & FLUID_FLOW_ABSOLUTE), sample_size);
bb_freeData(&bb_temp);
}
}
@@ -2867,7 +2860,7 @@ static void compute_flowsemission(Scene *scene,
static void update_flowsfluids(struct Depsgraph *depsgraph,
Scene *scene,
Object *ob,
- FluidDomainSettings *mds,
+ FluidDomainSettings *fds,
float time_per_frame,
float frame_length,
int frame,
@@ -2876,15 +2869,15 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
FluidObjectBB *bb_maps = NULL;
Object **flowobjs = NULL;
uint numflowobjs = 0;
- bool is_resume = (mds->cache_frame_pause_data == frame);
- bool is_first_frame = (mds->cache_frame_start == frame);
+ bool is_resume = (fds->cache_frame_pause_data == frame);
+ bool is_first_frame = (fds->cache_frame_start == frame);
flowobjs = BKE_collision_objects_create(
- depsgraph, ob, mds->fluid_group, &numflowobjs, eModifierType_Fluid);
+ depsgraph, ob, fds->fluid_group, &numflowobjs, eModifierType_Fluid);
/* Update all flow related flags and ensure that corresponding grids get initialized. */
- update_flowsflags(mds, flowobjs, numflowobjs);
- ensure_flowsfields(mds);
+ update_flowsflags(fds, flowobjs, numflowobjs);
+ ensure_flowsfields(fds);
/* Allocate emission map for each flow object. */
bb_maps = MEM_callocN(sizeof(struct FluidObjectBB) * numflowobjs, "fluid_flow_bb_maps");
@@ -2897,44 +2890,44 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
flowobjs,
frame,
frame_length,
- mds,
+ fds,
numflowobjs,
time_per_frame);
/* Adjust domain size if needed. Only do this once for every frame. */
- if (mds->type == FLUID_DOMAIN_TYPE_GAS && mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
- adaptive_domain_adjust(mds, ob, bb_maps, numflowobjs, dt);
- }
-
- float *phi_in = manta_get_phi_in(mds->fluid);
- float *phistatic_in = manta_get_phistatic_in(mds->fluid);
- float *phiout_in = manta_get_phiout_in(mds->fluid);
- float *phioutstatic_in = manta_get_phioutstatic_in(mds->fluid);
-
- float *density = manta_smoke_get_density(mds->fluid);
- float *color_r = manta_smoke_get_color_r(mds->fluid);
- float *color_g = manta_smoke_get_color_g(mds->fluid);
- float *color_b = manta_smoke_get_color_b(mds->fluid);
- float *fuel = manta_smoke_get_fuel(mds->fluid);
- float *heat = manta_smoke_get_heat(mds->fluid);
- float *react = manta_smoke_get_react(mds->fluid);
-
- float *density_in = manta_smoke_get_density_in(mds->fluid);
- float *heat_in = manta_smoke_get_heat_in(mds->fluid);
- float *color_r_in = manta_smoke_get_color_r_in(mds->fluid);
- float *color_g_in = manta_smoke_get_color_g_in(mds->fluid);
- float *color_b_in = manta_smoke_get_color_b_in(mds->fluid);
- float *fuel_in = manta_smoke_get_fuel_in(mds->fluid);
- float *react_in = manta_smoke_get_react_in(mds->fluid);
- float *emission_in = manta_smoke_get_emission_in(mds->fluid);
-
- float *velx_initial = manta_get_in_velocity_x(mds->fluid);
- float *vely_initial = manta_get_in_velocity_y(mds->fluid);
- float *velz_initial = manta_get_in_velocity_z(mds->fluid);
+ if (fds->type == FLUID_DOMAIN_TYPE_GAS && fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
+ adaptive_domain_adjust(fds, ob, bb_maps, numflowobjs, dt);
+ }
+
+ float *phi_in = manta_get_phi_in(fds->fluid);
+ float *phistatic_in = manta_get_phistatic_in(fds->fluid);
+ float *phiout_in = manta_get_phiout_in(fds->fluid);
+ float *phioutstatic_in = manta_get_phioutstatic_in(fds->fluid);
+
+ float *density = manta_smoke_get_density(fds->fluid);
+ float *color_r = manta_smoke_get_color_r(fds->fluid);
+ float *color_g = manta_smoke_get_color_g(fds->fluid);
+ float *color_b = manta_smoke_get_color_b(fds->fluid);
+ float *fuel = manta_smoke_get_fuel(fds->fluid);
+ float *heat = manta_smoke_get_heat(fds->fluid);
+ float *react = manta_smoke_get_react(fds->fluid);
+
+ float *density_in = manta_smoke_get_density_in(fds->fluid);
+ float *heat_in = manta_smoke_get_heat_in(fds->fluid);
+ float *color_r_in = manta_smoke_get_color_r_in(fds->fluid);
+ float *color_g_in = manta_smoke_get_color_g_in(fds->fluid);
+ float *color_b_in = manta_smoke_get_color_b_in(fds->fluid);
+ float *fuel_in = manta_smoke_get_fuel_in(fds->fluid);
+ float *react_in = manta_smoke_get_react_in(fds->fluid);
+ float *emission_in = manta_smoke_get_emission_in(fds->fluid);
+
+ float *velx_initial = manta_get_in_velocity_x(fds->fluid);
+ float *vely_initial = manta_get_in_velocity_y(fds->fluid);
+ float *velz_initial = manta_get_in_velocity_z(fds->fluid);
uint z;
/* Grid reset before writing again. */
- for (z = 0; z < mds->res[0] * mds->res[1] * mds->res[2]; z++) {
+ for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) {
/* Only reset static phi on first frame, dynamic phi gets reset every time. */
if (phistatic_in && is_first_frame) {
phistatic_in[z] = PHI_MAX;
@@ -2978,23 +2971,23 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
/* Apply emission data for every flow object. */
for (int flow_index = 0; flow_index < numflowobjs; flow_index++) {
Object *flowobj = flowobjs[flow_index];
- FluidModifierData *mmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flowobj,
+ FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flowobj,
eModifierType_Fluid);
/* Sanity check. */
- if (!mmd2) {
+ if (!fmd2) {
continue;
}
/* Check for initialized flow object. */
- if ((mmd2->type & MOD_FLUID_TYPE_FLOW) && mmd2->flow) {
- FluidFlowSettings *mfs = mmd2->flow;
+ if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
+ FluidFlowSettings *ffs = fmd2->flow;
- bool is_inflow = (mfs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW);
- bool is_geometry = (mfs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
- bool is_outflow = (mfs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW);
+ bool is_inflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW);
+ bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
+ bool is_outflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW);
bool is_static = is_static_object(flowobj) &&
- ((mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0);
+ ((fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0);
FluidObjectBB *bb = &bb_maps[flow_index];
float *velocity_map = bb->velocity;
@@ -3015,13 +3008,13 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
e_index = manta_get_index(ex, bb->res[0], ey, bb->res[1], ez);
/* Get domain index. */
- dx = gx - mds->res_min[0];
- dy = gy - mds->res_min[1];
- dz = gz - mds->res_min[2];
- d_index = manta_get_index(dx, mds->res[0], dy, mds->res[1], dz);
+ dx = gx - fds->res_min[0];
+ dy = gy - fds->res_min[1];
+ dz = gz - fds->res_min[2];
+ d_index = manta_get_index(dx, fds->res[0], dy, fds->res[1], dz);
/* Make sure emission cell is inside the new domain boundary. */
- if (dx < 0 || dy < 0 || dz < 0 || dx >= mds->res[0] || dy >= mds->res[1] ||
- dz >= mds->res[2]) {
+ if (dx < 0 || dy < 0 || dz < 0 || dx >= fds->res[0] || dy >= fds->res[1] ||
+ dz >= fds->res[2]) {
continue;
}
@@ -3042,7 +3035,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
}
/* Do not apply inflow after the first frame when in geometry mode. */
else if (is_geometry && !is_first_frame) {
- apply_inflow_fields(mfs,
+ apply_inflow_fields(ffs,
0.0f,
PHI_MAX,
d_index,
@@ -3068,7 +3061,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
float *levelset = ((is_first_frame || is_resume) && is_static && !is_geometry) ?
phistatic_in :
phi_in;
- apply_inflow_fields(mfs,
+ apply_inflow_fields(ffs,
emission_map[e_index],
distance_map[e_index],
d_index,
@@ -3088,7 +3081,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
color_b,
levelset,
emission_in);
- if (mfs->flags & FLUID_FLOW_INITVELOCITY) {
+ if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
/* Use the initial velocity from the inflow object with the highest velocity for
* now. */
float vel_initial[3];
@@ -3119,7 +3112,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
typedef struct UpdateEffectorsData {
Scene *scene;
- FluidDomainSettings *mds;
+ FluidDomainSettings *fds;
ListBase *effectors;
float *density;
@@ -3139,14 +3132,14 @@ static void update_effectors_task_cb(void *__restrict userdata,
const TaskParallelTLS *__restrict UNUSED(tls))
{
UpdateEffectorsData *data = userdata;
- FluidDomainSettings *mds = data->mds;
+ FluidDomainSettings *fds = data->fds;
- for (int y = 0; y < mds->res[1]; y++) {
- for (int z = 0; z < mds->res[2]; z++) {
+ for (int y = 0; y < fds->res[1]; y++) {
+ for (int z = 0; z < fds->res[2]; z++) {
EffectedPoint epoint;
float mag;
float voxel_center[3] = {0, 0, 0}, vel[3] = {0, 0, 0}, retvel[3] = {0, 0, 0};
- const uint index = manta_get_index(x, mds->res[0], y, mds->res[1], z);
+ const uint index = manta_get_index(x, fds->res[0], y, fds->res[1], z);
if ((data->fuel && MAX2(data->density[index], data->fuel[index]) < FLT_EPSILON) ||
(data->density && data->density[index] < FLT_EPSILON) ||
@@ -3160,26 +3153,27 @@ static void update_effectors_task_cb(void *__restrict userdata,
vel[0] = data->velocity_x[index];
vel[1] = data->velocity_y[index];
vel[2] = data->velocity_z[index];
- mul_v3_fl(vel, mds->dx);
+ mul_v3_fl(vel, fds->dx);
/* convert vel to global space */
mag = len_v3(vel);
- mul_mat3_m4_v3(mds->obmat, vel);
+ mul_mat3_m4_v3(fds->obmat, vel);
normalize_v3(vel);
mul_v3_fl(vel, mag);
- voxel_center[0] = mds->p0[0] + mds->cell_size[0] * ((float)(x + mds->res_min[0]) + 0.5f);
- voxel_center[1] = mds->p0[1] + mds->cell_size[1] * ((float)(y + mds->res_min[1]) + 0.5f);
- voxel_center[2] = mds->p0[2] + mds->cell_size[2] * ((float)(z + mds->res_min[2]) + 0.5f);
- mul_m4_v3(mds->obmat, voxel_center);
+ voxel_center[0] = fds->p0[0] + fds->cell_size[0] * ((float)(x + fds->res_min[0]) + 0.5f);
+ voxel_center[1] = fds->p0[1] + fds->cell_size[1] * ((float)(y + fds->res_min[1]) + 0.5f);
+ voxel_center[2] = fds->p0[2] + fds->cell_size[2] * ((float)(z + fds->res_min[2]) + 0.5f);
+ mul_m4_v3(fds->obmat, voxel_center);
/* 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, fds->effector_weights, &epoint, retvel, NULL, NULL);
/* convert retvel to local space */
mag = len_v3(retvel);
- mul_mat3_m4_v3(mds->imat, retvel);
+ mul_mat3_m4_v3(fds->imat, retvel);
normalize_v3(retvel);
mul_v3_fl(retvel, mag);
@@ -3192,40 +3186,40 @@ static void update_effectors_task_cb(void *__restrict userdata,
}
static void update_effectors(
- Depsgraph *depsgraph, Scene *scene, Object *ob, FluidDomainSettings *mds, float UNUSED(dt))
+ Depsgraph *depsgraph, Scene *scene, Object *ob, FluidDomainSettings *fds, float UNUSED(dt))
{
ListBase *effectors;
/* make sure smoke flow influence is 0.0f */
- mds->effector_weights->weight[PFIELD_FLUIDFLOW] = 0.0f;
- effectors = BKE_effectors_create(depsgraph, ob, NULL, mds->effector_weights);
+ fds->effector_weights->weight[PFIELD_FLUIDFLOW] = 0.0f;
+ effectors = BKE_effectors_create(depsgraph, ob, NULL, fds->effector_weights);
if (effectors) {
// precalculate wind forces
UpdateEffectorsData data;
data.scene = scene;
- data.mds = mds;
+ data.fds = fds;
data.effectors = effectors;
- data.density = manta_smoke_get_density(mds->fluid);
- data.fuel = manta_smoke_get_fuel(mds->fluid);
- data.force_x = manta_get_force_x(mds->fluid);
- data.force_y = manta_get_force_y(mds->fluid);
- data.force_z = manta_get_force_z(mds->fluid);
- data.velocity_x = manta_get_velocity_x(mds->fluid);
- data.velocity_y = manta_get_velocity_y(mds->fluid);
- data.velocity_z = manta_get_velocity_z(mds->fluid);
- data.flags = manta_smoke_get_flags(mds->fluid);
- data.phi_obs_in = manta_get_phiobs_in(mds->fluid);
+ data.density = manta_smoke_get_density(fds->fluid);
+ data.fuel = manta_smoke_get_fuel(fds->fluid);
+ data.force_x = manta_get_force_x(fds->fluid);
+ data.force_y = manta_get_force_y(fds->fluid);
+ data.force_z = manta_get_force_z(fds->fluid);
+ data.velocity_x = manta_get_velocity_x(fds->fluid);
+ data.velocity_y = manta_get_velocity_y(fds->fluid);
+ data.velocity_z = manta_get_velocity_z(fds->fluid);
+ data.flags = manta_smoke_get_flags(fds->fluid);
+ data.phi_obs_in = manta_get_phiobs_in(fds->fluid);
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 2;
- BLI_task_parallel_range(0, mds->res[0], &data, update_effectors_task_cb, &settings);
+ BLI_task_parallel_range(0, fds->res[0], &data, update_effectors_task_cb, &settings);
}
BKE_effectors_free(effectors);
}
-static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Object *ob)
+static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Object *ob)
{
Mesh *me;
MVert *mverts;
@@ -3253,13 +3247,13 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
int i;
int num_verts, num_normals, num_faces;
- if (!mds->fluid) {
+ if (!fds->fluid) {
return NULL;
}
- num_verts = manta_liquid_get_num_verts(mds->fluid);
- num_normals = manta_liquid_get_num_normals(mds->fluid);
- num_faces = manta_liquid_get_num_triangles(mds->fluid);
+ num_verts = manta_liquid_get_num_verts(fds->fluid);
+ num_normals = manta_liquid_get_num_normals(fds->fluid);
+ num_faces = manta_liquid_get_num_triangles(fds->fluid);
# ifdef DEBUG_PRINT
/* Debugging: Print number of vertices, normals, and faces. */
@@ -3273,19 +3267,19 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
BLI_assert(num_verts == num_normals);
/* If needed, vertex velocities will be read too. */
- bool use_speedvectors = mds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS;
+ bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS;
FluidDomainVertexVelocity *velarray = NULL;
float time_mult = 25.f * DT_DEFAULT;
if (use_speedvectors) {
- if (mds->mesh_velocities) {
- MEM_freeN(mds->mesh_velocities);
+ if (fds->mesh_velocities) {
+ MEM_freeN(fds->mesh_velocities);
}
- mds->mesh_velocities = MEM_calloc_arrayN(
+ fds->mesh_velocities = MEM_calloc_arrayN(
num_verts, sizeof(FluidDomainVertexVelocity), "fluid_mesh_vertvelocities");
- mds->totvert = num_verts;
- velarray = mds->mesh_velocities;
+ fds->totvert = num_verts;
+ velarray = fds->mesh_velocities;
}
me = BKE_mesh_new_nomain(num_verts, 0, 0, num_faces * 3, num_faces);
@@ -3297,10 +3291,10 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
mloops = me->mloop;
/* Get size (dimension) but considering scaling scaling. */
- copy_v3_v3(cell_size_scaled, mds->cell_size);
+ copy_v3_v3(cell_size_scaled, fds->cell_size);
mul_v3_v3(cell_size_scaled, ob->scale);
- madd_v3fl_v3fl_v3fl_v3i(min, mds->p0, cell_size_scaled, mds->res_min);
- madd_v3fl_v3fl_v3fl_v3i(max, mds->p0, cell_size_scaled, mds->res_max);
+ madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min);
+ madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, cell_size_scaled, fds->res_max);
sub_v3_v3v3(size, max, min);
/* Biggest dimension will be used for upscaling. */
@@ -3312,9 +3306,9 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
co_scale[2] = max_size / ob->scale[2];
float co_offset[3];
- co_offset[0] = (mds->p0[0] + mds->p1[0]) / 2.0f;
- co_offset[1] = (mds->p0[1] + mds->p1[1]) / 2.0f;
- co_offset[2] = (mds->p0[2] + mds->p1[2]) / 2.0f;
+ co_offset[0] = (fds->p0[0] + fds->p1[0]) / 2.0f;
+ co_offset[1] = (fds->p0[1] + fds->p1[1]) / 2.0f;
+ co_offset[2] = (fds->p0[2] + fds->p1[2]) / 2.0f;
/* Normals. */
normals = MEM_callocN(sizeof(short) * num_normals * 3, "Fluidmesh_tmp_normals");
@@ -3323,21 +3317,17 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
for (i = 0, no_s = normals; i < num_verts && i < num_normals; i++, mverts++, no_s += 3) {
/* Vertices (data is normalized cube around domain origin). */
- mverts->co[0] = manta_liquid_get_vertex_x_at(mds->fluid, i);
- mverts->co[1] = manta_liquid_get_vertex_y_at(mds->fluid, i);
- mverts->co[2] = manta_liquid_get_vertex_z_at(mds->fluid, i);
-
- /* If reading raw data directly from manta, normalize now (e.g. during replay mode).
- * If reading data from files from disk, omit this normalization. */
- if (!manta_liquid_mesh_from_file(mds->fluid)) {
- // normalize to unit cube around 0
- mverts->co[0] -= ((float)mds->res[0] * mds->mesh_scale) * 0.5f;
- mverts->co[1] -= ((float)mds->res[1] * mds->mesh_scale) * 0.5f;
- mverts->co[2] -= ((float)mds->res[2] * mds->mesh_scale) * 0.5f;
- mverts->co[0] *= mds->dx / mds->mesh_scale;
- mverts->co[1] *= mds->dx / mds->mesh_scale;
- mverts->co[2] *= mds->dx / mds->mesh_scale;
- }
+ mverts->co[0] = manta_liquid_get_vertex_x_at(fds->fluid, i);
+ mverts->co[1] = manta_liquid_get_vertex_y_at(fds->fluid, i);
+ mverts->co[2] = manta_liquid_get_vertex_z_at(fds->fluid, i);
+
+ /* Adjust coordinates from Mantaflow to match viewport scaling. */
+ float tmp[3] = {(float)fds->res[0], (float)fds->res[1], (float)fds->res[2]};
+ /* Scale to unit cube around 0. */
+ mul_v3_fl(tmp, fds->mesh_scale * 0.5f);
+ sub_v3_v3(mverts->co, tmp);
+ /* Apply scaling of domain object. */
+ mul_v3_fl(mverts->co, fds->dx / fds->mesh_scale);
mul_v3_v3(mverts->co, co_scale);
add_v3_v3(mverts->co, co_offset);
@@ -3351,9 +3341,9 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
# endif
/* Normals (data is normalized cube around domain origin). */
- no[0] = manta_liquid_get_normal_x_at(mds->fluid, i);
- no[1] = manta_liquid_get_normal_y_at(mds->fluid, i);
- no[2] = manta_liquid_get_normal_z_at(mds->fluid, i);
+ no[0] = manta_liquid_get_normal_x_at(fds->fluid, i);
+ no[1] = manta_liquid_get_normal_y_at(fds->fluid, i);
+ no[2] = manta_liquid_get_normal_z_at(fds->fluid, i);
normal_float_to_short_v3(no_s, no);
# ifdef DEBUG_PRINT
@@ -3362,9 +3352,9 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
# endif
if (use_speedvectors) {
- velarray[i].vel[0] = manta_liquid_get_vertvel_x_at(mds->fluid, i) * (mds->dx / time_mult);
- velarray[i].vel[1] = manta_liquid_get_vertvel_y_at(mds->fluid, i) * (mds->dx / time_mult);
- velarray[i].vel[2] = manta_liquid_get_vertvel_z_at(mds->fluid, i) * (mds->dx / time_mult);
+ velarray[i].vel[0] = manta_liquid_get_vertvel_x_at(fds->fluid, i) * (fds->dx / time_mult);
+ velarray[i].vel[1] = manta_liquid_get_vertvel_y_at(fds->fluid, i) * (fds->dx / time_mult);
+ velarray[i].vel[2] = manta_liquid_get_vertvel_z_at(fds->fluid, i) * (fds->dx / time_mult);
# ifdef DEBUG_PRINT
/* Debugging: Print velocities of vertices. */
printf("velarray[%d].vel[0]: %f, velarray[%d].vel[1]: %f, velarray[%d].vel[2]: %f\n",
@@ -3387,9 +3377,9 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
mpolys->loopstart = i * 3;
mpolys->totloop = 3;
- mloops[0].v = manta_liquid_get_triangle_x_at(mds->fluid, i);
- mloops[1].v = manta_liquid_get_triangle_y_at(mds->fluid, i);
- mloops[2].v = manta_liquid_get_triangle_z_at(mds->fluid, i);
+ mloops[0].v = manta_liquid_get_triangle_x_at(fds->fluid, i);
+ mloops[1].v = manta_liquid_get_triangle_y_at(fds->fluid, i);
+ mloops[2].v = manta_liquid_get_triangle_z_at(fds->fluid, i);
# ifdef DEBUG_PRINT
/* Debugging: Print mesh faces. */
printf("mloops[0].v: %d, mloops[1].v: %d, mloops[2].v: %d\n",
@@ -3408,7 +3398,7 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj
return me;
}
-static Mesh *create_smoke_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Object *ob)
+static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Object *ob)
{
Mesh *result;
MVert *mverts;
@@ -3427,7 +3417,7 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obje
float ob_cache_loc[3] = {0};
/* Just copy existing mesh if there is no content or if the adaptive domain is not being used. */
- if (mds->total_cells <= 1 || (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0) {
+ if (fds->total_cells <= 1 || (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0) {
return BKE_mesh_copy_for_eval(orgmesh, false);
}
@@ -3438,8 +3428,8 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obje
if (num_verts) {
/* Volume bounds. */
- madd_v3fl_v3fl_v3fl_v3i(min, mds->p0, mds->cell_size, mds->res_min);
- madd_v3fl_v3fl_v3fl_v3i(max, mds->p0, mds->cell_size, mds->res_max);
+ madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, fds->cell_size, fds->res_min);
+ madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, fds->cell_size, fds->res_max);
/* Set vertices of smoke BB. Especially important, when BB changes (adaptive domain). */
/* Top slab */
@@ -3537,13 +3527,13 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obje
* it was originally simulated at (if object moves without manta step). */
invert_m4_m4(ob->imat, ob->obmat);
mul_m4_v3(ob->obmat, ob_loc);
- mul_m4_v3(mds->obmat, ob_cache_loc);
- sub_v3_v3v3(mds->obj_shift_f, ob_cache_loc, ob_loc);
+ mul_m4_v3(fds->obmat, ob_cache_loc);
+ sub_v3_v3v3(fds->obj_shift_f, ob_cache_loc, ob_loc);
/* Convert shift to local space and apply to vertices. */
- mul_mat3_m4_v3(ob->imat, mds->obj_shift_f);
+ mul_mat3_m4_v3(ob->imat, fds->obj_shift_f);
/* Apply shift to vertices. */
for (i = 0; i < num_verts; i++) {
- add_v3_v3(mverts[i].co, mds->obj_shift_f);
+ add_v3_v3(mverts[i].co, fds->obj_shift_f);
}
}
@@ -3553,49 +3543,49 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obje
}
static int manta_step(
- Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me, FluidModifierData *mmd, int frame)
+ Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me, FluidModifierData *fmd, int frame)
{
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
float dt, frame_length, time_total, time_total_old;
float time_per_frame;
bool init_resolution = true;
/* Store baking success - bake might be aborted anytime by user. */
int result = 1;
- int mode = mds->cache_type;
+ int mode = fds->cache_type;
bool mode_replay = (mode == FLUID_DOMAIN_CACHE_REPLAY);
/* Update object state. */
- invert_m4_m4(mds->imat, ob->obmat);
- copy_m4_m4(mds->obmat, ob->obmat);
+ invert_m4_m4(fds->imat, ob->obmat);
+ copy_m4_m4(fds->obmat, ob->obmat);
/* Gas domain might use adaptive domain. */
- if (mds->type == FLUID_DOMAIN_TYPE_GAS) {
- init_resolution = (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) != 0;
+ if (fds->type == FLUID_DOMAIN_TYPE_GAS) {
+ init_resolution = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) != 0;
}
- manta_set_domain_from_mesh(mds, ob, me, init_resolution);
+ manta_set_domain_from_mesh(fds, ob, me, init_resolution);
/* Use local variables for adaptive loop, dt can change. */
- frame_length = mds->frame_length;
- dt = mds->dt;
+ frame_length = fds->frame_length;
+ dt = fds->dt;
time_per_frame = 0;
- time_total = mds->time_total;
+ time_total = fds->time_total;
/* Keep track of original total time to correct small errors at end of step. */
- time_total_old = mds->time_total;
+ time_total_old = fds->time_total;
BLI_mutex_lock(&object_update_lock);
/* Loop as long as time_per_frame (sum of sub dt's) does not exceed actual framelength. */
while (time_per_frame + FLT_EPSILON < frame_length) {
- manta_adapt_timestep(mds->fluid);
- dt = manta_get_timestep(mds->fluid);
+ manta_adapt_timestep(fds->fluid);
+ dt = manta_get_timestep(fds->fluid);
/* Save adapted dt so that MANTA object can access it (important when adaptive domain creates
* new MANTA object). */
- mds->dt = dt;
+ fds->dt = dt;
/* Calculate inflow geometry. */
- update_flowsfluids(depsgraph, scene, ob, mds, time_per_frame, frame_length, frame, dt);
+ update_flowsfluids(depsgraph, scene, ob, fds, time_per_frame, frame_length, frame, dt);
/* If user requested stop, quit baking */
if (G.is_break && !mode_replay) {
@@ -3603,10 +3593,10 @@ static int manta_step(
break;
}
- manta_update_variables(mds->fluid, mmd);
+ manta_update_variables(fds->fluid, fmd);
/* Calculate obstacle geometry. */
- update_obstacles(depsgraph, scene, ob, mds, time_per_frame, frame_length, frame, dt);
+ update_obstacles(depsgraph, scene, ob, fds, time_per_frame, frame_length, frame, dt);
/* If user requested stop, quit baking */
if (G.is_break && !mode_replay) {
@@ -3615,23 +3605,23 @@ static int manta_step(
}
/* Only bake if the domain is bigger than one cell (important for adaptive domain). */
- if (mds->total_cells > 1) {
- update_effectors(depsgraph, scene, ob, mds, dt);
- manta_bake_data(mds->fluid, mmd, frame);
+ if (fds->total_cells > 1) {
+ update_effectors(depsgraph, scene, ob, fds, dt);
+ manta_bake_data(fds->fluid, fmd, frame);
}
/* Count for how long this while loop is running. */
time_per_frame += dt;
time_total += dt;
- mds->time_per_frame = time_per_frame;
- mds->time_total = time_total;
+ fds->time_per_frame = time_per_frame;
+ fds->time_total = time_total;
}
/* Total time must not exceed framecount times framelength. Correct tiny errors here. */
- CLAMP(mds->time_total, mds->time_total, time_total_old + mds->frame_length);
+ CLAMP(fds->time_total, fds->time_total, time_total_old + fds->frame_length);
- if (mds->type == FLUID_DOMAIN_TYPE_GAS && result) {
- manta_smoke_calc_transparency(mds, DEG_get_evaluated_view_layer(depsgraph));
+ if (fds->type == FLUID_DOMAIN_TYPE_GAS && result) {
+ manta_smoke_calc_transparency(fds, DEG_get_evaluated_view_layer(depsgraph));
}
BLI_mutex_unlock(&object_update_lock);
@@ -3639,115 +3629,136 @@ static int manta_step(
}
static void manta_guiding(
- Depsgraph *depsgraph, Scene *scene, Object *ob, FluidModifierData *mmd, int frame)
+ Depsgraph *depsgraph, Scene *scene, Object *ob, FluidModifierData *fmd, int frame)
{
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
float fps = scene->r.frs_sec / scene->r.frs_sec_base;
- float dt = DT_DEFAULT * (25.0f / fps) * mds->time_scale;
+ float dt = DT_DEFAULT * (25.0f / fps) * fds->time_scale;
BLI_mutex_lock(&object_update_lock);
- update_obstacles(depsgraph, scene, ob, mds, dt, dt, frame, dt);
- manta_bake_guiding(mds->fluid, mmd, frame);
+ update_obstacles(depsgraph, scene, ob, fds, dt, dt, frame, dt);
+ manta_bake_guiding(fds->fluid, fmd, frame);
BLI_mutex_unlock(&object_update_lock);
}
-static void BKE_fluid_modifier_processFlow(FluidModifierData *mmd,
+static void BKE_fluid_modifier_processFlow(FluidModifierData *fmd,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,
Mesh *me,
const int scene_framenr)
{
- if (scene_framenr >= mmd->time) {
- BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me);
+ if (scene_framenr >= fmd->time) {
+ BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me);
}
- if (mmd->flow) {
- if (mmd->flow->mesh) {
- BKE_id_free(NULL, mmd->flow->mesh);
+ if (fmd->flow) {
+ if (fmd->flow->mesh) {
+ BKE_id_free(NULL, fmd->flow->mesh);
}
- mmd->flow->mesh = BKE_mesh_copy_for_eval(me, false);
+ fmd->flow->mesh = BKE_mesh_copy_for_eval(me, false);
}
- if (scene_framenr > mmd->time) {
- mmd->time = scene_framenr;
+ if (scene_framenr > fmd->time) {
+ fmd->time = scene_framenr;
}
- else if (scene_framenr < mmd->time) {
- mmd->time = scene_framenr;
- BKE_fluid_modifier_reset_ex(mmd, false);
+ else if (scene_framenr < fmd->time) {
+ fmd->time = scene_framenr;
+ BKE_fluid_modifier_reset_ex(fmd, false);
}
}
-static void BKE_fluid_modifier_processEffector(FluidModifierData *mmd,
+static void BKE_fluid_modifier_processEffector(FluidModifierData *fmd,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,
Mesh *me,
const int scene_framenr)
{
- if (scene_framenr >= mmd->time) {
- BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me);
+ if (scene_framenr >= fmd->time) {
+ BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me);
}
- if (mmd->effector) {
- if (mmd->effector->mesh) {
- BKE_id_free(NULL, mmd->effector->mesh);
+ if (fmd->effector) {
+ if (fmd->effector->mesh) {
+ BKE_id_free(NULL, fmd->effector->mesh);
}
- mmd->effector->mesh = BKE_mesh_copy_for_eval(me, false);
+ fmd->effector->mesh = BKE_mesh_copy_for_eval(me, false);
}
- if (scene_framenr > mmd->time) {
- mmd->time = scene_framenr;
+ if (scene_framenr > fmd->time) {
+ fmd->time = scene_framenr;
}
- else if (scene_framenr < mmd->time) {
- mmd->time = scene_framenr;
- BKE_fluid_modifier_reset_ex(mmd, false);
+ else if (scene_framenr < fmd->time) {
+ fmd->time = scene_framenr;
+ BKE_fluid_modifier_reset_ex(fmd, false);
}
}
-static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
+static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,
Mesh *me,
const int scene_framenr)
{
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
Object *guide_parent = NULL;
Object **objs = NULL;
uint numobj = 0;
- FluidModifierData *mmd_parent = NULL;
+ FluidModifierData *fmd_parent = NULL;
bool is_startframe, has_advanced;
- is_startframe = (scene_framenr == mds->cache_frame_start);
- has_advanced = (scene_framenr == mmd->time + 1);
+ is_startframe = (scene_framenr == fds->cache_frame_start);
+ has_advanced = (scene_framenr == fmd->time + 1);
+ int mode = fds->cache_type;
/* Do not process modifier if current frame is out of cache range. */
- if (scene_framenr < mds->cache_frame_start || scene_framenr > mds->cache_frame_end) {
- return;
+ switch (mode) {
+ case FLUID_DOMAIN_CACHE_ALL:
+ case FLUID_DOMAIN_CACHE_MODULAR:
+ if (fds->cache_frame_offset > 0) {
+ if (scene_framenr < fds->cache_frame_start ||
+ scene_framenr > fds->cache_frame_end + fds->cache_frame_offset) {
+ return;
+ }
+ }
+ else {
+ if (scene_framenr < fds->cache_frame_start + fds->cache_frame_offset ||
+ scene_framenr > fds->cache_frame_end) {
+ return;
+ }
+ }
+ break;
+ case FLUID_DOMAIN_CACHE_REPLAY:
+ default:
+ if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end) {
+ return;
+ }
+ break;
}
/* Reset fluid if no fluid present. Also resets active fields. */
- if (!mds->fluid) {
- BKE_fluid_modifier_reset_ex(mmd, false);
+ if (!fds->fluid) {
+ BKE_fluid_modifier_reset_ex(fmd, false);
}
/* Ensure cache directory is not relative. */
const char *relbase = BKE_modifier_path_relbase_from_global(ob);
- BLI_path_abs(mds->cache_directory, relbase);
+ BLI_path_abs(fds->cache_directory, relbase);
/* Ensure that all flags are up to date before doing any baking and/or cache reading. */
objs = BKE_collision_objects_create(
- depsgraph, ob, mds->fluid_group, &numobj, eModifierType_Fluid);
- update_flowsflags(mds, objs, numobj);
+ depsgraph, ob, fds->fluid_group, &numobj, eModifierType_Fluid);
+ update_flowsflags(fds, objs, numobj);
if (objs) {
MEM_freeN(objs);
}
objs = BKE_collision_objects_create(
- depsgraph, ob, mds->effector_group, &numobj, eModifierType_Fluid);
- update_obstacleflags(mds, objs, numobj);
+ depsgraph, ob, fds->effector_group, &numobj, eModifierType_Fluid);
+ update_obstacleflags(fds, objs, numobj);
if (objs) {
MEM_freeN(objs);
}
@@ -3756,129 +3767,125 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
# if 0
/* If the just updated flags now carry the 'outdated' flag, reset the cache here!
* Plus sanity check: Do not clear cache on file load. */
- if (mds->cache_flag & FLUID_DOMAIN_OUTDATED_DATA &&
- ((mds->flags & FLUID_DOMAIN_FILE_LOAD) == 0)) {
- BKE_fluid_cache_free_all(mds, ob);
- BKE_fluid_modifier_reset_ex(mmd, false);
+ if (fds->cache_flag & FLUID_DOMAIN_OUTDATED_DATA &&
+ ((fds->flags & FLUID_DOMAIN_FILE_LOAD) == 0)) {
+ BKE_fluid_cache_free_all(fds, ob);
+ BKE_fluid_modifier_reset_ex(fmd, false);
}
# endif
/* Fluid domain init must not fail in order to continue modifier evaluation. */
- if (!mds->fluid && !BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me)) {
+ if (!fds->fluid && !BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me)) {
CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!");
return;
}
- BLI_assert(mds->fluid);
+ BLI_assert(fds->fluid);
/* Guiding parent res pointer needs initialization. */
- guide_parent = mds->guide_parent;
+ guide_parent = fds->guide_parent;
if (guide_parent) {
- mmd_parent = (FluidModifierData *)BKE_modifiers_findby_type(guide_parent, eModifierType_Fluid);
- if (mmd_parent && mmd_parent->domain) {
- copy_v3_v3_int(mds->guide_res, mmd_parent->domain->res);
+ fmd_parent = (FluidModifierData *)BKE_modifiers_findby_type(guide_parent, eModifierType_Fluid);
+ if (fmd_parent && fmd_parent->domain) {
+ copy_v3_v3_int(fds->guide_res, fmd_parent->domain->res);
}
}
/* Ensure that time parameters are initialized correctly before every step. */
float fps = scene->r.frs_sec / scene->r.frs_sec_base;
- mds->frame_length = DT_DEFAULT * (25.0f / fps) * mds->time_scale;
- mds->dt = mds->frame_length;
- mds->time_per_frame = 0;
+ fds->frame_length = DT_DEFAULT * (25.0f / fps) * fds->time_scale;
+ fds->dt = fds->frame_length;
+ fds->time_per_frame = 0;
/* Ensure that gravity is copied over every frame (could be keyframed). */
- if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
- copy_v3_v3(mds->gravity, scene->physics_settings.gravity);
- mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity);
- }
+ update_final_gravity(fds, scene);
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, fds->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;
bool with_smoke, with_liquid;
- with_smoke = mds->type == FLUID_DOMAIN_TYPE_GAS;
- with_liquid = mds->type == FLUID_DOMAIN_TYPE_LIQUID;
+ with_smoke = fds->type == FLUID_DOMAIN_TYPE_GAS;
+ with_liquid = fds->type == FLUID_DOMAIN_TYPE_LIQUID;
bool drops, bubble, floater;
- drops = mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY;
- bubble = mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE;
- floater = mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM;
-
- 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;
- with_noise = mds->flags & FLUID_DOMAIN_USE_NOISE;
- with_mesh = mds->flags & FLUID_DOMAIN_USE_MESH;
- with_guide = mds->flags & FLUID_DOMAIN_USE_GUIDE;
+ drops = fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY;
+ bubble = fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE;
+ floater = fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM;
+
+ bool with_resumable_cache = fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE;
+ bool with_script, with_noise, with_mesh, with_particles, with_guide;
+ with_script = fds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT;
+ with_noise = fds->flags & FLUID_DOMAIN_USE_NOISE;
+ with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
+ with_guide = fds->flags & FLUID_DOMAIN_USE_GUIDE;
with_particles = drops || bubble || floater;
bool has_data, has_noise, has_mesh, has_particles, has_guide, has_config;
- has_data = manta_has_data(mds->fluid, mmd, scene_framenr);
- has_noise = manta_has_noise(mds->fluid, mmd, scene_framenr);
- has_mesh = manta_has_mesh(mds->fluid, mmd, scene_framenr);
- has_particles = manta_has_particles(mds->fluid, mmd, scene_framenr);
- has_guide = manta_has_guiding(mds->fluid, mmd, scene_framenr, guide_parent);
+ has_data = manta_has_data(fds->fluid, fmd, scene_framenr);
+ has_noise = manta_has_noise(fds->fluid, fmd, scene_framenr);
+ has_mesh = manta_has_mesh(fds->fluid, fmd, scene_framenr);
+ has_particles = manta_has_particles(fds->fluid, fmd, scene_framenr);
+ has_guide = manta_has_guiding(fds->fluid, fmd, scene_framenr, guide_parent);
has_config = false;
bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
- baking_data = mds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
- baking_noise = mds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
- baking_mesh = mds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
- baking_particles = mds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
- baking_guide = mds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
+ baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
+ baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
+ baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
+ baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
+ baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
bool resume_data, resume_noise, resume_mesh, resume_particles, resume_guide;
- resume_data = (!is_startframe) && (mds->cache_frame_pause_data == scene_framenr);
- resume_noise = (!is_startframe) && (mds->cache_frame_pause_noise == scene_framenr);
- resume_mesh = (!is_startframe) && (mds->cache_frame_pause_mesh == scene_framenr);
- resume_particles = (!is_startframe) && (mds->cache_frame_pause_particles == scene_framenr);
- resume_guide = (!is_startframe) && (mds->cache_frame_pause_guide == scene_framenr);
+ resume_data = (!is_startframe) && (fds->cache_frame_pause_data == scene_framenr);
+ resume_noise = (!is_startframe) && (fds->cache_frame_pause_noise == scene_framenr);
+ resume_mesh = (!is_startframe) && (fds->cache_frame_pause_mesh == scene_framenr);
+ resume_particles = (!is_startframe) && (fds->cache_frame_pause_particles == scene_framenr);
+ resume_guide = (!is_startframe) && (fds->cache_frame_pause_guide == scene_framenr);
bool read_cache, bake_cache;
read_cache = false;
bake_cache = baking_data || baking_noise || baking_mesh || baking_particles || baking_guide;
bool next_data, next_noise, next_mesh, next_particles, next_guide;
- next_data = manta_has_data(mds->fluid, mmd, next_frame);
- next_noise = manta_has_noise(mds->fluid, mmd, next_frame);
- next_mesh = manta_has_mesh(mds->fluid, mmd, next_frame);
- next_particles = manta_has_particles(mds->fluid, mmd, next_frame);
- next_guide = manta_has_guiding(mds->fluid, mmd, next_frame, guide_parent);
+ next_data = manta_has_data(fds->fluid, fmd, next_frame);
+ next_noise = manta_has_noise(fds->fluid, fmd, next_frame);
+ next_mesh = manta_has_mesh(fds->fluid, fmd, next_frame);
+ next_particles = manta_has_particles(fds->fluid, fmd, next_frame);
+ next_guide = manta_has_guiding(fds->fluid, fmd, next_frame, guide_parent);
bool prev_data, prev_noise, prev_mesh, prev_particles, prev_guide;
- prev_data = manta_has_data(mds->fluid, mmd, prev_frame);
- prev_noise = manta_has_noise(mds->fluid, mmd, prev_frame);
- prev_mesh = manta_has_mesh(mds->fluid, mmd, prev_frame);
- prev_particles = manta_has_particles(mds->fluid, mmd, prev_frame);
- prev_guide = manta_has_guiding(mds->fluid, mmd, prev_frame, guide_parent);
+ prev_data = manta_has_data(fds->fluid, fmd, prev_frame);
+ prev_noise = manta_has_noise(fds->fluid, fmd, prev_frame);
+ prev_mesh = manta_has_mesh(fds->fluid, fmd, prev_frame);
+ prev_particles = manta_has_particles(fds->fluid, fmd, prev_frame);
+ prev_guide = manta_has_guiding(fds->fluid, fmd, prev_frame, guide_parent);
/* Unused for now. */
UNUSED_VARS(has_guide, prev_guide, next_mesh, next_guide);
bool with_gdomain;
- with_gdomain = (mds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN);
+ with_gdomain = (fds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN);
int o_res[3], o_min[3], o_max[3], o_shift[3];
- int mode = mds->cache_type;
/* 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) {
read_cache = true;
bake_cache = false;
+
+ /* Apply frame offset. */
+ data_frame -= fmd->domain->cache_frame_offset;
+ noise_frame -= fmd->domain->cache_frame_offset;
+ mesh_frame -= fmd->domain->cache_frame_offset;
+ particles_frame -= fmd->domain->cache_frame_offset;
break;
}
@@ -3901,10 +3908,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, fds->cache_frame_end);
/* Force to read cache as we're resuming the bake */
read_cache = true;
@@ -3928,102 +3935,77 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
break;
}
+ /* Adaptive domain needs to know about current state, so save it here. */
+ copy_v3_v3_int(o_res, fds->res);
+ copy_v3_v3_int(o_min, fds->res_min);
+ copy_v3_v3_int(o_max, fds->res_max);
+ copy_v3_v3_int(o_shift, fds->shift);
+
+ bool read_partial = false, read_all = false;
/* Try to read from cache and keep track of read success. */
if (read_cache) {
/* Read mesh cache. */
if (with_liquid && with_mesh) {
- has_config = manta_read_config(mds->fluid, mmd, mesh_frame);
+ has_config = manta_read_config(fds->fluid, fmd, 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(fds->fluid, fmd, mesh_frame);
}
/* Read particles cache. */
if (with_liquid && with_particles) {
- has_config = manta_read_config(mds->fluid, mmd, particles_frame);
+ has_config = manta_read_config(fds->fluid, fmd, 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(fds->fluid, fmd, particles_frame, read_all);
}
/* Read guide cache. */
if (with_guide) {
- FluidModifierData *mmd2 = (with_gdomain) ? mmd_parent : mmd;
- has_guide = manta_read_guiding(mds->fluid, mmd2, scene_framenr, with_gdomain);
+ FluidModifierData *fmd2 = (with_gdomain) ? fmd_parent : fmd;
+ has_guide = manta_read_guiding(fds->fluid, fmd2, scene_framenr, with_gdomain);
}
/* Read noise and data cache */
if (with_smoke && with_noise) {
- has_config = manta_read_config(mds->fluid, mmd, noise_frame);
+ has_config = manta_read_config(fds->fluid, fmd, noise_frame);
/* Only reallocate when just reading cache or when resuming during bake. */
- if ((!baking_noise || (baking_noise && resume_noise)) && has_config &&
- 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);
+ if (has_data && has_config && manta_needs_realloc(fds->fluid, fmd)) {
+ BKE_fluid_reallocate_copy_fluid(
+ fds, o_res, fds->res, o_min, fds->res_min, o_max, o_shift, fds->shift);
}
- /* When using the adaptive domain, copy all data that was read to a new fluid object. */
- if (with_adaptive && baking_noise) {
- /* Adaptive domain needs to know about current state, so save it, then copy. */
- copy_v3_v3_int(o_res, mds->res);
- copy_v3_v3_int(o_min, mds->res_min);
- copy_v3_v3_int(o_max, mds->res_max);
- copy_v3_v3_int(o_shift, mds->shift);
- if (has_config && manta_needs_realloc(mds->fluid, mmd)) {
- BKE_fluid_reallocate_copy_fluid(
- 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_noise;
+ read_all = !read_partial && with_resumable_cache;
+ has_noise = manta_read_noise(fds->fluid, fmd, noise_frame, read_all);
+
+ read_partial = !baking_data && !baking_noise && next_data && next_noise;
+ read_all = !read_partial && with_resumable_cache;
+ has_data = manta_read_data(fds->fluid, fmd, data_frame, read_all);
}
/* Read data cache only */
else {
- has_config = manta_read_config(mds->fluid, mmd, data_frame);
+ has_config = manta_read_config(fds->fluid, fmd, data_frame);
if (with_smoke) {
/* Read config and realloc fluid object if needed. */
- 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);
+ if (has_config && manta_needs_realloc(fds->fluid, fmd)) {
+ BKE_fluid_reallocate_fluid(fds, fds->res, 1);
}
}
+
+ read_partial = !baking_data && !baking_particles && !baking_mesh && next_data;
+ read_all = !read_partial && with_resumable_cache;
+ has_data = manta_read_data(fds->fluid, fmd, 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;
@@ -4052,126 +4034,130 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
/* Trigger bake calls individually */
if (bake_cache) {
/* Ensure fresh variables at every animation step */
- manta_update_variables(mds->fluid, mmd);
+ manta_update_variables(fds->fluid, fmd);
/* Export mantaflow python script on first frame (once only) and for any bake type */
if (with_script && is_startframe) {
if (with_smoke) {
- manta_smoke_export_script(mmd->domain->fluid, mmd);
+ manta_smoke_export_script(fmd->domain->fluid, fmd);
}
if (with_liquid) {
- manta_liquid_export_script(mmd->domain->fluid, mmd);
+ manta_liquid_export_script(fmd->domain->fluid, fmd);
}
}
if (baking_guide && with_guide) {
- manta_guiding(depsgraph, scene, ob, mmd, scene_framenr);
+ manta_guiding(depsgraph, scene, ob, fmd, scene_framenr);
}
if (baking_data) {
/* Only save baked data if all of it completed successfully. */
- if (manta_step(depsgraph, scene, ob, me, mmd, scene_framenr)) {
- manta_write_config(mds->fluid, mmd, scene_framenr);
- manta_write_data(mds->fluid, mmd, scene_framenr);
+ if (manta_step(depsgraph, scene, ob, me, fmd, scene_framenr)) {
+ manta_write_config(fds->fluid, fmd, scene_framenr);
+ manta_write_data(fds->fluid, fmd, scene_framenr);
}
}
if (has_data || baking_data) {
if (baking_noise && with_smoke && with_noise) {
/* Ensure that no bake occurs if domain was minimized by adaptive domain. */
- if (mds->total_cells > 1) {
- manta_bake_noise(mds->fluid, mmd, scene_framenr);
+ if (fds->total_cells > 1) {
+ manta_bake_noise(fds->fluid, fmd, scene_framenr);
}
- manta_write_noise(mds->fluid, mmd, scene_framenr);
+ manta_write_noise(fds->fluid, fmd, scene_framenr);
}
if (baking_mesh && with_liquid && with_mesh) {
- manta_bake_mesh(mds->fluid, mmd, scene_framenr);
+ manta_bake_mesh(fds->fluid, fmd, scene_framenr);
}
if (baking_particles && with_liquid && with_particles) {
- manta_bake_particles(mds->fluid, mmd, scene_framenr);
+ manta_bake_particles(fds->fluid, fmd, scene_framenr);
}
}
}
- mds->flags &= ~FLUID_DOMAIN_FILE_LOAD;
- mmd->time = scene_framenr;
+ fds->flags &= ~FLUID_DOMAIN_FILE_LOAD;
+ fmd->time = scene_framenr;
}
static void BKE_fluid_modifier_process(
- FluidModifierData *mmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me)
+ FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me)
{
const int scene_framenr = (int)DEG_get_ctime(depsgraph);
- if ((mmd->type & MOD_FLUID_TYPE_FLOW)) {
- BKE_fluid_modifier_processFlow(mmd, depsgraph, scene, ob, me, scene_framenr);
+ if ((fmd->type & MOD_FLUID_TYPE_FLOW)) {
+ BKE_fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr);
}
- else if (mmd->type & MOD_FLUID_TYPE_EFFEC) {
- BKE_fluid_modifier_processEffector(mmd, depsgraph, scene, ob, me, scene_framenr);
+ else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
+ BKE_fluid_modifier_processEffector(fmd, depsgraph, scene, ob, me, scene_framenr);
}
- else if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
- BKE_fluid_modifier_processDomain(mmd, depsgraph, scene, ob, me, scene_framenr);
+ else if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ BKE_fluid_modifier_processDomain(fmd, depsgraph, scene, ob, me, scene_framenr);
}
}
struct Mesh *BKE_fluid_modifier_do(
- FluidModifierData *mmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me)
+ FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me)
{
- /* Lock so preview render does not read smoke data while it gets modified. */
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
- BLI_rw_mutex_lock(mmd->domain->fluid_mutex, THREAD_LOCK_WRITE);
- }
-
- BKE_fluid_modifier_process(mmd, depsgraph, scene, ob, me);
-
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
- BLI_rw_mutex_unlock(mmd->domain->fluid_mutex);
- }
-
/* Optimization: Do not update viewport during bakes (except in replay mode)
* Reason: UI is locked and updated liquid / smoke geometry is not visible anyways. */
bool needs_viewport_update = false;
- if (mmd->domain) {
- FluidDomainSettings *mds = mmd->domain;
- /* Always update viewport in cache replay mode. */
- if (mds->cache_type == FLUID_DOMAIN_CACHE_REPLAY ||
- mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
- needs_viewport_update = true;
+ /* Optimization: Only process modifier if object is not being altered. */
+ if (!G.moving) {
+ /* Lock so preview render does not read smoke data while it gets modified. */
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
+ BLI_rw_mutex_lock(fmd->domain->fluid_mutex, THREAD_LOCK_WRITE);
}
- /* In other cache modes, only update the viewport when no bake is going on. */
- else {
- bool with_mesh;
- with_mesh = mds->flags & FLUID_DOMAIN_USE_MESH;
- bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
- baking_data = mds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
- baking_noise = mds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
- baking_mesh = mds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
- baking_particles = mds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
- baking_guide = mds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
-
- if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles &&
- !baking_guide) {
+
+ BKE_fluid_modifier_process(fmd, depsgraph, scene, ob, me);
+
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
+ BLI_rw_mutex_unlock(fmd->domain->fluid_mutex);
+ }
+
+ if (fmd->domain) {
+ FluidDomainSettings *fds = fmd->domain;
+
+ /* Always update viewport in cache replay mode. */
+ if (fds->cache_type == FLUID_DOMAIN_CACHE_REPLAY ||
+ fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
needs_viewport_update = true;
}
+ /* In other cache modes, only update the viewport when no bake is going on. */
+ else {
+ bool with_mesh;
+ with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
+ bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
+ baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
+ baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
+ baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
+ baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
+ baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
+
+ if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles &&
+ !baking_guide) {
+ needs_viewport_update = true;
+ }
+ }
}
}
Mesh *result = NULL;
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) {
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
if (needs_viewport_update) {
/* Return generated geometry depending on domain type. */
- if (mmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) {
- result = create_liquid_geometry(mmd->domain, me, ob);
+ if (fmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) {
+ result = create_liquid_geometry(fmd->domain, me, ob);
}
- if (mmd->domain->type == FLUID_DOMAIN_TYPE_GAS) {
- result = create_smoke_geometry(mmd->domain, me, ob);
+ if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) {
+ result = create_smoke_geometry(fmd->domain, me, ob);
}
}
/* Clear flag outside of locked block (above). */
- mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA;
- mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE;
- mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_MESH;
- mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_PARTICLES;
- mmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_GUIDE;
+ fmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA;
+ fmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE;
+ fmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_MESH;
+ fmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_PARTICLES;
+ fmd->domain->cache_flag &= ~FLUID_DOMAIN_OUTDATED_GUIDE;
}
if (!result) {
@@ -4192,7 +4178,7 @@ struct Mesh *BKE_fluid_modifier_do(
}
static float calc_voxel_transp(
- float *result, float *input, int res[3], int *pixel, float *t_ray, float correct)
+ float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct)
{
const size_t index = manta_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]);
@@ -4303,70 +4289,65 @@ static void bresenham_linie_3D(int x1,
cb(result, input, res, pixel, t_ray, correct);
}
-static void manta_smoke_calc_transparency(FluidDomainSettings *mds, ViewLayer *view_layer)
+static void manta_smoke_calc_transparency(FluidDomainSettings *fds, ViewLayer *view_layer)
{
float bv[6] = {0};
float light[3];
- int a, z, slabsize = mds->res[0] * mds->res[1], size = mds->res[0] * mds->res[1] * mds->res[2];
- float *density = manta_smoke_get_density(mds->fluid);
- float *shadow = manta_smoke_get_shadow(mds->fluid);
- float correct = -7.0f * mds->dx;
+ int slabsize = fds->res[0] * fds->res[1];
+ float *density = manta_smoke_get_density(fds->fluid);
+ float *shadow = manta_smoke_get_shadow(fds->fluid);
+ float correct = -7.0f * fds->dx;
if (!get_light(view_layer, light)) {
return;
}
- /* convert light pos to sim cell space */
- mul_m4_v3(mds->imat, light);
- light[0] = (light[0] - mds->p0[0]) / mds->cell_size[0] - 0.5f - (float)mds->res_min[0];
- light[1] = (light[1] - mds->p0[1]) / mds->cell_size[1] - 0.5f - (float)mds->res_min[1];
- light[2] = (light[2] - mds->p0[2]) / mds->cell_size[2] - 0.5f - (float)mds->res_min[2];
+ /* Convert light pos to sim cell space. */
+ mul_m4_v3(fds->imat, light);
+ light[0] = (light[0] - fds->p0[0]) / fds->cell_size[0] - 0.5f - (float)fds->res_min[0];
+ light[1] = (light[1] - fds->p0[1]) / fds->cell_size[1] - 0.5f - (float)fds->res_min[1];
+ light[2] = (light[2] - fds->p0[2]) / fds->cell_size[2] - 0.5f - (float)fds->res_min[2];
- for (a = 0; a < size; a++) {
- shadow[a] = -1.0f;
- }
-
- /* calculate domain bounds in sim cell space */
+ /* Calculate domain bounds in sim cell space. */
// 0,2,4 = 0.0f
- bv[1] = (float)mds->res[0]; // x
- bv[3] = (float)mds->res[1]; // y
- bv[5] = (float)mds->res[2]; // z
+ bv[1] = (float)fds->res[0]; // x
+ bv[3] = (float)fds->res[1]; // y
+ bv[5] = (float)fds->res[2]; // z
- for (z = 0; z < mds->res[2]; z++) {
+ for (int z = 0; z < fds->res[2]; z++) {
size_t index = z * slabsize;
- int x, y;
- for (y = 0; y < mds->res[1]; y++) {
- for (x = 0; x < mds->res[0]; x++, index++) {
+ for (int y = 0; y < fds->res[1]; y++) {
+ for (int x = 0; x < fds->res[0]; x++, index++) {
float voxel_center[3];
float pos[3];
int cell[3];
float t_ray = 1.0;
- if (shadow[index] >= 0.0f) {
- continue;
- }
+ /* Reset shadow value.*/
+ shadow[index] = -1.0f;
+
voxel_center[0] = (float)x;
voxel_center[1] = (float)y;
voxel_center[2] = (float)z;
- // get starting cell (light pos)
+ /* Get starting cell (light pos). */
if (BLI_bvhtree_bb_raycast(bv, light, voxel_center, pos) > FLT_EPSILON) {
- // we're outside -> use point on side of domain
+ /* We're outside -> use point on side of domain. */
cell[0] = (int)floor(pos[0]);
cell[1] = (int)floor(pos[1]);
cell[2] = (int)floor(pos[2]);
}
else {
- // we're inside -> use light itself
+ /* We're inside -> use light itself. */
cell[0] = (int)floor(light[0]);
cell[1] = (int)floor(light[1]);
cell[2] = (int)floor(light[2]);
}
- /* clamp within grid bounds */
- CLAMP(cell[0], 0, mds->res[0] - 1);
- CLAMP(cell[1], 0, mds->res[1] - 1);
- CLAMP(cell[2], 0, mds->res[2] - 1);
+ /* Clamp within grid bounds */
+ CLAMP(cell[0], 0, fds->res[0] - 1);
+ CLAMP(cell[1], 0, fds->res[1] - 1);
+ CLAMP(cell[2], 0, fds->res[2] - 1);
bresenham_linie_3D(cell[0],
cell[1],
@@ -4378,10 +4359,10 @@ static void manta_smoke_calc_transparency(FluidDomainSettings *mds, ViewLayer *v
calc_voxel_transp,
shadow,
density,
- mds->res,
+ fds->res,
correct);
- // convention -> from a RGBA float array, use G value for t_ray
+ /* Convention -> from a RGBA float array, use G value for t_ray. */
shadow[index] = t_ray;
}
}
@@ -4392,35 +4373,35 @@ static void manta_smoke_calc_transparency(FluidDomainSettings *mds, ViewLayer *v
* Returns fluid density or -1.0f if outside domain. */
float BKE_fluid_get_velocity_at(struct Object *ob, float position[3], float velocity[3])
{
- FluidModifierData *mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
zero_v3(velocity);
- if (mmd && (mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain && mmd->domain->fluid) {
- FluidDomainSettings *mds = mmd->domain;
+ if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain && fmd->domain->fluid) {
+ FluidDomainSettings *fds = fmd->domain;
float time_mult = 25.f * DT_DEFAULT;
- float size_mult = MAX3(mds->global_size[0], mds->global_size[1], mds->global_size[2]) /
- mds->maxres;
+ float size_mult = MAX3(fds->global_size[0], fds->global_size[1], fds->global_size[2]) /
+ fds->maxres;
float vel_mag;
float density = 0.0f, fuel = 0.0f;
float pos[3];
copy_v3_v3(pos, position);
- manta_pos_to_cell(mds, pos);
+ manta_pos_to_cell(fds, pos);
/* Check if position is outside domain max bounds. */
- if (pos[0] < mds->res_min[0] || pos[1] < mds->res_min[1] || pos[2] < mds->res_min[2]) {
+ if (pos[0] < fds->res_min[0] || pos[1] < fds->res_min[1] || pos[2] < fds->res_min[2]) {
return -1.0f;
}
- if (pos[0] > mds->res_max[0] || pos[1] > mds->res_max[1] || pos[2] > mds->res_max[2]) {
+ if (pos[0] > fds->res_max[0] || pos[1] > fds->res_max[1] || pos[2] > fds->res_max[2]) {
return -1.0f;
}
/* map pos between 0.0 - 1.0 */
- pos[0] = (pos[0] - mds->res_min[0]) / ((float)mds->res[0]);
- pos[1] = (pos[1] - mds->res_min[1]) / ((float)mds->res[1]);
- pos[2] = (pos[2] - mds->res_min[2]) / ((float)mds->res[2]);
+ pos[0] = (pos[0] - fds->res_min[0]) / ((float)fds->res[0]);
+ pos[1] = (pos[1] - fds->res_min[1]) / ((float)fds->res[1]);
+ pos[2] = (pos[2] - fds->res_min[2]) / ((float)fds->res[2]);
/* Check if position is outside active area. */
- if (mds->type == FLUID_DOMAIN_TYPE_GAS && mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
+ if (fds->type == FLUID_DOMAIN_TYPE_GAS && fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
if (pos[0] < 0.0f || pos[1] < 0.0f || pos[2] < 0.0f) {
return 0.0f;
}
@@ -4430,9 +4411,9 @@ float BKE_fluid_get_velocity_at(struct Object *ob, float position[3], float velo
}
/* Get interpolated velocity at given position. */
- velocity[0] = BLI_voxel_sample_trilinear(manta_get_velocity_x(mds->fluid), mds->res, pos);
- velocity[1] = BLI_voxel_sample_trilinear(manta_get_velocity_y(mds->fluid), mds->res, pos);
- velocity[2] = BLI_voxel_sample_trilinear(manta_get_velocity_z(mds->fluid), mds->res, pos);
+ velocity[0] = BLI_voxel_sample_trilinear(manta_get_velocity_x(fds->fluid), fds->res, pos);
+ velocity[1] = BLI_voxel_sample_trilinear(manta_get_velocity_y(fds->fluid), fds->res, pos);
+ velocity[2] = BLI_voxel_sample_trilinear(manta_get_velocity_z(fds->fluid), fds->res, pos);
/* Convert simulation units to Blender units. */
mul_v3_fl(velocity, size_mult);
@@ -4440,32 +4421,32 @@ float BKE_fluid_get_velocity_at(struct Object *ob, float position[3], float velo
/* Convert velocity direction to global space. */
vel_mag = len_v3(velocity);
- mul_mat3_m4_v3(mds->obmat, velocity);
+ mul_mat3_m4_v3(fds->obmat, velocity);
normalize_v3(velocity);
mul_v3_fl(velocity, vel_mag);
/* Use max value of fuel or smoke density. */
- density = BLI_voxel_sample_trilinear(manta_smoke_get_density(mds->fluid), mds->res, pos);
- if (manta_smoke_has_fuel(mds->fluid)) {
- fuel = BLI_voxel_sample_trilinear(manta_smoke_get_fuel(mds->fluid), mds->res, pos);
+ density = BLI_voxel_sample_trilinear(manta_smoke_get_density(fds->fluid), fds->res, pos);
+ if (manta_smoke_has_fuel(fds->fluid)) {
+ fuel = BLI_voxel_sample_trilinear(manta_smoke_get_fuel(fds->fluid), fds->res, pos);
}
return MAX2(density, fuel);
}
return -1.0f;
}
-int BKE_fluid_get_data_flags(FluidDomainSettings *mds)
+int BKE_fluid_get_data_flags(FluidDomainSettings *fds)
{
int flags = 0;
- if (mds->fluid) {
- if (manta_smoke_has_heat(mds->fluid)) {
+ if (fds->fluid) {
+ if (manta_smoke_has_heat(fds->fluid)) {
flags |= FLUID_DOMAIN_ACTIVE_HEAT;
}
- if (manta_smoke_has_fuel(mds->fluid)) {
+ if (manta_smoke_has_fuel(fds->fluid)) {
flags |= FLUID_DOMAIN_ACTIVE_FIRE;
}
- if (manta_smoke_has_colors(mds->fluid)) {
+ if (manta_smoke_has_colors(fds->fluid)) {
flags |= FLUID_DOMAIN_ACTIVE_COLORS;
}
}
@@ -4482,7 +4463,7 @@ void BKE_fluid_particle_system_create(struct Main *bmain,
{
ParticleSystem *psys;
ParticleSettings *part;
- ParticleSystemModifierData *pmmd;
+ ParticleSystemModifierData *pfmd;
/* add particle system */
part = BKE_particlesettings_add(bmain, pset_name);
@@ -4498,25 +4479,25 @@ void BKE_fluid_particle_system_create(struct Main *bmain,
BLI_addtail(&ob->particlesystem, psys);
/* add modifier */
- pmmd = (ParticleSystemModifierData *)BKE_modifier_new(eModifierType_ParticleSystem);
- BLI_strncpy(pmmd->modifier.name, psys_name, sizeof(pmmd->modifier.name));
- pmmd->psys = psys;
- BLI_addtail(&ob->modifiers, pmmd);
- BKE_modifier_unique_name(&ob->modifiers, (ModifierData *)pmmd);
+ pfmd = (ParticleSystemModifierData *)BKE_modifier_new(eModifierType_ParticleSystem);
+ BLI_strncpy(pfmd->modifier.name, psys_name, sizeof(pfmd->modifier.name));
+ pfmd->psys = psys;
+ BLI_addtail(&ob->modifiers, pfmd);
+ BKE_modifier_unique_name(&ob->modifiers, (ModifierData *)pfmd);
}
void BKE_fluid_particle_system_destroy(struct Object *ob, const int particle_type)
{
- ParticleSystemModifierData *pmmd;
+ ParticleSystemModifierData *pfmd;
ParticleSystem *psys, *next_psys;
for (psys = ob->particlesystem.first; psys; psys = next_psys) {
next_psys = psys->next;
if (psys->part->type == particle_type) {
/* clear modifier */
- pmmd = psys_get_modifier(ob, psys);
- BLI_remlink(&ob->modifiers, pmmd);
- BKE_modifier_free((ModifierData *)pmmd);
+ pfmd = psys_get_modifier(ob, psys);
+ BLI_remlink(&ob->modifiers, pfmd);
+ BKE_modifier_free((ModifierData *)pfmd);
/* clear particle system */
BLI_remlink(&ob->particlesystem, psys);
@@ -4605,13 +4586,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 +4598,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);
@@ -4671,616 +4644,620 @@ void BKE_fluid_effector_type_set(Object *UNUSED(object), FluidEffectorSettings *
* Use for versioning, even when fluids are disabled.
* \{ */
-static void BKE_fluid_modifier_freeDomain(FluidModifierData *mmd)
+static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd)
{
- if (mmd->domain) {
- if (mmd->domain->fluid) {
+ if (fmd->domain) {
+ if (fmd->domain->fluid) {
#ifdef WITH_FLUID
- manta_free(mmd->domain->fluid);
+ manta_free(fmd->domain->fluid);
#endif
}
- if (mmd->domain->fluid_mutex) {
- BLI_rw_mutex_free(mmd->domain->fluid_mutex);
+ if (fmd->domain->fluid_mutex) {
+ BLI_rw_mutex_free(fmd->domain->fluid_mutex);
}
- if (mmd->domain->effector_weights) {
- MEM_freeN(mmd->domain->effector_weights);
+ if (fmd->domain->effector_weights) {
+ MEM_freeN(fmd->domain->effector_weights);
}
- mmd->domain->effector_weights = NULL;
+ fmd->domain->effector_weights = NULL;
- if (!(mmd->modifier.flag & eModifierFlag_SharedCaches)) {
- BKE_ptcache_free_list(&(mmd->domain->ptcaches[0]));
- mmd->domain->point_cache[0] = NULL;
+ if (!(fmd->modifier.flag & eModifierFlag_SharedCaches)) {
+ BKE_ptcache_free_list(&(fmd->domain->ptcaches[0]));
+ fmd->domain->point_cache[0] = NULL;
}
- if (mmd->domain->mesh_velocities) {
- MEM_freeN(mmd->domain->mesh_velocities);
+ if (fmd->domain->mesh_velocities) {
+ MEM_freeN(fmd->domain->mesh_velocities);
}
- mmd->domain->mesh_velocities = NULL;
+ fmd->domain->mesh_velocities = NULL;
- if (mmd->domain->coba) {
- MEM_freeN(mmd->domain->coba);
+ if (fmd->domain->coba) {
+ MEM_freeN(fmd->domain->coba);
}
- MEM_freeN(mmd->domain);
- mmd->domain = NULL;
+ MEM_freeN(fmd->domain);
+ fmd->domain = NULL;
}
}
-static void BKE_fluid_modifier_freeFlow(FluidModifierData *mmd)
+static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd)
{
- if (mmd->flow) {
- if (mmd->flow->mesh) {
- BKE_id_free(NULL, mmd->flow->mesh);
+ if (fmd->flow) {
+ if (fmd->flow->mesh) {
+ BKE_id_free(NULL, fmd->flow->mesh);
}
- mmd->flow->mesh = NULL;
+ fmd->flow->mesh = NULL;
- if (mmd->flow->verts_old) {
- MEM_freeN(mmd->flow->verts_old);
+ if (fmd->flow->verts_old) {
+ MEM_freeN(fmd->flow->verts_old);
}
- mmd->flow->verts_old = NULL;
- mmd->flow->numverts = 0;
- mmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
+ fmd->flow->verts_old = NULL;
+ fmd->flow->numverts = 0;
+ fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
- MEM_freeN(mmd->flow);
- mmd->flow = NULL;
+ MEM_freeN(fmd->flow);
+ fmd->flow = NULL;
}
}
-static void BKE_fluid_modifier_freeEffector(FluidModifierData *mmd)
+static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd)
{
- if (mmd->effector) {
- if (mmd->effector->mesh) {
- BKE_id_free(NULL, mmd->effector->mesh);
+ if (fmd->effector) {
+ if (fmd->effector->mesh) {
+ BKE_id_free(NULL, fmd->effector->mesh);
}
- mmd->effector->mesh = NULL;
+ fmd->effector->mesh = NULL;
- if (mmd->effector->verts_old) {
- MEM_freeN(mmd->effector->verts_old);
+ if (fmd->effector->verts_old) {
+ MEM_freeN(fmd->effector->verts_old);
}
- mmd->effector->verts_old = NULL;
- mmd->effector->numverts = 0;
- mmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
+ fmd->effector->verts_old = NULL;
+ fmd->effector->numverts = 0;
+ fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
- MEM_freeN(mmd->effector);
- mmd->effector = NULL;
+ MEM_freeN(fmd->effector);
+ fmd->effector = NULL;
}
}
-static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *mmd, bool need_lock)
+static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock)
{
- if (!mmd) {
+ if (!fmd) {
return;
}
- if (mmd->domain) {
- if (mmd->domain->fluid) {
+ if (fmd->domain) {
+ if (fmd->domain->fluid) {
if (need_lock) {
- BLI_rw_mutex_lock(mmd->domain->fluid_mutex, THREAD_LOCK_WRITE);
+ BLI_rw_mutex_lock(fmd->domain->fluid_mutex, THREAD_LOCK_WRITE);
}
#ifdef WITH_FLUID
- manta_free(mmd->domain->fluid);
+ manta_free(fmd->domain->fluid);
#endif
- mmd->domain->fluid = NULL;
+ fmd->domain->fluid = NULL;
if (need_lock) {
- BLI_rw_mutex_unlock(mmd->domain->fluid_mutex);
+ BLI_rw_mutex_unlock(fmd->domain->fluid_mutex);
}
}
- mmd->time = -1;
- mmd->domain->total_cells = 0;
- mmd->domain->active_fields = 0;
+ fmd->time = -1;
+ fmd->domain->total_cells = 0;
+ fmd->domain->active_fields = 0;
}
- else if (mmd->flow) {
- if (mmd->flow->verts_old) {
- MEM_freeN(mmd->flow->verts_old);
+ else if (fmd->flow) {
+ if (fmd->flow->verts_old) {
+ MEM_freeN(fmd->flow->verts_old);
}
- mmd->flow->verts_old = NULL;
- mmd->flow->numverts = 0;
- mmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
+ fmd->flow->verts_old = NULL;
+ fmd->flow->numverts = 0;
+ fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
}
- else if (mmd->effector) {
- if (mmd->effector->verts_old) {
- MEM_freeN(mmd->effector->verts_old);
+ else if (fmd->effector) {
+ if (fmd->effector->verts_old) {
+ MEM_freeN(fmd->effector->verts_old);
}
- mmd->effector->verts_old = NULL;
- mmd->effector->numverts = 0;
- mmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
+ fmd->effector->verts_old = NULL;
+ fmd->effector->numverts = 0;
+ fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
}
}
-void BKE_fluid_modifier_reset(struct FluidModifierData *mmd)
+void BKE_fluid_modifier_reset(struct FluidModifierData *fmd)
{
- BKE_fluid_modifier_reset_ex(mmd, true);
+ BKE_fluid_modifier_reset_ex(fmd, true);
}
-void BKE_fluid_modifier_free(FluidModifierData *mmd)
+void BKE_fluid_modifier_free(FluidModifierData *fmd)
{
- if (!mmd) {
+ if (!fmd) {
return;
}
- BKE_fluid_modifier_freeDomain(mmd);
- BKE_fluid_modifier_freeFlow(mmd);
- BKE_fluid_modifier_freeEffector(mmd);
+ BKE_fluid_modifier_freeDomain(fmd);
+ BKE_fluid_modifier_freeFlow(fmd);
+ BKE_fluid_modifier_freeEffector(fmd);
}
-void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd)
+void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd)
{
- if (!mmd) {
+ if (!fmd) {
return;
}
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
- if (mmd->domain) {
- BKE_fluid_modifier_freeDomain(mmd);
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ if (fmd->domain) {
+ BKE_fluid_modifier_freeDomain(fmd);
}
/* domain object data */
- mmd->domain = MEM_callocN(sizeof(FluidDomainSettings), "FluidDomain");
- mmd->domain->mmd = mmd;
- mmd->domain->effector_weights = BKE_effector_add_weights(NULL);
- mmd->domain->fluid = NULL;
- mmd->domain->fluid_mutex = BLI_rw_mutex_alloc();
- mmd->domain->force_group = NULL;
- mmd->domain->fluid_group = NULL;
- mmd->domain->effector_group = NULL;
+ fmd->domain = MEM_callocN(sizeof(FluidDomainSettings), "FluidDomain");
+ fmd->domain->fmd = fmd;
+ fmd->domain->effector_weights = BKE_effector_add_weights(NULL);
+ fmd->domain->fluid = NULL;
+ fmd->domain->fluid_mutex = BLI_rw_mutex_alloc();
+ fmd->domain->force_group = NULL;
+ fmd->domain->fluid_group = NULL;
+ fmd->domain->effector_group = NULL;
/* adaptive domain options */
- mmd->domain->adapt_margin = 4;
- mmd->domain->adapt_res = 0;
- mmd->domain->adapt_threshold = 0.02f;
+ fmd->domain->adapt_margin = 4;
+ fmd->domain->adapt_res = 0;
+ fmd->domain->adapt_threshold = 0.02f;
/* fluid domain options */
- mmd->domain->maxres = 32;
- mmd->domain->solver_res = 3;
- mmd->domain->border_collisions = 0; // open domain
- mmd->domain->flags = FLUID_DOMAIN_USE_DISSOLVE_LOG | FLUID_DOMAIN_USE_ADAPTIVE_TIME;
- mmd->domain->gravity[0] = 0.0f;
- mmd->domain->gravity[1] = 0.0f;
- mmd->domain->gravity[2] = -9.81f;
- mmd->domain->active_fields = 0;
- mmd->domain->type = FLUID_DOMAIN_TYPE_GAS;
- mmd->domain->boundary_width = 1;
+ fmd->domain->maxres = 32;
+ fmd->domain->solver_res = 3;
+ fmd->domain->border_collisions = 0; // open domain
+ fmd->domain->flags = FLUID_DOMAIN_USE_DISSOLVE_LOG | FLUID_DOMAIN_USE_ADAPTIVE_TIME;
+ fmd->domain->gravity[0] = 0.0f;
+ fmd->domain->gravity[1] = 0.0f;
+ fmd->domain->gravity[2] = -9.81f;
+ fmd->domain->active_fields = 0;
+ fmd->domain->type = FLUID_DOMAIN_TYPE_GAS;
+ fmd->domain->boundary_width = 1;
/* smoke domain options */
- mmd->domain->alpha = 1.0f;
- mmd->domain->beta = 1.0f;
- mmd->domain->diss_speed = 5;
- mmd->domain->vorticity = 0;
- mmd->domain->active_color[0] = 0.0f;
- mmd->domain->active_color[1] = 0.0f;
- mmd->domain->active_color[2] = 0.0f;
- mmd->domain->highres_sampling = SM_HRES_FULLSAMPLE;
+ fmd->domain->alpha = 1.0f;
+ fmd->domain->beta = 1.0f;
+ fmd->domain->diss_speed = 5;
+ fmd->domain->vorticity = 0;
+ fmd->domain->active_color[0] = 0.0f;
+ fmd->domain->active_color[1] = 0.0f;
+ fmd->domain->active_color[2] = 0.0f;
+ fmd->domain->highres_sampling = SM_HRES_FULLSAMPLE;
/* flame options */
- mmd->domain->burning_rate = 0.75f;
- mmd->domain->flame_smoke = 1.0f;
- mmd->domain->flame_vorticity = 0.5f;
- mmd->domain->flame_ignition = 1.5f;
- mmd->domain->flame_max_temp = 3.0f;
- mmd->domain->flame_smoke_color[0] = 0.7f;
- mmd->domain->flame_smoke_color[1] = 0.7f;
- mmd->domain->flame_smoke_color[2] = 0.7f;
+ fmd->domain->burning_rate = 0.75f;
+ fmd->domain->flame_smoke = 1.0f;
+ fmd->domain->flame_vorticity = 0.5f;
+ fmd->domain->flame_ignition = 1.5f;
+ fmd->domain->flame_max_temp = 3.0f;
+ fmd->domain->flame_smoke_color[0] = 0.7f;
+ fmd->domain->flame_smoke_color[1] = 0.7f;
+ fmd->domain->flame_smoke_color[2] = 0.7f;
/* noise options */
- mmd->domain->noise_strength = 1.0;
- mmd->domain->noise_pos_scale = 2.0f;
- mmd->domain->noise_time_anim = 0.1f;
- mmd->domain->noise_scale = 2;
- mmd->domain->noise_type = FLUID_NOISE_TYPE_WAVELET;
+ fmd->domain->noise_strength = 1.0;
+ fmd->domain->noise_pos_scale = 2.0f;
+ fmd->domain->noise_time_anim = 0.1f;
+ fmd->domain->noise_scale = 2;
+ fmd->domain->noise_type = FLUID_NOISE_TYPE_WAVELET;
/* liquid domain options */
- mmd->domain->simulation_method = FLUID_DOMAIN_METHOD_FLIP;
- mmd->domain->flip_ratio = 0.97f;
- mmd->domain->particle_randomness = 0.1f;
- mmd->domain->particle_number = 2;
- mmd->domain->particle_minimum = 8;
- mmd->domain->particle_maximum = 16;
- mmd->domain->particle_radius = 1.0f;
- mmd->domain->particle_band_width = 3.0f;
- mmd->domain->fractions_threshold = 0.05f;
+ fmd->domain->simulation_method = FLUID_DOMAIN_METHOD_FLIP;
+ fmd->domain->flip_ratio = 0.97f;
+ fmd->domain->particle_randomness = 0.1f;
+ fmd->domain->particle_number = 2;
+ fmd->domain->particle_minimum = 8;
+ fmd->domain->particle_maximum = 16;
+ fmd->domain->particle_radius = 1.0f;
+ fmd->domain->particle_band_width = 3.0f;
+ fmd->domain->fractions_threshold = 0.05f;
+ fmd->domain->sys_particle_maximum = 0;
/* diffusion options*/
- mmd->domain->surface_tension = 0.0f;
- mmd->domain->viscosity_base = 1.0f;
- mmd->domain->viscosity_exponent = 6.0f;
+ fmd->domain->surface_tension = 0.0f;
+ fmd->domain->viscosity_base = 1.0f;
+ fmd->domain->viscosity_exponent = 6.0f;
/* mesh options */
- mmd->domain->mesh_velocities = NULL;
- mmd->domain->mesh_concave_upper = 3.5f;
- mmd->domain->mesh_concave_lower = 0.4f;
- mmd->domain->mesh_particle_radius = 2.0;
- mmd->domain->mesh_smoothen_pos = 1;
- mmd->domain->mesh_smoothen_neg = 1;
- mmd->domain->mesh_scale = 2;
- mmd->domain->totvert = 0;
- mmd->domain->mesh_generator = FLUID_DOMAIN_MESH_IMPROVED;
+ fmd->domain->mesh_velocities = NULL;
+ fmd->domain->mesh_concave_upper = 3.5f;
+ fmd->domain->mesh_concave_lower = 0.4f;
+ fmd->domain->mesh_particle_radius = 2.0;
+ fmd->domain->mesh_smoothen_pos = 1;
+ fmd->domain->mesh_smoothen_neg = 1;
+ fmd->domain->mesh_scale = 2;
+ fmd->domain->totvert = 0;
+ fmd->domain->mesh_generator = FLUID_DOMAIN_MESH_IMPROVED;
/* secondary particle options */
- mmd->domain->sndparticle_tau_min_wc = 2.0;
- mmd->domain->sndparticle_tau_max_wc = 8.0;
- mmd->domain->sndparticle_tau_min_ta = 5.0;
- mmd->domain->sndparticle_tau_max_ta = 20.0;
- mmd->domain->sndparticle_tau_min_k = 1.0;
- mmd->domain->sndparticle_tau_max_k = 5.0;
- mmd->domain->sndparticle_k_wc = 200;
- mmd->domain->sndparticle_k_ta = 40;
- mmd->domain->sndparticle_k_b = 0.5;
- mmd->domain->sndparticle_k_d = 0.6;
- mmd->domain->sndparticle_l_min = 10.0;
- mmd->domain->sndparticle_l_max = 25.0;
- mmd->domain->sndparticle_boundary = SNDPARTICLE_BOUNDARY_DELETE;
- mmd->domain->sndparticle_combined_export = SNDPARTICLE_COMBINED_EXPORT_OFF;
- mmd->domain->sndparticle_potential_radius = 2;
- mmd->domain->sndparticle_update_radius = 2;
- mmd->domain->particle_type = 0;
- mmd->domain->particle_scale = 1;
+ fmd->domain->sndparticle_tau_min_wc = 2.0;
+ fmd->domain->sndparticle_tau_max_wc = 8.0;
+ fmd->domain->sndparticle_tau_min_ta = 5.0;
+ fmd->domain->sndparticle_tau_max_ta = 20.0;
+ fmd->domain->sndparticle_tau_min_k = 1.0;
+ fmd->domain->sndparticle_tau_max_k = 5.0;
+ fmd->domain->sndparticle_k_wc = 200;
+ fmd->domain->sndparticle_k_ta = 40;
+ fmd->domain->sndparticle_k_b = 0.5;
+ fmd->domain->sndparticle_k_d = 0.6;
+ fmd->domain->sndparticle_l_min = 10.0;
+ fmd->domain->sndparticle_l_max = 25.0;
+ fmd->domain->sndparticle_boundary = SNDPARTICLE_BOUNDARY_DELETE;
+ fmd->domain->sndparticle_combined_export = SNDPARTICLE_COMBINED_EXPORT_OFF;
+ fmd->domain->sndparticle_potential_radius = 2;
+ fmd->domain->sndparticle_update_radius = 2;
+ fmd->domain->particle_type = 0;
+ fmd->domain->particle_scale = 1;
/* fluid guide options */
- mmd->domain->guide_parent = NULL;
- mmd->domain->guide_alpha = 2.0f;
- mmd->domain->guide_beta = 5;
- mmd->domain->guide_vel_factor = 2.0f;
- mmd->domain->guide_source = FLUID_DOMAIN_GUIDE_SRC_DOMAIN;
+ fmd->domain->guide_parent = NULL;
+ fmd->domain->guide_alpha = 2.0f;
+ fmd->domain->guide_beta = 5;
+ fmd->domain->guide_vel_factor = 2.0f;
+ fmd->domain->guide_source = FLUID_DOMAIN_GUIDE_SRC_DOMAIN;
/* cache options */
- mmd->domain->cache_frame_start = 1;
- mmd->domain->cache_frame_end = 250;
- mmd->domain->cache_frame_pause_data = 0;
- mmd->domain->cache_frame_pause_noise = 0;
- mmd->domain->cache_frame_pause_mesh = 0;
- mmd->domain->cache_frame_pause_particles = 0;
- mmd->domain->cache_frame_pause_guide = 0;
- mmd->domain->cache_flag = 0;
- mmd->domain->cache_type = FLUID_DOMAIN_CACHE_REPLAY;
- mmd->domain->cache_mesh_format = FLUID_DOMAIN_FILE_BIN_OBJECT;
+ fmd->domain->cache_frame_start = 1;
+ fmd->domain->cache_frame_end = 250;
+ fmd->domain->cache_frame_pause_data = 0;
+ fmd->domain->cache_frame_pause_noise = 0;
+ fmd->domain->cache_frame_pause_mesh = 0;
+ fmd->domain->cache_frame_pause_particles = 0;
+ fmd->domain->cache_frame_pause_guide = 0;
+ fmd->domain->cache_frame_offset = 0;
+ fmd->domain->cache_flag = 0;
+ fmd->domain->cache_type = FLUID_DOMAIN_CACHE_REPLAY;
+ fmd->domain->cache_mesh_format = FLUID_DOMAIN_FILE_BIN_OBJECT;
#ifdef WITH_OPENVDB
- mmd->domain->cache_data_format = FLUID_DOMAIN_FILE_OPENVDB;
- mmd->domain->cache_particle_format = FLUID_DOMAIN_FILE_OPENVDB;
- mmd->domain->cache_noise_format = FLUID_DOMAIN_FILE_OPENVDB;
+ fmd->domain->cache_data_format = FLUID_DOMAIN_FILE_OPENVDB;
+ fmd->domain->cache_particle_format = FLUID_DOMAIN_FILE_OPENVDB;
+ fmd->domain->cache_noise_format = FLUID_DOMAIN_FILE_OPENVDB;
#else
- mmd->domain->cache_data_format = FLUID_DOMAIN_FILE_UNI;
- mmd->domain->cache_particle_format = FLUID_DOMAIN_FILE_UNI;
- mmd->domain->cache_noise_format = FLUID_DOMAIN_FILE_UNI;
+ fmd->domain->cache_data_format = FLUID_DOMAIN_FILE_UNI;
+ fmd->domain->cache_particle_format = FLUID_DOMAIN_FILE_UNI;
+ fmd->domain->cache_noise_format = FLUID_DOMAIN_FILE_UNI;
#endif
char cache_name[64];
BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
BKE_modifier_path_init(
- mmd->domain->cache_directory, sizeof(mmd->domain->cache_directory), cache_name);
+ fmd->domain->cache_directory, sizeof(fmd->domain->cache_directory), cache_name);
/* time options */
- mmd->domain->time_scale = 1.0;
- mmd->domain->cfl_condition = 4.0;
- mmd->domain->timesteps_minimum = 1;
- mmd->domain->timesteps_maximum = 4;
+ fmd->domain->time_scale = 1.0;
+ fmd->domain->cfl_condition = 4.0;
+ fmd->domain->timesteps_minimum = 1;
+ fmd->domain->timesteps_maximum = 4;
/* display options */
- mmd->domain->slice_method = FLUID_DOMAIN_SLICE_VIEW_ALIGNED;
- mmd->domain->axis_slice_method = AXIS_SLICE_FULL;
- mmd->domain->slice_axis = 0;
- mmd->domain->interp_method = 0;
- mmd->domain->draw_velocity = false;
- mmd->domain->slice_per_voxel = 5.0f;
- mmd->domain->slice_depth = 0.5f;
- mmd->domain->display_thickness = 1.0f;
- mmd->domain->coba = NULL;
- mmd->domain->vector_scale = 1.0f;
- mmd->domain->vector_draw_type = VECTOR_DRAW_NEEDLE;
- mmd->domain->use_coba = false;
- mmd->domain->coba_field = FLUID_DOMAIN_FIELD_DENSITY;
+ fmd->domain->slice_method = FLUID_DOMAIN_SLICE_VIEW_ALIGNED;
+ fmd->domain->axis_slice_method = AXIS_SLICE_FULL;
+ fmd->domain->slice_axis = 0;
+ fmd->domain->interp_method = 0;
+ fmd->domain->draw_velocity = false;
+ fmd->domain->slice_per_voxel = 5.0f;
+ fmd->domain->slice_depth = 0.5f;
+ fmd->domain->display_thickness = 1.0f;
+ fmd->domain->coba = NULL;
+ fmd->domain->vector_scale = 1.0f;
+ fmd->domain->vector_draw_type = VECTOR_DRAW_NEEDLE;
+ fmd->domain->use_coba = false;
+ fmd->domain->coba_field = FLUID_DOMAIN_FIELD_DENSITY;
/* -- Deprecated / unsed options (below)-- */
/* pointcache options */
- BLI_listbase_clear(&mmd->domain->ptcaches[1]);
- mmd->domain->point_cache[0] = BKE_ptcache_add(&(mmd->domain->ptcaches[0]));
- mmd->domain->point_cache[0]->flag |= PTCACHE_DISK_CACHE;
- mmd->domain->point_cache[0]->step = 1;
- mmd->domain->point_cache[1] = NULL; /* Deprecated */
- mmd->domain->cache_comp = SM_CACHE_LIGHT;
- mmd->domain->cache_high_comp = SM_CACHE_LIGHT;
+ BLI_listbase_clear(&fmd->domain->ptcaches[1]);
+ fmd->domain->point_cache[0] = BKE_ptcache_add(&(fmd->domain->ptcaches[0]));
+ fmd->domain->point_cache[0]->flag |= PTCACHE_DISK_CACHE;
+ fmd->domain->point_cache[0]->step = 1;
+ fmd->domain->point_cache[1] = NULL; /* Deprecated */
+ fmd->domain->cache_comp = SM_CACHE_LIGHT;
+ fmd->domain->cache_high_comp = SM_CACHE_LIGHT;
/* OpenVDB cache options */
#ifdef WITH_OPENVDB_BLOSC
- mmd->domain->openvdb_comp = VDB_COMPRESSION_BLOSC;
+ fmd->domain->openvdb_compression = VDB_COMPRESSION_BLOSC;
#else
- mmd->domain->openvdb_comp = VDB_COMPRESSION_ZIP;
+ fmd->domain->openvdb_compression = VDB_COMPRESSION_ZIP;
#endif
- mmd->domain->clipping = 1e-6f;
- mmd->domain->data_depth = 0;
+ fmd->domain->clipping = 1e-6f;
+ fmd->domain->openvdb_data_depth = 0;
}
- else if (mmd->type & MOD_FLUID_TYPE_FLOW) {
- if (mmd->flow) {
- BKE_fluid_modifier_freeFlow(mmd);
+ else if (fmd->type & MOD_FLUID_TYPE_FLOW) {
+ if (fmd->flow) {
+ BKE_fluid_modifier_freeFlow(fmd);
}
/* flow object data */
- mmd->flow = MEM_callocN(sizeof(FluidFlowSettings), "MantaFlow");
- mmd->flow->mmd = mmd;
- mmd->flow->mesh = NULL;
- mmd->flow->psys = NULL;
- mmd->flow->noise_texture = NULL;
+ fmd->flow = MEM_callocN(sizeof(FluidFlowSettings), "MantaFlow");
+ fmd->flow->fmd = fmd;
+ fmd->flow->mesh = NULL;
+ fmd->flow->psys = NULL;
+ fmd->flow->noise_texture = NULL;
/* initial velocity */
- mmd->flow->verts_old = NULL;
- mmd->flow->numverts = 0;
- mmd->flow->vel_multi = 1.0f;
- mmd->flow->vel_normal = 0.0f;
- mmd->flow->vel_random = 0.0f;
- mmd->flow->vel_coord[0] = 0.0f;
- mmd->flow->vel_coord[1] = 0.0f;
- mmd->flow->vel_coord[2] = 0.0f;
+ fmd->flow->verts_old = NULL;
+ fmd->flow->numverts = 0;
+ fmd->flow->vel_multi = 1.0f;
+ fmd->flow->vel_normal = 0.0f;
+ fmd->flow->vel_random = 0.0f;
+ fmd->flow->vel_coord[0] = 0.0f;
+ fmd->flow->vel_coord[1] = 0.0f;
+ fmd->flow->vel_coord[2] = 0.0f;
/* emission */
- mmd->flow->density = 1.0f;
- mmd->flow->color[0] = 0.7f;
- mmd->flow->color[1] = 0.7f;
- mmd->flow->color[2] = 0.7f;
- mmd->flow->fuel_amount = 1.0f;
- mmd->flow->temperature = 1.0f;
- mmd->flow->volume_density = 0.0f;
- mmd->flow->surface_distance = 1.5f;
- mmd->flow->particle_size = 1.0f;
- mmd->flow->subframes = 0;
+ fmd->flow->density = 1.0f;
+ fmd->flow->color[0] = 0.7f;
+ fmd->flow->color[1] = 0.7f;
+ fmd->flow->color[2] = 0.7f;
+ fmd->flow->fuel_amount = 1.0f;
+ fmd->flow->temperature = 1.0f;
+ fmd->flow->volume_density = 0.0f;
+ fmd->flow->surface_distance = 1.5f;
+ fmd->flow->particle_size = 1.0f;
+ fmd->flow->subframes = 0;
/* texture control */
- mmd->flow->source = FLUID_FLOW_SOURCE_MESH;
- mmd->flow->texture_size = 1.0f;
+ fmd->flow->source = FLUID_FLOW_SOURCE_MESH;
+ fmd->flow->texture_size = 1.0f;
- mmd->flow->type = FLUID_FLOW_TYPE_SMOKE;
- mmd->flow->behavior = FLUID_FLOW_BEHAVIOR_GEOMETRY;
- mmd->flow->flags = FLUID_FLOW_ABSOLUTE | FLUID_FLOW_USE_PART_SIZE | FLUID_FLOW_USE_INFLOW;
+ fmd->flow->type = FLUID_FLOW_TYPE_SMOKE;
+ fmd->flow->behavior = FLUID_FLOW_BEHAVIOR_GEOMETRY;
+ fmd->flow->flags = FLUID_FLOW_ABSOLUTE | FLUID_FLOW_USE_PART_SIZE | FLUID_FLOW_USE_INFLOW;
}
- else if (mmd->type & MOD_FLUID_TYPE_EFFEC) {
- if (mmd->effector) {
- BKE_fluid_modifier_freeEffector(mmd);
+ else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
+ if (fmd->effector) {
+ BKE_fluid_modifier_freeEffector(fmd);
}
/* effector object data */
- mmd->effector = MEM_callocN(sizeof(FluidEffectorSettings), "MantaEffector");
- mmd->effector->mmd = mmd;
- mmd->effector->mesh = NULL;
- mmd->effector->verts_old = NULL;
- mmd->effector->numverts = 0;
- mmd->effector->surface_distance = 0.0f;
- mmd->effector->type = FLUID_EFFECTOR_TYPE_COLLISION;
- mmd->effector->flags = FLUID_EFFECTOR_USE_EFFEC;
+ fmd->effector = MEM_callocN(sizeof(FluidEffectorSettings), "MantaEffector");
+ fmd->effector->fmd = fmd;
+ fmd->effector->mesh = NULL;
+ fmd->effector->verts_old = NULL;
+ fmd->effector->numverts = 0;
+ fmd->effector->surface_distance = 0.0f;
+ fmd->effector->type = FLUID_EFFECTOR_TYPE_COLLISION;
+ fmd->effector->flags = FLUID_EFFECTOR_USE_EFFEC;
/* guide options */
- mmd->effector->guide_mode = FLUID_EFFECTOR_GUIDE_OVERRIDE;
- mmd->effector->vel_multi = 1.0f;
+ fmd->effector->guide_mode = FLUID_EFFECTOR_GUIDE_OVERRIDE;
+ fmd->effector->vel_multi = 1.0f;
}
}
-void BKE_fluid_modifier_copy(const struct FluidModifierData *mmd,
- struct FluidModifierData *tmmd,
+void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd,
+ struct FluidModifierData *tfmd,
const int flag)
{
- tmmd->type = mmd->type;
- tmmd->time = mmd->time;
+ tfmd->type = fmd->type;
+ tfmd->time = fmd->time;
- BKE_fluid_modifier_create_type_data(tmmd);
+ BKE_fluid_modifier_create_type_data(tfmd);
- if (tmmd->domain) {
- FluidDomainSettings *tmds = tmmd->domain;
- FluidDomainSettings *mds = mmd->domain;
+ if (tfmd->domain) {
+ FluidDomainSettings *tfds = tfmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
/* domain object data */
- tmds->fluid_group = mds->fluid_group;
- tmds->force_group = mds->force_group;
- tmds->effector_group = mds->effector_group;
- if (tmds->effector_weights) {
- MEM_freeN(tmds->effector_weights);
+ tfds->fluid_group = fds->fluid_group;
+ tfds->force_group = fds->force_group;
+ tfds->effector_group = fds->effector_group;
+ if (tfds->effector_weights) {
+ MEM_freeN(tfds->effector_weights);
}
- tmds->effector_weights = MEM_dupallocN(mds->effector_weights);
+ tfds->effector_weights = MEM_dupallocN(fds->effector_weights);
/* adaptive domain options */
- tmds->adapt_margin = mds->adapt_margin;
- tmds->adapt_res = mds->adapt_res;
- tmds->adapt_threshold = mds->adapt_threshold;
+ tfds->adapt_margin = fds->adapt_margin;
+ tfds->adapt_res = fds->adapt_res;
+ tfds->adapt_threshold = fds->adapt_threshold;
/* fluid domain options */
- tmds->maxres = mds->maxres;
- tmds->solver_res = mds->solver_res;
- tmds->border_collisions = mds->border_collisions;
- tmds->flags = mds->flags;
- tmds->gravity[0] = mds->gravity[0];
- tmds->gravity[1] = mds->gravity[1];
- tmds->gravity[2] = mds->gravity[2];
- tmds->active_fields = mds->active_fields;
- tmds->type = mds->type;
- tmds->boundary_width = mds->boundary_width;
+ tfds->maxres = fds->maxres;
+ tfds->solver_res = fds->solver_res;
+ tfds->border_collisions = fds->border_collisions;
+ tfds->flags = fds->flags;
+ tfds->gravity[0] = fds->gravity[0];
+ tfds->gravity[1] = fds->gravity[1];
+ tfds->gravity[2] = fds->gravity[2];
+ tfds->active_fields = fds->active_fields;
+ tfds->type = fds->type;
+ tfds->boundary_width = fds->boundary_width;
/* smoke domain options */
- tmds->alpha = mds->alpha;
- tmds->beta = mds->beta;
- tmds->diss_speed = mds->diss_speed;
- tmds->vorticity = mds->vorticity;
- tmds->highres_sampling = mds->highres_sampling;
+ tfds->alpha = fds->alpha;
+ tfds->beta = fds->beta;
+ tfds->diss_speed = fds->diss_speed;
+ tfds->vorticity = fds->vorticity;
+ tfds->highres_sampling = fds->highres_sampling;
/* flame options */
- tmds->burning_rate = mds->burning_rate;
- tmds->flame_smoke = mds->flame_smoke;
- tmds->flame_vorticity = mds->flame_vorticity;
- tmds->flame_ignition = mds->flame_ignition;
- tmds->flame_max_temp = mds->flame_max_temp;
- copy_v3_v3(tmds->flame_smoke_color, mds->flame_smoke_color);
+ tfds->burning_rate = fds->burning_rate;
+ tfds->flame_smoke = fds->flame_smoke;
+ tfds->flame_vorticity = fds->flame_vorticity;
+ tfds->flame_ignition = fds->flame_ignition;
+ tfds->flame_max_temp = fds->flame_max_temp;
+ copy_v3_v3(tfds->flame_smoke_color, fds->flame_smoke_color);
/* noise options */
- tmds->noise_strength = mds->noise_strength;
- tmds->noise_pos_scale = mds->noise_pos_scale;
- tmds->noise_time_anim = mds->noise_time_anim;
- tmds->noise_scale = mds->noise_scale;
- tmds->noise_type = mds->noise_type;
+ tfds->noise_strength = fds->noise_strength;
+ tfds->noise_pos_scale = fds->noise_pos_scale;
+ tfds->noise_time_anim = fds->noise_time_anim;
+ tfds->noise_scale = fds->noise_scale;
+ tfds->noise_type = fds->noise_type;
/* liquid domain options */
- tmds->flip_ratio = mds->flip_ratio;
- tmds->particle_randomness = mds->particle_randomness;
- tmds->particle_number = mds->particle_number;
- tmds->particle_minimum = mds->particle_minimum;
- tmds->particle_maximum = mds->particle_maximum;
- tmds->particle_radius = mds->particle_radius;
- tmds->particle_band_width = mds->particle_band_width;
- tmds->fractions_threshold = mds->fractions_threshold;
+ tfds->flip_ratio = fds->flip_ratio;
+ tfds->particle_randomness = fds->particle_randomness;
+ tfds->particle_number = fds->particle_number;
+ tfds->particle_minimum = fds->particle_minimum;
+ tfds->particle_maximum = fds->particle_maximum;
+ tfds->particle_radius = fds->particle_radius;
+ tfds->particle_band_width = fds->particle_band_width;
+ tfds->fractions_threshold = fds->fractions_threshold;
+ tfds->sys_particle_maximum = fds->sys_particle_maximum;
/* diffusion options*/
- tmds->surface_tension = mds->surface_tension;
- tmds->viscosity_base = mds->viscosity_base;
- tmds->viscosity_exponent = mds->viscosity_exponent;
+ tfds->surface_tension = fds->surface_tension;
+ tfds->viscosity_base = fds->viscosity_base;
+ tfds->viscosity_exponent = fds->viscosity_exponent;
/* mesh options */
- if (mds->mesh_velocities) {
- tmds->mesh_velocities = MEM_dupallocN(mds->mesh_velocities);
- }
- tmds->mesh_concave_upper = mds->mesh_concave_upper;
- tmds->mesh_concave_lower = mds->mesh_concave_lower;
- tmds->mesh_particle_radius = mds->mesh_particle_radius;
- tmds->mesh_smoothen_pos = mds->mesh_smoothen_pos;
- tmds->mesh_smoothen_neg = mds->mesh_smoothen_neg;
- tmds->mesh_scale = mds->mesh_scale;
- tmds->totvert = mds->totvert;
- tmds->mesh_generator = mds->mesh_generator;
+ if (fds->mesh_velocities) {
+ tfds->mesh_velocities = MEM_dupallocN(fds->mesh_velocities);
+ }
+ tfds->mesh_concave_upper = fds->mesh_concave_upper;
+ tfds->mesh_concave_lower = fds->mesh_concave_lower;
+ tfds->mesh_particle_radius = fds->mesh_particle_radius;
+ tfds->mesh_smoothen_pos = fds->mesh_smoothen_pos;
+ tfds->mesh_smoothen_neg = fds->mesh_smoothen_neg;
+ tfds->mesh_scale = fds->mesh_scale;
+ tfds->totvert = fds->totvert;
+ tfds->mesh_generator = fds->mesh_generator;
/* secondary particle options */
- tmds->sndparticle_k_b = mds->sndparticle_k_b;
- tmds->sndparticle_k_d = mds->sndparticle_k_d;
- tmds->sndparticle_k_ta = mds->sndparticle_k_ta;
- tmds->sndparticle_k_wc = mds->sndparticle_k_wc;
- tmds->sndparticle_l_max = mds->sndparticle_l_max;
- tmds->sndparticle_l_min = mds->sndparticle_l_min;
- tmds->sndparticle_tau_max_k = mds->sndparticle_tau_max_k;
- tmds->sndparticle_tau_max_ta = mds->sndparticle_tau_max_ta;
- tmds->sndparticle_tau_max_wc = mds->sndparticle_tau_max_wc;
- tmds->sndparticle_tau_min_k = mds->sndparticle_tau_min_k;
- tmds->sndparticle_tau_min_ta = mds->sndparticle_tau_min_ta;
- tmds->sndparticle_tau_min_wc = mds->sndparticle_tau_min_wc;
- tmds->sndparticle_boundary = mds->sndparticle_boundary;
- tmds->sndparticle_combined_export = mds->sndparticle_combined_export;
- tmds->sndparticle_potential_radius = mds->sndparticle_potential_radius;
- tmds->sndparticle_update_radius = mds->sndparticle_update_radius;
- tmds->particle_type = mds->particle_type;
- tmds->particle_scale = mds->particle_scale;
+ tfds->sndparticle_k_b = fds->sndparticle_k_b;
+ tfds->sndparticle_k_d = fds->sndparticle_k_d;
+ tfds->sndparticle_k_ta = fds->sndparticle_k_ta;
+ tfds->sndparticle_k_wc = fds->sndparticle_k_wc;
+ tfds->sndparticle_l_max = fds->sndparticle_l_max;
+ tfds->sndparticle_l_min = fds->sndparticle_l_min;
+ tfds->sndparticle_tau_max_k = fds->sndparticle_tau_max_k;
+ tfds->sndparticle_tau_max_ta = fds->sndparticle_tau_max_ta;
+ tfds->sndparticle_tau_max_wc = fds->sndparticle_tau_max_wc;
+ tfds->sndparticle_tau_min_k = fds->sndparticle_tau_min_k;
+ tfds->sndparticle_tau_min_ta = fds->sndparticle_tau_min_ta;
+ tfds->sndparticle_tau_min_wc = fds->sndparticle_tau_min_wc;
+ tfds->sndparticle_boundary = fds->sndparticle_boundary;
+ tfds->sndparticle_combined_export = fds->sndparticle_combined_export;
+ tfds->sndparticle_potential_radius = fds->sndparticle_potential_radius;
+ tfds->sndparticle_update_radius = fds->sndparticle_update_radius;
+ tfds->particle_type = fds->particle_type;
+ tfds->particle_scale = fds->particle_scale;
/* fluid guide options */
- tmds->guide_parent = mds->guide_parent;
- tmds->guide_alpha = mds->guide_alpha;
- tmds->guide_beta = mds->guide_beta;
- tmds->guide_vel_factor = mds->guide_vel_factor;
- copy_v3_v3_int(tmds->guide_res, mds->guide_res);
- tmds->guide_source = mds->guide_source;
+ tfds->guide_parent = fds->guide_parent;
+ tfds->guide_alpha = fds->guide_alpha;
+ tfds->guide_beta = fds->guide_beta;
+ tfds->guide_vel_factor = fds->guide_vel_factor;
+ copy_v3_v3_int(tfds->guide_res, fds->guide_res);
+ tfds->guide_source = fds->guide_source;
/* cache options */
- tmds->cache_frame_start = mds->cache_frame_start;
- tmds->cache_frame_end = mds->cache_frame_end;
- tmds->cache_frame_pause_data = mds->cache_frame_pause_data;
- tmds->cache_frame_pause_noise = mds->cache_frame_pause_noise;
- tmds->cache_frame_pause_mesh = mds->cache_frame_pause_mesh;
- tmds->cache_frame_pause_particles = mds->cache_frame_pause_particles;
- tmds->cache_frame_pause_guide = mds->cache_frame_pause_guide;
- tmds->cache_flag = mds->cache_flag;
- tmds->cache_type = mds->cache_type;
- tmds->cache_mesh_format = mds->cache_mesh_format;
- tmds->cache_data_format = mds->cache_data_format;
- tmds->cache_particle_format = mds->cache_particle_format;
- tmds->cache_noise_format = mds->cache_noise_format;
- BLI_strncpy(tmds->cache_directory, mds->cache_directory, sizeof(tmds->cache_directory));
+ tfds->cache_frame_start = fds->cache_frame_start;
+ tfds->cache_frame_end = fds->cache_frame_end;
+ tfds->cache_frame_pause_data = fds->cache_frame_pause_data;
+ tfds->cache_frame_pause_noise = fds->cache_frame_pause_noise;
+ tfds->cache_frame_pause_mesh = fds->cache_frame_pause_mesh;
+ tfds->cache_frame_pause_particles = fds->cache_frame_pause_particles;
+ tfds->cache_frame_pause_guide = fds->cache_frame_pause_guide;
+ tfds->cache_frame_offset = fds->cache_frame_offset;
+ tfds->cache_flag = fds->cache_flag;
+ tfds->cache_type = fds->cache_type;
+ tfds->cache_mesh_format = fds->cache_mesh_format;
+ tfds->cache_data_format = fds->cache_data_format;
+ tfds->cache_particle_format = fds->cache_particle_format;
+ tfds->cache_noise_format = fds->cache_noise_format;
+ BLI_strncpy(tfds->cache_directory, fds->cache_directory, sizeof(tfds->cache_directory));
/* time options */
- tmds->time_scale = mds->time_scale;
- tmds->cfl_condition = mds->cfl_condition;
- tmds->timesteps_minimum = mds->timesteps_minimum;
- tmds->timesteps_maximum = mds->timesteps_maximum;
+ tfds->time_scale = fds->time_scale;
+ tfds->cfl_condition = fds->cfl_condition;
+ tfds->timesteps_minimum = fds->timesteps_minimum;
+ tfds->timesteps_maximum = fds->timesteps_maximum;
/* display options */
- tmds->slice_method = mds->slice_method;
- tmds->axis_slice_method = mds->axis_slice_method;
- tmds->slice_axis = mds->slice_axis;
- tmds->interp_method = mds->interp_method;
- tmds->draw_velocity = mds->draw_velocity;
- tmds->slice_per_voxel = mds->slice_per_voxel;
- tmds->slice_depth = mds->slice_depth;
- tmds->display_thickness = mds->display_thickness;
- if (mds->coba) {
- tmds->coba = MEM_dupallocN(mds->coba);
- }
- tmds->vector_scale = mds->vector_scale;
- tmds->vector_draw_type = mds->vector_draw_type;
- tmds->use_coba = mds->use_coba;
- tmds->coba_field = mds->coba_field;
+ tfds->slice_method = fds->slice_method;
+ tfds->axis_slice_method = fds->axis_slice_method;
+ tfds->slice_axis = fds->slice_axis;
+ tfds->interp_method = fds->interp_method;
+ tfds->draw_velocity = fds->draw_velocity;
+ tfds->slice_per_voxel = fds->slice_per_voxel;
+ tfds->slice_depth = fds->slice_depth;
+ tfds->display_thickness = fds->display_thickness;
+ if (fds->coba) {
+ tfds->coba = MEM_dupallocN(fds->coba);
+ }
+ tfds->vector_scale = fds->vector_scale;
+ tfds->vector_draw_type = fds->vector_draw_type;
+ tfds->use_coba = fds->use_coba;
+ tfds->coba_field = fds->coba_field;
/* -- Deprecated / unsed options (below)-- */
/* pointcache options */
- BKE_ptcache_free_list(&(tmds->ptcaches[0]));
+ BKE_ptcache_free_list(&(tfds->ptcaches[0]));
if (flag & LIB_ID_CREATE_NO_MAIN) {
/* Share the cache with the original object's modifier. */
- tmmd->modifier.flag |= eModifierFlag_SharedCaches;
- tmds->point_cache[0] = mds->point_cache[0];
- tmds->ptcaches[0] = mds->ptcaches[0];
+ tfmd->modifier.flag |= eModifierFlag_SharedCaches;
+ tfds->point_cache[0] = fds->point_cache[0];
+ tfds->ptcaches[0] = fds->ptcaches[0];
}
else {
- tmds->point_cache[0] = BKE_ptcache_copy_list(
- &(tmds->ptcaches[0]), &(mds->ptcaches[0]), flag);
+ tfds->point_cache[0] = BKE_ptcache_copy_list(
+ &(tfds->ptcaches[0]), &(fds->ptcaches[0]), flag);
}
/* OpenVDB cache options */
- tmds->openvdb_comp = mds->openvdb_comp;
- tmds->clipping = mds->clipping;
- tmds->data_depth = mds->data_depth;
+ tfds->openvdb_compression = fds->openvdb_compression;
+ tfds->clipping = fds->clipping;
+ tfds->openvdb_data_depth = fds->openvdb_data_depth;
}
- else if (tmmd->flow) {
- FluidFlowSettings *tmfs = tmmd->flow;
- FluidFlowSettings *mfs = mmd->flow;
+ else if (tfmd->flow) {
+ FluidFlowSettings *tffs = tfmd->flow;
+ FluidFlowSettings *ffs = fmd->flow;
- tmfs->psys = mfs->psys;
- tmfs->noise_texture = mfs->noise_texture;
+ tffs->psys = ffs->psys;
+ tffs->noise_texture = ffs->noise_texture;
/* initial velocity */
- tmfs->vel_multi = mfs->vel_multi;
- tmfs->vel_normal = mfs->vel_normal;
- tmfs->vel_random = mfs->vel_random;
- tmfs->vel_coord[0] = mfs->vel_coord[0];
- tmfs->vel_coord[1] = mfs->vel_coord[1];
- tmfs->vel_coord[2] = mfs->vel_coord[2];
+ tffs->vel_multi = ffs->vel_multi;
+ tffs->vel_normal = ffs->vel_normal;
+ tffs->vel_random = ffs->vel_random;
+ tffs->vel_coord[0] = ffs->vel_coord[0];
+ tffs->vel_coord[1] = ffs->vel_coord[1];
+ tffs->vel_coord[2] = ffs->vel_coord[2];
/* emission */
- tmfs->density = mfs->density;
- copy_v3_v3(tmfs->color, mfs->color);
- tmfs->fuel_amount = mfs->fuel_amount;
- tmfs->temperature = mfs->temperature;
- tmfs->volume_density = mfs->volume_density;
- tmfs->surface_distance = mfs->surface_distance;
- tmfs->particle_size = mfs->particle_size;
- tmfs->subframes = mfs->subframes;
+ tffs->density = ffs->density;
+ copy_v3_v3(tffs->color, ffs->color);
+ tffs->fuel_amount = ffs->fuel_amount;
+ tffs->temperature = ffs->temperature;
+ tffs->volume_density = ffs->volume_density;
+ tffs->surface_distance = ffs->surface_distance;
+ tffs->particle_size = ffs->particle_size;
+ tffs->subframes = ffs->subframes;
/* texture control */
- tmfs->texture_size = mfs->texture_size;
- tmfs->texture_offset = mfs->texture_offset;
- BLI_strncpy(tmfs->uvlayer_name, mfs->uvlayer_name, sizeof(tmfs->uvlayer_name));
- tmfs->vgroup_density = mfs->vgroup_density;
-
- tmfs->type = mfs->type;
- tmfs->behavior = mfs->behavior;
- tmfs->source = mfs->source;
- tmfs->texture_type = mfs->texture_type;
- tmfs->flags = mfs->flags;
- }
- else if (tmmd->effector) {
- FluidEffectorSettings *tmes = tmmd->effector;
- FluidEffectorSettings *mes = mmd->effector;
-
- tmes->surface_distance = mes->surface_distance;
- tmes->type = mes->type;
- tmes->flags = mes->flags;
- tmes->subframes = mes->subframes;
+ tffs->texture_size = ffs->texture_size;
+ tffs->texture_offset = ffs->texture_offset;
+ BLI_strncpy(tffs->uvlayer_name, ffs->uvlayer_name, sizeof(tffs->uvlayer_name));
+ tffs->vgroup_density = ffs->vgroup_density;
+
+ tffs->type = ffs->type;
+ tffs->behavior = ffs->behavior;
+ tffs->source = ffs->source;
+ tffs->texture_type = ffs->texture_type;
+ tffs->flags = ffs->flags;
+ }
+ else if (tfmd->effector) {
+ FluidEffectorSettings *tfes = tfmd->effector;
+ FluidEffectorSettings *fes = fmd->effector;
+
+ tfes->surface_distance = fes->surface_distance;
+ tfes->type = fes->type;
+ tfes->flags = fes->flags;
+ tfes->subframes = fes->subframes;
/* guide options */
- tmes->guide_mode = mes->guide_mode;
- tmes->vel_multi = mes->vel_multi;
+ tfes->guide_mode = fes->guide_mode;
+ tfes->vel_multi = fes->vel_multi;
}
}
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index 5771eb053f7..958acf0589b 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) {
@@ -797,7 +797,7 @@ static bool vfont_to_curve(Object *ob,
}
else {
char32_t *mem_tmp;
- slen = cu->len_wchar;
+ slen = cu->len_char32;
/* Create unicode string */
mem_tmp = MEM_malloc_arrayN((slen + 1), sizeof(*mem_tmp), "convertedmem");
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index b460e6d6dc0..eeb55c44d6e 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -282,6 +282,10 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_all)
}
}
+/**
+ * Delete grease pencil evaluated data
+ * \param gpd_eval: Grease pencil data-block
+ */
void BKE_gpencil_eval_delete(bGPdata *gpd_eval)
{
BKE_gpencil_free(gpd_eval, true);
@@ -289,10 +293,25 @@ 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 */
-/* add a new gp-frame to the given layer */
+/**
+ * Add a new gp-frame to the given layer.
+ * \param gpl: Grease pencil layer
+ * \param cframe: Frame number
+ * \return Pointer to new frame
+ */
bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
{
bGPDframe *gpf = NULL, *gf = NULL;
@@ -346,7 +365,12 @@ bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
return gpf;
}
-/* add a copy of the active gp-frame to the given layer */
+/**
+ * Add a copy of the active gp-frame to the given layer.
+ * \param gpl: Grease pencil layer
+ * \param cframe: Frame number
+ * \return Pointer to new frame
+ */
bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
{
bGPDframe *new_frame;
@@ -401,7 +425,13 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
return new_frame;
}
-/* add a new gp-layer and make it the active layer */
+/**
+ * Add a new gp-layer and make it the active layer.
+ * \param gpd: Grease pencil data-block
+ * \param name: Name of the layer
+ * \param setactive: Set as active
+ * \return Pointer to new layer
+ */
bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
{
bGPDlayer *gpl = NULL;
@@ -417,7 +447,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 +499,12 @@ 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.
+ * \param bmain: Main pointer
+ * \param name: Name of the datablock
+ * \return Pointer to new data-block
+ */
bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
{
bGPdata *gpd;
@@ -496,7 +531,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;
@@ -514,10 +549,11 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
/* Utilities for easier bulk-creation of geometry */
/**
- * 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
+ * Populate stroke with point data from data buffers.
+ * \param gps: Grease pencil stroke
+ * \param array: Flat array of point data values. Each entry has #GP_PRIM_DATABUF_SIZE values.
+ * \param totpoints: Total of points
+ * \param mat: 4x4 transform matrix to transform points into the right coordinate space.
*/
void BKE_gpencil_stroke_add_points(bGPDstroke *gps,
const float *array,
@@ -538,7 +574,13 @@ void BKE_gpencil_stroke_add_points(bGPDstroke *gps,
}
}
-/* Create a new stroke, with pre-allocated data buffers. */
+/**
+ * Create a new stroke, with pre-allocated data buffers.
+ * \param mat_idx: Index of the material
+ * \param totpoints: Total points
+ * \param thickness: Stroke thickness
+ * \return Pointer to new stroke
+ */
bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
{
/* allocate memory for a new stroke */
@@ -572,7 +614,15 @@ bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
return gps;
}
-/* Create a new stroke and add to frame. */
+/**
+ * Create a new stroke and add to frame.
+ * \param gpf: Grease pencil frame
+ * \param mat_idx: Material index
+ * \param totpoints: Total points
+ * \param thickness: Stroke thickness
+ * \param insert_at_head: Add to the head of the strokes list
+ * \return Pointer to new stroke
+ */
bGPDstroke *BKE_gpencil_stroke_add(
bGPDframe *gpf, int mat_idx, int totpoints, short thickness, const bool insert_at_head)
{
@@ -591,7 +641,16 @@ bGPDstroke *BKE_gpencil_stroke_add(
return gps;
}
-/* Add a stroke and copy the temporary drawing color value from one of the existing stroke */
+/**
+ * Add a stroke and copy the temporary drawing color value
+ * from one of the existing stroke.
+ * \param gpf: Grease pencil frame
+ * \param existing: Stroke with the style to copy
+ * \param mat_idx: Material index
+ * \param totpoints: Total points
+ * \param thickness: Stroke thickness
+ * \return Pointer to new stroke
+ */
bGPDstroke *BKE_gpencil_stroke_add_existing_style(
bGPDframe *gpf, bGPDstroke *existing, int mat_idx, int totpoints, short thickness)
{
@@ -606,7 +665,11 @@ bGPDstroke *BKE_gpencil_stroke_add_existing_style(
/* ************************************************** */
/* Data Duplication */
-/* make a copy of a given gpencil weights */
+/**
+ * Make a copy of a given gpencil weights.
+ * \param gps_src: Source grease pencil stroke
+ * \param gps_dst: Destination grease pencil stroke
+ */
void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_dst)
{
if (gps_src == NULL) {
@@ -617,7 +680,12 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d
BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints);
}
-/* make a copy of a given gpencil stroke */
+/**
+ * Make a copy of a given grease-pencil stroke.
+ * \param gps_src: Source grease pencil strokes.
+ * \param dup_points: Duplicate points data.
+ * \return Pointer to new stroke.
+ */
bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_points)
{
bGPDstroke *gps_dst = NULL;
@@ -642,7 +710,11 @@ bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_poi
return gps_dst;
}
-/* make a copy of a given gpencil frame */
+/**
+ * Make a copy of a given gpencil frame.
+ * \param gpf_src: Source grease pencil frame
+ * \return Pointer to new frame
+ */
bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src)
{
bGPDstroke *gps_dst = NULL;
@@ -669,7 +741,11 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src)
return gpf_dst;
}
-/* make a copy of strokes between gpencil frames */
+/**
+ * Make a copy of strokes between gpencil frames.
+ * \param gpf_src: Source grease pencil frame
+ * \param gpf_dst: Destination grease pencil frame
+ */
void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_dst)
{
bGPDstroke *gps_dst = NULL;
@@ -687,7 +763,11 @@ void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_ds
}
}
-/* make a copy of a given gpencil layer */
+/**
+ * Make a copy of a given gpencil layer.
+ * \param gpl_src: Source grease pencil layer
+ * \return Pointer to new layer
+ */
bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src)
{
const bGPDframe *gpf_src;
@@ -728,7 +808,13 @@ 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.
+ *
+ * \param bmain: Main pointer
+ * \param gpd: Grease pencil data-block
+ * \return Pointer to new data-block
+ */
bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd)
{
bGPdata *gpd_copy;
@@ -736,8 +822,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;
@@ -770,7 +859,10 @@ bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool in
/* ************************************************** */
/* GP Stroke API */
-/* ensure selection status of stroke is in sync with its points */
+/**
+ * Ensure selection status of stroke is in sync with its points.
+ * \param gps: Grease pencil stroke
+ */
void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
{
bGPDspoint *pt;
@@ -797,7 +889,11 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
/* ************************************************** */
/* GP Frame API */
-/* delete the last stroke of the given frame */
+/**
+ * Delete the last stroke of the given frame.
+ * \param gpl: Grease pencil layer
+ * \param gpf: Grease pencil frame
+ */
void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
{
bGPDstroke *gps = (gpf) ? gpf->strokes.last : NULL;
@@ -829,7 +925,11 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
/* ************************************************** */
/* GP Layer API */
-/* Check if the given layer is able to be edited or not */
+/**
+ * Check if the given layer is able to be edited or not.
+ * \param gpl: Grease pencil layer
+ * \return True if layer is editable
+ */
bool BKE_gpencil_layer_is_editable(const bGPDlayer *gpl)
{
/* Sanity check */
@@ -846,7 +946,12 @@ bool BKE_gpencil_layer_is_editable(const bGPDlayer *gpl)
return false;
}
-/* Look up the gp-frame on the requested frame number, but don't add a new one */
+/**
+ * Look up the gp-frame on the requested frame number, but don't add a new one.
+ * \param gpl: Grease pencil layer
+ * \param cframe: Frame number
+ * \return Pointer to frame
+ */
bGPDframe *BKE_gpencil_layer_frame_find(bGPDlayer *gpl, int cframe)
{
bGPDframe *gpf;
@@ -863,9 +968,14 @@ bGPDframe *BKE_gpencil_layer_frame_find(bGPDlayer *gpl, int cframe)
return NULL;
}
-/* get the appropriate gp-frame from a given layer
+/** Get the appropriate gp-frame from a given layer
* - this sets the layer's actframe var (if allowed to)
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
+ *
+ * \param gpl: Grease pencil layer
+ * \param cframe: Frame number
+ * \param addnew: Add option
+ * \return Pointer to new frame
*/
bGPDframe *BKE_gpencil_layer_frame_get(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
{
@@ -1021,7 +1131,12 @@ bGPDframe *BKE_gpencil_layer_frame_get(bGPDlayer *gpl, int cframe, eGP_GetFrame_
return gpl->actframe;
}
-/* delete the given frame from a layer */
+/**
+ * Delete the given frame from a layer.
+ * \param gpl: Grease pencil layer
+ * \param gpf: Grease pencil frame
+ * \return True if delete was done
+ */
bool BKE_gpencil_layer_frame_delete(bGPDlayer *gpl, bGPDframe *gpf)
{
bool changed = false;
@@ -1045,6 +1160,12 @@ bool BKE_gpencil_layer_frame_delete(bGPDlayer *gpl, bGPDframe *gpf)
return changed;
}
+/**
+ * Get layer by name
+ * \param gpd: Grease pencil data-block
+ * \param name: Layer name
+ * \return Pointer to layer
+ */
bGPDlayer *BKE_gpencil_layer_named_get(bGPdata *gpd, const char *name)
{
if (name[0] == '\0') {
@@ -1053,6 +1174,12 @@ bGPDlayer *BKE_gpencil_layer_named_get(bGPdata *gpd, const char *name)
return BLI_findstring(&gpd->layers, name, offsetof(bGPDlayer, info));
}
+/**
+ * Get mask layer by name.
+ * \param gpl: Grease pencil layer
+ * \param name: Mask name
+ * \return Pointer to mask layer
+ */
bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(bGPDlayer *gpl, const char *name)
{
if (name[0] == '\0') {
@@ -1061,6 +1188,12 @@ bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(bGPDlayer *gpl, const char *nam
return BLI_findstring(&gpl->mask_layers, name, offsetof(bGPDlayer_Mask, name));
}
+/**
+ * Add grease pencil mask layer.
+ * \param gpl: Grease pencil layer
+ * \param name: Name of the mask
+ * \return Pointer to new mask layer
+ */
bGPDlayer_Mask *BKE_gpencil_layer_mask_add(bGPDlayer *gpl, const char *name)
{
@@ -1072,6 +1205,11 @@ bGPDlayer_Mask *BKE_gpencil_layer_mask_add(bGPDlayer *gpl, const char *name)
return mask;
}
+/**
+ * Remove grease pencil mask layer.
+ * \param gpl: Grease pencil layer
+ * \param mask: Grease pencil mask layer
+ */
void BKE_gpencil_layer_mask_remove(bGPDlayer *gpl, bGPDlayer_Mask *mask)
{
BLI_freelinkN(&gpl->mask_layers, mask);
@@ -1079,6 +1217,11 @@ void BKE_gpencil_layer_mask_remove(bGPDlayer *gpl, bGPDlayer_Mask *mask)
CLAMP_MIN(gpl->act_mask, 0);
}
+/**
+ * Remove any reference to mask layer.
+ * \param gpd: Grease pencil data-block
+ * \param name: Name of the mask layer
+ */
void BKE_gpencil_layer_mask_remove_ref(bGPdata *gpd, const char *name)
{
bGPDlayer_Mask *mask_next;
@@ -1110,6 +1253,11 @@ static int gpencil_cb_sort_masks(const void *arg1, const void *arg2)
return val;
}
+/**
+ * Sort grease pencil mask layers.
+ * \param gpd: Grease pencil data-block
+ * \param gpl: Grease pencil layer
+ */
void BKE_gpencil_layer_mask_sort(bGPdata *gpd, bGPDlayer *gpl)
{
/* Update sort index. */
@@ -1125,6 +1273,10 @@ void BKE_gpencil_layer_mask_sort(bGPdata *gpd, bGPDlayer *gpl)
BLI_listbase_sort(&gpl->mask_layers, gpencil_cb_sort_masks);
}
+/**
+ * Sort all grease pencil mask layer.
+ * \param gpd: Grease pencil data-block
+ */
void BKE_gpencil_layer_mask_sort_all(bGPdata *gpd)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -1153,12 +1305,21 @@ static int gpencil_cb_cmp_frame(void *thunk, const void *a, const void *b)
return 0;
}
+/**
+ * Sort grease pencil frames.
+ * \param gpl: Grease pencil layer
+ * \param r_has_duplicate_frames: Duplicated frames flag
+ */
void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames)
{
BLI_listbase_sort_r(&gpl->frames, gpencil_cb_cmp_frame, r_has_duplicate_frames);
}
-/* get the active gp-layer for editing */
+/**
+ * Get the active grease pencil layer for editing.
+ * \param gpd: Grease pencil data-block
+ * \return Pointer to layer
+ */
bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd)
{
/* error checking */
@@ -1177,7 +1338,11 @@ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd)
return NULL;
}
-/* set the active gp-layer */
+/**
+ * Set active grease pencil layer.
+ * \param gpd: Grease pencil data-block
+ * \param active: Grease pencil layer to set as active
+ */
void BKE_gpencil_layer_active_set(bGPdata *gpd, bGPDlayer *active)
{
/* error checking */
@@ -1200,7 +1365,11 @@ void BKE_gpencil_layer_active_set(bGPdata *gpd, bGPDlayer *active)
}
}
-/* Set locked layers for autolock mode. */
+/**
+ * Set locked layers for autolock mode.
+ * \param gpd: Grease pencil data-block
+ * \param unlock: Unlock flag
+ */
void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock)
{
BLI_assert(gpd != NULL);
@@ -1231,7 +1400,11 @@ void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock)
}
}
-/* delete the active gp-layer */
+/**
+ * Delete grease pencil layer.
+ * \param gpd: Grease pencil data-block
+ * \param gpl: Grease pencil layer
+ */
void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl)
{
/* error checking */
@@ -1254,6 +1427,11 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl)
BLI_freelinkN(&gpd->layers, gpl);
}
+/**
+ * Get grease pencil material from brush.
+ * \param brush: Brush
+ * \return Pointer to material
+ */
Material *BKE_gpencil_brush_material_get(Brush *brush)
{
Material *ma = NULL;
@@ -1266,6 +1444,11 @@ Material *BKE_gpencil_brush_material_get(Brush *brush)
return ma;
}
+/**
+ * Set grease pencil brush material.
+ * \param brush: Brush
+ * \param ma: Material
+ */
void BKE_gpencil_brush_material_set(Brush *brush, Material *ma)
{
BLI_assert(brush);
@@ -1281,7 +1464,13 @@ void BKE_gpencil_brush_material_set(Brush *brush, Material *ma)
}
}
-/* Adds the pinned material to the object if necessary. */
+/**
+ * Adds the pinned material to the object if necessary.
+ * \param bmain: Main pointer
+ * \param ob: Grease pencil object
+ * \param brush: Brush
+ * \return Pointer to material
+ */
Material *BKE_gpencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
{
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
@@ -1301,7 +1490,13 @@ Material *BKE_gpencil_object_material_ensure_from_brush(Main *bmain, Object *ob,
}
}
-/* Assigns the material to object (if not already present) and returns its index (mat_nr). */
+/**
+ * Assigns the material to object (if not already present) and returns its index (mat_nr).
+ * \param bmain: Main pointer
+ * \param ob: Grease pencil object
+ * \param material: Material
+ * \return Index of the material
+ */
int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *material)
{
if (!material) {
@@ -1317,9 +1512,12 @@ 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
+ * Creates a new grease-pencil material and assigns it to object.
+ * \param bmain: Main pointer
+ * \param ob: Grease pencil object
+ * \param name: Material name
+ * \param r_index: value is set to zero based index of the new material if \a r_index is not NULL.
+ * \return Material pointer.
*/
Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
{
@@ -1335,7 +1533,12 @@ Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *n
return ma;
}
-/* Returns the material for a brush with respect to its pinned state. */
+/**
+ * Returns the material for a brush with respect to its pinned state.
+ * \param ob: Grease pencil object
+ * \param brush: Brush
+ * \return Material pointer
+ */
Material *BKE_gpencil_object_material_from_brush_get(Object *ob, Brush *brush)
{
if ((brush) && (brush->gpencil_settings) &&
@@ -1348,7 +1551,12 @@ Material *BKE_gpencil_object_material_from_brush_get(Object *ob, Brush *brush)
}
}
-/* Returns the material index for a brush with respect to its pinned state. */
+/**
+ * Returns the material index for a brush with respect to its pinned state.
+ * \param ob: Grease pencil object
+ * \param brush: Brush
+ * \return Material index.
+ */
int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush)
{
if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) {
@@ -1359,7 +1567,12 @@ int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush)
}
}
-/* Guaranteed to return a material assigned to object. Returns never NULL. */
+/**
+ * Guaranteed to return a material assigned to object. Returns never NULL.
+ * \param bmain: Main pointer
+ * \param ob: Grease pencil object
+ * \return Material pointer.
+ */
Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main *bmain,
Object *ob,
ToolSettings *ts)
@@ -1373,7 +1586,13 @@ Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main
}
}
-/* Guaranteed to return a material assigned to object. Returns never NULL. */
+/**
+ * Guaranteed to return a material assigned to object. Returns never NULL.
+ * \param bmain: Main pointer
+ * \param ob: Grease pencil object.
+ * \param brush: Brush
+ * \return Material pointer
+ */
Material *BKE_gpencil_object_material_ensure_from_active_input_brush(Main *bmain,
Object *ob,
Brush *brush)
@@ -1394,6 +1613,8 @@ Material *BKE_gpencil_object_material_ensure_from_active_input_brush(Main *bmain
/**
* Guaranteed to return a material assigned to object. Returns never NULL.
* Only use this for materials unrelated to user input.
+ * \param ob: Grease pencil object
+ * \return Material pointer
*/
Material *BKE_gpencil_object_material_ensure_from_active_input_material(Object *ob)
{
@@ -1405,7 +1626,11 @@ Material *BKE_gpencil_object_material_ensure_from_active_input_material(Object *
return BKE_material_default_gpencil();
}
-/* Get active color, and add all default settings if we don't find anything */
+/**
+ * Get active color, and add all default settings if we don't find anything.
+ * \param ob: Grease pencil object
+ * \return Material pointer
+ */
Material *BKE_gpencil_object_material_ensure_active(Object *ob)
{
Material *ma = NULL;
@@ -1424,6 +1649,11 @@ Material *BKE_gpencil_object_material_ensure_active(Object *ob)
}
/* ************************************************** */
+/**
+ * Check if stroke has any point selected
+ * \param gps: Grease pencil stroke
+ * \return True if selected
+ */
bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
{
const bGPDspoint *pt;
@@ -1439,7 +1669,11 @@ bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
/* ************************************************** */
/* GP Object - Vertex Groups */
-/* remove a vertex group */
+/**
+ * Remove a vertex group.
+ * \param ob: Grease pencil object
+ * \param defgroup: deform group
+ */
void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup)
{
bGPdata *gpd = ob->data;
@@ -1478,6 +1712,10 @@ void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup)
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
+/**
+ * Ensure stroke has vertex group.
+ * \param gps: Grease pencil stroke
+ */
void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
{
if (gps->dvert == NULL) {
@@ -1491,9 +1729,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 +1752,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)
@@ -1544,13 +1782,19 @@ float BKE_gpencil_multiframe_falloff_calc(
value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f);
}
else {
- value = 1.0f;
+ /* Center of the curve. */
+ value = BKE_curvemapping_evaluateF(cur_falloff, 0, 0.5f);
}
return value;
}
-/* reassign strokes using a material */
+/**
+ * Reassign strokes using a material.
+ * \param gpd: Grease pencil data-block
+ * \param totcol: Total materials
+ * \param index: Index of the material
+ */
void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -1566,7 +1810,12 @@ void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index)
}
}
-/* remove strokes using a material */
+/**
+ * Remove strokes using a material.
+ * \param gpd: Grease pencil data-block
+ * \param index: Index of the material
+ * \return True if removed
+ */
bool BKE_gpencil_material_index_used(bGPdata *gpd, int index)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -1582,6 +1831,12 @@ bool BKE_gpencil_material_index_used(bGPdata *gpd, int index)
return false;
}
+/**
+ * Remap material
+ * \param gpd: Grease pencil data-block
+ * \param remap: Remap index
+ * \param remap_len: Remap length
+ */
void BKE_gpencil_material_remap(struct bGPdata *gpd,
const unsigned int *remap,
unsigned int remap_len)
@@ -1607,7 +1862,15 @@ void BKE_gpencil_material_remap(struct bGPdata *gpd,
#undef MAT_NR_REMAP
}
-/* Load a table with material conversion index for merged materials. */
+/**
+ * Load a table with material conversion index for merged materials.
+ * \param ob: Grease pencil object
+ * \param hue_threshold: Threshold for Hue
+ * \param sat_threshold: Threshold for Saturation
+ * \param val_threshold: Threshold for Value
+ * \param r_mat_table : return material table
+ * \return True if done
+ */
bool BKE_gpencil_merge_materials_table_get(Object *ob,
const float hue_threshold,
const float sat_threshold,
@@ -1703,7 +1966,10 @@ bool BKE_gpencil_merge_materials_table_get(Object *ob,
return changed;
}
-/* statistics functions */
+/**
+ * Calc grease pencil statistics functions.
+ * \param gpd: Grease pencil data-block
+ */
void BKE_gpencil_stats_update(bGPdata *gpd)
{
gpd->totlayer = 0;
@@ -1723,7 +1989,12 @@ void BKE_gpencil_stats_update(bGPdata *gpd)
}
}
-/* get material index (0-based like mat_nr not actcol) */
+/**
+ * Get material index (0-based like mat_nr not actcol).
+ * \param ob: Grease pencil object
+ * \param ma: Material
+ * \return Index of the material
+ */
int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
{
short *totcol = BKE_object_material_len_p(ob);
@@ -1738,7 +2009,11 @@ int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
return -1;
}
-/* Create a default palette */
+/**
+ * Create a default palette.
+ * \param bmain: Main pointer
+ * \param scene: Scene
+ */
void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
{
const int totcol = 120;
@@ -1788,6 +2063,14 @@ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
}
}
+/**
+ * Create grease pencil strokes from image
+ * \param sima: Image
+ * \param gpf: Grease pencil frame
+ * \param size: Size
+ * \param mask: Mask
+ * \return True if done
+ */
bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size, const bool mask)
{
Image *image = sima->image;
@@ -1803,7 +2086,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 +2131,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 +2157,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,
@@ -2046,6 +2328,11 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
}
}
+/**
+ * Update original pointers in evaluated frame.
+ * \param gpf_orig: Original grease-pencil frame.
+ * \param gpf_eval: Evaluated grease pencil frame.
+ */
void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig,
const struct bGPDframe *gpf_eval)
{
@@ -2074,6 +2361,11 @@ void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig
}
}
+/**
+ * Update pointers of eval data to original data to keep references.
+ * \param ob_orig: Original grease pencil object
+ * \param ob_eval: Evaluated grease pencil object
+ */
void BKE_gpencil_update_orig_pointers(const Object *ob_orig, const Object *ob_eval)
{
bGPdata *gpd_eval = (bGPdata *)ob_eval->data;
@@ -2081,7 +2373,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;
@@ -2104,6 +2396,13 @@ void BKE_gpencil_update_orig_pointers(const Object *ob_orig, const Object *ob_ev
}
}
+/**
+ * Get parent matrix, including layer parenting.
+ * \param depsgraph: Depsgraph
+ * \param obact: Grease pencil object
+ * \param gpl: Grease pencil layer
+ * \param diff_mat: Result parent matrix
+ */
void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph,
Object *obact,
bGPDlayer *gpl,
@@ -2153,6 +2452,11 @@ void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph,
}
}
+/**
+ * Update parent matrix.
+ * \param depsgraph: Depsgraph
+ * \param ob: Grease pencil object
+ */
void BKE_gpencil_update_layer_parent(const Depsgraph *depsgraph, Object *ob)
{
if (ob->type != OB_GPENCIL) {
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..53bfd3aa8ff 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -32,23 +32,36 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_hash.h"
#include "BLI_math_vector.h"
#include "BLI_polyfill_2d.h"
+#include "BLT_translation.h"
+
+#include "DNA_gpencil_modifier_types.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"
/* GP Object - Boundbox Support */
/**
- * Get min/max coordinate bounds for single stroke
- * \return Returns whether we found any selected points
+ *Get min/max coordinate bounds for single stroke.
+ * \param gps: Grease pencil stroke
+ * \param use_select: Include only selected points
+ * \param r_min: Result minimum coordinates
+ * \param r_max: Result maximum coordinates
+ * \return True if it was possible to calculate
*/
bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps,
const bool use_select,
@@ -72,7 +85,13 @@ bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps,
return changed;
}
-/* get min/max bounds of all strokes in GP datablock */
+/**
+ * Get min/max bounds of all strokes in grease pencil data-block.
+ * \param gpd: Grease pencil datablock
+ * \param r_min: Result minimum coordinates
+ * \param r_max: Result maximum coordinates
+ * \return True if it was possible to calculate
+ */
bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
{
bool changed = false;
@@ -88,7 +107,7 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
if (gpf != NULL) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- changed = BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
+ changed |= BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
}
}
}
@@ -96,7 +115,11 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
return changed;
}
-/* compute center of bounding box */
+/**
+ * Compute center of bounding box.
+ * \param gpd: Grease pencil data-block
+ * \param r_centroid: Location of the center
+ */
void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
{
float min[3], max[3], tot[3];
@@ -107,14 +130,20 @@ void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
mul_v3_v3fl(r_centroid, tot, 0.5f);
}
-/* Compute stroke bounding box. */
+/**
+ * Compute stroke bounding box.
+ * \param gps: Grease pencil Stroke
+ */
void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
{
INIT_MINMAX(gps->boundbox_min, gps->boundbox_max);
BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max);
}
-/* create bounding box values */
+/**
+ * Create bounding box values.
+ * \param ob: Grease pencil object
+ */
static void boundbox_gpencil(Object *ob)
{
BoundBox *bb;
@@ -138,7 +167,11 @@ static void boundbox_gpencil(Object *ob)
bb->flag &= ~BOUNDBOX_DIRTY;
}
-/* get bounding box */
+/**
+ * Get grease pencil object bounding box.
+ * \param ob: Grease pencil object
+ * \return Bounding box
+ */
BoundBox *BKE_gpencil_boundbox_get(Object *ob)
{
if (ELEM(NULL, ob, ob->data)) {
@@ -585,6 +618,14 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
return true;
}
+/**
+ * Split stroke.
+ * \param gpf: Grease pencil frame
+ * \param gps: Grease pencil original stroke
+ * \param before_index: Position of the point to split
+ * \param remaining_gps: Secondary stroke after split.
+ * \return True if the split was done
+ */
bool BKE_gpencil_stroke_split(bGPDframe *gpf,
bGPDstroke *gps,
const int before_index,
@@ -711,7 +752,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
}
/**
- * Apply smooth to stroke point
+ * Apply smooth position to stroke point.
* \param gps: Stroke to smooth
* \param i: Point index
* \param inf: Amount of smoothing to apply
@@ -774,7 +815,11 @@ bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf)
}
/**
- * Apply smooth for strength to stroke point */
+ * Apply smooth strength to stroke point.
+ * \param gps: Stroke to smooth
+ * \param point_index: Point index
+ * \param influence: Amount of smoothing to apply
+ */
bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
{
bGPDspoint *ptb = &gps->points[point_index];
@@ -834,7 +879,11 @@ bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float
}
/**
- * Apply smooth for thickness to stroke point (use pressure) */
+ * Apply smooth for thickness to stroke point (use pressure).
+ * \param gps: Stroke to smooth
+ * \param point_index: Point index
+ * \param influence: Amount of smoothing to apply
+ */
bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
{
bGPDspoint *ptb = &gps->points[point_index];
@@ -894,6 +943,9 @@ bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float
/**
* Apply smooth for UV rotation to stroke point (use pressure).
+ * \param gps: Stroke to smooth
+ * \param point_index: Point index
+ * \param influence: Amount of smoothing to apply
*/
bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
{
@@ -931,7 +983,15 @@ bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influe
return true;
}
-/* Get points of stroke always flat to view not affected by camera view or view position */
+
+/**
+ * Get points of stroke always flat to view not affected
+ * by camera view or view position.
+ * \param points: Array of grease pencil points (3D)
+ * \param totpoints: Total of points
+ * \param points2d: Result array of 2D points
+ * \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0)
+ */
void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
int totpoints,
float (*points2d)[2],
@@ -984,12 +1044,20 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
points2d[i][1] = dot_v3v3(loc, locy);
}
- /* Concave (-1), Convex (1), or Autodetect (0)? */
+ /* Concave (-1), Convex (1), or Auto-detect (0)? */
*r_direction = (int)locy[2];
}
-/* Get points of stroke always flat to view not affected by camera view or view position
- * using another stroke as reference
+/**
+ * Get points of stroke always flat to view not affected by camera view or view position
+ * using another stroke as reference.
+ * \param ref_points: Array of reference points (3D)
+ * \param ref_totpoints: Total reference points
+ * \param points: Array of points to flat (3D)
+ * \param totpoints: Total points
+ * \param points2d: Result array of 2D points
+ * \param scale: Scale factor
+ * \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0)
*/
void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
int ref_totpoints,
@@ -1071,15 +1139,15 @@ void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
points2d[i][1] = dot_v3v3(loc, locy);
}
- /* Concave (-1), Convex (1), or Autodetect (0)? */
+ /* Concave (-1), Convex (1), or Auto-detect (0)? */
*r_direction = (int)locy[2];
}
-/* calc texture coordinates using flat projected points */
+/* Calc texture coordinates using flat projected points. */
static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
bGPDstroke *gps,
const float minv[2],
- float maxv[2],
+ const float maxv[2],
float (*r_uv)[2])
{
const float s = sin(gps->uv_rotation);
@@ -1114,8 +1182,10 @@ static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
}
}
-/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was
- * modified) */
+/**
+ * Triangulate stroke to generate data for filling areas.
+ * \param gps: Grease pencil stroke
+ */
void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
{
BLI_assert(gps->totpoints >= 3);
@@ -1174,7 +1244,10 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
MEM_SAFE_FREE(uv);
}
-/* texture coordinate utilities */
+/**
+ * Update Stroke UV data.
+ * \param gps: Grease pencil stroke
+ */
void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
{
if (gps == NULL || gps->totpoints == 0) {
@@ -1190,7 +1263,10 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
}
}
-/* Recalc the internal geometry caches for fill and uvs. */
+/**
+ * Recalc all internal geometry data for the stroke
+ * \param gps: Grease pencil stroke
+ */
void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
{
if (gps == NULL) {
@@ -1212,6 +1288,12 @@ void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
BKE_gpencil_stroke_boundingbox_calc(gps);
}
+/**
+ * Calculate grease pencil stroke length.
+ * \param gps: Grease pencil stroke
+ * \param use_3d: Set to true to use 3D points
+ * \return Length of the stroke
+ */
float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
{
if (!gps->points || gps->totpoints < 2) {
@@ -1235,7 +1317,7 @@ float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
}
/**
- * Trim stroke to the first intersection or loop
+ * Trim stroke to the first intersection or loop.
* \param gps: Stroke data
*/
bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
@@ -1244,7 +1326,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++) {
@@ -1331,7 +1414,7 @@ bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
}
/**
- * Close stroke
+ * Close grease pencil stroke.
* \param gps: Stroke to close
*/
bool BKE_gpencil_stroke_close(bGPDstroke *gps)
@@ -1418,7 +1501,13 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
return true;
}
-/* Dissolve points in stroke */
+
+/**
+ * Dissolve points in stroke.
+ * \param gpf: Grease pencil frame
+ * \param gps: Grease pencil stroke
+ * \param tag: Type of tag for point
+ */
void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag)
{
bGPDspoint *pt;
@@ -1499,8 +1588,341 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta
}
}
+/**
+ * Calculate stroke normals.
+ * \param gps: Grease pencil stroke
+ * \param r_normal: Return Normal vector normalized
+ */
+void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
+{
+ if (gps->totpoints < 3) {
+ zero_v3(r_normal);
+ return;
+ }
+
+ bGPDspoint *points = gps->points;
+ int totpoints = gps->totpoints;
+
+ const bGPDspoint *pt0 = &points[0];
+ const bGPDspoint *pt1 = &points[1];
+ const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
+
+ float vec1[3];
+ float vec2[3];
+
+ /* initial vector (p0 -> p1) */
+ sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
+
+ /* point vector at 3/4 */
+ sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
+
+ /* vector orthogonal to polygon plane */
+ cross_v3_v3v3(r_normal, vec1, vec2);
+
+ /* Normalize vector */
+ normalize_v3(r_normal);
+}
+
+/* Stroke Simplify ------------------------------------- */
+
+/** Reduce a series of points to a simplified version, but
+ * maintains the general shape of the series
+ *
+ * Ramer - Douglas - Peucker algorithm
+ * by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
+ * \param gps: Grease pencil stroke
+ * \param epsilon: Epsilon value to define precision of the algorithm
+ */
+void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
+{
+ bGPDspoint *old_points = MEM_dupallocN(gps->points);
+ int totpoints = gps->totpoints;
+ char *marked = NULL;
+ char work;
+
+ int start = 0;
+ int end = gps->totpoints - 1;
+
+ marked = MEM_callocN(totpoints, "GP marked array");
+ marked[start] = 1;
+ marked[end] = 1;
+
+ work = 1;
+ int totmarked = 0;
+ /* while still reducing */
+ while (work) {
+ int ls, le;
+ work = 0;
+
+ ls = start;
+ le = start + 1;
+
+ /* while not over interval */
+ while (ls < end) {
+ int max_i = 0;
+ /* divided to get more control */
+ float max_dist = epsilon / 10.0f;
+
+ /* find the next marked point */
+ while (marked[le] == 0) {
+ le++;
+ }
+
+ for (int i = ls + 1; i < le; i++) {
+ float point_on_line[3];
+ float dist;
+
+ closest_to_line_segment_v3(
+ point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
+
+ dist = len_v3v3(point_on_line, &old_points[i].x);
+
+ if (dist > max_dist) {
+ max_dist = dist;
+ max_i = i;
+ }
+ }
+
+ if (max_i != 0) {
+ work = 1;
+ marked[max_i] = 1;
+ totmarked++;
+ }
+
+ ls = le;
+ le = ls + 1;
+ }
+ }
+
+ /* adding points marked */
+ MDeformVert *old_dvert = NULL;
+ MDeformVert *dvert_src = NULL;
+
+ if (gps->dvert != NULL) {
+ old_dvert = MEM_dupallocN(gps->dvert);
+ }
+ /* resize gps */
+ int j = 0;
+ for (int i = 0; i < totpoints; i++) {
+ bGPDspoint *pt_src = &old_points[i];
+ bGPDspoint *pt = &gps->points[j];
+
+ if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
+ memcpy(pt, pt_src, sizeof(bGPDspoint));
+ if (gps->dvert != NULL) {
+ dvert_src = &old_dvert[i];
+ MDeformVert *dvert = &gps->dvert[j];
+ memcpy(dvert, dvert_src, sizeof(MDeformVert));
+ if (dvert_src->dw) {
+ memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
+ }
+ }
+ j++;
+ }
+ else {
+ if (gps->dvert != NULL) {
+ dvert_src = &old_dvert[i];
+ BKE_gpencil_free_point_weights(dvert_src);
+ }
+ }
+ }
+
+ gps->totpoints = j;
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+
+ MEM_SAFE_FREE(old_points);
+ MEM_SAFE_FREE(old_dvert);
+ MEM_SAFE_FREE(marked);
+}
+
+/**
+ * Simplify alternate vertex of stroke except extremes.
+ * \param gps: Grease pencil stroke
+ */
+void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
+{
+ if (gps->totpoints < 5) {
+ return;
+ }
+
+ /* save points */
+ bGPDspoint *old_points = MEM_dupallocN(gps->points);
+ MDeformVert *old_dvert = NULL;
+ MDeformVert *dvert_src = NULL;
+
+ if (gps->dvert != NULL) {
+ old_dvert = MEM_dupallocN(gps->dvert);
+ }
+
+ /* resize gps */
+ int newtot = (gps->totpoints - 2) / 2;
+ if (((gps->totpoints - 2) % 2) > 0) {
+ newtot++;
+ }
+ newtot += 2;
+
+ gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
+ if (gps->dvert != NULL) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
+ }
+
+ int j = 0;
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt_src = &old_points[i];
+ bGPDspoint *pt = &gps->points[j];
+
+ if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
+ memcpy(pt, pt_src, sizeof(bGPDspoint));
+ if (gps->dvert != NULL) {
+ dvert_src = &old_dvert[i];
+ MDeformVert *dvert = &gps->dvert[j];
+ memcpy(dvert, dvert_src, sizeof(MDeformVert));
+ if (dvert_src->dw) {
+ memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
+ }
+ }
+ j++;
+ }
+ else {
+ if (gps->dvert != NULL) {
+ dvert_src = &old_dvert[i];
+ BKE_gpencil_free_point_weights(dvert_src);
+ }
+ }
+ }
+
+ gps->totpoints = j;
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+
+ MEM_SAFE_FREE(old_points);
+ MEM_SAFE_FREE(old_dvert);
+}
+
+/**
+ * Subdivide grease pencil stroke.
+ * \param gps: Grease pencil stroke
+ * \param level: Level of subdivision
+ * \param type: Type of subdivision
+ */
+void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
+{
+ bGPDspoint *temp_points;
+ MDeformVert *temp_dverts = NULL;
+ MDeformVert *dvert = NULL;
+ MDeformVert *dvert_final = NULL;
+ MDeformVert *dvert_next = NULL;
+ int totnewpoints, oldtotpoints;
+ int i2;
+
+ for (int s = 0; s < level; s++) {
+ totnewpoints = gps->totpoints - 1;
+ /* duplicate points in a temp area */
+ temp_points = MEM_dupallocN(gps->points);
+ oldtotpoints = gps->totpoints;
+
+ /* resize the points arrays */
+ gps->totpoints += totnewpoints;
+ gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ if (gps->dvert != NULL) {
+ temp_dverts = MEM_dupallocN(gps->dvert);
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+ }
+
+ /* move points from last to first to new place */
+ i2 = gps->totpoints - 1;
+ for (int i = oldtotpoints - 1; i > 0; i--) {
+ bGPDspoint *pt = &temp_points[i];
+ bGPDspoint *pt_final = &gps->points[i2];
+
+ copy_v3_v3(&pt_final->x, &pt->x);
+ pt_final->pressure = pt->pressure;
+ pt_final->strength = pt->strength;
+ pt_final->time = pt->time;
+ pt_final->flag = pt->flag;
+ pt_final->runtime.pt_orig = pt->runtime.pt_orig;
+ pt_final->runtime.idx_orig = pt->runtime.idx_orig;
+ copy_v4_v4(pt_final->vert_color, pt->vert_color);
+
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[i];
+ dvert_final = &gps->dvert[i2];
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = dvert->dw;
+ }
+ i2 -= 2;
+ }
+ /* interpolate mid points */
+ i2 = 1;
+ for (int i = 0; i < oldtotpoints - 1; i++) {
+ bGPDspoint *pt = &temp_points[i];
+ bGPDspoint *next = &temp_points[i + 1];
+ bGPDspoint *pt_final = &gps->points[i2];
+
+ /* add a half way point */
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->runtime.pt_orig = NULL;
+ pt_final->flag = 0;
+ interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
+
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[i];
+ dvert_next = &temp_dverts[i + 1];
+ dvert_final = &gps->dvert[i2];
+
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+
+ /* interpolate weight values */
+ for (int d = 0; d < dvert->totweight; d++) {
+ MDeformWeight *dw_a = &dvert->dw[d];
+ if (dvert_next->totweight > d) {
+ MDeformWeight *dw_b = &dvert_next->dw[d];
+ MDeformWeight *dw_final = &dvert_final->dw[d];
+ dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
+ }
+ }
+ }
+
+ i2 += 2;
+ }
+
+ MEM_SAFE_FREE(temp_points);
+ MEM_SAFE_FREE(temp_dverts);
+
+ /* move points to smooth stroke (not simple type )*/
+ if (type != GP_SUBDIV_SIMPLE) {
+ /* duplicate points in a temp area with the new subdivide data */
+ temp_points = MEM_dupallocN(gps->points);
+
+ /* extreme points are not changed */
+ for (int i = 0; i < gps->totpoints - 2; i++) {
+ bGPDspoint *pt = &temp_points[i];
+ bGPDspoint *next = &temp_points[i + 1];
+ bGPDspoint *pt_final = &gps->points[i + 1];
+
+ /* move point */
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ }
+ /* free temp memory */
+ MEM_SAFE_FREE(temp_points);
+ }
+ }
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gps);
+}
+
/* 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,7 +2002,431 @@ void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
BKE_gpencil_stroke_geometry_update(gps);
}
-/* Apply Transforms */
+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,
+ int stroke_mat_index,
+ 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, MAX2(stroke_mat_index, 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;
+}
+
+static int gpencil_material_find_index_by_name_prefix(Object *ob, const char *name_prefix)
+{
+ const int name_prefix_len = strlen(name_prefix);
+ for (int i = 0; i < ob->totcol; i++) {
+ Material *ma = BKE_object_material_get(ob, i + 1);
+ if ((ma != NULL) && (ma->gp_style != NULL) &&
+ (STREQLEN(ma->id.name + 2, name_prefix, name_prefix_len))) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+/**
+ * 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.
+ * \simple_material: Create only 2 materials (stroke and fill)
+ */
+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,
+ const bool simple_material)
+{
+ 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;
+ int stroke_mat_index = gpencil_material_find_index_by_name_prefix(ob_gp, "Stroke");
+ int fill_mat_index = gpencil_material_find_index_by_name_prefix(ob_gp, "Fill");
+
+ /* 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) {
+ if (stroke_mat_index == -1) {
+ gpencil_add_material(bmain, ob_gp, "Stroke", default_colors[0], true, false, &r_idx);
+ stroke_mat_index = ob_gp->totcol - 1;
+ }
+ }
+ /* 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 or use simple materials, create a simple fill. */
+ if ((!material_found) || (simple_material)) {
+ if (fill_mat_index == -1) {
+ gpencil_add_material(bmain, ob_gp, "Fill", default_colors[1], false, true, &r_idx);
+ fill_mat_index = ob_gp->totcol - 1;
+ }
+ }
+ 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. */
+ int mat_idx = (simple_material) || (mp->mat_nr + 1 > ob_gp->totcol - 1) ?
+ MAX2(fill_mat_index, 0) :
+ mp->mat_nr + 1;
+
+ bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, 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, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
+
+ /* Tag for recalculation */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+}
+
+/**
+ * Apply grease pencil Transforms.
+ * \param gpd: Grease pencil data-block
+ * \param mat: Transformation matrix
+ */
void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4])
{
if (gpd == NULL) {
@@ -1613,4 +2459,152 @@ void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4])
}
}
}
+
+/* Used for "move only origins" in object_data_transform.c */
+int BKE_gpencil_stroke_point_count(bGPdata *gpd)
+{
+ int total_points = 0;
+
+ if (gpd == NULL) {
+ return 0;
+ }
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* FIXME: For now, we just skip parented layers.
+ * Otherwise, we have to update each frame to find
+ * the current parent position/effects.
+ */
+ if (gpl->parent) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ total_points += gps->totpoints;
+ }
+ }
+ }
+ return total_points;
+}
+
+/* Used for "move only origins" in object_data_transform.c */
+void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_data)
+{
+ if (gpd == NULL) {
+ return;
+ }
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* FIXME: For now, we just skip parented layers.
+ * Otherwise, we have to update each frame to find
+ * the current parent position/effects.
+ */
+ if (gpl->parent) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDspoint *pt;
+ int i;
+
+ for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
+ copy_v3_v3(elem_data->co, &pt->x);
+ elem_data->pressure = pt->pressure;
+ elem_data++;
+ }
+ }
+ }
+ }
+}
+
+/* Used for "move only origins" in object_data_transform.c */
+void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates *elem_data)
+{
+ if (gpd == NULL) {
+ return;
+ }
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* FIXME: For now, we just skip parented layers.
+ * Otherwise, we have to update each frame to find
+ * the current parent position/effects.
+ */
+ if (gpl->parent) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDspoint *pt;
+ int i;
+
+ for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
+ copy_v3_v3(&pt->x, elem_data->co);
+ pt->pressure = elem_data->pressure;
+ elem_data++;
+ }
+
+ /* Distortion may mean we need to re-triangulate. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ }
+ }
+ }
+}
+
+/* Used for "move only origins" in object_data_transform.c */
+void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
+ const GPencilPointCoordinates *elem_data,
+ const float mat[4][4])
+{
+ if (gpd == NULL) {
+ return;
+ }
+
+ const float scalef = mat4_to_scale(mat);
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* FIXME: For now, we just skip parented layers.
+ * Otherwise, we have to update each frame to find
+ * the current parent position/effects.
+ */
+ if (gpl->parent) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDspoint *pt;
+ int i;
+
+ for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
+ mul_v3_m4v3(&pt->x, mat, elem_data->co);
+ pt->pressure = elem_data->pressure * scalef;
+ elem_data++;
+ }
+
+ /* Distortion may mean we need to re-triangulate. */
+ BKE_gpencil_stroke_geometry_update(gps);
+ }
+ }
+ }
+}
+
+/**
+ * Set a random color to stroke using vertex color.
+ * \param gps: Stroke
+ */
+void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
+{
+ BLI_assert(gps->totpoints > 0);
+
+ float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ bGPDspoint *pt = &gps->points[0];
+ color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z));
+ color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x));
+ color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y));
+ for (int i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ copy_v4_v4(pt->vert_color, color);
+ }
+}
/** \} */
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index b889b91e366..e92bf5a4502 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -53,217 +53,10 @@
#include "MOD_gpencil_modifiertypes.h"
-static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL};
-
-/* *************************************************** */
-/* Geometry Utilities */
-
-/* calculate stroke normal using some points */
-void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
-{
- if (gps->totpoints < 3) {
- zero_v3(r_normal);
- return;
- }
-
- bGPDspoint *points = gps->points;
- int totpoints = gps->totpoints;
-
- const bGPDspoint *pt0 = &points[0];
- const bGPDspoint *pt1 = &points[1];
- const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
-
- float vec1[3];
- float vec2[3];
-
- /* initial vector (p0 -> p1) */
- sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
-
- /* point vector at 3/4 */
- sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
-
- /* vector orthogonal to polygon plane */
- cross_v3_v3v3(r_normal, vec1, vec2);
-
- /* Normalize vector */
- normalize_v3(r_normal);
-}
-
-/* Stroke Simplify ------------------------------------- */
-
-/* Reduce a series of points to a simplified version, but
- * maintains the general shape of the series
- *
- * Ramer - Douglas - Peucker algorithm
- * by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
- */
-void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
-{
- bGPDspoint *old_points = MEM_dupallocN(gps->points);
- int totpoints = gps->totpoints;
- char *marked = NULL;
- char work;
-
- int start = 0;
- int end = gps->totpoints - 1;
-
- marked = MEM_callocN(totpoints, "GP marked array");
- marked[start] = 1;
- marked[end] = 1;
-
- work = 1;
- int totmarked = 0;
- /* while still reducing */
- while (work) {
- int ls, le;
- work = 0;
-
- ls = start;
- le = start + 1;
-
- /* while not over interval */
- while (ls < end) {
- int max_i = 0;
- /* divided to get more control */
- float max_dist = epsilon / 10.0f;
-
- /* find the next marked point */
- while (marked[le] == 0) {
- le++;
- }
-
- for (int i = ls + 1; i < le; i++) {
- float point_on_line[3];
- float dist;
-
- closest_to_line_segment_v3(
- point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
-
- dist = len_v3v3(point_on_line, &old_points[i].x);
-
- if (dist > max_dist) {
- max_dist = dist;
- max_i = i;
- }
- }
-
- if (max_i != 0) {
- work = 1;
- marked[max_i] = 1;
- totmarked++;
- }
-
- ls = le;
- le = ls + 1;
- }
- }
-
- /* adding points marked */
- MDeformVert *old_dvert = NULL;
- MDeformVert *dvert_src = NULL;
-
- if (gps->dvert != NULL) {
- old_dvert = MEM_dupallocN(gps->dvert);
- }
- /* resize gps */
- int j = 0;
- for (int i = 0; i < totpoints; i++) {
- bGPDspoint *pt_src = &old_points[i];
- bGPDspoint *pt = &gps->points[j];
+#include "CLG_log.h"
- if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
- memcpy(pt, pt_src, sizeof(bGPDspoint));
- if (gps->dvert != NULL) {
- dvert_src = &old_dvert[i];
- MDeformVert *dvert = &gps->dvert[j];
- memcpy(dvert, dvert_src, sizeof(MDeformVert));
- if (dvert_src->dw) {
- memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
- }
- }
- j++;
- }
- else {
- if (gps->dvert != NULL) {
- dvert_src = &old_dvert[i];
- BKE_gpencil_free_point_weights(dvert_src);
- }
- }
- }
-
- gps->totpoints = j;
-
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
-
- MEM_SAFE_FREE(old_points);
- MEM_SAFE_FREE(old_dvert);
- MEM_SAFE_FREE(marked);
-}
-
-/* Simplify alternate vertex of stroke except extremes */
-void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
-{
- if (gps->totpoints < 5) {
- return;
- }
-
- /* save points */
- bGPDspoint *old_points = MEM_dupallocN(gps->points);
- MDeformVert *old_dvert = NULL;
- MDeformVert *dvert_src = NULL;
-
- if (gps->dvert != NULL) {
- old_dvert = MEM_dupallocN(gps->dvert);
- }
-
- /* resize gps */
- int newtot = (gps->totpoints - 2) / 2;
- if (((gps->totpoints - 2) % 2) > 0) {
- newtot++;
- }
- newtot += 2;
-
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
- }
-
- int j = 0;
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt_src = &old_points[i];
- bGPDspoint *pt = &gps->points[j];
-
- if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
- memcpy(pt, pt_src, sizeof(bGPDspoint));
- if (gps->dvert != NULL) {
- dvert_src = &old_dvert[i];
- MDeformVert *dvert = &gps->dvert[j];
- memcpy(dvert, dvert_src, sizeof(MDeformVert));
- if (dvert_src->dw) {
- memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
- }
- }
- j++;
- }
- else {
- if (gps->dvert != NULL) {
- dvert_src = &old_dvert[i];
- BKE_gpencil_free_point_weights(dvert_src);
- }
- }
- }
-
- gps->totpoints = j;
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
-
- MEM_SAFE_FREE(old_points);
- MEM_SAFE_FREE(old_dvert);
-}
-
-/* *************************************************** */
-/* Modifier Utilities */
+static CLG_LogRef LOG = {"bke.gpencil_modifier"};
+static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL};
/* Lattice Modifier ---------------------------------- */
/* Usually, evaluation of the lattice modifier is self-contained.
@@ -272,11 +65,13 @@ void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
* each loop over all the geometry being evaluated.
*/
-/* init lattice deform data */
+/**
+ * Init grease pencil lattice deform data.
+ * \param ob: Grease pencil object
+ */
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;
@@ -286,24 +81,26 @@ void BKE_gpencil_lattice_init(Object *ob)
return;
}
if (mmd->cache_data) {
- end_latt_deform((struct LatticeDeformData *)mmd->cache_data);
+ BKE_lattice_deform_data_destroy(mmd->cache_data);
}
/* init deform data */
- mmd->cache_data = (struct LatticeDeformData *)init_latt_deform(latob, ob);
+ mmd->cache_data = BKE_lattice_deform_data_create(latob, ob);
}
}
}
-/* clear lattice deform data */
+/**
+ * Clear grease pencil lattice deform data.
+ * \param ob: Grease pencil object
+ */
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)) {
- end_latt_deform((struct LatticeDeformData *)mmd->cache_data);
+ BKE_lattice_deform_data_destroy(mmd->cache_data);
mmd->cache_data = NULL;
}
}
@@ -313,11 +110,14 @@ void BKE_gpencil_lattice_clear(Object *ob)
/* *************************************************** */
/* Modifier Methods - Evaluation Loops, etc. */
-/* check if exist geometry modifiers */
+/**
+ * Check if object has grease pencil Geometry modifiers.
+ * \param ob: Grease pencil object
+ * \return True if exist
+ */
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) {
@@ -327,11 +127,14 @@ bool BKE_gpencil_has_geometry_modifiers(Object *ob)
return false;
}
-/* check if exist time modifiers */
+/**
+ * Check if object has grease pencil Time modifiers.
+ * \param ob: Grease pencil object
+ * \return True if exist
+ */
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) {
@@ -341,11 +144,14 @@ bool BKE_gpencil_has_time_modifiers(Object *ob)
return false;
}
-/* Check if exist transform stroke modifiers (to rotate sculpt or edit). */
+/**
+ * Check if object has grease pencil transform stroke modifiers.
+ * \param ob: Grease pencil object
+ * \return True if exist
+ */
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 +168,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);
@@ -388,8 +193,12 @@ static int gpencil_time_modifier(
/* if no time modifier, return original frame number */
return nfra;
}
-/* *************************************************** */
+/**
+ * Set current grease pencil active frame.
+ * \param depsgraph: Current depsgraph
+ * \param gpd: Grease pencil data-block.
+ */
void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd)
{
DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd);
@@ -413,12 +222,20 @@ void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd)
}
}
+/**
+ * Initialize grease pencil modifier.
+ */
void BKE_gpencil_modifier_init(void)
{
/* Initialize modifier types */
gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */
}
+/**
+ * Create new grease pencil modifier.
+ * \param type: Type of modifier
+ * \return New modifier pointer
+ */
GpencilModifierData *BKE_gpencil_modifier_new(int type)
{
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(type);
@@ -428,9 +245,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;
@@ -454,6 +271,11 @@ static void modifier_free_data_id_us_cb(void *UNUSED(userData),
}
}
+/**
+ * Free grease pencil modifier data
+ * \param md: Modifier data
+ * \param flag: Flags
+ */
void BKE_gpencil_modifier_free_ex(GpencilModifierData *md, const int flag)
{
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
@@ -478,6 +300,10 @@ void BKE_gpencil_modifier_free_ex(GpencilModifierData *md, const int flag)
MEM_freeN(md);
}
+/**
+ * Free grease pencil modifier data
+ * \param md: Modifier data
+ */
void BKE_gpencil_modifier_free(GpencilModifierData *md)
{
BKE_gpencil_modifier_free_ex(md, 0);
@@ -498,6 +324,11 @@ bool BKE_gpencil_modifier_unique_name(ListBase *modifiers, GpencilModifierData *
return false;
}
+/**
+ * Check if grease pencil modifier depends on time.
+ * \param md: Modifier data
+ * \return True if depends on time
+ */
bool BKE_gpencil_modifier_depends_ontime(GpencilModifierData *md)
{
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
@@ -505,10 +336,16 @@ bool BKE_gpencil_modifier_depends_ontime(GpencilModifierData *md)
return mti->dependsOnTime && mti->dependsOnTime(md);
}
+/**
+ * Get grease pencil modifier information.
+ * \param type: Type of modifier
+ * \return Pointer to type
+ */
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 +353,25 @@ const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType
}
}
+/**
+ * Get the idname of the modifier type's panel, which was defined in the #panelRegister callback.
+ *
+ * \param type: Type of modifier
+ * \param r_idname: ID name
+ */
+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);
+}
+
+/**
+ * Generic grease pencil modifier copy data.
+ * \param md_src: Source modifier data
+ * \param md_dst: Target modifier data
+ */
void BKE_gpencil_modifier_copydata_generic(const GpencilModifierData *md_src,
GpencilModifierData *md_dst)
{
@@ -545,6 +401,12 @@ static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData),
}
}
+/**
+ * Copy grease pencil modifier data.
+ * \param md: Source modifier data
+ * \param target: Target modifier data
+ * \parm flag: Flags
+ */
void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md,
GpencilModifierData *target,
const int flag)
@@ -553,6 +415,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);
@@ -569,6 +432,11 @@ void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md,
}
}
+/**
+ * Copy grease pencil modifier data.
+ * \param md: Source modifier data
+ * \param target: Target modifier data
+ */
void BKE_gpencil_modifier_copydata(GpencilModifierData *md, GpencilModifierData *target)
{
BKE_gpencil_modifier_copydata_ex(md, target, 0);
@@ -587,6 +455,37 @@ GpencilModifierData *BKE_gpencil_modifiers_findby_type(Object *ob, GpencilModifi
return md;
}
+/**
+ * Set grease pencil modifier error.
+ * \param md: Modifier data
+ * \param _format: Format
+ */
+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);
+}
+
+/**
+ * Link grease pencil modifier related IDs.
+ * \param ob: Grease pencil object
+ * \param walk: Walk option
+ * \param userData: User data
+ */
void BKE_gpencil_modifiers_foreach_ID_link(Object *ob, GreasePencilIDWalkFunc walk, void *userData)
{
GpencilModifierData *md = ob->greasepencil_modifiers.first;
@@ -605,6 +504,12 @@ void BKE_gpencil_modifiers_foreach_ID_link(Object *ob, GreasePencilIDWalkFunc wa
}
}
+/**
+ * Link grease pencil modifier related Texts.
+ * \param ob: Grease pencil object
+ * \param walk: Walk option
+ * \param userData: User data
+ */
void BKE_gpencil_modifiers_foreach_tex_link(Object *ob,
GreasePencilTexWalkFunc walk,
void *userData)
@@ -620,124 +525,25 @@ void BKE_gpencil_modifiers_foreach_tex_link(Object *ob,
}
}
+/**
+ * Find grease pencil modifier by name.
+ * \param ob: Grease pencil object
+ * \param name: Name to find
+ * \return Pointer to modifier
+ */
GpencilModifierData *BKE_gpencil_modifiers_findby_name(Object *ob, const char *name)
{
return BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name));
}
-void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
-{
- bGPDspoint *temp_points;
- MDeformVert *temp_dverts = NULL;
- MDeformVert *dvert = NULL;
- MDeformVert *dvert_final = NULL;
- MDeformVert *dvert_next = NULL;
- int totnewpoints, oldtotpoints;
- int i2;
-
- for (int s = 0; s < level; s++) {
- totnewpoints = gps->totpoints - 1;
- /* duplicate points in a temp area */
- temp_points = MEM_dupallocN(gps->points);
- oldtotpoints = gps->totpoints;
-
- /* resize the points arrays */
- gps->totpoints += totnewpoints;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
- if (gps->dvert != NULL) {
- temp_dverts = MEM_dupallocN(gps->dvert);
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
- }
-
- /* move points from last to first to new place */
- i2 = gps->totpoints - 1;
- for (int i = oldtotpoints - 1; i > 0; i--) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *pt_final = &gps->points[i2];
-
- copy_v3_v3(&pt_final->x, &pt->x);
- pt_final->pressure = pt->pressure;
- pt_final->strength = pt->strength;
- pt_final->time = pt->time;
- pt_final->flag = pt->flag;
- pt_final->runtime.pt_orig = pt->runtime.pt_orig;
- pt_final->runtime.idx_orig = pt->runtime.idx_orig;
- copy_v4_v4(pt_final->vert_color, pt->vert_color);
-
- if (gps->dvert != NULL) {
- dvert = &temp_dverts[i];
- dvert_final = &gps->dvert[i2];
- dvert_final->totweight = dvert->totweight;
- dvert_final->dw = dvert->dw;
- }
- i2 -= 2;
- }
- /* interpolate mid points */
- i2 = 1;
- for (int i = 0; i < oldtotpoints - 1; i++) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *next = &temp_points[i + 1];
- bGPDspoint *pt_final = &gps->points[i2];
-
- /* add a half way point */
- interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
- pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
- pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
- CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
- pt_final->time = interpf(pt->time, next->time, 0.5f);
- pt_final->runtime.pt_orig = NULL;
- pt_final->flag = 0;
- interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
-
- if (gps->dvert != NULL) {
- dvert = &temp_dverts[i];
- dvert_next = &temp_dverts[i + 1];
- dvert_final = &gps->dvert[i2];
-
- dvert_final->totweight = dvert->totweight;
- dvert_final->dw = MEM_dupallocN(dvert->dw);
-
- /* interpolate weight values */
- for (int d = 0; d < dvert->totweight; d++) {
- MDeformWeight *dw_a = &dvert->dw[d];
- if (dvert_next->totweight > d) {
- MDeformWeight *dw_b = &dvert_next->dw[d];
- MDeformWeight *dw_final = &dvert_final->dw[d];
- dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
- }
- }
- }
-
- i2 += 2;
- }
-
- MEM_SAFE_FREE(temp_points);
- MEM_SAFE_FREE(temp_dverts);
-
- /* move points to smooth stroke (not simple type )*/
- if (type != GP_SUBDIV_SIMPLE) {
- /* duplicate points in a temp area with the new subdivide data */
- temp_points = MEM_dupallocN(gps->points);
-
- /* extreme points are not changed */
- for (int i = 0; i < gps->totpoints - 2; i++) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *next = &temp_points[i + 1];
- bGPDspoint *pt_final = &gps->points[i + 1];
-
- /* move point */
- interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
- }
- /* free temp memory */
- MEM_SAFE_FREE(temp_points);
- }
- }
-
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gps);
-}
-
-/* Remap frame (Time modifier) */
+/**
+ * Remap grease pencil frame (Time modifier)
+ * \param depsgraph: Current depsgraph
+ * \param scene: Current scene
+ * \param ob: Grease pencil object
+ * \param gpl: Grease pencil layer
+ * \return New frame number
+ */
static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl)
{
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
@@ -752,7 +558,13 @@ static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob
return remap_cfra;
}
-/* Get the current frame retimed with time modifiers. */
+/** Get the current frame re-timed with time modifiers.
+ * \param depsgraph: Current depsgraph.
+ * \param scene: Current scene
+ * \param ob: Grease pencil object
+ * \param gpl: Grease pencil layer
+ * \return New frame number
+ */
bGPDframe *BKE_gpencil_frame_retime_get(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
@@ -820,6 +632,12 @@ static bGPdata *gpencil_copy_for_eval(bGPdata *gpd)
return result;
}
+/**
+ * Prepare grease pencil eval data for modifiers
+ * \param depsgraph: Current depsgraph
+ * \param scene: Current scene
+ * \param ob: Grease pencil object
+ */
void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
bGPdata *gpd_eval = (bGPdata *)ob->data;
@@ -859,10 +677,14 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
}
ob->runtime.gpd_eval = gpencil_copy_for_eval(ob->runtime.gpd_orig);
gpencil_assign_object_eval(ob);
- BKE_gpencil_update_orig_pointers(ob_orig, (Object *)ob);
+ BKE_gpencil_update_orig_pointers(ob_orig, ob);
}
-/* Calculate gpencil modifiers */
+/** Calculate gpencil modifiers.
+ * \param depsgraph: Current depsgraph
+ * \param scene: Current scene
+ * \param ob: Grease pencil object
+ */
void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
bGPdata *gpd = (bGPdata *)ob->data;
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/idtype.c b/source/blender/blenkernel/intern/idtype.c
index fcd3bc9c5b4..1166ad9ad2f 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -28,6 +28,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_ghash.h"
#include "BLI_utildefines.h"
#include "CLG_log.h"
@@ -35,13 +36,32 @@
#include "BLT_translation.h"
#include "DNA_ID.h"
+#include "DNA_node_types.h"
+#include "DNA_scene_types.h"
#include "BKE_main.h"
+#include "BKE_node.h"
#include "BKE_idtype.h"
// static CLG_LogRef LOG = {"bke.idtype"};
+uint BKE_idtype_cache_key_hash(const void *key_v)
+{
+ const IDCacheKey *key = key_v;
+ size_t hash = BLI_ghashutil_uinthash(key->id_session_uuid);
+ hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_uinthash((uint)key->offset_in_ID));
+ return (uint)BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(key->cache_v));
+}
+
+bool BKE_idtype_cache_key_cmp(const void *key_a_v, const void *key_b_v)
+{
+ const IDCacheKey *key_a = key_a_v;
+ const IDCacheKey *key_b = key_b_v;
+ return (key_a->id_session_uuid != key_b->id_session_uuid) ||
+ (key_a->offset_in_ID != key_b->offset_in_ID) || (key_a->cache_v != key_b->cache_v);
+}
+
static IDTypeInfo *id_types[MAX_LIBARRAY] = {NULL};
static void id_type_init(void)
@@ -453,3 +473,33 @@ short BKE_idtype_idcode_iter_step(int *index)
{
return (*index < ARRAY_SIZE(id_types)) ? BKE_idtype_idcode_from_index((*index)++) : 0;
}
+
+/** Wrapper around IDTypeInfo foreach_cache that also handles embedded IDs. */
+void BKE_idtype_id_foreach_cache(struct ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id);
+ if (type_info->foreach_cache != NULL) {
+ type_info->foreach_cache(id, function_callback, user_data);
+ }
+
+ /* Handle 'private IDs'. */
+ bNodeTree *nodetree = ntreeFromID(id);
+ if (nodetree != NULL) {
+ type_info = BKE_idtype_get_info_from_id(&nodetree->id);
+ if (type_info->foreach_cache != NULL) {
+ type_info->foreach_cache(&nodetree->id, function_callback, user_data);
+ }
+ }
+
+ if (GS(id->name) == ID_SCE) {
+ Scene *scene = (Scene *)id;
+ if (scene->master_collection != NULL) {
+ type_info = BKE_idtype_get_info_from_id(&scene->master_collection->id);
+ if (type_info->foreach_cache != NULL) {
+ type_info->foreach_cache(&scene->master_collection->id, function_callback, user_data);
+ }
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index b4a3f249c63..fa0be9ea441 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -57,6 +57,7 @@
#include "DNA_packedFile_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "DNA_simulation_types.h"
#include "DNA_world_types.h"
#include "BLI_blenlib.h"
@@ -64,7 +65,7 @@
#include "BLI_mempool.h"
#include "BLI_system.h"
#include "BLI_threads.h"
-#include "BLI_timecode.h" /* for stamp timecode format */
+#include "BLI_timecode.h" /* For stamp time-code format. */
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -89,7 +90,6 @@
#include "RE_pipeline.h"
-#include "GPU_draw.h"
#include "GPU_texture.h"
#include "BLI_sys_types.h" // for intptr_t support
@@ -185,6 +185,37 @@ static void image_free_data(ID *id)
BLI_freelistN(&image->tiles);
}
+static void image_foreach_cache(ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ Image *image = (Image *)id;
+ IDCacheKey key = {
+ .id_session_uuid = id->session_uuid,
+ .offset_in_ID = offsetof(Image, cache),
+ .cache_v = image->cache,
+ };
+ function_callback(id, &key, (void **)&image->cache, 0, user_data);
+
+ for (int eye = 0; eye < 2; eye++) {
+ for (int a = 0; a < TEXTARGET_COUNT; a++) {
+ key.offset_in_ID = offsetof(Image, gputexture[a][eye]);
+ key.cache_v = image->gputexture[a][eye];
+ function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data);
+ }
+ }
+
+ key.offset_in_ID = offsetof(Image, rr);
+ key.cache_v = image->rr;
+ function_callback(id, &key, (void **)&image->rr, 0, user_data);
+
+ LISTBASE_FOREACH (RenderSlot *, slot, &image->renderslots) {
+ key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(slot->name);
+ key.cache_v = slot->render;
+ function_callback(id, &key, (void **)&slot->render, 0, user_data);
+ }
+}
+
IDTypeInfo IDType_ID_IM = {
.id_code = ID_IM,
.id_filter = FILTER_ID_IM,
@@ -200,6 +231,7 @@ IDTypeInfo IDType_ID_IM = {
.free_data = image_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = image_foreach_cache,
};
/* prototypes */
@@ -360,13 +392,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);
- }
+ BKE_image_free_gputextures(ima);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
tile->ok = IMA_OK;
@@ -648,7 +674,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 +694,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 +720,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 +856,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 +872,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 +907,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 +1007,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 +1046,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);
}
}
@@ -3214,6 +3240,12 @@ static void image_walk_id_all_users(
if (scene->nodetree && scene->use_nodes && !skip_nested_nodes) {
image_walk_ntree_all_users(scene->nodetree, &scene->id, customdata, callback);
}
+ break;
+ }
+ case ID_SIM: {
+ Simulation *simulation = (Simulation *)id;
+ image_walk_ntree_all_users(simulation->nodetree, &simulation->id, customdata, callback);
+ break;
}
default:
break;
@@ -3318,8 +3350,7 @@ static void image_free_tile(Image *ima, ImageTile *tile)
for (int i = 0; i < TEXTARGET_COUNT; i++) {
/* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other
* two. */
- if (tile != ima->tiles.first &&
- !(ELEM(i, TEXTARGET_TEXTURE_2D_ARRAY, TEXTARGET_TEXTURE_TILE_MAPPING))) {
+ if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) {
continue;
}
@@ -3385,7 +3416,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) {
@@ -3580,7 +3611,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la
}
ImageTile *tile = MEM_callocN(sizeof(ImageTile), "image new tile");
- tile->ok = 1;
+ tile->ok = IMA_OK;
tile->tile_number = tile_number;
if (next_tile) {
@@ -3596,13 +3627,13 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la
for (int eye = 0; eye < 2; eye++) {
/* Reallocate GPU tile array. */
- if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] != NULL) {
- GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye]);
- ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] = NULL;
+ if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) {
+ GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]);
+ ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL;
}
- if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] != NULL) {
- GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye]);
- ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] = NULL;
+ if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) {
+ GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]);
+ ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL;
}
}
@@ -3643,12 +3674,12 @@ 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);
BKE_image_release_ibuf(ima, tile_ibuf, NULL);
- tile->ok = 1;
+ tile->ok = IMA_OK;
return true;
}
return false;
@@ -3807,7 +3838,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);
@@ -4337,7 +4368,7 @@ static ImBuf *load_image_single(Image *ima,
/* make packed file for autopack */
if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) {
- ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile");
+ ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Pack-file");
BLI_addtail(&ima->packedfiles, imapf);
STRNCPY(imapf->filepath, filepath);
@@ -4893,7 +4924,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,
@@ -5187,24 +5218,32 @@ int BKE_image_user_frame_get(const ImageUser *iuser, int cfra, bool *r_is_in_ran
void BKE_image_user_frame_calc(Image *ima, ImageUser *iuser, int cfra)
{
if (iuser) {
- bool is_in_range;
- const int framenr = BKE_image_user_frame_get(iuser, cfra, &is_in_range);
+ if (ima && BKE_image_is_animated(ima)) {
+ /* Compute current frame for animated image. */
+ bool is_in_range;
+ const int framenr = BKE_image_user_frame_get(iuser, cfra, &is_in_range);
- if (is_in_range) {
- iuser->flag |= IMA_USER_FRAME_IN_RANGE;
+ if (is_in_range) {
+ iuser->flag |= IMA_USER_FRAME_IN_RANGE;
+ }
+ else {
+ iuser->flag &= ~IMA_USER_FRAME_IN_RANGE;
+ }
+
+ iuser->framenr = framenr;
}
else {
- iuser->flag &= ~IMA_USER_FRAME_IN_RANGE;
+ /* Set fixed frame number for still image. */
+ iuser->framenr = 0;
+ iuser->flag |= IMA_USER_FRAME_IN_RANGE;
}
- iuser->framenr = framenr;
-
- if (ima && BKE_image_is_animated(ima) && ima->gpuframenr != framenr) {
+ if (ima && ima->gpuframenr != iuser->framenr) {
/* Note: a single texture and refresh doesn't really work when
* multiple image users may use different frames, this is to
* be improved with perhaps a GPU texture cache. */
ima->gpuflag |= IMA_GPU_REFRESH;
- ima->gpuframenr = framenr;
+ ima->gpuframenr = iuser->framenr;
}
if (iuser->ok == 0) {
@@ -5300,11 +5339,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 +5500,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 +5517,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 +5692,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_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
new file mode 100644
index 00000000000..e3ed7cdc8de
--- /dev/null
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -0,0 +1,777 @@
+/*
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_boxpack_2d.h"
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_threads.h"
+
+#include "DNA_image_types.h"
+#include "DNA_userdef_types.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+
+#include "GPU_extensions.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "PIL_time.h"
+
+/* Prototypes. */
+static void gpu_free_unused_buffers(void);
+static void image_free_gpu(Image *ima, const bool immediate);
+
+/* -------------------------------------------------------------------- */
+/** \name UDIM gpu texture
+ * \{ */
+
+static bool is_over_resolution_limit(int w, int h)
+{
+ return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h));
+}
+
+static int smaller_power_of_2_limit(int num)
+{
+ return power_of_2_min_i(GPU_texture_size_with_limit(num));
+}
+
+static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye)
+{
+ GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye];
+
+ if (tilearray == NULL) {
+ return 0;
+ }
+
+ float array_w = GPU_texture_width(tilearray);
+ float array_h = GPU_texture_height(tilearray);
+
+ ImageTile *last_tile = (ImageTile *)ima->tiles.last;
+ /* Tiles are sorted by number. */
+ int max_tile = last_tile->tile_number - 1001;
+
+ /* create image */
+ int width = max_tile + 1;
+ float *data = (float *)MEM_callocN(width * 8 * sizeof(float), __func__);
+ for (int i = 0; i < width; i++) {
+ data[4 * i] = -1.0f;
+ }
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ int i = tile->tile_number - 1001;
+ data[4 * i] = tile->runtime.tilearray_layer;
+
+ float *tile_info = &data[4 * width + 4 * i];
+ tile_info[0] = tile->runtime.tilearray_offset[0] / array_w;
+ tile_info[1] = tile->runtime.tilearray_offset[1] / array_h;
+ tile_info[2] = tile->runtime.tilearray_size[0] / array_w;
+ tile_info[3] = tile->runtime.tilearray_size[1] / array_h;
+ }
+
+ GPUTexture *tex = GPU_texture_create_1d_array(width, 2, GPU_RGBA32F, data, NULL);
+ GPU_texture_mipmap_mode(tex, false, false);
+
+ MEM_freeN(data);
+
+ return tex;
+}
+
+typedef struct PackTile {
+ FixedSizeBoxPack boxpack;
+ ImageTile *tile;
+ float pack_score;
+} PackTile;
+
+static int compare_packtile(const void *a, const void *b)
+{
+ const PackTile *tile_a = (const PackTile *)a;
+ const PackTile *tile_b = (const PackTile *)b;
+
+ return tile_a->pack_score < tile_b->pack_score;
+}
+
+static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
+{
+ int arraywidth = 0, arrayheight = 0;
+ ListBase boxes = {NULL};
+
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ ImageUser iuser;
+ BKE_imageuser_default(&iuser);
+ iuser.tile = tile->tile_number;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+
+ if (ibuf) {
+ PackTile *packtile = (PackTile *)MEM_callocN(sizeof(PackTile), __func__);
+ packtile->tile = tile;
+ packtile->boxpack.w = ibuf->x;
+ packtile->boxpack.h = ibuf->y;
+
+ if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) {
+ packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w);
+ packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h);
+ }
+ arraywidth = max_ii(arraywidth, packtile->boxpack.w);
+ arrayheight = max_ii(arrayheight, packtile->boxpack.h);
+
+ /* We sort the tiles by decreasing size, with an additional penalty term
+ * for high aspect ratios. This improves packing efficiency. */
+ float w = packtile->boxpack.w, h = packtile->boxpack.h;
+ packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h;
+
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ BLI_addtail(&boxes, packtile);
+ }
+ }
+
+ BLI_assert(arraywidth > 0 && arrayheight > 0);
+
+ BLI_listbase_sort(&boxes, compare_packtile);
+ int arraylayers = 0;
+ /* Keep adding layers until all tiles are packed. */
+ while (boxes.first != NULL) {
+ ListBase packed = {NULL};
+ BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed);
+ BLI_assert(packed.first != NULL);
+
+ LISTBASE_FOREACH (PackTile *, packtile, &packed) {
+ ImageTile *tile = packtile->tile;
+ int *tileoffset = tile->runtime.tilearray_offset;
+ int *tilesize = tile->runtime.tilearray_size;
+
+ tileoffset[0] = packtile->boxpack.x;
+ tileoffset[1] = packtile->boxpack.y;
+ tilesize[0] = packtile->boxpack.w;
+ tilesize[1] = packtile->boxpack.h;
+ tile->runtime.tilearray_layer = arraylayers;
+ }
+
+ BLI_freelistN(&packed);
+ arraylayers++;
+ }
+
+ const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH);
+ /* Create Texture without content. */
+ GPUTexture *tex = IMB_touch_gpu_texture(
+ main_ibuf, arraywidth, arrayheight, arraylayers, use_high_bitdepth);
+
+ GPU_texture_bind(tex, 0);
+
+ /* Upload each tile one by one. */
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ int tilelayer = tile->runtime.tilearray_layer;
+ int *tileoffset = tile->runtime.tilearray_offset;
+ int *tilesize = tile->runtime.tilearray_size;
+
+ if (tilesize[0] == 0 || tilesize[1] == 0) {
+ continue;
+ }
+
+ ImageUser iuser;
+ BKE_imageuser_default(&iuser);
+ iuser.tile = tile->tile_number;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+
+ if (ibuf) {
+ const bool store_premultiplied = ibuf->rect_float ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) :
+ (ima->alpha_mode == IMA_ALPHA_PREMUL);
+ IMB_update_gpu_texture_sub(tex,
+ ibuf,
+ UNPACK2(tileoffset),
+ tilelayer,
+ UNPACK2(tilesize),
+ use_high_bitdepth,
+ store_premultiplied);
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ }
+
+ if (GPU_mipmap_enabled()) {
+ GPU_texture_generate_mipmap(tex);
+ GPU_texture_mipmap_mode(tex, true, true);
+ if (ima) {
+ ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
+ }
+ }
+ else {
+ GPU_texture_mipmap_mode(tex, false, true);
+ }
+
+ GPU_texture_unbind(tex);
+
+ return tex;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Regular gpu texture
+ * \{ */
+
+static GPUTexture **get_image_gpu_texture_ptr(Image *ima,
+ eGPUTextureTarget textarget,
+ const int multiview_eye)
+{
+ const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT);
+ BLI_assert(in_range);
+
+ if (in_range) {
+ return &(ima->gputexture[textarget][multiview_eye]);
+ }
+ return NULL;
+}
+
+static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget)
+{
+ switch (textarget) {
+ case TEXTARGET_2D_ARRAY:
+ return GPU_texture_create_error(2, true);
+ case TEXTARGET_TILE_MAPPING:
+ return GPU_texture_create_error(1, true);
+ case TEXTARGET_2D:
+ default:
+ return GPU_texture_create_error(2, false);
+ }
+}
+
+static GPUTexture *image_get_gpu_texture(Image *ima,
+ ImageUser *iuser,
+ ImBuf *ibuf,
+ eGPUTextureTarget textarget)
+{
+ if (ima == NULL) {
+ 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) {
+ image_free_gpu(ima, true);
+ ima->gpuflag &= ~IMA_GPU_REFRESH;
+ }
+
+ /* Tag as in active use for garbage collector. */
+ BKE_image_tag_time(ima);
+
+ /* Test if we already have a texture. */
+ GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, iuser ? iuser->multiview_eye : 0);
+ if (*tex) {
+ return *tex;
+ }
+
+ /* Check if we have a valid image. If not, we return a dummy
+ * texture with zero bind-code so we don't keep trying. */
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ if (tile == NULL || tile->ok == 0) {
+ *tex = image_gpu_texture_error_create(textarget);
+ return *tex;
+ }
+
+ /* check if we have a valid image buffer */
+ ImBuf *ibuf_intern = ibuf;
+ if (ibuf_intern == NULL) {
+ ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL);
+ if (ibuf_intern == NULL) {
+ *tex = image_gpu_texture_error_create(textarget);
+ return *tex;
+ }
+ }
+
+ if (textarget == TEXTARGET_2D_ARRAY) {
+ *tex = gpu_texture_create_tile_array(ima, ibuf_intern);
+ }
+ else if (textarget == TEXTARGET_TILE_MAPPING) {
+ *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0);
+ }
+ else {
+ const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH);
+ const bool store_premultiplied = ibuf_intern->rect_float ?
+ (ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false) :
+ (ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true);
+
+ *tex = IMB_create_gpu_texture(ibuf_intern, use_high_bitdepth, store_premultiplied);
+
+ if (GPU_mipmap_enabled()) {
+ GPU_texture_bind(*tex, 0);
+ GPU_texture_generate_mipmap(*tex);
+ GPU_texture_unbind(*tex);
+ if (ima) {
+ ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
+ }
+ GPU_texture_mipmap_mode(*tex, true, true);
+ }
+ else {
+ GPU_texture_mipmap_mode(*tex, false, true);
+ }
+ }
+
+ /* if `ibuf` was given, we don't own the `ibuf_intern` */
+ if (ibuf == NULL) {
+ BKE_image_release_ibuf(ima, ibuf_intern, NULL);
+ }
+
+ GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
+
+ return *tex;
+}
+
+GPUTexture *BKE_image_get_gpu_texture(Image *image, ImageUser *iuser, ImBuf *ibuf)
+{
+ return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D);
+}
+
+GPUTexture *BKE_image_get_gpu_tiles(Image *image, ImageUser *iuser, ImBuf *ibuf)
+{
+ return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D_ARRAY);
+}
+
+GPUTexture *BKE_image_get_gpu_tilemap(Image *image, ImageUser *iuser, ImBuf *ibuf)
+{
+ return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_TILE_MAPPING);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name 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_free_unused_buffers(void)
+{
+ if (gpu_texture_free_queue == NULL) {
+ return;
+ }
+
+ BLI_mutex_lock(&gpu_texture_queue_mutex);
+
+ if (gpu_texture_free_queue != NULL) {
+ GPUTexture *tex;
+ while ((tex = (GPUTexture *)BLI_linklist_pop(&gpu_texture_free_queue))) {
+ GPU_texture_free(tex);
+ }
+ gpu_texture_free_queue = NULL;
+ }
+
+ BLI_mutex_unlock(&gpu_texture_queue_mutex);
+}
+
+void BKE_image_free_unused_gpu_textures()
+{
+ if (BLI_thread_is_main()) {
+ gpu_free_unused_buffers();
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Deletion
+ * \{ */
+
+static void image_free_gpu(Image *ima, const bool immediate)
+{
+ for (int eye = 0; eye < 2; eye++) {
+ for (int i = 0; i < TEXTARGET_COUNT; i++) {
+ if (ima->gputexture[i][eye] != NULL) {
+ 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;
+}
+
+void BKE_image_free_gputextures(Image *ima)
+{
+ image_free_gpu(ima, BLI_thread_is_main());
+}
+
+void BKE_image_free_all_gputextures(Main *bmain)
+{
+ if (bmain) {
+ LISTBASE_FOREACH (Image *, ima, &bmain->images) {
+ BKE_image_free_gputextures(ima);
+ }
+ }
+}
+
+/* same as above but only free animated images */
+void BKE_image_free_anim_gputextures(Main *bmain)
+{
+ if (bmain) {
+ LISTBASE_FOREACH (Image *, ima, &bmain->images) {
+ if (BKE_image_is_animated(ima)) {
+ BKE_image_free_gputextures(ima);
+ }
+ }
+ }
+}
+
+void BKE_image_free_old_gputextures(Main *bmain)
+{
+ static int lasttime = 0;
+ int ctime = (int)PIL_check_seconds_timer();
+
+ /*
+ * Run garbage collector once for every collecting period of time
+ * if textimeout is 0, that's the option to NOT run the collector
+ */
+ if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) {
+ return;
+ }
+
+ /* of course not! */
+ if (G.is_rendering) {
+ return;
+ }
+
+ lasttime = ctime;
+
+ LISTBASE_FOREACH (Image *, ima, &bmain->images) {
+ if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) {
+ /* If it's in GL memory, deallocate and set time tag to current time
+ * This gives textures a "second chance" to be used before dying. */
+ if (BKE_image_has_opengl_texture(ima)) {
+ BKE_image_free_gputextures(ima);
+ ima->lastused = ctime;
+ }
+ /* Otherwise, just kill the buffers */
+ else {
+ BKE_image_free_buffers(ima);
+ }
+ }
+ }
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Paint Update
+ * \{ */
+
+static ImBuf *update_do_scale(uchar *rect,
+ float *rect_float,
+ int *x,
+ int *y,
+ int *w,
+ int *h,
+ int limit_w,
+ int limit_h,
+ int full_w,
+ int full_h)
+{
+ /* Partial update with scaling. */
+ float xratio = limit_w / (float)full_w;
+ float yratio = limit_h / (float)full_h;
+
+ int part_w = *w, part_h = *h;
+
+ /* Find sub coordinates in scaled image. Take ceiling because we will be
+ * losing 1 pixel due to rounding errors in x,y. */
+ *x *= xratio;
+ *y *= yratio;
+ *w = (int)ceil(xratio * (*w));
+ *h = (int)ceil(yratio * (*h));
+
+ /* ...but take back if we are over the limit! */
+ if (*x + *w > limit_w) {
+ (*w)--;
+ }
+ if (*y + *h > limit_h) {
+ (*h)--;
+ }
+
+ /* Scale pixels. */
+ ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4);
+ IMB_scaleImBuf(ibuf, *w, *h);
+
+ return ibuf;
+}
+
+static void gpu_texture_update_scaled(GPUTexture *tex,
+ uchar *rect,
+ float *rect_float,
+ int full_w,
+ int full_h,
+ int x,
+ int y,
+ int layer,
+ const int *tile_offset,
+ const int *tile_size,
+ int w,
+ int h)
+{
+ ImBuf *ibuf;
+ if (layer > -1) {
+ ibuf = update_do_scale(
+ rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h);
+
+ /* Shift to account for tile packing. */
+ x += tile_offset[0];
+ y += tile_offset[1];
+ }
+ else {
+ /* Partial update with scaling. */
+ int limit_w = smaller_power_of_2_limit(full_w);
+ int limit_h = smaller_power_of_2_limit(full_h);
+
+ ibuf = update_do_scale(rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h);
+ }
+
+ void *data = (ibuf->rect_float) ? (void *)(ibuf->rect_float) : (void *)(ibuf->rect);
+ eGPUDataFormat data_format = (ibuf->rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE;
+
+ GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1);
+
+ IMB_freeImBuf(ibuf);
+}
+
+static void gpu_texture_update_unscaled(GPUTexture *tex,
+ uchar *rect,
+ float *rect_float,
+ int x,
+ int y,
+ int layer,
+ const int tile_offset[2],
+ int w,
+ int h,
+ int tex_stride,
+ int tex_offset)
+{
+ if (layer > -1) {
+ /* Shift to account for tile packing. */
+ x += tile_offset[0];
+ y += tile_offset[1];
+ }
+
+ void *data = (rect_float) ? (void *)(rect_float + tex_offset) : (void *)(rect + tex_offset);
+ eGPUDataFormat data_format = (rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE;
+
+ /* Partial update without scaling. Stride and offset are used to copy only a
+ * subset of a possible larger buffer than what we are updating. */
+ GPU_unpack_row_length_set(tex_stride);
+
+ GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1);
+ /* Restore default. */
+ GPU_unpack_row_length_set(0);
+}
+
+static void gpu_texture_update_from_ibuf(
+ GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h)
+{
+ bool scaled;
+ if (tile != NULL) {
+ int *tilesize = tile->runtime.tilearray_size;
+ scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]);
+ }
+ else {
+ scaled = is_over_resolution_limit(ibuf->x, ibuf->y);
+ }
+
+ if (scaled) {
+ /* Extra padding to account for bleed from neighboring pixels. */
+ const int padding = 4;
+ const int xmax = min_ii(x + w + padding, ibuf->x);
+ const int ymax = min_ii(y + h + padding, ibuf->y);
+ x = max_ii(x - padding, 0);
+ y = max_ii(y - padding, 0);
+ w = xmax - x;
+ h = ymax - y;
+ }
+
+ /* Get texture data pointers. */
+ float *rect_float = ibuf->rect_float;
+ uchar *rect = (uchar *)ibuf->rect;
+ int tex_stride = ibuf->x;
+ int tex_offset = ibuf->channels * (y * ibuf->x + x);
+
+ if (rect_float == NULL) {
+ /* Byte pixels. */
+ if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
+ const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(
+ ibuf->rect_colorspace);
+
+ rect = (uchar *)MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__);
+ if (rect == NULL) {
+ return;
+ }
+
+ tex_stride = w;
+ tex_offset = 0;
+
+ /* Convert to scene linear with sRGB compression, and premultiplied for
+ * correct texture interpolation. */
+ const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL);
+ IMB_colormanagement_imbuf_to_byte_texture(
+ rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied);
+ }
+ }
+ else {
+ /* Float pixels. */
+ const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT);
+
+ if (ibuf->channels != 4 || scaled || !store_premultiplied) {
+ rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * w * h, __func__);
+ if (rect_float == NULL) {
+ return;
+ }
+
+ tex_stride = w;
+ tex_offset = 0;
+
+ IMB_colormanagement_imbuf_to_float_texture(
+ rect_float, x, y, w, h, ibuf, store_premultiplied);
+ }
+ }
+
+ GPU_texture_bind(tex, 0);
+
+ if (scaled) {
+ /* Slower update where we first have to scale the input pixels. */
+ if (tile != NULL) {
+ int *tileoffset = tile->runtime.tilearray_offset;
+ int *tilesize = tile->runtime.tilearray_size;
+ int tilelayer = tile->runtime.tilearray_layer;
+ gpu_texture_update_scaled(
+ tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h);
+ }
+ else {
+ gpu_texture_update_scaled(
+ tex, rect, rect_float, ibuf->x, ibuf->y, x, y, -1, NULL, NULL, w, h);
+ }
+ }
+ else {
+ /* Fast update at same resolution. */
+ if (tile != NULL) {
+ int *tileoffset = tile->runtime.tilearray_offset;
+ int tilelayer = tile->runtime.tilearray_layer;
+ gpu_texture_update_unscaled(
+ tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset);
+ }
+ else {
+ gpu_texture_update_unscaled(
+ tex, rect, rect_float, x, y, -1, NULL, w, h, tex_stride, tex_offset);
+ }
+ }
+
+ /* Free buffers if needed. */
+ if (rect && rect != (uchar *)ibuf->rect) {
+ MEM_freeN(rect);
+ }
+ if (rect_float && rect_float != ibuf->rect_float) {
+ MEM_freeN(rect_float);
+ }
+
+ if (GPU_mipmap_enabled()) {
+ GPU_texture_generate_mipmap(tex);
+ }
+ else {
+ ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
+ }
+
+ GPU_texture_unbind(tex);
+}
+
+/* Partial update of texture for texture painting. This is often much
+ * quicker than fully updating the texture for high resolution images. */
+void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
+{
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+
+ if ((ibuf == NULL) || (w == 0) || (h == 0)) {
+ /* Full reload of texture. */
+ BKE_image_free_gputextures(ima);
+ }
+
+ GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0];
+ /* Check if we need to update the main gputexture. */
+ if (tex != NULL && tile == ima->tiles.first) {
+ gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h);
+ }
+
+ /* Check if we need to update the array gputexture. */
+ tex = ima->gputexture[TEXTARGET_2D_ARRAY][0];
+ if (tex != NULL) {
+ gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h);
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+}
+
+/* these two functions are called on entering and exiting texture paint mode,
+ * temporary disabling/enabling mipmapping on all images for quick texture
+ * updates with glTexSubImage2D. images that didn't change don't have to be
+ * re-uploaded to OpenGL */
+void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap)
+{
+ LISTBASE_FOREACH (Image *, ima, &bmain->images) {
+ if (BKE_image_has_opengl_texture(ima)) {
+ if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) {
+ for (int eye = 0; eye < 2; eye++) {
+ for (int a = 0; a < TEXTARGET_COUNT; a++) {
+ if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) {
+ GPUTexture *tex = ima->gputexture[a][eye];
+ if (tex != NULL) {
+ GPU_texture_mipmap_mode(tex, mipmap, true);
+ }
+ }
+ }
+ }
+ }
+ else {
+ BKE_image_free_gputextures(ima);
+ }
+ }
+ else {
+ ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
+ }
+ }
+}
+
+/** \} */
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/key.c b/source/blender/blenkernel/intern/key.c
index af8ab22e14b..a71b9cc2a1d 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -529,7 +529,13 @@ static int setkeys(float fac, ListBase *lb, KeyBlock *k[], float t[4], int cycl)
return 0;
}
-static void flerp(int tot, float *in, float *f0, float *f1, float *f2, float *f3, float *t)
+static void flerp(int tot,
+ float *in,
+ const float *f0,
+ const float *f1,
+ const float *f2,
+ const float *f3,
+ const float *t)
{
int a;
@@ -538,7 +544,7 @@ static void flerp(int tot, float *in, float *f0, float *f1, float *f2, float *f3
}
}
-static void rel_flerp(int tot, float *in, float *ref, float *out, float fac)
+static void rel_flerp(int tot, float *in, const float *ref, const float *out, float fac)
{
int a;
@@ -1541,6 +1547,134 @@ float *BKE_key_evaluate_object(Object *ob, int *r_totelem)
return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0);
}
+/**
+ * \param shape_index: The index to use or all (when -1).
+ */
+int BKE_keyblock_element_count_from_shape(const Key *key, const int shape_index)
+{
+ int result = 0;
+ int index = 0;
+ for (const KeyBlock *kb = key->block.first; kb; kb = kb->next, index++) {
+ if ((shape_index == -1) || (index == shape_index)) {
+ result += kb->totelem;
+ }
+ }
+ return result;
+}
+
+int BKE_keyblock_element_count(const Key *key)
+{
+ return BKE_keyblock_element_count_from_shape(key, -1);
+}
+
+/**
+ * \param shape_index: The index to use or all (when -1).
+ */
+size_t BKE_keyblock_element_calc_size_from_shape(const Key *key, const int shape_index)
+{
+ return (size_t)BKE_keyblock_element_count_from_shape(key, shape_index) * key->elemsize;
+}
+
+size_t BKE_keyblock_element_calc_size(const Key *key)
+{
+ return BKE_keyblock_element_calc_size_from_shape(key, -1);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Key-Block Data Access
+ *
+ * Utilities for getting/setting key data as a single array,
+ * use #BKE_keyblock_element_calc_size to allocate the size of the data needed.
+ * \{ */
+
+/**
+ * \param shape_index: The index to use or all (when -1).
+ */
+void BKE_keyblock_data_get_from_shape(const Key *key, float (*arr)[3], const int shape_index)
+{
+ uint8_t *elements = (uint8_t *)arr;
+ int index = 0;
+ for (const KeyBlock *kb = key->block.first; kb; kb = kb->next, index++) {
+ if ((shape_index == -1) || (index == shape_index)) {
+ const int block_elem_len = kb->totelem * key->elemsize;
+ memcpy(elements, kb->data, block_elem_len);
+ elements += block_elem_len;
+ }
+ }
+}
+
+void BKE_keyblock_data_get(const Key *key, float (*arr)[3])
+{
+ BKE_keyblock_data_get_from_shape(key, arr, -1);
+}
+
+/**
+ * Set the data to all key-blocks (or shape_index if != -1).
+ */
+void BKE_keyblock_data_set_with_mat4(Key *key,
+ const int shape_index,
+ const float (*coords)[3],
+ const float mat[4][4])
+{
+ if (key->elemsize != sizeof(float[3])) {
+ BLI_assert(!"Invalid elemsize");
+ return;
+ }
+
+ const float(*elements)[3] = coords;
+
+ int index = 0;
+ for (KeyBlock *kb = key->block.first; kb; kb = kb->next, index++) {
+ if ((shape_index == -1) || (index == shape_index)) {
+ const int block_elem_len = kb->totelem;
+ float(*block_data)[3] = (float(*)[3])kb->data;
+ for (int data_offset = 0; data_offset < block_elem_len; ++data_offset) {
+ const float *src_data = (const float *)(elements + data_offset);
+ float *dst_data = (float *)(block_data + data_offset);
+ mul_v3_m4v3(dst_data, mat, src_data);
+ }
+ elements += block_elem_len;
+ }
+ }
+}
+
+/**
+ * Set the data for all key-blocks (or shape_index if != -1),
+ * transforming by \a mat.
+ */
+void BKE_keyblock_curve_data_set_with_mat4(
+ Key *key, const ListBase *nurb, const int shape_index, const void *data, const float mat[4][4])
+{
+ const uint8_t *elements = data;
+
+ int index = 0;
+ for (KeyBlock *kb = key->block.first; kb; kb = kb->next, index++) {
+ if ((shape_index == -1) || (index == shape_index)) {
+ const int block_elem_size = kb->totelem * key->elemsize;
+ BKE_keyblock_curve_data_transform(nurb, mat, elements, kb->data);
+ elements += block_elem_size;
+ }
+ }
+}
+
+/**
+ * Set the data for all key-blocks (or shape_index if != -1).
+ */
+void BKE_keyblock_data_set(Key *key, const int shape_index, const void *data)
+{
+ const uint8_t *elements = data;
+ int index = 0;
+ for (KeyBlock *kb = key->block.first; kb; kb = kb->next, index++) {
+ if ((shape_index == -1) || (index == shape_index)) {
+ const int block_elem_size = kb->totelem * key->elemsize;
+ memcpy(kb->data, elements, block_elem_size);
+ elements += block_elem_size;
+ }
+ }
+}
+
+/** \} */
+
bool BKE_key_idtype_support(const short id_type)
{
switch (id_type) {
@@ -1897,6 +2031,37 @@ void BKE_keyblock_update_from_curve(Curve *UNUSED(cu), KeyBlock *kb, ListBase *n
}
}
+void BKE_keyblock_curve_data_transform(const ListBase *nurb,
+ const float mat[4][4],
+ const void *src_data,
+ void *dst_data)
+{
+ const float *src = src_data;
+ float *dst = dst_data;
+ for (Nurb *nu = nurb->first; nu; nu = nu->next) {
+ if (nu->bezt) {
+ for (int a = nu->pntsu; a; a--) {
+ for (int i = 0; i < 3; i++) {
+ mul_v3_m4v3(&dst[i * 3], mat, &src[i * 3]);
+ }
+ dst[9] = src[9];
+ dst[10] = src[10];
+ src += KEYELEM_FLOAT_LEN_BEZTRIPLE;
+ dst += KEYELEM_FLOAT_LEN_BEZTRIPLE;
+ }
+ }
+ else {
+ for (int a = nu->pntsu * nu->pntsv; a; a--) {
+ mul_v3_m4v3(dst, mat, src);
+ dst[3] = src[3];
+ dst[4] = src[4];
+ src += KEYELEM_FLOAT_LEN_BPOINT;
+ dst += KEYELEM_FLOAT_LEN_BPOINT;
+ }
+ }
+ }
+}
+
void BKE_keyblock_convert_from_curve(Curve *cu, KeyBlock *kb, ListBase *nurb)
{
int tot;
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index e7a2421a625..8820434cbcf 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -31,7 +31,6 @@
#include "BLI_bitmap.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
-#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -40,16 +39,13 @@
#include "DNA_defaults.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
-#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
-#include "BKE_anim_path.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_idtype.h"
-#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
@@ -299,7 +295,7 @@ void BKE_lattice_resize(Lattice *lt, int uNew, int vNew, int wNew, Object *ltOb)
copy_m4_m4(mat, ltOb->obmat);
unit_m4(ltOb->obmat);
- lattice_deform_verts(ltOb, NULL, NULL, vert_coords, uNew * vNew * wNew, 0, NULL, 1.0f);
+ BKE_lattice_deform_coords(ltOb, NULL, vert_coords, uNew * vNew * wNew, 0, NULL, 1.0f);
copy_m4_m4(ltOb->obmat, mat);
lt->typeu = typeu;
@@ -349,651 +345,13 @@ Lattice *BKE_lattice_copy(Main *bmain, const Lattice *lt)
return lt_copy;
}
-typedef struct LatticeDeformData {
- Object *object;
- float *latticedata;
- float latmat[4][4];
-} LatticeDeformData;
-
-LatticeDeformData *init_latt_deform(Object *oblatt, Object *ob)
-{
- /* we make an array with all differences */
- Lattice *lt = oblatt->data;
- BPoint *bp;
- DispList *dl = oblatt->runtime.curve_cache ?
- BKE_displist_find(&oblatt->runtime.curve_cache->disp, DL_VERTS) :
- NULL;
- const float *co = dl ? dl->verts : NULL;
- float *fp, imat[4][4];
- float fu, fv, fw;
- int u, v, w;
- float *latticedata;
- float latmat[4][4];
- LatticeDeformData *lattice_deform_data;
-
- if (lt->editlatt) {
- lt = lt->editlatt->latt;
- }
- bp = lt->def;
-
- fp = latticedata = MEM_mallocN(sizeof(float) * 3 * lt->pntsu * lt->pntsv * lt->pntsw,
- "latticedata");
-
- /* for example with a particle system: (ob == NULL) */
- if (ob == NULL) {
- /* in deformspace, calc matrix */
- invert_m4_m4(latmat, oblatt->obmat);
-
- /* back: put in deform array */
- invert_m4_m4(imat, latmat);
- }
- else {
- /* in deformspace, calc matrix */
- invert_m4_m4(imat, oblatt->obmat);
- mul_m4_m4m4(latmat, imat, ob->obmat);
-
- /* back: put in deform array */
- invert_m4_m4(imat, latmat);
- }
-
- for (w = 0, fw = lt->fw; w < lt->pntsw; w++, fw += lt->dw) {
- for (v = 0, fv = lt->fv; v < lt->pntsv; v++, fv += lt->dv) {
- for (u = 0, fu = lt->fu; u < lt->pntsu; u++, bp++, co += 3, fp += 3, fu += lt->du) {
- if (dl) {
- fp[0] = co[0] - fu;
- fp[1] = co[1] - fv;
- fp[2] = co[2] - fw;
- }
- else {
- fp[0] = bp->vec[0] - fu;
- fp[1] = bp->vec[1] - fv;
- fp[2] = bp->vec[2] - fw;
- }
-
- mul_mat3_m4_v3(imat, fp);
- }
- }
- }
-
- lattice_deform_data = MEM_mallocN(sizeof(LatticeDeformData), "Lattice Deform Data");
- lattice_deform_data->latticedata = latticedata;
- lattice_deform_data->object = oblatt;
- copy_m4_m4(lattice_deform_data->latmat, latmat);
-
- return lattice_deform_data;
-}
-
-void calc_latt_deform(LatticeDeformData *lattice_deform_data, float co[3], float weight)
-{
- Object *ob = lattice_deform_data->object;
- Lattice *lt = ob->data;
- float u, v, w, tu[4], tv[4], tw[4];
- float vec[3];
- int idx_w, idx_v, idx_u;
- int ui, vi, wi, uu, vv, ww;
-
- /* vgroup influence */
- int defgrp_index = -1;
- float co_prev[3], weight_blend = 0.0f;
- MDeformVert *dvert = BKE_lattice_deform_verts_get(ob);
- float *__restrict latticedata = lattice_deform_data->latticedata;
-
- if (lt->editlatt) {
- lt = lt->editlatt->latt;
- }
- if (latticedata == NULL) {
- return;
- }
-
- if (lt->vgroup[0] && dvert) {
- defgrp_index = BKE_object_defgroup_name_index(ob, lt->vgroup);
- copy_v3_v3(co_prev, co);
- }
-
- /* co is in local coords, treat with latmat */
- mul_v3_m4v3(vec, lattice_deform_data->latmat, co);
-
- /* u v w coords */
-
- if (lt->pntsu > 1) {
- u = (vec[0] - lt->fu) / lt->du;
- ui = (int)floor(u);
- u -= ui;
- key_curve_position_weights(u, tu, lt->typeu);
- }
- else {
- tu[0] = tu[2] = tu[3] = 0.0;
- tu[1] = 1.0;
- ui = 0;
- }
-
- if (lt->pntsv > 1) {
- v = (vec[1] - lt->fv) / lt->dv;
- vi = (int)floor(v);
- v -= vi;
- key_curve_position_weights(v, tv, lt->typev);
- }
- else {
- tv[0] = tv[2] = tv[3] = 0.0;
- tv[1] = 1.0;
- vi = 0;
- }
-
- if (lt->pntsw > 1) {
- w = (vec[2] - lt->fw) / lt->dw;
- wi = (int)floor(w);
- w -= wi;
- key_curve_position_weights(w, tw, lt->typew);
- }
- else {
- tw[0] = tw[2] = tw[3] = 0.0;
- tw[1] = 1.0;
- wi = 0;
- }
-
- for (ww = wi - 1; ww <= wi + 2; ww++) {
- w = tw[ww - wi + 1];
-
- if (w != 0.0f) {
- if (ww > 0) {
- if (ww < lt->pntsw) {
- idx_w = ww * lt->pntsu * lt->pntsv;
- }
- else {
- idx_w = (lt->pntsw - 1) * lt->pntsu * lt->pntsv;
- }
- }
- else {
- idx_w = 0;
- }
-
- for (vv = vi - 1; vv <= vi + 2; vv++) {
- v = w * tv[vv - vi + 1];
-
- if (v != 0.0f) {
- if (vv > 0) {
- if (vv < lt->pntsv) {
- idx_v = idx_w + vv * lt->pntsu;
- }
- else {
- idx_v = idx_w + (lt->pntsv - 1) * lt->pntsu;
- }
- }
- else {
- idx_v = idx_w;
- }
-
- for (uu = ui - 1; uu <= ui + 2; uu++) {
- u = weight * v * tu[uu - ui + 1];
-
- if (u != 0.0f) {
- if (uu > 0) {
- if (uu < lt->pntsu) {
- idx_u = idx_v + uu;
- }
- else {
- idx_u = idx_v + (lt->pntsu - 1);
- }
- }
- else {
- idx_u = idx_v;
- }
-
- madd_v3_v3fl(co, &latticedata[idx_u * 3], u);
-
- if (defgrp_index != -1) {
- weight_blend += (u * BKE_defvert_find_weight(dvert + idx_u, defgrp_index));
- }
- }
- }
- }
- }
- }
- }
-
- if (defgrp_index != -1) {
- interp_v3_v3v3(co, co_prev, co, weight_blend);
- }
-}
-
-void end_latt_deform(LatticeDeformData *lattice_deform_data)
-{
- if (lattice_deform_data->latticedata) {
- MEM_freeN(lattice_deform_data->latticedata);
- }
-
- MEM_freeN(lattice_deform_data);
-}
-
-/* calculations is in local space of deformed object
- * so we store in latmat transform from path coord inside object
- */
-typedef struct {
- float dmin[3], dmax[3];
- float curvespace[4][4], objectspace[4][4], objectspace3[3][3];
- int no_rot_axis;
-} CurveDeform;
-
-static void init_curve_deform(Object *par, Object *ob, CurveDeform *cd)
-{
- invert_m4_m4(ob->imat, ob->obmat);
- mul_m4_m4m4(cd->objectspace, ob->imat, par->obmat);
- invert_m4_m4(cd->curvespace, cd->objectspace);
- copy_m3_m4(cd->objectspace3, cd->objectspace);
- cd->no_rot_axis = 0;
-}
-
-/* this makes sure we can extend for non-cyclic.
- *
- * returns OK: 1/0
- */
-static bool where_on_path_deform(
- Object *ob, float ctime, float vec[4], float dir[3], float quat[4], float *radius)
-{
- BevList *bl;
- float ctime1;
- int cycl = 0;
-
- /* test for cyclic */
- bl = ob->runtime.curve_cache->bev.first;
- if (!bl->nr) {
- return false;
- }
- if (bl->poly > -1) {
- cycl = 1;
- }
-
- if (cycl == 0) {
- ctime1 = CLAMPIS(ctime, 0.0f, 1.0f);
- }
- else {
- ctime1 = ctime;
- }
-
- /* vec needs 4 items */
- if (where_on_path(ob, ctime1, vec, dir, quat, radius, NULL)) {
-
- if (cycl == 0) {
- Path *path = ob->runtime.curve_cache->path;
- float dvec[3];
-
- if (ctime < 0.0f) {
- sub_v3_v3v3(dvec, path->data[1].vec, path->data[0].vec);
- mul_v3_fl(dvec, ctime * (float)path->len);
- add_v3_v3(vec, dvec);
- if (quat) {
- copy_qt_qt(quat, path->data[0].quat);
- }
- if (radius) {
- *radius = path->data[0].radius;
- }
- }
- else if (ctime > 1.0f) {
- sub_v3_v3v3(dvec, path->data[path->len - 1].vec, path->data[path->len - 2].vec);
- mul_v3_fl(dvec, (ctime - 1.0f) * (float)path->len);
- add_v3_v3(vec, dvec);
- if (quat) {
- copy_qt_qt(quat, path->data[path->len - 1].quat);
- }
- if (radius) {
- *radius = path->data[path->len - 1].radius;
- }
- /* weight - not used but could be added */
- }
- }
- return true;
- }
- return false;
-}
-
-/* for each point, rotate & translate to curve */
-/* use path, since it has constant distances */
-/* co: local coord, result local too */
-/* returns quaternion for rotation, using cd->no_rot_axis */
-/* axis is using another define!!! */
-static bool calc_curve_deform(
- Object *par, float co[3], const short axis, CurveDeform *cd, float r_quat[4])
-{
- Curve *cu = par->data;
- float fac, loc[4], dir[3], new_quat[4], radius;
- short index;
- const bool is_neg_axis = (axis > 2);
-
- if (par->runtime.curve_cache == NULL) {
- /* Happens with a cyclic dependencies. */
- return false;
- }
-
- if (par->runtime.curve_cache->path == NULL) {
- return false; /* happens on append, cyclic dependencies and empty curves */
- }
-
- /* options */
- if (is_neg_axis) {
- index = axis - 3;
- if (cu->flag & CU_STRETCH) {
- fac = -(co[index] - cd->dmax[index]) / (cd->dmax[index] - cd->dmin[index]);
- }
- else {
- fac = -(co[index] - cd->dmax[index]) / (par->runtime.curve_cache->path->totdist);
- }
- }
- else {
- index = axis;
- if (cu->flag & CU_STRETCH) {
- fac = (co[index] - cd->dmin[index]) / (cd->dmax[index] - cd->dmin[index]);
- }
- else {
- if (LIKELY(par->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
- fac = +(co[index] - cd->dmin[index]) / (par->runtime.curve_cache->path->totdist);
- }
- else {
- fac = 0.0f;
- }
- }
- }
-
- if (where_on_path_deform(par, fac, loc, dir, new_quat, &radius)) { /* returns OK */
- float quat[4], cent[3];
-
- if (cd->no_rot_axis) { /* set by caller */
-
- /* This is not exactly the same as 2.4x, since the axis is having rotation removed rather
- * than changing the axis before calculating the tilt but serves much the same purpose. */
- float dir_flat[3] = {0, 0, 0}, q[4];
- copy_v3_v3(dir_flat, dir);
- dir_flat[cd->no_rot_axis - 1] = 0.0f;
-
- normalize_v3(dir);
- normalize_v3(dir_flat);
-
- rotation_between_vecs_to_quat(q, dir, dir_flat); /* Could this be done faster? */
-
- mul_qt_qtqt(new_quat, q, new_quat);
- }
-
- /* Logic for 'cent' orientation *
- *
- * The way 'co' is copied to 'cent' may seem to have no meaning, but it does.
- *
- * Use a curve modifier to stretch a cube out, color each side RGB,
- * positive side light, negative dark.
- * view with X up (default), from the angle that you can see 3 faces RGB colors (light),
- * anti-clockwise
- * Notice X,Y,Z Up all have light colors and each ordered CCW.
- *
- * Now for Neg Up XYZ, the colors are all dark, and ordered clockwise - Campbell
- *
- * note: moved functions into quat_apply_track/vec_apply_track
- * */
- copy_qt_qt(quat, new_quat);
- copy_v3_v3(cent, co);
-
- /* zero the axis which is not used,
- * the big block of text above now applies to these 3 lines */
- quat_apply_track(
- quat,
- axis,
- (axis == 0 || axis == 2) ? 1 : 0); /* up flag is a dummy, set so no rotation is done */
- vec_apply_track(cent, axis);
- cent[index] = 0.0f;
-
- /* scale if enabled */
- if (cu->flag & CU_PATH_RADIUS) {
- mul_v3_fl(cent, radius);
- }
-
- /* local rotation */
- normalize_qt(quat);
- mul_qt_v3(quat, cent);
-
- /* translation */
- add_v3_v3v3(co, cent, loc);
-
- if (r_quat) {
- copy_qt_qt(r_quat, quat);
- }
-
- return true;
- }
- return false;
-}
-
-void curve_deform_verts(Object *cuOb,
- Object *target,
- float (*vert_coords)[3],
- int numVerts,
- MDeformVert *dvert,
- const int defgrp_index,
- short flag,
- short defaxis)
-{
- Curve *cu;
- int a;
- CurveDeform cd;
- const bool is_neg_axis = (defaxis > 2);
- const bool invert_vgroup = (flag & MOD_CURVE_INVERT_VGROUP) != 0;
-
- if (cuOb->type != OB_CURVE) {
- return;
- }
-
- cu = cuOb->data;
-
- init_curve_deform(cuOb, target, &cd);
-
- /* dummy bounds, keep if CU_DEFORM_BOUNDS_OFF is set */
- if (is_neg_axis == false) {
- cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = 0.0f;
- cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 1.0f;
- }
- else {
- /* negative, these bounds give a good rest position */
- cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = -1.0f;
- cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 0.0f;
- }
-
- if (dvert) {
- MDeformVert *dvert_iter;
- float vec[3];
-
- if (cu->flag & CU_DEFORM_BOUNDS_OFF) {
- for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) {
- const float weight = invert_vgroup ?
- 1.0f - BKE_defvert_find_weight(dvert_iter, defgrp_index) :
- BKE_defvert_find_weight(dvert_iter, defgrp_index);
-
- if (weight > 0.0f) {
- mul_m4_v3(cd.curvespace, vert_coords[a]);
- copy_v3_v3(vec, vert_coords[a]);
- calc_curve_deform(cuOb, vec, defaxis, &cd, NULL);
- interp_v3_v3v3(vert_coords[a], vert_coords[a], vec, weight);
- mul_m4_v3(cd.objectspace, vert_coords[a]);
- }
- }
- }
- else {
- /* set mesh min/max bounds */
- INIT_MINMAX(cd.dmin, cd.dmax);
-
- for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) {
- const float weight = invert_vgroup ?
- 1.0f - BKE_defvert_find_weight(dvert_iter, defgrp_index) :
- BKE_defvert_find_weight(dvert_iter, defgrp_index);
- if (weight > 0.0f) {
- mul_m4_v3(cd.curvespace, vert_coords[a]);
- minmax_v3v3_v3(cd.dmin, cd.dmax, vert_coords[a]);
- }
- }
-
- for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) {
- const float weight = invert_vgroup ?
- 1.0f - BKE_defvert_find_weight(dvert_iter, defgrp_index) :
- BKE_defvert_find_weight(dvert_iter, defgrp_index);
-
- if (weight > 0.0f) {
- /* already in 'cd.curvespace', prev for loop */
- copy_v3_v3(vec, vert_coords[a]);
- calc_curve_deform(cuOb, vec, defaxis, &cd, NULL);
- interp_v3_v3v3(vert_coords[a], vert_coords[a], vec, weight);
- mul_m4_v3(cd.objectspace, vert_coords[a]);
- }
- }
- }
- }
- else {
- if (cu->flag & CU_DEFORM_BOUNDS_OFF) {
- for (a = 0; a < numVerts; a++) {
- mul_m4_v3(cd.curvespace, vert_coords[a]);
- calc_curve_deform(cuOb, vert_coords[a], defaxis, &cd, NULL);
- mul_m4_v3(cd.objectspace, vert_coords[a]);
- }
- }
- else {
- /* set mesh min max bounds */
- INIT_MINMAX(cd.dmin, cd.dmax);
-
- for (a = 0; a < numVerts; a++) {
- mul_m4_v3(cd.curvespace, vert_coords[a]);
- minmax_v3v3_v3(cd.dmin, cd.dmax, vert_coords[a]);
- }
-
- for (a = 0; a < numVerts; a++) {
- /* already in 'cd.curvespace', prev for loop */
- calc_curve_deform(cuOb, vert_coords[a], defaxis, &cd, NULL);
- mul_m4_v3(cd.objectspace, vert_coords[a]);
- }
- }
- }
-}
-
-/* input vec and orco = local coord in armature space */
-/* orco is original not-animated or deformed reference point */
-/* result written in vec and mat */
-void curve_deform_vector(
- Object *cuOb, Object *target, float orco[3], float vec[3], float mat[3][3], int no_rot_axis)
-{
- CurveDeform cd;
- float quat[4];
-
- if (cuOb->type != OB_CURVE) {
- unit_m3(mat);
- return;
- }
-
- init_curve_deform(cuOb, target, &cd);
- cd.no_rot_axis = no_rot_axis; /* option to only rotate for XY, for example */
-
- copy_v3_v3(cd.dmin, orco);
- copy_v3_v3(cd.dmax, orco);
-
- mul_m4_v3(cd.curvespace, vec);
-
- if (calc_curve_deform(cuOb, vec, target->trackflag, &cd, quat)) {
- float qmat[3][3];
-
- quat_to_mat3(qmat, quat);
- mul_m3_m3m3(mat, qmat, cd.objectspace3);
- }
- else {
- unit_m3(mat);
- }
-
- mul_m4_v3(cd.objectspace, vec);
-}
-
-typedef struct LatticeDeformUserdata {
- LatticeDeformData *lattice_deform_data;
- float (*vert_coords)[3];
- MDeformVert *dvert;
- int defgrp_index;
- float fac;
- bool invert_vgroup;
-} LatticeDeformUserdata;
-
-static void lattice_deform_vert_task(void *__restrict userdata,
- const int index,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- const LatticeDeformUserdata *data = userdata;
-
- if (data->dvert != NULL) {
- const float weight = data->invert_vgroup ?
- 1.0f -
- BKE_defvert_find_weight(data->dvert + index, data->defgrp_index) :
- BKE_defvert_find_weight(data->dvert + index, data->defgrp_index);
- if (weight > 0.0f) {
- calc_latt_deform(data->lattice_deform_data, data->vert_coords[index], weight * data->fac);
- }
- }
- else {
- calc_latt_deform(data->lattice_deform_data, data->vert_coords[index], data->fac);
- }
-}
-
-void lattice_deform_verts(Object *laOb,
- Object *target,
- Mesh *mesh,
- float (*vert_coords)[3],
- int numVerts,
- short flag,
- const char *vgroup,
- float fac)
-{
- LatticeDeformData *lattice_deform_data;
- MDeformVert *dvert = NULL;
- int defgrp_index = -1;
-
- if (laOb->type != OB_LATTICE) {
- return;
- }
-
- lattice_deform_data = init_latt_deform(laOb, target);
-
- /* Check whether to use vertex groups (only possible if target is a Mesh or Lattice).
- * We want either a Mesh/Lattice with no derived data, or derived data with deformverts.
- */
- if (vgroup && vgroup[0] && target && ELEM(target->type, OB_MESH, OB_LATTICE)) {
- defgrp_index = BKE_object_defgroup_name_index(target, vgroup);
-
- if (defgrp_index != -1) {
- /* if there's derived data without deformverts, don't use vgroups */
- if (mesh) {
- dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT);
- }
- else if (target->type == OB_LATTICE) {
- dvert = ((Lattice *)target->data)->dvert;
- }
- else {
- dvert = ((Mesh *)target->data)->dvert;
- }
- }
- }
-
- LatticeDeformUserdata data = {
- .lattice_deform_data = lattice_deform_data,
- .vert_coords = vert_coords,
- .dvert = dvert,
- .defgrp_index = defgrp_index,
- .fac = fac,
- .invert_vgroup = (flag & MOD_LATTICE_INVERT_VGROUP) != 0,
- };
-
- TaskParallelSettings settings;
- BLI_parallel_range_settings_defaults(&settings);
- settings.min_iter_per_thread = 32;
- BLI_task_parallel_range(0, numVerts, &data, lattice_deform_vert_task, &settings);
-
- end_latt_deform(lattice_deform_data);
-}
-
bool object_deform_mball(Object *ob, ListBase *dispbase)
{
if (ob->parent && ob->parent->type == OB_LATTICE && ob->partype == PARSKEL) {
DispList *dl;
for (dl = dispbase->first; dl; dl = dl->next) {
- lattice_deform_verts(ob->parent, ob, NULL, (float(*)[3])dl->verts, dl->nr, 0, NULL, 1.0f);
+ BKE_lattice_deform_coords(ob->parent, ob, (float(*)[3])dl->verts, dl->nr, 0, NULL, 1.0f);
}
return true;
@@ -1194,7 +552,7 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
}
}
-struct MDeformVert *BKE_lattice_deform_verts_get(struct Object *oblatt)
+struct MDeformVert *BKE_lattice_deform_verts_get(const struct Object *oblatt)
{
Lattice *lt = (Lattice *)oblatt->data;
BLI_assert(oblatt->type == OB_LATTICE);
diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c
new file mode 100644
index 00000000000..674ee9ed2c5
--- /dev/null
+++ b/source/blender/blenkernel/intern/lattice_deform.c
@@ -0,0 +1,470 @@
+/*
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * Deform coordinates by a lattice object (used by modifier).
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_lattice_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_displist.h"
+#include "BKE_editmesh.h"
+#include "BKE_key.h"
+#include "BKE_lattice.h"
+#include "BKE_modifier.h"
+
+#include "BKE_deform.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Lattice Deform API
+ * \{ */
+
+typedef struct LatticeDeformData {
+ const Object *object;
+ float *latticedata;
+ float latmat[4][4];
+} LatticeDeformData;
+
+LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Object *ob)
+{
+ /* we make an array with all differences */
+ Lattice *lt = oblatt->data;
+ BPoint *bp;
+ DispList *dl = oblatt->runtime.curve_cache ?
+ BKE_displist_find(&oblatt->runtime.curve_cache->disp, DL_VERTS) :
+ NULL;
+ const float *co = dl ? dl->verts : NULL;
+ float *fp, imat[4][4];
+ float fu, fv, fw;
+ int u, v, w;
+ float *latticedata;
+ float latmat[4][4];
+ LatticeDeformData *lattice_deform_data;
+
+ if (lt->editlatt) {
+ lt = lt->editlatt->latt;
+ }
+ bp = lt->def;
+
+ fp = latticedata = MEM_mallocN(sizeof(float) * 3 * lt->pntsu * lt->pntsv * lt->pntsw,
+ "latticedata");
+
+ /* for example with a particle system: (ob == NULL) */
+ if (ob == NULL) {
+ /* in deformspace, calc matrix */
+ invert_m4_m4(latmat, oblatt->obmat);
+
+ /* back: put in deform array */
+ invert_m4_m4(imat, latmat);
+ }
+ else {
+ /* in deformspace, calc matrix */
+ invert_m4_m4(imat, oblatt->obmat);
+ mul_m4_m4m4(latmat, imat, ob->obmat);
+
+ /* back: put in deform array */
+ invert_m4_m4(imat, latmat);
+ }
+
+ for (w = 0, fw = lt->fw; w < lt->pntsw; w++, fw += lt->dw) {
+ for (v = 0, fv = lt->fv; v < lt->pntsv; v++, fv += lt->dv) {
+ for (u = 0, fu = lt->fu; u < lt->pntsu; u++, bp++, co += 3, fp += 3, fu += lt->du) {
+ if (dl) {
+ fp[0] = co[0] - fu;
+ fp[1] = co[1] - fv;
+ fp[2] = co[2] - fw;
+ }
+ else {
+ fp[0] = bp->vec[0] - fu;
+ fp[1] = bp->vec[1] - fv;
+ fp[2] = bp->vec[2] - fw;
+ }
+
+ mul_mat3_m4_v3(imat, fp);
+ }
+ }
+ }
+
+ lattice_deform_data = MEM_mallocN(sizeof(LatticeDeformData), "Lattice Deform Data");
+ lattice_deform_data->latticedata = latticedata;
+ lattice_deform_data->object = oblatt;
+ copy_m4_m4(lattice_deform_data->latmat, latmat);
+
+ return lattice_deform_data;
+}
+
+void BKE_lattice_deform_data_eval_co(LatticeDeformData *lattice_deform_data,
+ float co[3],
+ float weight)
+{
+ const Object *ob = lattice_deform_data->object;
+ Lattice *lt = ob->data;
+ float u, v, w, tu[4], tv[4], tw[4];
+ float vec[3];
+ int idx_w, idx_v, idx_u;
+ int ui, vi, wi, uu, vv, ww;
+
+ /* vgroup influence */
+ int defgrp_index = -1;
+ float co_prev[3], weight_blend = 0.0f;
+ const MDeformVert *dvert = BKE_lattice_deform_verts_get(ob);
+ float *__restrict latticedata = lattice_deform_data->latticedata;
+
+ if (lt->editlatt) {
+ lt = lt->editlatt->latt;
+ }
+ if (latticedata == NULL) {
+ return;
+ }
+
+ if (lt->vgroup[0] && dvert) {
+ defgrp_index = BKE_object_defgroup_name_index(ob, lt->vgroup);
+ copy_v3_v3(co_prev, co);
+ }
+
+ /* co is in local coords, treat with latmat */
+ mul_v3_m4v3(vec, lattice_deform_data->latmat, co);
+
+ /* u v w coords */
+
+ if (lt->pntsu > 1) {
+ u = (vec[0] - lt->fu) / lt->du;
+ ui = (int)floor(u);
+ u -= ui;
+ key_curve_position_weights(u, tu, lt->typeu);
+ }
+ else {
+ tu[0] = tu[2] = tu[3] = 0.0;
+ tu[1] = 1.0;
+ ui = 0;
+ }
+
+ if (lt->pntsv > 1) {
+ v = (vec[1] - lt->fv) / lt->dv;
+ vi = (int)floor(v);
+ v -= vi;
+ key_curve_position_weights(v, tv, lt->typev);
+ }
+ else {
+ tv[0] = tv[2] = tv[3] = 0.0;
+ tv[1] = 1.0;
+ vi = 0;
+ }
+
+ if (lt->pntsw > 1) {
+ w = (vec[2] - lt->fw) / lt->dw;
+ wi = (int)floor(w);
+ w -= wi;
+ key_curve_position_weights(w, tw, lt->typew);
+ }
+ else {
+ tw[0] = tw[2] = tw[3] = 0.0;
+ tw[1] = 1.0;
+ wi = 0;
+ }
+
+ for (ww = wi - 1; ww <= wi + 2; ww++) {
+ w = tw[ww - wi + 1];
+
+ if (w != 0.0f) {
+ if (ww > 0) {
+ if (ww < lt->pntsw) {
+ idx_w = ww * lt->pntsu * lt->pntsv;
+ }
+ else {
+ idx_w = (lt->pntsw - 1) * lt->pntsu * lt->pntsv;
+ }
+ }
+ else {
+ idx_w = 0;
+ }
+
+ for (vv = vi - 1; vv <= vi + 2; vv++) {
+ v = w * tv[vv - vi + 1];
+
+ if (v != 0.0f) {
+ if (vv > 0) {
+ if (vv < lt->pntsv) {
+ idx_v = idx_w + vv * lt->pntsu;
+ }
+ else {
+ idx_v = idx_w + (lt->pntsv - 1) * lt->pntsu;
+ }
+ }
+ else {
+ idx_v = idx_w;
+ }
+
+ for (uu = ui - 1; uu <= ui + 2; uu++) {
+ u = weight * v * tu[uu - ui + 1];
+
+ if (u != 0.0f) {
+ if (uu > 0) {
+ if (uu < lt->pntsu) {
+ idx_u = idx_v + uu;
+ }
+ else {
+ idx_u = idx_v + (lt->pntsu - 1);
+ }
+ }
+ else {
+ idx_u = idx_v;
+ }
+
+ madd_v3_v3fl(co, &latticedata[idx_u * 3], u);
+
+ if (defgrp_index != -1) {
+ weight_blend += (u * BKE_defvert_find_weight(dvert + idx_u, defgrp_index));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (defgrp_index != -1) {
+ interp_v3_v3v3(co, co_prev, co, weight_blend);
+ }
+}
+
+void BKE_lattice_deform_data_destroy(LatticeDeformData *lattice_deform_data)
+{
+ if (lattice_deform_data->latticedata) {
+ MEM_freeN(lattice_deform_data->latticedata);
+ }
+
+ MEM_freeN(lattice_deform_data);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Lattice Deform #BKE_lattice_deform_coords API
+ *
+ * #BKE_lattice_deform_coords and related functions.
+ * \{ */
+
+typedef struct LatticeDeformUserdata {
+ LatticeDeformData *lattice_deform_data;
+ float (*vert_coords)[3];
+ const MDeformVert *dvert;
+ int defgrp_index;
+ float fac;
+ bool invert_vgroup;
+
+ /** Specific data types. */
+ struct {
+ int cd_dvert_offset;
+ } bmesh;
+} LatticeDeformUserdata;
+
+static void lattice_deform_vert_with_dvert(const LatticeDeformUserdata *data,
+ const int index,
+ const MDeformVert *dvert)
+{
+ if (dvert != NULL) {
+ const float weight = data->invert_vgroup ?
+ 1.0f - BKE_defvert_find_weight(dvert, data->defgrp_index) :
+ BKE_defvert_find_weight(dvert, data->defgrp_index);
+ if (weight > 0.0f) {
+ BKE_lattice_deform_data_eval_co(
+ data->lattice_deform_data, data->vert_coords[index], weight * data->fac);
+ }
+ }
+ else {
+ BKE_lattice_deform_data_eval_co(
+ data->lattice_deform_data, data->vert_coords[index], data->fac);
+ }
+}
+
+static void lattice_deform_vert_task(void *__restrict userdata,
+ const int index,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const LatticeDeformUserdata *data = userdata;
+ lattice_deform_vert_with_dvert(data, index, data->dvert ? &data->dvert[index] : NULL);
+}
+
+static void lattice_vert_task_editmesh(void *__restrict userdata, MempoolIterData *iter)
+{
+ const LatticeDeformUserdata *data = userdata;
+ BMVert *v = (BMVert *)iter;
+ MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
+ lattice_deform_vert_with_dvert(data, BM_elem_index_get(v), dvert);
+}
+
+static void lattice_vert_task_editmesh_no_dvert(void *__restrict userdata, MempoolIterData *iter)
+{
+ const LatticeDeformUserdata *data = userdata;
+ BMVert *v = (BMVert *)iter;
+ lattice_deform_vert_with_dvert(data, BM_elem_index_get(v), NULL);
+}
+
+static void lattice_deform_coords_impl(const Object *ob_lattice,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ const float fac,
+ const Mesh *me_target,
+ BMEditMesh *em_target)
+{
+ LatticeDeformData *lattice_deform_data;
+ const MDeformVert *dvert = NULL;
+ int defgrp_index = -1;
+ int cd_dvert_offset = -1;
+
+ if (ob_lattice->type != OB_LATTICE) {
+ return;
+ }
+
+ lattice_deform_data = BKE_lattice_deform_data_create(ob_lattice, ob_target);
+
+ /* Check whether to use vertex groups (only possible if ob_target is a Mesh or Lattice).
+ * We want either a Mesh/Lattice with no derived data, or derived data with deformverts.
+ */
+ if (defgrp_name && defgrp_name[0] && ob_target && ELEM(ob_target->type, OB_MESH, OB_LATTICE)) {
+ defgrp_index = BKE_object_defgroup_name_index(ob_target, defgrp_name);
+
+ if (defgrp_index != -1) {
+ /* if there's derived data without deformverts, don't use vgroups */
+ if (em_target) {
+ cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT);
+ }
+ else if (me_target) {
+ dvert = CustomData_get_layer(&me_target->vdata, CD_MDEFORMVERT);
+ }
+ else if (ob_target->type == OB_LATTICE) {
+ dvert = ((Lattice *)ob_target->data)->dvert;
+ }
+ else {
+ dvert = ((Mesh *)ob_target->data)->dvert;
+ }
+ }
+ }
+
+ LatticeDeformUserdata data = {
+ .lattice_deform_data = lattice_deform_data,
+ .vert_coords = vert_coords,
+ .dvert = dvert,
+ .defgrp_index = defgrp_index,
+ .fac = fac,
+ .invert_vgroup = (flag & MOD_LATTICE_INVERT_VGROUP) != 0,
+ .bmesh =
+ {
+ .cd_dvert_offset = cd_dvert_offset,
+ },
+ };
+
+ if (em_target != NULL) {
+ /* While this could cause an extra loop over mesh data, in most cases this will
+ * have already been properly set. */
+ BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
+
+ if (cd_dvert_offset != -1) {
+ BLI_task_parallel_mempool(em_target->bm->vpool, &data, lattice_vert_task_editmesh, true);
+ }
+ else {
+ BLI_task_parallel_mempool(
+ em_target->bm->vpool, &data, lattice_vert_task_editmesh_no_dvert, true);
+ }
+ }
+ else {
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 32;
+ BLI_task_parallel_range(0, vert_coords_len, &data, lattice_deform_vert_task, &settings);
+ }
+
+ BKE_lattice_deform_data_destroy(lattice_deform_data);
+}
+
+void BKE_lattice_deform_coords(const Object *ob_lattice,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ float fac)
+{
+ lattice_deform_coords_impl(
+ ob_lattice, ob_target, vert_coords, vert_coords_len, flag, defgrp_name, fac, NULL, NULL);
+}
+
+void BKE_lattice_deform_coords_with_mesh(const Object *ob_lattice,
+ const Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ const float fac,
+ const Mesh *me_target)
+{
+ lattice_deform_coords_impl(ob_lattice,
+ ob_target,
+ vert_coords,
+ vert_coords_len,
+ flag,
+ defgrp_name,
+ fac,
+ me_target,
+ NULL);
+}
+
+void BKE_lattice_deform_coords_with_editmesh(const struct Object *ob_lattice,
+ const struct Object *ob_target,
+ float (*vert_coords)[3],
+ const int vert_coords_len,
+ const short flag,
+ const char *defgrp_name,
+ const float influence,
+ struct BMEditMesh *em_target)
+{
+ lattice_deform_coords_impl(ob_lattice,
+ ob_target,
+ vert_coords,
+ vert_coords_len,
+ flag,
+ defgrp_name,
+ influence,
+ NULL,
+ em_target);
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index f03bf60817f..64649d84320 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -695,8 +695,8 @@ int BKE_layer_collection_findindex(ViewLayer *view_layer, const LayerCollection
* stores state like selection. */
static void layer_collection_sync(ViewLayer *view_layer,
- const ListBase *lb_scene,
- ListBase *lb_layer,
+ const ListBase *lb_collections,
+ ListBase *lb_layer_collections,
ListBase *new_object_bases,
short parent_exclude,
short parent_restrict,
@@ -708,11 +708,10 @@ static void layer_collection_sync(ViewLayer *view_layer,
* linking we can only sync after the fact. */
/* Remove layer collections that no longer have a corresponding scene collection. */
- for (LayerCollection *lc = lb_layer->first; lc;) {
- /* Note ID remap can set lc->collection to NULL when deleting collections. */
- LayerCollection *lc_next = lc->next;
+ LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, lb_layer_collections) {
+ /* Note that ID remap can set lc->collection to NULL when deleting collections. */
Collection *collection = (lc->collection) ?
- BLI_findptr(lb_scene,
+ BLI_findptr(lb_collections,
lc->collection,
offsetof(CollectionChild, collection)) :
NULL;
@@ -724,21 +723,20 @@ static void layer_collection_sync(ViewLayer *view_layer,
/* Free recursively. */
layer_collection_free(view_layer, lc);
- BLI_freelinkN(lb_layer, lc);
+ BLI_freelinkN(lb_layer_collections, lc);
}
-
- lc = lc_next;
}
/* Add layer collections for any new scene collections, and ensure order is the same. */
ListBase new_lb_layer = {NULL, NULL};
- LISTBASE_FOREACH (const CollectionChild *, child, lb_scene) {
+ LISTBASE_FOREACH (const CollectionChild *, child, lb_collections) {
Collection *collection = child->collection;
- LayerCollection *lc = BLI_findptr(lb_layer, collection, offsetof(LayerCollection, collection));
+ LayerCollection *lc = BLI_findptr(
+ lb_layer_collections, collection, offsetof(LayerCollection, collection));
if (lc) {
- BLI_remlink(lb_layer, lc);
+ BLI_remlink(lb_layer_collections, lc);
BLI_addtail(&new_lb_layer, lc);
}
else {
@@ -845,8 +843,8 @@ static void layer_collection_sync(ViewLayer *view_layer,
}
/* Replace layer collection list with new one. */
- *lb_layer = new_lb_layer;
- BLI_assert(BLI_listbase_count(lb_scene) == BLI_listbase_count(lb_layer));
+ *lb_layer_collections = new_lb_layer;
+ BLI_assert(BLI_listbase_count(lb_collections) == BLI_listbase_count(lb_layer_collections));
}
/**
@@ -876,9 +874,9 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
}
/* Generate new layer connections and object bases when collections changed. */
- CollectionChild child = {NULL, NULL, scene->master_collection};
- const ListBase collections = {&child, &child};
- ListBase new_object_bases = {NULL, NULL};
+ CollectionChild child = {.next = NULL, .prev = NULL, .collection = scene->master_collection};
+ const ListBase collections = {.first = &child, .last = &child};
+ ListBase new_object_bases = {.first = NULL, .last = NULL};
const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0;
layer_collection_sync(view_layer,
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 3ad429c5f5a..a64e550579d 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,
@@ -123,6 +123,16 @@ static void lib_id_library_local_paths(Main *bmain, Library *lib, ID *id)
(void *)bpath_user_data);
}
+static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData *cb_data)
+{
+ ID *id = cb_data->user_data;
+ if (*cb_data->id_pointer == id) {
+ DEG_id_tag_update_ex(cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO);
+ return IDWALK_RET_STOP_ITER;
+ }
+ return IDWALK_RET_NOP;
+}
+
/**
* Pull an ID out of a library (make it local). Only call this for IDs that
* don't have other library users.
@@ -145,6 +155,21 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
}
}
+ /* Conceptually, an ID made local is not the same as the linked one anymore. Reflect that by
+ * regenerating its session UUID. */
+ 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 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;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ BKE_library_foreach_ID_link(
+ bmain, id_iter, lib_id_clear_library_data_users_update_cb, id, IDWALK_READONLY);
+ }
+ FOREACH_MAIN_ID_END;
+
/* Internal shape key blocks inside data-blocks also stores id->lib,
* make sure this stays in sync (note that we do not need any explicit handling for real EMBEDDED
* IDs here, this is down automatically in `lib_id_expand_local_cb()`. */
@@ -200,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;
@@ -258,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) {
@@ -421,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.
@@ -583,6 +608,47 @@ 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 eDupli_ID_Flags duplicate_flags)
+{
+ if (id == NULL) {
+ return id;
+ }
+ if (id->newid == NULL) {
+ const bool do_linked_id = (duplicate_flags & USER_DUP_LINKED_ID) != 0;
+ if (!(do_linked_id || !ID_IS_LINKED(id))) {
+ return 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. */
+
+ BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags);
+ if (key_new != NULL) {
+ BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags);
+ }
+ /* Note that actions of embedded data (root nodetrees and master collections) are handled
+ * by `BKE_animdata_duplicate_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).
*/
@@ -611,6 +677,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) {
@@ -1047,8 +1116,10 @@ void BKE_lib_libblock_session_uuid_ensure(ID *id)
/**
* Re-generate a new session-wise uuid for the given \a id.
*
- * \warning This has a very specific use-case (to handle UI-related data-blocks that are kept
- * across new file reading, when we do keep existing UI). No other usage is expected currently.
+ * \warning This has a few very specific use-cases, no other usage is expected currently:
+ * - To handle UI-related data-blocks that are kept across new file reading, when we do keep
+ * existing UI.
+ * - For IDs that are made local without needing any copying.
*/
void BKE_lib_libblock_session_uuid_renew(ID *id)
{
@@ -1156,7 +1227,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);
@@ -1694,7 +1765,7 @@ static void library_make_local_copying_check(ID *id,
continue;
}
- /* Shapekeys are considered 'private' to their owner ID here, and never tagged
+ /* Shape-keys are considered 'private' to their owner ID here, and never tagged
* (since they cannot be linked), so we have to switch effective parent to their owner.
*/
if (GS(par_id->name) == ID_KE) {
@@ -2090,7 +2161,7 @@ void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id, char separa
/**
* Generate full name of the data-block (without ID code, but with library if any),
- * with a 3-character prefix prepended indicating whether it comes from a library,
+ * with a 2 to 3 character prefix prepended indicating whether it comes from a library,
* is overriding, has a fake or no user, etc.
*
* \note Result is unique to a given ID type in a given Main database.
@@ -2099,16 +2170,27 @@ void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id, char separa
* will be filled with generated string.
* \param separator_char: Character to use for separating name and library name. Can be 0 to use
* default (' ').
+ * \param r_prefix_len: The length of the prefix added.
*/
void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI],
const ID *id,
- char separator_char)
+ const bool add_lib_hint,
+ char separator_char,
+ int *r_prefix_len)
{
- 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;
- BKE_id_full_name_get(name + 3, id, separator_char);
+ 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 + i, id, separator_char);
+
+ if (r_prefix_len) {
+ *r_prefix_len = i;
+ }
}
/**
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 7c96d0a6401..561db7d62c2 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"
@@ -142,10 +144,16 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
}
#endif
+ Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : NULL;
+
if ((flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0) {
BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0);
}
+ if ((flag & LIB_ID_FREE_NO_MAIN) == 0 && 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 d16428ccd60..2989c910c45 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -27,19 +27,30 @@
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
+#include "DNA_collection_types.h"
+#include "DNA_key_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "BKE_armature.h"
+#include "BKE_collection.h"
+#include "BKE_idtype.h"
+#include "BKE_key.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
+#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_scene.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
+#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "RNA_access.h"
@@ -61,20 +72,6 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op);
static void lib_override_library_property_operation_clear(
IDOverrideLibraryPropertyOperation *opop);
-/* Temp, for until library override is ready and tested enough to go 'public',
- * we hide it by default in UI and such. */
-static bool _lib_override_library_enabled = true;
-
-void BKE_lib_override_library_enable(const bool do_enable)
-{
- _lib_override_library_enabled = do_enable;
-}
-
-bool BKE_lib_override_library_is_enabled()
-{
- return _lib_override_library_enabled;
-}
-
/** Initialize empty overriding of \a reference_id by \a local_id. */
IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
{
@@ -113,7 +110,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 +122,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 {
@@ -157,8 +155,8 @@ void BKE_lib_override_library_clear(IDOverrideLibrary *override, const bool do_i
{
BLI_assert(override != NULL);
- if (override->runtime != NULL) {
- BLI_ghash_clear(override->runtime, NULL, NULL);
+ if (!ELEM(NULL, override->runtime, override->runtime->rna_path_to_override_properties)) {
+ BLI_ghash_clear(override->runtime->rna_path_to_override_properties, NULL, NULL);
}
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) {
@@ -178,8 +176,10 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
BLI_assert(*override != NULL);
if ((*override)->runtime != NULL) {
- BLI_ghash_free((*override)->runtime, NULL, NULL);
- (*override)->runtime = NULL;
+ if ((*override)->runtime->rna_path_to_override_properties != NULL) {
+ BLI_ghash_free((*override)->runtime->rna_path_to_override_properties, NULL, NULL);
+ }
+ MEM_SAFE_FREE((*override)->runtime);
}
BKE_lib_override_library_clear(*override, do_id_user);
@@ -197,7 +197,16 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
id_us_min(local_id);
BKE_lib_override_library_init(local_id, reference_id);
- local_id->override_library->flag |= OVERRIDE_LIBRARY_AUTO;
+
+ /* 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;
}
@@ -213,6 +222,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) {
@@ -223,6 +238,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;
@@ -247,14 +269,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);
@@ -266,58 +289,357 @@ 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;
+ }
+
+ 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) {
+ /* 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;
+ }
+ }
+
+ BLI_freelistN(&todo_ids);
+
+ return success;
+}
+
+static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint tag)
+{
+ void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id);
+ if (entry_vp == NULL) {
+ /* Already processed. */
+ return (id->tag & tag) != 0;
+ }
- if (reference_id->newid == NULL) {
+ /* 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 (MainIDRelationsEntry *entry = *entry_vp; entry != NULL; entry = entry->next) {
+ if ((entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
+ /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
+ * actual dependencies. */
continue;
}
+ /* We only consider IDs from the same library. */
+ if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) {
+ if (lib_override_hierarchy_recursive_tag(bmain, *entry->id_pointer, tag)) {
+ id->tag |= tag;
+ }
+ }
+ }
- /* 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);
+ return (id->tag & tag) != 0;
+}
+
+/**
+ * Tag all IDs in given \a bmain that are being used by given \a id_root ID or its dependencies,
+ * recursively.
+ *
+ * This will include all local IDs, and all IDs from the same library as the \a id_root.
+ *
+ * \param id_root The root of the hierarchy of dependencies to be tagged.
+ * \param do_create_main_relashionships Whether main relations needs to be created or already exist
+ * (in any case, they will be freed by this function).
+ */
+void BKE_lib_override_library_dependencies_tag(struct Main *bmain,
+ struct ID *id_root,
+ const uint tag,
+ const bool do_create_main_relashionships)
+{
+ if (do_create_main_relashionships) {
+ BKE_main_relations_create(bmain, 0);
+ }
+
+ /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey
+ * has a driver using an armature object's bone, we need to override the shapekey/obdata, the
+ * objects using them, etc.) */
+ lib_override_hierarchy_recursive_tag(bmain, id_root, tag);
+
+ BKE_main_relations_free(bmain);
+}
+
+static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_data)
+{
+ if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) {
+ return IDWALK_RET_STOP_RECURSION;
+ }
+
+ 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;
+
+ BLI_assert(id_owner == cb_data->id_self);
+
+ if (ELEM(id, NULL, id_owner)) {
+ return IDWALK_RET_NOP;
+ }
+
+ 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 (id->lib != library_root) {
+ /* We do not override data-blocks from other libraries, nor do we process them. */
+ return IDWALK_RET_STOP_RECURSION;
+ }
+
+ /* 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;
+}
+
+/**
+ * Advanced 'smart' function to create fully functional overrides.
+ *
+ * \note Currently it only does special things if given \a id_root is an object of collection, more
+ * specific behaviors may be added in the future for other ID types.
+ *
+ * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at
+ * its beginning, so caller code can add extra data-blocks to be overridden as well.
+ *
+ * \note In the future that same function may be extended to support 'refresh' of overrides
+ * (rebuilding overrides from linked data, trying to preserve local overrides already defined).
+ *
+ * \param id_root The root ID to create an override from.
+ * \param id_reference some reference ID used to do some post-processing after overrides have been
+ * created, may be NULL. Typically, the Empty object instantiating the linked
+ * collection we override, currently.
+ * \return true if override was successfully created.
+ */
+bool BKE_lib_override_library_create(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+{
+ /* Tag all collections and objects, as well as other IDs using them. */
+ id_root->tag |= LIB_TAG_DOIT;
+
+ BKE_main_relations_create(bmain, 0);
+
+ if (ELEM(GS(id_root->name), ID_OB, ID_GR)) {
+ BKE_library_foreach_ID_link(bmain,
+ id_root,
+ lib_override_library_make_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_MAIN_ID_END;
}
- BLI_freelistN(&todo_ids);
+ /* Note that this call will also free the main relations data we created above. */
+ BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false);
- return ret;
+ const bool success = BKE_lib_override_library_create_from_tag(bmain);
+
+ if (success) {
+ BKE_main_collection_sync(bmain);
+
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
+ (Object *)id_reference :
+ NULL;
+ Collection *collection_new = ((Collection *)id_root->newid);
+ if (ob_reference != NULL) {
+ BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
+ }
+ else {
+ BKE_collection_add_from_collection(
+ bmain, scene, ((Collection *)id_root), collection_new);
+ }
+
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
+ if (ob_new != NULL && ob_new->id.override_library != NULL) {
+ if (ob_reference != NULL) {
+ Base *base;
+ if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
+ BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
+ base = BKE_view_layer_base_find(view_layer, ob_new);
+ DEG_id_tag_update_ex(
+ bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+ }
+
+ if (ob_new == (Object *)ob_reference->id.newid) {
+ /* TODO: is setting active needed? */
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+ }
+ }
+ else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
+ BKE_collection_object_add(bmain, collection_new, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+ }
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case ID_OB: {
+ BKE_collection_object_add_from(
+ bmain, scene, (Object *)id_root, ((Object *)id_root->newid));
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* We need to ensure all new overrides of objects are properly instantiated. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ Object *ob_new = (Object *)ob->id.newid;
+ if (ob_new != NULL) {
+ BLI_assert(ob_new->id.override_library != NULL &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ Collection *default_instantiating_collection = NULL;
+ if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) {
+ if (default_instantiating_collection == NULL) {
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+ break;
+ }
+ case ID_OB: {
+ /* Add the new container collection to one of the collections instantiating the
+ * root object, or scene's master collection if none found. */
+ Object *ob_root = (Object *)id_root;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob_root) &&
+ BKE_view_layer_has_collection(view_layer, collection) &&
+ !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
+ default_instantiating_collection = BKE_collection_add(
+ bmain, collection, "OVERRIDE_HIDDEN");
+ }
+ }
+ if (default_instantiating_collection == NULL) {
+ 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);
+ }
+ }
+ }
+ }
+
+ /* Cleanup. */
+ BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ return success;
}
-/* We only build override GHash on request. */
-BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure(
+BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure(
IDOverrideLibrary *override)
{
if (override->runtime == NULL) {
- override->runtime = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
+ override->runtime = MEM_callocN(sizeof(*override->runtime), __func__);
+ }
+ return override->runtime;
+}
+
+/* We only build override GHash on request. */
+BLI_INLINE GHash *override_library_rna_path_mapping_ensure(IDOverrideLibrary *override)
+{
+ IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_runtime_ensure(override);
+ if (override_runtime->rna_path_to_override_properties == NULL) {
+ override_runtime->rna_path_to_override_properties = 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);
+ BLI_ghash_insert(override_runtime->rna_path_to_override_properties, op->rna_path, op);
}
}
- return override->runtime;
+ return override_runtime->rna_path_to_override_properties;
}
/**
@@ -326,7 +648,7 @@ BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure(
IDOverrideLibraryProperty *BKE_lib_override_library_property_find(IDOverrideLibrary *override,
const char *rna_path)
{
- IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_mapping_ensure(override);
+ GHash *override_runtime = override_library_rna_path_mapping_ensure(override);
return BLI_ghash_lookup(override_runtime, rna_path);
}
@@ -344,8 +666,7 @@ IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibra
op->rna_path = BLI_strdup(rna_path);
BLI_addtail(&override->properties, op);
- IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_mapping_ensure(
- override);
+ GHash *override_runtime = override_library_rna_path_mapping_ensure(override);
BLI_ghash_insert(override_runtime, op->rna_path, op);
if (r_created) {
@@ -391,8 +712,11 @@ void lib_override_library_property_clear(IDOverrideLibraryProperty *op)
void BKE_lib_override_library_property_delete(IDOverrideLibrary *override,
IDOverrideLibraryProperty *override_property)
{
- if (override->runtime != NULL) {
- BLI_ghash_remove(override->runtime, override_property->rna_path, NULL, NULL);
+ if (!ELEM(NULL, override->runtime, override->runtime->rna_path_to_override_properties)) {
+ BLI_ghash_remove(override->runtime->rna_path_to_override_properties,
+ override_property->rna_path,
+ NULL,
+ NULL);
}
lib_override_library_property_clear(override_property);
BLI_freelinkN(&override->properties, override_property);
@@ -470,7 +794,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,
@@ -618,7 +943,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;
@@ -650,6 +975,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,
@@ -673,7 +999,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;
@@ -713,6 +1039,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)) {
@@ -736,13 +1063,13 @@ 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 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);
bool ret = false;
- if (!is_template && (force_auto || local->override_library->flag & OVERRIDE_LIBRARY_AUTO)) {
+ if (!is_template) {
/* Do not attempt to generate overriding rules from an empty place-holder generated by link
* code when it cannot find to actual library/ID. Much better to keep the local datablock as
* is in the file in that case, until broken lib is fixed. */
@@ -751,9 +1078,9 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local, const bo
}
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) {
@@ -770,6 +1097,7 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local, const bo
&rnaptr_local,
&rnaptr_reference,
NULL,
+ 0,
local->override_library,
RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE,
&report_flags);
@@ -791,29 +1119,182 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local, const bo
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) ||
- (ID_IS_OVERRIDE_LIBRARY_AUTO(id) && (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) {
- BKE_lib_override_library_operations_create(bmain, id, force_auto);
- id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ (force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) {
+ 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
+}
+
+static bool lib_override_library_id_reset_do(Main *bmain, ID *id_root)
+{
+ bool was_op_deleted = false;
+
+ LISTBASE_FOREACH_MUTABLE (
+ IDOverrideLibraryProperty *, op, &id_root->override_library->properties) {
+ bool do_op_delete = true;
+ const bool is_collection = op->rna_prop_type == PROP_COLLECTION;
+ if (is_collection || op->rna_prop_type == PROP_POINTER) {
+ PointerRNA ptr_root, ptr_root_lib, ptr, ptr_lib;
+ PropertyRNA *prop, *prop_lib;
+
+ RNA_pointer_create(id_root, &RNA_ID, id_root, &ptr_root);
+ RNA_pointer_create(id_root->override_library->reference,
+ &RNA_ID,
+ id_root->override_library->reference,
+ &ptr_root_lib);
+
+ bool prop_exists = RNA_path_resolve_property(&ptr_root, op->rna_path, &ptr, &prop);
+ BLI_assert(prop_exists);
+ prop_exists = RNA_path_resolve_property(&ptr_root_lib, op->rna_path, &ptr_lib, &prop_lib);
+
+ if (prop_exists) {
+ BLI_assert(ELEM(RNA_property_type(prop), PROP_POINTER, PROP_COLLECTION));
+ BLI_assert(RNA_property_type(prop) == RNA_property_type(prop_lib));
+ if (is_collection) {
+ ptr.type = RNA_property_pointer_type(&ptr, prop);
+ ptr_lib.type = RNA_property_pointer_type(&ptr_lib, prop_lib);
+ }
+ else {
+ ptr = RNA_property_pointer_get(&ptr, prop);
+ ptr_lib = RNA_property_pointer_get(&ptr_lib, prop_lib);
+ }
+ if (ptr.owner_id != NULL && ptr_lib.owner_id != NULL) {
+ BLI_assert(ptr.type == ptr_lib.type);
+ do_op_delete = !(RNA_struct_is_ID(ptr.type) && ptr.owner_id->override_library != NULL &&
+ ptr.owner_id->override_library->reference == ptr_lib.owner_id);
+ }
+ }
+ }
+
+ if (do_op_delete) {
+ BKE_lib_override_library_property_delete(id_root->override_library, op);
+ was_op_deleted = true;
+ }
+ }
+
+ if (was_op_deleted) {
+ DEG_id_tag_update_ex(bmain, id_root, ID_RECALC_COPY_ON_WRITE);
+ IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_runtime_ensure(
+ id_root->override_library);
+ override_runtime->tag |= IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD;
+ }
+
+ return was_op_deleted;
+}
+
+/** Reset all overrides in given \a id_root, while preserving ID relations. */
+void BKE_lib_override_library_id_reset(Main *bmain, ID *id_root)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ return;
+ }
+
+ if (lib_override_library_id_reset_do(bmain, id_root)) {
+ if (id_root->override_library->runtime != NULL &&
+ (id_root->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD) !=
+ 0) {
+ BKE_lib_override_library_update(bmain, id_root);
+ id_root->override_library->runtime->tag &= ~IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD;
+ }
+ }
+}
+
+static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, ID *id_root)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ return;
+ }
+
+ void **entry_pp = BLI_ghash_lookup(bmain->relations->id_user_to_used, id_root);
+ if (entry_pp == NULL) {
+ /* Already processed. */
+ return;
+ }
+
+ lib_override_library_id_reset_do(bmain, id_root);
+
+ /* 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_root);
+
+ for (MainIDRelationsEntry *entry = *entry_pp; entry != NULL; entry = entry->next) {
+ if ((entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
+ /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
+ * actual dependencies. */
+ continue;
+ }
+ /* We only consider IDs from the same library. */
+ if (entry->id_pointer != NULL) {
+ ID *id_entry = *entry->id_pointer;
+ if (id_entry->override_library != NULL) {
+ lib_override_library_id_hierarchy_recursive_reset(bmain, id_entry);
+ }
+ }
+ }
+}
+
+/** Reset all overrides in given \a id_root and its dependencies, while preserving ID relations. */
+void BKE_lib_override_library_id_hierarchy_reset(Main *bmain, ID *id_root)
+{
+ BKE_main_relations_create(bmain, 0);
+
+ lib_override_library_id_hierarchy_recursive_reset(bmain, id_root);
+
+ BKE_main_relations_free(bmain);
+
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || id->override_library->runtime == NULL ||
+ (id->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD) == 0) {
+ continue;
+ }
+ BKE_lib_override_library_update(bmain, id);
+ id->override_library->runtime->tag &= ~IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD;
+ }
+ FOREACH_MAIN_ID_END;
}
/** Set or clear given tag in all operations as unused in that override property data. */
@@ -868,7 +1349,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) {
@@ -901,7 +1382,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;
}
@@ -923,8 +1404,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). */
@@ -941,11 +1422,19 @@ 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) {
+ 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);
@@ -961,6 +1450,18 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
* So when we'll free tmp_id, we'll actually free old, outdated data from local. */
BKE_lib_id_swap(bmain, local, tmp_id);
+ if (local_key != NULL && tmp_key != NULL) {
+ /* This is some kind of hard-coded 'always enforced override'... */
+ BKE_lib_id_swap(bmain, &local_key->id, &tmp_key->id);
+ tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ /* The swap of local and tmp_id inverted those pointers, we need to redefine proper
+ * relationships. */
+ *BKE_key_from_id_p(local) = local_key;
+ *BKE_key_from_id_p(tmp_id) = tmp_key;
+ local_key->from = local;
+ tmp_key->from = tmp_id;
+ }
+
/* Again, horribly inn-efficient in our case, we need something off-Main
* (aka more generic nolib copy/free stuff)! */
/* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */
@@ -977,9 +1478,10 @@ 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_id_tag_update_ex(bmain, local, ID_RECALC_ALL);
+ DEG_relations_tag_update(bmain);
}
/** Update all overrides from given \a bmain. */
@@ -1027,17 +1529,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
@@ -1045,8 +1546,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
@@ -1079,7 +1580,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...). */
@@ -1088,8 +1589,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_query.c b/source/blender/blenkernel/intern/lib_query.c
index 00a42b12e07..0f81d45c10f 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -412,6 +412,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
return ELEM(id_type_used, ID_MA);
case ID_VO:
return ELEM(id_type_used, ID_MA);
+ case ID_SIM:
+ return ELEM(id_type_used, ID_OB, ID_IM);
case ID_IM:
case ID_VF:
case ID_TXT:
@@ -422,7 +424,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
case ID_PAL:
case ID_PC:
case ID_CF:
- case ID_SIM:
/* Those types never use/reference other IDs... */
return false;
case ID_IP:
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index ba986b1661b..d4246056efe 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -666,9 +666,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 64ffea22363..48c98be626d 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -81,23 +81,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/mask.c b/source/blender/blenkernel/intern/mask.c
index 49c909850de..7e859799a4e 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -624,7 +624,7 @@ void BKE_mask_point_segment_co(MaskSpline *spline, MaskSplinePoint *point, float
co, bezt->vec[1], bezt->vec[2], bezt_next->vec[0], bezt_next->vec[1], u);
}
-BLI_INLINE void orthogonal_direction_get(float vec[2], float result[2])
+BLI_INLINE void orthogonal_direction_get(const float vec[2], float result[2])
{
result[0] = -vec[1];
result[1] = vec[0];
diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c
index 412ccd3ab39..01d44d070b3 100644
--- a/source/blender/blenkernel/intern/mask_rasterize.c
+++ b/source/blender/blenkernel/intern/mask_rasterize.c
@@ -769,8 +769,8 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle,
BLI_assert(tot_diff_feather_points == tot_diff_point);
- /* note: only added for convenience, we don't infact use these to scanfill,
- * only to create feather faces after scanfill */
+ /* Note: only added for convenience, we don't in fact use these to scan-fill,
+ * only to create feather faces after scan-fill. */
for (j = 0; j < tot_diff_feather_points; j++) {
copy_v2_v2(co_feather, diff_feather_points[j]);
sf_vert = BLI_scanfill_vert_add(&sf_ctx, co_feather);
@@ -1256,7 +1256,7 @@ static float maskrasterize_layer_z_depth_quad(
return w[2] + w[3]; /* we can make this assumption for small speedup */
}
-static float maskrasterize_layer_isect(unsigned int *face,
+static float maskrasterize_layer_isect(const unsigned int *face,
float (*cos)[3],
const float dist_orig,
const float xy[2])
@@ -1489,6 +1489,8 @@ static void maskrasterize_buffer_cb(void *__restrict userdata,
void BKE_maskrasterize_buffer(MaskRasterHandle *mr_handle,
const unsigned int width,
const unsigned int height,
+ /* Cannot be const, because it is assigned to non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
float *buffer)
{
const float x_inv = 1.0f / (float)width;
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index d4de04a9e98..aa72493e472 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -955,18 +955,26 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
void BKE_object_material_array_assign(Main *bmain,
struct Object *ob,
struct Material ***matar,
- short totcol)
+ int totcol,
+ const bool to_object_only)
{
int actcol_orig = ob->actcol;
- short i;
while ((ob->totcol > totcol) && BKE_object_material_slot_remove(bmain, ob)) {
/* pass */
}
/* now we have the right number of slots */
- for (i = 0; i < totcol; i++) {
- BKE_object_material_assign(bmain, ob, (*matar)[i], i + 1, BKE_MAT_ASSIGN_USERPREF);
+ for (int i = 0; i < totcol; i++) {
+ if (to_object_only && ob->matbits[i] == 0) {
+ /* If we only assign to object, and that slot uses obdata material, do nothing. */
+ continue;
+ }
+ BKE_object_material_assign(bmain,
+ ob,
+ (*matar)[i],
+ i + 1,
+ to_object_only ? BKE_MAT_ASSIGN_OBJECT : BKE_MAT_ASSIGN_USERPREF);
}
if (actcol_orig > ob->totcol) {
@@ -1272,8 +1280,6 @@ void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma)
if (ma->paint_clone_slot >= count) {
ma->paint_clone_slot = count - 1;
}
-
- return;
}
void BKE_texpaint_slots_refresh_object(Scene *scene, struct Object *ob)
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 9e5565d744a..a0f3bc9e74d 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -286,12 +286,14 @@ int BKE_mesh_nurbs_displist_to_mdata(Object *ob,
}
}
else if (dl->type == DL_SURF) {
- int tot;
- totvert += dl->parts * dl->nr;
- tot = (dl->parts - 1 + ((dl->flag & DL_CYCL_V) == 2)) *
- (dl->nr - 1 + (dl->flag & DL_CYCL_U));
- totpoly += tot;
- totloop += tot * 4;
+ if (dl->parts != 0) {
+ int tot;
+ totvert += dl->parts * dl->nr;
+ tot = (((dl->flag & DL_CYCL_U) ? 1 : 0) + (dl->nr - 1)) *
+ (((dl->flag & DL_CYCL_V) ? 1 : 0) + (dl->parts - 1));
+ totpoly += tot;
+ totloop += tot * 4;
+ }
}
else if (dl->type == DL_INDEX3) {
int tot;
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 433db26ded8..49957b584ad 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);
}
@@ -2828,7 +2888,7 @@ void BKE_mesh_loops_to_mface_corners(
void BKE_mesh_loops_to_tessdata(CustomData *fdata,
CustomData *ldata,
MFace *mface,
- int *polyindices,
+ const int *polyindices,
unsigned int (*loopindices)[4],
const int num_faces)
{
@@ -2921,7 +2981,7 @@ void BKE_mesh_loops_to_tessdata(CustomData *fdata,
void BKE_mesh_tangent_loops_to_tessdata(CustomData *fdata,
CustomData *ldata,
MFace *mface,
- int *polyindices,
+ const int *polyindices,
unsigned int (*loopindices)[4],
const int num_faces,
const char *layer_name)
diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c
index 4ed9b31dbb5..686f58a0ceb 100644
--- a/source/blender/blenkernel/intern/mesh_mapping.c
+++ b/source/blender/blenkernel/intern/mesh_mapping.c
@@ -953,7 +953,7 @@ void BKE_mesh_loop_islands_free(MeshIslandStore *island_store)
void BKE_mesh_loop_islands_add(MeshIslandStore *island_store,
const int item_num,
- int *items_indices,
+ const int *items_indices,
const int num_island_items,
int *island_item_indices,
const int num_innercut_items,
diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c
index 404d6a581ae..a4991675d2d 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -1071,7 +1071,7 @@ static void mesh_island_to_astar_graph_edge_process(MeshIslandStore *islands,
BLI_bitmap *done_edges,
MeshElemMap *edge_to_poly_map,
const bool is_edge_innercut,
- int *poly_island_index_map,
+ const int *poly_island_index_map,
float (*poly_centers)[3],
unsigned char *poly_status)
{
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..b9eb3876dde 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.c
+++ b/source/blender/blenkernel/intern/mesh_runtime.c
@@ -43,8 +43,6 @@
/** \name Mesh Runtime Struct Utils
* \{ */
-static ThreadRWMutex loops_cache_lock = PTHREAD_RWLOCK_INITIALIZER;
-
/**
* Default values defined at read time.
*/
@@ -159,23 +157,21 @@ const MLoopTri *BKE_mesh_runtime_looptri_ensure(Mesh *mesh)
{
MLoopTri *looptri;
- BLI_rw_mutex_lock(&loops_cache_lock, THREAD_LOCK_READ);
+ ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex;
+ BLI_mutex_lock(mesh_eval_mutex);
+
looptri = mesh->runtime.looptris.array;
- BLI_rw_mutex_unlock(&loops_cache_lock);
if (looptri != NULL) {
BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len);
}
else {
- BLI_rw_mutex_lock(&loops_cache_lock, THREAD_LOCK_WRITE);
- /* We need to ensure array is still NULL inside mutex-protected code,
- * some other thread might have already recomputed those looptris. */
- if (mesh->runtime.looptris.array == NULL) {
- BKE_mesh_runtime_looptri_recalc(mesh);
- }
+ BKE_mesh_runtime_looptri_recalc(mesh);
looptri = mesh->runtime.looptris.array;
- BLI_rw_mutex_unlock(&loops_cache_lock);
}
+
+ BLI_mutex_unlock(mesh_eval_mutex);
+
return looptri;
}
@@ -203,26 +199,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/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c
index 98b77256ad7..acd272ac305 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.c
+++ b/source/blender/blenkernel/intern/mesh_wrapper.c
@@ -40,6 +40,7 @@
#include "BLI_ghash.h"
#include "BLI_math.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BKE_editmesh.h"
@@ -53,7 +54,7 @@
Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
const CustomData_MeshMasks *cd_mask_extra,
- float (*vertexCos)[3],
+ const float (*vert_coords)[3],
const Mesh *me_settings)
{
Mesh *me = BKE_id_new_nomain(ID_ME, NULL);
@@ -83,7 +84,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
#endif
EditMeshData *edit_data = me->runtime.edit_data;
- edit_data->vertexCos = vertexCos;
+ edit_data->vertexCos = vert_coords;
return me;
}
@@ -96,9 +97,14 @@ Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em,
void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
{
+ ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex;
+ BLI_mutex_lock(mesh_eval_mutex);
+
if (me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) {
+ BLI_mutex_unlock(mesh_eval_mutex);
return;
}
+
const eMeshWrapperType geom_type_orig = me->runtime.wrapper_type;
me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
@@ -130,6 +136,8 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
if (me->runtime.wrapper_type_finalize) {
BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra);
}
+
+ BLI_mutex_unlock(mesh_eval_mutex);
}
bool BKE_mesh_wrapper_minmax(const Mesh *me, float min[3], float max[3])
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index d4b7d05c228..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;
@@ -878,10 +878,7 @@ void BKE_modifier_free_temporary_data(ModifierData *md)
if (md->type == eModifierType_Armature) {
ArmatureModifierData *amd = (ArmatureModifierData *)md;
- if (amd->prevCos) {
- MEM_freeN(amd->prevCos);
- amd->prevCos = NULL;
- }
+ MEM_SAFE_FREE(amd->vert_coords_prev);
}
}
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index fe7c2055aef..5b4dbc66cc2 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -129,6 +129,23 @@ static void movie_clip_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
+static void movie_clip_foreach_cache(ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ MovieClip *movie_clip = (MovieClip *)id;
+ IDCacheKey key = {
+ .id_session_uuid = id->session_uuid,
+ .offset_in_ID = offsetof(MovieClip, cache),
+ .cache_v = movie_clip->cache,
+ };
+ function_callback(id, &key, (void **)&movie_clip->cache, 0, user_data);
+
+ key.offset_in_ID = offsetof(MovieClip, tracking.camera.intrinsics);
+ key.cache_v = movie_clip->tracking.camera.intrinsics;
+ function_callback(id, &key, (void **)&movie_clip->tracking.camera.intrinsics, 0, user_data);
+}
+
IDTypeInfo IDType_ID_MC = {
.id_code = ID_MC,
.id_filter = FILTER_ID_MC,
@@ -144,6 +161,7 @@ IDTypeInfo IDType_ID_MC = {
.free_data = movie_clip_free_data,
.make_local = NULL,
.foreach_id = movie_clip_foreach_id,
+ .foreach_cache = movie_clip_foreach_cache,
};
/*********************** movieclip buffer loaders *************************/
@@ -220,19 +238,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 +264,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 +413,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 +463,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 +549,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 +692,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 +776,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 +813,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 +839,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 +1757,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));
}
}
@@ -1848,3 +1866,84 @@ void BKE_movieclip_eval_selection_update(struct Depsgraph *depsgraph, MovieClip
DEG_debug_print_eval(depsgraph, __func__, clip->id.name, clip);
movieclip_selection_sync(clip, (MovieClip *)clip->id.orig_id);
}
+
+/* -------------------------------------------------------------------- */
+/** \name GPU textures
+ * \{ */
+
+static GPUTexture **movieclip_get_gputexture_ptr(MovieClip *clip,
+ MovieClipUser *cuser,
+ eGPUTextureTarget textarget)
+{
+ LISTBASE_FOREACH (MovieClip_RuntimeGPUTexture *, tex, &clip->runtime.gputextures) {
+ if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) {
+ if (tex == NULL) {
+ tex = (MovieClip_RuntimeGPUTexture *)MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture),
+ __func__);
+
+ for (int i = 0; i < TEXTARGET_COUNT; i++) {
+ tex->gputexture[i] = NULL;
+ }
+
+ memcpy(&tex->user, cuser, sizeof(MovieClipUser));
+ BLI_addtail(&clip->runtime.gputextures, tex);
+ }
+
+ return &tex->gputexture[textarget];
+ }
+ }
+ return NULL;
+}
+
+GPUTexture *BKE_movieclip_get_gpu_texture(MovieClip *clip, MovieClipUser *cuser)
+{
+ if (clip == NULL) {
+ return NULL;
+ }
+
+ GPUTexture **tex = movieclip_get_gputexture_ptr(clip, cuser, TEXTARGET_2D);
+ if (*tex) {
+ return *tex;
+ }
+
+ /* check if we have a valid image buffer */
+ ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser);
+ if (ibuf == NULL) {
+ *tex = GPU_texture_create_error(2, false);
+ return *tex;
+ }
+
+ /* This only means RGBA16F instead of RGBA32F. */
+ const bool high_bitdepth = false;
+ const bool store_premultiplied = ibuf->rect_float ? false : true;
+ *tex = IMB_create_gpu_texture(ibuf, high_bitdepth, store_premultiplied);
+
+ /* Do not generate mips for movieclips... too slow. */
+ GPU_texture_mipmap_mode(*tex, false, true);
+
+ IMB_freeImBuf(ibuf);
+
+ return *tex;
+}
+
+void BKE_movieclip_free_gputexture(struct MovieClip *clip)
+{
+ /* number of gpu textures to keep around as cache
+ * We don't want to keep too many GPU textures for
+ * movie clips around, as they can be large.*/
+ const int MOVIECLIP_NUM_GPUTEXTURES = 1;
+
+ while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) {
+ MovieClip_RuntimeGPUTexture *tex = (MovieClip_RuntimeGPUTexture *)BLI_pophead(
+ &clip->runtime.gputextures);
+ for (int i = 0; i < TEXTARGET_COUNT; i++) {
+ /* free glsl image binding */
+ if (tex->gputexture[i]) {
+ GPU_texture_free(tex->gputexture[i]);
+ tex->gputexture[i] = NULL;
+ }
+ }
+ MEM_freeN(tex);
+ }
+}
+/** \} */ \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 7e78be6d66e..71d49dd1c19 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -175,7 +175,7 @@ static BLI_bitmap *multires_mdisps_upsample_hidden(BLI_bitmap *lo_hidden,
return subd;
}
-static BLI_bitmap *multires_mdisps_downsample_hidden(BLI_bitmap *old_hidden,
+static BLI_bitmap *multires_mdisps_downsample_hidden(const BLI_bitmap *old_hidden,
int old_level,
int new_level)
{
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
index 3564ae80d24..e12e692ea23 100644
--- a/source/blender/blenkernel/intern/multires_reshape_smooth.c
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -271,7 +271,7 @@ static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_sm
for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
surface_grid[grid_index].points = MEM_calloc_arrayN(
- sizeof(SurfacePoint), grid_area, "delta grid dispalcement");
+ sizeof(SurfacePoint), grid_area, "delta grid displacement");
}
reshape_smooth_context->base_surface_grids = surface_grid;
diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c
index 6bd7b6b6a98..fa1a53f946e 100644
--- a/source/blender/blenkernel/intern/multires_unsubdivide.c
+++ b/source/blender/blenkernel/intern/multires_unsubdivide.c
@@ -80,7 +80,7 @@
/**
* Used to check if a vertex is in a disconnected element ID.
*/
-static bool is_vertex_in_id(BMVert *v, int *elem_id, int elem)
+static bool is_vertex_in_id(BMVert *v, const int *elem_id, int elem)
{
const int v_index = BM_elem_index_get(v);
return elem_id[v_index] == elem;
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 48c6727add5..8d7cb90fb71 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -61,6 +61,7 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_simulation.h"
#include "BLI_ghash.h"
#include "BLI_threads.h"
@@ -315,6 +316,33 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
+static void node_foreach_cache(ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ bNodeTree *nodetree = (bNodeTree *)id;
+ IDCacheKey key = {
+ .id_session_uuid = id->session_uuid,
+ .offset_in_ID = offsetof(bNodeTree, previews),
+ .cache_v = nodetree->previews,
+ };
+
+ /* TODO, see also `direct_link_nodetree()` in readfile.c. */
+#if 0
+ function_callback(id, &key, (void **)&nodetree->previews, 0, user_data);
+#endif
+
+ if (nodetree->type == NTREE_COMPOSIT) {
+ for (bNode *node = nodetree->nodes.first; node; node = node->next) {
+ if (node->type == CMP_NODE_MOVIEDISTORTION) {
+ key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(node->name);
+ key.cache_v = node->storage;
+ function_callback(id, &key, (void **)&node->storage, 0, user_data);
+ }
+ }
+ }
+}
+
IDTypeInfo IDType_ID_NT = {
.id_code = ID_NT,
.id_filter = FILTER_ID_NT,
@@ -330,6 +358,7 @@ IDTypeInfo IDType_ID_NT = {
.free_data = ntree_free_data,
.make_local = NULL,
.foreach_id = node_foreach_id,
+ .foreach_cache = node_foreach_cache,
};
static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype)
@@ -748,7 +777,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) {
@@ -817,12 +846,12 @@ static void socket_id_user_increment(bNodeSocket *sock)
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = sock->default_value;
- id_us_plus(&default_value->value->id);
+ id_us_plus((ID *)default_value->value);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *default_value = sock->default_value;
- id_us_plus(&default_value->value->id);
+ id_us_plus((ID *)default_value->value);
break;
}
case SOCK_FLOAT:
@@ -2465,6 +2494,7 @@ ID *BKE_node_tree_find_owner_ID(Main *bmain, struct bNodeTree *ntree)
&bmain->textures,
&bmain->scenes,
&bmain->linestyles,
+ &bmain->simulations,
NULL};
for (int i = 0; lists[i] != NULL; i++) {
@@ -3216,7 +3246,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(node->id)) {
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';
@@ -3609,6 +3639,16 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup)
FOREACH_NODETREE_END;
}
+static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ntree)
+{
+ FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
+ if (GS(owner_id->name) == ID_SIM && ntree == simulation_ntree) {
+ BKE_simulation_update_dependencies((Simulation *)owner_id, main);
+ }
+ }
+ FOREACH_NODETREE_END;
+}
+
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
{
bNode *node;
@@ -3651,7 +3691,6 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
ntreeInterfaceTypeUpdate(ntree);
}
- /* XXX hack, should be done by depsgraph!! */
if (bmain) {
ntreeUpdateAllUsers(bmain, &ntree->id);
}
@@ -3667,6 +3706,11 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
ntree_validate_links(ntree);
}
+ if (bmain != NULL && ntree->typeinfo == ntreeType_Simulation &&
+ (ntree->id.flag & LIB_EMBEDDED_DATA)) {
+ ntreeUpdateSimulationDependencies(bmain, ntree);
+ }
+
/* clear update flags */
for (node = ntree->nodes.first; node; node = node->next) {
node->update = 0;
@@ -4317,6 +4361,7 @@ static void registerFunctionNodes(void)
register_node_type_fn_switch();
register_node_type_fn_group_instance_id();
register_node_type_fn_combine_strings();
+ register_node_type_fn_object_transforms();
}
void init_nodesystem(void)
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 127c5243c7e..f77c79a2be7 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1532,12 +1532,12 @@ void BKE_object_copy_particlesystems(Object *ob_dst, const Object *ob_src, const
}
}
else if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if (mmd->type == MOD_FLUID_TYPE_FLOW) {
- if (mmd->flow) {
- if (mmd->flow->psys == psys) {
- mmd->flow->psys = npsys;
+ if (fmd->type == MOD_FLUID_TYPE_FLOW) {
+ if (fmd->flow) {
+ if (fmd->flow->psys == psys) {
+ fmd->flow->psys = npsys;
}
}
}
@@ -1758,300 +1758,172 @@ 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,
+ 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);
+ /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
+ * all expected linked data. */
+ if (ID_IS_LINKED(ob)) {
+ dupflag |= USER_DUP_LINKED_ID;
+ }
+ }
+
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);
- }
+ BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag);
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], 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);
- }
- }
- }
-
- id = obn->data;
- didit = 0;
-
- 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;
+ BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag);
}
}
- /* 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);
- }
+ ID *id_old = obn->data;
+ ID *id_new = NULL;
+ const bool need_to_duplicate_obdata = (id_old != NULL) && (id_old->newid == NULL);
- 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_old, dupflag);
}
- }
+ break;
+ case OB_CURVE:
+ if (dupflag & USER_DUP_CURVE) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_SURF:
+ if (dupflag & USER_DUP_SURF) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_FONT:
+ if (dupflag & USER_DUP_FONT) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_MBALL:
+ if (dupflag & USER_DUP_MBALL) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_LAMP:
+ if (dupflag & USER_DUP_LAMP) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_ARMATURE:
+ if (dupflag & USER_DUP_ARM) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_LATTICE:
+ if (dupflag != 0) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_CAMERA:
+ if (dupflag != 0) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_LIGHTPROBE:
+ if (dupflag & USER_DUP_LIGHTPROBE) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_SPEAKER:
+ if (dupflag != 0) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_GPENCIL:
+ if (dupflag & USER_DUP_GPENCIL) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_HAIR:
+ if (dupflag & USER_DUP_HAIR) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_POINTCLOUD:
+ if (dupflag & USER_DUP_POINTCLOUD) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ case OB_VOLUME:
+ if (dupflag & USER_DUP_VOLUME) {
+ id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag);
+ }
+ break;
+ }
+ /* If obdata has been copied, we may also have to duplicate the materials assigned to it. */
+ if (need_to_duplicate_obdata && !ELEM(id_new, NULL, id_old)) {
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], 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);
- if (ob->data != NULL) {
+#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 (obn->data != NULL) {
DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS);
}
@@ -2216,7 +2088,7 @@ void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob)
/* type conversions */
if (target->type == OB_ARMATURE) {
copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */
- BKE_pose_rest(ob->pose); /* clear all transforms in channels */
+ BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */
BKE_pose_rebuild(bmain, ob, ob->data, true); /* set all internal links */
armature_set_id_extern(ob);
@@ -2512,7 +2384,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])
@@ -2538,7 +2409,7 @@ static bool ob_parcurve(Object *ob, Object *par, float mat[4][4])
/* ctime is now a proper var setting of Curve which gets set by Animato like any other var
* that's animated, but this will only work if it actually is animated.
*
- * We divide the curvetime calculated in the previous step by the length of the path,
+ * We divide the curve-time calculated in the previous step by the length of the path,
* to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range.
*/
if (cu->pathlen) {
@@ -2616,9 +2487,10 @@ static void give_parvert(Object *par, int nr, float vec[3])
if (me_eval) {
int count = 0;
- const int numVerts = me_eval->totvert;
+ int numVerts = me_eval->totvert;
- if (em && me_eval->runtime.is_original) {
+ if (em && me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ numVerts = em->bm->totvert;
if (em->bm->elem_table_dirty & BM_VERT) {
#ifdef VPARENT_THREADING_HACK
BLI_mutex_lock(&vparent_lock);
@@ -2631,10 +2503,18 @@ static void give_parvert(Object *par, int nr, float vec[3])
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
#endif
}
+ if (nr < numVerts) {
+ if (me_eval && me_eval->runtime.edit_data && me_eval->runtime.edit_data->vertexCos) {
+ add_v3_v3(vec, me_eval->runtime.edit_data->vertexCos[nr]);
+ }
+ else {
+ const BMVert *v = BM_vert_at_index(em->bm, nr);
+ add_v3_v3(vec, v->co);
+ }
+ count++;
+ }
}
-
- if (CustomData_has_layer(&me_eval->vdata, CD_ORIGINDEX) &&
- !(em && me_eval->runtime.is_original)) {
+ else if (CustomData_has_layer(&me_eval->vdata, CD_ORIGINDEX)) {
const int *index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
/* Get the average of all verts with (original index == nr). */
for (int i = 0; i < numVerts; i++) {
@@ -2866,7 +2746,10 @@ void BKE_object_where_is_calc_time(Depsgraph *depsgraph, Scene *scene, Object *o
{
/* Execute drivers and animation. */
const bool flush_to_original = DEG_is_active(depsgraph);
- BKE_animsys_evaluate_animdata(&ob->id, ob->adt, ctime, ADT_RECALC_ALL, flush_to_original);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ ctime);
+ BKE_animsys_evaluate_animdata(
+ &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ALL, flush_to_original);
object_where_is_calc_ex(depsgraph, scene, ob, ctime, NULL, NULL);
}
@@ -3179,9 +3062,9 @@ void BKE_object_dimensions_set_ex(Object *ob,
}
}
- if (len[i] > 0.0f) {
-
- ob->scale[i] = copysignf(value[i] / len[i], ob->scale[i]);
+ const float scale = copysignf(value[i] / len[i], ob->scale[i]);
+ if (isfinite(scale)) {
+ ob->scale[i] = scale;
}
}
}
@@ -4081,15 +3964,20 @@ int BKE_object_is_modified(Scene *scene, Object *ob)
return flag;
}
-/* Check of objects moves in time. */
-/* NOTE: This function is currently optimized for usage in combination
- * with mti->canDeform, so modifiers can quickly check if their target
- * objects moves (causing deformation motion blur) or not.
+/**
+ * Check of objects moves in time.
+ *
+ * \note This function is currently optimized for usage in combination
+ * with modifier deformation checks (#eModifierTypeType_OnlyDeform),
+ * so modifiers can quickly check if their target objects moves
+ * (causing deformation motion blur) or not.
*
* This makes it possible to give some degree of false-positives here,
* but it's currently an acceptable tradeoff between complexity and check
* speed. In combination with checks of modifier stack and real life usage
- * percentage of false-positives shouldn't be that height.
+ * percentage of false-positives shouldn't be that high.
+ *
+ * \note This function does not consider physics systems.
*/
bool BKE_object_moves_in_time(const Object *object, bool recurse_parent)
{
@@ -4713,9 +4601,9 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
}
}
else if (type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if (mmd && (mmd->type & MOD_FLUID_TYPE_DOMAIN) != 0) {
+ if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) != 0) {
return true;
}
}
@@ -4763,9 +4651,13 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
/* was originally ID_RECALC_ALL - TODO - which flags are really needed??? */
/* TODO(sergey): What about animation? */
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ frame);
+
ob->id.recalc |= ID_RECALC_ALL;
if (update_mesh) {
- BKE_animsys_evaluate_animdata(&ob->id, ob->adt, frame, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(
+ &ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original);
/* ignore cache clear during subframe updates
* to not mess up cache validity */
object_cacheIgnoreClear(ob, 1);
@@ -4779,12 +4671,14 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
/* for curve following objects, parented curve has to be updated too */
if (ob->type == OB_CURVE) {
Curve *cu = ob->data;
- BKE_animsys_evaluate_animdata(&cu->id, cu->adt, frame, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(
+ &cu->id, cu->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original);
}
/* and armatures... */
if (ob->type == OB_ARMATURE) {
bArmature *arm = ob->data;
- BKE_animsys_evaluate_animdata(&arm->id, arm->adt, frame, ADT_RECALC_ANIM, flush_to_original);
+ BKE_animsys_evaluate_animdata(
+ &arm->id, arm->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original);
BKE_pose_where_is(depsgraph, scene, ob);
}
diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c
index 6ca1442497a..51ec89cf77d 100644
--- a/source/blender/blenkernel/intern/object_deform.c
+++ b/source/blender/blenkernel/intern/object_deform.c
@@ -68,7 +68,7 @@ static Lattice *object_defgroup_lattice_get(ID *id)
*
* \param map: an array mapping old indices to new indices.
*/
-void BKE_object_defgroup_remap_update_users(Object *ob, int *map)
+void BKE_object_defgroup_remap_update_users(Object *ob, const int *map)
{
ModifierData *md;
ParticleSystem *psys;
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index 474142e8555..413acde62d5 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -186,7 +186,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, Object *ob, float mat[4]
dob->random_id = BLI_hash_string(dob->ob->id.name + 2);
if (dob->persistent_id[0] != INT_MAX) {
- for (i = 0; i < MAX_DUPLI_RECUR * 2; i++) {
+ for (i = 0; i < MAX_DUPLI_RECUR; i++) {
dob->random_id = BLI_hash_int_2d(dob->random_id, (unsigned int)dob->persistent_id[i]);
}
}
@@ -523,7 +523,7 @@ static void make_duplis_font(const DupliContext *ctx)
/* Safety check even if it might fail badly when called for original object. */
const bool is_eval_curve = DEG_is_evaluated_id(&cu->id);
- /* advance matching BLI_strncpy_wchar_from_utf8 */
+ /* Advance matching BLI_str_utf8_as_utf32. */
for (a = 0; a < text_len; a++, ct++) {
/* XXX That G.main is *really* ugly, but not sure what to do here...
@@ -1053,7 +1053,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
}
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
}
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index c5ef5acb08b..7f378e6e80b 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -145,7 +145,7 @@ void BKE_object_eval_transform_final(Depsgraph *depsgraph, Object *ob)
DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob);
/* Make sure inverse matrix is always up to date. This way users of it
* do not need to worry about recalculating it. */
- invert_m4_m4(ob->imat, ob->obmat);
+ invert_m4_m4_safe(ob->imat, ob->obmat);
/* Set negative scale flag in object. */
if (is_negative_m4(ob->obmat)) {
ob->transflag |= OB_NEG_SCALE;
@@ -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/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 8957628c76a..f94ef946851 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -147,19 +147,19 @@ static void init_complex(fftw_complex cmpl, float real, float image)
cmpl[1] = image;
}
-static void add_comlex_c(fftw_complex res, fftw_complex cmpl1, fftw_complex cmpl2)
+static void add_comlex_c(fftw_complex res, const fftw_complex cmpl1, const fftw_complex cmpl2)
{
res[0] = cmpl1[0] + cmpl2[0];
res[1] = cmpl1[1] + cmpl2[1];
}
-static void mul_complex_f(fftw_complex res, fftw_complex cmpl, float f)
+static void mul_complex_f(fftw_complex res, const fftw_complex cmpl, float f)
{
res[0] = cmpl[0] * (double)f;
res[1] = cmpl[1] * (double)f;
}
-static void mul_complex_c(fftw_complex res, fftw_complex cmpl1, fftw_complex cmpl2)
+static void mul_complex_c(fftw_complex res, const fftw_complex cmpl1, const fftw_complex cmpl2)
{
fftwf_complex temp;
temp[0] = cmpl1[0] * cmpl2[0] - cmpl1[1] * cmpl2[1];
@@ -178,7 +178,7 @@ static float image_c(fftw_complex cmpl)
return cmpl[1];
}
-static void conj_complex(fftw_complex res, fftw_complex cmpl1)
+static void conj_complex(fftw_complex res, const fftw_complex cmpl1)
{
res[0] = cmpl1[0];
res[1] = -cmpl1[1];
@@ -831,7 +831,7 @@ void BKE_ocean_init(struct Ocean *o,
o->_A = A;
o->_w = w;
o->_damp_reflections = 1.0f - damp;
- o->_wind_alignment = alignment;
+ o->_wind_alignment = alignment * 10.0f;
o->_depth = depth;
o->_Lx = Lx;
o->_Lz = Lz;
@@ -845,7 +845,7 @@ void BKE_ocean_init(struct Ocean *o,
/* Common JONSWAP parameters. */
o->_fetch_jonswap = fetch_jonswap;
- o->_sharpen_peak_jonswap = sharpen_peak_jonswap;
+ o->_sharpen_peak_jonswap = sharpen_peak_jonswap * 10.0f;
o->_do_disp_y = do_height_field;
o->_do_normals = do_normals;
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..d4edcf88389 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1310,10 +1310,17 @@ 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;
+
+ MEM_SAFE_FREE(ss->preview_vert_index_list);
+
+ MEM_SAFE_FREE(ss->vertex_info.connected_component);
+ MEM_SAFE_FREE(ss->vertex_info.boundary);
+
+ MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
}
void BKE_sculptsession_bm_to_me_for_render(Object *object)
@@ -1366,8 +1373,6 @@ void BKE_sculptsession_free(Object *ob)
MEM_SAFE_FREE(ss->deform_cos);
MEM_SAFE_FREE(ss->deform_imats);
- MEM_SAFE_FREE(ss->preview_vert_index_list);
-
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 +1479,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 UNUSED(need_colors))
{
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
@@ -1490,6 +1499,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)) {
@@ -1535,6 +1546,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. */
@@ -1662,14 +1674,27 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
BLI_assert(me_eval != NULL);
+ sculpt_update_object(depsgraph, ob_orig, me_eval, false, false, false);
+}
+
+void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
+{
+ Mesh *orig_me = BKE_object_get_original_mesh(object);
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return;
+ }
+
+ if (CustomData_has_layer(&orig_me->vdata, CD_PROP_COLOR)) {
+ return;
+ }
- sculpt_update_object(depsgraph, ob_orig, me_eval, false, false);
+ 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);
}
-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 +1704,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 eb485e1522f..a003daf1042 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -283,8 +283,8 @@ int count_particles_mod(ParticleSystem *psys, int totgr, int cur)
}
return tot;
}
-/* we allocate path cache memory in chunks instead of a big contiguous
- * chunk, windows' memory allocater fails to find big blocks of memory often */
+/* We allocate path cache memory in chunks instead of a big contiguous
+ * chunk, windows' memory allocator fails to find big blocks of memory often. */
#define PATH_CACHE_BUF_SIZE 1024
@@ -410,7 +410,7 @@ struct LatticeDeformData *psys_create_lattice_deform_data(ParticleSimulationData
}
}
if (lattice) {
- lattice_deform_data = init_latt_deform(lattice, NULL);
+ lattice_deform_data = BKE_lattice_deform_data_create(lattice, NULL);
}
}
@@ -1297,7 +1297,7 @@ static void do_particle_interpolation(ParticleSystem *psys,
dfra = keys[2].time - keys[1].time;
keytime = (real_t - keys[1].time) / dfra;
- /* convert velocity to timestep size */
+ /* Convert velocity to time-step size. */
if (pind->keyed || pind->cache || point_vel) {
invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f);
mul_v3_fl(keys[1].vel, invdt);
@@ -1305,8 +1305,8 @@ static void do_particle_interpolation(ParticleSystem *psys,
interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime);
}
- /* Now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between
- * [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation). */
+ /* Now we should have in chronological order k1<=k2<=t<=k3<=k4 with key-time between
+ * [0, 1]->[k2, k3] (k1 & k4 used for cardinal & b-spline interpolation). */
psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ?
-1 /* signal for cubic interpolation */
:
@@ -2262,6 +2262,7 @@ static void do_path_effectors(ParticleSimulationData *sim,
sim->psys->part->effector_weights,
&epoint,
force,
+ NULL,
NULL);
mul_v3_fl(force,
@@ -3150,7 +3151,8 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra, const bool use_re
/* lattices have to be calculated separately to avoid mixups between effector calculations */
if (psys->lattice_deform_data) {
for (k = 0, ca = cache[p]; k <= segments; k++, ca++) {
- calc_latt_deform(psys->lattice_deform_data, ca->co, psys->lattice_strength);
+ BKE_lattice_deform_data_eval_co(
+ psys->lattice_deform_data, ca->co, psys->lattice_strength);
}
}
}
@@ -3185,7 +3187,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra, const bool use_re
psys->totcached = totpart;
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
@@ -3609,7 +3611,8 @@ void psys_mat_hair_to_global(
/************************************************/
/* ParticleSettings handling */
/************************************************/
-ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, const char *name)
+static ModifierData *object_add_or_copy_particle_system(
+ Main *bmain, Scene *scene, Object *ob, const char *name, const ParticleSystem *psys_orig)
{
ParticleSystem *psys;
ModifierData *md;
@@ -3620,7 +3623,7 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob,
}
if (name == NULL) {
- name = DATA_("ParticleSettings");
+ name = (psys_orig != NULL) ? psys_orig->name : DATA_("ParticleSettings");
}
psys = ob->particlesystem.first;
@@ -3633,8 +3636,13 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob,
BLI_addtail(&ob->particlesystem, psys);
psys_unique_name(ob, psys, name);
- psys->part = BKE_particlesettings_add(bmain, psys->name);
-
+ if (psys_orig != NULL) {
+ psys->part = psys_orig->part;
+ id_us_plus(&psys->part->id);
+ }
+ else {
+ psys->part = BKE_particlesettings_add(bmain, psys->name);
+ }
md = BKE_modifier_new(eModifierType_ParticleSystem);
BLI_strncpy(md->name, psys->name, sizeof(md->name));
BKE_modifier_unique_name(&ob->modifiers, md);
@@ -3654,6 +3662,20 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob,
return md;
}
+
+ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, const char *name)
+{
+ return object_add_or_copy_particle_system(bmain, scene, ob, name, NULL);
+}
+
+ModifierData *object_copy_particle_system(Main *bmain,
+ Scene *scene,
+ Object *ob,
+ const ParticleSystem *psys_orig)
+{
+ return object_add_or_copy_particle_system(bmain, scene, ob, NULL, psys_orig);
+}
+
void object_remove_particle_system(Main *bmain, Scene *UNUSED(scene), Object *ob)
{
ParticleSystem *psys = psys_get_current(ob);
@@ -3666,43 +3688,43 @@ void object_remove_particle_system(Main *bmain, Scene *UNUSED(scene), Object *ob
/* Clear particle system in fluid modifier. */
if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid))) {
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
/* Clear particle system pointer in flow settings. */
- if ((mmd->type == MOD_FLUID_TYPE_FLOW) && mmd->flow && mmd->flow->psys) {
- if (mmd->flow->psys == psys) {
- mmd->flow->psys = NULL;
+ if ((fmd->type == MOD_FLUID_TYPE_FLOW) && fmd->flow && fmd->flow->psys) {
+ if (fmd->flow->psys == psys) {
+ fmd->flow->psys = NULL;
}
}
/* Clear particle flag in domain settings when removing particle system manually. */
- if (mmd->type == MOD_FLUID_TYPE_DOMAIN) {
+ if (fmd->type == MOD_FLUID_TYPE_DOMAIN) {
if (psys->part->type == PART_FLUID_FLIP) {
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
}
if (psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_SPRAYFOAM ||
psys->part->type == PART_FLUID_SPRAYBUBBLE ||
psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) {
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY;
}
if (psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_SPRAYFOAM ||
psys->part->type == PART_FLUID_FOAMBUBBLE ||
psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) {
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM;
}
if (psys->part->type == PART_FLUID_BUBBLE || psys->part->type == PART_FLUID_FOAMBUBBLE ||
psys->part->type == PART_FLUID_SPRAYBUBBLE ||
psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) {
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE;
}
if (psys->part->type == PART_FLUID_TRACER) {
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER;
}
/* Disable combined export if combined particle system was deleted. */
if (psys->part->type == PART_FLUID_SPRAYFOAM || psys->part->type == PART_FLUID_SPRAYBUBBLE ||
psys->part->type == PART_FLUID_FOAMBUBBLE ||
psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE) {
- mmd->domain->sndparticle_combined_export = SNDPARTICLE_COMBINED_EXPORT_OFF;
+ fmd->domain->sndparticle_combined_export = SNDPARTICLE_COMBINED_EXPORT_OFF;
}
}
}
@@ -4413,7 +4435,8 @@ void psys_get_particle_on_path(ParticleSimulationData *sim,
}
if (psys->lattice_deform_data && edit == 0) {
- calc_latt_deform(psys->lattice_deform_data, state->co, psys->lattice_strength);
+ BKE_lattice_deform_data_eval_co(
+ psys->lattice_deform_data, state->co, psys->lattice_strength);
}
}
}
@@ -4696,7 +4719,8 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta
do_child_modifiers(&modifier_ctx, mat, state, t);
if (psys->lattice_deform_data) {
- calc_latt_deform(psys->lattice_deform_data, state->co, psys->lattice_strength);
+ BKE_lattice_deform_data_eval_co(
+ psys->lattice_deform_data, state->co, psys->lattice_strength);
}
}
else {
@@ -4760,7 +4784,8 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta
}
if (sim->psys->lattice_deform_data) {
- calc_latt_deform(sim->psys->lattice_deform_data, state->co, psys->lattice_strength);
+ BKE_lattice_deform_data_eval_co(
+ sim->psys->lattice_deform_data, state->co, psys->lattice_strength);
}
}
@@ -4782,7 +4807,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.
*
@@ -4970,12 +4995,13 @@ void psys_apply_hair_lattice(Depsgraph *depsgraph, Scene *scene, Object *ob, Par
hkey = pa->hair;
for (h = 0; h < pa->totkey; h++, hkey++) {
mul_m4_v3(hairmat, hkey->co);
- calc_latt_deform(psys->lattice_deform_data, hkey->co, psys->lattice_strength);
+ BKE_lattice_deform_data_eval_co(
+ psys->lattice_deform_data, hkey->co, psys->lattice_strength);
mul_m4_v3(imat, hkey->co);
}
}
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
/* protect the applied shape */
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index 7b9b2484dbe..e0dccd4d14a 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -433,7 +433,7 @@ static void psys_uv_to_w(float u, float v, int quad, float *w)
}
/* Find the index in "sum" array before "value" is crossed. */
-static int distribute_binary_search(float *sum, int n, float value)
+static int distribute_binary_search(const float *sum, int n, float value)
{
int mid, low = 0, high = n - 1;
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 31d51a74e7f..bec9cbbad79 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -536,7 +536,7 @@ void psys_thread_context_free(ParticleThreadContext *ctx)
}
if (ctx->sim.psys->lattice_deform_data) {
- end_latt_deform(ctx->sim.psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(ctx->sim.psys->lattice_deform_data);
ctx->sim.psys->lattice_deform_data = NULL;
}
@@ -1939,7 +1939,7 @@ static void sphclassical_density_accum_cb(void *userdata,
return;
}
- /* Smoothing factor. Utilise the Wendland kernel. gnuplot:
+ /* Smoothing factor. Utilize the Wendland kernel. gnuplot:
* q1(x) = (2.0 - x)**4 * ( 1.0 + 2.0 * x)
* plot [0:2] q1(x) */
q = qfac / pow3f(pfr->h) * pow4f(2.0f - rij_h) * (1.0f + 2.0f * rij_h);
@@ -2054,7 +2054,7 @@ static void sphclassical_force_cb(void *sphdata_v,
npressure = stiffness * (pow7f(npa->sphdensity / rest_density) - 1.0f);
- /* First derivative of smoothing factor. Utilise the Wendland kernel.
+ /* First derivative of smoothing factor. Utilize the Wendland kernel.
* gnuplot:
* q2(x) = 2.0 * (2.0 - x)**4 - 4.0 * (2.0 - x)**3 * (1.0 + 2.0 * x)
* plot [0:2] q2(x)
@@ -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);
@@ -2942,7 +2947,7 @@ static int collision_response(ParticleSimulationData *sim,
/* get exact velocity right before collision */
madd_v3_v3v3fl(v0, col->ve1, col->acc, dt1);
- /* Convert collider velocity from 1/framestep to 1/s TODO:
+ /* Convert collider velocity from `1/frame_step` to `1/s` TODO:
* here we assume 1 frame step for collision modifier. */
mul_v3_fl(pce->vel, col->inv_timestep);
@@ -4126,7 +4131,7 @@ static void cached_step(ParticleSimulationData *sim, float cfra, const bool use_
}
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
@@ -4183,11 +4188,11 @@ static void particles_fluid_step(ParticleSimulationData *sim,
#else
{
Object *ob = sim->ob;
- FluidModifierData *mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob,
+ FluidModifierData *fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob,
eModifierType_Fluid);
- if (mmd && mmd->domain && mmd->domain->fluid) {
- FluidDomainSettings *mds = mmd->domain;
+ if (fmd && fmd->domain && fmd->domain->fluid) {
+ FluidDomainSettings *fds = fmd->domain;
ParticleSettings *part = psys->part;
ParticleData *pa = NULL;
@@ -4205,15 +4210,15 @@ static void particles_fluid_step(ParticleSimulationData *sim,
/* Sanity check: parts also enabled in fluid domain? */
if ((particles_has_flip(part->type) &&
- (mds->particle_type & FLUID_DOMAIN_PARTICLE_FLIP) == 0) ||
+ (fds->particle_type & FLUID_DOMAIN_PARTICLE_FLIP) == 0) ||
(particles_has_spray(part->type) &&
- (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) == 0) ||
+ (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) == 0) ||
(particles_has_bubble(part->type) &&
- (mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) == 0) ||
+ (fds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) == 0) ||
(particles_has_foam(part->type) &&
- (mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) == 0) ||
+ (fds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) == 0) ||
(particles_has_tracer(part->type) &&
- (mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) == 0)) {
+ (fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) == 0)) {
BLI_snprintf(debugStrBuffer,
sizeof(debugStrBuffer),
"particles_fluid_step::error - found particle system that is not enabled in "
@@ -4223,15 +4228,15 @@ static void particles_fluid_step(ParticleSimulationData *sim,
/* Count particle amount. tottypepart is only important for snd particles. */
if (part->type == PART_FLUID_FLIP) {
- tottypepart = totpart = manta_liquid_get_num_flip_particles(mds->fluid);
+ tottypepart = totpart = manta_liquid_get_num_flip_particles(fds->fluid);
}
if (particles_has_spray(part->type) || particles_has_bubble(part->type) ||
particles_has_foam(part->type) || particles_has_tracer(part->type)) {
- totpart = manta_liquid_get_num_snd_particles(mds->fluid);
+ totpart = manta_liquid_get_num_snd_particles(fds->fluid);
/* tottypepart is the amount of particles of a snd particle type. */
for (p = 0; p < totpart; p++) {
- flagActivePart = manta_liquid_get_snd_particle_flag_at(mds->fluid, p);
+ flagActivePart = manta_liquid_get_snd_particle_flag_at(fds->fluid, p);
if (particles_has_spray(part->type) && (flagActivePart & PARTICLE_TYPE_SPRAY)) {
tottypepart++;
}
@@ -4276,39 +4281,39 @@ static void particles_fluid_step(ParticleSimulationData *sim,
/* flag, res, upres, pos, vel for FLIP and snd particles have different getters. */
if (part->type == PART_FLUID_FLIP) {
- flagActivePart = manta_liquid_get_flip_particle_flag_at(mds->fluid, p);
+ flagActivePart = manta_liquid_get_flip_particle_flag_at(fds->fluid, p);
- resX = (float)manta_get_res_x(mds->fluid);
- resY = (float)manta_get_res_y(mds->fluid);
- resZ = (float)manta_get_res_z(mds->fluid);
+ resX = (float)manta_get_res_x(fds->fluid);
+ resY = (float)manta_get_res_y(fds->fluid);
+ resZ = (float)manta_get_res_z(fds->fluid);
upres = 1;
- posX = manta_liquid_get_flip_particle_position_x_at(mds->fluid, p);
- posY = manta_liquid_get_flip_particle_position_y_at(mds->fluid, p);
- posZ = manta_liquid_get_flip_particle_position_z_at(mds->fluid, p);
+ posX = manta_liquid_get_flip_particle_position_x_at(fds->fluid, p);
+ posY = manta_liquid_get_flip_particle_position_y_at(fds->fluid, p);
+ posZ = manta_liquid_get_flip_particle_position_z_at(fds->fluid, p);
- velX = manta_liquid_get_flip_particle_velocity_x_at(mds->fluid, p);
- velY = manta_liquid_get_flip_particle_velocity_y_at(mds->fluid, p);
- velZ = manta_liquid_get_flip_particle_velocity_z_at(mds->fluid, p);
+ velX = manta_liquid_get_flip_particle_velocity_x_at(fds->fluid, p);
+ velY = manta_liquid_get_flip_particle_velocity_y_at(fds->fluid, p);
+ velZ = manta_liquid_get_flip_particle_velocity_z_at(fds->fluid, p);
}
else if (particles_has_spray(part->type) || particles_has_bubble(part->type) ||
particles_has_foam(part->type) || particles_has_tracer(part->type)) {
- flagActivePart = manta_liquid_get_snd_particle_flag_at(mds->fluid, p);
+ flagActivePart = manta_liquid_get_snd_particle_flag_at(fds->fluid, p);
- resX = (float)manta_liquid_get_particle_res_x(mds->fluid);
- resY = (float)manta_liquid_get_particle_res_y(mds->fluid);
- resZ = (float)manta_liquid_get_particle_res_z(mds->fluid);
+ resX = (float)manta_liquid_get_particle_res_x(fds->fluid);
+ resY = (float)manta_liquid_get_particle_res_y(fds->fluid);
+ resZ = (float)manta_liquid_get_particle_res_z(fds->fluid);
- upres = manta_liquid_get_particle_upres(mds->fluid);
+ upres = manta_liquid_get_particle_upres(fds->fluid);
- posX = manta_liquid_get_snd_particle_position_x_at(mds->fluid, p);
- posY = manta_liquid_get_snd_particle_position_y_at(mds->fluid, p);
- posZ = manta_liquid_get_snd_particle_position_z_at(mds->fluid, p);
+ posX = manta_liquid_get_snd_particle_position_x_at(fds->fluid, p);
+ posY = manta_liquid_get_snd_particle_position_y_at(fds->fluid, p);
+ posZ = manta_liquid_get_snd_particle_position_z_at(fds->fluid, p);
- velX = manta_liquid_get_snd_particle_velocity_x_at(mds->fluid, p);
- velY = manta_liquid_get_snd_particle_velocity_y_at(mds->fluid, p);
- velZ = manta_liquid_get_snd_particle_velocity_z_at(mds->fluid, p);
+ velX = manta_liquid_get_snd_particle_velocity_x_at(fds->fluid, p);
+ velY = manta_liquid_get_snd_particle_velocity_y_at(fds->fluid, p);
+ velZ = manta_liquid_get_snd_particle_velocity_z_at(fds->fluid, p);
}
else {
BLI_snprintf(debugStrBuffer,
@@ -4357,10 +4362,10 @@ static void particles_fluid_step(ParticleSimulationData *sim,
}
/* Get size (dimension) but considering scaling */
- copy_v3_v3(cell_size_scaled, mds->cell_size);
+ copy_v3_v3(cell_size_scaled, fds->cell_size);
mul_v3_v3(cell_size_scaled, ob->scale);
- madd_v3fl_v3fl_v3fl_v3i(min, mds->p0, cell_size_scaled, mds->res_min);
- madd_v3fl_v3fl_v3fl_v3i(max, mds->p0, cell_size_scaled, mds->res_max);
+ madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min);
+ madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, cell_size_scaled, fds->res_max);
sub_v3_v3v3(size, max, min);
/* Biggest dimension will be used for up-scaling. */
@@ -4374,7 +4379,7 @@ static void particles_fluid_step(ParticleSimulationData *sim,
float resDomain[3] = {resX, resY, resZ};
mul_v3_fl(resDomain, 0.5f);
sub_v3_v3(pa->state.co, resDomain);
- mul_v3_fl(pa->state.co, mds->dx);
+ mul_v3_fl(pa->state.co, fds->dx);
/* Match domain dimension / size. */
float scaleAbs[3] = {
@@ -4388,9 +4393,9 @@ static void particles_fluid_step(ParticleSimulationData *sim,
/* Add origin offset to particle position. */
zero_v3(tmp);
zero_v3(tmp2);
- sub_v3_v3v3(tmp2, mds->p1, mds->p0);
+ sub_v3_v3v3(tmp2, fds->p1, fds->p0);
mul_v3_fl(tmp2, 0.5f);
- add_v3_v3v3(tmp, tmp, mds->p1);
+ add_v3_v3v3(tmp, tmp, fds->p1);
sub_v3_v3(tmp, tmp2);
mul_v3_v3(tmp, ob->scale);
add_v3_v3(pa->state.co, tmp);
@@ -4402,7 +4407,7 @@ static void particles_fluid_step(ParticleSimulationData *sim,
/* Set particle velocity. */
float velParticle[3] = {velX, velY, velZ};
copy_v3_v3(pa->state.vel, velParticle);
- mul_v3_fl(pa->state.vel, mds->dx);
+ mul_v3_fl(pa->state.vel, fds->dx);
# if 0
/* Debugging: Print particle velocity. */
printf("pa->state.vel[0]: %f, pa->state.vel[1]: %f, pa->state.vel[2]: %f\n",
@@ -4616,7 +4621,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_
/* cleanup */
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
}
@@ -4849,8 +4854,10 @@ void particle_system_update(struct Depsgraph *depsgraph,
for (i = 0; i <= part->hair_step; i++) {
hcfra = 100.0f * (float)i / (float)psys->part->hair_step;
if ((part->flag & PART_HAIR_REGROW) == 0) {
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, hcfra);
BKE_animsys_evaluate_animdata(
- &part_local->id, part_local->adt, hcfra, ADT_RECALC_ANIM, false);
+ &part_local->id, part_local->adt, &anim_eval_context, ADT_RECALC_ANIM, false);
}
system_step(&sim, hcfra, use_render_params);
psys->cfra = hcfra;
@@ -4961,6 +4968,7 @@ void particle_system_update(struct Depsgraph *depsgraph,
psys_orig->flag = (psys->flag & ~PSYS_SHARED_CACHES);
psys_orig->cfra = psys->cfra;
psys_orig->recalc = psys->recalc;
+ psys_orig->part->totpart = part->totpart;
}
}
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 19f28047b80..67988427bd2 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -358,7 +358,7 @@ static void update_vb(PBVH *pbvh, PBVHNode *node, BBC *prim_bbc, int offset, int
/* Returns the number of visible quads in the nodes' grids. */
int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden,
- int *grid_indices,
+ const int *grid_indices,
int totgrid,
int gridsize)
{
@@ -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);
}
@@ -1537,7 +1542,7 @@ static void pbvh_update_visibility_task_cb(void *__restrict userdata,
PBVHUpdateData *data = userdata;
PBVH *pbvh = data->pbvh;
PBVHNode *node = data->nodes[n];
- if (node->flag & PBVH_UpdateMask) {
+ if (node->flag & PBVH_UpdateVisibility) {
switch (BKE_pbvh_type(pbvh)) {
case PBVH_FACES:
pbvh_faces_node_visibility_update(pbvh, node);
@@ -1549,7 +1554,7 @@ static void pbvh_update_visibility_task_cb(void *__restrict userdata,
pbvh_bmesh_node_visibility_update(node);
break;
}
- node->flag &= ~PBVH_UpdateMask;
+ node->flag &= ~PBVH_UpdateVisibility;
}
}
@@ -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 |
@@ -1762,6 +1772,11 @@ void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden)
}
}
+bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node)
+{
+ return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyHidden);
+}
+
void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked)
{
BLI_assert(node->flag & PBVH_Leaf);
@@ -2905,6 +2920,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 +2993,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 9cd17310f07..c2c5b42dbb0 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -138,6 +138,7 @@ static int ptcache_data_size[] = {
static int ptcache_extra_datasize[] = {
0,
sizeof(ParticleSpring),
+ sizeof(float) * 3,
};
/* forward declarations */
@@ -176,6 +177,23 @@ static int ptcache_basic_header_write(PTCacheFile *pf)
return 1;
}
+static void ptcache_add_extra_data(PTCacheMem *pm,
+ unsigned int type,
+ unsigned int count,
+ void *data)
+{
+ PTCacheExtra *extra = MEM_callocN(sizeof(PTCacheExtra), "Point cache: extra data descriptor");
+
+ extra->type = type;
+ extra->totdata = count;
+
+ size_t size = extra->totdata * ptcache_extra_datasize[extra->type];
+
+ extra->data = MEM_mallocN(size, "Point cache: extra data");
+ memcpy(extra->data, data, size);
+
+ BLI_addtail(&pm->extradata, extra);
+}
/* Softbody functions */
static int ptcache_softbody_write(int index, void *soft_v, void **data, int UNUSED(cfra))
{
@@ -188,7 +206,7 @@ static int ptcache_softbody_write(int index, void *soft_v, void **data, int UNUS
return 1;
}
static void ptcache_softbody_read(
- int index, void *soft_v, void **data, float UNUSED(cfra), float *old_data)
+ int index, void *soft_v, void **data, float UNUSED(cfra), const float *old_data)
{
SoftBody *soft = soft_v;
BodyPoint *bp = soft->bpoint + index;
@@ -202,8 +220,13 @@ static void ptcache_softbody_read(
PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, bp->vec);
}
}
-static void ptcache_softbody_interpolate(
- int index, void *soft_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+static void ptcache_softbody_interpolate(int index,
+ void *soft_v,
+ void **data,
+ float cfra,
+ float cfra1,
+ float cfra2,
+ const float *old_data)
{
SoftBody *soft = soft_v;
BodyPoint *bp = soft->bpoint + index;
@@ -298,7 +321,7 @@ static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra
return 1 + (pa->state.time >= pa->time && pa->prev_state.time <= pa->time);
}
static void ptcache_particle_read(
- int index, void *psys_v, void **data, float cfra, float *old_data)
+ int index, void *psys_v, void **data, float cfra, const float *old_data)
{
ParticleSystem *psys = psys_v;
ParticleData *pa;
@@ -365,8 +388,13 @@ static void ptcache_particle_read(
unit_qt(pa->state.rot);
}
}
-static void ptcache_particle_interpolate(
- int index, void *psys_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+static void ptcache_particle_interpolate(int index,
+ void *psys_v,
+ void **data,
+ float cfra,
+ float cfra1,
+ float cfra2,
+ const float *old_data)
{
ParticleSystem *psys = psys_v;
ParticleData *pa;
@@ -467,21 +495,12 @@ static int ptcache_particle_totwrite(void *psys_v, int cfra)
static void ptcache_particle_extra_write(void *psys_v, PTCacheMem *pm, int UNUSED(cfra))
{
ParticleSystem *psys = psys_v;
- PTCacheExtra *extra = NULL;
if (psys->part->phystype == PART_PHYS_FLUID && psys->part->fluid &&
psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS && psys->tot_fluidsprings &&
psys->fluid_springs) {
- extra = MEM_callocN(sizeof(PTCacheExtra), "Point cache: fluid extra data");
-
- extra->type = BPHYS_EXTRA_FLUID_SPRINGS;
- extra->totdata = psys->tot_fluidsprings;
-
- extra->data = MEM_callocN(extra->totdata * ptcache_extra_datasize[extra->type],
- "Point cache: extra data");
- memcpy(extra->data, psys->fluid_springs, extra->totdata * ptcache_extra_datasize[extra->type]);
-
- BLI_addtail(&pm->extradata, extra);
+ ptcache_add_extra_data(
+ pm, BPHYS_EXTRA_FLUID_SPRINGS, psys->tot_fluidsprings, psys->fluid_springs);
}
}
@@ -519,7 +538,7 @@ static int ptcache_cloth_write(int index, void *cloth_v, void **data, int UNUSED
return 1;
}
static void ptcache_cloth_read(
- int index, void *cloth_v, void **data, float UNUSED(cfra), float *old_data)
+ int index, void *cloth_v, void **data, float UNUSED(cfra), const float *old_data)
{
ClothModifierData *clmd = cloth_v;
Cloth *cloth = clmd->clothObject;
@@ -536,8 +555,13 @@ static void ptcache_cloth_read(
PTCACHE_DATA_TO(data, BPHYS_DATA_XCONST, 0, vert->xconst);
}
}
-static void ptcache_cloth_interpolate(
- int index, void *cloth_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+static void ptcache_cloth_interpolate(int index,
+ void *cloth_v,
+ void **data,
+ float cfra,
+ float cfra1,
+ float cfra2,
+ const float *old_data)
{
ClothModifierData *clmd = cloth_v;
Cloth *cloth = clmd->clothObject;
@@ -575,6 +599,33 @@ static void ptcache_cloth_interpolate(
/* should vert->xconst be interpolated somehow too? - jahka */
}
+static void ptcache_cloth_extra_write(void *cloth_v, PTCacheMem *pm, int UNUSED(cfra))
+{
+ ClothModifierData *clmd = cloth_v;
+ Cloth *cloth = clmd->clothObject;
+
+ if (!is_zero_v3(cloth->average_acceleration)) {
+ ptcache_add_extra_data(pm, BPHYS_EXTRA_CLOTH_ACCELERATION, 1, cloth->average_acceleration);
+ }
+}
+static void ptcache_cloth_extra_read(void *cloth_v, PTCacheMem *pm, float UNUSED(cfra))
+{
+ ClothModifierData *clmd = cloth_v;
+ Cloth *cloth = clmd->clothObject;
+ PTCacheExtra *extra = pm->extradata.first;
+
+ zero_v3(cloth->average_acceleration);
+
+ for (; extra; extra = extra->next) {
+ switch (extra->type) {
+ case BPHYS_EXTRA_CLOTH_ACCELERATION: {
+ copy_v3_v3(cloth->average_acceleration, extra->data);
+ break;
+ }
+ }
+ }
+}
+
static int ptcache_cloth_totpoint(void *cloth_v, int UNUSED(cfra))
{
ClothModifierData *clmd = cloth_v;
@@ -591,11 +642,11 @@ static void ptcache_cloth_error(void *cloth_v, const char *message)
/* Smoke functions */
static int ptcache_smoke_totpoint(void *smoke_v, int UNUSED(cfra))
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
+ FluidDomainSettings *fds = fmd->domain;
- if (mds->fluid) {
- return mds->base_res[0] * mds->base_res[1] * mds->base_res[2];
+ if (fds->fluid) {
+ return fds->base_res[0] * fds->base_res[1] * fds->base_res[2];
}
else {
return 0;
@@ -604,28 +655,28 @@ static int ptcache_smoke_totpoint(void *smoke_v, int UNUSED(cfra))
static void ptcache_smoke_error(void *smoke_v, const char *message)
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
- BKE_modifier_set_error(&mmd->modifier, "%s", message);
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
+ BKE_modifier_set_error(&fmd->modifier, "%s", message);
}
# define SMOKE_CACHE_VERSION "1.04"
static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v)
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
+ FluidDomainSettings *fds = fmd->domain;
int ret = 0;
- int fluid_fields = BKE_fluid_get_data_flags(mds);
+ int fluid_fields = BKE_fluid_get_data_flags(fds);
/* version header */
ptcache_file_write(pf, SMOKE_CACHE_VERSION, 4, sizeof(char));
ptcache_file_write(pf, &fluid_fields, 1, sizeof(int));
- ptcache_file_write(pf, &mds->active_fields, 1, sizeof(int));
- ptcache_file_write(pf, &mds->res, 3, sizeof(int));
- ptcache_file_write(pf, &mds->dx, 1, sizeof(float));
+ ptcache_file_write(pf, &fds->active_fields, 1, sizeof(int));
+ ptcache_file_write(pf, &fds->res, 3, sizeof(int));
+ ptcache_file_write(pf, &fds->dx, 1, sizeof(float));
- if (mds->fluid) {
- size_t res = mds->res[0] * mds->res[1] * mds->res[2];
+ if (fds->fluid) {
+ size_t res = fds->res[0] * fds->res[1] * fds->res[2];
float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
unsigned char *obstacles;
unsigned int in_len = sizeof(float) * (unsigned int)res;
@@ -633,11 +684,11 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v)
"pointcache_lzo_buffer");
// int mode = res >= 1000000 ? 2 : 1;
int mode = 1; // light
- if (mds->cache_comp == SM_CACHE_HEAVY) {
+ if (fds->cache_comp == SM_CACHE_HEAVY) {
mode = 2; // heavy
}
- smoke_export(mds->fluid,
+ smoke_export(fds->fluid,
&dt,
&dx,
&dens,
@@ -655,7 +706,7 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v)
&obstacles,
NULL);
- ptcache_file_compressed_write(pf, (unsigned char *)mds->shadow, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)fds->shadow, in_len, out, mode);
ptcache_file_compressed_write(pf, (unsigned char *)dens, in_len, out, mode);
if (fluid_fields & FLUID_DOMAIN_ACTIVE_HEAT) {
ptcache_file_compressed_write(pf, (unsigned char *)heat, in_len, out, mode);
@@ -677,43 +728,43 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v)
ptcache_file_compressed_write(pf, (unsigned char *)obstacles, (unsigned int)res, out, mode);
ptcache_file_write(pf, &dt, 1, sizeof(float));
ptcache_file_write(pf, &dx, 1, sizeof(float));
- ptcache_file_write(pf, &mds->p0, 3, sizeof(float));
- ptcache_file_write(pf, &mds->p1, 3, sizeof(float));
- ptcache_file_write(pf, &mds->dp0, 3, sizeof(float));
- ptcache_file_write(pf, &mds->shift, 3, sizeof(int));
- ptcache_file_write(pf, &mds->obj_shift_f, 3, sizeof(float));
- ptcache_file_write(pf, &mds->obmat, 16, sizeof(float));
- ptcache_file_write(pf, &mds->base_res, 3, sizeof(int));
- ptcache_file_write(pf, &mds->res_min, 3, sizeof(int));
- ptcache_file_write(pf, &mds->res_max, 3, sizeof(int));
- ptcache_file_write(pf, &mds->active_color, 3, sizeof(float));
+ ptcache_file_write(pf, &fds->p0, 3, sizeof(float));
+ ptcache_file_write(pf, &fds->p1, 3, sizeof(float));
+ ptcache_file_write(pf, &fds->dp0, 3, sizeof(float));
+ ptcache_file_write(pf, &fds->shift, 3, sizeof(int));
+ ptcache_file_write(pf, &fds->obj_shift_f, 3, sizeof(float));
+ ptcache_file_write(pf, &fds->obmat, 16, sizeof(float));
+ ptcache_file_write(pf, &fds->base_res, 3, sizeof(int));
+ ptcache_file_write(pf, &fds->res_min, 3, sizeof(int));
+ ptcache_file_write(pf, &fds->res_max, 3, sizeof(int));
+ ptcache_file_write(pf, &fds->active_color, 3, sizeof(float));
MEM_freeN(out);
ret = 1;
}
- if (mds->wt) {
+ if (fds->wt) {
int res_big_array[3];
int res_big;
- int res = mds->res[0] * mds->res[1] * mds->res[2];
+ int res = fds->res[0] * fds->res[1] * fds->res[2];
float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
unsigned int in_len = sizeof(float) * (unsigned int)res;
unsigned int in_len_big;
unsigned char *out;
int mode;
- smoke_turbulence_get_res(mds->wt, res_big_array);
+ smoke_turbulence_get_res(fds->wt, res_big_array);
res_big = res_big_array[0] * res_big_array[1] * res_big_array[2];
// mode = res_big >= 1000000 ? 2 : 1;
mode = 1; // light
- if (mds->cache_high_comp == SM_CACHE_HEAVY) {
+ if (fds->cache_high_comp == SM_CACHE_HEAVY) {
mode = 2; // heavy
}
in_len_big = sizeof(float) * (unsigned int)res_big;
- smoke_turbulence_export(mds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+ smoke_turbulence_export(fds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len_big), "pointcache_lzo_buffer");
ptcache_file_compressed_write(pf, (unsigned char *)dens, in_len_big, out, mode);
@@ -744,24 +795,24 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v)
/* read old smoke cache from 2.64 */
static int ptcache_smoke_read_old(PTCacheFile *pf, void *smoke_v)
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
+ FluidDomainSettings *fds = fmd->domain;
- if (mds->fluid) {
- const size_t res = mds->res[0] * mds->res[1] * mds->res[2];
+ if (fds->fluid) {
+ const size_t res = fds->res[0] * fds->res[1] * fds->res[2];
const unsigned int out_len = (unsigned int)res * sizeof(float);
float dt, dx, *dens, *heat, *heatold, *vx, *vy, *vz;
unsigned char *obstacles;
float *tmp_array = MEM_callocN(out_len, "Smoke old cache tmp");
- int fluid_fields = BKE_fluid_get_data_flags(mds);
+ int fluid_fields = BKE_fluid_get_data_flags(fds);
/* Part part of the new cache header */
- mds->active_color[0] = 0.7f;
- mds->active_color[1] = 0.7f;
- mds->active_color[2] = 0.7f;
+ fds->active_color[0] = 0.7f;
+ fds->active_color[1] = 0.7f;
+ fds->active_color[2] = 0.7f;
- smoke_export(mds->fluid,
+ smoke_export(fds->fluid,
&dt,
&dx,
&dens,
@@ -779,7 +830,7 @@ static int ptcache_smoke_read_old(PTCacheFile *pf, void *smoke_v)
&obstacles,
NULL);
- ptcache_file_compressed_read(pf, (unsigned char *)mds->shadow, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)fds->shadow, out_len);
ptcache_file_compressed_read(pf, (unsigned char *)dens, out_len);
ptcache_file_compressed_read(pf, (unsigned char *)tmp_array, out_len);
@@ -803,19 +854,19 @@ static int ptcache_smoke_read_old(PTCacheFile *pf, void *smoke_v)
MEM_freeN(tmp_array);
- if (pf->data_types & (1 << BPHYS_DATA_SMOKE_HIGH) && mds->wt) {
+ if (pf->data_types & (1 << BPHYS_DATA_SMOKE_HIGH) && fds->wt) {
int res_big, res_big_array[3];
float *tcu, *tcv, *tcw;
unsigned int out_len_big;
unsigned char *tmp_array_big;
- smoke_turbulence_get_res(mds->wt, res_big_array);
+ smoke_turbulence_get_res(fds->wt, res_big_array);
res_big = res_big_array[0] * res_big_array[1] * res_big_array[2];
out_len_big = sizeof(float) * (unsigned int)res_big;
tmp_array_big = MEM_callocN(out_len_big, "Smoke old cache tmp");
smoke_turbulence_export(
- mds->wt, &dens, NULL, NULL, NULL, NULL, NULL, NULL, &tcu, &tcv, &tcw);
+ fds->wt, &dens, NULL, NULL, NULL, NULL, NULL, NULL, &tcu, &tcv, &tcw);
ptcache_file_compressed_read(pf, (unsigned char *)dens, out_len_big);
ptcache_file_compressed_read(pf, (unsigned char *)tmp_array_big, out_len_big);
@@ -833,12 +884,12 @@ static int ptcache_smoke_read_old(PTCacheFile *pf, void *smoke_v)
static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
+ FluidDomainSettings *fds = fmd->domain;
char version[4];
int ch_res[3];
float ch_dx;
- int fluid_fields = BKE_fluid_get_data_flags(mds);
+ int fluid_fields = BKE_fluid_get_data_flags(fds);
int cache_fields = 0;
int active_fields = 0;
int reallocate = 0;
@@ -858,8 +909,8 @@ static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
ptcache_file_read(pf, &ch_dx, 1, sizeof(float));
/* check if resolution has changed */
- if (mds->res[0] != ch_res[0] || mds->res[1] != ch_res[1] || mds->res[2] != ch_res[2]) {
- if (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
+ if (fds->res[0] != ch_res[0] || fds->res[1] != ch_res[1] || fds->res[2] != ch_res[2]) {
+ if (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
reallocate = 1;
}
else {
@@ -867,26 +918,26 @@ static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
}
}
/* check if active fields have changed */
- if (fluid_fields != cache_fields || active_fields != mds->active_fields) {
+ if (fluid_fields != cache_fields || active_fields != fds->active_fields) {
reallocate = 1;
}
/* reallocate fluid if needed*/
if (reallocate) {
- mds->active_fields = active_fields | cache_fields;
- BKE_fluid_reallocate_fluid(mds, ch_res, 1);
- mds->dx = ch_dx;
- copy_v3_v3_int(mds->res, ch_res);
- mds->total_cells = ch_res[0] * ch_res[1] * ch_res[2];
+ fds->active_fields = active_fields | cache_fields;
+ BKE_fluid_reallocate_fluid(fds, ch_res, 1);
+ fds->dx = ch_dx;
+ copy_v3_v3_int(fds->res, ch_res);
+ fds->total_cells = ch_res[0] * ch_res[1] * ch_res[2];
}
- if (mds->fluid) {
- size_t res = mds->res[0] * mds->res[1] * mds->res[2];
+ if (fds->fluid) {
+ size_t res = fds->res[0] * fds->res[1] * fds->res[2];
float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
unsigned char *obstacles;
unsigned int out_len = (unsigned int)res * sizeof(float);
- smoke_export(mds->fluid,
+ smoke_export(fds->fluid,
&dt,
&dx,
&dens,
@@ -904,7 +955,7 @@ static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
&obstacles,
NULL);
- ptcache_file_compressed_read(pf, (unsigned char *)mds->shadow, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)fds->shadow, out_len);
ptcache_file_compressed_read(pf, (unsigned char *)dens, out_len);
if (cache_fields & FLUID_DOMAIN_ACTIVE_HEAT) {
ptcache_file_compressed_read(pf, (unsigned char *)heat, out_len);
@@ -926,30 +977,30 @@ static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
ptcache_file_compressed_read(pf, (unsigned char *)obstacles, (unsigned int)res);
ptcache_file_read(pf, &dt, 1, sizeof(float));
ptcache_file_read(pf, &dx, 1, sizeof(float));
- ptcache_file_read(pf, &mds->p0, 3, sizeof(float));
- ptcache_file_read(pf, &mds->p1, 3, sizeof(float));
- ptcache_file_read(pf, &mds->dp0, 3, sizeof(float));
- ptcache_file_read(pf, &mds->shift, 3, sizeof(int));
- ptcache_file_read(pf, &mds->obj_shift_f, 3, sizeof(float));
- ptcache_file_read(pf, &mds->obmat, 16, sizeof(float));
- ptcache_file_read(pf, &mds->base_res, 3, sizeof(int));
- ptcache_file_read(pf, &mds->res_min, 3, sizeof(int));
- ptcache_file_read(pf, &mds->res_max, 3, sizeof(int));
- ptcache_file_read(pf, &mds->active_color, 3, sizeof(float));
- }
-
- if (pf->data_types & (1 << BPHYS_DATA_SMOKE_HIGH) && mds->wt) {
- int res = mds->res[0] * mds->res[1] * mds->res[2];
+ ptcache_file_read(pf, &fds->p0, 3, sizeof(float));
+ ptcache_file_read(pf, &fds->p1, 3, sizeof(float));
+ ptcache_file_read(pf, &fds->dp0, 3, sizeof(float));
+ ptcache_file_read(pf, &fds->shift, 3, sizeof(int));
+ ptcache_file_read(pf, &fds->obj_shift_f, 3, sizeof(float));
+ ptcache_file_read(pf, &fds->obmat, 16, sizeof(float));
+ ptcache_file_read(pf, &fds->base_res, 3, sizeof(int));
+ ptcache_file_read(pf, &fds->res_min, 3, sizeof(int));
+ ptcache_file_read(pf, &fds->res_max, 3, sizeof(int));
+ ptcache_file_read(pf, &fds->active_color, 3, sizeof(float));
+ }
+
+ if (pf->data_types & (1 << BPHYS_DATA_SMOKE_HIGH) && fds->wt) {
+ int res = fds->res[0] * fds->res[1] * fds->res[2];
int res_big, res_big_array[3];
float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
unsigned int out_len = sizeof(float) * (unsigned int)res;
unsigned int out_len_big;
- smoke_turbulence_get_res(mds->wt, res_big_array);
+ smoke_turbulence_get_res(fds->wt, res_big_array);
res_big = res_big_array[0] * res_big_array[1] * res_big_array[2];
out_len_big = sizeof(float) * (unsigned int)res_big;
- smoke_turbulence_export(mds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+ smoke_turbulence_export(fds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
ptcache_file_compressed_read(pf, (unsigned char *)dens, out_len_big);
if (cache_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
@@ -984,88 +1035,88 @@ static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
* with `vs` = voxel size, and `px, py, pz`,
* the min position of the domain's bounding box.
*/
-static void compute_fluid_matrices(FluidDomainSettings *mds)
+static void compute_fluid_matrices(FluidDomainSettings *fds)
{
float bbox_min[3];
- copy_v3_v3(bbox_min, mds->p0);
+ copy_v3_v3(bbox_min, fds->p0);
- if (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
- bbox_min[0] += (mds->cell_size[0] * (float)mds->res_min[0]);
- bbox_min[1] += (mds->cell_size[1] * (float)mds->res_min[1]);
- bbox_min[2] += (mds->cell_size[2] * (float)mds->res_min[2]);
- add_v3_v3(bbox_min, mds->obj_shift_f);
+ if (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
+ bbox_min[0] += (fds->cell_size[0] * (float)fds->res_min[0]);
+ bbox_min[1] += (fds->cell_size[1] * (float)fds->res_min[1]);
+ bbox_min[2] += (fds->cell_size[2] * (float)fds->res_min[2]);
+ add_v3_v3(bbox_min, fds->obj_shift_f);
}
/* construct low res matrix */
- size_to_mat4(mds->fluidmat, mds->cell_size);
- copy_v3_v3(mds->fluidmat[3], bbox_min);
+ size_to_mat4(fds->fluidmat, fds->cell_size);
+ copy_v3_v3(fds->fluidmat[3], bbox_min);
/* The smoke simulator stores voxels cell-centered, whilst VDB is node
* centered, so we offset the matrix by half a voxel to compensate. */
- madd_v3_v3fl(mds->fluidmat[3], mds->cell_size, 0.5f);
+ madd_v3_v3fl(fds->fluidmat[3], fds->cell_size, 0.5f);
- mul_m4_m4m4(mds->fluidmat, mds->obmat, mds->fluidmat);
+ mul_m4_m4m4(fds->fluidmat, fds->obmat, fds->fluidmat);
- if (mds->wt) {
+ if (fds->wt) {
float voxel_size_high[3];
/* construct high res matrix */
- mul_v3_v3fl(voxel_size_high, mds->cell_size, 1.0f / (float)(mds->amplify + 1));
- size_to_mat4(mds->fluidmat_wt, voxel_size_high);
- copy_v3_v3(mds->fluidmat_wt[3], bbox_min);
+ mul_v3_v3fl(voxel_size_high, fds->cell_size, 1.0f / (float)(fds->amplify + 1));
+ size_to_mat4(fds->fluidmat_wt, voxel_size_high);
+ copy_v3_v3(fds->fluidmat_wt[3], bbox_min);
/* Same here, add half a voxel to adjust the position of the fluid. */
- madd_v3_v3fl(mds->fluidmat_wt[3], voxel_size_high, 0.5f);
+ madd_v3_v3fl(fds->fluidmat_wt[3], voxel_size_high, 0.5f);
- mul_m4_m4m4(mds->fluidmat_wt, mds->obmat, mds->fluidmat_wt);
+ mul_m4_m4m4(fds->fluidmat_wt, fds->obmat, fds->fluidmat_wt);
}
}
static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke_v)
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
- FluidDomainSettings *mds = mmd->domain;
-
- OpenVDBWriter_set_flags(writer, mds->openvdb_comp, (mds->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);
- OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/min_resolution", mds->res_min);
- OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/max_resolution", mds->res_max);
- OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/base_resolution", mds->base_res);
- OpenVDBWriter_add_meta_v3(writer, "blender/smoke/min_bbox", mds->p0);
- OpenVDBWriter_add_meta_v3(writer, "blender/smoke/max_bbox", mds->p1);
- OpenVDBWriter_add_meta_v3(writer, "blender/smoke/dp0", mds->dp0);
- OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/shift", mds->shift);
- OpenVDBWriter_add_meta_v3(writer, "blender/smoke/obj_shift_f", mds->obj_shift_f);
- OpenVDBWriter_add_meta_v3(writer, "blender/smoke/active_color", mds->active_color);
- OpenVDBWriter_add_meta_mat4(writer, "blender/smoke/obmat", mds->obmat);
-
- int fluid_fields = BKE_fluid_get_data_flags(mds);
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
+ FluidDomainSettings *fds = fmd->domain;
+
+ OpenVDBWriter_set_flags(writer, fds->openvdb_compression, (fds->openvdb_data_depth == 16));
+
+ OpenVDBWriter_add_meta_int(writer, "blender/smoke/active_fields", fds->active_fields);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/resolution", fds->res);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/min_resolution", fds->res_min);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/max_resolution", fds->res_max);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/base_resolution", fds->base_res);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/min_bbox", fds->p0);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/max_bbox", fds->p1);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/dp0", fds->dp0);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/shift", fds->shift);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/obj_shift_f", fds->obj_shift_f);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/active_color", fds->active_color);
+ OpenVDBWriter_add_meta_mat4(writer, "blender/smoke/obmat", fds->obmat);
+
+ int fluid_fields = BKE_fluid_get_data_flags(fds);
struct OpenVDBFloatGrid *clip_grid = NULL;
- compute_fluid_matrices(mds);
+ compute_fluid_matrices(fds);
OpenVDBWriter_add_meta_int(writer, "blender/smoke/fluid_fields", fluid_fields);
- if (mds->wt) {
+ if (fds->wt) {
struct OpenVDBFloatGrid *wt_density_grid;
float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
- smoke_turbulence_export(mds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+ smoke_turbulence_export(fds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
wt_density_grid = OpenVDB_export_grid_fl(
- writer, "density", dens, mds->res_wt, mds->fluidmat_wt, mds->clipping, NULL);
+ writer, "density", dens, fds->res_wt, fds->fluidmat_wt, fds->clipping, NULL);
clip_grid = wt_density_grid;
if (fluid_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
OpenVDB_export_grid_fl(
- writer, "flame", flame, mds->res_wt, mds->fluidmat_wt, mds->clipping, wt_density_grid);
+ writer, "flame", flame, fds->res_wt, fds->fluidmat_wt, fds->clipping, wt_density_grid);
OpenVDB_export_grid_fl(
- writer, "fuel", fuel, mds->res_wt, mds->fluidmat_wt, mds->clipping, wt_density_grid);
+ writer, "fuel", fuel, fds->res_wt, fds->fluidmat_wt, fds->clipping, wt_density_grid);
OpenVDB_export_grid_fl(
- writer, "react", react, mds->res_wt, mds->fluidmat_wt, mds->clipping, wt_density_grid);
+ writer, "react", react, fds->res_wt, fds->fluidmat_wt, fds->clipping, wt_density_grid);
}
if (fluid_fields & FLUID_DOMAIN_ACTIVE_COLORS) {
@@ -1074,11 +1125,11 @@ static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke
r,
g,
b,
- mds->res_wt,
- mds->fluidmat_wt,
+ fds->res_wt,
+ fds->fluidmat_wt,
VEC_INVARIANT,
true,
- mds->clipping,
+ fds->clipping,
wt_density_grid);
}
@@ -1087,20 +1138,20 @@ static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke
tcu,
tcv,
tcw,
- mds->res,
- mds->fluidmat,
+ fds->res,
+ fds->fluidmat,
VEC_INVARIANT,
false,
- mds->clipping,
+ fds->clipping,
wt_density_grid);
}
- if (mds->fluid) {
+ if (fds->fluid) {
struct OpenVDBFloatGrid *density_grid;
float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
unsigned char *obstacles;
- smoke_export(mds->fluid,
+ smoke_export(fds->fluid,
&dt,
&dx,
&dens,
@@ -1121,45 +1172,45 @@ static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke
OpenVDBWriter_add_meta_fl(writer, "blender/smoke/dx", dx);
OpenVDBWriter_add_meta_fl(writer, "blender/smoke/dt", dt);
- const char *name = (!mds->wt) ? "density" : "density_low";
+ const char *name = (!fds->wt) ? "density" : "density_low";
density_grid = OpenVDB_export_grid_fl(
- writer, name, dens, mds->res, mds->fluidmat, mds->clipping, NULL);
- clip_grid = mds->wt ? clip_grid : density_grid;
+ writer, name, dens, fds->res, fds->fluidmat, fds->clipping, NULL);
+ clip_grid = fds->wt ? clip_grid : density_grid;
OpenVDB_export_grid_fl(
- writer, "shadow", mds->shadow, mds->res, mds->fluidmat, mds->clipping, NULL);
+ writer, "shadow", fds->shadow, fds->res, fds->fluidmat, fds->clipping, NULL);
if (fluid_fields & FLUID_DOMAIN_ACTIVE_HEAT) {
OpenVDB_export_grid_fl(
- writer, "heat", heat, mds->res, mds->fluidmat, mds->clipping, clip_grid);
+ writer, "heat", heat, fds->res, fds->fluidmat, fds->clipping, clip_grid);
OpenVDB_export_grid_fl(
- writer, "heat_old", heatold, mds->res, mds->fluidmat, mds->clipping, clip_grid);
+ writer, "heat_old", heatold, fds->res, fds->fluidmat, fds->clipping, clip_grid);
}
if (fluid_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
- name = (!mds->wt) ? "flame" : "flame_low";
+ name = (!fds->wt) ? "flame" : "flame_low";
OpenVDB_export_grid_fl(
- writer, name, flame, mds->res, mds->fluidmat, mds->clipping, density_grid);
- name = (!mds->wt) ? "fuel" : "fuel_low";
+ writer, name, flame, fds->res, fds->fluidmat, fds->clipping, density_grid);
+ name = (!fds->wt) ? "fuel" : "fuel_low";
OpenVDB_export_grid_fl(
- writer, name, fuel, mds->res, mds->fluidmat, mds->clipping, density_grid);
- name = (!mds->wt) ? "react" : "react_low";
+ writer, name, fuel, fds->res, fds->fluidmat, fds->clipping, density_grid);
+ name = (!fds->wt) ? "react" : "react_low";
OpenVDB_export_grid_fl(
- writer, name, react, mds->res, mds->fluidmat, mds->clipping, density_grid);
+ writer, name, react, fds->res, fds->fluidmat, fds->clipping, density_grid);
}
if (fluid_fields & FLUID_DOMAIN_ACTIVE_COLORS) {
- name = (!mds->wt) ? "color" : "color_low";
+ name = (!fds->wt) ? "color" : "color_low";
OpenVDB_export_grid_vec(writer,
name,
r,
g,
b,
- mds->res,
- mds->fluidmat,
+ fds->res,
+ fds->fluidmat,
VEC_INVARIANT,
true,
- mds->clipping,
+ fds->clipping,
density_grid);
}
@@ -1168,14 +1219,14 @@ static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke
vx,
vy,
vz,
- mds->res,
- mds->fluidmat,
+ fds->res,
+ fds->fluidmat,
VEC_CONTRAVARIANT_RELATIVE,
false,
- mds->clipping,
+ fds->clipping,
clip_grid);
OpenVDB_export_grid_ch(
- writer, "obstacles", obstacles, mds->res, mds->fluidmat, mds->clipping, NULL);
+ writer, "obstacles", obstacles, fds->res, fds->fluidmat, fds->clipping, NULL);
}
return 1;
@@ -1183,38 +1234,38 @@ static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke
static int ptcache_smoke_openvdb_read(struct OpenVDBReader *reader, void *smoke_v)
{
- FluidModifierData *mmd = (FluidModifierData *)smoke_v;
+ FluidModifierData *fmd = (FluidModifierData *)smoke_v;
- if (!mmd) {
+ if (!fmd) {
return 0;
}
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
- int fluid_fields = BKE_fluid_get_data_flags(mds);
+ int fluid_fields = BKE_fluid_get_data_flags(fds);
int active_fields, cache_fields = 0;
int cache_res[3];
float cache_dx;
bool reallocate = false;
- OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/min_resolution", mds->res_min);
- OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/max_resolution", mds->res_max);
- OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/base_resolution", mds->base_res);
- OpenVDBReader_get_meta_v3(reader, "blender/smoke/min_bbox", mds->p0);
- OpenVDBReader_get_meta_v3(reader, "blender/smoke/max_bbox", mds->p1);
- OpenVDBReader_get_meta_v3(reader, "blender/smoke/dp0", mds->dp0);
- OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/shift", mds->shift);
- OpenVDBReader_get_meta_v3(reader, "blender/smoke/obj_shift_f", mds->obj_shift_f);
- OpenVDBReader_get_meta_v3(reader, "blender/smoke/active_color", mds->active_color);
- OpenVDBReader_get_meta_mat4(reader, "blender/smoke/obmat", mds->obmat);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/min_resolution", fds->res_min);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/max_resolution", fds->res_max);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/base_resolution", fds->base_res);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/min_bbox", fds->p0);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/max_bbox", fds->p1);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/dp0", fds->dp0);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/shift", fds->shift);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/obj_shift_f", fds->obj_shift_f);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/active_color", fds->active_color);
+ OpenVDBReader_get_meta_mat4(reader, "blender/smoke/obmat", fds->obmat);
OpenVDBReader_get_meta_int(reader, "blender/smoke/fluid_fields", &cache_fields);
OpenVDBReader_get_meta_int(reader, "blender/smoke/active_fields", &active_fields);
OpenVDBReader_get_meta_fl(reader, "blender/smoke/dx", &cache_dx);
OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/resolution", cache_res);
/* check if resolution has changed */
- if (mds->res[0] != cache_res[0] || mds->res[1] != cache_res[1] || mds->res[2] != cache_res[2]) {
- if (mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
+ if (fds->res[0] != cache_res[0] || fds->res[1] != cache_res[1] || fds->res[2] != cache_res[2]) {
+ if (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) {
reallocate = true;
}
else {
@@ -1223,24 +1274,24 @@ static int ptcache_smoke_openvdb_read(struct OpenVDBReader *reader, void *smoke_
}
/* check if active fields have changed */
- if ((fluid_fields != cache_fields) || (active_fields != mds->active_fields)) {
+ if ((fluid_fields != cache_fields) || (active_fields != fds->active_fields)) {
reallocate = true;
}
/* reallocate fluid if needed*/
if (reallocate) {
- mds->active_fields = active_fields | cache_fields;
- BKE_fluid_reallocate_fluid(mds, cache_dx, cache_res, 1);
- mds->dx = cache_dx;
- copy_v3_v3_int(mds->res, cache_res);
- mds->total_cells = cache_res[0] * cache_res[1] * cache_res[2];
+ fds->active_fields = active_fields | cache_fields;
+ BKE_fluid_reallocate_fluid(fds, cache_dx, cache_res, 1);
+ fds->dx = cache_dx;
+ copy_v3_v3_int(fds->res, cache_res);
+ fds->total_cells = cache_res[0] * cache_res[1] * cache_res[2];
}
- if (mds->fluid) {
+ if (fds->fluid) {
float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
unsigned char *obstacles;
- smoke_export(mds->fluid,
+ smoke_export(fds->fluid,
&dt,
&dx,
&dens,
@@ -1260,52 +1311,52 @@ static int ptcache_smoke_openvdb_read(struct OpenVDBReader *reader, void *smoke_
OpenVDBReader_get_meta_fl(reader, "blender/smoke/dt", &dt);
- OpenVDB_import_grid_fl(reader, "shadow", &mds->shadow, mds->res);
+ OpenVDB_import_grid_fl(reader, "shadow", &fds->shadow, fds->res);
- const char *name = (!mds->wt) ? "density" : "density_low";
- OpenVDB_import_grid_fl(reader, name, &dens, mds->res);
+ const char *name = (!fds->wt) ? "density" : "density_low";
+ OpenVDB_import_grid_fl(reader, name, &dens, fds->res);
if (cache_fields & FLUID_DOMAIN_ACTIVE_HEAT) {
- OpenVDB_import_grid_fl(reader, "heat", &heat, mds->res);
- OpenVDB_import_grid_fl(reader, "heat_old", &heatold, mds->res);
+ OpenVDB_import_grid_fl(reader, "heat", &heat, fds->res);
+ OpenVDB_import_grid_fl(reader, "heat_old", &heatold, fds->res);
}
if (cache_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
- name = (!mds->wt) ? "flame" : "flame_low";
- OpenVDB_import_grid_fl(reader, name, &flame, mds->res);
- name = (!mds->wt) ? "fuel" : "fuel_low";
- OpenVDB_import_grid_fl(reader, name, &fuel, mds->res);
- name = (!mds->wt) ? "react" : "react_low";
- OpenVDB_import_grid_fl(reader, name, &react, mds->res);
+ name = (!fds->wt) ? "flame" : "flame_low";
+ OpenVDB_import_grid_fl(reader, name, &flame, fds->res);
+ name = (!fds->wt) ? "fuel" : "fuel_low";
+ OpenVDB_import_grid_fl(reader, name, &fuel, fds->res);
+ name = (!fds->wt) ? "react" : "react_low";
+ OpenVDB_import_grid_fl(reader, name, &react, fds->res);
}
if (cache_fields & FLUID_DOMAIN_ACTIVE_COLORS) {
- name = (!mds->wt) ? "color" : "color_low";
- OpenVDB_import_grid_vec(reader, name, &r, &g, &b, mds->res);
+ name = (!fds->wt) ? "color" : "color_low";
+ OpenVDB_import_grid_vec(reader, name, &r, &g, &b, fds->res);
}
- OpenVDB_import_grid_vec(reader, "velocity", &vx, &vy, &vz, mds->res);
- OpenVDB_import_grid_ch(reader, "obstacles", &obstacles, mds->res);
+ OpenVDB_import_grid_vec(reader, "velocity", &vx, &vy, &vz, fds->res);
+ OpenVDB_import_grid_ch(reader, "obstacles", &obstacles, fds->res);
}
- if (mds->wt) {
+ if (fds->wt) {
float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
- smoke_turbulence_export(mds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+ smoke_turbulence_export(fds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
- OpenVDB_import_grid_fl(reader, "density", &dens, mds->res_wt);
+ OpenVDB_import_grid_fl(reader, "density", &dens, fds->res_wt);
if (cache_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
- OpenVDB_import_grid_fl(reader, "flame", &flame, mds->res_wt);
- OpenVDB_import_grid_fl(reader, "fuel", &fuel, mds->res_wt);
- OpenVDB_import_grid_fl(reader, "react", &react, mds->res_wt);
+ OpenVDB_import_grid_fl(reader, "flame", &flame, fds->res_wt);
+ OpenVDB_import_grid_fl(reader, "fuel", &fuel, fds->res_wt);
+ OpenVDB_import_grid_fl(reader, "react", &react, fds->res_wt);
}
if (cache_fields & FLUID_DOMAIN_ACTIVE_COLORS) {
- OpenVDB_import_grid_vec(reader, "color", &r, &g, &b, mds->res_wt);
+ OpenVDB_import_grid_vec(reader, "color", &r, &g, &b, fds->res_wt);
}
- OpenVDB_import_grid_vec(reader, "texture coordinates", &tcu, &tcv, &tcw, mds->res);
+ OpenVDB_import_grid_vec(reader, "texture coordinates", &tcu, &tcv, &tcw, fds->res);
}
OpenVDBReader_free(reader);
@@ -1460,7 +1511,7 @@ static int ptcache_rigidbody_write(int index, void *rb_v, void **data, int UNUSE
if (ob && ob->rigidbody_object) {
RigidBodyOb *rbo = ob->rigidbody_object;
- if (rbo->type == RBO_TYPE_ACTIVE) {
+ if (rbo->type == RBO_TYPE_ACTIVE && rbo->shared->physics_object != NULL) {
#ifdef WITH_BULLET
RB_body_get_position(rbo->shared->physics_object, rbo->pos);
RB_body_get_orientation(rbo->shared->physics_object, rbo->orn);
@@ -1473,7 +1524,7 @@ static int ptcache_rigidbody_write(int index, void *rb_v, void **data, int UNUSE
return 1;
}
static void ptcache_rigidbody_read(
- int index, void *rb_v, void **data, float UNUSED(cfra), float *old_data)
+ int index, void *rb_v, void **data, float UNUSED(cfra), const float *old_data)
{
RigidBodyWorld *rbw = rb_v;
Object *ob = NULL;
@@ -1498,8 +1549,13 @@ static void ptcache_rigidbody_read(
}
}
}
-static void ptcache_rigidbody_interpolate(
- int index, void *rb_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+static void ptcache_rigidbody_interpolate(int index,
+ void *rb_v,
+ void **data,
+ float cfra,
+ float cfra1,
+ float cfra2,
+ const float *old_data)
{
RigidBodyWorld *rbw = rb_v;
Object *ob = NULL;
@@ -1557,7 +1613,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;
@@ -1596,7 +1652,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;
@@ -1661,7 +1717,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;
@@ -1681,8 +1737,8 @@ void BKE_ptcache_id_from_cloth(PTCacheID *pid, Object *ob, ClothModifierData *cl
pid->write_stream = NULL;
pid->read_stream = NULL;
- pid->write_extra_data = NULL;
- pid->read_extra_data = NULL;
+ pid->write_extra_data = ptcache_cloth_extra_write;
+ pid->read_extra_data = ptcache_cloth_extra_read;
pid->interpolate_extra_data = NULL;
pid->write_header = ptcache_basic_header_write;
@@ -1696,21 +1752,21 @@ void BKE_ptcache_id_from_cloth(PTCacheID *pid, Object *ob, ClothModifierData *cl
pid->max_step = 1;
pid->file_type = PTCACHE_FILE_PTCACHE;
}
-void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidModifierData *mmd)
+void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidModifierData *fmd)
{
- FluidDomainSettings *mds = mmd->domain;
+ FluidDomainSettings *fds = fmd->domain;
memset(pid, 0, sizeof(PTCacheID));
- pid->ob = ob;
- pid->calldata = mmd;
+ pid->owner_id = &ob->id;
+ pid->calldata = fmd;
pid->type = PTCACHE_TYPE_SMOKE_DOMAIN;
- pid->stack_index = mds->point_cache[0]->index;
+ pid->stack_index = fds->point_cache[0]->index;
- pid->cache = mds->point_cache[0];
- pid->cache_ptr = &(mds->point_cache[0]);
- pid->ptcaches = &(mds->ptcaches[0]);
+ pid->cache = fds->point_cache[0];
+ pid->cache_ptr = &(fds->point_cache[0]);
+ pid->ptcaches = &(fds->ptcaches[0]);
pid->totpoint = pid->totwrite = ptcache_smoke_totpoint;
pid->error = ptcache_smoke_error;
@@ -1735,16 +1791,16 @@ void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidMo
pid->data_types = 0;
pid->info_types = 0;
- if (mds->fluid) {
+ if (fds->fluid) {
pid->data_types |= (1 << BPHYS_DATA_SMOKE_LOW);
- if (mds->flags & FLUID_DOMAIN_USE_NOISE) {
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE) {
pid->data_types |= (1 << BPHYS_DATA_SMOKE_HIGH);
}
}
pid->default_step = 1;
pid->max_step = 1;
- pid->file_type = mmd->domain->cache_file_format;
+ pid->file_type = fmd->domain->cache_file_format;
}
void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, Object *ob, DynamicPaintSurface *surface)
@@ -1752,7 +1808,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;
@@ -1793,7 +1849,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;
@@ -1829,87 +1885,6 @@ void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, Object *ob, RigidBodyWorld *r
pid->file_type = PTCACHE_FILE_PTCACHE;
}
-static int ptcache_sim_particle_totpoint(void *state_v, int UNUSED(cfra))
-{
- ParticleSimulationState *state = (ParticleSimulationState *)state_v;
- return state->tot_particles;
-}
-
-static void ptcache_sim_particle_error(void *UNUSED(state_v), const char *UNUSED(message))
-{
-}
-
-static int ptcache_sim_particle_write(int index, void *state_v, void **data, int UNUSED(cfra))
-{
- ParticleSimulationState *state = (ParticleSimulationState *)state_v;
-
- const float *positions = (const float *)CustomData_get_layer_named(
- &state->attributes, CD_LOCATION, "Position");
-
- PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, positions + (index * 3));
-
- return 1;
-}
-static void ptcache_sim_particle_read(
- int index, void *state_v, void **data, float UNUSED(cfra), float *UNUSED(old_data))
-{
- ParticleSimulationState *state = (ParticleSimulationState *)state_v;
-
- BLI_assert(index < state->tot_particles);
- float *positions = (float *)CustomData_get_layer_named(
- &state->attributes, CD_LOCATION, "Position");
-
- PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, positions + (index * 3));
-}
-
-void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, ParticleSimulationState *state)
-{
- memset(pid, 0, sizeof(PTCacheID));
-
- ParticleSimulationState *state_orig;
- if (state->head.orig_state != NULL) {
- state_orig = (ParticleSimulationState *)state->head.orig_state;
- }
- else {
- state_orig = state;
- }
-
- pid->calldata = state;
- pid->type = PTCACHE_TYPE_SIM_PARTICLES;
- pid->cache = state_orig->point_cache;
- pid->cache_ptr = &state_orig->point_cache;
- pid->ptcaches = &state_orig->ptcaches;
- pid->totpoint = ptcache_sim_particle_totpoint;
- pid->totwrite = ptcache_sim_particle_totpoint;
- pid->error = ptcache_sim_particle_error;
-
- pid->write_point = ptcache_sim_particle_write;
- pid->read_point = ptcache_sim_particle_read;
- pid->interpolate_point = NULL;
-
- pid->write_stream = NULL;
- pid->read_stream = NULL;
-
- pid->write_openvdb_stream = NULL;
- pid->read_openvdb_stream = NULL;
-
- pid->write_extra_data = NULL;
- pid->read_extra_data = NULL;
- pid->interpolate_extra_data = NULL;
-
- pid->write_header = NULL;
- pid->read_header = NULL;
-
- pid->data_types = 1 << BPHYS_DATA_LOCATION;
- pid->info_types = 0;
-
- pid->stack_index = 0;
-
- pid->default_step = 1;
- pid->max_step = 1;
- pid->file_type = PTCACHE_FILE_PTCACHE;
-}
-
/**
* \param ob: Optional, may be NULL.
* \param scene: Optional may be NULL.
@@ -1988,8 +1963,8 @@ static bool foreach_object_modifier_ptcache(Object *object,
}
}
else if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
BKE_ptcache_id_from_smoke(&pid, object, (FluidModifierData *)md);
if (!callback(&pid, callback_user_data)) {
return false;
@@ -2009,21 +1984,7 @@ static bool foreach_object_modifier_ptcache(Object *object,
}
}
else if (md->type == eModifierType_Simulation) {
- SimulationModifierData *smd = (SimulationModifierData *)md;
- if (smd->simulation) {
- LISTBASE_FOREACH (SimulationState *, state, &smd->simulation->states) {
- switch ((eSimulationStateType)state->type) {
- case SIM_STATE_TYPE_PARTICLES: {
- ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
- BKE_ptcache_id_from_sim_particles(&pid, particle_state);
- if (!callback(&pid, callback_user_data)) {
- return false;
- }
- break;
- }
- }
- }
- }
+ /* TODO(jacques) */
}
}
return true;
@@ -2152,9 +2113,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;
@@ -2210,7 +2171,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++));
@@ -2227,7 +2188,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);
@@ -2252,7 +2214,9 @@ static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_p
return len; /* make sure the above string is always 16 chars */
}
-/* youll need to close yourself after! */
+/**
+ * Caller must close after!
+ */
static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra)
{
PTCacheFile *pf;
@@ -2261,7 +2225,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
@@ -3472,7 +3436,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
@@ -3652,7 +3616,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) {
@@ -3837,8 +3800,8 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode)
reset |= BKE_ptcache_id_reset(scene, &pid, mode);
}
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
BKE_ptcache_id_from_smoke(&pid, ob, (FluidModifierData *)md);
reset |= BKE_ptcache_id_reset(scene, &pid, mode);
}
@@ -4083,7 +4046,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) {
@@ -4100,7 +4063,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)) {
@@ -4389,7 +4353,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..7c335a8e98c 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -466,10 +466,10 @@ static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob)
return shape;
}
-/* Create new physics sim collision shape for object and store it,
- * or remove the existing one first and replace...
+/* Helper function to create physics collision shape for object.
+ * Returns a new collision shape.
*/
-static void rigidbody_validate_sim_shape(Object *ob, bool rebuild)
+static rbCollisionShape *rigidbody_validate_sim_shape_helper(RigidBodyWorld *rbw, Object *ob)
{
RigidBodyOb *rbo = ob->rigidbody_object;
rbCollisionShape *new_shape = NULL;
@@ -484,12 +484,7 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild)
/* sanity check */
if (rbo == NULL) {
- return;
- }
-
- /* don't create a new shape if we already have one and don't want to rebuild it */
- if (rbo->shared->physics_shape && !rebuild) {
- return;
+ return NULL;
}
/* if automatically determining dimensions, use the Object's boundbox
@@ -539,7 +534,7 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild)
break;
case RB_SHAPE_CONVEXH:
- /* try to emged collision margin */
+ /* try to embed collision margin */
has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f);
if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) {
@@ -555,18 +550,69 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild)
case RB_SHAPE_TRIMESH:
new_shape = rigidbody_get_shape_trimesh_from_mesh(ob);
break;
+ case RB_SHAPE_COMPOUND:
+ new_shape = RB_shape_new_compound();
+ rbCollisionShape *childShape = NULL;
+ float loc[3], rot[4];
+ float mat[4][4];
+ /* Add children to the compound shape */
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, childObject) {
+ if (childObject->parent == ob) {
+ childShape = rigidbody_validate_sim_shape_helper(rbw, childObject);
+ if (childShape) {
+ BKE_object_matrix_local_get(childObject, mat);
+ mat4_to_loc_quat(loc, rot, mat);
+ RB_compound_add_child_shape(new_shape, childShape, loc, rot);
+ }
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+
+ break;
}
- /* use box shape if we can't fall back to old shape */
- if (new_shape == NULL && rbo->shared->physics_shape == NULL) {
+ /* use box shape if it failed to create new shape */
+ if (new_shape == NULL) {
new_shape = RB_shape_new_box(size[0], size[1], size[2]);
}
+ if (new_shape) {
+ RB_shape_set_margin(new_shape, RBO_GET_MARGIN(rbo));
+ }
+
+ return new_shape;
+}
+
+/* Create new physics sim collision shape for object and store it,
+ * or remove the existing one first and replace...
+ */
+static void rigidbody_validate_sim_shape(RigidBodyWorld *rbw, Object *ob, bool rebuild)
+{
+ RigidBodyOb *rbo = ob->rigidbody_object;
+ rbCollisionShape *new_shape = NULL;
+
+ /* sanity check */
+ if (rbo == NULL) {
+ return;
+ }
+
+ /* don't create a new shape if we already have one and don't want to rebuild it */
+ if (rbo->shared->physics_shape && !rebuild) {
+ return;
+ }
+
+ /* Also don't create a shape if this object is parent of a compound shape */
+ if (ob->parent != NULL && ob->parent->rigidbody_object != NULL &&
+ ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) {
+ return;
+ }
+
+ new_shape = rigidbody_validate_sim_shape_helper(rbw, ob);
+
/* assign new collision shape if creation was successful */
if (new_shape) {
if (rbo->shared->physics_shape) {
RB_shape_delete(rbo->shared->physics_shape);
}
rbo->shared->physics_shape = new_shape;
- RB_shape_set_margin(rbo->shared->physics_shape, RBO_GET_MARGIN(rbo));
}
}
@@ -750,7 +796,7 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool
/* FIXME we shouldn't always have to rebuild collision shapes when rebuilding objects,
* but it's needed for constraints to update correctly. */
if (rbo->shared->physics_shape == NULL || rebuild) {
- rigidbody_validate_sim_shape(ob, true);
+ rigidbody_validate_sim_shape(rbw, ob, true);
}
if (rbo->shared->physics_object) {
@@ -760,6 +806,12 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool
/* remove rigid body if it already exists before creating a new one */
if (rbo->shared->physics_object) {
RB_body_delete(rbo->shared->physics_object);
+ rbo->shared->physics_object = NULL;
+ }
+ /* Don't create rigid body object if the parent is a compound shape */
+ if (ob->parent != NULL && ob->parent->rigidbody_object != NULL &&
+ ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) {
+ return;
}
mat4_to_loc_quat(loc, rot, ob->obmat);
@@ -793,7 +845,7 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool
rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED);
}
- if (rbw && rbw->shared->physics_world) {
+ if (rbw && rbw->shared->physics_world && rbo->shared->physics_object) {
RB_dworld_add_body(rbw->shared->physics_world, rbo->shared->physics_object, rbo->col_groups);
}
}
@@ -1179,9 +1231,12 @@ RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type)
* - object must exist
* - cannot add rigid body if it already exists
*/
- if (ob == NULL || (ob->rigidbody_object != NULL)) {
+ if (ob == NULL) {
return NULL;
}
+ if (ob->rigidbody_object != NULL) {
+ return ob->rigidbody_object;
+ }
/* create new settings data, and link it up */
rbo = MEM_callocN(sizeof(RigidBodyOb), "RigidBodyOb");
@@ -1348,9 +1403,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)
{
@@ -1529,7 +1585,11 @@ static void rigidbody_update_ob_array(RigidBodyWorld *rbw)
int n = 0;
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) {
(void)object;
- n++;
+ /* Ignore if this object is the direct child of an object with a compound shape */
+ if (object->parent == NULL || object->parent->rigidbody_object == NULL ||
+ object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) {
+ n++;
+ }
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
@@ -1540,8 +1600,12 @@ static void rigidbody_update_ob_array(RigidBodyWorld *rbw)
int i = 0;
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) {
- rbw->objects[i] = object;
- i++;
+ /* Ignore if this object is the direct child of an object with a compound shape */
+ if (object->parent == NULL || object->parent->rigidbody_object == NULL ||
+ object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) {
+ rbw->objects[i] = object;
+ i++;
+ }
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
@@ -1646,7 +1710,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],
@@ -1753,11 +1817,13 @@ static void rigidbody_update_simulation(Depsgraph *depsgraph,
/* refresh shape... */
if (rbo->flag & RBO_FLAG_NEEDS_RESHAPE) {
/* mesh/shape data changed, so force shape refresh */
- rigidbody_validate_sim_shape(ob, true);
+ rigidbody_validate_sim_shape(rbw, ob, true);
/* now tell RB sim about it */
/* XXX: we assume that this can only get applied for active/passive shapes
* that will be included as rigidbodies. */
- RB_body_set_collision_shape(rbo->shared->physics_object, rbo->shared->physics_shape);
+ if (rbo->shared->physics_object != NULL && rbo->shared->physics_shape != NULL) {
+ RB_body_set_collision_shape(rbo->shared->physics_object, rbo->shared->physics_shape);
+ }
}
}
rbo->flag &= ~(RBO_FLAG_NEEDS_VALIDATE | RBO_FLAG_NEEDS_RESHAPE);
@@ -1816,7 +1882,8 @@ static void rigidbody_update_simulation_post_step(Depsgraph *depsgraph, RigidBod
Base *base = BKE_view_layer_base_find(view_layer, ob);
RigidBodyOb *rbo = ob->rigidbody_object;
/* Reset kinematic state for transformed objects. */
- if (rbo && base && (base->flag & BASE_SELECTED) && (G.moving & G_TRANSFORM_OBJ)) {
+ if (rbo && base && (base->flag & BASE_SELECTED) && (G.moving & G_TRANSFORM_OBJ) &&
+ rbo->shared->physics_object) {
RB_body_set_kinematic_state(rbo->shared->physics_object,
rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED);
RB_body_set_mass(rbo->shared->physics_object, RBO_GET_MASS(rbo));
@@ -1839,8 +1906,13 @@ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime)
{
RigidBodyOb *rbo = ob->rigidbody_object;
+ /* True if the shape of this object's parent is of type compound */
+ bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL &&
+ ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND);
+
/* keep original transform for kinematic and passive objects */
- if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE) {
+ if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE ||
+ obCompoundParent) {
return;
}
@@ -1962,7 +2034,11 @@ void BKE_rigidbody_rebuild_world(Depsgraph *depsgraph, Scene *scene, float ctime
int n = 0;
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) {
(void)object;
- n++;
+ /* Ignore if this object is the direct child of an object with a compound shape */
+ if (object->parent == NULL || object->parent->rigidbody_object == NULL ||
+ object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) {
+ n++;
+ }
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 7cf424f53e0..5ae2f4b9005 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -570,6 +570,24 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
+static void scene_foreach_cache(ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ Scene *scene = (Scene *)id;
+ IDCacheKey key = {
+ .id_session_uuid = id->session_uuid,
+ .offset_in_ID = offsetof(Scene, eevee.light_cache_data),
+ .cache_v = scene->eevee.light_cache_data,
+ };
+
+ function_callback(id,
+ &key,
+ (void **)&scene->eevee.light_cache_data,
+ IDTYPE_CACHE_CB_FLAGS_PERSISTENT,
+ user_data);
+}
+
IDTypeInfo IDType_ID_SCE = {
.id_code = ID_SCE,
.id_filter = FILTER_ID_SCE,
@@ -587,6 +605,7 @@ IDTypeInfo IDType_ID_SCE = {
* support all possible corner cases. */
.make_local = NULL,
.foreach_id = scene_foreach_id,
+ .foreach_cache = scene_foreach_cache,
};
const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE";
@@ -751,7 +770,6 @@ void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src)
Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
{
- const bool is_scene_liboverride = ID_IS_OVERRIDE_LIBRARY(sce);
Scene *sce_copy;
/* TODO this should/could most likely be replaced by call to more generic code at some point...
@@ -822,48 +840,72 @@ 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);
+ 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);
+ BKE_animdata_duplicate_id_action(bmain, &sce_copy->id, duplicate_flags);
+
/* 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);
+ /* In case root duplicated ID is linked, assume we want to get a local copy of it and
+ * duplicate all expected linked data. */
+ if (ID_IS_LINKED(sce)) {
+ duplicate_flags |= USER_DUP_LINKED_ID;
+ }
+ }
+
/* 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, (ID *)lineset->linestyle, 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, (ID *)sce->world, 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, (ID *)sce->gpd, 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 +2313,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..4a2ad88bb28 100644
--- a/source/blender/blenkernel/intern/seqeffects.c
+++ b/source/blender/blenkernel/intern/seqeffects.c
@@ -2668,7 +2668,7 @@ static void RVAddBitmaps_float(float *a, float *b, float *c, int width, int heig
}
static void RVIsolateHighlights_float(
- float *in, float *out, int width, int height, float threshold, float boost, float clamp)
+ const float *in, float *out, int width, int height, float threshold, float boost, float clamp)
{
int x, y, index;
float intensity;
@@ -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);
}
@@ -3423,7 +3423,7 @@ static void do_gaussian_blur_effect_byte_x(Sequence *seq,
int y,
int frame_width,
int UNUSED(frame_height),
- unsigned char *rect,
+ const unsigned char *rect,
unsigned char *out)
{
#define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4)
@@ -3473,7 +3473,7 @@ static void do_gaussian_blur_effect_byte_y(Sequence *seq,
int y,
int UNUSED(frame_width),
int frame_height,
- unsigned char *rect,
+ const unsigned char *rect,
unsigned char *out)
{
#define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4)
@@ -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 80ad8561333..a630170d6d5 100644
--- a/source/blender/blenkernel/intern/seqmodifier.c
+++ b/source/blender/blenkernel/intern/seqmodifier.c
@@ -48,14 +48,16 @@
static SequenceModifierTypeInfo *modifiersTypes[NUM_SEQUENCE_MODIFIER_TYPES];
static bool modifierTypesInit = false;
-/*********************** Modifiers *************************/
+/* -------------------------------------------------------------------- */
+/** \name Modifier Multi-Threading Utilities
+ * \{ */
typedef void (*modifier_apply_threaded_cb)(int width,
int height,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v);
typedef struct ModifierInitData {
@@ -163,7 +165,11 @@ static void modifier_apply_threaded(ImBuf *ibuf,
ibuf->y, sizeof(ModifierThread), &init_data, modifier_init_handle, modifier_do_thread);
}
-/* **** Color Balance Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Color Balance Modifier
+ * \{ */
static void colorBalance_init_data(SequenceModifierData *smd)
{
@@ -196,7 +202,11 @@ static SequenceModifierTypeInfo seqModifier_ColorBalance = {
colorBalance_apply, /* apply */
};
-/* **** White Balance Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name White Balance Modifier
+ * \{ */
static void whiteBalance_init_data(SequenceModifierData *smd)
{
@@ -213,7 +223,7 @@ static void whiteBalance_apply_threaded(int width,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v)
{
int x, y;
@@ -288,7 +298,11 @@ static SequenceModifierTypeInfo seqModifier_WhiteBalance = {
whiteBalance_apply, /* apply */
};
-/* **** Curves Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Curves Modifier
+ * \{ */
static void curves_init_data(SequenceModifierData *smd)
{
@@ -317,7 +331,7 @@ static void curves_apply_threaded(int width,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v)
{
CurveMapping *curve_mapping = (CurveMapping *)data_v;
@@ -402,7 +416,11 @@ static SequenceModifierTypeInfo seqModifier_Curves = {
curves_apply, /* apply */
};
-/* **** Hue Correct Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hue Correct Modifier
+ * \{ */
static void hue_correct_init_data(SequenceModifierData *smd)
{
@@ -443,7 +461,7 @@ static void hue_correct_apply_threaded(int width,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v)
{
CurveMapping *curve_mapping = (CurveMapping *)data_v;
@@ -522,7 +540,11 @@ static SequenceModifierTypeInfo seqModifier_HueCorrect = {
hue_correct_apply, /* apply */
};
-/* **** Bright/Contrast Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Bright/Contrast Modifier
+ * \{ */
typedef struct BrightContrastThreadData {
float bright;
@@ -534,7 +556,7 @@ static void brightcontrast_apply_threaded(int width,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v)
{
BrightContrastThreadData *data = (BrightContrastThreadData *)data_v;
@@ -625,14 +647,18 @@ static SequenceModifierTypeInfo seqModifier_BrightContrast = {
brightcontrast_apply, /* apply */
};
-/* **** Mask Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mask Modifier
+ * \{ */
static void maskmodifier_apply_threaded(int width,
int height,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *UNUSED(data_v))
{
int x, y;
@@ -694,7 +720,11 @@ static SequenceModifierTypeInfo seqModifier_Mask = {
maskmodifier_apply, /* apply */
};
-/* **** Tonemap Modifier **** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Tonemap Modifier
+ * \{ */
typedef struct AvgLogLum {
SequencerTonemapModifierData *tmmd;
@@ -725,7 +755,7 @@ static void tonemapmodifier_apply_threaded_simple(int width,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v)
{
AvgLogLum *avg = (AvgLogLum *)data_v;
@@ -784,7 +814,7 @@ static void tonemapmodifier_apply_threaded_photoreceptor(int width,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
- float *mask_rect_float,
+ const float *mask_rect_float,
void *data_v)
{
AvgLogLum *avg = (AvgLogLum *)data_v;
@@ -906,7 +936,11 @@ static SequenceModifierTypeInfo seqModifier_Tonemap = {
tonemapmodifier_apply, /* apply */
};
-/*********************** Modifier functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Modifier Functions
+ * \{ */
static void sequence_modifier_type_info_init(void)
{
@@ -1092,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/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c
index 30a371b5b28..ff3829bdebb 100644
--- a/source/blender/blenkernel/intern/seqprefetch.c
+++ b/source/blender/blenkernel/intern/seqprefetch.c
@@ -183,6 +183,10 @@ static float seq_prefetch_cfra(PrefetchJob *pfjob)
{
return pfjob->cfra + pfjob->num_frames_prefetched;
}
+static AnimationEvalContext seq_prefetch_anim_eval_context(PrefetchJob *pfjob)
+{
+ return BKE_animsys_eval_context_construct(pfjob->depsgraph, seq_prefetch_cfra(pfjob));
+}
void BKE_sequencer_prefetch_get_time_range(Scene *scene, int *start, int *end)
{
@@ -435,8 +439,9 @@ static void *seq_prefetch_frames(void *job)
seq_prefetch_update_depsgraph(pfjob);
AnimData *adt = BKE_animdata_from_id(&pfjob->context_cpy.scene->id);
+ AnimationEvalContext anim_eval_context = seq_prefetch_anim_eval_context(pfjob);
BKE_animsys_evaluate_animdata(
- &pfjob->context_cpy.scene->id, adt, seq_prefetch_cfra(pfjob), ADT_RECALC_ALL, false);
+ &pfjob->context_cpy.scene->id, adt, &anim_eval_context, ADT_RECALC_ALL, false);
/* This is quite hacky solution:
* We need cross-reference original scene with copy for cache.
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 90edebfaa97..b16fc08a4af 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -114,8 +114,7 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context,
float cfra,
clock_t begin,
bool use_preprocess,
- const bool is_proxy_image,
- const bool is_preprocessed);
+ const bool is_proxy_image);
static ImBuf *seq_render_strip(const SeqRenderData *context,
SeqRenderState *state,
Sequence *seq,
@@ -406,17 +405,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 +842,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 +911,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);
}
@@ -1097,6 +1090,64 @@ void BKE_sequence_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, cons
BKE_sequence_calc(scene, seq);
}
+void BKE_sequence_movie_reload_if_needed(struct Main *bmain,
+ struct Scene *scene,
+ struct Sequence *seq,
+ bool *r_was_reloaded,
+ bool *r_can_produce_frames)
+{
+ BLI_assert(seq->type == SEQ_TYPE_MOVIE ||
+ !"This function is only implemented for movie strips.");
+
+ bool must_reload = false;
+
+ /* The Sequence struct allows for multiple anim structs to be associated with one strip. This
+ * function will return true only if there is at least one 'anim' AND all anims can produce
+ * frames. */
+
+ if (BLI_listbase_is_empty(&seq->anims)) {
+ /* No anim present, so reloading is always necessary. */
+ must_reload = true;
+ }
+ else {
+ LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) {
+ if (!IMB_anim_can_produce_frames(sanim->anim)) {
+ /* Anim cannot produce frames, try reloading. */
+ must_reload = true;
+ break;
+ }
+ };
+ }
+
+ if (!must_reload) {
+ /* There are one or more anims, and all can produce frames. */
+ *r_was_reloaded = false;
+ *r_can_produce_frames = true;
+ return;
+ }
+
+ BKE_sequence_reload_new_file(bmain, scene, seq, true);
+ *r_was_reloaded = true;
+
+ if (BLI_listbase_is_empty(&seq->anims)) {
+ /* No anims present after reloading => no frames can be produced. */
+ *r_can_produce_frames = false;
+ return;
+ }
+
+ /* Check if there are still anims that cannot produce frames. */
+ LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) {
+ if (!IMB_anim_can_produce_frames(sanim->anim)) {
+ /* There still is an anim that cannot produce frames. */
+ *r_can_produce_frames = false;
+ return;
+ }
+ };
+
+ /* There are one or more anims, and all can produce frames. */
+ *r_can_produce_frames = true;
+}
+
void BKE_sequencer_sort(Scene *scene)
{
/* all strips together per kind, and in order of y location ("machine") */
@@ -1188,7 +1239,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",
@@ -1338,30 +1389,6 @@ ListBase *BKE_sequence_seqbase_get(Sequence *seq, int *r_offset)
/*********************** DO THE SEQUENCE *************************/
-static void make_black_ibuf(ImBuf *ibuf)
-{
- unsigned int *rect;
- float *rect_float;
- int tot;
-
- if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) {
- return;
- }
-
- tot = ibuf->x * ibuf->y;
-
- rect = ibuf->rect;
- rect_float = ibuf->rect_float;
-
- if (rect) {
- memset(rect, 0, tot * sizeof(char) * 4);
- }
-
- if (rect_float) {
- memset(rect_float, 0, tot * sizeof(float) * 4);
- }
-}
-
static void multibuf(ImBuf *ibuf, const float fmul)
{
char *rt;
@@ -1783,6 +1810,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 +1844,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 +2337,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)
@@ -2451,7 +2448,7 @@ static void color_balance_byte_float(StripColorBalance *cb_,
static void color_balance_float_float(StripColorBalance *cb_,
float *rect_float,
- float *mask_rect_float,
+ const float *mask_rect_float,
int width,
int height,
float mul)
@@ -2693,8 +2690,7 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
Sequence *seq,
float cfra,
ImBuf *ibuf,
- const bool is_proxy_image,
- const bool is_preprocessed)
+ const bool is_proxy_image)
{
Scene *scene = context->scene;
float mul;
@@ -2708,15 +2704,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
if (seq->flag & (SEQ_USE_CROP | SEQ_USE_TRANSFORM)) {
StripCrop c = {0};
StripTransform t = {0};
- int sx, sy, dx, dy;
-
- if (is_proxy_image) {
- double f = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size);
-
- if (f != 1.0) {
- IMB_scalefastImBuf(ibuf, ibuf->x * f, ibuf->y * f);
- }
- }
if (seq->flag & SEQ_USE_CROP && seq->strip->crop) {
c = *seq->strip->crop;
@@ -2725,33 +2712,41 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
t = *seq->strip->transform;
}
- if (is_preprocessed) {
- double xscale = scene->r.xsch ? ((double)context->rectx / (double)scene->r.xsch) : 1.0;
- double yscale = scene->r.ysch ? ((double)context->recty / (double)scene->r.ysch) : 1.0;
- if (seq->flag & SEQ_USE_TRANSFORM) {
- t.xofs *= xscale;
- t.yofs *= yscale;
+ /* Calculate scale factor for current image if needed. */
+ double scale_factor, image_scale_factor = 1.0;
+ if (context->preview_render_size == SEQ_PROXY_RENDER_SIZE_SCENE) {
+ scale_factor = image_scale_factor = (double)scene->r.size / 100;
+ }
+ else {
+ scale_factor = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size);
+ if (!is_proxy_image) {
+ image_scale_factor = scale_factor;
}
- if (seq->flag & SEQ_USE_CROP) {
- c.left *= xscale;
- c.right *= xscale;
- c.top *= yscale;
- c.bottom *= yscale;
+ }
+
+ if (image_scale_factor != 1.0) {
+ if (context->for_render) {
+ IMB_scaleImBuf(ibuf, ibuf->x * image_scale_factor, ibuf->y * image_scale_factor);
+ }
+ else {
+ IMB_scalefastImBuf(ibuf, ibuf->x * image_scale_factor, ibuf->y * image_scale_factor);
}
}
+ t.xofs *= scale_factor;
+ t.yofs *= scale_factor;
+ c.left *= scale_factor;
+ c.right *= scale_factor;
+ c.top *= scale_factor;
+ c.bottom *= scale_factor;
+
+ int sx, sy, dx, dy;
sx = ibuf->x - c.left - c.right;
sy = ibuf->y - c.top - c.bottom;
if (seq->flag & SEQ_USE_TRANSFORM) {
- if (is_preprocessed) {
- dx = context->rectx;
- dy = context->recty;
- }
- else {
- dx = scene->r.xsch;
- dy = scene->r.ysch;
- }
+ dx = context->rectx;
+ dy = context->recty;
}
else {
dx = sx;
@@ -2760,19 +2755,15 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
if (c.top + c.bottom >= ibuf->y || c.left + c.right >= ibuf->x || t.xofs >= dx ||
t.yofs >= dy) {
- make_black_ibuf(ibuf);
+ return NULL;
}
- else {
- ImBuf *i = IMB_allocImBuf(dx, dy, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
- IMB_rectcpy(i, ibuf, t.xofs, t.yofs, c.left, c.bottom, sx, sy);
- sequencer_imbuf_assign_spaces(scene, i);
-
- IMB_metadata_copy(i, ibuf);
- IMB_freeImBuf(ibuf);
-
- ibuf = i;
- }
+ ImBuf *i = IMB_allocImBuf(dx, dy, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
+ IMB_rectcpy(i, ibuf, t.xofs, t.yofs, c.left, c.bottom, sx, sy);
+ sequencer_imbuf_assign_spaces(scene, i);
+ IMB_metadata_copy(i, ibuf);
+ IMB_freeImBuf(ibuf);
+ ibuf = i;
}
if (seq->flag & SEQ_FLIPX) {
@@ -2977,9 +2968,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 +2979,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 +3012,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);
}
}
- /* 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 +3141,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);
+ }
- totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
+ 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;
+
+ 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);
+ &localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, 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 +3258,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 +3301,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 +3324,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;
@@ -3344,7 +3361,9 @@ static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr
/* anim-data */
adt = BKE_animdata_from_id(&mask->id);
- BKE_animsys_evaluate_animdata(&mask_temp->id, adt, mask->sfra + nr, ADT_RECALC_ANIM, false);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ context->depsgraph, mask->sfra + nr);
+ BKE_animsys_evaluate_animdata(&mask_temp->id, adt, &anim_eval_context, ADT_RECALC_ANIM, false);
maskbuf = MEM_mallocN(sizeof(float) * context->rectx * context->recty, __func__);
@@ -3695,7 +3714,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 +3767,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 */
@@ -3813,8 +3833,7 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context,
float cfra,
clock_t begin,
bool use_preprocess,
- const bool is_proxy_image,
- const bool is_preprocessed)
+ const bool is_proxy_image)
{
if (context->is_proxy_render == false &&
(ibuf->x != context->rectx || ibuf->y != context->recty)) {
@@ -3823,11 +3842,17 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context,
if (use_preprocess) {
float cost = seq_estimate_render_cost_end(context->scene, begin);
- BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false);
+
+ /* TODO (Richard): It should be possible to store in cache if image is proxy,
+ * but it adds quite a bit of complexity. Since proxies are fast to read, I would
+ * rather simplify existing code a bit. */
+ if (!is_proxy_image) {
+ BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false);
+ }
/* Reset timer so we can get partial render time. */
begin = seq_estimate_render_cost_begin();
- ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed);
+ ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image);
}
float cost = seq_estimate_render_cost_end(context->scene, begin);
@@ -3843,49 +3868,30 @@ static ImBuf *seq_render_strip(const SeqRenderData *context,
ImBuf *ibuf = NULL;
bool use_preprocess = false;
bool is_proxy_image = false;
- /* all effects are handled similarly with the exception of speed effect */
- int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT :
- seq->type;
- bool is_preprocessed = !ELEM(
- type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP);
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);
+ context, seq, ibuf, cfra, begin, use_preprocess, is_proxy_image);
}
+
+ if (ibuf == NULL) {
+ ibuf = IMB_allocImBuf(context->rectx, context->recty, 32, IB_rect);
+ sequencer_imbuf_assign_spaces(context->scene, ibuf);
+ }
+
return ibuf;
}
@@ -4295,6 +4301,10 @@ void BKE_sequence_invalidate_movieclip_strips(Main *bmain, MovieClip *clip_targe
void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
{
+ if (scene->ed == NULL) {
+ return;
+ }
+
Sequence *seq;
BKE_sequencer_cache_cleanup(scene);
@@ -5052,7 +5062,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)
@@ -5721,7 +5731,7 @@ static Sequence *seq_dupli(const Scene *scene_src,
struct SeqEffectHandle sh;
sh = BKE_sequence_get_effect(seq);
if (sh.copy) {
- sh.copy(seq, seqn, flag);
+ sh.copy(seqn, seq, flag);
}
seqn->strip->stripdata = NULL;
@@ -6036,3 +6046,84 @@ bool BKE_sequencer_check_scene_recursion(Scene *scene, ReportList *reports)
return false;
}
+
+/* Check if "seq_main" (indirectly) uses strip "seq". */
+bool BKE_sequencer_render_loop_check(Sequence *seq_main, Sequence *seq)
+{
+ if (seq_main == seq) {
+ return true;
+ }
+
+ if ((seq_main->seq1 && BKE_sequencer_render_loop_check(seq_main->seq1, seq)) ||
+ (seq_main->seq2 && BKE_sequencer_render_loop_check(seq_main->seq2, seq)) ||
+ (seq_main->seq3 && BKE_sequencer_render_loop_check(seq_main->seq3, seq))) {
+ return true;
+ }
+
+ SequenceModifierData *smd;
+ for (smd = seq_main->modifiers.first; smd; smd = smd->next) {
+ if (smd->mask_sequence && BKE_sequencer_render_loop_check(smd->mask_sequence, seq)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq)
+{
+ LISTBASE_FOREACH (Sequence *, user_seq, seqbase) {
+ /* Look in metas for usage of seq. */
+ if (user_seq->type == SEQ_TYPE_META) {
+ sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq);
+ }
+
+ /* Clear seq from modifiers. */
+ SequenceModifierData *smd;
+ for (smd = user_seq->modifiers.first; smd; smd = smd->next) {
+ if (smd->mask_sequence == seq) {
+ smd->mask_sequence = NULL;
+ }
+ }
+
+ /* Remove effects, that use seq. */
+ if ((user_seq->seq1 && user_seq->seq1 == seq) || (user_seq->seq2 && user_seq->seq2 == seq) ||
+ (user_seq->seq3 && user_seq->seq3 == seq)) {
+ user_seq->flag |= SEQ_FLAG_DELETE;
+ /* Strips can be used as mask even if not in same seqbase. */
+ sequencer_flag_users_for_removal(scene, &scene->ed->seqbase, user_seq);
+ }
+ }
+}
+
+/* Flag seq and its users (effects) for removal. */
+void BKE_sequencer_flag_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq)
+{
+ if (seq == NULL || (seq->flag & SEQ_FLAG_DELETE) != 0) {
+ return;
+ }
+
+ /* Flag and remove meta children. */
+ if (seq->type == SEQ_TYPE_META) {
+ LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) {
+ BKE_sequencer_flag_for_removal(scene, &seq->seqbase, meta_child);
+ }
+ }
+
+ seq->flag |= SEQ_FLAG_DELETE;
+ sequencer_flag_users_for_removal(scene, seqbase, seq);
+}
+
+/* Remove all flagged sequences, return true if sequence is removed. */
+void BKE_sequencer_remove_flagged_sequences(Scene *scene, ListBase *seqbase)
+{
+ LISTBASE_FOREACH_MUTABLE (Sequence *, seq, seqbase) {
+ if (seq->flag & SEQ_FLAG_DELETE) {
+ if (seq->type == SEQ_TYPE_META) {
+ BKE_sequencer_remove_flagged_sequences(scene, &seq->seqbase);
+ }
+ BLI_remlink(seqbase, seq);
+ BKE_sequence_free(scene, seq, true);
+ }
+ }
+}
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/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c
index c59042bc045..29f4c7dc6c1 100644
--- a/source/blender/blenkernel/intern/shrinkwrap.c
+++ b/source/blender/blenkernel/intern/shrinkwrap.c
@@ -51,6 +51,7 @@
#include "BKE_editmesh.h"
#include "BKE_mesh.h" /* for OMP limits. */
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
#include "BKE_subsurf.h"
#include "DEG_depsgraph_query.h"
@@ -115,7 +116,16 @@ bool BKE_shrinkwrap_init_tree(
{
memset(data, 0, sizeof(*data));
- if (!mesh || mesh->totvert <= 0) {
+ if (mesh == NULL) {
+ return false;
+ }
+
+ /* We could create a BVH tree from the edit mesh,
+ * however accessing normals from the face/loop normals gets more involved.
+ * Convert mesh data since this isn't typically used in edit-mode. */
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+
+ if (mesh->totvert <= 0) {
return false;
}
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 20a23ab8b38..c0fc8fcb464 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -31,6 +31,7 @@
#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -47,16 +48,36 @@
#include "BKE_pointcache.h"
#include "BKE_simulation.h"
+#include "NOD_node_tree_multi_function.hh"
#include "NOD_simulation.h"
+#include "BLI_map.hh"
#include "BLT_translation.h"
+#include "FN_attributes_ref.hh"
+#include "FN_multi_function_network_evaluation.hh"
+#include "FN_multi_function_network_optimization.hh"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
-using blender::float3;
-using blender::MutableSpan;
-using blender::Span;
+#include "SIM_simulation_update.hh"
+
+using StateInitFunction = void (*)(SimulationState *state);
+using StateResetFunction = void (*)(SimulationState *state);
+using StateRemoveFunction = void (*)(SimulationState *state);
+using StateCopyFunction = void (*)(const SimulationState *src, SimulationState *dst);
+
+struct SimulationStateType {
+ const char *name;
+ int size;
+ StateInitFunction init;
+ StateResetFunction reset;
+ StateRemoveFunction remove;
+ StateCopyFunction copy;
+};
+
+static const SimulationStateType *try_get_state_type(blender::StringRefNull type_name);
static void simulation_init_data(ID *id)
{
@@ -67,20 +88,12 @@ static void simulation_init_data(ID *id)
bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname);
simulation->nodetree = ntree;
-
- /* Add a default particle simulation state for now. */
- ParticleSimulationState *state = (ParticleSimulationState *)MEM_callocN(
- sizeof(ParticleSimulationState), __func__);
- CustomData_reset(&state->attributes);
-
- state->point_cache = BKE_ptcache_add(&state->ptcaches);
- BLI_addtail(&simulation->states, state);
}
static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag)
{
Simulation *simulation_dst = (Simulation *)id_dst;
- Simulation *simulation_src = (Simulation *)id_src;
+ const Simulation *simulation_src = (const Simulation *)id_src;
/* We always need allocation of our private ID data. */
const int flag_private_id_data = flag & ~LIB_ID_CREATE_NO_ALLOCATE;
@@ -93,19 +106,14 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
}
BLI_listbase_clear(&simulation_dst->states);
-
LISTBASE_FOREACH (const SimulationState *, state_src, &simulation_src->states) {
- switch ((eSimulationStateType)state_src->type) {
- case SIM_STATE_TYPE_PARTICLES: {
- ParticleSimulationState *particle_state_dst = (ParticleSimulationState *)MEM_callocN(
- sizeof(ParticleSimulationState), __func__);
- CustomData_reset(&particle_state_dst->attributes);
-
- BLI_addtail(&simulation_dst->states, particle_state_dst);
- break;
- }
- }
+ SimulationState *state_dst = BKE_simulation_state_add(
+ simulation_dst, state_src->type, state_src->name);
+ BKE_simulation_state_copy_data(state_src, state_dst);
}
+
+ BLI_listbase_clear(&simulation_dst->dependencies);
+ BLI_duplicatelist(&simulation_dst->dependencies, &simulation_src->dependencies);
}
static void simulation_free_data(ID *id)
@@ -120,17 +128,9 @@ static void simulation_free_data(ID *id)
simulation->nodetree = nullptr;
}
- LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) {
- switch ((eSimulationStateType)state->type) {
- case SIM_STATE_TYPE_PARTICLES: {
- ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
- CustomData_free(&particle_state->attributes, particle_state->tot_particles);
- BKE_ptcache_free_list(&particle_state->ptcaches);
- break;
- }
- }
- MEM_freeN(state);
- }
+ BKE_simulation_state_remove_all(simulation);
+
+ BLI_freelistN(&simulation->dependencies);
}
static void simulation_foreach_id(ID *id, LibraryForeachIDData *data)
@@ -140,6 +140,9 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data)
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree);
}
+ LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
+ BKE_LIB_FOREACHID_PROCESS_ID(data, dependency->id, IDWALK_CB_USER);
+ }
}
IDTypeInfo IDType_ID_SIM = {
@@ -168,94 +171,203 @@ void *BKE_simulation_add(Main *bmain, const char *name)
return simulation;
}
-static MutableSpan<float3> get_particle_positions(ParticleSimulationState *state)
+SimulationState *BKE_simulation_state_add(Simulation *simulation,
+ const char *type,
+ const char *name)
{
- return MutableSpan<float3>(
- (float3 *)CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Position"),
- state->tot_particles);
+ BLI_assert(simulation != nullptr);
+ BLI_assert(name != nullptr);
+
+ const SimulationStateType *state_type = try_get_state_type(type);
+ BLI_assert(state_type != nullptr);
+
+ SimulationState *state = (SimulationState *)MEM_callocN(state_type->size, AT);
+ state->type = BLI_strdup(type);
+ state->name = BLI_strdup(name);
+
+ state_type->init(state);
+ BLI_addtail(&simulation->states, state);
+ return state;
}
-static void ensure_attributes_exist(ParticleSimulationState *state)
+void BKE_simulation_state_remove(Simulation *simulation, SimulationState *state)
{
- if (CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Position") == nullptr) {
- CustomData_add_layer_named(
- &state->attributes, CD_LOCATION, CD_CALLOC, nullptr, state->tot_particles, "Position");
- }
+ BLI_assert(simulation != nullptr);
+ BLI_assert(state != nullptr);
+ BLI_assert(BLI_findindex(&simulation->states, state) >= 0);
+
+ BLI_remlink(&simulation->states, state);
+ const SimulationStateType *state_type = try_get_state_type(state->type);
+ BLI_assert(state_type != nullptr);
+ state_type->remove(state);
+ MEM_freeN(state->name);
+ MEM_freeN(state->type);
+ MEM_freeN(state);
}
-static void copy_particle_state_to_cow(ParticleSimulationState *state_orig,
- ParticleSimulationState *state_cow)
+void BKE_simulation_state_remove_all(Simulation *simulation)
{
- ensure_attributes_exist(state_cow);
- CustomData_free(&state_cow->attributes, state_cow->tot_particles);
- CustomData_copy(&state_orig->attributes,
- &state_cow->attributes,
- CD_MASK_ALL,
- CD_DUPLICATE,
- state_orig->tot_particles);
- state_cow->current_frame = state_orig->current_frame;
- state_cow->tot_particles = state_orig->tot_particles;
+ BLI_assert(simulation != nullptr);
+
+ while (!BLI_listbase_is_empty(&simulation->states)) {
+ BKE_simulation_state_remove(simulation, (SimulationState *)simulation->states.first);
+ }
}
-void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation)
+void BKE_simulation_state_reset(Simulation *UNUSED(simulation), SimulationState *state)
{
- int current_frame = scene->r.cfra;
+ BLI_assert(state != nullptr);
- ParticleSimulationState *state_cow = (ParticleSimulationState *)simulation->states.first;
- ParticleSimulationState *state_orig = (ParticleSimulationState *)state_cow->head.orig_state;
+ const SimulationStateType *state_type = try_get_state_type(state->type);
+ BLI_assert(state_type != nullptr);
+ state_type->reset(state);
+}
- if (current_frame == state_cow->current_frame) {
- return;
+void BKE_simulation_state_reset_all(Simulation *simulation)
+{
+ BLI_assert(simulation != nullptr);
+
+ LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
+ BKE_simulation_state_reset(simulation, state);
}
+}
- /* Number of particles should be stored in the cache, but for now assume it is constant. */
- state_cow->tot_particles = state_orig->tot_particles;
- CustomData_realloc(&state_cow->attributes, state_orig->tot_particles);
- ensure_attributes_exist(state_cow);
+void BKE_simulation_state_copy_data(const SimulationState *src_state, SimulationState *dst_state)
+{
+ BLI_assert(src_state != nullptr);
+ BLI_assert(dst_state != nullptr);
+ BLI_assert(STREQ(src_state->type, dst_state->type));
- PTCacheID pid_cow;
- BKE_ptcache_id_from_sim_particles(&pid_cow, state_cow);
- BKE_ptcache_id_time(&pid_cow, scene, current_frame, nullptr, nullptr, nullptr);
+ const SimulationStateType *state_type = try_get_state_type(src_state->type);
+ BLI_assert(state_type != nullptr);
+ state_type->copy(src_state, dst_state);
+}
- /* If successfull, this will read the state directly into the cow state. */
- int cache_result = BKE_ptcache_read(&pid_cow, current_frame, true);
- if (cache_result == PTCACHE_READ_EXACT) {
- state_cow->current_frame = current_frame;
- return;
+SimulationState *BKE_simulation_state_try_find_by_name(Simulation *simulation, const char *name)
+{
+ if (simulation == nullptr) {
+ return nullptr;
+ }
+ if (name == nullptr) {
+ return nullptr;
}
- /* Below we modify the original state/cache. Only the active depsgraph is allowed to do that. */
- if (!DEG_is_active(depsgraph)) {
- return;
+ LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
+ if (STREQ(state->name, name)) {
+ return state;
+ }
}
+ return nullptr;
+}
- PTCacheID pid_orig;
- BKE_ptcache_id_from_sim_particles(&pid_orig, state_orig);
- BKE_ptcache_id_time(&pid_orig, scene, current_frame, nullptr, nullptr, nullptr);
+SimulationState *BKE_simulation_state_try_find_by_name_and_type(Simulation *simulation,
+ const char *name,
+ const char *type)
+{
+ if (type == nullptr) {
+ return nullptr;
+ }
- if (current_frame == 1) {
- state_orig->tot_particles = 100;
- state_orig->current_frame = 1;
- CustomData_realloc(&state_orig->attributes, state_orig->tot_particles);
- ensure_attributes_exist(state_orig);
+ SimulationState *state = BKE_simulation_state_try_find_by_name(simulation, name);
+ if (state == nullptr) {
+ return nullptr;
+ }
+ if (STREQ(state->type, type)) {
+ return state;
+ }
+ return nullptr;
+}
- MutableSpan<float3> positions = get_particle_positions(state_orig);
- for (uint i : positions.index_range()) {
- positions[i] = {i / 10.0f, 0, 0};
- }
+void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation)
+{
+ blender::sim::update_simulation_in_depsgraph(depsgraph, scene, simulation);
+}
- BKE_ptcache_write(&pid_orig, current_frame);
- copy_particle_state_to_cow(state_orig, state_cow);
+void BKE_simulation_update_dependencies(Simulation *simulation, Main *bmain)
+{
+ bool dependencies_changed = blender::sim::update_simulation_dependencies(simulation);
+ if (dependencies_changed) {
+ DEG_relations_tag_update(bmain);
}
- else if (current_frame == state_orig->current_frame + 1) {
- state_orig->current_frame = current_frame;
- ensure_attributes_exist(state_orig);
- MutableSpan<float3> positions = get_particle_positions(state_orig);
- for (float3 &position : positions) {
- position.z += 0.1f;
- }
+}
- BKE_ptcache_write(&pid_orig, current_frame);
- copy_particle_state_to_cow(state_orig, state_cow);
+using StateTypeMap = blender::Map<std::string, std::unique_ptr<SimulationStateType>>;
+
+template<typename T>
+static void add_state_type(StateTypeMap &map,
+ const char *name,
+ void (*init)(T *state),
+ void (*reset)(T *state),
+ void (*remove)(T *state),
+ void (*copy)(const T *src, T *dst))
+{
+ SimulationStateType state_type{
+ name,
+ (int)sizeof(T),
+ (StateInitFunction)init,
+ (StateResetFunction)reset,
+ (StateRemoveFunction)remove,
+ (StateCopyFunction)copy,
+ };
+ map.add_new(name, std::make_unique<SimulationStateType>(state_type));
+}
+
+static StateTypeMap init_state_types()
+{
+ StateTypeMap map;
+ add_state_type<ParticleSimulationState>(
+ map,
+ SIM_TYPE_NAME_PARTICLE_SIMULATION,
+ [](ParticleSimulationState *state) { CustomData_reset(&state->attributes); },
+ [](ParticleSimulationState *state) {
+ CustomData_free(&state->attributes, state->tot_particles);
+ state->tot_particles = 0;
+ state->next_particle_id = 0;
+ },
+ [](ParticleSimulationState *state) {
+ CustomData_free(&state->attributes, state->tot_particles);
+ },
+ [](const ParticleSimulationState *src, ParticleSimulationState *dst) {
+ CustomData_free(&dst->attributes, dst->tot_particles);
+ dst->tot_particles = src->tot_particles;
+ dst->next_particle_id = src->next_particle_id;
+ CustomData_copy(
+ &src->attributes, &dst->attributes, CD_MASK_ALL, CD_DUPLICATE, src->tot_particles);
+ });
+
+ add_state_type<ParticleMeshEmitterSimulationState>(
+ map,
+ SIM_TYPE_NAME_PARTICLE_MESH_EMITTER,
+ [](ParticleMeshEmitterSimulationState *UNUSED(state)) {},
+ [](ParticleMeshEmitterSimulationState *state) { state->last_birth_time = 0.0f; },
+ [](ParticleMeshEmitterSimulationState *UNUSED(state)) {},
+ [](const ParticleMeshEmitterSimulationState *src, ParticleMeshEmitterSimulationState *dst) {
+ dst->last_birth_time = src->last_birth_time;
+ });
+ return map;
+}
+
+static StateTypeMap &get_state_types()
+{
+ static StateTypeMap state_type_map = init_state_types();
+ return state_type_map;
+}
+
+static const SimulationStateType *try_get_state_type(blender::StringRefNull type_name)
+{
+ std::unique_ptr<SimulationStateType> *type = get_state_types().lookup_ptr_as(type_name);
+ if (type == nullptr) {
+ return nullptr;
}
+ return type->get();
+}
+
+template<> const char *BKE_simulation_get_state_type_name<ParticleSimulationState>()
+{
+ return SIM_TYPE_NAME_PARTICLE_SIMULATION;
+}
+
+template<> const char *BKE_simulation_get_state_type_name<ParticleMeshEmitterSimulationState>()
+{
+ return SIM_TYPE_NAME_PARTICLE_MESH_EMITTER;
}
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 68d0822a223..b7b325644ca 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -165,24 +165,28 @@ static void free_softbody_intern(SoftBody *sb);
/*physical unit of force is [kg * m / sec^2]*/
-static float sb_grav_force_scale(Object *UNUSED(ob))
-/* since unit of g is [m/sec^2] and F = mass * g we rescale unit mass of node to 1 gramm
- * put it to a function here, so we can add user options later without touching simulation code
+/**
+ * Since unit of g is [m/sec^2] and F = mass * g we re-scale unit mass of node to 1 gram
+ * put it to a function here, so we can add user options later without touching simulation code.
*/
+static float sb_grav_force_scale(Object *UNUSED(ob))
{
return (0.001f);
}
-static float sb_fric_force_scale(Object *UNUSED(ob))
-/* rescaling unit of drag [1 / sec] to somehow reasonable
- * put it to a function here, so we can add user options later without touching simulation code
+/**
+ * Re-scaling unit of drag [1 / sec] to somehow reasonable
+ * put it to a function here, so we can add user options later without touching simulation code.
*/
+static float sb_fric_force_scale(Object *UNUSED(ob))
{
return (0.01f);
}
+/**
+ * Defining the frames to *real* time relation.
+ */
static float sb_time_scale(Object *ob)
-/* defining the frames to *real* time relation */
{
SoftBody *sb = ob->soft; /* is supposed to be there */
if (sb) {
@@ -481,7 +485,6 @@ static void ccd_mesh_update(Object *ob, ccd_Mesh *pccd_M)
mima->maxy = max_ff(mima->maxy, v[1] + hull);
mima->maxz = max_ff(mima->maxz, v[2] + hull);
}
- return;
}
static void ccd_mesh_free(ccd_Mesh *ccdm)
@@ -1479,7 +1482,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 +2111,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..1fcfc9b060f 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -112,6 +112,20 @@ static void sound_free_data(ID *id)
}
}
+static void sound_foreach_cache(ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ bSound *sound = (bSound *)id;
+ IDCacheKey key = {
+ .id_session_uuid = id->session_uuid,
+ .offset_in_ID = offsetof(bSound, waveform),
+ .cache_v = sound->waveform,
+ };
+
+ function_callback(id, &key, &sound->waveform, 0, user_data);
+}
+
IDTypeInfo IDType_ID_SO = {
.id_code = ID_SO,
.id_filter = FILTER_ID_SO,
@@ -128,6 +142,7 @@ IDTypeInfo IDType_ID_SO = {
.free_data = sound_free_data,
.make_local = NULL,
.foreach_id = NULL,
+ .foreach_cache = sound_foreach_cache,
};
#ifdef WITH_AUDASPACE
@@ -172,7 +187,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 +208,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 +467,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..46341652544 100644
--- a/source/blender/blenkernel/intern/studiolight.c
+++ b/source/blender/blenkernel/intern/studiolight.c
@@ -875,7 +875,6 @@ BLI_INLINE void studiolight_spherical_harmonics_eval(StudioLight *sl,
color[i] = studiolight_spherical_harmonics_geomerics_eval(
normal, sh[0][i], sh[1][i], sh[2][i], sh[3][i]);
}
- return;
#else
/* L0 */
mul_v3_v3fl(color, sl->spherical_harmonics_coefs[0], 0.282095f);
@@ -1426,9 +1425,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..22649a2af07 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,95 @@ 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;
+}
+
+static void adjacet_vertices_index_from_adjacent_edge(const SubdivCCG *subdiv_ccg,
+ const SubdivCCGCoord *coord,
+ const MLoop *mloop,
+ const MPoly *mpoly,
+ int *r_v1,
+ int *r_v2)
+{
+ const int grid_size_1 = subdiv_ccg->grid_size - 1;
+ const int poly_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord->grid_index);
+ const MPoly *p = &mpoly[poly_index];
+ *r_v1 = mloop[coord->grid_index].v;
+ if (coord->x == grid_size_1) {
+ const MLoop *next = ME_POLY_LOOP_NEXT(mloop, p, coord->grid_index);
+ *r_v2 = next->v;
+ }
+ if (coord->y == grid_size_1) {
+ const MLoop *prev = ME_POLY_LOOP_PREV(mloop, p, coord->grid_index);
+ *r_v2 = prev->v;
+ }
+}
+
+SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg,
+ const SubdivCCGCoord *coord,
+ const MLoop *mloop,
+ const MPoly *mpoly,
+ int *r_v1,
+ int *r_v2)
+{
+
+ const int grid_size_1 = subdiv_ccg->grid_size - 1;
+ if (is_corner_grid_coord(subdiv_ccg, coord)) {
+ if (coord->x == 0 && coord->y == 0) {
+ /* Grid corner in the center of a poly. */
+ return SUBDIV_CCG_ADJACENT_NONE;
+ }
+ if (coord->x == grid_size_1 && coord->y == grid_size_1) {
+ /* Grid corner adjacent to a coarse mesh vertex. */
+ *r_v1 = *r_v2 = mloop[coord->grid_index].v;
+ return SUBDIV_CCG_ADJACENT_VERTEX;
+ }
+ /* Grid corner adjacent to the middle of a coarse mesh edge. */
+ adjacet_vertices_index_from_adjacent_edge(subdiv_ccg, coord, mloop, mpoly, r_v1, r_v2);
+ return SUBDIV_CCG_ADJACENT_EDGE;
+ }
+
+ if (is_boundary_grid_coord(subdiv_ccg, coord)) {
+ if (!is_inner_edge_grid_coordinate(subdiv_ccg, coord)) {
+ /* Grid boundary adjacent to a coarse mesh edge. */
+ adjacet_vertices_index_from_adjacent_edge(subdiv_ccg, coord, mloop, mpoly, r_v1, r_v2);
+ return SUBDIV_CCG_ADJACENT_EDGE;
+ }
+ }
+ return SUBDIV_CCG_ADJACENT_NONE;
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 527b54a1aa2..5f85e1a1664 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -59,37 +59,9 @@
# 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.
- */
-
-/***/
-
-/****************************** Prototypes ************************/
+/* -------------------------------------------------------------------- */
+/** \name Prototypes
+ * \{ */
static void txt_pop_first(Text *text);
static void txt_pop_last(Text *text);
@@ -97,7 +69,11 @@ static void txt_delete_line(Text *text, TextLine *line);
static void txt_delete_sel(Text *text);
static void txt_make_dirty(Text *text);
-/****************************** Text Datablock ************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Data-Block
+ * \{ */
static void text_init_data(ID *id)
{
@@ -106,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;
@@ -153,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;
@@ -186,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
@@ -209,7 +184,11 @@ IDTypeInfo IDType_ID_TXT = {
.foreach_id = NULL,
};
-/***/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Add, Free, Validation
+ * \{ */
/**
* \note caller must handle `compiled` member.
@@ -308,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;
@@ -331,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;
@@ -345,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");
@@ -362,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;
@@ -376,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);
@@ -436,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;
@@ -495,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)) {
@@ -529,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)) {
@@ -549,9 +528,11 @@ void BKE_text_file_modified_ignore(Text *text)
text->mtime = st.st_mtime;
}
-/*****************************/
-/* Editing utility functions */
-/*****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Editing Utility Functions
+ * \{ */
static void make_new_line(TextLine *line, char *newline)
{
@@ -696,9 +677,11 @@ static void txt_make_dirty(Text *text)
#endif
}
-/****************************/
-/* Cursor utility functions */
-/****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cursor Utility Functions
+ * \{ */
static void txt_curs_cur(Text *text, TextLine ***linep, int **charp)
{
@@ -722,9 +705,15 @@ bool txt_cursor_is_line_end(Text *text)
return (text->selc == text->sell->len);
}
-/*****************************/
-/* Cursor movement functions */
-/*****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \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)
{
@@ -1095,9 +1084,11 @@ void txt_move_to(Text *text, unsigned int line, unsigned int ch, const bool sel)
}
}
-/****************************/
-/* Text selection functions */
-/****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Selection Functions
+ * \{ */
static void txt_curs_swap(Text *text)
{
@@ -1293,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
*
@@ -1393,9 +1386,9 @@ void txt_from_buf_for_undo(Text *text, const char *buf, int buf_len)
/** \} */
-/***************************/
-/* Cut and paste functions */
-/***************************/
+/* -------------------------------------------------------------------- */
+/** \name Cut and Paste Functions
+ * \{ */
char *txt_to_buf(Text *text, int *r_buf_strlen)
{
@@ -1474,59 +1467,6 @@ char *txt_to_buf(Text *text, int *r_buf_strlen)
return buf;
}
-int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
-{
- TextLine *tl, *startl;
- const char *s = NULL;
-
- if (!text->curl || !text->sell) {
- return 0;
- }
-
- txt_order_cursors(text, false);
-
- tl = startl = text->sell;
-
- if (match_case) {
- s = strstr(&tl->line[text->selc], findstr);
- }
- else {
- s = BLI_strcasestr(&tl->line[text->selc], findstr);
- }
- while (!s) {
- tl = tl->next;
- if (!tl) {
- if (wrap) {
- tl = text->lines.first;
- }
- else {
- break;
- }
- }
-
- if (match_case) {
- s = strstr(tl->line, findstr);
- }
- else {
- s = BLI_strcasestr(tl->line, findstr);
- }
- if (tl == startl) {
- break;
- }
- }
-
- if (s) {
- int newl = txt_get_span(text->lines.first, tl);
- int newc = (int)(s - tl->line);
- txt_move_to(text, newl, newc, 0);
- txt_move_to(text, newl, newc + strlen(findstr), 1);
- return 1;
- }
- else {
- return 0;
- }
-}
-
char *txt_sel_to_buf(Text *text, int *r_buf_strlen)
{
char *buf;
@@ -1670,9 +1610,70 @@ void txt_insert_buf(Text *text, const char *in_buffer)
MEM_freeN(buffer);
}
-/**************************/
-/* Line editing functions */
-/**************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Find String in Text
+ * \{ */
+
+int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
+{
+ TextLine *tl, *startl;
+ const char *s = NULL;
+
+ if (!text->curl || !text->sell) {
+ return 0;
+ }
+
+ txt_order_cursors(text, false);
+
+ tl = startl = text->sell;
+
+ if (match_case) {
+ s = strstr(&tl->line[text->selc], findstr);
+ }
+ else {
+ s = BLI_strcasestr(&tl->line[text->selc], findstr);
+ }
+ while (!s) {
+ tl = tl->next;
+ if (!tl) {
+ if (wrap) {
+ tl = text->lines.first;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (match_case) {
+ s = strstr(tl->line, findstr);
+ }
+ else {
+ s = BLI_strcasestr(tl->line, findstr);
+ }
+ if (tl == startl) {
+ break;
+ }
+ }
+
+ if (s) {
+ int newl = txt_get_span(text->lines.first, tl);
+ int newc = (int)(s - tl->line);
+ txt_move_to(text, newl, newc, 0);
+ txt_move_to(text, newl, newc + strlen(findstr), 1);
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Line Editing Functions
+ * \{ */
void txt_split_curline(Text *text)
{
@@ -2081,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.
*
@@ -2294,9 +2293,11 @@ int txt_setcurr_tab_spaces(Text *text, int space)
return i;
}
-/*******************************/
-/* Character utility functions */
-/*******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Character Queries
+ * \{ */
int text_check_bracket(const char ch)
{
@@ -2418,3 +2419,5 @@ int text_find_identifier_start(const char *str, int i)
i++;
return i;
}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index 97d95cb7e46..f17467e4a26 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -509,7 +509,7 @@ void BKE_tracking_clipboard_copy_tracks(MovieTracking *tracking, MovieTrackingOb
}
}
-/* Check whether there're any tracks in the clipboard. */
+/* Check whether there are any tracks in the clipboard. */
bool BKE_tracking_clipboard_has_tracks(void)
{
return (BLI_listbase_is_empty(&tracking_clipboard.tracks) == false);
diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c
index e155dedeef0..0809e8dda6d 100644
--- a/source/blender/blenkernel/intern/undo_system.c
+++ b/source/blender/blenkernel/intern/undo_system.c
@@ -507,9 +507,7 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack,
/* Might not be final place for this to be called - probably only want to call it from some
* undo handlers, not all of them? */
- if (BKE_lib_override_library_is_enabled()) {
- BKE_lib_override_library_main_operations_create(G_MAIN, false);
- }
+ BKE_lib_override_library_main_operations_create(G_MAIN, false);
/* Remove all undos after (also when 'ustack->step_active == NULL'). */
while (ustack->steps.last != ustack->step_active) {
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 26c5810aefa..48b920c8a05 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -152,7 +152,7 @@ static struct VolumeFileCache {
~VolumeFileCache()
{
- assert(cache.size() == 0);
+ assert(cache.empty());
}
Entry *add_metadata_user(const Entry &template_entry)
@@ -483,6 +483,20 @@ static void volume_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
+static void volume_foreach_cache(ID *id,
+ IDTypeForeachCacheFunctionCallback function_callback,
+ void *user_data)
+{
+ Volume *volume = (Volume *)id;
+ IDCacheKey key = {
+ /* id_session_uuid */ id->session_uuid,
+ /*offset_in_ID*/ offsetof(Volume, runtime.grids),
+ /* cache_v */ volume->runtime.grids,
+ };
+
+ function_callback(id, &key, (void **)&volume->runtime.grids, 0, user_data);
+}
+
IDTypeInfo IDType_ID_VO = {
/* id_code */ ID_VO,
/* id_filter */ FILTER_ID_VO,
@@ -498,6 +512,7 @@ IDTypeInfo IDType_ID_VO = {
/* free_data */ volume_free_data,
/* make_local */ nullptr,
/* foreach_id */ volume_foreach_id,
+ /* foreach_cache */ volume_foreach_cache,
};
void BKE_volume_init_grids(Volume *volume)
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index c72747a8c51..67e9afdf7fa 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -32,6 +32,8 @@
extern "C" {
#endif
+struct AnimationEvalContext;
+
/* --------------- NLA Evaluation DataTypes ----------------------- */
/* used for list of strips to accumulate at current time */
@@ -168,13 +170,17 @@ float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode);
/* these functions are only defined here to avoid problems with the order
* in which they get defined. */
-NlaEvalStrip *nlastrips_ctime_get_strip(
- ListBase *list, ListBase *strips, short index, float ctime, const bool flush_to_original);
+NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list,
+ ListBase *strips,
+ short index,
+ const struct AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original);
void nlastrip_evaluate(PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context,
const bool flush_to_original);
void nladata_flush_channels(PointerRNA *ptr,
NlaEvalData *channels,
diff --git a/source/blender/blenlib/BLI_allocator.hh b/source/blender/blenlib/BLI_allocator.hh
index d57703f71bc..47d8156476f 100644
--- a/source/blender/blenlib/BLI_allocator.hh
+++ b/source/blender/blenlib/BLI_allocator.hh
@@ -84,8 +84,8 @@ class RawAllocator {
void *ptr = malloc(size + alignment + sizeof(MemHead));
void *used_ptr = (void *)((uintptr_t)POINTER_OFFSET(ptr, alignment + sizeof(MemHead)) &
~((uintptr_t)alignment - 1));
- uint offset = (uint)((uintptr_t)used_ptr - (uintptr_t)ptr);
- BLI_assert(offset >= sizeof(MemHead));
+ int offset = (int)((intptr_t)used_ptr - (intptr_t)ptr);
+ BLI_assert(offset >= (int)sizeof(MemHead));
((MemHead *)used_ptr - 1)->offset = (int)offset;
return used_ptr;
}
diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index b929d1220da..c7b4bdc977f 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -13,6 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
#ifndef __BLI_ARRAY_HH__
#define __BLI_ARRAY_HH__
@@ -27,8 +28,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,
@@ -53,11 +53,8 @@ template<
typename T,
/**
* The number of values that can be stored in the array, without doing a heap allocation.
- *
- * When T is large, the small buffer optimization is disabled by default to avoid large
- * unexpected allocations on the stack. It can still be enabled explicitely though.
*/
- uint InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this array. Should rarely be changed, except when you don't want that
* MEM_* functions are used internally.
@@ -66,16 +63,16 @@ template<
class Array {
private:
/** The beginning of the array. It might point into the inline buffer. */
- T *m_data;
+ T *data_;
/** Number of elements in the array. */
- uint m_size;
+ int64_t size_;
/** Used for allocations when the inline buffer is too small. */
- Allocator m_allocator;
+ Allocator allocator_;
/** A placeholder buffer that will remain uninitialized until it is used. */
- AlignedBuffer<sizeof(T) * InlineBufferCapacity, alignof(T)> m_inline_buffer;
+ TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
public:
/**
@@ -83,23 +80,29 @@ class Array {
*/
Array()
{
- m_data = this->inline_buffer();
- m_size = 0;
+ data_ = inline_buffer_;
+ size_ = 0;
}
/**
* Create a new array that contains copies of all values.
*/
- Array(Span<T> values)
+ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator)
{
- m_size = values.size();
- m_data = this->get_buffer_for_size(values.size());
- uninitialized_copy_n(values.data(), m_size, m_data);
+ size_ = values.size();
+ data_ = this->get_buffer_for_size(values.size());
+ uninitialized_convert_n<U, T>(values.data(), size_, data_);
}
/**
* Create a new array that contains copies of all values.
*/
+ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Array(const std::initializer_list<U> &values) : Array(Span<U>(values))
+ {
+ }
+
Array(const std::initializer_list<T> &values) : Array(Span<T>(values))
{
}
@@ -112,53 +115,69 @@ class Array {
* even for non-trivial types. This should not be the default though, because one can easily mess
* up when dealing with uninitialized memory.
*/
- explicit Array(uint size)
+ explicit Array(int64_t size)
{
- m_size = size;
- m_data = this->get_buffer_for_size(size);
- default_construct_n(m_data, size);
+ size_ = size;
+ data_ = this->get_buffer_for_size(size);
+ default_construct_n(data_, size);
}
/**
* Create a new array with the given size. All values will be initialized by copying the given
* default.
*/
- Array(uint size, const T &value)
+ Array(int64_t size, const T &value)
{
- m_size = size;
- m_data = this->get_buffer_for_size(size);
- uninitialized_fill_n(m_data, m_size, value);
+ BLI_assert(size >= 0);
+ size_ = size;
+ data_ = this->get_buffer_for_size(size);
+ uninitialized_fill_n(data_, size_, value);
}
- Array(const Array &other) : m_allocator(other.m_allocator)
+ /**
+ * 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(int64_t size, NoInitialization)
{
- m_size = other.size();
+ BLI_assert(size >= 0);
+ size_ = size;
+ data_ = this->get_buffer_for_size(size);
+ }
- m_data = this->get_buffer_for_size(other.size());
- uninitialized_copy_n(other.data(), m_size, m_data);
+ Array(const Array &other) : Array(other.as_span(), other.allocator_)
+ {
}
- Array(Array &&other) noexcept : m_allocator(other.m_allocator)
+ Array(Array &&other) noexcept : allocator_(other.allocator_)
{
- m_size = other.m_size;
+ size_ = other.size_;
if (!other.uses_inline_buffer()) {
- m_data = other.m_data;
+ data_ = other.data_;
}
else {
- m_data = this->get_buffer_for_size(m_size);
- uninitialized_relocate_n(other.m_data, m_size, m_data);
+ data_ = this->get_buffer_for_size(size_);
+ uninitialized_relocate_n(other.data_, size_, data_);
}
- other.m_data = other.inline_buffer();
- other.m_size = 0;
+ other.data_ = other.inline_buffer_;
+ other.size_ = 0;
}
~Array()
{
- destruct_n(m_data, m_size);
+ destruct_n(data_, size_);
if (!this->uses_inline_buffer()) {
- m_allocator.deallocate((void *)m_data);
+ allocator_.deallocate((void *)data_);
}
}
@@ -184,44 +203,58 @@ class Array {
return *this;
}
+ T &operator[](int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ return data_[index];
+ }
+
+ const T &operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ return data_[index];
+ }
+
operator Span<T>() const
{
- return Span<T>(m_data, m_size);
+ return Span<T>(data_, size_);
}
operator MutableSpan<T>()
{
- return MutableSpan<T>(m_data, m_size);
+ return MutableSpan<T>(data_, size_);
}
- Span<T> as_span() const
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ operator Span<U>() const
{
- return *this;
+ return Span<U>(data_, size_);
}
- MutableSpan<T> as_mutable_span()
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ operator MutableSpan<U>()
{
- return *this;
+ return MutableSpan<U>(data_, size_);
}
- T &operator[](uint index)
+ Span<T> as_span() const
{
- BLI_assert(index < m_size);
- return m_data[index];
+ return *this;
}
- const T &operator[](uint index) const
+ MutableSpan<T> as_mutable_span()
{
- BLI_assert(index < m_size);
- return m_data[index];
+ return *this;
}
/**
* Returns the number of elements in the array.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -229,23 +262,15 @@ class Array {
*/
bool is_empty() const
{
- return m_size == 0;
+ return size_ == 0;
}
/**
- * Copies the value to all indices in the array.
+ * Copies the given value to every element in the array.
*/
- void fill(const T &value)
+ void fill(const T &value) const
{
- initialized_fill_n(m_data, m_size, value);
- }
-
- /**
- * Copies the value to the given indices in the array.
- */
- void fill_indices(Span<uint> indices, const T &value)
- {
- MutableSpan<T>(*this).fill_indices(indices, value);
+ initialized_fill_n(data_, size_, value);
}
/**
@@ -253,31 +278,31 @@ class Array {
*/
const T *data() const
{
- return m_data;
+ return data_;
}
T *data()
{
- return m_data;
+ return data_;
}
const T *begin() const
{
- return m_data;
+ return data_;
}
const T *end() const
{
- return m_data + m_size;
+ return data_ + size_;
}
T *begin()
{
- return m_data;
+ return data_;
}
T *end()
{
- return m_data + m_size;
+ return data_ + size_;
}
/**
@@ -285,7 +310,7 @@ class Array {
*/
IndexRange index_range() const
{
- return IndexRange(m_size);
+ return IndexRange(size_);
}
/**
@@ -294,7 +319,7 @@ class Array {
*/
void clear_without_destruct()
{
- m_size = 0;
+ size_ = 0;
}
/**
@@ -302,45 +327,47 @@ class Array {
*/
Allocator &allocator()
{
- return m_allocator;
+ return allocator_;
}
/**
* Get the value of the InlineBufferCapacity template argument. This is the number of elements
* that can be stored without doing an allocation.
*/
- static uint inline_buffer_capacity()
+ static int64_t inline_buffer_capacity()
{
return InlineBufferCapacity;
}
private:
- T *get_buffer_for_size(uint size)
+ T *get_buffer_for_size(int64_t size)
{
if (size <= InlineBufferCapacity) {
- return this->inline_buffer();
+ return inline_buffer_;
}
else {
return this->allocate(size);
}
}
- T *inline_buffer() const
- {
- return (T *)m_inline_buffer.ptr();
- }
-
- T *allocate(uint size)
+ T *allocate(int64_t size)
{
- return (T *)m_allocator.allocate(size * sizeof(T), alignof(T), AT);
+ return (T *)allocator_.allocate((size_t)size * sizeof(T), alignof(T), AT);
}
bool uses_inline_buffer() const
{
- return m_data == this->inline_buffer();
+ return data_ == inline_buffer_;
}
};
+/**
+ * Same as a normal Array, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
+ */
+template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
+using RawArray = Array<T, InlineBufferCapacity, RawAllocator>;
+
} // namespace blender
#endif /* __BLI_ARRAY_HH__ */
diff --git a/source/blender/blenlib/BLI_blenlib.h b/source/blender/blenlib/BLI_blenlib.h
index 6dd1abacf78..4ebef814337 100644
--- a/source/blender/blenlib/BLI_blenlib.h
+++ b/source/blender/blenlib/BLI_blenlib.h
@@ -28,7 +28,7 @@
* a call to a BLI function that is not prototyped here, please add a
* prototype here. The library offers mathematical operations (mainly
* vector and matrix calculus), an abstraction layer for file i/o,
- * functions for calculating Perlin noise, scanfilling services for
+ * functions for calculating Perlin noise, scan-filling services for
* triangles, and a system for guarded memory
* allocation/deallocation. There is also a patch to make MS Windows
* behave more or less Posix-compliant.
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh
index 432459c9998..72caa5b1118 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)
{
}
@@ -47,6 +51,25 @@ struct Color4f {
stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
return stream;
}
+
+ friend bool operator==(const Color4f &a, const Color4f &b)
+ {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+ }
+
+ friend bool operator!=(const Color4f &a, const Color4f &b)
+ {
+ return !(a == b);
+ }
+
+ uint64_t hash() const
+ {
+ uint64_t x1 = *(uint32_t *)&r;
+ uint64_t x2 = *(uint32_t *)&g;
+ uint64_t x3 = *(uint32_t *)&b;
+ uint64_t x4 = *(uint32_t *)&a;
+ return (x1 * 1283591) ^ (x2 * 850177) ^ (x3 * 735391) ^ (x4 * 442319);
+ }
};
struct Color4b {
@@ -85,6 +108,22 @@ struct Color4b {
stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
return stream;
}
+
+ friend bool operator==(const Color4b &a, const Color4b &b)
+ {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+ }
+
+ friend bool operator!=(const Color4b &a, const Color4b &b)
+ {
+ return !(a == b);
+ }
+
+ uint64_t hash() const
+ {
+ return ((uint64_t)r * 1283591) ^ ((uint64_t)g * 850177) ^ ((uint64_t)b * 735391) ^
+ ((uint64_t)a * 442319);
+ }
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_disjoint_set.hh b/source/blender/blenlib/BLI_disjoint_set.hh
new file mode 100644
index 00000000000..e0580709a44
--- /dev/null
+++ b/source/blender/blenlib/BLI_disjoint_set.hh
@@ -0,0 +1,106 @@
+/*
+ * 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 __BLI_DISJOINT_SET_HH__
+#define __BLI_DISJOINT_SET_HH__
+
+/** \file
+ * \ingroup bli
+ *
+ * This implements the disjoint set data structure with path compression and union by rank.
+ */
+
+#include "BLI_array.hh"
+
+namespace blender {
+
+class DisjointSet {
+ private:
+ Array<int64_t> parents_;
+ Array<int64_t> ranks_;
+
+ public:
+ /**
+ * Create a new disjoint set with the given size. Initially, every element is in a separate set.
+ */
+ DisjointSet(int64_t size) : parents_(size), ranks_(size, 0)
+ {
+ BLI_assert(size >= 0);
+ for (int64_t i = 0; i < size; i++) {
+ parents_[i] = i;
+ }
+ }
+
+ /**
+ * Join the sets containing elements x and y. Nothing happens when they have been in the same set
+ * before.
+ */
+ void join(int64_t x, int64_t y)
+ {
+ int64_t root1 = this->find_root(x);
+ int64_t root2 = this->find_root(y);
+
+ /* x and y are in the same set already. */
+ if (root1 == root2) {
+ return;
+ }
+
+ /* Implement union by rank heuristic. */
+ if (ranks_[root1] < ranks_[root2]) {
+ std::swap(root1, root2);
+ }
+ parents_[root2] = root1;
+
+ if (ranks_[root1] == ranks_[root2]) {
+ ranks_[root1]++;
+ }
+ }
+
+ /**
+ * Return true when x and y are in the same set.
+ */
+ bool in_same_set(int64_t x, int64_t y)
+ {
+ int64_t root1 = this->find_root(x);
+ int64_t root2 = this->find_root(y);
+ return root1 == root2;
+ }
+
+ /**
+ * Find the element that represents the set containing x currently.
+ */
+ int64_t find_root(int64_t x)
+ {
+ /* Find root by following parents. */
+ int64_t root = x;
+ while (parents_[root] != root) {
+ root = parents_[root];
+ }
+
+ /* Compress path. */
+ while (parents_[x] != root) {
+ int64_t parent = parents_[x];
+ parents_[x] = root;
+ x = parent;
+ }
+
+ return root;
+ }
+};
+
+} // namespace blender
+
+#endif /* __BLI_DISJOINT_SET_HH__ */
diff --git a/source/blender/blenlib/BLI_dot_export.hh b/source/blender/blenlib/BLI_dot_export.hh
index 67af4391a55..0870d8c4c30 100644
--- a/source/blender/blenlib/BLI_dot_export.hh
+++ b/source/blender/blenlib/BLI_dot_export.hh
@@ -25,17 +25,16 @@
*/
#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 blender::dot {
class Graph;
class DirectedGraph;
@@ -49,25 +48,25 @@ class AttributeList;
class AttributeList {
private:
- Map<std::string, std::string> m_attributes;
+ Map<std::string, std::string> attributes_;
public:
void export__as_bracket_list(std::stringstream &ss) const;
void set(StringRef key, StringRef value)
{
- m_attributes.add_overwrite(key, value);
+ attributes_.add_overwrite(key, value);
}
};
class Graph {
private:
- AttributeList m_attributes;
- Vector<std::unique_ptr<Node>> m_nodes;
- Vector<std::unique_ptr<Cluster>> m_clusters;
+ AttributeList attributes_;
+ Vector<std::unique_ptr<Node>> nodes_;
+ Vector<std::unique_ptr<Cluster>> clusters_;
- Set<Node *> m_top_level_nodes;
- Set<Cluster *> m_top_level_clusters;
+ Set<Node *> top_level_nodes_;
+ Set<Cluster *> top_level_clusters_;
friend Cluster;
friend Node;
@@ -80,7 +79,7 @@ class Graph {
void set_attribute(StringRef key, StringRef value)
{
- m_attributes.set(key, value);
+ attributes_.set(key, value);
}
void set_rankdir(Attr_rankdir rankdir)
@@ -93,16 +92,16 @@ class Graph {
class Cluster {
private:
- AttributeList m_attributes;
- Graph &m_graph;
- Cluster *m_parent = nullptr;
- Set<Cluster *> m_children;
- Set<Node *> m_nodes;
+ AttributeList attributes_;
+ Graph &graph_;
+ Cluster *parent_ = nullptr;
+ Set<Cluster *> children_;
+ Set<Node *> nodes_;
friend Graph;
friend Node;
- Cluster(Graph &graph) : m_graph(graph)
+ Cluster(Graph &graph) : graph_(graph)
{
}
@@ -111,7 +110,7 @@ class Cluster {
void set_attribute(StringRef key, StringRef value)
{
- m_attributes.set(key, value);
+ attributes_.set(key, value);
}
void set_parent_cluster(Cluster *cluster);
@@ -125,25 +124,25 @@ class Cluster {
class Node {
private:
- AttributeList m_attributes;
- Graph &m_graph;
- Cluster *m_cluster = nullptr;
+ AttributeList attributes_;
+ Graph &graph_;
+ Cluster *cluster_ = nullptr;
friend Graph;
- Node(Graph &graph) : m_graph(graph)
+ Node(Graph &graph) : graph_(graph)
{
}
public:
const AttributeList &attributes() const
{
- return m_attributes;
+ return attributes_;
}
AttributeList &attributes()
{
- return m_attributes;
+ return attributes_;
}
void set_parent_cluster(Cluster *cluster);
@@ -154,7 +153,7 @@ class Node {
void set_attribute(StringRef key, StringRef value)
{
- m_attributes.set(key, value);
+ attributes_.set(key, value);
}
void set_shape(Attr_shape shape)
@@ -176,7 +175,7 @@ class Node {
class UndirectedGraph final : public Graph {
private:
- Vector<std::unique_ptr<UndirectedEdge>> m_edges;
+ Vector<std::unique_ptr<UndirectedEdge>> edges_;
public:
std::string to_dot_string() const;
@@ -186,7 +185,7 @@ class UndirectedGraph final : public Graph {
class DirectedGraph final : public Graph {
private:
- Vector<std::unique_ptr<DirectedEdge>> m_edges;
+ Vector<std::unique_ptr<DirectedEdge>> edges_;
public:
std::string to_dot_string() const;
@@ -196,12 +195,12 @@ class DirectedGraph final : public Graph {
class NodePort {
private:
- Node *m_node;
- Optional<std::string> m_port_name;
+ Node *node_;
+ std::optional<std::string> port_name_;
public:
- NodePort(Node &node, Optional<std::string> port_name = {})
- : m_node(&node), m_port_name(std::move(port_name))
+ NodePort(Node &node, std::optional<std::string> port_name = {})
+ : node_(&node), port_name_(std::move(port_name))
{
}
@@ -210,18 +209,18 @@ class NodePort {
class Edge : blender::NonCopyable, blender::NonMovable {
protected:
- AttributeList m_attributes;
- NodePort m_a;
- NodePort m_b;
+ AttributeList attributes_;
+ NodePort a_;
+ NodePort b_;
public:
- Edge(NodePort a, NodePort b) : m_a(std::move(a)), m_b(std::move(b))
+ Edge(NodePort a, NodePort b) : a_(std::move(a)), b_(std::move(b))
{
}
void set_attribute(StringRef key, StringRef value)
{
- m_attributes.set(key, value);
+ attributes_.set(key, value);
}
void set_arrowhead(Attr_arrowType type)
@@ -262,7 +261,7 @@ std::string color_attr_from_hsv(float h, float s, float v);
class NodeWithSocketsRef {
private:
- Node *m_node;
+ Node *node_;
public:
NodeWithSocketsRef(Node &node,
@@ -270,20 +269,24 @@ class NodeWithSocketsRef {
Span<std::string> input_names,
Span<std::string> output_names);
- NodePort input(uint index) const
+ Node &node()
+ {
+ return *node_;
+ }
+
+ NodePort input(int index) const
{
std::string port = "\"in" + std::to_string(index) + "\"";
- return NodePort(*m_node, port);
+ return NodePort(*node_, port);
}
- NodePort output(uint index) const
+ NodePort output(int index) const
{
std::string port = "\"out" + std::to_string(index) + "\"";
- return NodePort(*m_node, port);
+ return NodePort(*node_, port);
}
};
-} // namespace DotExport
-} // namespace blender
+} // namespace blender::dot
#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..94c7025b2a6 100644
--- a/source/blender/blenlib/BLI_dot_export_attribute_enums.hh
+++ b/source/blender/blenlib/BLI_dot_export_attribute_enums.hh
@@ -19,8 +19,7 @@
#include "BLI_string_ref.hh"
-namespace blender {
-namespace DotExport {
+namespace blender ::dot {
enum class Attr_rankdir {
LeftToRight,
@@ -119,7 +118,6 @@ inline StringRef dirType_to_string(Attr_dirType value)
return "";
}
-} // namespace DotExport
-} // namespace blender
+} // namespace blender::dot
#endif /* __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_HH__ */
diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh
index 94da5d18ad2..5fe9d1b8ca9 100644
--- a/source/blender/blenlib/BLI_float2.hh
+++ b/source/blender/blenlib/BLI_float2.hh
@@ -48,6 +48,34 @@ struct float2 {
return &x;
}
+ float2 &operator+=(const float2 &other)
+ {
+ x += other.x;
+ y += other.y;
+ return *this;
+ }
+
+ float2 &operator-=(const float2 &other)
+ {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+ }
+
+ float2 &operator*=(float factor)
+ {
+ x *= factor;
+ y *= factor;
+ return *this;
+ }
+
+ float2 &operator/=(float divisor)
+ {
+ x /= divisor;
+ y /= divisor;
+ return *this;
+ }
+
friend float2 operator+(const float2 &a, const float2 &b)
{
return {a.x + b.x, a.y + b.y};
@@ -79,6 +107,16 @@ struct float2 {
stream << "(" << v.x << ", " << v.y << ")";
return stream;
}
+
+ friend bool operator==(const float2 &a, const float2 &b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend bool operator!=(const float2 &a, const float2 &b)
+ {
+ return !(a == b);
+ }
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh
index 7ef4f1b4973..a0979bc75bd 100644
--- a/source/blender/blenlib/BLI_float3.hh
+++ b/source/blender/blenlib/BLI_float3.hh
@@ -58,56 +58,6 @@ struct float3 {
return &x;
}
- float normalize_and_get_length()
- {
- return normalize_v3(*this);
- }
-
- float3 normalized() const
- {
- float3 result;
- normalize_v3_v3(result, *this);
- return result;
- }
-
- float length() const
- {
- return len_v3(*this);
- }
-
- float length_squared() const
- {
- return len_squared_v3(*this);
- }
-
- void reflect(const float3 &normal)
- {
- *this = this->reflected(normal);
- }
-
- float3 reflected(const float3 &normal) const
- {
- float3 result;
- reflect_v3_v3v3(result, *this, normal);
- return result;
- }
-
- static float3 safe_divide(const float3 &a, const float3 &b)
- {
- float3 result;
- result.x = (b.x == 0.0f) ? 0.0f : a.x / b.x;
- result.y = (b.y == 0.0f) ? 0.0f : a.y / b.y;
- result.z = (b.z == 0.0f) ? 0.0f : a.z / b.z;
- return result;
- }
-
- void invert()
- {
- x = -x;
- y = -y;
- z = -z;
- }
-
friend float3 operator+(const float3 &a, const float3 &b)
{
return {a.x + b.x, a.y + b.y, a.z + b.z};
@@ -178,6 +128,85 @@ struct float3 {
return stream;
}
+ friend bool operator==(const float3 &a, const float3 &b)
+ {
+ return a.x == b.x && a.y == b.y && a.z == b.z;
+ }
+
+ friend bool operator!=(const float3 &a, const float3 &b)
+ {
+ return !(a == b);
+ }
+
+ float normalize_and_get_length()
+ {
+ return normalize_v3(*this);
+ }
+
+ /**
+ * Normalizes the vector in place.
+ */
+ void normalize()
+ {
+ normalize_v3(*this);
+ }
+
+ /**
+ * Returns a normalized vector. The original vector is not changed.
+ */
+ float3 normalized() const
+ {
+ float3 result;
+ normalize_v3_v3(result, *this);
+ return result;
+ }
+
+ float length() const
+ {
+ return len_v3(*this);
+ }
+
+ float length_squared() const
+ {
+ return len_squared_v3(*this);
+ }
+
+ void reflect(const float3 &normal)
+ {
+ *this = this->reflected(normal);
+ }
+
+ float3 reflected(const float3 &normal) const
+ {
+ float3 result;
+ reflect_v3_v3v3(result, *this, normal);
+ return result;
+ }
+
+ static float3 safe_divide(const float3 &a, const float3 &b)
+ {
+ float3 result;
+ result.x = (b.x == 0.0f) ? 0.0f : a.x / b.x;
+ result.y = (b.y == 0.0f) ? 0.0f : a.y / b.y;
+ result.z = (b.z == 0.0f) ? 0.0f : a.z / b.z;
+ return result;
+ }
+
+ void invert()
+ {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ uint64_t hash() const
+ {
+ uint64_t x1 = *(uint32_t *)&x;
+ uint64_t x2 = *(uint32_t *)&y;
+ uint64_t x3 = *(uint32_t *)&z;
+ return (x1 * 435109) ^ (x2 * 380867) ^ (x3 * 1059217);
+ }
+
static float dot(const float3 &a, const float3 &b)
{
return a.x * b.x + a.y * b.y + a.z * b.z;
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 0abfb751ebf..b4f12f17cc2 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -46,23 +46,6 @@ struct float4x4 {
return (const float *)this;
}
- float4x4 inverted() const
- {
- float result[4][4];
- invert_m4_m4(result, values);
- return result;
- }
-
- /**
- * Matrix inversion can be implemented more efficiently for affine matrices.
- */
- float4x4 inverted_affine() const
- {
- BLI_assert(values[0][3] == 0.0f && values[1][3] == 0.0f && values[2][3] == 0.0f &&
- values[3][3] == 1.0f);
- return this->inverted();
- }
-
friend float4x4 operator*(const float4x4 &a, const float4x4 &b)
{
float4x4 result;
@@ -86,6 +69,35 @@ struct float4x4 {
return m * float3(v);
}
+ float4x4 inverted() const
+ {
+ float4x4 result;
+ invert_m4_m4(result.values, values);
+ return result;
+ }
+
+ /**
+ * Matrix inversion can be implemented more efficiently for affine matrices.
+ */
+ float4x4 inverted_affine() const
+ {
+ BLI_assert(values[0][3] == 0.0f && values[1][3] == 0.0f && values[2][3] == 0.0f &&
+ values[3][3] == 1.0f);
+ return this->inverted();
+ }
+
+ float4x4 transposed() const
+ {
+ float4x4 result;
+ transpose_m4_m4(result.values, values);
+ return result;
+ }
+
+ float4x4 inverted_transposed_affine() const
+ {
+ return this->inverted_affine().transposed();
+ }
+
struct float3x3_ref {
const float4x4 &data;
@@ -108,6 +120,16 @@ struct float4x4 {
interp_m4_m4m4(result, a.values, b.values, t);
return result;
}
+
+ uint64_t hash() const
+ {
+ uint64_t h = 435109;
+ for (int i = 0; i < 16; i++) {
+ float value = ((const float *)this)[i];
+ h = h * 33 + (*(uint32_t *)&value);
+ }
+ return h;
+ }
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h
index 141c631381b..31a9658bd7e 100644
--- a/source/blender/blenlib/BLI_ghash.h
+++ b/source/blender/blenlib/BLI_ghash.h
@@ -371,20 +371,6 @@ unsigned int BLI_ghashutil_uinthash_v4_murmur(const unsigned int key[4]);
bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b);
#define BLI_ghashutil_inthash_v4_cmp BLI_ghashutil_uinthash_v4_cmp
-unsigned int BLI_ghashutil_uinthash_v2(const unsigned int key[2]);
-#define BLI_ghashutil_inthash_v2(key) \
- (CHECK_TYPE_ANY(key, int *, const int *), BLI_ghashutil_uinthash_v2((const unsigned int *)key))
-#define BLI_ghashutil_inthash_v2_p ((GSetHashFP)BLI_ghashutil_uinthash_v2)
-#define BLI_ghashutil_uinthash_v2_p ((GSetHashFP)BLI_ghashutil_uinthash_v2)
-unsigned int BLI_ghashutil_uinthash_v2_murmur(const unsigned int key[2]);
-#define BLI_ghashutil_inthash_v2_murmur(key) \
- (CHECK_TYPE_ANY(key, int *, const int *), \
- BLI_ghashutil_uinthash_v2_murmur((const unsigned int *)key))
-#define BLI_ghashutil_inthash_v2_p_murmur ((GSetHashFP)BLI_ghashutil_uinthash_v2_murmur)
-#define BLI_ghashutil_uinthash_v2_p_murmur ((GSetHashFP)BLI_ghashutil_uinthash_v2_murmur)
-bool BLI_ghashutil_uinthash_v2_cmp(const void *a, const void *b);
-#define BLI_ghashutil_inthash_v2_cmp BLI_ghashutil_uinthash_v2_cmp
-
typedef struct GHashPair {
const void *first;
const void *second;
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index 57d5f7f9d8a..b14a4ca933c 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,43 +32,43 @@
*
* 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
+ * The computed hash is an unsigned 64 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.
*
* There are three main ways to provide a hash table implementation with a custom hash function.
*
* - 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
+ * member function to it. The function should return `uint64_t` and take no arguments. This
+ * 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> {
- * uint32_t operator()(const TheType &value) const {
+ * uint64_t operator()(const TheType &value) const {
* return ...;
* }
* };
*
* - When you want to provide a different hash function for a type that already has a default hash
* function: Implement a struct like the one below and pass it as template parameter to the hash
- * table explicitely.
+ * table explicitly.
*
* struct MyCustomHash {
- * uint32_t operator()(const TheType &value) const {
+ * uint64_t operator()(const TheType &value) const {
* return ...;
* }
* };
@@ -86,22 +86,32 @@
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
+ uint64_t operator()(const T &value) const
{
return value.hash();
}
};
+/**
+ * Use the same hash function for const and non const variants of a type.
+ */
+template<typename T> struct DefaultHash<const T> {
+ uint64_t operator()(const T &value) const
+ {
+ return DefaultHash<T>{}(value);
+ }
+};
+
#define TRIVIAL_DEFAULT_INT_HASH(TYPE) \
template<> struct DefaultHash<TYPE> { \
- uint32_t operator()(TYPE value) const \
+ uint64_t operator()(TYPE value) const \
{ \
- return (uint32_t)value; \
+ return (uint64_t)value; \
} \
}
@@ -117,36 +127,29 @@ TRIVIAL_DEFAULT_INT_HASH(int16_t);
TRIVIAL_DEFAULT_INT_HASH(uint16_t);
TRIVIAL_DEFAULT_INT_HASH(int32_t);
TRIVIAL_DEFAULT_INT_HASH(uint32_t);
-
-template<> struct DefaultHash<uint64_t> {
- uint32_t operator()(uint64_t value) const
- {
- uint32_t low = (uint32_t)value;
- uint32_t high = (uint32_t)(value >> 32);
- return low ^ (high * 0x45d9f3b);
- }
-};
-
-template<> struct DefaultHash<int64_t> {
- uint32_t operator()(uint64_t value) const
- {
- return DefaultHash<uint64_t>{}((uint64_t)value);
- }
-};
+TRIVIAL_DEFAULT_INT_HASH(int64_t);
+TRIVIAL_DEFAULT_INT_HASH(uint64_t);
/**
* One should try to avoid using floats as keys in hash tables, but sometimes it is convenient.
*/
template<> struct DefaultHash<float> {
- uint32_t operator()(float value) const
+ uint64_t operator()(float value) const
{
return *(uint32_t *)&value;
}
};
-inline uint32_t hash_string(StringRef str)
+template<> struct DefaultHash<bool> {
+ uint64_t operator()(bool value) const
+ {
+ return (uint64_t)(value != false) * 1298191;
+ }
+};
+
+inline uint64_t hash_string(StringRef str)
{
- uint32_t hash = 5381;
+ uint64_t hash = 5381;
for (char c : str) {
hash = hash * 33 + c;
}
@@ -155,24 +158,24 @@ 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
+ uint64_t operator()(StringRef value) const
{
return hash_string(value);
}
};
template<> struct DefaultHash<StringRef> {
- uint32_t operator()(StringRef value) const
+ uint64_t operator()(StringRef value) const
{
return hash_string(value);
}
};
template<> struct DefaultHash<StringRefNull> {
- uint32_t operator()(StringRef value) const
+ uint64_t operator()(StringRef value) const
{
return hash_string(value);
}
@@ -182,26 +185,26 @@ template<> struct DefaultHash<StringRefNull> {
* While we cannot guarantee that the lower 4 bits of a pointer are zero, it is often the case.
*/
template<typename T> struct DefaultHash<T *> {
- uint32_t operator()(const T *value) const
+ uint64_t operator()(const T *value) const
{
uintptr_t ptr = (uintptr_t)value;
- uint32_t hash = (uint32_t)(ptr >> 4);
+ uint64_t hash = (uint64_t)(ptr >> 4);
return hash;
}
};
template<typename T> struct DefaultHash<std::unique_ptr<T>> {
- uint32_t operator()(const std::unique_ptr<T> &value) const
+ uint64_t operator()(const std::unique_ptr<T> &value) const
{
return DefaultHash<T *>{}(value.get());
}
};
template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> {
- uint32_t operator()(const std::pair<T1, T2> &value) const
+ uint64_t operator()(const std::pair<T1, T2> &value) const
{
- uint32_t hash1 = DefaultHash<T1>{}(value.first);
- uint32_t hash2 = DefaultHash<T2>{}(value.second);
+ uint64_t hash1 = DefaultHash<T1>{}(value.first);
+ uint64_t hash2 = DefaultHash<T2>{}(value.second);
return hash1 ^ (hash2 * 33);
}
};
diff --git a/source/blender/blenlib/BLI_hash_tables.hh b/source/blender/blenlib/BLI_hash_tables.hh
index b565b396a7a..5d8f8862a09 100644
--- a/source/blender/blenlib/BLI_hash_tables.hh
+++ b/source/blender/blenlib/BLI_hash_tables.hh
@@ -30,6 +30,7 @@
#include "BLI_math_base.h"
#include "BLI_memory_utils.hh"
#include "BLI_string.h"
+#include "BLI_string_ref.hh"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
@@ -38,61 +39,67 @@ 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)
+inline constexpr int64_t is_power_of_2_constexpr(const int64_t x)
{
- return (n & (n - 1)) == 0;
+ BLI_assert(x >= 0);
+ return (x & (x - 1)) == 0;
}
-inline constexpr uint32_t log2_floor_u_constexpr(uint32_t x)
+inline constexpr int64_t log2_floor_constexpr(const int64_t x)
{
- return x <= 1 ? 0 : 1 + log2_floor_u_constexpr(x >> 1);
+ BLI_assert(x >= 0);
+ return x <= 1 ? 0 : 1 + log2_floor_constexpr(x >> 1);
}
-inline constexpr uint32_t log2_ceil_u_constexpr(uint32_t x)
+inline constexpr int64_t log2_ceil_constexpr(const int64_t x)
{
- return (is_power_of_2_i_constexpr((int)x)) ? log2_floor_u_constexpr(x) :
- log2_floor_u_constexpr(x) + 1;
+ BLI_assert(x >= 0);
+ return (is_power_of_2_constexpr((int)x)) ? log2_floor_constexpr(x) : log2_floor_constexpr(x) + 1;
}
-inline constexpr uint32_t power_of_2_max_u_constexpr(uint32_t x)
+inline constexpr int64_t power_of_2_max_constexpr(const int64_t x)
{
- return 1u << log2_ceil_u_constexpr(x);
+ BLI_assert(x >= 0);
+ return 1ll << log2_ceil_constexpr(x);
}
-template<typename IntT> inline constexpr IntT ceil_division(IntT x, IntT y)
+template<typename IntT> inline constexpr IntT ceil_division(const IntT x, const IntT y)
{
- BLI_STATIC_ASSERT(!std::is_signed<IntT>::value, "");
+ BLI_assert(x >= 0);
+ BLI_assert(y >= 0);
return x / y + ((x % y) != 0);
}
-template<typename IntT> inline constexpr IntT floor_division(IntT x, IntT y)
+template<typename IntT> inline constexpr IntT floor_division(const IntT x, const IntT y)
{
- BLI_STATIC_ASSERT(!std::is_signed<IntT>::value, "");
+ BLI_assert(x >= 0);
+ BLI_assert(y >= 0);
return x / y;
}
-inline constexpr uint32_t ceil_division_by_fraction(uint32_t x,
- uint32_t numerator,
- uint32_t denominator)
+inline constexpr int64_t ceil_division_by_fraction(const int64_t x,
+ const int64_t numerator,
+ const int64_t denominator)
{
- return (uint32_t)ceil_division((uint64_t)x * (uint64_t)denominator, (uint64_t)numerator);
+ return (int64_t)ceil_division((uint64_t)x * (uint64_t)denominator, (uint64_t)numerator);
}
-inline constexpr uint32_t floor_multiplication_with_fraction(uint32_t x,
- uint32_t numerator,
- uint32_t denominator)
+inline constexpr int64_t floor_multiplication_with_fraction(const int64_t x,
+ const int64_t numerator,
+ const int64_t denominator)
{
- return (uint32_t)((uint64_t)x * (uint64_t)numerator / (uint64_t)denominator);
+ return (int64_t)((uint64_t)x * (uint64_t)numerator / (uint64_t)denominator);
}
-inline constexpr uint32_t total_slot_amount_for_usable_slots(uint32_t min_usable_slots,
- uint32_t max_load_factor_numerator,
- uint32_t max_load_factor_denominator)
+inline constexpr int64_t total_slot_amount_for_usable_slots(
+ const int64_t min_usable_slots,
+ const int64_t max_load_factor_numerator,
+ const int64_t max_load_factor_denominator)
{
- return power_of_2_max_u_constexpr(ceil_division_by_fraction(
+ return power_of_2_max_constexpr(ceil_division_by_fraction(
min_usable_slots, max_load_factor_numerator, max_load_factor_denominator));
}
@@ -108,37 +115,37 @@ inline constexpr uint32_t total_slot_amount_for_usable_slots(uint32_t min_usable
class LoadFactor {
private:
- uint8_t m_numerator;
- uint8_t m_denominator;
+ uint8_t numerator_;
+ uint8_t denominator_;
public:
LoadFactor(uint8_t numerator, uint8_t denominator)
- : m_numerator(numerator), m_denominator(denominator)
+ : numerator_(numerator), denominator_(denominator)
{
BLI_assert(numerator > 0);
BLI_assert(numerator < denominator);
}
- void compute_total_and_usable_slots(uint32_t min_total_slots,
- uint32_t min_usable_slots,
- uint32_t *r_total_slots,
- uint32_t *r_usable_slots) const
+ void compute_total_and_usable_slots(int64_t min_total_slots,
+ int64_t min_usable_slots,
+ int64_t *r_total_slots,
+ int64_t *r_usable_slots) const
{
BLI_assert(is_power_of_2_i((int)min_total_slots));
- uint32_t total_slots = this->compute_total_slots(min_usable_slots, m_numerator, m_denominator);
+ int64_t total_slots = this->compute_total_slots(min_usable_slots, numerator_, denominator_);
total_slots = std::max(total_slots, min_total_slots);
- uint32_t usable_slots = floor_multiplication_with_fraction(
- total_slots, m_numerator, m_denominator);
+ const int64_t usable_slots = floor_multiplication_with_fraction(
+ total_slots, numerator_, denominator_);
BLI_assert(min_usable_slots <= usable_slots);
*r_total_slots = total_slots;
*r_usable_slots = usable_slots;
}
- static constexpr uint32_t compute_total_slots(uint32_t min_usable_slots,
- uint8_t numerator,
- uint8_t denominator)
+ static constexpr int64_t compute_total_slots(int64_t min_usable_slots,
+ uint8_t numerator,
+ uint8_t denominator)
{
return total_slot_amount_for_usable_slots(min_usable_slots, numerator, denominator);
}
@@ -157,10 +164,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.
*
* \{ */
@@ -260,72 +267,72 @@ template<typename Pointer> struct PointerKeyInfo {
class HashTableStats {
private:
- Vector<uint32_t> m_keys_by_collision_count;
- uint32_t m_total_collisions;
- float m_average_collisions;
- uint32_t m_size;
- uint32_t m_capacity;
- uint32_t m_removed_amount;
- float m_load_factor;
- float m_removed_load_factor;
- uint32_t m_size_per_element;
- uint32_t m_size_in_bytes;
- const void *m_address;
+ Vector<int64_t> keys_by_collision_count_;
+ int64_t total_collisions_;
+ float average_collisions_;
+ int64_t size_;
+ int64_t capacity_;
+ int64_t removed_amount_;
+ float load_factor_;
+ float removed_load_factor_;
+ int64_t size_per_element_;
+ int64_t size_in_bytes_;
+ const void *address_;
public:
/**
* Requires that the hash table has the following methods:
- * - count_collisions(key) -> uint32_t
- * - size() -> uint32_t
- * - capacity() -> uint32_t
- * - removed_amount() -> uint32_t
- * - size_per_element() -> uint32_t
- * - size_in_bytes() -> uint32_t
+ * - count_collisions(key) -> int64_t
+ * - size() -> int64_t
+ * - capacity() -> int64_t
+ * - removed_amount() -> int64_t
+ * - size_per_element() -> int64_t
+ * - size_in_bytes() -> int64_t
*/
template<typename HashTable, typename Keys>
HashTableStats(const HashTable &hash_table, const Keys &keys)
{
- m_total_collisions = 0;
- m_size = hash_table.size();
- m_capacity = hash_table.capacity();
- m_removed_amount = hash_table.removed_amount();
- m_size_per_element = hash_table.size_per_element();
- m_size_in_bytes = hash_table.size_in_bytes();
- m_address = (const void *)&hash_table;
+ total_collisions_ = 0;
+ size_ = hash_table.size();
+ capacity_ = hash_table.capacity();
+ removed_amount_ = hash_table.removed_amount();
+ size_per_element_ = hash_table.size_per_element();
+ size_in_bytes_ = hash_table.size_in_bytes();
+ address_ = (const void *)&hash_table;
for (const auto &key : keys) {
- uint32_t collisions = hash_table.count_collisions(key);
- if (m_keys_by_collision_count.size() <= collisions) {
- m_keys_by_collision_count.append_n_times(
- 0, collisions - m_keys_by_collision_count.size() + 1);
+ int64_t collisions = hash_table.count_collisions(key);
+ if (keys_by_collision_count_.size() <= collisions) {
+ keys_by_collision_count_.append_n_times(0,
+ collisions - keys_by_collision_count_.size() + 1);
}
- m_keys_by_collision_count[collisions]++;
- m_total_collisions += collisions;
+ keys_by_collision_count_[collisions]++;
+ total_collisions_ += collisions;
}
- m_average_collisions = (m_size == 0) ? 0 : (float)m_total_collisions / (float)m_size;
- m_load_factor = (float)m_size / (float)m_capacity;
- m_removed_load_factor = (float)m_removed_amount / (float)m_capacity;
+ average_collisions_ = (size_ == 0) ? 0 : (float)total_collisions_ / (float)size_;
+ load_factor_ = (float)size_ / (float)capacity_;
+ removed_load_factor_ = (float)removed_amount_ / (float)capacity_;
}
void print(StringRef name = "")
{
std::cout << "Hash Table Stats: " << name << "\n";
- std::cout << " Address: " << m_address << "\n";
- std::cout << " Total Slots: " << m_capacity << "\n";
- std::cout << " Occupied Slots: " << m_size << " (" << m_load_factor * 100.0f << " %)\n";
- std::cout << " Removed Slots: " << m_removed_amount << " (" << m_removed_load_factor * 100.0f
+ std::cout << " Address: " << address_ << "\n";
+ std::cout << " Total Slots: " << capacity_ << "\n";
+ std::cout << " Occupied Slots: " << size_ << " (" << load_factor_ * 100.0f << " %)\n";
+ std::cout << " Removed Slots: " << removed_amount_ << " (" << removed_load_factor_ * 100.0f
<< " %)\n";
char memory_size_str[15];
- BLI_str_format_byte_unit(memory_size_str, m_size_in_bytes, true);
+ BLI_str_format_byte_unit(memory_size_str, size_in_bytes_, true);
std::cout << " Size: ~" << memory_size_str << "\n";
- std::cout << " Size per Slot: " << m_size_per_element << " bytes\n";
+ std::cout << " Size per Slot: " << size_per_element_ << " bytes\n";
- std::cout << " Average Collisions: " << m_average_collisions << "\n";
- for (uint32_t collision_count : m_keys_by_collision_count.index_range()) {
+ std::cout << " Average Collisions: " << average_collisions_ << "\n";
+ for (int64_t collision_count : keys_by_collision_count_.index_range()) {
std::cout << " " << collision_count
- << " Collisions: " << m_keys_by_collision_count[collision_count] << "\n";
+ << " Collisions: " << keys_by_collision_count_[collision_count] << "\n";
}
}
};
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index cc1bf05f936..ff271faa0c2 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -46,7 +46,7 @@ namespace blender {
class IndexMask {
private:
/* The underlying reference to sorted integers. */
- Span<uint> m_indices;
+ Span<int64_t> indices_;
public:
/* Creates an IndexMask that contains no indices. */
@@ -57,10 +57,10 @@ class IndexMask {
* This constructor asserts that the given integers are in ascending order and that there are no
* duplicates.
*/
- IndexMask(Span<uint> indices) : m_indices(indices)
+ IndexMask(Span<int64_t> indices) : indices_(indices)
{
#ifdef DEBUG
- for (uint i = 1; i < indices.size(); i++) {
+ for (int64_t i = 1; i < indices.size(); i++) {
BLI_assert(indices[i - 1] < indices[i]);
}
#endif
@@ -70,7 +70,7 @@ class IndexMask {
* Use this method when you know that no indices are skipped. It is more efficient than preparing
* an integer array all the time.
*/
- IndexMask(IndexRange range) : m_indices(range.as_span())
+ IndexMask(IndexRange range) : indices_(range.as_span())
{
}
@@ -84,58 +84,58 @@ class IndexMask {
* Do this:
* do_something_with_an_index_mask({3, 4, 5});
*/
- IndexMask(const std::initializer_list<uint> &indices) : IndexMask(Span<uint>(indices))
+ IndexMask(const std::initializer_list<int64_t> &indices) : IndexMask(Span<int64_t>(indices))
{
}
/**
* Creates an IndexMask that references the indices [0, n-1].
*/
- explicit IndexMask(uint n) : IndexMask(IndexRange(n))
+ explicit IndexMask(int64_t n) : IndexMask(IndexRange(n))
{
}
- operator Span<uint>() const
+ operator Span<int64_t>() const
{
- return m_indices;
+ return indices_;
}
- const uint *begin() const
+ const int64_t *begin() const
{
- return m_indices.begin();
+ return indices_.begin();
}
- const uint *end() const
+ const int64_t *end() const
{
- return m_indices.end();
+ return indices_.end();
}
/**
* Returns the n-th index referenced by this IndexMask. The `index_mask` method returns an
* IndexRange containing all indices that can be used as parameter here.
*/
- uint operator[](uint n) const
+ int64_t operator[](int64_t n) const
{
- return m_indices[n];
+ return indices_[n];
}
/**
* Returns the minimum size an array has to have, if the integers in this IndexMask are going to
* be used as indices in that array.
*/
- uint min_array_size() const
+ int64_t min_array_size() const
{
- if (m_indices.size() == 0) {
+ if (indices_.size() == 0) {
return 0;
}
else {
- return m_indices.last() + 1;
+ return indices_.last() + 1;
}
}
- Span<uint> indices() const
+ Span<int64_t> indices() const
{
- return m_indices;
+ return indices_;
}
/**
@@ -143,7 +143,7 @@ class IndexMask {
*/
bool is_range() const
{
- return m_indices.size() > 0 && m_indices.last() - m_indices.first() == m_indices.size() - 1;
+ return indices_.size() > 0 && indices_.last() - indices_.first() == indices_.size() - 1;
}
/**
@@ -153,7 +153,7 @@ class IndexMask {
IndexRange as_range() const
{
BLI_assert(this->is_range());
- return IndexRange{m_indices.first(), m_indices.size()};
+ return IndexRange{indices_.first(), indices_.size()};
}
/**
@@ -167,12 +167,12 @@ class IndexMask {
{
if (this->is_range()) {
IndexRange range = this->as_range();
- for (uint i : range) {
+ for (int64_t i : range) {
callback(i);
}
}
else {
- for (uint i : m_indices) {
+ for (int64_t i : indices_) {
callback(i);
}
}
@@ -187,23 +187,23 @@ class IndexMask {
*/
IndexRange index_range() const
{
- return m_indices.index_range();
+ return indices_.index_range();
}
/**
* Returns the largest index that is referenced by this IndexMask.
*/
- uint last() const
+ int64_t last() const
{
- return m_indices.last();
+ return indices_.last();
}
/**
* Returns the number of indices referenced by this IndexMask.
*/
- uint size() const
+ int64_t size() const
{
- return m_indices.size();
+ return indices_.size();
}
};
diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh
index 25192429a5d..7c813f58b2c 100644
--- a/source/blender/blenlib/BLI_index_range.hh
+++ b/source/blender/blenlib/BLI_index_range.hh
@@ -27,29 +27,29 @@
* I'd argue that the second loop is more readable and less error prone than the first one. That is
* not necessarily always the case, but often it is.
*
- * for (uint i = 0; i < 10; i++) {
- * for (uint j = 0; j < 20; j++) {
- * for (uint k = 0; k < 30; k++) {
+ * for (int64_t i = 0; i < 10; i++) {
+ * for (int64_t j = 0; j < 20; j++) {
+ * for (int64_t k = 0; k < 30; k++) {
*
- * for (uint i : IndexRange(10)) {
- * for (uint j : IndexRange(20)) {
- * for (uint k : IndexRange(30)) {
+ * for (int64_t i : IndexRange(10)) {
+ * for (int64_t j : IndexRange(20)) {
+ * for (int64_t k : IndexRange(30)) {
*
* Some containers like blender::Vector have an index_range() method. This will return the
* IndexRange that contains all indices that can be used to access the container. This is
* particularly useful when you want to iterate over the indices and the elements (much like
* Python's enumerate(), just worse). Again, I think the second example here is better:
*
- * for (uint i = 0; i < my_vector_with_a_long_name.size(); i++) {
+ * for (int64_t i = 0; i < my_vector_with_a_long_name.size(); i++) {
* do_something(i, my_vector_with_a_long_name[i]);
*
- * for (uint i : my_vector_with_a_long_name.index_range()) {
+ * for (int64_t i : my_vector_with_a_long_name.index_range()) {
* do_something(i, my_vector_with_a_long_name[i]);
*
* Ideally this could be could be even closer to Python's enumerate(). We might get that in the
* future with newer C++ versions.
*
- * One other important feature is the as_span method. This method returns an Span<uint>
+ * One other important feature is the as_span method. This method returns an Span<int64_t>
* that contains the interval as individual numbers.
*/
@@ -70,68 +70,72 @@ template<typename T> class Span;
class IndexRange {
private:
- uint m_start = 0;
- uint m_size = 0;
+ int64_t start_ = 0;
+ int64_t size_ = 0;
public:
IndexRange() = default;
- explicit IndexRange(uint size) : m_start(0), m_size(size)
+ explicit IndexRange(int64_t size) : start_(0), size_(size)
{
+ BLI_assert(size >= 0);
}
- IndexRange(uint start, uint size) : m_start(start), m_size(size)
+ IndexRange(int64_t start, int64_t size) : start_(start), size_(size)
{
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
}
template<typename T>
- IndexRange(const tbb::blocked_range<T> &range) : m_start(range.begin()), m_size(range.size())
+ IndexRange(const tbb::blocked_range<T> &range) : start_(range.begin()), size_(range.size())
{
}
class Iterator {
private:
- uint m_current;
+ int64_t current_;
public:
- Iterator(uint current) : m_current(current)
+ Iterator(int64_t current) : current_(current)
{
}
Iterator &operator++()
{
- m_current++;
+ current_++;
return *this;
}
bool operator!=(const Iterator &iterator) const
{
- return m_current != iterator.m_current;
+ return current_ != iterator.current_;
}
- uint operator*() const
+ int64_t operator*() const
{
- return m_current;
+ return current_;
}
};
Iterator begin() const
{
- return Iterator(m_start);
+ return Iterator(start_);
}
Iterator end() const
{
- return Iterator(m_start + m_size);
+ return Iterator(start_ + size_);
}
/**
* Access an element in the range.
*/
- uint operator[](uint index) const
+ int64_t operator[](int64_t index) const
{
+ BLI_assert(index >= 0);
BLI_assert(index < this->size());
- return m_start + index;
+ return start_ + index;
}
/**
@@ -139,84 +143,88 @@ class IndexRange {
*/
friend bool operator==(IndexRange a, IndexRange b)
{
- return (a.m_size == b.m_size) && (a.m_start == b.m_start || a.m_size == 0);
+ return (a.size_ == b.size_) && (a.start_ == b.start_ || a.size_ == 0);
}
/**
* Get the amount of numbers in the range.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
* Create a new range starting at the end of the current one.
*/
- IndexRange after(uint n) const
+ IndexRange after(int64_t n) const
{
- return IndexRange(m_start + m_size, n);
+ BLI_assert(n >= 0);
+ return IndexRange(start_ + size_, n);
}
/**
* Create a new range that ends at the start of the current one.
*/
- IndexRange before(uint n) const
+ IndexRange before(int64_t n) const
{
- return IndexRange(m_start - n, n);
+ BLI_assert(n >= 0);
+ return IndexRange(start_ - n, n);
}
/**
* Get the first element in the range.
* Asserts when the range is empty.
*/
- uint first() const
+ int64_t first() const
{
BLI_assert(this->size() > 0);
- return m_start;
+ return start_;
}
/**
* Get the last element in the range.
* Asserts when the range is empty.
*/
- uint last() const
+ int64_t last() const
{
BLI_assert(this->size() > 0);
- return m_start + m_size - 1;
+ return start_ + size_ - 1;
}
/**
* Get the element one after the end. The returned value is undefined when the range is empty.
*/
- uint one_after_last() const
+ int64_t one_after_last() const
{
- return m_start + m_size;
+ return start_ + size_;
}
/**
* Get the first element in the range. The returned value is undefined when the range is empty.
*/
- uint start() const
+ int64_t start() const
{
- return m_start;
+ return start_;
}
/**
* Returns true when the range contains a certain number, otherwise false.
*/
- bool contains(uint value) const
+ bool contains(int64_t value) const
{
- return value >= m_start && value < m_start + m_size;
+ return value >= start_ && value < start_ + size_;
}
/**
- * 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
+ IndexRange slice(int64_t start, int64_t size) const
{
- uint new_start = m_start + start;
- BLI_assert(new_start + size <= m_start + m_size || size == 0);
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ int64_t new_start = start_ + start;
+ BLI_assert(new_start + size <= start_ + size_ || size == 0);
return IndexRange(new_start, size);
}
IndexRange slice(IndexRange range) const
@@ -227,7 +235,7 @@ class IndexRange {
/**
* Get read-only access to a memory buffer that contains the range as actual numbers.
*/
- Span<uint> as_span() const;
+ Span<int64_t> as_span() const;
friend std::ostream &operator<<(std::ostream &stream, IndexRange range)
{
diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h
index 70fa633eeac..9e4e30181b9 100644
--- a/source/blender/blenlib/BLI_kdopbvh.h
+++ b/source/blender/blenlib/BLI_kdopbvh.h
@@ -174,6 +174,8 @@ BVHTreeOverlap *BLI_bvhtree_overlap(const BVHTree *tree1,
BVHTree_OverlapCallback callback,
void *userdata);
+int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_tot);
+
int BLI_bvhtree_get_len(const BVHTree *tree);
int BLI_bvhtree_get_tree_type(const BVHTree *tree);
float BLI_bvhtree_get_epsilon(const BVHTree *tree);
diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh
index f968f9f15ce..39a3ed27f42 100644
--- a/source/blender/blenlib/BLI_linear_allocator.hh
+++ b/source/blender/blenlib/BLI_linear_allocator.hh
@@ -33,30 +33,30 @@ namespace blender {
template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopyable, NonMovable {
private:
- Allocator m_allocator;
- Vector<void *> m_owned_buffers;
- Vector<Span<char>> m_unused_borrowed_buffers;
+ Allocator allocator_;
+ Vector<void *> owned_buffers_;
+ Vector<Span<char>> unused_borrowed_buffers_;
- uintptr_t m_current_begin;
- uintptr_t m_current_end;
- uint m_next_min_alloc_size;
+ uintptr_t current_begin_;
+ uintptr_t current_end_;
+ int64_t next_min_alloc_size_;
#ifdef DEBUG
- uint m_debug_allocated_amount = 0;
+ int64_t debug_allocated_amount_ = 0;
#endif
public:
LinearAllocator()
{
- m_current_begin = 0;
- m_current_end = 0;
- m_next_min_alloc_size = 64;
+ current_begin_ = 0;
+ current_end_ = 0;
+ next_min_alloc_size_ = 64;
}
~LinearAllocator()
{
- for (void *ptr : m_owned_buffers) {
- m_allocator.deallocate(ptr);
+ for (void *ptr : owned_buffers_) {
+ allocator_.deallocate(ptr);
}
}
@@ -66,21 +66,23 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
*
* The alignment has to be a power of 2.
*/
- void *allocate(uint size, uint alignment)
+ void *allocate(const int64_t size, const int64_t alignment)
{
+ BLI_assert(size >= 0);
BLI_assert(alignment >= 1);
BLI_assert(is_power_of_2_i(alignment));
#ifdef DEBUG
- m_debug_allocated_amount += size;
+ debug_allocated_amount_ += size;
#endif
- uintptr_t alignment_mask = alignment - 1;
- uintptr_t potential_allocation_begin = (m_current_begin + alignment_mask) & ~alignment_mask;
- uintptr_t potential_allocation_end = potential_allocation_begin + size;
+ const uintptr_t alignment_mask = alignment - 1;
+ const uintptr_t potential_allocation_begin = (current_begin_ + alignment_mask) &
+ ~alignment_mask;
+ const uintptr_t potential_allocation_end = potential_allocation_begin + size;
- if (potential_allocation_end <= m_current_end) {
- m_current_begin = potential_allocation_end;
+ if (potential_allocation_end <= current_end_) {
+ current_begin_ = potential_allocation_end;
return (void *)potential_allocation_begin;
}
else {
@@ -104,7 +106,7 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
*
* This method only allocates memory and does not construct the instance.
*/
- template<typename T> MutableSpan<T> allocate_array(uint size)
+ template<typename T> MutableSpan<T> allocate_array(int64_t size)
{
return MutableSpan<T>((T *)this->allocate(sizeof(T) * size, alignof(T)), size);
}
@@ -140,22 +142,22 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
*/
StringRefNull copy_string(StringRef str)
{
- uint alloc_size = str.size() + 1;
+ const int64_t alloc_size = str.size() + 1;
char *buffer = (char *)this->allocate(alloc_size, 1);
str.copy(buffer, alloc_size);
return StringRefNull((const char *)buffer);
}
- MutableSpan<void *> allocate_elements_and_pointer_array(uint element_amount,
- uint element_size,
- uint element_alignment)
+ MutableSpan<void *> allocate_elements_and_pointer_array(int64_t element_amount,
+ int64_t element_size,
+ int64_t element_alignment)
{
void *pointer_buffer = this->allocate(element_amount * sizeof(void *), alignof(void *));
void *elements_buffer = this->allocate(element_amount * element_size, element_alignment);
MutableSpan<void *> pointers((void **)pointer_buffer, element_amount);
void *next_element_buffer = elements_buffer;
- for (uint i : IndexRange(element_amount)) {
+ for (int64_t i : IndexRange(element_amount)) {
pointers[i] = next_element_buffer;
next_element_buffer = POINTER_OFFSET(next_element_buffer, element_size);
}
@@ -164,14 +166,14 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
}
template<typename T, typename... Args>
- Span<T *> construct_elements_and_pointer_array(uint n, Args &&... args)
+ Span<T *> construct_elements_and_pointer_array(int64_t n, Args &&... args)
{
MutableSpan<void *> void_pointers = this->allocate_elements_and_pointer_array(
n, sizeof(T), alignof(T));
MutableSpan<T *> pointers = void_pointers.cast<T *>();
- for (uint i : IndexRange(n)) {
- new (pointers[i]) T(std::forward<Args>(args)...);
+ for (int64_t i : IndexRange(n)) {
+ new ((void *)pointers[i]) T(std::forward<Args>(args)...);
}
return pointers;
@@ -183,7 +185,7 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
*/
void provide_buffer(void *buffer, uint size)
{
- m_unused_borrowed_buffers.append(Span<char>((char *)buffer, size));
+ unused_borrowed_buffers_.append(Span<char>((char *)buffer, size));
}
template<size_t Size, size_t Alignment>
@@ -193,25 +195,26 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
}
private:
- void allocate_new_buffer(uint min_allocation_size)
+ void allocate_new_buffer(int64_t min_allocation_size)
{
- for (uint i : m_unused_borrowed_buffers.index_range()) {
- Span<char> buffer = m_unused_borrowed_buffers[i];
+ for (int64_t i : unused_borrowed_buffers_.index_range()) {
+ Span<char> buffer = unused_borrowed_buffers_[i];
if (buffer.size() >= min_allocation_size) {
- m_unused_borrowed_buffers.remove_and_reorder(i);
- m_current_begin = (uintptr_t)buffer.begin();
- m_current_end = (uintptr_t)buffer.end();
+ unused_borrowed_buffers_.remove_and_reorder(i);
+ current_begin_ = (uintptr_t)buffer.begin();
+ current_end_ = (uintptr_t)buffer.end();
return;
}
}
- uint size_in_bytes = power_of_2_min_u(std::max(min_allocation_size, m_next_min_alloc_size));
- m_next_min_alloc_size = size_in_bytes * 2;
+ const int64_t size_in_bytes = power_of_2_min_u(
+ std::max(min_allocation_size, next_min_alloc_size_));
+ next_min_alloc_size_ = size_in_bytes * 2;
- void *buffer = m_allocator.allocate(size_in_bytes, 8, AT);
- m_owned_buffers.append(buffer);
- m_current_begin = (uintptr_t)buffer;
- m_current_end = m_current_begin + size_in_bytes;
+ void *buffer = allocator_.allocate(size_in_bytes, 8, AT);
+ owned_buffers_.append(buffer);
+ current_begin_ = (uintptr_t)buffer;
+ current_end_ = current_begin_ + size_in_bytes;
}
};
diff --git a/source/blender/blenlib/BLI_linklist.h b/source/blender/blenlib/BLI_linklist.h
index 06796d6592a..324da859af1 100644
--- a/source/blender/blenlib/BLI_linklist.h
+++ b/source/blender/blenlib/BLI_linklist.h
@@ -55,6 +55,7 @@ int BLI_linklist_count(const LinkNode *list) ATTR_WARN_UNUSED_RESULT;
int BLI_linklist_index(const LinkNode *list, void *ptr) ATTR_WARN_UNUSED_RESULT;
LinkNode *BLI_linklist_find(LinkNode *list, int index) ATTR_WARN_UNUSED_RESULT;
+LinkNode *BLI_linklist_find_last(LinkNode *list) ATTR_WARN_UNUSED_RESULT;
void BLI_linklist_reverse(LinkNode **listp) ATTR_NONNULL(1);
diff --git a/source/blender/blenlib/BLI_listbase_wrapper.hh b/source/blender/blenlib/BLI_listbase_wrapper.hh
index a77e2d66458..46f4a9d49fa 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"
@@ -33,10 +33,10 @@ namespace blender {
template<typename T> class ListBaseWrapper {
private:
- ListBase *m_listbase;
+ ListBase *listbase_;
public:
- ListBaseWrapper(ListBase *listbase) : m_listbase(listbase)
+ ListBaseWrapper(ListBase *listbase) : listbase_(listbase)
{
BLI_assert(listbase);
}
@@ -47,17 +47,17 @@ template<typename T> class ListBaseWrapper {
class Iterator {
private:
- ListBase *m_listbase;
- T *m_current;
+ ListBase *listbase_;
+ T *current_;
public:
- Iterator(ListBase *listbase, T *current) : m_listbase(listbase), m_current(current)
+ Iterator(ListBase *listbase, T *current) : listbase_(listbase), current_(current)
{
}
Iterator &operator++()
{
- m_current = m_current->next;
+ current_ = current_->next;
return *this;
}
@@ -70,35 +70,35 @@ template<typename T> class ListBaseWrapper {
bool operator!=(const Iterator &iterator) const
{
- return m_current != iterator.m_current;
+ return current_ != iterator.current_;
}
T *operator*() const
{
- return m_current;
+ return current_;
}
};
Iterator begin() const
{
- return Iterator(m_listbase, (T *)m_listbase->first);
+ return Iterator(listbase_, (T *)listbase_->first);
}
Iterator end() const
{
- return Iterator(m_listbase, nullptr);
+ return Iterator(listbase_, nullptr);
}
T get(uint index) const
{
- void *ptr = BLI_findlink(m_listbase, index);
+ void *ptr = BLI_findlink(listbase_, index);
BLI_assert(ptr);
return (T *)ptr;
}
- uint index_of(const T *value) const
+ int64_t index_of(const T *value) const
{
- uint index = 0;
+ int64_t index = 0;
for (T *ptr : *this) {
if (ptr == value) {
return index;
@@ -106,7 +106,7 @@ template<typename T> class ListBaseWrapper {
index++;
}
BLI_assert(false);
- return 0;
+ return -1;
}
};
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 9737367ebca..dd375272fdb 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 {
@@ -92,13 +92,10 @@ template<
* The minimum number of elements that can be stored in this Map without doing a heap
* allocation. This is useful when you expect to have many small maps. However, keep in mind
* that (unlike vector) initializing a map has a O(n) cost in the number of slots.
- *
- * When Key or Value are large, the small buffer optimization is disabled by default to avoid
- * large unexpected allocations on the stack. It can still be enabled explicitely though.
*/
- uint32_t InlineBufferCapacity = (sizeof(Key) + sizeof(Value) < 100) ? 4 : 0,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key) + sizeof(Value)),
/**
- * 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,
/**
@@ -129,30 +126,30 @@ class Map {
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
* subtracting the removed slots from the occupied-and-removed slots.
*/
- uint32_t m_removed_slots;
- uint32_t m_occupied_and_removed_slots;
+ int64_t removed_slots_;
+ int64_t occupied_and_removed_slots_;
/**
* The maximum number of slots that can be used (either occupied or removed) until the set has to
* grow. This is the total number of slots times the max load factor.
*/
- uint32_t m_usable_slots;
+ int64_t usable_slots_;
/**
* The number of slots minus one. This is a bit mask that can be used to turn any integer into a
* valid slot index efficiently.
*/
- uint32_t m_slot_mask;
+ uint64_t slot_mask_;
/** This is called to hash incoming keys. */
- Hash m_hash;
+ Hash hash_;
/** This is called to check equality of two keys. */
- IsEqual m_is_equal;
+ IsEqual is_equal_;
/** The max load factor is 1/2 = 50% by default. */
#define LOAD_FACTOR 1, 2
- LoadFactor m_max_load_factor = LoadFactor(LOAD_FACTOR);
+ LoadFactor max_load_factor_ = LoadFactor(LOAD_FACTOR);
using SlotArray =
Array<Slot, LoadFactor::compute_total_slots(InlineBufferCapacity, LOAD_FACTOR), Allocator>;
#undef LOAD_FACTOR
@@ -161,12 +158,12 @@ class Map {
* This is the array that contains the actual slots. There is always at least one empty slot and
* the size of the array is a power of two.
*/
- SlotArray m_slots;
+ SlotArray slots_;
/** Iterate over a slot index sequence for a given hash. */
#define MAP_SLOT_PROBING_BEGIN(HASH, R_SLOT) \
- SLOT_PROBING_BEGIN (ProbingStrategy, HASH, m_slot_mask, SLOT_INDEX) \
- auto &R_SLOT = m_slots[SLOT_INDEX];
+ SLOT_PROBING_BEGIN (ProbingStrategy, HASH, slot_mask_, SLOT_INDEX) \
+ auto &R_SLOT = slots_[SLOT_INDEX];
#define MAP_SLOT_PROBING_END() SLOT_PROBING_END()
public:
@@ -176,13 +173,13 @@ class Map {
* operation is performed on the first insertion.
*/
Map()
- : m_removed_slots(0),
- m_occupied_and_removed_slots(0),
- m_usable_slots(0),
- m_slot_mask(0),
- m_hash(),
- m_is_equal(),
- m_slots(1)
+ : removed_slots_(0),
+ occupied_and_removed_slots_(0),
+ usable_slots_(0),
+ slot_mask_(0),
+ hash_(),
+ is_equal_(),
+ slots_(1)
{
}
@@ -191,13 +188,13 @@ class Map {
Map(const Map &other) = default;
Map(Map &&other) noexcept
- : m_removed_slots(other.m_removed_slots),
- m_occupied_and_removed_slots(other.m_occupied_and_removed_slots),
- m_usable_slots(other.m_usable_slots),
- m_slot_mask(other.m_slot_mask),
- m_hash(std::move(other.m_hash)),
- m_is_equal(std::move(other.m_is_equal)),
- m_slots(std::move(other.m_slots))
+ : removed_slots_(other.removed_slots_),
+ occupied_and_removed_slots_(other.occupied_and_removed_slots_),
+ usable_slots_(other.usable_slots_),
+ slot_mask_(other.slot_mask_),
+ hash_(std::move(other.hash_)),
+ is_equal_(std::move(other.is_equal_)),
+ slots_(std::move(other.slots_))
{
other.~Map();
new (&other) Map();
@@ -233,19 +230,19 @@ class Map {
*/
void add_new(const Key &key, const Value &value)
{
- this->add_new__impl(key, value, m_hash(key));
+ this->add_new__impl(key, value, hash_(key));
}
void add_new(const Key &key, Value &&value)
{
- this->add_new__impl(key, std::move(value), m_hash(key));
+ this->add_new__impl(key, std::move(value), hash_(key));
}
void add_new(Key &&key, const Value &value)
{
- this->add_new__impl(std::move(key), value, m_hash(key));
+ this->add_new__impl(std::move(key), value, hash_(key));
}
void add_new(Key &&key, Value &&value)
{
- this->add_new__impl(std::move(key), std::move(value), m_hash(key));
+ this->add_new__impl(std::move(key), std::move(value), hash_(key));
}
/**
@@ -271,15 +268,11 @@ class Map {
{
return this->add_as(std::move(key), std::move(value));
}
-
- /**
- * Same as `add`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey, typename ForwardValue>
bool add_as(ForwardKey &&key, ForwardValue &&value)
{
return this->add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), m_hash(key));
+ std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
}
/**
@@ -305,15 +298,11 @@ class Map {
{
return this->add_overwrite_as(std::move(key), std::move(value));
}
-
- /**
- * Same as `add_overwrite`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey, typename ForwardValue>
bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value)
{
return this->add_overwrite__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), m_hash(key));
+ std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
}
/**
@@ -325,13 +314,9 @@ class Map {
{
return this->contains_as(key);
}
-
- /**
- * Same as `contains`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool contains_as(const ForwardKey &key) const
{
- return this->contains__impl(key, m_hash(key));
+ return this->contains__impl(key, hash_(key));
}
/**
@@ -344,13 +329,9 @@ class Map {
{
return this->remove_as(key);
}
-
- /**
- * Same as `remove`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool remove_as(const ForwardKey &key)
{
- return this->remove__impl(key, m_hash(key));
+ return this->remove__impl(key, hash_(key));
}
/**
@@ -361,14 +342,9 @@ class Map {
{
this->remove_contained_as(key);
}
-
- /**
- * Same as `remove_contained`, but accepts other key types that are supported by the hash
- * function.
- */
template<typename ForwardKey> void remove_contained_as(const ForwardKey &key)
{
- this->remove_contained__impl(key, m_hash(key));
+ this->remove_contained__impl(key, hash_(key));
}
/**
@@ -379,30 +355,22 @@ class Map {
{
return this->pop_as(key);
}
-
- /**
- * Same as `pop`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> Value pop_as(const ForwardKey &key)
{
- return this->pop__impl(key, m_hash(key));
+ return this->pop__impl(key, hash_(key));
}
/**
* 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);
}
-
- /**
- * 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));
+ return this->pop_try__impl(key, hash_(key));
}
/**
@@ -417,14 +385,10 @@ class Map {
{
return this->pop_default_as(key, std::move(default_value));
}
-
- /**
- * Same as `pop_default`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey, typename ForwardValue>
Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
{
- return this->pop_default__impl(key, std::forward<ForwardValue>(default_value), m_hash(key));
+ return this->pop_default__impl(key, std::forward<ForwardValue>(default_value), hash_(key));
}
/**
@@ -460,17 +424,13 @@ class Map {
{
return this->add_or_modify_as(std::move(key), create_value, modify_value);
}
-
- /**
- * Same as `add_or_modify`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey, typename CreateValueF, typename ModifyValueF>
auto add_or_modify_as(ForwardKey &&key,
const CreateValueF &create_value,
const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
{
return this->add_or_modify__impl(
- std::forward<Key>(key), create_value, modify_value, m_hash(key));
+ std::forward<ForwardKey>(key), create_value, modify_value, hash_(key));
}
/**
@@ -487,17 +447,13 @@ class Map {
{
return this->lookup_ptr_as(key);
}
-
- /**
- * Same as `lookup_ptr`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> const Value *lookup_ptr_as(const ForwardKey &key) const
{
- return this->lookup_ptr__impl(key, m_hash(key));
+ return this->lookup_ptr__impl(key, hash_(key));
}
template<typename ForwardKey> Value *lookup_ptr_as(const ForwardKey &key)
{
- return const_cast<Value *>(this->lookup_ptr__impl(key, m_hash(key)));
+ return const_cast<Value *>(this->lookup_ptr__impl(key, hash_(key)));
}
/**
@@ -512,10 +468,6 @@ class Map {
{
return this->lookup_as(key);
}
-
- /**
- * Same as `lookup`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> const Value &lookup_as(const ForwardKey &key) const
{
const Value *ptr = this->lookup_ptr_as(key);
@@ -537,10 +489,6 @@ class Map {
{
return this->lookup_default_as(key, default_value);
}
-
- /**
- * Same as `lookup_default`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey, typename ForwardValue>
Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
{
@@ -573,16 +521,11 @@ class Map {
{
return this->lookup_or_add_as(std::move(key), std::move(value));
}
-
- /**
- * Same as `lookup_or_add`, but accepts other key types that are supported by the hash
- * function.
- */
template<typename ForwardKey, typename ForwardValue>
Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value)
{
return this->lookup_or_add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), m_hash(key));
+ std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
}
/**
@@ -602,15 +545,10 @@ class Map {
{
return this->lookup_or_add_cb_as(std::move(key), create_value);
}
-
- /**
- * Same as `lookup_or_add_cb`, but accepts other key types that are supported by the hash
- * function.
- */
template<typename ForwardKey, typename CreateValueF>
Value &lookup_or_add_cb_as(ForwardKey &&key, const CreateValueF &create_value)
{
- return this->lookup_or_add_cb__impl(std::forward<ForwardKey>(key), create_value, m_hash(key));
+ return this->lookup_or_add_cb__impl(std::forward<ForwardKey>(key), create_value, hash_(key));
}
/**
@@ -625,11 +563,6 @@ class Map {
{
return this->lookup_or_add_default_as(std::move(key));
}
-
- /**
- * Same as `lookup_or_add_default`, but accepts other key types that are supported by the hash
- * function.
- */
template<typename ForwardKey> Value &lookup_or_add_default_as(ForwardKey &&key)
{
return this->lookup_or_add_cb_as(std::forward<ForwardKey>(key), []() { return Value(); });
@@ -641,9 +574,9 @@ class Map {
*/
template<typename FuncT> void foreach_item(const FuncT &func) const
{
- uint32_t size = this->size();
- for (uint32_t i = 0; i < size; i++) {
- const Slot &slot = m_slots[i];
+ int64_t size = slots_.size();
+ for (int64_t i = 0; i < size; i++) {
+ const Slot &slot = slots_[i];
if (slot.is_occupied()) {
const Key &key = *slot.key();
const Value &value = *slot.value();
@@ -657,21 +590,19 @@ class Map {
* This uses the "curiously recurring template pattern" (CRTP).
*/
template<typename SubIterator> struct BaseIterator {
- Slot *m_slots;
- uint32_t m_total_slots;
- uint32_t m_current_slot;
-
- BaseIterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
- : m_slots(const_cast<Slot *>(slots)),
- m_total_slots(total_slots),
- m_current_slot(current_slot)
+ Slot *slots_;
+ int64_t total_slots_;
+ int64_t current_slot_;
+
+ BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ : slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot)
{
}
BaseIterator &operator++()
{
- while (++m_current_slot < m_total_slots) {
- if (m_slots[m_current_slot].is_occupied()) {
+ while (++current_slot_ < total_slots_) {
+ if (slots_[current_slot_].is_occupied()) {
break;
}
}
@@ -680,16 +611,16 @@ class Map {
friend bool operator!=(const BaseIterator &a, const BaseIterator &b)
{
- BLI_assert(a.m_slots == b.m_slots);
- BLI_assert(a.m_total_slots == b.m_total_slots);
- return a.m_current_slot != b.m_current_slot;
+ BLI_assert(a.slots_ == b.slots_);
+ BLI_assert(a.total_slots_ == b.total_slots_);
+ return a.current_slot_ != b.current_slot_;
}
SubIterator begin() const
{
- for (uint32_t i = 0; i < m_total_slots; i++) {
- if (m_slots[i].is_occupied()) {
- return SubIterator(m_slots, m_total_slots, i);
+ for (int64_t i = 0; i < total_slots_; i++) {
+ if (slots_[i].is_occupied()) {
+ return SubIterator(slots_, total_slots_, i);
}
}
return this->end();
@@ -697,18 +628,18 @@ class Map {
SubIterator end() const
{
- return SubIterator(m_slots, m_total_slots, m_total_slots);
+ return SubIterator(slots_, total_slots_, total_slots_);
}
Slot &current_slot() const
{
- return m_slots[m_current_slot];
+ return slots_[current_slot_];
}
};
class KeyIterator final : public BaseIterator<KeyIterator> {
public:
- KeyIterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
+ KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<KeyIterator>(slots, total_slots, current_slot)
{
}
@@ -721,7 +652,7 @@ class Map {
class ValueIterator final : public BaseIterator<ValueIterator> {
public:
- ValueIterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
+ ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<ValueIterator>(slots, total_slots, current_slot)
{
}
@@ -734,7 +665,7 @@ class Map {
class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
public:
- MutableValueIterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
+ MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
{
}
@@ -745,18 +676,28 @@ class Map {
}
};
+ struct Item {
+ const Key &key;
+ const Value &value;
+ };
+
+ struct MutableItem {
+ const Key &key;
+ Value &value;
+
+ operator Item() const
+ {
+ return Item{key, value};
+ }
+ };
+
class ItemIterator final : public BaseIterator<ItemIterator> {
public:
- ItemIterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
+ ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<ItemIterator>(slots, total_slots, current_slot)
{
}
- struct Item {
- const Key &key;
- const Value &value;
- };
-
Item operator*() const
{
const Slot &slot = this->current_slot();
@@ -766,17 +707,12 @@ class Map {
class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
public:
- MutableItemIterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
+ MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
: BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
{
}
- struct Item {
- const Key &key;
- Value &value;
- };
-
- Item operator*() const
+ MutableItem operator*() const
{
Slot &slot = this->current_slot();
return {*slot.key(), *slot.value()};
@@ -789,7 +725,7 @@ class Map {
*/
KeyIterator keys() const
{
- return KeyIterator(m_slots.data(), m_slots.size(), 0);
+ return KeyIterator(slots_.data(), slots_.size(), 0);
}
/**
@@ -798,7 +734,7 @@ class Map {
*/
ValueIterator values() const
{
- return ValueIterator(m_slots.data(), m_slots.size(), 0);
+ return ValueIterator(slots_.data(), slots_.size(), 0);
}
/**
@@ -807,7 +743,7 @@ class Map {
*/
MutableValueIterator values()
{
- return MutableValueIterator(m_slots.data(), m_slots.size(), 0);
+ return MutableValueIterator(slots_.data(), slots_.size(), 0);
}
/**
@@ -817,7 +753,7 @@ class Map {
*/
ItemIterator items() const
{
- return ItemIterator(m_slots.data(), m_slots.size(), 0);
+ return ItemIterator(slots_.data(), slots_.size(), 0);
}
/**
@@ -829,7 +765,7 @@ class Map {
*/
MutableItemIterator items()
{
- return MutableItemIterator(m_slots.data(), m_slots.size(), 0);
+ return MutableItemIterator(slots_.data(), slots_.size(), 0);
}
/**
@@ -838,15 +774,15 @@ class Map {
void print_stats(StringRef name = "") const
{
HashTableStats stats(*this, this->keys());
- stats.print();
+ stats.print(name);
}
/**
* Return the number of key-value-pairs that are stored in the map.
*/
- uint32_t size() const
+ int64_t size() const
{
- return m_occupied_and_removed_slots - m_removed_slots;
+ return occupied_and_removed_slots_ - removed_slots_;
}
/**
@@ -856,29 +792,29 @@ class Map {
*/
bool is_empty() const
{
- return m_occupied_and_removed_slots == m_removed_slots;
+ return occupied_and_removed_slots_ == removed_slots_;
}
/**
* Returns the number of available slots. This is mostly for debugging purposes.
*/
- uint32_t capacity() const
+ int64_t capacity() const
{
- return m_slots.size();
+ return slots_.size();
}
/**
* Returns the amount of removed slots in the set. This is mostly for debugging purposes.
*/
- uint32_t removed_amount() const
+ int64_t removed_amount() const
{
- return m_removed_slots;
+ return removed_slots_;
}
/**
* Returns the bytes required per element. This is mostly for debugging purposes.
*/
- uint32_t size_per_element() const
+ int64_t size_per_element() const
{
return sizeof(Slot);
}
@@ -887,18 +823,18 @@ class Map {
* Returns the approximate memory requirements of the map in bytes. This becomes more exact the
* larger the map becomes.
*/
- uint32_t size_in_bytes() const
+ int64_t size_in_bytes() const
{
- return sizeof(Slot) * m_slots.size();
+ return (int64_t)(sizeof(Slot) * slots_.size());
}
/**
* Potentially resize the map such that the specified number of elements can be added without
* another grow operation.
*/
- void reserve(uint32_t n)
+ void reserve(int64_t n)
{
- if (m_usable_slots < n) {
+ if (usable_slots_ < n) {
this->realloc_and_reinsert(n);
}
}
@@ -916,35 +852,36 @@ class Map {
* Get the number of collisions that the probing strategy has to go through to find the key or
* determine that it is not in the map.
*/
- uint32_t count_collisions(const Key &key) const
+ int64_t count_collisions(const Key &key) const
{
- return this->count_collisions__impl(key, m_hash(key));
+ return this->count_collisions__impl(key, hash_(key));
}
private:
- BLI_NOINLINE void realloc_and_reinsert(uint32_t min_usable_slots)
+ BLI_NOINLINE void realloc_and_reinsert(int64_t min_usable_slots)
{
- uint32_t total_slots, usable_slots;
- m_max_load_factor.compute_total_and_usable_slots(
+ int64_t total_slots, usable_slots;
+ max_load_factor_.compute_total_and_usable_slots(
SlotArray::inline_buffer_capacity(), min_usable_slots, &total_slots, &usable_slots);
- uint32_t new_slot_mask = total_slots - 1;
+ BLI_assert(total_slots >= 1);
+ const uint64_t new_slot_mask = (uint64_t)total_slots - 1;
/**
* Optimize the case when the map was empty beforehand. We can avoid some copies here.
*/
if (this->size() == 0) {
- m_slots.~Array();
- new (&m_slots) SlotArray(total_slots);
- m_removed_slots = 0;
- m_occupied_and_removed_slots = 0;
- m_usable_slots = usable_slots;
- m_slot_mask = new_slot_mask;
+ slots_.~Array();
+ new (&slots_) SlotArray(total_slots);
+ removed_slots_ = 0;
+ occupied_and_removed_slots_ = 0;
+ usable_slots_ = usable_slots;
+ slot_mask_ = new_slot_mask;
return;
}
SlotArray new_slots(total_slots);
- for (Slot &slot : m_slots) {
+ for (Slot &slot : slots_) {
if (slot.is_occupied()) {
this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask);
}
@@ -952,19 +889,19 @@ class Map {
/* All occupied slots have been destructed already and empty/removed slots are assumed to be
* trivially destructible. */
- m_slots.clear_without_destruct();
- m_slots = std::move(new_slots);
- m_occupied_and_removed_slots -= m_removed_slots;
- m_usable_slots = usable_slots;
- m_removed_slots = 0;
- m_slot_mask = new_slot_mask;
+ slots_.clear_without_destruct();
+ slots_ = std::move(new_slots);
+ occupied_and_removed_slots_ -= removed_slots_;
+ usable_slots_ = usable_slots;
+ removed_slots_ = 0;
+ slot_mask_ = new_slot_mask;
}
void add_after_grow_and_destruct_old(Slot &old_slot,
SlotArray &new_slots,
- uint32_t new_slot_mask)
+ uint64_t new_slot_mask)
{
- uint32_t hash = old_slot.get_hash(Hash());
+ uint64_t hash = old_slot.get_hash(Hash());
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
@@ -975,13 +912,13 @@ class Map {
SLOT_PROBING_END();
}
- template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint32_t hash) const
+ template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint64_t hash) const
{
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
return false;
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return true;
}
}
@@ -989,12 +926,12 @@ class Map {
}
template<typename ForwardKey, typename ForwardValue>
- void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint32_t hash)
+ void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
BLI_assert(!this->contains_as(key));
this->ensure_can_add();
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
@@ -1006,29 +943,29 @@ class Map {
}
template<typename ForwardKey, typename ForwardValue>
- bool add__impl(ForwardKey &&key, ForwardValue &&value, uint32_t hash)
+ bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
return true;
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return false;
}
}
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint64_t hash)
{
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
slot.remove();
- m_removed_slots++;
+ removed_slots_++;
return true;
}
if (slot.is_empty()) {
@@ -1038,14 +975,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint64_t hash)
{
BLI_assert(this->contains_as(key));
- m_removed_slots++;
+ removed_slots_++;
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
slot.remove();
return;
}
@@ -1053,14 +990,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey> Value pop__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey> Value pop__impl(const ForwardKey &key, uint64_t hash)
{
BLI_assert(this->contains_as(key));
- m_removed_slots++;
+ removed_slots_++;
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
Value value = std::move(*slot.value());
slot.remove();
return value;
@@ -1069,13 +1006,14 @@ 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, uint64_t hash)
{
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
- Optional<Value> value = std::move(*slot.value());
+ if (slot.contains(key, is_equal_, hash)) {
+ std::optional<Value> value = std::move(*slot.value());
slot.remove();
- m_removed_slots++;
+ removed_slots_++;
return value;
}
if (slot.is_empty()) {
@@ -1086,13 +1024,13 @@ class Map {
}
template<typename ForwardKey, typename ForwardValue>
- Value pop_default__impl(const ForwardKey &key, ForwardValue &&default_value, uint32_t hash)
+ Value pop_default__impl(const ForwardKey &key, ForwardValue &&default_value, uint64_t hash)
{
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
Value value = std::move(*slot.value());
slot.remove();
- m_removed_slots++;
+ removed_slots_++;
return value;
}
if (slot.is_empty()) {
@@ -1106,23 +1044,23 @@ class Map {
auto add_or_modify__impl(ForwardKey &&key,
const CreateValueF &create_value,
const ModifyValueF &modify_value,
- uint32_t hash) -> decltype(create_value(nullptr))
+ uint64_t hash) -> decltype(create_value(nullptr))
{
using CreateReturnT = decltype(create_value(nullptr));
using ModifyReturnT = decltype(modify_value(nullptr));
- BLI_STATIC_ASSERT((std::is_same<CreateReturnT, ModifyReturnT>::value),
+ BLI_STATIC_ASSERT((std::is_same_v<CreateReturnT, ModifyReturnT>),
"Both callbacks should return the same type.");
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
slot.occupy_without_value(std::forward<ForwardKey>(key), hash);
Value *value_ptr = slot.value();
return create_value(value_ptr);
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
Value *value_ptr = slot.value();
return modify_value(value_ptr);
}
@@ -1131,17 +1069,17 @@ class Map {
}
template<typename ForwardKey, typename CreateValueF>
- Value &lookup_or_add_cb__impl(ForwardKey &&key, const CreateValueF &create_value, uint32_t hash)
+ Value &lookup_or_add_cb__impl(ForwardKey &&key, const CreateValueF &create_value, uint64_t hash)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), create_value(), hash);
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
return *slot.value();
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return *slot.value();
}
}
@@ -1149,17 +1087,17 @@ class Map {
}
template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint32_t hash)
+ Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
return *slot.value();
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return *slot.value();
}
}
@@ -1167,10 +1105,10 @@ class Map {
}
template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint32_t hash)
+ bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
auto create_func = [&](Value *ptr) {
- new (ptr) Value(std::forward<ForwardValue>(value));
+ new ((void *)ptr) Value(std::forward<ForwardValue>(value));
return true;
};
auto modify_func = [&](Value *ptr) {
@@ -1182,13 +1120,13 @@ class Map {
}
template<typename ForwardKey>
- const Value *lookup_ptr__impl(const ForwardKey &key, uint32_t hash) const
+ const Value *lookup_ptr__impl(const ForwardKey &key, uint64_t hash) const
{
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
return nullptr;
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return slot.value();
}
}
@@ -1196,12 +1134,12 @@ class Map {
}
template<typename ForwardKey>
- uint32_t count_collisions__impl(const ForwardKey &key, uint32_t hash) const
+ int64_t count_collisions__impl(const ForwardKey &key, uint64_t hash) const
{
- uint32_t collisions = 0;
+ int64_t collisions = 0;
MAP_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return collisions;
}
if (slot.is_empty()) {
@@ -1214,73 +1152,88 @@ class Map {
void ensure_can_add()
{
- if (m_occupied_and_removed_slots >= m_usable_slots) {
+ if (occupied_and_removed_slots_ >= usable_slots_) {
this->realloc_and_reinsert(this->size() + 1);
- BLI_assert(m_occupied_and_removed_slots < m_usable_slots);
+ BLI_assert(occupied_and_removed_slots_ < usable_slots_);
}
}
};
/**
+ * Same as a normal Map, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
+ */
+template<typename Key,
+ typename Value,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key) +
+ sizeof(Value)),
+ typename ProbingStrategy = DefaultProbingStrategy,
+ typename Hash = DefaultHash<Key>,
+ typename IsEqual = DefaultEquality,
+ typename Slot = typename DefaultMapSlot<Key, Value>::type>
+using RawMap =
+ Map<Key, Value, InlineBufferCapacity, ProbingStrategy, Hash, IsEqual, Slot, RawAllocator>;
+
+/**
* A wrapper for std::unordered_map with the API of blender::Map. This can be used for
* benchmarking.
*/
template<typename Key, typename Value> class StdUnorderedMapWrapper {
private:
using MapType = std::unordered_map<Key, Value, blender::DefaultHash<Key>>;
- MapType m_map;
+ MapType map_;
public:
- uint32_t size() const
+ int64_t size() const
{
- return (uint32_t)m_map.size();
+ return (int64_t)map_.size();
}
bool is_empty() const
{
- return m_map.empty();
+ return map_.empty();
}
- void reserve(uint32_t n)
+ void reserve(int64_t n)
{
- m_map.reserve(n);
+ map_.reserve(n);
}
template<typename ForwardKey, typename ForwardValue>
void add_new(ForwardKey &&key, ForwardValue &&value)
{
- m_map.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
+ map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
}
template<typename ForwardKey, typename ForwardValue>
bool add(ForwardKey &&key, ForwardValue &&value)
{
- return m_map.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
+ return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
}
bool contains(const Key &key) const
{
- return m_map.find(key) != m_map.end();
+ return map_.find(key) != map_.end();
}
bool remove(const Key &key)
{
- return (bool)m_map.erase(key);
+ return (bool)map_.erase(key);
}
Value &lookup(const Key &key)
{
- return m_map.find(key)->second;
+ return map_.find(key)->second;
}
const Value &lookup(const Key &key) const
{
- return m_map.find(key)->second;
+ return map_.find(key)->second;
}
void clear()
{
- m_map.clear();
+ map_.clear();
}
void print_stats(StringRef UNUSED(name) = "") const
diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh
index 9ea2c4cad89..b5360795a13 100644
--- a/source/blender/blenlib/BLI_map_slots.hh
+++ b/source/blender/blenlib/BLI_map_slots.hh
@@ -52,9 +52,9 @@ template<typename Key, typename Value> class SimpleMapSlot {
Removed = 2,
};
- State m_state;
- AlignedBuffer<sizeof(Key), alignof(Key)> m_key_buffer;
- AlignedBuffer<sizeof(Value), alignof(Value)> m_value_buffer;
+ State state_;
+ TypedBuffer<Key> key_buffer_;
+ TypedBuffer<Value> value_buffer_;
public:
/**
@@ -62,7 +62,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
SimpleMapSlot()
{
- m_state = Empty;
+ state_ = Empty;
}
/**
@@ -70,9 +70,9 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
~SimpleMapSlot()
{
- if (m_state == Occupied) {
- this->key()->~Key();
- this->value()->~Value();
+ if (state_ == Occupied) {
+ key_buffer_.ref().~Key();
+ value_buffer_.ref().~Value();
}
}
@@ -82,24 +82,24 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
SimpleMapSlot(const SimpleMapSlot &other)
{
- m_state = other.m_state;
- if (other.m_state == Occupied) {
- new (this->key()) Key(*other.key());
- new (this->value()) Value(*other.value());
+ state_ = other.state_;
+ if (other.state_ == Occupied) {
+ new (&key_buffer_) Key(*other.key_buffer_);
+ new (&value_buffer_) Value(*other.value_buffer_);
}
}
/**
- * The move construtor has to copy the state. If the other slot was occupied, the key and value
+ * The move constructor has to copy the state. If the other slot was occupied, the key and value
* from the other have to moved as well. The other slot stays in the state it was in before. Its
* optionally stored key and value remain in a moved-from state.
*/
SimpleMapSlot(SimpleMapSlot &&other) noexcept
{
- m_state = other.m_state;
- if (other.m_state == Occupied) {
- new (this->key()) Key(std::move(*other.key()));
- new (this->value()) Value(std::move(*other.value()));
+ state_ = other.state_;
+ if (other.state_ == Occupied) {
+ new (&key_buffer_) Key(std::move(*other.key_buffer_));
+ new (&value_buffer_) Value(std::move(*other.value_buffer_));
}
}
@@ -108,7 +108,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
Key *key()
{
- return (Key *)m_key_buffer.ptr();
+ return key_buffer_;
}
/**
@@ -116,7 +116,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
const Key *key() const
{
- return (const Key *)m_key_buffer.ptr();
+ return key_buffer_;
}
/**
@@ -124,7 +124,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
Value *value()
{
- return (Value *)m_value_buffer.ptr();
+ return value_buffer_;
}
/**
@@ -132,7 +132,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
const Value *value() const
{
- return (const Value *)m_value_buffer.ptr();
+ return value_buffer_;
}
/**
@@ -140,7 +140,7 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
bool is_occupied() const
{
- return m_state == Occupied;
+ return state_ == Occupied;
}
/**
@@ -148,32 +148,32 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
bool is_empty() const
{
- return m_state == Empty;
+ return state_ == Empty;
}
/**
* Returns the hash of the currently stored key. In this simple map slot implementation, we just
* computed the hash here. Other implementations might store the hash in the slot instead.
*/
- template<typename Hash> uint32_t get_hash(const Hash &hash)
+ template<typename Hash> uint64_t get_hash(const Hash &hash)
{
BLI_assert(this->is_occupied());
- return hash(*this->key());
+ return hash(*key_buffer_);
}
/**
* Move the other slot into this slot and destruct it. We do destruction here, because this way
* we can avoid a comparison with the state, since we know the slot is occupied.
*/
- void relocate_occupied_here(SimpleMapSlot &other, uint32_t UNUSED(hash))
+ void relocate_occupied_here(SimpleMapSlot &other, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(other.is_occupied());
- m_state = Occupied;
- new (this->key()) Key(std::move(*other.key()));
- new (this->value()) Value(std::move(*other.value()));
- other.key()->~Key();
- other.value()->~Value();
+ state_ = Occupied;
+ new (&key_buffer_) Key(std::move(*other.key_buffer_));
+ new (&value_buffer_) Value(std::move(*other.value_buffer_));
+ other.key_buffer_.ref().~Key();
+ other.value_buffer_.ref().~Value();
}
/**
@@ -181,10 +181,10 @@ template<typename Key, typename Value> class SimpleMapSlot {
* key. The hash can be used by other slot implementations to determine inequality faster.
*/
template<typename ForwardKey, typename IsEqual>
- bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, uint64_t UNUSED(hash)) const
{
- if (m_state == Occupied) {
- return is_equal(key, *this->key());
+ if (state_ == Occupied) {
+ return is_equal(key, *key_buffer_);
}
return false;
}
@@ -194,22 +194,22 @@ template<typename Key, typename Value> class SimpleMapSlot {
* constructed by calling the constructor with the given key/value as parameter.
*/
template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint32_t hash)
+ void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
BLI_assert(!this->is_occupied());
this->occupy_without_value(std::forward<ForwardKey>(key), hash);
- new (this->value()) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value));
}
/**
* Change the state of this slot from empty/removed to occupied, but leave the value
* uninitialized. The caller is responsible to construct the value afterwards.
*/
- template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint32_t UNUSED(hash))
+ template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
- m_state = Occupied;
- new (this->key()) Key(std::forward<ForwardKey>(key));
+ state_ = Occupied;
+ new (&key_buffer_) Key(std::forward<ForwardKey>(key));
}
/**
@@ -219,9 +219,9 @@ template<typename Key, typename Value> class SimpleMapSlot {
void remove()
{
BLI_assert(this->is_occupied());
- m_state = Removed;
- this->key()->~Key();
- this->value()->~Value();
+ state_ = Removed;
+ key_buffer_.ref().~Key();
+ value_buffer_.ref().~Value();
}
};
@@ -235,107 +235,107 @@ template<typename Key, typename Value> class SimpleMapSlot {
*/
template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot {
private:
- Key m_key = KeyInfo::get_empty();
- AlignedBuffer<sizeof(Value), alignof(Value)> m_value_buffer;
+ Key key_ = KeyInfo::get_empty();
+ TypedBuffer<Value> value_buffer_;
public:
IntrusiveMapSlot() = default;
~IntrusiveMapSlot()
{
- if (KeyInfo::is_not_empty_or_removed(m_key)) {
- this->value()->~Value();
+ if (KeyInfo::is_not_empty_or_removed(key_)) {
+ value_buffer_.ref().~Value();
}
}
- IntrusiveMapSlot(const IntrusiveMapSlot &other) : m_key(other.m_key)
+ IntrusiveMapSlot(const IntrusiveMapSlot &other) : key_(other.key_)
{
- if (KeyInfo::is_not_empty_or_removed(m_key)) {
- new (this->value()) Value(*other.value());
+ if (KeyInfo::is_not_empty_or_removed(key_)) {
+ new (&value_buffer_) Value(*other.value_buffer_);
}
}
- IntrusiveMapSlot(IntrusiveMapSlot &&other) noexcept : m_key(other.m_key)
+ IntrusiveMapSlot(IntrusiveMapSlot &&other) noexcept : key_(other.key_)
{
- if (KeyInfo::is_not_empty_or_removed(m_key)) {
- new (this->value()) Value(std::move(*other.value()));
+ if (KeyInfo::is_not_empty_or_removed(key_)) {
+ new (&value_buffer_) Value(std::move(*other.value_buffer_));
}
}
Key *key()
{
- return &m_key;
+ return &key_;
}
const Key *key() const
{
- return &m_key;
+ return &key_;
}
Value *value()
{
- return (Value *)m_value_buffer.ptr();
+ return value_buffer_;
}
const Value *value() const
{
- return (const Value *)m_value_buffer.ptr();
+ return value_buffer_;
}
bool is_occupied() const
{
- return KeyInfo::is_not_empty_or_removed(m_key);
+ return KeyInfo::is_not_empty_or_removed(key_);
}
bool is_empty() const
{
- return KeyInfo::is_empty(m_key);
+ return KeyInfo::is_empty(key_);
}
- template<typename Hash> uint32_t get_hash(const Hash &hash)
+ template<typename Hash> uint64_t get_hash(const Hash &hash)
{
BLI_assert(this->is_occupied());
- return hash(*this->key());
+ return hash(key_);
}
- void relocate_occupied_here(IntrusiveMapSlot &other, uint32_t UNUSED(hash))
+ void relocate_occupied_here(IntrusiveMapSlot &other, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(other.is_occupied());
- m_key = std::move(other.m_key);
- new (this->value()) Value(std::move(*other.value()));
- other.m_key.~Key();
- other.value()->~Value();
+ key_ = std::move(other.key_);
+ new (&value_buffer_) Value(std::move(*other.value_buffer_));
+ other.key_.~Key();
+ other.value_buffer_.ref().~Value();
}
template<typename ForwardKey, typename IsEqual>
- bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, uint64_t UNUSED(hash)) const
{
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- return is_equal(key, m_key);
+ return is_equal(key, key_);
}
template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint32_t hash)
+ void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
this->occupy_without_value(std::forward<ForwardKey>(key), hash);
- new (this->value()) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value));
}
- template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint32_t UNUSED(hash))
+ template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- m_key = std::forward<ForwardKey>(key);
+ key_ = std::forward<ForwardKey>(key);
}
void remove()
{
BLI_assert(this->is_occupied());
- KeyInfo::remove(m_key);
- this->value()->~Value();
+ KeyInfo::remove(key_);
+ value_buffer_.ref().~Value();
}
};
diff --git a/source/blender/blenlib/BLI_math_base_safe.h b/source/blender/blenlib/BLI_math_base_safe.h
new file mode 100644
index 00000000000..88a08c3cbc7
--- /dev/null
+++ b/source/blender/blenlib/BLI_math_base_safe.h
@@ -0,0 +1,50 @@
+/*
+ * 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 __BLI_MATH_BASE_SAFE_H__
+#define __BLI_MATH_BASE_SAFE_H__
+
+/** \file
+ * \ingroup bli
+ *
+ * This file provides safe alternatives to common math functions like sqrt, powf.
+ * In this context "safe" means that the output is not NaN if the input is not NaN.
+ */
+
+#include "BLI_math_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINLINE float safe_divide(float a, float b);
+MINLINE float safe_modf(float a, float b);
+MINLINE float safe_logf(float a, float base);
+MINLINE float safe_sqrtf(float a);
+MINLINE float safe_inverse_sqrtf(float a);
+MINLINE float safe_asinf(float a);
+MINLINE float safe_acosf(float a);
+MINLINE float safe_powf(float base, float exponent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#if BLI_MATH_DO_INLINE
+# include "intern/math_base_safe_inline.c"
+#endif
+
+#endif /* __BLI_MATH_BASE_SAFE_H__ */
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index ba95da4092e..48f8e7d31d9 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -148,8 +148,12 @@ void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, fl
/********* lift/gamma/gain / ASC-CDL conversion ***********/
-void lift_gamma_gain_to_asc_cdl(
- float *lift, float *gamma, float *gain, float *offset, float *slope, float *power);
+void lift_gamma_gain_to_asc_cdl(const float *lift,
+ const float *gamma,
+ const float *gain,
+ float *offset,
+ float *slope,
+ float *power);
#if BLI_MATH_DO_INLINE
# include "intern/math_color_inline.c"
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 563bcad5d14..3a24209b07c 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],
@@ -450,11 +454,11 @@ bool isect_ray_seg_v2(const float ray_origin[2],
float *r_lambda,
float *r_u);
-bool isect_ray_seg_v3(const float ray_origin[3],
- const float ray_direction[3],
- const float v0[3],
- const float v1[3],
- float *r_lambda);
+bool isect_ray_line_v3(const float ray_origin[3],
+ const float ray_direction[3],
+ const float v0[3],
+ const float v1[3],
+ float *r_lambda);
/* point in polygon */
bool isect_point_poly_v2(const float pt[2],
@@ -667,6 +671,13 @@ void projmat_dimensions(const float projmat[4][4],
float *r_top,
float *r_near,
float *r_far);
+void projmat_dimensions_db(const float projmat[4][4],
+ double *r_left,
+ double *r_right,
+ double *r_bottom,
+ double *r_top,
+ double *r_near,
+ double *r_far);
void projmat_from_subregion(const float projmat[4][4],
const int win_size[2],
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 2d11797bc34..33fcd750aee 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -64,7 +64,7 @@ void swap_m3m3(float A[3][3], float B[3][3]);
void swap_m4m4(float A[4][4], float B[4][4]);
/* Build index shuffle matrix */
-void shuffle_m4(float R[4][4], int index[4]);
+void shuffle_m4(float R[4][4], const int index[4]);
/******************************** Arithmetic *********************************/
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index d46c02a961c..362ab3769a0 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -133,6 +133,7 @@ MINLINE void sub_v3_v3v3_db(double r[3], const double a[3], const double b[3]);
MINLINE void sub_v4_v4(float r[4], const float a[4]);
MINLINE void sub_v4_v4v4(float r[4], const float a[4], const float b[4]);
+MINLINE void sub_v2db_v2fl_v2fl(double r[2], const float a[2], const float b[2]);
MINLINE void sub_v3db_v3fl_v3fl(double r[3], const float a[3], const float b[3]);
MINLINE void mul_v2_fl(float r[2], float f);
@@ -205,6 +206,7 @@ MINLINE double dot_v3db_v3fl(const double a[3], const float b[3]) ATTR_WARN_UNUS
MINLINE double dot_v3v3_db(const double a[3], const double b[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float cross_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE double cross_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3]);
MINLINE void cross_v3_v3v3_hi_prec(float r[3], const float a[3], const float b[3]);
MINLINE void cross_v3_v3v3_db(double r[3], const double a[3], const double b[3]);
@@ -221,6 +223,7 @@ MINLINE float len_manhattan_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE int len_manhattan_v2_int(const int v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE double len_v2_db(const double v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE double len_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2v2_int(const int v1[2], const int v2[2]);
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index de9fc956bfb..9f65fe0742e 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -19,61 +19,86 @@
/** \file
* \ingroup bli
+ * Some of the functions below have very similar alternatives in the standard library. However, it
+ * is rather annoying to use those when debugging. Therefore, some more specialized and easier to
+ * debug functions are provided here.
*/
#include <memory>
+#include <new>
+#include <type_traits>
#include "BLI_utildefines.h"
namespace blender {
/**
- * Call the default constructor on n consecutive elements. For trivially constructible types, this
- * does nothing.
+ * Call the destructor on n consecutive values. For trivially destructible types, this does
+ * nothing.
+ *
+ * Exception Safety: Destructors shouldn't throw exceptions.
*
* Before:
- * ptr: uninitialized
- * After:
* ptr: initialized
+ * After:
+ * ptr: uninitialized
*/
-template<typename T> void default_construct_n(T *ptr, uint n)
+template<typename T> void destruct_n(T *ptr, int64_t n)
{
+ BLI_assert(n >= 0);
+
+ static_assert(std::is_nothrow_destructible_v<T>,
+ "This should be true for all types. Destructors are noexcept by default.");
+
/* This is not strictly necessary, because the loop below will be optimized away anyway. It is
- * nice to make behavior this explicitely, though. */
- if (std::is_trivially_constructible<T>::value) {
+ * nice to make behavior this explicitly, though. */
+ if (std::is_trivially_destructible_v<T>) {
return;
}
- for (uint i = 0; i < n; i++) {
- new (ptr + i) T;
+ for (int64_t i = 0; i < n; i++) {
+ ptr[i].~T();
}
}
/**
- * Call the destructor on n consecutive values. For trivially destructible types, this does
- * nothing.
+ * Call the default constructor on n consecutive elements. For trivially constructible types, this
+ * does nothing.
+ *
+ * Exception Safety: Strong.
*
* Before:
- * ptr: initialized
- * After:
* ptr: uninitialized
+ * After:
+ * ptr: initialized
*/
-template<typename T> void destruct_n(T *ptr, uint n)
+template<typename T> void default_construct_n(T *ptr, int64_t n)
{
+ BLI_assert(n >= 0);
+
/* This is not strictly necessary, because the loop below will be optimized away anyway. It is
- * nice to make behavior this explicitely, though. */
- if (std::is_trivially_destructible<T>::value) {
+ * nice to make behavior this explicitly, though. */
+ if (std::is_trivially_constructible_v<T>) {
return;
}
- for (uint i = 0; i < n; i++) {
- ptr[i].~T();
+ int64_t current = 0;
+ try {
+ for (; current < n; current++) {
+ new ((void *)(ptr + current)) T;
+ }
+ }
+ catch (...) {
+ destruct_n(ptr, current);
+ throw;
}
}
/**
* Copy n values from src to dst.
*
+ * Exception Safety: Basic.
+ *
* Before:
* src: initialized
* dst: initialized
@@ -81,9 +106,11 @@ template<typename T> void destruct_n(T *ptr, uint n)
* src: initialized
* dst: initialized
*/
-template<typename T> void initialized_copy_n(const T *src, uint n, T *dst)
+template<typename T> void initialized_copy_n(const T *src, int64_t n, T *dst)
{
- for (uint i = 0; i < n; i++) {
+ BLI_assert(n >= 0);
+
+ for (int64_t i = 0; i < n; i++) {
dst[i] = src[i];
}
}
@@ -91,6 +118,36 @@ template<typename T> void initialized_copy_n(const T *src, uint n, T *dst)
/**
* Copy n values from src to dst.
*
+ * Exception Safety: Strong.
+ *
+ * Before:
+ * src: initialized
+ * dst: uninitialized
+ * After:
+ * src: initialized
+ * dst: initialized
+ */
+template<typename T> void uninitialized_copy_n(const T *src, int64_t n, T *dst)
+{
+ BLI_assert(n >= 0);
+
+ int64_t current = 0;
+ try {
+ for (; current < n; current++) {
+ new ((void *)(dst + current)) T(src[current]);
+ }
+ }
+ catch (...) {
+ destruct_n(dst, current);
+ throw;
+ }
+}
+
+/**
+ * Convert n values from type `From` to type `To`.
+ *
+ * Exception Safety: Strong.
+ *
* Before:
* src: initialized
* dst: uninitialized
@@ -98,16 +155,28 @@ template<typename T> void initialized_copy_n(const T *src, uint n, T *dst)
* src: initialized
* dst: initialized
*/
-template<typename T> void uninitialized_copy_n(const T *src, uint n, T *dst)
+template<typename From, typename To>
+void uninitialized_convert_n(const From *src, int64_t n, To *dst)
{
- for (uint i = 0; i < n; i++) {
- new (dst + i) T(src[i]);
+ BLI_assert(n >= 0);
+
+ int64_t current = 0;
+ try {
+ for (; current < n; current++) {
+ new ((void *)(dst + current)) To((To)src[current]);
+ }
+ }
+ catch (...) {
+ destruct_n(dst, current);
+ throw;
}
}
/**
* Move n values from src to dst.
*
+ * Exception Safety: Basic.
+ *
* Before:
* src: initialized
* dst: initialized
@@ -115,9 +184,11 @@ template<typename T> void uninitialized_copy_n(const T *src, uint n, T *dst)
* src: initialized, moved-from
* dst: initialized
*/
-template<typename T> void initialized_move_n(T *src, uint n, T *dst)
+template<typename T> void initialized_move_n(T *src, int64_t n, T *dst)
{
- for (uint i = 0; i < n; i++) {
+ BLI_assert(n >= 0);
+
+ for (int64_t i = 0; i < n; i++) {
dst[i] = std::move(src[i]);
}
}
@@ -125,6 +196,8 @@ template<typename T> void initialized_move_n(T *src, uint n, T *dst)
/**
* Move n values from src to dst.
*
+ * Exception Safety: Basic.
+ *
* Before:
* src: initialized
* dst: uninitialized
@@ -132,10 +205,19 @@ template<typename T> void initialized_move_n(T *src, uint n, T *dst)
* src: initialized, moved-from
* dst: initialized
*/
-template<typename T> void uninitialized_move_n(T *src, uint n, T *dst)
+template<typename T> void uninitialized_move_n(T *src, int64_t n, T *dst)
{
- for (uint i = 0; i < n; i++) {
- new (dst + i) T(std::move(src[i]));
+ BLI_assert(n >= 0);
+
+ int64_t current = 0;
+ try {
+ for (; current < n; current++) {
+ new ((void *)(dst + current)) T(std::move(src[current]));
+ }
+ }
+ catch (...) {
+ destruct_n(dst, current);
+ throw;
}
}
@@ -143,6 +225,8 @@ template<typename T> void uninitialized_move_n(T *src, uint n, T *dst)
* Relocate n values from src to dst. Relocation is a move followed by destruction of the src
* value.
*
+ * Exception Safety: Basic.
+ *
* Before:
* src: initialized
* dst: initialized
@@ -150,8 +234,10 @@ template<typename T> void uninitialized_move_n(T *src, uint n, T *dst)
* src: uninitialized
* dst: initialized
*/
-template<typename T> void initialized_relocate_n(T *src, uint n, T *dst)
+template<typename T> void initialized_relocate_n(T *src, int64_t n, T *dst)
{
+ BLI_assert(n >= 0);
+
initialized_move_n(src, n, dst);
destruct_n(src, n);
}
@@ -160,15 +246,19 @@ template<typename T> void initialized_relocate_n(T *src, uint n, T *dst)
* Relocate n values from src to dst. Relocation is a move followed by destruction of the src
* value.
*
+ * Exception Safety: Basic.
+ *
* Before:
* src: initialized
- * dst: uinitialized
+ * dst: uninitialized
* After:
* src: uninitialized
* dst: initialized
*/
-template<typename T> void uninitialized_relocate_n(T *src, uint n, T *dst)
+template<typename T> void uninitialized_relocate_n(T *src, int64_t n, T *dst)
{
+ BLI_assert(n >= 0);
+
uninitialized_move_n(src, n, dst);
destruct_n(src, n);
}
@@ -176,14 +266,18 @@ template<typename T> void uninitialized_relocate_n(T *src, uint n, T *dst)
/**
* Copy the value to n consecutive elements.
*
+ * Exception Safety: Basic.
+ *
* Before:
* dst: initialized
* After:
* dst: initialized
*/
-template<typename T> void initialized_fill_n(T *dst, uint n, const T &value)
+template<typename T> void initialized_fill_n(T *dst, int64_t n, const T &value)
{
- for (uint i = 0; i < n; i++) {
+ BLI_assert(n >= 0);
+
+ for (int64_t i = 0; i < n; i++) {
dst[i] = value;
}
}
@@ -191,24 +285,27 @@ template<typename T> void initialized_fill_n(T *dst, uint n, const T &value)
/**
* Copy the value to n consecutive elements.
*
+ * Exception Safety: Strong.
+ *
* Before:
* dst: uninitialized
* After:
* dst: initialized
*/
-template<typename T> void uninitialized_fill_n(T *dst, uint n, const T &value)
+template<typename T> void uninitialized_fill_n(T *dst, int64_t n, const T &value)
{
- for (uint i = 0; i < n; i++) {
- new (dst + i) T(value);
- }
-}
+ BLI_assert(n >= 0);
-/**
- * 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)...));
+ int64_t current = 0;
+ try {
+ for (; current < n; current++) {
+ new ((void *)(dst + current)) T(value);
+ }
+ }
+ catch (...) {
+ destruct_n(dst, current);
+ throw;
+ }
}
template<typename T> struct DestructValueAtAddress {
@@ -225,30 +322,112 @@ template<typename T> struct DestructValueAtAddress {
template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddress<T>>;
/**
- * An `AlignedBuffer` is simply a byte array with the given size and alignment. The buffer will
+ * An `AlignedBuffer` is a byte array with at least the given size and alignment. The buffer will
* not be initialized by the default constructor.
- *
- * This can be used to reserve memory for C++ objects whose lifetime is different from the
- * lifetime of the object they are embedded in. It's used by containers with small buffer
- * optimization and hash table implementations.
*/
template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer {
private:
/* Don't create an empty array. This causes problems with some compilers. */
- char m_buffer[(Size > 0) ? Size : 1];
+ char buffer_[(Size > 0) ? Size : 1];
public:
+ operator void *()
+ {
+ return (void *)buffer_;
+ }
+
+ operator const void *() const
+ {
+ return (void *)buffer_;
+ }
+
void *ptr()
{
- return (void *)m_buffer;
+ return (void *)buffer_;
}
const void *ptr() const
{
- return (const void *)m_buffer;
+ return (const void *)buffer_;
+ }
+};
+
+/**
+ * This can be used to reserve memory for C++ objects whose lifetime is different from the
+ * lifetime of the object they are embedded in. It's used by containers with small buffer
+ * optimization and hash table implementations.
+ */
+template<typename T, int64_t Size = 1> class TypedBuffer {
+ private:
+ AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_;
+
+ public:
+ operator T *()
+ {
+ return (T *)&buffer_;
+ }
+
+ operator const T *() const
+ {
+ return (const T *)&buffer_;
}
+
+ T &operator*()
+ {
+ return *(T *)&buffer_;
+ }
+
+ const T &operator*() const
+ {
+ return *(const T *)&buffer_;
+ }
+
+ T *ptr()
+ {
+ return (T *)&buffer_;
+ }
+
+ const T *ptr() const
+ {
+ return (const T *)&buffer_;
+ }
+
+ T &ref()
+ {
+ return *(T *)&buffer_;
+ }
+
+ const T &ref() const
+ {
+ return *(const T *)&buffer_;
+ }
+};
+
+/**
+ * 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 {
};
+/**
+ * Helper variable that checks if a pointer type can be converted into another pointer type without
+ * issues. Possible issues are casting away const and casting a pointer to a child class.
+ * Adding const or casting to a parent class is fine.
+ */
+template<typename From, typename To>
+inline constexpr bool is_convertible_pointer_v =
+ std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>;
+
+/**
+ * Inline buffers for small-object-optimization should be disable by default. Otherwise we might
+ * get large unexpected allocations on the stack.
+ */
+inline constexpr int64_t default_inline_buffer_capacity(size_t element_size)
+{
+ return ((int64_t)element_size < 100) ? 4 : 0;
+}
+
} // namespace blender
#endif /* __BLI_MEMORY_UTILS_HH__ */
diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh
new file mode 100644
index 00000000000..c20c4ef9677
--- /dev/null
+++ b/source/blender/blenlib/BLI_multi_value_map.hh
@@ -0,0 +1,134 @@
+/*
+ * 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 __BLI_MULTI_VALUE_MAP_HH__
+#define __BLI_MULTI_VALUE_MAP_HH__
+
+/** \file
+ * \ingroup bli
+ *
+ * A `blender::MultiValueMap<Key, Value>` is an unordered associative container that stores
+ * key-value pairs. It is different from `blender::Map` in that it can store multiple values for
+ * the same key. The list of values that corresponds to a specific key can contain duplicates
+ * and their order is maintained.
+ *
+ * This data structure is different from a `std::multi_map`, because multi_map can store the same
+ * key more than once and MultiValueMap can't.
+ *
+ * Currently, this class exists mainly for convenience. There are no performance benefits over
+ * using Map<Key, Vector<Value>>. In the future, a better implementation for this data structure
+ * can be developed.
+ */
+
+#include "BLI_map.hh"
+#include "BLI_vector.hh"
+
+namespace blender {
+
+template<typename Key, typename Value> class MultiValueMap {
+ private:
+ using MapType = Map<Key, Vector<Value>>;
+ MapType map_;
+
+ public:
+ /**
+ * Add a new value for the given key. If the map contains the key already, the value will be
+ * appended to the list of corresponding values.
+ */
+ void add(const Key &key, const Value &value)
+ {
+ this->add_as(key, value);
+ }
+ void add(const Key &key, Value &&value)
+ {
+ this->add_as(key, std::move(value));
+ }
+ void add(Key &&key, const Value &value)
+ {
+ this->add_as(std::move(key), value);
+ }
+ void add(Key &&key, Value &&value)
+ {
+ this->add_as(std::move(key), std::move(value));
+ }
+ template<typename ForwardKey, typename ForwardValue>
+ void add_as(ForwardKey &&key, ForwardValue &&value)
+ {
+ Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key));
+ vector.append(std::forward<ForwardValue>(value));
+ }
+
+ /**
+ * Add all given values to the key.
+ */
+ void add_multiple(const Key &key, Span<Value> values)
+ {
+ this->add_multiple_as(key, values);
+ }
+ void add_multiple(Key &&key, Span<Value> values)
+ {
+ this->add_multiple_as(std::move(key), values);
+ }
+ template<typename ForwardKey> void add_multiple_as(ForwardKey &&key, Span<Value> values)
+ {
+ Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key));
+ vector.extend(values);
+ }
+
+ /**
+ * Get a span to all the values that are stored for the given key.
+ */
+ Span<Value> lookup(const Key &key) const
+ {
+ return this->lookup_as(key);
+ }
+ template<typename ForwardKey> Span<Value> lookup_as(const ForwardKey &key) const
+ {
+ const Vector<Value> *vector = map_.lookup_ptr_as(key);
+ if (vector != nullptr) {
+ return vector->as_span();
+ }
+ return {};
+ }
+
+ /**
+ * Note: This signature will change when the implementation changes.
+ */
+ typename MapType::ItemIterator items() const
+ {
+ return map_.items();
+ }
+
+ /**
+ * Note: This signature will change when the implementation changes.
+ */
+ typename MapType::KeyIterator keys() const
+ {
+ return map_.keys();
+ }
+
+ /**
+ * Note: This signature will change when the implementation changes.
+ */
+ typename MapType::ValueIterator values() const
+ {
+ return map_.values();
+ }
+};
+
+} // namespace blender
+
+#endif /* __BLI_MULTI_VALUE_MAP_HH__ */
diff --git a/source/blender/blenlib/BLI_optional.hh b/source/blender/blenlib/BLI_optional.hh
deleted file mode 100644
index b5f98d6fa97..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 (this->value_ptr()) T(value);
- m_set = true;
- }
- }
-
- void set(T &&value)
- {
- if (m_set) {
- this->value() = std::move(value);
- }
- else {
- new (this->value_ptr()) T(std::move(value));
- m_set = true;
- }
- }
-
- void set_new(const T &value)
- {
- BLI_assert(!m_set);
- new (this->value_ptr()) T(value);
- m_set = true;
- }
-
- void set_new(T &&value)
- {
- BLI_assert(!m_set);
- new (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_probing_strategies.hh b/source/blender/blenlib/BLI_probing_strategies.hh
index 29ebe28aff9..0e5338fa6ed 100644
--- a/source/blender/blenlib/BLI_probing_strategies.hh
+++ b/source/blender/blenlib/BLI_probing_strategies.hh
@@ -25,17 +25,17 @@
* values based on an initial hash value.
*
* A probing strategy has to implement the following methods:
- * - Constructor(uint32_t hash): Start a new probing sequence based on the given hash.
- * - get() const -> uint32_t: Get the current value in the sequence.
+ * - Constructor(uint64_t hash): Start a new probing sequence based on the given hash.
+ * - get() const -> uint64_t: Get the current value in the sequence.
* - next() -> void: Update the internal state, so that the next value can be accessed with get().
- * - linear_steps() -> uint32_t: Returns number of linear probing steps that should be done.
+ * - linear_steps() -> int64_t: Returns number of linear probing steps that should be done.
*
* Using linear probing steps between larger jumps can result in better performance, due to
* improved cache usage. It's a way of getting the benefits or linear probing without the
* clustering issues. However, more linear steps can also make things slower when the initial hash
* produces many collisions.
*
- * Every probing strategy has to guarantee, that every possible uint32_t is returned eventually.
+ * Every probing strategy has to guarantee, that every possible uint64_t is returned eventually.
* This is necessary for correctness. If this is not the case, empty slots might not be found.
*
* The SLOT_PROBING_BEGIN and SLOT_PROBING_END macros can be used to implement a loop that iterates
@@ -65,24 +65,24 @@ namespace blender {
*/
class LinearProbingStrategy {
private:
- uint32_t m_hash;
+ uint64_t hash_;
public:
- LinearProbingStrategy(uint32_t hash) : m_hash(hash)
+ LinearProbingStrategy(const uint64_t hash) : hash_(hash)
{
}
void next()
{
- m_hash++;
+ hash_++;
}
- uint32_t get() const
+ uint64_t get() const
{
- return m_hash;
+ return hash_;
}
- uint32_t linear_steps() const
+ int64_t linear_steps() const
{
return UINT32_MAX;
}
@@ -101,28 +101,28 @@ class LinearProbingStrategy {
*/
class QuadraticProbingStrategy {
private:
- uint32_t m_original_hash;
- uint32_t m_current_hash;
- uint32_t m_iteration;
+ uint64_t original_hash_;
+ uint64_t current_hash_;
+ uint64_t iteration_;
public:
- QuadraticProbingStrategy(uint32_t hash)
- : m_original_hash(hash), m_current_hash(hash), m_iteration(1)
+ QuadraticProbingStrategy(const uint64_t hash)
+ : original_hash_(hash), current_hash_(hash), iteration_(1)
{
}
void next()
{
- m_current_hash = m_original_hash + ((m_iteration * m_iteration + m_iteration) >> 1);
- m_iteration++;
+ current_hash_ = original_hash_ + ((iteration_ * iteration_ + iteration_) >> 1);
+ iteration_++;
}
- uint32_t get() const
+ uint64_t get() const
{
- return m_current_hash;
+ return current_hash_;
}
- uint32_t linear_steps() const
+ int64_t linear_steps() const
{
return 1;
}
@@ -138,13 +138,13 @@ class QuadraticProbingStrategy {
* PreShuffle: When true, the initial call to next() will be done to the constructor. This can help
* when the hash function has put little information into the lower bits.
*/
-template<uint32_t LinearSteps = 1, bool PreShuffle = false> class PythonProbingStrategy {
+template<uint64_t LinearSteps = 1, bool PreShuffle = false> class PythonProbingStrategy {
private:
- uint32_t m_hash;
- uint32_t m_perturb;
+ uint64_t hash_;
+ uint64_t perturb_;
public:
- PythonProbingStrategy(uint32_t hash) : m_hash(hash), m_perturb(hash)
+ PythonProbingStrategy(const uint64_t hash) : hash_(hash), perturb_(hash)
{
if (PreShuffle) {
this->next();
@@ -153,16 +153,16 @@ template<uint32_t LinearSteps = 1, bool PreShuffle = false> class PythonProbingS
void next()
{
- m_perturb >>= 5;
- m_hash = 5 * m_hash + 1 + m_perturb;
+ perturb_ >>= 5;
+ hash_ = 5 * hash_ + 1 + perturb_;
}
- uint32_t get() const
+ uint64_t get() const
{
- return m_hash;
+ return hash_;
}
- uint32_t linear_steps() const
+ int64_t linear_steps() const
{
return LinearSteps;
}
@@ -173,13 +173,13 @@ template<uint32_t LinearSteps = 1, bool PreShuffle = false> class PythonProbingS
* method. This way more bits are taken into account earlier. After a couple of collisions (that
* should happen rarely), it will fallback to a sequence that hits every slot.
*/
-template<uint32_t LinearSteps = 2, bool PreShuffle = false> class ShuffleProbingStrategy {
+template<uint64_t LinearSteps = 2, bool PreShuffle = false> class ShuffleProbingStrategy {
private:
- uint32_t m_hash;
- uint32_t m_perturb;
+ uint64_t hash_;
+ uint64_t perturb_;
public:
- ShuffleProbingStrategy(uint32_t hash) : m_hash(hash), m_perturb(hash)
+ ShuffleProbingStrategy(const uint64_t hash) : hash_(hash), perturb_(hash)
{
if (PreShuffle) {
this->next();
@@ -188,21 +188,21 @@ template<uint32_t LinearSteps = 2, bool PreShuffle = false> class ShuffleProbing
void next()
{
- if (m_perturb != 0) {
- m_perturb >>= 10;
- m_hash = ((m_hash >> 16) ^ m_hash) * 0x45d9f3b + m_perturb;
+ if (perturb_ != 0) {
+ perturb_ >>= 10;
+ hash_ = ((hash_ >> 16) ^ hash_) * 0x45d9f3b + perturb_;
}
else {
- m_hash = 5 * m_hash + 1;
+ hash_ = 5 * hash_ + 1;
}
}
- uint32_t get() const
+ uint64_t get() const
{
- return m_hash;
+ return hash_;
}
- uint32_t linear_steps() const
+ int64_t linear_steps() const
{
return LinearSteps;
}
@@ -233,10 +233,10 @@ using DefaultProbingStrategy = PythonProbingStrategy<>;
#define SLOT_PROBING_BEGIN(PROBING_STRATEGY, HASH, MASK, R_SLOT_INDEX) \
PROBING_STRATEGY probing_strategy(HASH); \
do { \
- uint32_t linear_offset = 0; \
- uint32_t current_hash = probing_strategy.get(); \
+ int64_t linear_offset = 0; \
+ uint64_t current_hash = probing_strategy.get(); \
do { \
- uint32_t R_SLOT_INDEX = (current_hash + linear_offset) & MASK;
+ int64_t R_SLOT_INDEX = (int64_t)((current_hash + (uint64_t)linear_offset) & MASK);
#define SLOT_PROBING_END() \
} while (++linear_offset < probing_strategy.linear_steps()); \
diff --git a/source/blender/blenlib/BLI_rand.h b/source/blender/blenlib/BLI_rand.h
index ae78ea3af16..c55bbd26db5 100644
--- a/source/blender/blenlib/BLI_rand.h
+++ b/source/blender/blenlib/BLI_rand.h
@@ -105,12 +105,12 @@ int BLI_rng_thread_rand(RNG_THREAD_ARRAY *rngarr, int thread) ATTR_WARN_UNUSED_R
/** Return the _n_th number of the given low-discrepancy sequence. */
void BLI_halton_1d(unsigned int prime, double offset, int n, double *r);
-void BLI_halton_2d(unsigned int prime[2], double offset[2], int n, double *r);
-void BLI_halton_3d(unsigned int prime[3], double offset[3], int n, double *r);
+void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r);
+void BLI_halton_3d(const unsigned int prime[3], double offset[3], int n, double *r);
void BLI_hammersley_1d(unsigned int n, double *r);
/** Return the whole low-discrepancy sequence up to _n_. */
-void BLI_halton_2d_sequence(unsigned int prime[2], double offset[2], int n, double *r);
+void BLI_halton_2d_sequence(const unsigned int prime[2], double offset[2], int n, double *r);
void BLI_hammersley_2d_sequence(unsigned int n, double *r);
#ifdef __cplusplus
diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh
new file mode 100644
index 00000000000..7a98ee0f2bb
--- /dev/null
+++ b/source/blender/blenlib/BLI_rand.hh
@@ -0,0 +1,147 @@
+/*
+ * 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
+ */
+
+#ifndef __BLI_RAND_HH__
+#define __BLI_RAND_HH__
+
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+#include "BLI_math.h"
+#include "BLI_span.hh"
+#include "BLI_utildefines.h"
+
+namespace blender {
+
+class RandomNumberGenerator {
+ private:
+ uint64_t x_;
+
+ public:
+ RandomNumberGenerator(uint32_t seed = 0)
+ {
+ this->seed(seed);
+ }
+
+ /**
+ * Set the seed for future random numbers.
+ */
+ void seed(uint32_t seed)
+ {
+ constexpr uint64_t lowseed = 0x330E;
+ x_ = (((uint64_t)seed) << 16) | lowseed;
+ }
+
+ void seed_random(uint32_t seed);
+
+ uint32_t get_uint32()
+ {
+ this->step();
+ return (uint32_t)(x_ >> 17);
+ }
+
+ int32_t get_int32()
+ {
+ this->step();
+ return (int32_t)(x_ >> 17);
+ }
+
+ /**
+ * \return Random value (0..N), but never N.
+ */
+ int32_t get_int32(int32_t max_exclusive)
+ {
+ BLI_assert(max_exclusive > 0);
+ return this->get_int32() % max_exclusive;
+ }
+
+ /**
+ * \return Random value (0..1), but never 1.0.
+ */
+ double get_double()
+ {
+ return (double)this->get_int32() / 0x80000000;
+ }
+
+ /**
+ * \return Random value (0..1), but never 1.0.
+ */
+ float get_float()
+ {
+ return (float)this->get_int32() / 0x80000000;
+ }
+
+ template<typename T> void shuffle(MutableSpan<T> values)
+ {
+ /* Cannot shuffle arrays of this size yet. */
+ BLI_assert(values.size() <= INT32_MAX);
+
+ for (int i = values.size() - 1; i >= 2; i--) {
+ int j = this->get_int32(i);
+ if (i != j) {
+ std::swap(values[i], values[j]);
+ }
+ }
+ }
+
+ /**
+ * Compute uniformly distributed barycentric coordinates.
+ */
+ float3 get_barycentric_coordinates()
+ {
+ float rand1 = this->get_float();
+ float rand2 = this->get_float();
+
+ if (rand1 + rand2 > 1.0f) {
+ rand1 = 1.0f - rand1;
+ rand2 = 1.0f - rand2;
+ }
+
+ return float3(rand1, rand2, 1.0f - rand1 - rand2);
+ }
+
+ float2 get_unit_float2();
+ float3 get_unit_float3();
+ float2 get_triangle_sample(float2 v1, float2 v2, float2 v3);
+ void get_bytes(MutableSpan<char> r_bytes);
+
+ /**
+ * Simulate getting \a n random values.
+ */
+ void skip(int64_t n)
+ {
+ while (n--) {
+ this->step();
+ }
+ }
+
+ private:
+ void step()
+ {
+ constexpr uint64_t multiplier = 0x5DEECE66Dll;
+ constexpr uint64_t addend = 0xB;
+ constexpr uint64_t mask = 0x0000FFFFFFFFFFFFll;
+
+ x_ = (multiplier * x_ + addend) & mask;
+ }
+};
+
+} // namespace blender
+
+#endif /* __BLI_RAND_HH__ */
diff --git a/source/blender/blenlib/BLI_rect.h b/source/blender/blenlib/BLI_rect.h
index b1faae03583..14d18308ed6 100644
--- a/source/blender/blenlib/BLI_rect.h
+++ b/source/blender/blenlib/BLI_rect.h
@@ -63,6 +63,7 @@ void BLI_rcti_translate(struct rcti *rect, int x, int y);
void BLI_rcti_recenter(struct rcti *rect, int x, int y);
void BLI_rctf_recenter(struct rctf *rect, float x, float y);
void BLI_rcti_resize(struct rcti *rect, int x, int y);
+void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y);
void BLI_rctf_resize(struct rctf *rect, float x, float y);
void BLI_rcti_scale(rcti *rect, const float scale);
void BLI_rctf_scale(rctf *rect, const float scale);
diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_collector.hh
new file mode 100644
index 00000000000..e1be87d8af2
--- /dev/null
+++ b/source/blender/blenlib/BLI_resource_collector.hh
@@ -0,0 +1,151 @@
+/*
+ * 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 __BLI_RESOURCE_COLLECTOR_HH__
+#define __BLI_RESOURCE_COLLECTOR_HH__
+
+/** \file
+ * \ingroup bli
+ *
+ * A ResourceCollector holds an arbitrary set of resources, that will be destructed and/or freed
+ * when the ResourceCollector is destructed. This is useful when some object has to take ownership
+ * of other objects, but it does not know the type of those other objects.
+ *
+ * Resources owned by the ResourceCollector will be freed in reverse order. That allows resources
+ * that are added later to depend on resources that have been added before.
+ */
+
+#include "BLI_linear_allocator.hh"
+#include "BLI_utility_mixins.hh"
+#include "BLI_vector.hh"
+
+namespace blender {
+
+class ResourceCollector : NonCopyable, NonMovable {
+ private:
+ struct ResourceData {
+ void *data;
+ void (*free)(void *data);
+ const char *debug_name;
+ };
+
+ LinearAllocator<> m_allocator;
+ Vector<ResourceData> m_resources;
+
+ public:
+ ResourceCollector() = default;
+
+ ~ResourceCollector()
+ {
+ /* Free in reversed order. */
+ for (int64_t i = m_resources.size(); i--;) {
+ ResourceData &data = m_resources[i];
+ data.free(data.data);
+ }
+ }
+
+ /**
+ * Pass ownership of the resource to the ResourceCollector. It will be destructed and freed when
+ * the collector is destructed.
+ */
+ template<typename T> void add(std::unique_ptr<T> resource, const char *name)
+ {
+ BLI_assert(resource.get() != nullptr);
+ this->add(
+ resource.release(),
+ [](void *data) {
+ T *typed_data = reinterpret_cast<T *>(data);
+ delete typed_data;
+ },
+ name);
+ }
+
+ /**
+ * Pass ownership of the resource to the ResourceCollector. It will be destructed when the
+ * collector is destructed.
+ */
+ template<typename T> void add(destruct_ptr<T> resource, const char *name)
+ {
+ /* There is no need to keep track of such types. */
+ if (std::is_trivially_destructible_v<T>) {
+ resource.release();
+ return;
+ }
+
+ BLI_assert(resource.get() != nullptr);
+ this->add(
+ resource.release(),
+ [](void *data) {
+ T *typed_data = reinterpret_cast<T *>(data);
+ typed_data->~T();
+ },
+ name);
+ }
+
+ /**
+ * Pass ownership of some resource to the ResourceCollector. The given free function will be
+ * called when the collector is destructed.
+ */
+ void add(void *userdata, void (*free)(void *), const char *name)
+ {
+ ResourceData data;
+ data.debug_name = name;
+ data.data = userdata;
+ data.free = free;
+ m_resources.append(data);
+ }
+
+ /**
+ * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory
+ * allocated through this allocator will be freed when the collector is destructed.
+ */
+ LinearAllocator<> &linear_allocator()
+ {
+ return m_allocator;
+ }
+
+ /**
+ * Utility method to construct an instance of type T that will be owned by the ResourceCollector.
+ */
+ template<typename T, typename... Args> T &construct(const char *name, Args &&... args)
+ {
+ T *value = m_allocator.construct<T>(std::forward<Args>(args)...);
+ this->add(destruct_ptr<T>(value), name);
+ return *value;
+ }
+
+ /**
+ * Print the names of all the resources that are owned by this ResourceCollector. This can be
+ * useful for debugging.
+ */
+ void print(StringRef name) const
+ {
+ if (m_resources.size() == 0) {
+ std::cout << "\"" << name << "\" has no resources.\n";
+ return;
+ }
+ else {
+ std::cout << "Resources for \"" << name << "\":\n";
+ for (const ResourceData &data : m_resources) {
+ std::cout << " " << data.data << ": " << data.debug_name << '\n';
+ }
+ }
+ }
+};
+
+} // namespace blender
+
+#endif /* __BLI_RESOURCE_COLLECTOR_HH__ */
diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh
index ece9fb05d8c..90adea69e06 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
@@ -89,11 +89,8 @@ template<
* The minimum number of elements that can be stored in this Set without doing a heap
* allocation. This is useful when you expect to have many small sets. However, keep in mind
* that (unlike vector) initializing a set has a O(n) cost in the number of slots.
- *
- * When Key is large, the small buffer optimization is disabled by default to avoid large
- * unexpected allocations on the stack. It can still be enabled explicitely though.
*/
- uint32_t InlineBufferCapacity = (sizeof(Key) < 100) ? 4 : 0,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key)),
/**
* The strategy used to deal with collisions. They are defined in BLI_probing_strategies.hh.
*/
@@ -128,30 +125,30 @@ class Set {
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
* subtracting the removed slots from the occupied-and-removed slots.
*/
- uint32_t m_removed_slots;
- uint32_t m_occupied_and_removed_slots;
+ int64_t removed_slots_;
+ int64_t occupied_and_removed_slots_;
/**
* The maximum number of slots that can be used (either occupied or removed) until the set has to
* grow. This is the total number of slots times the max load factor.
*/
- uint32_t m_usable_slots;
+ int64_t usable_slots_;
/**
* The number of slots minus one. This is a bit mask that can be used to turn any integer into a
* valid slot index efficiently.
*/
- uint32_t m_slot_mask;
+ uint64_t slot_mask_;
/** This is called to hash incoming keys. */
- Hash m_hash;
+ Hash hash_;
/** This is called to check equality of two keys. */
- IsEqual m_is_equal;
+ IsEqual is_equal_;
/** The max load factor is 1/2 = 50% by default. */
#define LOAD_FACTOR 1, 2
- LoadFactor m_max_load_factor = LoadFactor(LOAD_FACTOR);
+ LoadFactor max_load_factor_ = LoadFactor(LOAD_FACTOR);
using SlotArray =
Array<Slot, LoadFactor::compute_total_slots(InlineBufferCapacity, LOAD_FACTOR), Allocator>;
#undef LOAD_FACTOR
@@ -160,12 +157,12 @@ class Set {
* This is the array that contains the actual slots. There is always at least one empty slot and
* the size of the array is a power of two.
*/
- SlotArray m_slots;
+ SlotArray slots_;
/** Iterate over a slot index sequence for a given hash. */
#define SET_SLOT_PROBING_BEGIN(HASH, R_SLOT) \
- SLOT_PROBING_BEGIN (ProbingStrategy, HASH, m_slot_mask, SLOT_INDEX) \
- auto &R_SLOT = m_slots[SLOT_INDEX];
+ SLOT_PROBING_BEGIN (ProbingStrategy, HASH, slot_mask_, SLOT_INDEX) \
+ auto &R_SLOT = slots_[SLOT_INDEX];
#define SET_SLOT_PROBING_END() SLOT_PROBING_END()
public:
@@ -175,11 +172,11 @@ class Set {
* grow operation is performed on the first insertion.
*/
Set()
- : m_removed_slots(0),
- m_occupied_and_removed_slots(0),
- m_usable_slots(0),
- m_slot_mask(0),
- m_slots(1)
+ : removed_slots_(0),
+ occupied_and_removed_slots_(0),
+ usable_slots_(0),
+ slot_mask_(0),
+ slots_(1)
{
}
@@ -196,13 +193,13 @@ class Set {
Set(const Set &other) = default;
Set(Set &&other) noexcept
- : m_removed_slots(other.m_removed_slots),
- m_occupied_and_removed_slots(other.m_occupied_and_removed_slots),
- m_usable_slots(other.m_usable_slots),
- m_slot_mask(other.m_slot_mask),
- m_hash(std::move(other.m_hash)),
- m_is_equal(std::move(other.m_is_equal)),
- m_slots(std::move(other.m_slots))
+ : removed_slots_(other.removed_slots_),
+ occupied_and_removed_slots_(other.occupied_and_removed_slots_),
+ usable_slots_(other.usable_slots_),
+ slot_mask_(other.slot_mask_),
+ hash_(std::move(other.hash_)),
+ is_equal_(std::move(other.is_equal_)),
+ slots_(std::move(other.slots_))
{
other.~Set();
new (&other) Set();
@@ -239,11 +236,11 @@ class Set {
*/
void add_new(const Key &key)
{
- this->add_new__impl(key, m_hash(key));
+ this->add_new__impl(key, hash_(key));
}
void add_new(Key &&key)
{
- this->add_new__impl(std::move(key), m_hash(key));
+ this->add_new__impl(std::move(key), hash_(key));
}
/**
@@ -260,13 +257,9 @@ class Set {
{
return this->add_as(std::move(key));
}
-
- /**
- * Same as `add`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool add_as(ForwardKey &&key)
{
- return this->add__impl(std::forward<ForwardKey>(key), m_hash(key));
+ return this->add__impl(std::forward<ForwardKey>(key), hash_(key));
}
/**
@@ -303,13 +296,53 @@ class Set {
{
return this->contains_as(key);
}
+ template<typename ForwardKey> bool contains_as(const ForwardKey &key) const
+ {
+ return this->contains__impl(key, hash_(key));
+ }
/**
- * Same as `contains`, but accepts other key types that are supported by the hash function.
+ * Returns the key that is stored in the set that compares equal to the given key. This invokes
+ * undefined behavior when the key is not in the set.
*/
- template<typename ForwardKey> bool contains_as(const ForwardKey &key) const
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ return this->lookup_key__impl(key, hash_(key));
+ }
+
+ /**
+ * Returns the key that is stored in the set that compares equal to the given key. If the key is
+ * not in the set, the given default value is returned instead.
+ */
+ const Key &lookup_key_default(const Key &key, const Key &default_value) const
{
- return this->contains__impl(key, m_hash(key));
+ return this->lookup_key_default_as(key, default_value);
+ }
+ template<typename ForwardKey>
+ const Key &lookup_key_default_as(const ForwardKey &key, const Key &default_key) const
+ {
+ const Key *ptr = this->lookup_key_ptr__impl(key, hash_(key));
+ if (ptr == nullptr) {
+ return default_key;
+ }
+ return *ptr;
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the set that compares equal to the given key.
+ * If the key is not in the set, nullptr is returned instead.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ return this->lookup_key_ptr__impl(key, hash_(key));
}
/**
@@ -321,13 +354,9 @@ class Set {
{
return this->remove_as(key);
}
-
- /**
- * Same as `remove`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool remove_as(const ForwardKey &key)
{
- return this->remove__impl(key, m_hash(key));
+ return this->remove__impl(key, hash_(key));
}
/**
@@ -337,14 +366,9 @@ class Set {
{
this->remove_contained_as(key);
}
-
- /**
- * Same as `remove_contained`, but accepts other key types that are supported by the hash
- * function.
- */
template<typename ForwardKey> void remove_contained_as(const ForwardKey &key)
{
- this->remove_contained__impl(key, m_hash(key));
+ this->remove_contained__impl(key, hash_(key));
}
/**
@@ -356,20 +380,20 @@ class Set {
*/
class Iterator {
private:
- const Slot *m_slots;
- uint32_t m_total_slots;
- uint32_t m_current_slot;
+ const Slot *slots_;
+ int64_t total_slots_;
+ int64_t current_slot_;
public:
- Iterator(const Slot *slots, uint32_t total_slots, uint32_t current_slot)
- : m_slots(slots), m_total_slots(total_slots), m_current_slot(current_slot)
+ Iterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ : slots_(slots), total_slots_(total_slots), current_slot_(current_slot)
{
}
Iterator &operator++()
{
- while (++m_current_slot < m_total_slots) {
- if (m_slots[m_current_slot].is_occupied()) {
+ while (++current_slot_ < total_slots_) {
+ if (slots_[current_slot_].is_occupied()) {
break;
}
}
@@ -378,22 +402,22 @@ class Set {
const Key &operator*() const
{
- return *m_slots[m_current_slot].key();
+ return *slots_[current_slot_].key();
}
friend bool operator!=(const Iterator &a, const Iterator &b)
{
- BLI_assert(a.m_slots == b.m_slots);
- BLI_assert(a.m_total_slots == b.m_total_slots);
- return a.m_current_slot != b.m_current_slot;
+ BLI_assert(a.slots_ == b.slots_);
+ BLI_assert(a.total_slots_ == b.total_slots_);
+ return a.current_slot_ != b.current_slot_;
}
};
Iterator begin() const
{
- for (uint32_t i = 0; i < m_slots.size(); i++) {
- if (m_slots[i].is_occupied()) {
- return Iterator(m_slots.data(), m_slots.size(), i);
+ for (int64_t i = 0; i < slots_.size(); i++) {
+ if (slots_[i].is_occupied()) {
+ return Iterator(slots_.data(), slots_.size(), i);
}
}
return this->end();
@@ -401,7 +425,7 @@ class Set {
Iterator end() const
{
- return Iterator(m_slots.data(), m_slots.size(), m_slots.size());
+ return Iterator(slots_.data(), slots_.size(), slots_.size());
}
/**
@@ -410,16 +434,16 @@ class Set {
void print_stats(StringRef name = "") const
{
HashTableStats stats(*this, *this);
- stats.print();
+ stats.print(name);
}
/**
* Get the number of collisions that the probing strategy has to go through to find the key or
* determine that it is not in the set.
*/
- uint32_t count_collisions(const Key &key) const
+ int64_t count_collisions(const Key &key) const
{
- return this->count_collisions__impl(key, m_hash(key));
+ return this->count_collisions__impl(key, hash_(key));
}
/**
@@ -433,7 +457,7 @@ class Set {
/**
* Creates a new slot array and reinserts all keys inside of that. This method can be used to get
- * rid of dummy slots. Also this is useful for benchmarking the grow function.
+ * rid of removed slots. Also this is useful for benchmarking the grow function.
*/
void rehash()
{
@@ -443,9 +467,9 @@ class Set {
/**
* Returns the number of keys stored in the set.
*/
- uint32_t size() const
+ int64_t size() const
{
- return m_occupied_and_removed_slots - m_removed_slots;
+ return occupied_and_removed_slots_ - removed_slots_;
}
/**
@@ -453,29 +477,29 @@ class Set {
*/
bool is_empty() const
{
- return m_occupied_and_removed_slots == m_removed_slots;
+ return occupied_and_removed_slots_ == removed_slots_;
}
/**
* Returns the number of available slots. This is mostly for debugging purposes.
*/
- uint32_t capacity() const
+ int64_t capacity() const
{
- return m_slots.size();
+ return slots_.size();
}
/**
* Returns the amount of removed slots in the set. This is mostly for debugging purposes.
*/
- uint32_t removed_amount() const
+ int64_t removed_amount() const
{
- return m_removed_slots;
+ return removed_slots_;
}
/**
* Returns the bytes required per element. This is mostly for debugging purposes.
*/
- uint32_t size_per_element() const
+ int64_t size_per_element() const
{
return sizeof(Slot);
}
@@ -484,18 +508,18 @@ class Set {
* Returns the approximate memory requirements of the set in bytes. This is more correct for
* larger sets.
*/
- uint32_t size_in_bytes() const
+ int64_t size_in_bytes() const
{
- return sizeof(Slot) * m_slots.size();
+ return sizeof(Slot) * slots_.size();
}
/**
* Potentially resize the set such that it can hold the specified number of keys without another
* grow operation.
*/
- void reserve(uint32_t n)
+ void reserve(const int64_t n)
{
- if (m_usable_slots < n) {
+ if (usable_slots_ < n) {
this->realloc_and_reinsert(n);
}
}
@@ -527,30 +551,31 @@ class Set {
}
private:
- BLI_NOINLINE void realloc_and_reinsert(uint32_t min_usable_slots)
+ BLI_NOINLINE void realloc_and_reinsert(const int64_t min_usable_slots)
{
- uint32_t total_slots, usable_slots;
- m_max_load_factor.compute_total_and_usable_slots(
+ int64_t total_slots, usable_slots;
+ max_load_factor_.compute_total_and_usable_slots(
SlotArray::inline_buffer_capacity(), min_usable_slots, &total_slots, &usable_slots);
- uint32_t new_slot_mask = total_slots - 1;
+ BLI_assert(total_slots >= 1);
+ const uint64_t new_slot_mask = (uint64_t)total_slots - 1;
/**
* Optimize the case when the set was empty beforehand. We can avoid some copies here.
*/
if (this->size() == 0) {
- m_slots.~Array();
- new (&m_slots) SlotArray(total_slots);
- m_removed_slots = 0;
- m_occupied_and_removed_slots = 0;
- m_usable_slots = usable_slots;
- m_slot_mask = new_slot_mask;
+ slots_.~Array();
+ new (&slots_) SlotArray(total_slots);
+ removed_slots_ = 0;
+ occupied_and_removed_slots_ = 0;
+ usable_slots_ = usable_slots;
+ slot_mask_ = new_slot_mask;
return;
}
/* The grown array that we insert the keys into. */
SlotArray new_slots(total_slots);
- for (Slot &slot : m_slots) {
+ for (Slot &slot : slots_) {
if (slot.is_occupied()) {
this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask);
}
@@ -558,19 +583,19 @@ class Set {
/* All occupied slots have been destructed already and empty/removed slots are assumed to be
* trivially destructible. */
- m_slots.clear_without_destruct();
- m_slots = std::move(new_slots);
- m_occupied_and_removed_slots -= m_removed_slots;
- m_usable_slots = usable_slots;
- m_removed_slots = 0;
- m_slot_mask = new_slot_mask;
+ slots_.clear_without_destruct();
+ slots_ = std::move(new_slots);
+ occupied_and_removed_slots_ -= removed_slots_;
+ usable_slots_ = usable_slots;
+ removed_slots_ = 0;
+ slot_mask_ = new_slot_mask;
}
void add_after_grow_and_destruct_old(Slot &old_slot,
SlotArray &new_slots,
- uint32_t new_slot_mask)
+ const uint64_t new_slot_mask)
{
- uint32_t hash = old_slot.get_hash(Hash());
+ const uint64_t hash = old_slot.get_hash(Hash());
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
@@ -582,20 +607,48 @@ class Set {
SLOT_PROBING_END();
}
- template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint32_t hash) const
+ template<typename ForwardKey>
+ bool contains__impl(const ForwardKey &key, const uint64_t hash) const
{
SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
return false;
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return true;
}
}
SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> void add_new__impl(ForwardKey &&key, uint32_t hash)
+ template<typename ForwardKey>
+ const Key &lookup_key__impl(const ForwardKey &key, const uint64_t hash) const
+ {
+ BLI_assert(this->contains_as(key));
+
+ SET_SLOT_PROBING_BEGIN (hash, slot) {
+ if (slot.contains(key, is_equal_, hash)) {
+ return *slot.key();
+ }
+ }
+ SET_SLOT_PROBING_END();
+ }
+
+ template<typename ForwardKey>
+ const Key *lookup_key_ptr__impl(const ForwardKey &key, const uint64_t hash) const
+ {
+ SET_SLOT_PROBING_BEGIN (hash, slot) {
+ if (slot.contains(key, is_equal_, hash)) {
+ return slot.key();
+ }
+ if (slot.is_empty()) {
+ return nullptr;
+ }
+ }
+ SET_SLOT_PROBING_END();
+ }
+
+ template<typename ForwardKey> void add_new__impl(ForwardKey &&key, const uint64_t hash)
{
BLI_assert(!this->contains_as(key));
@@ -604,36 +657,36 @@ class Set {
SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), hash);
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
return;
}
}
SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> bool add__impl(ForwardKey &&key, uint32_t hash)
+ template<typename ForwardKey> bool add__impl(ForwardKey &&key, const uint64_t hash)
{
this->ensure_can_add();
SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
slot.occupy(std::forward<ForwardKey>(key), hash);
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
return true;
}
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return false;
}
}
SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey> bool remove__impl(const ForwardKey &key, const uint64_t hash)
{
SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
slot.remove();
- m_removed_slots++;
+ removed_slots_++;
return true;
}
if (slot.is_empty()) {
@@ -643,13 +696,14 @@ class Set {
SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey>
+ void remove_contained__impl(const ForwardKey &key, const uint64_t hash)
{
BLI_assert(this->contains_as(key));
- m_removed_slots++;
+ removed_slots_++;
SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
slot.remove();
return;
}
@@ -658,12 +712,12 @@ class Set {
}
template<typename ForwardKey>
- uint32_t count_collisions__impl(const ForwardKey &key, uint32_t hash) const
+ int64_t count_collisions__impl(const ForwardKey &key, const uint64_t hash) const
{
- uint32_t collisions = 0;
+ int64_t collisions = 0;
SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash)) {
+ if (slot.contains(key, is_equal_, hash)) {
return collisions;
}
if (slot.is_empty()) {
@@ -676,9 +730,9 @@ class Set {
void ensure_can_add()
{
- if (m_occupied_and_removed_slots >= m_usable_slots) {
+ if (occupied_and_removed_slots_ >= usable_slots_) {
this->realloc_and_reinsert(this->size() + 1);
- BLI_assert(m_occupied_and_removed_slots < m_usable_slots);
+ BLI_assert(occupied_and_removed_slots_ < usable_slots_);
}
}
};
@@ -690,80 +744,92 @@ class Set {
template<typename Key> class StdUnorderedSetWrapper {
private:
using SetType = std::unordered_set<Key, blender::DefaultHash<Key>>;
- SetType m_set;
+ SetType set_;
public:
- uint32_t size() const
+ int64_t size() const
{
- return (uint32_t)m_set.size();
+ return (int64_t)set_.size();
}
bool is_empty() const
{
- return m_set.empty();
+ return set_.empty();
}
- void reserve(uint32_t n)
+ void reserve(int64_t n)
{
- m_set.reserve(n);
+ set_.reserve(n);
}
void add_new(const Key &key)
{
- m_set.insert(key);
+ set_.insert(key);
}
void add_new(Key &&key)
{
- m_set.insert(std::move(key));
+ set_.insert(std::move(key));
}
bool add(const Key &key)
{
- return m_set.insert(key).second;
+ return set_.insert(key).second;
}
bool add(Key &&key)
{
- return m_set.insert(std::move(key)).second;
+ return set_.insert(std::move(key)).second;
}
void add_multiple(Span<Key> keys)
{
for (const Key &key : keys) {
- m_set.insert(key);
+ set_.insert(key);
}
}
bool contains(const Key &key) const
{
- return m_set.find(key) != m_set.end();
+ return set_.find(key) != set_.end();
}
bool remove(const Key &key)
{
- return (bool)m_set.erase(key);
+ return (bool)set_.erase(key);
}
void remove_contained(const Key &key)
{
- return m_set.erase(key);
+ return set_.erase(key);
}
void clear()
{
- m_set.clear();
+ set_.clear();
}
typename SetType::iterator begin() const
{
- return m_set.begin();
+ return set_.begin();
}
typename SetType::iterator end() const
{
- return m_set.end();
+ return set_.end();
}
};
+/**
+ * Same as a normal Set, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
+ */
+template<typename Key,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key)),
+ typename ProbingStrategy = DefaultProbingStrategy,
+ typename Hash = DefaultHash<Key>,
+ typename IsEqual = DefaultEquality,
+ typename Slot = typename DefaultSetSlot<Key>::type>
+using RawSet = Set<Key, InlineBufferCapacity, ProbingStrategy, Hash, IsEqual, Slot, RawAllocator>;
+
} // namespace blender
#endif /* __BLI_SET_HH__ */
diff --git a/source/blender/blenlib/BLI_set_slots.hh b/source/blender/blenlib/BLI_set_slots.hh
index 15f56f2450e..b78ed37f534 100644
--- a/source/blender/blenlib/BLI_set_slots.hh
+++ b/source/blender/blenlib/BLI_set_slots.hh
@@ -50,8 +50,8 @@ template<typename Key> class SimpleSetSlot {
Removed = 2,
};
- State m_state;
- AlignedBuffer<sizeof(Key), alignof(Key)> m_buffer;
+ State state_;
+ TypedBuffer<Key> key_buffer_;
public:
/**
@@ -59,7 +59,7 @@ template<typename Key> class SimpleSetSlot {
*/
SimpleSetSlot()
{
- m_state = Empty;
+ state_ = Empty;
}
/**
@@ -67,8 +67,8 @@ template<typename Key> class SimpleSetSlot {
*/
~SimpleSetSlot()
{
- if (m_state == Occupied) {
- this->key()->~Key();
+ if (state_ == Occupied) {
+ key_buffer_.ref().~Key();
}
}
@@ -78,9 +78,9 @@ template<typename Key> class SimpleSetSlot {
*/
SimpleSetSlot(const SimpleSetSlot &other)
{
- m_state = other.m_state;
- if (other.m_state == Occupied) {
- new (this->key()) Key(*other.key());
+ state_ = other.state_;
+ if (other.state_ == Occupied) {
+ new (&key_buffer_) Key(*other.key_buffer_);
}
}
@@ -91,9 +91,9 @@ template<typename Key> class SimpleSetSlot {
*/
SimpleSetSlot(SimpleSetSlot &&other) noexcept
{
- m_state = other.m_state;
- if (other.m_state == Occupied) {
- new (this->key()) Key(std::move(*other.key()));
+ state_ = other.state_;
+ if (other.state_ == Occupied) {
+ new (&key_buffer_) Key(std::move(*other.key_buffer_));
}
}
@@ -102,7 +102,7 @@ template<typename Key> class SimpleSetSlot {
*/
Key *key()
{
- return (Key *)m_buffer.ptr();
+ return key_buffer_;
}
/**
@@ -110,7 +110,7 @@ template<typename Key> class SimpleSetSlot {
*/
const Key *key() const
{
- return (const Key *)m_buffer.ptr();
+ return key_buffer_;
}
/**
@@ -118,7 +118,7 @@ template<typename Key> class SimpleSetSlot {
*/
bool is_occupied() const
{
- return m_state == Occupied;
+ return state_ == Occupied;
}
/**
@@ -126,30 +126,30 @@ template<typename Key> class SimpleSetSlot {
*/
bool is_empty() const
{
- return m_state == Empty;
+ return state_ == Empty;
}
/**
* Return the hash of the currently stored key. In this simple set slot implementation, we just
* compute the hash here. Other implementations might store the hash in the slot instead.
*/
- template<typename Hash> uint32_t get_hash(const Hash &hash) const
+ template<typename Hash> uint64_t get_hash(const Hash &hash) const
{
BLI_assert(this->is_occupied());
- return hash(*this->key());
+ return hash(*key_buffer_);
}
/**
* Move the other slot into this slot and destruct it. We do destruction here, because this way
* we can avoid a comparison with the state, since we know the slot is occupied.
*/
- void relocate_occupied_here(SimpleSetSlot &other, uint32_t UNUSED(hash))
+ void relocate_occupied_here(SimpleSetSlot &other, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(other.is_occupied());
- m_state = Occupied;
- new (this->key()) Key(std::move(*other.key()));
- other.key()->~Key();
+ state_ = Occupied;
+ new (&key_buffer_) Key(std::move(*other.key_buffer_));
+ other.key_buffer_.ref().~Key();
}
/**
@@ -157,10 +157,10 @@ template<typename Key> class SimpleSetSlot {
* key. The hash is used by other slot implementations to determine inequality faster.
*/
template<typename ForwardKey, typename IsEqual>
- bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, uint64_t UNUSED(hash)) const
{
- if (m_state == Occupied) {
- return is_equal(key, *this->key());
+ if (state_ == Occupied) {
+ return is_equal(key, *key_buffer_);
}
return false;
}
@@ -169,11 +169,11 @@ template<typename Key> class SimpleSetSlot {
* Change the state of this slot from empty/removed to occupied. The key has to be constructed
* by calling the constructor with the given key as parameter.
*/
- template<typename ForwardKey> void occupy(ForwardKey &&key, uint32_t UNUSED(hash))
+ template<typename ForwardKey> void occupy(ForwardKey &&key, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
- m_state = Occupied;
- new (this->key()) Key(std::forward<ForwardKey>(key));
+ state_ = Occupied;
+ new (&key_buffer_) Key(std::forward<ForwardKey>(key));
}
/**
@@ -182,8 +182,8 @@ template<typename Key> class SimpleSetSlot {
void remove()
{
BLI_assert(this->is_occupied());
- m_state = Removed;
- this->key()->~Key();
+ state_ = Removed;
+ key_buffer_.ref().~Key();
}
};
@@ -199,102 +199,102 @@ template<typename Key> class HashedSetSlot {
Removed = 2,
};
- uint32_t m_hash;
- State m_state;
- AlignedBuffer<sizeof(Key), alignof(Key)> m_buffer;
+ uint64_t hash_;
+ State state_;
+ TypedBuffer<Key> key_buffer_;
public:
HashedSetSlot()
{
- m_state = Empty;
+ state_ = Empty;
}
~HashedSetSlot()
{
- if (m_state == Occupied) {
- this->key()->~Key();
+ if (state_ == Occupied) {
+ key_buffer_.ref().~Key();
}
}
HashedSetSlot(const HashedSetSlot &other)
{
- m_state = other.m_state;
- if (other.m_state == Occupied) {
- m_hash = other.m_hash;
- new (this->key()) Key(*other.key());
+ state_ = other.state_;
+ if (other.state_ == Occupied) {
+ hash_ = other.hash_;
+ new (&key_buffer_) Key(*other.key_buffer_);
}
}
HashedSetSlot(HashedSetSlot &&other) noexcept
{
- m_state = other.m_state;
- if (other.m_state == Occupied) {
- m_hash = other.m_hash;
- new (this->key()) Key(std::move(*other.key()));
+ state_ = other.state_;
+ if (other.state_ == Occupied) {
+ hash_ = other.hash_;
+ new (&key_buffer_) Key(std::move(*other.key_buffer_));
}
}
Key *key()
{
- return (Key *)m_buffer.ptr();
+ return key_buffer_;
}
const Key *key() const
{
- return (const Key *)m_buffer.ptr();
+ return key_buffer_;
}
bool is_occupied() const
{
- return m_state == Occupied;
+ return state_ == Occupied;
}
bool is_empty() const
{
- return m_state == Empty;
+ return state_ == Empty;
}
- template<typename Hash> uint32_t get_hash(const Hash &UNUSED(hash)) const
+ template<typename Hash> uint64_t get_hash(const Hash &UNUSED(hash)) const
{
BLI_assert(this->is_occupied());
- return m_hash;
+ return hash_;
}
- void relocate_occupied_here(HashedSetSlot &other, uint32_t hash)
+ void relocate_occupied_here(HashedSetSlot &other, const uint64_t hash)
{
BLI_assert(!this->is_occupied());
BLI_assert(other.is_occupied());
- m_state = Occupied;
- m_hash = hash;
- new (this->key()) Key(std::move(*other.key()));
- other.key()->~Key();
+ state_ = Occupied;
+ hash_ = hash;
+ new (&key_buffer_) Key(std::move(*other.key_buffer_));
+ key_buffer_.ref().~Key();
}
template<typename ForwardKey, typename IsEqual>
- bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t hash) const
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t hash) const
{
- /* m_hash might be uninitialized here, but that is ok. */
- if (m_hash == hash) {
- if (m_state == Occupied) {
- return is_equal(key, *this->key());
+ /* hash_ might be uninitialized here, but that is ok. */
+ if (hash_ == hash) {
+ if (state_ == Occupied) {
+ return is_equal(key, *key_buffer_);
}
}
return false;
}
- template<typename ForwardKey> void occupy(ForwardKey &&key, uint32_t hash)
+ template<typename ForwardKey> void occupy(ForwardKey &&key, const uint64_t hash)
{
BLI_assert(!this->is_occupied());
- m_state = Occupied;
- m_hash = hash;
- new (this->key()) Key(std::forward<ForwardKey>(key));
+ state_ = Occupied;
+ hash_ = hash;
+ new (&key_buffer_) Key(std::forward<ForwardKey>(key));
}
void remove()
{
BLI_assert(this->is_occupied());
- m_state = Removed;
- this->key()->~Key();
+ state_ = Removed;
+ key_buffer_.ref().~Key();
}
};
@@ -308,7 +308,7 @@ template<typename Key> class HashedSetSlot {
*/
template<typename Key, typename KeyInfo> class IntrusiveSetSlot {
private:
- Key m_key = KeyInfo::get_empty();
+ Key key_ = KeyInfo::get_empty();
public:
IntrusiveSetSlot() = default;
@@ -318,57 +318,57 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot {
Key *key()
{
- return &m_key;
+ return &key_;
}
const Key *key() const
{
- return &m_key;
+ return &key_;
}
bool is_occupied() const
{
- return KeyInfo::is_not_empty_or_removed(m_key);
+ return KeyInfo::is_not_empty_or_removed(key_);
}
bool is_empty() const
{
- return KeyInfo::is_empty(m_key);
+ return KeyInfo::is_empty(key_);
}
- template<typename Hash> uint32_t get_hash(const Hash &hash) const
+ template<typename Hash> uint64_t get_hash(const Hash &hash) const
{
BLI_assert(this->is_occupied());
- return hash(m_key);
+ return hash(key_);
}
- void relocate_occupied_here(IntrusiveSetSlot &other, uint32_t UNUSED(hash))
+ void relocate_occupied_here(IntrusiveSetSlot &other, const uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(other.is_occupied());
- m_key = std::move(other.m_key);
- other.m_key.~Key();
+ key_ = std::move(other.key_);
+ other.key_.~Key();
}
template<typename ForwardKey, typename IsEqual>
- bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const
+ bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t UNUSED(hash)) const
{
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- return is_equal(m_key, key);
+ return is_equal(key_, key);
}
- template<typename ForwardKey> void occupy(ForwardKey &&key, uint32_t UNUSED(hash))
+ template<typename ForwardKey> void occupy(ForwardKey &&key, const uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- m_key = std::forward<ForwardKey>(key);
+ key_ = std::forward<ForwardKey>(key);
}
void remove()
{
BLI_assert(this->is_occupied());
- KeyInfo::remove(m_key);
+ KeyInfo::remove(key_);
}
};
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index ce4e90d5e16..81b86f647f6 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -87,8 +87,8 @@ namespace blender {
*/
template<typename T> class Span {
private:
- const T *m_start = nullptr;
- uint m_size = 0;
+ const T *data_ = nullptr;
+ int64_t size_ = 0;
public:
/**
@@ -96,8 +96,15 @@ template<typename T> class Span {
*/
Span() = default;
- Span(const T *start, uint size) : m_start(start), m_size(size)
+ Span(const T *start, int64_t size) : data_(start), size_(size)
{
+ BLI_assert(size >= 0);
+ }
+
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr>
+ Span(const U *start, int64_t size) : data_((const T *)start), size_(size)
+ {
+ BLI_assert(size >= 0);
}
/**
@@ -111,11 +118,11 @@ template<typename T> class Span {
* Span<int> span = {1, 2, 3, 4};
* call_function_with_array(span);
*/
- Span(const std::initializer_list<T> &list) : Span(list.begin(), (uint)list.size())
+ Span(const std::initializer_list<T> &list) : Span(list.begin(), (int64_t)list.size())
{
}
- Span(const std::vector<T> &vector) : Span(vector.data(), (uint)vector.size())
+ Span(const std::vector<T> &vector) : Span(vector.data(), (int64_t)vector.size())
{
}
@@ -128,9 +135,8 @@ template<typename T> class Span {
* Span<T *> -> Span<const T *>
* Span<Derived *> -> Span<Base *>
*/
- template<typename U,
- typename std::enable_if<std::is_convertible<U *, T>::value>::type * = nullptr>
- Span(Span<U *> array) : Span((T *)array.data(), array.size())
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr>
+ Span(Span<U> array) : data_((T *)array.data()), size_(array.size())
{
}
@@ -138,10 +144,12 @@ template<typename T> class Span {
* Returns a contiguous part of the array. This invokes undefined behavior when the slice does
* not stay within the bounds of the array.
*/
- Span slice(uint start, uint size) const
+ Span slice(int64_t start, int64_t size) const
{
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
BLI_assert(start + size <= this->size() || size == 0);
- return Span(m_start + start, size);
+ return Span(data_ + start, size);
}
Span slice(IndexRange range) const
@@ -153,8 +161,9 @@ template<typename T> class Span {
* Returns a new Span with n elements removed from the beginning. This invokes undefined
* behavior when the array is too small.
*/
- Span drop_front(uint n) const
+ Span drop_front(int64_t n) const
{
+ BLI_assert(n >= 0);
BLI_assert(n <= this->size());
return this->slice(n, this->size() - n);
}
@@ -163,8 +172,9 @@ template<typename T> class Span {
* Returns a new Span with n elements removed from the beginning. This invokes undefined
* behavior when the array is too small.
*/
- Span drop_back(uint n) const
+ Span drop_back(int64_t n) const
{
+ BLI_assert(n >= 0);
BLI_assert(n <= this->size());
return this->slice(0, this->size() - n);
}
@@ -173,8 +183,9 @@ template<typename T> class Span {
* Returns a new Span that only contains the first n elements. This invokes undefined
* behavior when the array is too small.
*/
- Span take_front(uint n) const
+ Span take_front(int64_t n) const
{
+ BLI_assert(n >= 0);
BLI_assert(n <= this->size());
return this->slice(0, n);
}
@@ -183,8 +194,9 @@ template<typename T> class Span {
* Returns a new Span that only contains the last n elements. This invokes undefined
* behavior when the array is too small.
*/
- Span take_back(uint n) const
+ Span take_back(int64_t n) const
{
+ BLI_assert(n >= 0);
BLI_assert(n <= this->size());
return this->slice(this->size() - n, n);
}
@@ -195,35 +207,36 @@ template<typename T> class Span {
*/
const T *data() const
{
- return m_start;
+ return data_;
}
const T *begin() const
{
- return m_start;
+ return data_;
}
const T *end() const
{
- return m_start + m_size;
+ return data_ + size_;
}
/**
* Access an element in the array. This invokes undefined behavior when the index is out of
* bounds.
*/
- const T &operator[](uint index) const
+ const T &operator[](int64_t index) const
{
- BLI_assert(index < m_size);
- return m_start[index];
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ return data_[index];
}
/**
* Returns the number of elements in the referenced array.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -231,15 +244,15 @@ template<typename T> class Span {
*/
bool is_empty() const
{
- return m_size == 0;
+ return size_ == 0;
}
/**
* Returns the number of bytes referenced by this Span.
*/
- uint size_in_bytes() const
+ int64_t size_in_bytes() const
{
- return sizeof(T) * m_size;
+ return sizeof(T) * size_;
}
/**
@@ -269,9 +282,9 @@ template<typename T> class Span {
* Does a linear search to count how often the value is in the array.
* Returns the number of occurrences.
*/
- uint count(const T &value) const
+ int64_t count(const T &value) const
{
- uint counter = 0;
+ int64_t counter = 0;
for (const T &element : *this) {
if (element == value) {
counter++;
@@ -286,8 +299,8 @@ template<typename T> class Span {
*/
const T &first() const
{
- BLI_assert(m_size > 0);
- return m_start[0];
+ BLI_assert(size_ > 0);
+ return data_[0];
}
/**
@@ -296,18 +309,18 @@ template<typename T> class Span {
*/
const T &last() const
{
- BLI_assert(m_size > 0);
- return m_start[m_size - 1];
+ BLI_assert(size_ > 0);
+ return data_[size_ - 1];
}
/**
* Returns the element at the given index. If the index is out of range, return the fallback
* value.
*/
- T get(uint index, const T &fallback) const
+ T get(int64_t index, const T &fallback) const
{
- if (index < m_size) {
- return m_start[index];
+ if (index < size_ && index >= 0) {
+ return data_[index];
}
return fallback;
}
@@ -320,12 +333,12 @@ template<typename T> class Span {
{
/* The size should really be smaller than that. If it is not, the calling code should be
* changed. */
- BLI_assert(m_size < 1000);
+ BLI_assert(size_ < 1000);
- for (uint i = 0; i < m_size; i++) {
- const T &value = m_start[i];
- for (uint j = i + 1; j < m_size; j++) {
- if (value == m_start[j]) {
+ for (int64_t i = 0; i < size_; i++) {
+ const T &value = data_[i];
+ for (int64_t j = i + 1; j < size_; j++) {
+ if (value == data_[j]) {
return true;
}
}
@@ -342,10 +355,10 @@ template<typename T> class Span {
{
/* The size should really be smaller than that. If it is not, the calling code should be
* changed. */
- BLI_assert(m_size < 1000);
+ BLI_assert(size_ < 1000);
- for (uint i = 0; i < m_size; i++) {
- const T &value = m_start[i];
+ for (int64_t i = 0; i < size_; i++) {
+ const T &value = data_[i];
if (other.contains(value)) {
return true;
}
@@ -357,20 +370,20 @@ template<typename T> class Span {
* Returns the index of the first occurrence of the given value. This invokes undefined behavior
* when the value is not in the array.
*/
- uint first_index(const T &search_value) const
+ int64_t first_index(const T &search_value) const
{
- int index = this->first_index_try(search_value);
+ const int64_t index = this->first_index_try(search_value);
BLI_assert(index >= 0);
- return (uint)index;
+ return (int64_t)index;
}
/**
* Returns the index of the first occurrence of the given value or -1 if it does not exist.
*/
- int first_index_try(const T &search_value) const
+ int64_t first_index_try(const T &search_value) const
{
- for (uint i = 0; i < m_size; i++) {
- if (m_start[i] == search_value) {
+ for (int64_t i = 0; i < size_; i++) {
+ if (data_[i] == search_value) {
return i;
}
}
@@ -383,7 +396,7 @@ template<typename T> class Span {
*/
IndexRange index_range() const
{
- return IndexRange(m_size);
+ return IndexRange(size_);
}
/**
@@ -391,9 +404,9 @@ template<typename T> class Span {
*/
template<typename NewT> Span<NewT> cast() const
{
- BLI_assert((m_size * sizeof(T)) % sizeof(NewT) == 0);
- uint new_size = m_size * sizeof(T) / sizeof(NewT);
- return Span<NewT>(reinterpret_cast<const NewT *>(m_start), new_size);
+ BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0);
+ int64_t new_size = size_ * sizeof(T) / sizeof(NewT);
+ return Span<NewT>(reinterpret_cast<const NewT *>(data_), new_size);
}
/**
@@ -402,7 +415,7 @@ template<typename T> class Span {
*/
template<typename PrintLineF> void print_as_lines(std::string name, PrintLineF print_line) const
{
- std::cout << "Span: " << name << " \tSize:" << m_size << '\n';
+ std::cout << "Span: " << name << " \tSize:" << size_ << '\n';
for (const T &value : *this) {
std::cout << " ";
print_line(value);
@@ -426,28 +439,13 @@ template<typename T> class Span {
*/
template<typename T> class MutableSpan {
private:
- T *m_start;
- uint m_size;
+ T *data_;
+ int64_t size_;
public:
MutableSpan() = default;
- MutableSpan(T *start, uint size) : m_start(start), m_size(size)
- {
- }
-
- /**
- * Reference an initializer_list. Note that the data in the initializer_list is only valid until
- * the expression containing it is fully computed.
- *
- * Do:
- * call_function_with_array({1, 2, 3, 4});
- *
- * Don't:
- * MutableSpan<int> span = {1, 2, 3, 4};
- * call_function_with_array(span);
- */
- MutableSpan(std::initializer_list<T> &list) : MutableSpan(list.begin(), list.size())
+ MutableSpan(T *start, const int64_t size) : data_(start), size_(size)
{
}
@@ -461,15 +459,15 @@ template<typename T> class MutableSpan {
operator Span<T>() const
{
- return Span<T>(m_start, m_size);
+ return Span<T>(data_, size_);
}
/**
* Returns the number of elements in the array.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -477,18 +475,18 @@ template<typename T> class MutableSpan {
*/
void fill(const T &value)
{
- initialized_fill_n(m_start, m_size, value);
+ initialized_fill_n(data_, size_, value);
}
/**
* Replace a subset of all elements with the given value. This invokes undefined behavior when
* one of the indices is out of bounds.
*/
- void fill_indices(Span<uint> indices, const T &value)
+ void fill_indices(Span<int64_t> indices, const T &value)
{
- for (uint i : indices) {
- BLI_assert(i < m_size);
- m_start[i] = value;
+ for (int64_t i : indices) {
+ BLI_assert(i < size_);
+ data_[i] = value;
}
}
@@ -498,40 +496,40 @@ template<typename T> class MutableSpan {
*/
T *data() const
{
- return m_start;
+ return data_;
}
T *begin() const
{
- return m_start;
+ return data_;
}
T *end() const
{
- return m_start + m_size;
+ return data_ + size_;
}
- T &operator[](uint index) const
+ T &operator[](const int64_t index) const
{
BLI_assert(index < this->size());
- return m_start[index];
+ return data_[index];
}
/**
* Returns a contiguous part of the array. This invokes undefined behavior when the slice would
* go out of bounds.
*/
- MutableSpan slice(uint start, uint length) const
+ MutableSpan slice(const int64_t start, const int64_t length) const
{
BLI_assert(start + length <= this->size());
- return MutableSpan(m_start + start, length);
+ return MutableSpan(data_ + start, length);
}
/**
* Returns a new MutableSpan with n elements removed from the beginning. This invokes
* undefined behavior when the array is too small.
*/
- MutableSpan drop_front(uint n) const
+ MutableSpan drop_front(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(n, this->size() - n);
@@ -541,7 +539,7 @@ template<typename T> class MutableSpan {
* Returns a new MutableSpan with n elements removed from the end. This invokes undefined
* behavior when the array is too small.
*/
- MutableSpan drop_back(uint n) const
+ MutableSpan drop_back(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(0, this->size() - n);
@@ -551,7 +549,7 @@ template<typename T> class MutableSpan {
* Returns a new MutableSpan that only contains the first n elements. This invokes undefined
* behavior when the array is too small.
*/
- MutableSpan take_front(uint n) const
+ MutableSpan take_front(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(0, n);
@@ -561,7 +559,7 @@ template<typename T> class MutableSpan {
* Return a new MutableSpan that only contains the last n elements. This invokes undefined
* behavior when the array is too small.
*/
- MutableSpan take_back(uint n) const
+ MutableSpan take_back(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(this->size() - n, n);
@@ -573,7 +571,7 @@ template<typename T> class MutableSpan {
*/
Span<T> as_span() const
{
- return Span<T>(m_start, m_size);
+ return Span<T>(data_, size_);
}
/**
@@ -582,7 +580,7 @@ template<typename T> class MutableSpan {
*/
IndexRange index_range() const
{
- return IndexRange(m_size);
+ return IndexRange(size_);
}
/**
@@ -591,8 +589,34 @@ template<typename T> class MutableSpan {
*/
T &last() const
{
- BLI_assert(m_size > 0);
- return m_start[m_size - 1];
+ BLI_assert(size_ > 0);
+ return data_[size_ - 1];
+ }
+
+ /**
+ * Does a linear search to count how often the value is in the array.
+ * Returns the number of occurrences.
+ */
+ int64_t count(const T &value) const
+ {
+ int64_t counter = 0;
+ for (const T &element : *this) {
+ if (element == value) {
+ counter++;
+ }
+ }
+ return counter;
+ }
+
+ /**
+ * Copy all values from another span into this span. This invokes undefined behavior when the
+ * destination contains uninitialized data and T is not trivially copy constructible.
+ * The size of both spans is expected to be the same.
+ */
+ void copy_from(Span<T> values)
+ {
+ BLI_assert(size_ == values.size());
+ initialized_copy_n(values.data(), size_, data_);
}
/**
@@ -600,28 +624,20 @@ template<typename T> class MutableSpan {
*/
template<typename NewT> MutableSpan<NewT> cast() const
{
- BLI_assert((m_size * sizeof(T)) % sizeof(NewT) == 0);
- uint new_size = m_size * sizeof(T) / sizeof(NewT);
- return MutableSpan<NewT>(reinterpret_cast<NewT *>(m_start), new_size);
+ BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0);
+ int64_t new_size = size_ * sizeof(T) / sizeof(NewT);
+ return MutableSpan<NewT>(reinterpret_cast<NewT *>(data_), new_size);
}
};
/**
- * Shorthand to make use of automatic template parameter deduction.
- */
-template<typename T> Span<T> ref_c_array(const T *array, uint size)
-{
- return Span<T>(array, size);
-}
-
-/**
* Utilities to check that arrays have the same size in debug builds.
*/
template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 &v2)
{
UNUSED_VARS_NDEBUG(v1, v2);
#ifdef DEBUG
- uint size = v1.size();
+ int64_t size = v1.size();
BLI_assert(size == v1.size());
BLI_assert(size == v2.size());
#endif
@@ -632,7 +648,7 @@ void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3)
{
UNUSED_VARS_NDEBUG(v1, v2, v3);
#ifdef DEBUG
- uint size = v1.size();
+ int64_t size = v1.size();
BLI_assert(size == v1.size());
BLI_assert(size == v2.size());
BLI_assert(size == v3.size());
diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh
index 030d9c84c8e..75ae9df79a4 100644
--- a/source/blender/blenlib/BLI_stack.hh
+++ b/source/blender/blenlib/BLI_stack.hh
@@ -60,7 +60,7 @@ template<typename T> struct StackChunk {
/** Pointer to one element past the end of the referenced buffer. */
T *capacity_end;
- uint capacity() const
+ int64_t capacity() const
{
return capacity_end - begin;
}
@@ -73,11 +73,8 @@ template<
* The number of values that can be stored in this stack, without doing a heap allocation.
* Sometimes it can make sense to increase this value a lot. The memory in the inline buffer is
* not initialized when it is not needed.
- *
- * When T is large, the small buffer optimization is disabled by default to avoid large
- * unexpected allocations on the stack. It can still be enabled explicitely though.
*/
- uint InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this stack. Should rarely be changed, except when you don't want that
* MEM_* is used internally.
@@ -91,48 +88,46 @@ class Stack {
* Points to one element after top-most value in the stack.
*
* Invariant:
- * If m_size == 0
- * then: m_top == m_inline_chunk.begin
- * else: &peek() == m_top - 1;
+ * If size_ == 0
+ * then: top_ == inline_chunk_.begin
+ * else: &peek() == top_ - 1;
*/
- T *m_top;
+ T *top_;
- /** Points to the chunk that references the memory pointed to by m_top. */
- Chunk *m_top_chunk;
+ /** Points to the chunk that references the memory pointed to by top_. */
+ Chunk *top_chunk_;
/**
* Number of elements in the entire stack. The sum of initialized element counts in the chunks.
*/
- uint m_size;
+ int64_t size_;
/** The buffer used to implement small object optimization. */
- AlignedBuffer<sizeof(T) * InlineBufferCapacity, alignof(T)> m_inline_buffer;
+ TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
/**
* A chunk referencing the inline buffer. This is always the bottom-most chunk.
- * So m_inline_chunk.below == nullptr.
+ * So inline_chunk_.below == nullptr.
*/
- Chunk m_inline_chunk;
+ Chunk inline_chunk_;
/** Used for allocations when the inline buffer is not large enough. */
- Allocator m_allocator;
+ Allocator allocator_;
public:
/**
* Initialize an empty stack. No heap allocation is done.
*/
- Stack(Allocator allocator = {}) : m_allocator(allocator)
+ Stack(Allocator allocator = {}) : allocator_(allocator)
{
- T *inline_buffer = this->inline_buffer();
-
- m_inline_chunk.below = nullptr;
- m_inline_chunk.above = nullptr;
- m_inline_chunk.begin = inline_buffer;
- m_inline_chunk.capacity_end = inline_buffer + InlineBufferCapacity;
-
- m_top = inline_buffer;
- m_top_chunk = &m_inline_chunk;
- m_size = 0;
+ inline_chunk_.below = nullptr;
+ inline_chunk_.above = nullptr;
+ inline_chunk_.begin = inline_buffer_;
+ inline_chunk_.capacity_end = inline_buffer_ + InlineBufferCapacity;
+
+ top_ = inline_buffer_;
+ top_chunk_ = &inline_chunk_;
+ size_ = 0;
}
/**
@@ -157,46 +152,49 @@ class Stack {
{
}
- Stack(const Stack &other) : Stack(other.m_allocator)
+ Stack(const Stack &other) : Stack(other.allocator_)
{
- for (const Chunk *chunk = &other.m_inline_chunk; chunk; chunk = chunk->above) {
+ for (const Chunk *chunk = &other.inline_chunk_; chunk; chunk = chunk->above) {
const T *begin = chunk->begin;
- const T *end = (chunk == other.m_top_chunk) ? other.m_top : chunk->capacity_end;
+ const T *end = (chunk == other.top_chunk_) ? other.top_ : chunk->capacity_end;
this->push_multiple(Span<T>(begin, end - begin));
}
}
- Stack(Stack &&other) noexcept : Stack(other.m_allocator)
+ Stack(Stack &&other) noexcept : Stack(other.allocator_)
{
- uninitialized_relocate_n(other.inline_buffer(),
- std::min(other.m_size, InlineBufferCapacity),
- this->inline_buffer());
+ uninitialized_relocate_n<T>(
+ other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_);
- m_inline_chunk.above = other.m_inline_chunk.above;
- m_size = other.m_size;
+ inline_chunk_.above = other.inline_chunk_.above;
+ size_ = other.size_;
- if (m_size <= InlineBufferCapacity) {
- m_top_chunk = &m_inline_chunk;
- m_top = this->inline_buffer() + m_size;
+ if (inline_chunk_.above != nullptr) {
+ inline_chunk_.above->below = &inline_chunk_;
+ }
+
+ if (size_ <= InlineBufferCapacity) {
+ top_chunk_ = &inline_chunk_;
+ top_ = inline_buffer_ + size_;
}
else {
- m_top_chunk = other.m_top_chunk;
- m_top = other.m_top;
+ top_chunk_ = other.top_chunk_;
+ top_ = other.top_;
}
- other.m_size = 0;
- other.m_inline_chunk.above = nullptr;
- other.m_top_chunk = &other.m_inline_chunk;
- other.m_top = other.m_top_chunk->begin;
+ other.size_ = 0;
+ other.inline_chunk_.above = nullptr;
+ other.top_chunk_ = &other.inline_chunk_;
+ other.top_ = other.top_chunk_->begin;
}
~Stack()
{
this->destruct_all_elements();
Chunk *above_chunk;
- for (Chunk *chunk = m_inline_chunk.above; chunk; chunk = above_chunk) {
+ for (Chunk *chunk = inline_chunk_.above; chunk; chunk = above_chunk) {
above_chunk = chunk->above;
- m_allocator.deallocate(chunk);
+ allocator_.deallocate(chunk);
}
}
@@ -229,21 +227,21 @@ class Stack {
*/
void push(const T &value)
{
- if (m_top == m_top_chunk->capacity_end) {
+ if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
- new (m_top) T(value);
- m_top++;
- m_size++;
+ new (top_) T(value);
+ top_++;
+ size_++;
}
void push(T &&value)
{
- if (m_top == m_top_chunk->capacity_end) {
+ if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
- new (m_top) T(std::move(value));
- m_top++;
- m_size++;
+ new (top_) T(std::move(value));
+ top_++;
+ size_++;
}
/**
@@ -252,16 +250,16 @@ class Stack {
*/
T pop()
{
- BLI_assert(m_size > 0);
- m_top--;
- T value = std::move(*m_top);
- m_top->~T();
- m_size--;
-
- if (m_top == m_top_chunk->begin) {
- if (m_top_chunk->below != nullptr) {
- m_top_chunk = m_top_chunk->below;
- m_top = m_top_chunk->capacity_end;
+ BLI_assert(size_ > 0);
+ top_--;
+ T value = std::move(*top_);
+ top_->~T();
+ size_--;
+
+ if (top_ == top_chunk_->begin) {
+ if (top_chunk_->below != nullptr) {
+ top_chunk_ = top_chunk_->below;
+ top_ = top_chunk_->capacity_end;
}
}
return value;
@@ -273,15 +271,15 @@ class Stack {
*/
T &peek()
{
- BLI_assert(m_size > 0);
- BLI_assert(m_top > m_top_chunk->begin);
- return *(m_top - 1);
+ BLI_assert(size_ > 0);
+ BLI_assert(top_ > top_chunk_->begin);
+ return *(top_ - 1);
}
const T &peek() const
{
- BLI_assert(m_size > 0);
- BLI_assert(m_top > m_top_chunk->begin);
- return *(m_top - 1);
+ BLI_assert(size_ > 0);
+ BLI_assert(top_ > top_chunk_->begin);
+ return *(top_ - 1);
}
/**
@@ -293,19 +291,19 @@ class Stack {
{
Span<T> remaining_values = values;
while (!remaining_values.is_empty()) {
- if (m_top == m_top_chunk->capacity_end) {
+ if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(remaining_values.size());
}
- uint remaining_capacity = m_top_chunk->capacity_end - m_top;
- uint amount = std::min(remaining_values.size(), remaining_capacity);
- uninitialized_copy_n(remaining_values.data(), amount, m_top);
- m_top += amount;
+ const int64_t remaining_capacity = top_chunk_->capacity_end - top_;
+ const int64_t amount = std::min(remaining_values.size(), remaining_capacity);
+ uninitialized_copy_n(remaining_values.data(), amount, top_);
+ top_ += amount;
remaining_values = remaining_values.drop_front(amount);
}
- m_size += values.size();
+ size_ += values.size();
}
/**
@@ -313,15 +311,15 @@ class Stack {
*/
bool is_empty() const
{
- return m_size == 0;
+ return size_ == 0;
}
/**
* Returns the number of elements in the stack.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -331,31 +329,26 @@ class Stack {
void clear()
{
this->destruct_all_elements();
- m_top_chunk = &m_inline_chunk;
- m_top = m_top_chunk->begin;
+ top_chunk_ = &inline_chunk_;
+ top_ = top_chunk_->begin;
}
private:
- T *inline_buffer() const
- {
- return (T *)m_inline_buffer.ptr();
- }
-
/**
- * Changes m_top_chunk to point to a new chunk that is above the current one. The new chunk might
+ * Changes top_chunk_ to point to a new chunk that is above the current one. The new chunk might
* be smaller than the given size_hint. This happens when a chunk that has been allocated before
* is reused. The size of the new chunk will be at least one.
*
* This invokes undefined behavior when the currently active chunk is not full.
*/
- void activate_next_chunk(uint size_hint)
+ void activate_next_chunk(const int64_t size_hint)
{
- BLI_assert(m_top == m_top_chunk->capacity_end);
- if (m_top_chunk->above == nullptr) {
- uint new_capacity = std::max(size_hint, m_top_chunk->capacity() * 2 + 10);
+ BLI_assert(top_ == top_chunk_->capacity_end);
+ if (top_chunk_->above == nullptr) {
+ const int64_t new_capacity = std::max(size_hint, top_chunk_->capacity() * 2 + 10);
/* Do a single memory allocation for the Chunk and the array it references. */
- void *buffer = m_allocator.allocate(
+ void *buffer = allocator_.allocate(
sizeof(Chunk) + sizeof(T) * new_capacity + alignof(T), alignof(Chunk), AT);
void *chunk_buffer = buffer;
void *data_buffer = (void *)(((uintptr_t)buffer + sizeof(Chunk) + alignof(T) - 1) &
@@ -365,19 +358,19 @@ class Stack {
new_chunk->begin = (T *)data_buffer;
new_chunk->capacity_end = new_chunk->begin + new_capacity;
new_chunk->above = nullptr;
- new_chunk->below = m_top_chunk;
- m_top_chunk->above = new_chunk;
+ new_chunk->below = top_chunk_;
+ top_chunk_->above = new_chunk;
}
- m_top_chunk = m_top_chunk->above;
- m_top = m_top_chunk->begin;
+ top_chunk_ = top_chunk_->above;
+ top_ = top_chunk_->begin;
}
void destruct_all_elements()
{
- for (T *value = m_top_chunk->begin; value != m_top; value++) {
+ for (T *value = top_chunk_->begin; value != top_; value++) {
value->~T();
}
- for (Chunk *chunk = m_top_chunk->below; chunk; chunk = chunk->below) {
+ for (Chunk *chunk = top_chunk_->below; chunk; chunk = chunk->below) {
for (T *value = chunk->begin; value != chunk->capacity_end; value++) {
value->~T();
}
@@ -385,6 +378,13 @@ class Stack {
}
};
+/**
+ * Same as a normal Stack, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
+ */
+template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
+using RawStack = Stack<T, InlineBufferCapacity, RawAllocator>;
+
} /* namespace blender */
#endif /* __BLI_STACK_HH__ */
diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh
index 073137fe175..06fc66f6b55 100644
--- a/source/blender/blenlib/BLI_string_ref.hh
+++ b/source/blender/blenlib/BLI_string_ref.hh
@@ -59,10 +59,10 @@ class StringRef;
*/
class StringRefBase {
protected:
- const char *m_data;
- uint m_size;
+ const char *data_;
+ int64_t size_;
- StringRefBase(const char *data, uint size) : m_data(data), m_size(size)
+ StringRefBase(const char *data, const int64_t size) : data_(data), size_(size)
{
}
@@ -70,9 +70,9 @@ class StringRefBase {
/**
* Return the (byte-)length of the referenced string, without any null-terminator.
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -80,31 +80,31 @@ class StringRefBase {
*/
const char *data() const
{
- return m_data;
+ return data_;
}
operator Span<char>() const
{
- return Span<char>(m_data, m_size);
+ return Span<char>(data_, size_);
}
/**
- * 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
{
- return std::string(m_data, m_size);
+ return std::string(data_, (size_t)size_);
}
const char *begin() const
{
- return m_data;
+ return data_;
}
const char *end() const
{
- return m_data + m_size;
+ return data_ + size_;
}
/**
@@ -114,17 +114,17 @@ class StringRefBase {
*/
void unsafe_copy(char *dst) const
{
- memcpy(dst, m_data, m_size);
- dst[m_size] = '\0';
+ memcpy(dst, data_, (size_t)size_);
+ dst[size_] = '\0';
}
/**
* Copy the string into a buffer. The copied string will be null-terminated. This invokes
* undefined behavior when dst_size is too small. (Should we define the behavior?)
*/
- void copy(char *dst, uint dst_size) const
+ void copy(char *dst, const int64_t dst_size) const
{
- if (m_size < dst_size) {
+ if (size_ < dst_size) {
this->unsafe_copy(dst);
}
else {
@@ -137,7 +137,7 @@ class StringRefBase {
* Copy the string into a char array. The copied string will be null-terminated. This invokes
* undefined behavior when dst is too small.
*/
- template<uint N> void copy(char (&dst)[N])
+ template<size_t N> void copy(char (&dst)[N])
{
this->copy(dst, N);
}
@@ -152,7 +152,7 @@ class StringRefBase {
*/
bool endswith(StringRef suffix) const;
- StringRef substr(uint start, uint size) const;
+ StringRef substr(int64_t start, const int64_t size) const;
};
/**
@@ -168,37 +168,48 @@ class StringRefNull : public StringRefBase {
/**
* Construct a StringRefNull from a null terminated c-string. The pointer must not point to NULL.
*/
- StringRefNull(const char *str) : StringRefBase(str, (uint)strlen(str))
+ StringRefNull(const char *str) : StringRefBase(str, (int64_t)strlen(str))
{
BLI_assert(str != NULL);
- BLI_assert(m_data[m_size] == '\0');
+ BLI_assert(data_[size_] == '\0');
}
/**
* Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior
* when the given size is not the correct size of the string.
*/
- StringRefNull(const char *str, uint size) : StringRefBase(str, size)
+ StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size)
{
- BLI_assert((uint)strlen(str) == size);
+ BLI_assert((int64_t)strlen(str) == size);
}
/**
* Reference a std::string. Remember that when the std::string is destructed, the StringRefNull
* will point to uninitialized memory.
*/
- StringRefNull(const std::string &str) : StringRefNull(str.data())
+ StringRefNull(const std::string &str) : StringRefNull(str.c_str())
{
}
/**
* Get the char at the given index.
*/
- char operator[](uint index) const
+ char operator[](const int64_t index) const
{
+ BLI_assert(index >= 0);
/* Use '<=' instead of just '<', so that the null character can be accessed as well. */
- BLI_assert(index <= m_size);
- return m_data[index];
+ BLI_assert(index <= size_);
+ return data_[index];
+ }
+
+ /**
+ * Returns the beginning of a null-terminated char array.
+ *
+ * This is like ->data(), but can only be called on a StringRefNull.
+ */
+ const char *c_str() const
+ {
+ return data_;
}
};
@@ -221,11 +232,11 @@ class StringRef : public StringRefBase {
/**
* Create a StringRef from a null-terminated c-string.
*/
- StringRef(const char *str) : StringRefBase(str, str ? (uint)strlen(str) : 0)
+ StringRef(const char *str) : StringRefBase(str, str ? (int64_t)strlen(str) : 0)
{
}
- StringRef(const char *str, uint length) : StringRefBase(str, length)
+ StringRef(const char *str, const int64_t length) : StringRefBase(str, length)
{
}
@@ -234,7 +245,7 @@ class StringRef : public StringRefBase {
* second point points to a smaller address than the first one.
*/
StringRef(const char *begin, const char *one_after_end)
- : StringRefBase(begin, (uint)(one_after_end - begin))
+ : StringRefBase(begin, (int64_t)(one_after_end - begin))
{
BLI_assert(begin <= one_after_end);
}
@@ -243,17 +254,18 @@ class StringRef : public StringRefBase {
* Reference a std::string. Remember that when the std::string is destructed, the StringRef
* will point to uninitialized memory.
*/
- StringRef(const std::string &str) : StringRefBase(str.data(), (uint)str.size())
+ StringRef(const std::string &str) : StringRefBase(str.data(), (int64_t)str.size())
{
}
/**
* Return a new StringRef that does not contain the first n chars.
*/
- StringRef drop_prefix(uint n) const
+ StringRef drop_prefix(const int64_t n) const
{
- BLI_assert(n <= m_size);
- return StringRef(m_data + n, m_size - n);
+ BLI_assert(n >= 0);
+ BLI_assert(n <= size_);
+ return StringRef(data_ + n, size_ - n);
}
/**
@@ -269,10 +281,11 @@ class StringRef : public StringRefBase {
/**
* Get the char at the given index.
*/
- char operator[](uint index) const
+ char operator[](int64_t index) const
{
- BLI_assert(index < m_size);
- return m_data[index];
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ return data_[index];
}
};
@@ -287,13 +300,13 @@ inline std::ostream &operator<<(std::ostream &stream, StringRef ref)
inline std::ostream &operator<<(std::ostream &stream, StringRefNull ref)
{
- stream << std::string(ref.data(), ref.size());
+ stream << std::string(ref.data(), (size_t)ref.size());
return stream;
}
/**
- * Adding two StringRefs will allocate an std::string. This is not efficient, but convenient in
- * most cases.
+ * Adding two #StringRefs will allocate an std::string.
+ * This is not efficient, but convenient in most cases.
*/
inline std::string operator+(StringRef a, StringRef b)
{
@@ -305,7 +318,7 @@ inline bool operator==(StringRef a, StringRef b)
if (a.size() != b.size()) {
return false;
}
- return STREQLEN(a.data(), b.data(), a.size());
+ return STREQLEN(a.data(), b.data(), (size_t)a.size());
}
inline bool operator!=(StringRef a, StringRef b)
@@ -318,11 +331,11 @@ inline bool operator!=(StringRef a, StringRef b)
*/
inline bool StringRefBase::startswith(StringRef prefix) const
{
- if (m_size < prefix.m_size) {
+ if (size_ < prefix.size_) {
return false;
}
- for (uint i = 0; i < prefix.m_size; i++) {
- if (m_data[i] != prefix.m_data[i]) {
+ for (int64_t i = 0; i < prefix.size_; i++) {
+ if (data_[i] != prefix.data_[i]) {
return false;
}
}
@@ -334,12 +347,12 @@ inline bool StringRefBase::startswith(StringRef prefix) const
*/
inline bool StringRefBase::endswith(StringRef suffix) const
{
- if (m_size < suffix.m_size) {
+ if (size_ < suffix.size_) {
return false;
}
- uint offset = m_size - suffix.m_size;
- for (uint i = 0; i < suffix.m_size; i++) {
- if (m_data[offset + i] != suffix.m_data[i]) {
+ const int64_t offset = size_ - suffix.size_;
+ for (int64_t i = 0; i < suffix.size_; i++) {
+ if (data_[offset + i] != suffix.data_[i]) {
return false;
}
}
@@ -347,12 +360,14 @@ inline bool StringRefBase::endswith(StringRef suffix) const
}
/**
- * Return a new StringRef containing only a substring of the original string.
+ * Return a new #StringRef containing only a sub-string of the original string.
*/
-inline StringRef StringRefBase::substr(uint start, uint size) const
+inline StringRef StringRefBase::substr(const int64_t start, const int64_t size) const
{
- BLI_assert(start + size <= m_size);
- return StringRef(m_data + start, size);
+ BLI_assert(size >= 0);
+ BLI_assert(start >= 0);
+ BLI_assert(start + size <= size_);
+ return StringRef(data_ + start, size);
}
} // namespace blender
diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h
index 03fe27c10ed..920a0a8f650 100644
--- a/source/blender/blenlib/BLI_threads.h
+++ b/source/blender/blenlib/BLI_threads.h
@@ -28,10 +28,6 @@
#include "BLI_sys_types.h"
-#ifdef __APPLE__
-# include <libkern/OSAtomic.h>
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -100,10 +96,18 @@ void BLI_mutex_unlock(ThreadMutex *mutex);
/* Spin Lock */
-#if defined(__APPLE__)
-typedef OSSpinLock SpinLock;
+/* By default we use TBB for spin lock on all platforms. When building without
+ * TBB fall-back to spin lock implementation which is native to the platform.
+ *
+ * On macOS we use mutex lock instead of spin since the spin lock has been
+ * deprecated in SDK 10.12 and is discouraged from use. */
+
+#ifdef WITH_TBB
+typedef uint32_t SpinLock;
+#elif defined(__APPLE__)
+typedef ThreadMutex SpinLock;
#elif defined(_MSC_VER)
-typedef volatile int SpinLock;
+typedef volatile unsigned int SpinLock;
#else
typedef pthread_spinlock_t SpinLock;
#endif
diff --git a/source/blender/blenlib/BLI_timeit.hh b/source/blender/blenlib/BLI_timeit.hh
index 711a7f16ab4..f0968587597 100644
--- a/source/blender/blenlib/BLI_timeit.hh
+++ b/source/blender/blenlib/BLI_timeit.hh
@@ -23,8 +23,7 @@
#include "BLI_sys_types.h"
-namespace blender {
-namespace Timeit {
+namespace blender::timeit {
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
@@ -34,29 +33,28 @@ void print_duration(Nanoseconds duration);
class ScopedTimer {
private:
- std::string m_name;
- TimePoint m_start;
+ std::string name_;
+ TimePoint start_;
public:
- ScopedTimer(std::string name) : m_name(std::move(name))
+ ScopedTimer(std::string name) : name_(std::move(name))
{
- m_start = Clock::now();
+ start_ = Clock::now();
}
~ScopedTimer()
{
- TimePoint end = Clock::now();
- Nanoseconds duration = end - m_start;
+ const TimePoint end = Clock::now();
+ const Nanoseconds duration = end - start_;
- std::cout << "Timer '" << m_name << "' took ";
+ std::cout << "Timer '" << name_ << "' took ";
print_duration(duration);
std::cout << '\n';
}
};
-} // namespace Timeit
-} // namespace blender
+} // namespace blender::timeit
-#define SCOPED_TIMER(name) blender::Timeit::ScopedTimer scoped_timer(name)
+#define SCOPED_TIMER(name) blender::timeit::ScopedTimer scoped_timer(name)
#endif /* __BLI_TIMEIT_HH__ */
diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h
index 1f28f7e80c5..2699f2498ac 100644
--- a/source/blender/blenlib/BLI_utildefines.h
+++ b/source/blender/blenlib/BLI_utildefines.h
@@ -627,11 +627,11 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size);
/** \name String Macros
* \{ */
-/* Macro to convert a value to string in the preprocessor
- * STRINGIFY_ARG: gives the argument as a string
- * STRINGIFY_APPEND: appends any argument 'b' onto the string argument 'a',
- * used by STRINGIFY because some preprocessors warn about zero arguments
- * STRINGIFY: gives the argument's value as a string */
+/* Macro to convert a value to string in the pre-processor:
+ * - `STRINGIFY_ARG`: gives the argument as a string
+ * - `STRINGIFY_APPEND`: appends any argument 'b' onto the string argument 'a',
+ * used by `STRINGIFY` because some preprocessors warn about zero arguments
+ * - `STRINGIFY`: gives the argument's value as a string. */
#define STRINGIFY_ARG(x) "" #x
#define STRINGIFY_APPEND(a, b) "" a #b
#define STRINGIFY(x) STRINGIFY_APPEND("", x)
@@ -755,6 +755,43 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size);
/** \} */
/* -------------------------------------------------------------------- */
+/** \name C++ Macros
+ * \{ */
+
+#ifdef __cplusplus
+
+/* Useful to port C code using enums to C++ where enums are strongly typed.
+ * To use after the enum declaration. */
+# define ENUM_OPERATORS(_enum_type) \
+ inline constexpr _enum_type operator|(_enum_type a, _enum_type b) \
+ { \
+ return a = static_cast<_enum_type>(static_cast<int>(a) | b); \
+ } \
+ inline constexpr _enum_type operator&(_enum_type a, _enum_type b) \
+ { \
+ return a = static_cast<_enum_type>(static_cast<int>(a) & b); \
+ } \
+ inline constexpr _enum_type operator~(_enum_type a) \
+ { \
+ return a = static_cast<_enum_type>(~static_cast<int>(a)); \
+ } \
+ inline _enum_type &operator|=(_enum_type &a, _enum_type b) \
+ { \
+ return a = static_cast<_enum_type>(static_cast<int>(a) | b); \
+ } \
+ inline _enum_type &operator&=(_enum_type &a, _enum_type b) \
+ { \
+ return a = static_cast<_enum_type>(static_cast<int>(a) & b); \
+ }
+
+#else
+/* Output nothing. */
+# define ENUM_OPERATORS(_type)
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Misc Macros
* \{ */
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index b2b2da0a4b0..7eac511bf4a 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -68,9 +68,9 @@ template<
* not initialized when it is not needed.
*
* When T is large, the small buffer optimization is disabled by default to avoid large
- * unexpected allocations on the stack. It can still be enabled explicitely though.
+ * unexpected allocations on the stack. It can still be enabled explicitly though.
*/
- uint InlineBufferCapacity = (sizeof(T) < 100) ? 4 : 0,
+ int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T)),
/**
* The allocator used by this vector. Should rarely be changed, except when you don't want that
* MEM_* is used internally.
@@ -79,38 +79,38 @@ template<
class Vector {
private:
/**
- * Use pointers instead of storing the size explicitely. This reduces the number of instructions
+ * Use pointers instead of storing the size explicitly. This reduces the number of instructions
* in `append`.
*
* The pointers might point to the memory in the inline buffer.
*/
- T *m_begin;
- T *m_end;
- T *m_capacity_end;
+ T *begin_;
+ T *end_;
+ T *capacity_end_;
/** Used for allocations when the inline buffer is too small. */
- Allocator m_allocator;
+ Allocator allocator_;
/** A placeholder buffer that will remain uninitialized until it is used. */
- AlignedBuffer<(uint)sizeof(T) * InlineBufferCapacity, (uint)alignof(T)> m_inline_buffer;
+ TypedBuffer<T, InlineBufferCapacity> inline_buffer_;
/**
- * Store the size of the vector explicitely in debug builds. Otherwise you'd always have to call
+ * Store the size of the vector explicitly in debug builds. Otherwise you'd always have to call
* the `size` function or do the math to compute it from the pointers manually. This is rather
* annoying. Knowing the size of a vector is often quite essential when debugging some code.
*/
#ifndef NDEBUG
- uint m_debug_size;
-# define UPDATE_VECTOR_SIZE(ptr) (ptr)->m_debug_size = (uint)((ptr)->m_end - (ptr)->m_begin)
+ int64_t debug_size_;
+# define UPDATE_VECTOR_SIZE(ptr) (ptr)->debug_size_ = (int64_t)((ptr)->end_ - (ptr)->begin_)
#else
# define UPDATE_VECTOR_SIZE(ptr) ((void)0)
#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>
+ template<typename OtherT, int64_t OtherInlineBufferCapacity, typename OtherAllocator>
friend class Vector;
public:
@@ -118,11 +118,11 @@ class Vector {
* Create an empty vector.
* This does not do any memory allocation.
*/
- Vector()
+ Vector(Allocator allocator = {}) : allocator_(allocator)
{
- m_begin = this->inline_buffer();
- m_end = m_begin;
- m_capacity_end = m_begin + InlineBufferCapacity;
+ begin_ = inline_buffer_;
+ end_ = begin_;
+ capacity_end_ = begin_ + InlineBufferCapacity;
UPDATE_VECTOR_SIZE(this);
}
@@ -131,7 +131,7 @@ class Vector {
* The elements will be default constructed.
* If T is trivially constructible, the elements in the vector are not touched.
*/
- explicit Vector(uint size) : Vector()
+ explicit Vector(int64_t size) : Vector()
{
this->resize(size);
}
@@ -139,37 +139,48 @@ class Vector {
/**
* Create a vector filled with a specific value.
*/
- Vector(uint size, const T &value) : Vector()
+ Vector(int64_t size, const T &value) : Vector()
{
+ this->resize(size, value);
+ }
+
+ /**
+ * Create a vector from an array ref. The values in the vector are copy constructed.
+ */
+ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Vector(Span<U> values, Allocator allocator = {}) : Vector(allocator)
+ {
+ const int64_t size = values.size();
this->reserve(size);
this->increase_size_by_unchecked(size);
- blender::uninitialized_fill_n(m_begin, size, value);
+ uninitialized_convert_n<U, T>(values.data(), size, begin_);
}
/**
- * 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};
*/
+ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Vector(const std::initializer_list<U> &values) : Vector(Span<U>(values))
+ {
+ }
+
Vector(const std::initializer_list<T> &values) : Vector(Span<T>(values))
{
}
- /**
- * Create a vector from an array ref. The values in the vector are copy constructed.
- */
- Vector(Span<T> values) : Vector()
+ template<typename U,
+ size_t N,
+ typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+ Vector(const std::array<U, N> &values) : Vector(Span(values))
{
- uint size = values.size();
- this->reserve(size);
- this->increase_size_by_unchecked(size);
- blender::uninitialized_copy_n(values.data(), size, m_begin);
}
/**
- * Create a vector from any container. It must be possible to use the container in a range-for
- * loop.
+ * Create a vector from any container. It must be possible to use the container in a
+ * range-for loop.
*/
template<typename ContainerT> static Vector FromContainer(const ContainerT &container)
{
@@ -198,91 +209,69 @@ class Vector {
* Create a copy of another vector. The other vector will not be changed. If the other vector has
* less than InlineBufferCapacity elements, no allocation will be made.
*/
- Vector(const Vector &other) : m_allocator(other.m_allocator)
+ Vector(const Vector &other) : Vector(other.as_span(), other.allocator_)
{
- this->init_copy_from_other_vector(other);
}
/**
* Create a copy of a vector with a different InlineBufferCapacity. This needs to be handled
* separately, so that the other one is a valid copy constructor.
*/
- template<uint OtherInlineBufferCapacity>
+ template<int64_t OtherInlineBufferCapacity>
Vector(const Vector<T, OtherInlineBufferCapacity, Allocator> &other)
- : m_allocator(other.m_allocator)
+ : Vector(other.as_span(), other.allocator_)
{
- this->init_copy_from_other_vector(other);
}
/**
* Steal the elements from another vector. This does not do an allocation. The other vector will
* have zero elements afterwards.
*/
- template<uint OtherInlineBufferCapacity>
+ template<int64_t OtherInlineBufferCapacity>
Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept
- : m_allocator(other.m_allocator)
+ : allocator_(other.allocator_)
{
- uint size = other.size();
+ const int64_t size = other.size();
if (other.is_inline()) {
if (size <= InlineBufferCapacity) {
/* Copy between inline buffers. */
- m_begin = this->inline_buffer();
- m_end = m_begin + size;
- m_capacity_end = m_begin + InlineBufferCapacity;
- uninitialized_relocate_n(other.m_begin, size, m_begin);
+ begin_ = inline_buffer_;
+ end_ = begin_ + size;
+ capacity_end_ = begin_ + InlineBufferCapacity;
+ uninitialized_relocate_n(other.begin_, size, begin_);
}
else {
/* Copy from inline buffer to newly allocated buffer. */
- uint capacity = size;
- m_begin = (T *)m_allocator.allocate(sizeof(T) * capacity, alignof(T), AT);
- m_end = m_begin + size;
- m_capacity_end = m_begin + capacity;
- uninitialized_relocate_n(other.m_begin, size, m_begin);
+ const int64_t capacity = size;
+ begin_ = (T *)allocator_.allocate(sizeof(T) * (size_t)capacity, alignof(T), AT);
+ end_ = begin_ + size;
+ capacity_end_ = begin_ + capacity;
+ uninitialized_relocate_n(other.begin_, size, begin_);
}
}
else {
/* Steal the pointer. */
- m_begin = other.m_begin;
- m_end = other.m_end;
- m_capacity_end = other.m_capacity_end;
+ begin_ = other.begin_;
+ end_ = other.end_;
+ capacity_end_ = other.capacity_end_;
}
- other.m_begin = other.inline_buffer();
- other.m_end = other.m_begin;
- other.m_capacity_end = other.m_begin + OtherInlineBufferCapacity;
+ other.begin_ = other.inline_buffer_;
+ other.end_ = other.begin_;
+ other.capacity_end_ = other.begin_ + OtherInlineBufferCapacity;
UPDATE_VECTOR_SIZE(this);
UPDATE_VECTOR_SIZE(&other);
}
~Vector()
{
- destruct_n(m_begin, this->size());
+ destruct_n(begin_, this->size());
if (!this->is_inline()) {
- m_allocator.deallocate(m_begin);
+ allocator_.deallocate(begin_);
}
}
- operator Span<T>() const
- {
- return Span<T>(m_begin, this->size());
- }
-
- operator MutableSpan<T>()
- {
- return MutableSpan<T>(m_begin, this->size());
- }
-
- Span<T> as_span() const
- {
- return *this;
- }
-
- MutableSpan<T> as_mutable_span()
- {
- return *this;
- }
-
Vector &operator=(const Vector &other)
{
if (this == &other) {
@@ -310,11 +299,61 @@ class Vector {
}
/**
+ * Get the value at the given index. This invokes undefined behavior when the index is out of
+ * bounds.
+ */
+ const T &operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size());
+ return begin_[index];
+ }
+
+ T &operator[](int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size());
+ return begin_[index];
+ }
+
+ operator Span<T>() const
+ {
+ return Span<T>(begin_, this->size());
+ }
+
+ operator MutableSpan<T>()
+ {
+ return MutableSpan<T>(begin_, this->size());
+ }
+
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ operator Span<U>() const
+ {
+ return Span<U>(begin_, this->size());
+ }
+
+ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ operator MutableSpan<U>()
+ {
+ return MutableSpan<U>(begin_, this->size());
+ }
+
+ Span<T> as_span() const
+ {
+ return *this;
+ }
+
+ MutableSpan<T> as_mutable_span()
+ {
+ return *this;
+ }
+
+ /**
* Make sure that enough memory is allocated to hold min_capacity elements.
* This won't necessarily make an allocation when min_capacity is small.
* The actual size of the vector does not change.
*/
- void reserve(uint min_capacity)
+ void reserve(const int64_t min_capacity)
{
if (min_capacity > this->capacity()) {
this->realloc_to_at_least(min_capacity);
@@ -327,17 +366,18 @@ class Vector {
* destructed. If new_size is larger than the old size, the new elements at the end are default
* constructed. If T is trivially constructible, the memory is not touched by this function.
*/
- void resize(uint new_size)
+ void resize(const int64_t new_size)
{
- uint old_size = this->size();
+ BLI_assert(new_size >= 0);
+ const int64_t old_size = this->size();
if (new_size > old_size) {
this->reserve(new_size);
- default_construct_n(m_begin + old_size, new_size - old_size);
+ default_construct_n(begin_ + old_size, new_size - old_size);
}
else {
- destruct_n(m_begin + new_size, old_size - new_size);
+ destruct_n(begin_ + new_size, old_size - new_size);
}
- m_end = m_begin + new_size;
+ end_ = begin_ + new_size;
UPDATE_VECTOR_SIZE(this);
}
@@ -347,17 +387,18 @@ class Vector {
* destructed. If new_size is larger than the old size, the new elements will be copy constructed
* from the given value.
*/
- void resize(uint new_size, const T &value)
+ void resize(const int64_t new_size, const T &value)
{
- uint old_size = this->size();
+ BLI_assert(new_size >= 0);
+ const int64_t old_size = this->size();
if (new_size > old_size) {
this->reserve(new_size);
- uninitialized_fill_n(m_begin + old_size, new_size - old_size, value);
+ uninitialized_fill_n(begin_ + old_size, new_size - old_size, value);
}
else {
- destruct_n(m_begin + new_size, old_size - new_size);
+ destruct_n(begin_ + new_size, old_size - new_size);
}
- m_end = m_begin + new_size;
+ end_ = begin_ + new_size;
UPDATE_VECTOR_SIZE(this);
}
@@ -367,8 +408,8 @@ class Vector {
*/
void clear()
{
- destruct_n(m_begin, this->size());
- m_end = m_begin;
+ destruct_n(begin_, this->size());
+ end_ = begin_;
UPDATE_VECTOR_SIZE(this);
}
@@ -378,14 +419,14 @@ class Vector {
*/
void clear_and_make_inline()
{
- destruct_n(m_begin, this->size());
+ destruct_n(begin_, this->size());
if (!this->is_inline()) {
- m_allocator.deallocate(m_begin);
+ allocator_.deallocate(begin_);
}
- m_begin = this->inline_buffer();
- m_end = m_begin;
- m_capacity_end = m_begin + InlineBufferCapacity;
+ begin_ = inline_buffer_;
+ end_ = begin_;
+ capacity_end_ = begin_ + InlineBufferCapacity;
UPDATE_VECTOR_SIZE(this);
}
@@ -410,9 +451,9 @@ class Vector {
* Append the value to the vector and return the index that can be used to access the newly
* added value.
*/
- uint append_and_get_index(const T &value)
+ int64_t append_and_get_index(const T &value)
{
- uint index = this->size();
+ const int64_t index = this->size();
this->append(value);
return index;
}
@@ -436,16 +477,16 @@ class Vector {
*/
void append_unchecked(const T &value)
{
- BLI_assert(m_end < m_capacity_end);
- new (m_end) T(value);
- m_end++;
+ BLI_assert(end_ < capacity_end_);
+ new (end_) T(value);
+ end_++;
UPDATE_VECTOR_SIZE(this);
}
void append_unchecked(T &&value)
{
- BLI_assert(m_end < m_capacity_end);
- new (m_end) T(std::move(value));
- m_end++;
+ BLI_assert(end_ < capacity_end_);
+ new (end_) T(std::move(value));
+ end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -453,10 +494,11 @@ class Vector {
* Insert the same element n times at the end of the vector.
* This might result in a reallocation internally.
*/
- void append_n_times(const T &value, uint n)
+ void append_n_times(const T &value, const int64_t n)
{
+ BLI_assert(n >= 0);
this->reserve(this->size() + n);
- blender::uninitialized_fill_n(m_end, n, value);
+ blender::uninitialized_fill_n(end_, n, value);
this->increase_size_by_unchecked(n);
}
@@ -466,10 +508,10 @@ class Vector {
* useful when you want to call constructors in the vector yourself. This should only be done in
* very rare cases and has to be justified every time.
*/
- void increase_size_by_unchecked(uint n)
+ void increase_size_by_unchecked(const int64_t n)
{
- BLI_assert(m_end + n <= m_capacity_end);
- m_end += n;
+ BLI_assert(end_ + n <= capacity_end_);
+ end_ += n;
UPDATE_VECTOR_SIZE(this);
}
@@ -482,7 +524,7 @@ class Vector {
{
this->extend(array.data(), array.size());
}
- void extend(const T *start, uint amount)
+ void extend(const T *start, int64_t amount)
{
this->reserve(this->size() + amount);
this->extend_unchecked(start, amount);
@@ -508,52 +550,37 @@ class Vector {
{
this->extend_unchecked(array.data(), array.size());
}
- void extend_unchecked(const T *start, uint amount)
+ void extend_unchecked(const T *start, int64_t amount)
{
- BLI_assert(m_begin + amount <= m_capacity_end);
- blender::uninitialized_copy_n(start, amount, m_end);
- m_end += amount;
+ BLI_assert(amount >= 0);
+ BLI_assert(begin_ + amount <= capacity_end_);
+ blender::uninitialized_copy_n(start, amount, end_);
+ end_ += amount;
UPDATE_VECTOR_SIZE(this);
}
/**
* Return a reference to the last element in the vector.
- * This will assert when the vector is empty.
+ * This invokes undefined behavior when the vector is empty.
*/
const T &last() const
{
BLI_assert(this->size() > 0);
- return *(m_end - 1);
+ return *(end_ - 1);
}
T &last()
{
BLI_assert(this->size() > 0);
- return *(m_end - 1);
- }
-
- /**
- * Replace every element with a new value.
- */
- void fill(const T &value)
- {
- initialized_fill_n(m_begin, this->size(), value);
- }
-
- /**
- * Copy the value to all positions specified by the indices array.
- */
- void fill_indices(Span<uint> indices, const T &value)
- {
- MutableSpan<T>(*this).fill_indices(indices, value);
+ return *(end_ - 1);
}
/**
* Return how many values are currently stored in the vector.
*/
- uint size() const
+ int64_t size() const
{
- BLI_assert(m_debug_size == (uint)(m_end - m_begin));
- return (uint)(m_end - m_begin);
+ BLI_assert(debug_size_ == (int64_t)(end_ - begin_));
+ return (int64_t)(end_ - begin_);
}
/**
@@ -563,7 +590,7 @@ class Vector {
*/
bool is_empty() const
{
- return m_begin == m_end;
+ return begin_ == end_;
}
/**
@@ -573,8 +600,8 @@ class Vector {
void remove_last()
{
BLI_assert(!this->is_empty());
- m_end--;
- m_end->~T();
+ end_--;
+ end_->~T();
UPDATE_VECTOR_SIZE(this);
}
@@ -587,9 +614,9 @@ class Vector {
T pop_last()
{
BLI_assert(!this->is_empty());
- m_end--;
- T value = std::move(*m_end);
- m_end->~T();
+ end_--;
+ T value = std::move(*end_);
+ end_->~T();
UPDATE_VECTOR_SIZE(this);
return value;
}
@@ -598,26 +625,27 @@ class Vector {
* Delete any element in the vector. The empty space will be filled by the previously last
* element. This takes O(1) time.
*/
- void remove_and_reorder(uint index)
+ void remove_and_reorder(const int64_t index)
{
+ BLI_assert(index >= 0);
BLI_assert(index < this->size());
- T *element_to_remove = m_begin + index;
- m_end--;
- if (element_to_remove < m_end) {
- *element_to_remove = std::move(*m_end);
+ T *element_to_remove = begin_ + index;
+ end_--;
+ if (element_to_remove < end_) {
+ *element_to_remove = std::move(*end_);
}
- m_end->~T();
+ end_->~T();
UPDATE_VECTOR_SIZE(this);
}
/**
- * 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)
{
- uint index = this->first_index_of(value);
- this->remove_and_reorder((uint)index);
+ const int64_t index = this->first_index_of(value);
+ this->remove_and_reorder(index);
}
/**
@@ -627,15 +655,16 @@ class Vector {
*
* This is similar to std::vector::erase.
*/
- void remove(uint index)
+ void remove(const int64_t index)
{
+ BLI_assert(index >= 0);
BLI_assert(index < this->size());
- uint last_index = this->size() - 1;
- for (uint i = index; i < last_index; i++) {
- m_begin[i] = std::move(m_begin[i + 1]);
+ const int64_t last_index = this->size() - 1;
+ for (int64_t i = index; i < last_index; i++) {
+ begin_[i] = std::move(begin_[i + 1]);
}
- m_begin[last_index].~T();
- m_end--;
+ begin_[last_index].~T();
+ end_--;
UPDATE_VECTOR_SIZE(this);
}
@@ -643,11 +672,11 @@ class Vector {
* Do a linear search to find the value in the vector.
* When found, return the first index, otherwise return -1.
*/
- int first_index_of_try(const T &value) const
+ int64_t first_index_of_try(const T &value) const
{
- for (T *current = m_begin; current != m_end; current++) {
+ for (const T *current = begin_; current != end_; current++) {
if (*current == value) {
- return (int)(current - m_begin);
+ return (int64_t)(current - begin_);
}
}
return -1;
@@ -657,11 +686,11 @@ class Vector {
* Do a linear search to find the value in the vector and return the found index. This invokes
* undefined behavior when the value is not in the vector.
*/
- uint first_index_of(const T &value) const
+ int64_t first_index_of(const T &value) const
{
- int index = this->first_index_of_try(value);
+ const int64_t index = this->first_index_of_try(value);
BLI_assert(index >= 0);
- return (uint)index;
+ return index;
}
/**
@@ -674,19 +703,11 @@ class Vector {
}
/**
- * Get the value at the given index. This invokes undefined behavior when the index is out of
- * bounds.
+ * Copies the given value to every element in the vector.
*/
- const T &operator[](uint index) const
+ void fill(const T &value) const
{
- BLI_assert(index < this->size());
- return m_begin[index];
- }
-
- T &operator[](uint index)
- {
- BLI_assert(index < this->size());
- return m_begin[index];
+ initialized_fill_n(begin_, this->size(), value);
}
/**
@@ -694,7 +715,7 @@ class Vector {
*/
T *data()
{
- return m_begin;
+ return begin_;
}
/**
@@ -702,34 +723,34 @@ class Vector {
*/
const T *data() const
{
- return m_begin;
+ return begin_;
}
T *begin()
{
- return m_begin;
+ return begin_;
}
T *end()
{
- return m_end;
+ return end_;
}
const T *begin() const
{
- return m_begin;
+ return begin_;
}
const T *end() const
{
- return m_end;
+ return end_;
}
/**
* Get the current capacity of the vector, i.e. the maximum number of elements the vector can
* hold, before it has to reallocate.
*/
- uint capacity() const
+ int64_t capacity() const
{
- return (uint)(m_capacity_end - m_begin);
+ return (int64_t)(capacity_end_ - begin_);
}
/**
@@ -737,7 +758,7 @@ class Vector {
* Obviously, this should only be used when you actually need the index in the loop.
*
* Example:
- * for (uint i : myvector.index_range()) {
+ * for (int64_t i : myvector.index_range()) {
* do_something(i, my_vector[i]);
* }
*/
@@ -754,7 +775,7 @@ class Vector {
std::cout << "Vector Stats: " << name << "\n";
std::cout << " Address: " << this << "\n";
std::cout << " Elements: " << this->size() << "\n";
- std::cout << " Capacity: " << (m_capacity_end - m_begin) << "\n";
+ std::cout << " Capacity: " << (capacity_end_ - begin_) << "\n";
std::cout << " Inline Capacity: " << InlineBufferCapacity << "\n";
char memory_size_str[15];
@@ -763,26 +784,19 @@ class Vector {
}
private:
- T *inline_buffer() const
- {
- return (T *)m_inline_buffer.ptr();
- }
-
bool is_inline() const
{
- return m_begin == this->inline_buffer();
+ return begin_ == inline_buffer_;
}
void ensure_space_for_one()
{
- if (UNLIKELY(m_end >= m_capacity_end)) {
+ if (UNLIKELY(end_ >= capacity_end_)) {
this->realloc_to_at_least(this->size() + 1);
}
- std::vector<int> a;
- a.push_back(4);
}
- BLI_NOINLINE void realloc_to_at_least(uint min_capacity)
+ BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity)
{
if (this->capacity() >= min_capacity) {
return;
@@ -790,59 +804,32 @@ class Vector {
/* At least double the size of the previous allocation. Otherwise consecutive calls to grow can
* cause a reallocation every time even though min_capacity only increments. */
- uint min_new_capacity = this->capacity() * 2;
+ const int64_t min_new_capacity = this->capacity() * 2;
- uint new_capacity = std::max(min_capacity, min_new_capacity);
- uint size = this->size();
+ const int64_t new_capacity = std::max(min_capacity, min_new_capacity);
+ const int64_t size = this->size();
- T *new_array = (T *)m_allocator.allocate(new_capacity * (uint)sizeof(T), alignof(T), AT);
- uninitialized_relocate_n(m_begin, size, new_array);
+ T *new_array = (T *)allocator_.allocate((size_t)new_capacity * sizeof(T), alignof(T), AT);
+ uninitialized_relocate_n(begin_, size, new_array);
if (!this->is_inline()) {
- m_allocator.deallocate(m_begin);
+ allocator_.deallocate(begin_);
}
- m_begin = new_array;
- m_end = m_begin + size;
- m_capacity_end = m_begin + new_capacity;
- }
-
- /**
- * Initialize all properties, except for m_allocator, which has to be initialized beforehand.
- */
- template<uint OtherInlineBufferCapacity>
- void init_copy_from_other_vector(const Vector<T, OtherInlineBufferCapacity, Allocator> &other)
- {
- m_allocator = other.m_allocator;
-
- uint size = other.size();
- uint capacity = size;
-
- if (size <= InlineBufferCapacity) {
- m_begin = this->inline_buffer();
- capacity = InlineBufferCapacity;
- }
- else {
- m_begin = (T *)m_allocator.allocate(sizeof(T) * size, alignof(T), AT);
- capacity = size;
- }
-
- m_end = m_begin + size;
- m_capacity_end = m_begin + capacity;
-
- uninitialized_copy_n(other.data(), size, m_begin);
- UPDATE_VECTOR_SIZE(this);
+ begin_ = new_array;
+ end_ = begin_ + size;
+ capacity_end_ = begin_ + new_capacity;
}
};
#undef UPDATE_VECTOR_SIZE
/**
- * Use when the vector is used in the local scope of a function. It has a larger inline storage by
- * default to make allocations less likely.
+ * Same as a normal Vector, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
*/
-template<typename T, uint InlineBufferCapacity = 20>
-using ScopedVector = Vector<T, InlineBufferCapacity, GuardedAllocator>;
+template<typename T, int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(T))>
+using RawVector = Vector<T, InlineBufferCapacity, RawAllocator>;
} /* namespace blender */
diff --git a/source/blender/blenlib/BLI_vector_adaptor.hh b/source/blender/blenlib/BLI_vector_adaptor.hh
new file mode 100644
index 00000000000..cadffc0b445
--- /dev/null
+++ b/source/blender/blenlib/BLI_vector_adaptor.hh
@@ -0,0 +1,105 @@
+/*
+ * 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 __BLI_VECTOR_ADAPTOR_HH__
+#define __BLI_VECTOR_ADAPTOR_HH__
+
+/** \file
+ * \ingroup bli
+ *
+ * A `blender::VectorAdaptor` is a container with a fixed maximum size and does not own the
+ * underlying memory. When an adaptor is constructed, you have to provide it with an uninitialized
+ * array that will be filled when elements are added to the vector. The vector adaptor is not able
+ * to grow. Therefore, it is undefined behavior to add more elements than fit into the provided
+ * buffer.
+ */
+
+#include "BLI_span.hh"
+
+namespace blender {
+
+template<typename T> class VectorAdaptor {
+ private:
+ T *begin_;
+ T *end_;
+ T *capacity_end_;
+
+ public:
+ VectorAdaptor() : begin_(nullptr), end_(nullptr), capacity_end_(nullptr)
+ {
+ }
+
+ VectorAdaptor(T *data, int64_t capacity, int64_t size = 0)
+ : begin_(data), end_(data + size), capacity_end_(data + capacity)
+ {
+ }
+
+ VectorAdaptor(MutableSpan<T> span) : VectorAdaptor(span.data(), span.size(), 0)
+ {
+ }
+
+ void append(const T &value)
+ {
+ BLI_assert(end_ < capacity_end_);
+ new (end_) T(value);
+ end_++;
+ }
+
+ void append(T &&value)
+ {
+ BLI_assert(end_ < capacity_end_);
+ new (end_) T(std::move(value));
+ end_++;
+ }
+
+ void append_n_times(const T &value, int64_t n)
+ {
+ BLI_assert(end_ + n <= capacity_end_);
+ uninitialized_fill_n(end_, n, value);
+ end_ += n;
+ }
+
+ void extend(Span<T> values)
+ {
+ BLI_assert(end_ + values.size() <= capacity_end_);
+ uninitialized_copy_n(values.data(), values.size(), end_);
+ end_ += values.size();
+ }
+
+ int64_t capacity() const
+ {
+ return capacity_end_ - begin_;
+ }
+
+ int64_t size() const
+ {
+ return end_ - begin_;
+ }
+
+ bool is_empty() const
+ {
+ return begin_ == end_;
+ }
+
+ bool is_full() const
+ {
+ return end_ == capacity_end_;
+ }
+};
+
+} // namespace blender
+
+#endif /* __BLI_VECTOR_ADAPTOR_HH__ */
diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh
index d330d3c3247..f007d41118f 100644
--- a/source/blender/blenlib/BLI_vector_set.hh
+++ b/source/blender/blenlib/BLI_vector_set.hh
@@ -27,7 +27,7 @@
*
* All core operations (add, remove and contains) can be done in O(1) amortized expected time.
*
- * Using a VectorSet instead of a normal Set can be benefitial in any of the following
+ * Using a VectorSet instead of a normal Set can be beneficial in any of the following
* circumstances:
* - The insertion order is important.
* - Iteration over all keys has to be fast.
@@ -106,30 +106,30 @@ class VectorSet {
* Slots are either empty, occupied or removed. The number of occupied slots can be computed by
* subtracting the removed slots from the occupied-and-removed slots.
*/
- uint32_t m_removed_slots;
- uint32_t m_occupied_and_removed_slots;
+ int64_t removed_slots_;
+ int64_t occupied_and_removed_slots_;
/**
* The maximum number of slots that can be used (either occupied or removed) until the set has to
* grow. This is the total number of slots times the max load factor.
*/
- uint32_t m_usable_slots;
+ int64_t usable_slots_;
/**
* The number of slots minus one. This is a bit mask that can be used to turn any integer into a
* valid slot index efficiently.
*/
- uint32_t m_slot_mask;
+ uint64_t slot_mask_;
/** This is called to hash incoming keys. */
- Hash m_hash;
+ Hash hash_;
/** This is called to check equality of two keys. */
- IsEqual m_is_equal;
+ IsEqual is_equal_;
/** The max load factor is 1/2 = 50% by default. */
#define LOAD_FACTOR 1, 2
- LoadFactor m_max_load_factor = LoadFactor(LOAD_FACTOR);
+ LoadFactor max_load_factor_ = LoadFactor(LOAD_FACTOR);
using SlotArray = Array<Slot, LoadFactor::compute_total_slots(4, LOAD_FACTOR), Allocator>;
#undef LOAD_FACTOR
@@ -137,19 +137,19 @@ class VectorSet {
* This is the array that contains the actual slots. There is always at least one empty slot and
* the size of the array is a power of two.
*/
- SlotArray m_slots;
+ SlotArray slots_;
/**
* Pointer to an array that contains all keys. The keys are sorted by insertion order as long as
* no keys are removed. The first set->size() elements in this array are initialized. The
- * capacity of the array is m_usable_slots.
+ * capacity of the array is usable_slots_.
*/
- Key *m_keys;
+ Key *keys_;
/** Iterate over a slot index sequence for a given hash. */
#define VECTOR_SET_SLOT_PROBING_BEGIN(HASH, R_SLOT) \
- SLOT_PROBING_BEGIN (ProbingStrategy, HASH, m_slot_mask, SLOT_INDEX) \
- auto &R_SLOT = m_slots[SLOT_INDEX];
+ SLOT_PROBING_BEGIN (ProbingStrategy, HASH, slot_mask_, SLOT_INDEX) \
+ auto &R_SLOT = slots_[SLOT_INDEX];
#define VECTOR_SET_SLOT_PROBING_END() SLOT_PROBING_END()
public:
@@ -159,12 +159,12 @@ class VectorSet {
* is performed on the first insertion.
*/
VectorSet()
- : m_removed_slots(0),
- m_occupied_and_removed_slots(0),
- m_usable_slots(0),
- m_slot_mask(0),
- m_slots(1),
- m_keys(nullptr)
+ : removed_slots_(0),
+ occupied_and_removed_slots_(0),
+ usable_slots_(0),
+ slot_mask_(0),
+ slots_(1),
+ keys_(nullptr)
{
}
@@ -178,37 +178,37 @@ class VectorSet {
~VectorSet()
{
- destruct_n(m_keys, this->size());
- if (m_keys != nullptr) {
- this->deallocate_keys_array(m_keys);
+ destruct_n(keys_, this->size());
+ if (keys_ != nullptr) {
+ this->deallocate_keys_array(keys_);
}
}
VectorSet(const VectorSet &other)
- : m_removed_slots(other.m_removed_slots),
- m_occupied_and_removed_slots(other.m_occupied_and_removed_slots),
- m_usable_slots(other.m_usable_slots),
- m_slot_mask(other.m_slot_mask),
- m_slots(other.m_slots)
+ : removed_slots_(other.removed_slots_),
+ occupied_and_removed_slots_(other.occupied_and_removed_slots_),
+ usable_slots_(other.usable_slots_),
+ slot_mask_(other.slot_mask_),
+ slots_(other.slots_)
{
- m_keys = this->allocate_keys_array(m_usable_slots);
- uninitialized_copy_n(other.m_keys, other.size(), m_keys);
+ keys_ = this->allocate_keys_array(usable_slots_);
+ uninitialized_copy_n(other.keys_, other.size(), keys_);
}
VectorSet(VectorSet &&other) noexcept
- : m_removed_slots(other.m_removed_slots),
- m_occupied_and_removed_slots(other.m_occupied_and_removed_slots),
- m_usable_slots(other.m_usable_slots),
- m_slot_mask(other.m_slot_mask),
- m_slots(std::move(other.m_slots)),
- m_keys(other.m_keys)
+ : removed_slots_(other.removed_slots_),
+ occupied_and_removed_slots_(other.occupied_and_removed_slots_),
+ usable_slots_(other.usable_slots_),
+ slot_mask_(other.slot_mask_),
+ slots_(std::move(other.slots_)),
+ keys_(other.keys_)
{
- other.m_removed_slots = 0;
- other.m_occupied_and_removed_slots = 0;
- other.m_usable_slots = 0;
- other.m_slot_mask = 0;
- other.m_slots = SlotArray(1);
- other.m_keys = nullptr;
+ other.removed_slots_ = 0;
+ other.occupied_and_removed_slots_ = 0;
+ other.usable_slots_ = 0;
+ other.slot_mask_ = 0;
+ other.slots_ = SlotArray(1);
+ other.keys_ = nullptr;
}
VectorSet &operator=(const VectorSet &other)
@@ -236,17 +236,43 @@ class VectorSet {
}
/**
+ * Get the key stored at the given position in the vector.
+ */
+ const Key &operator[](const int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index <= this->size());
+ return keys_[index];
+ }
+
+ operator Span<Key>() const
+ {
+ return Span<Key>(keys_, this->size());
+ }
+
+ /**
+ * Get an Span referencing the keys vector. The referenced memory buffer is only valid as
+ * long as the vector set is not changed.
+ *
+ * The keys must not be changed, because this would change their hash value.
+ */
+ Span<Key> as_span() const
+ {
+ return *this;
+ }
+
+ /**
* Add a new key to the vector set. This invokes undefined behavior when the key is in the set
* already. When you know for certain that a key is not in the set yet, use this method for
* better performance. This also expresses the intent better.
*/
void add_new(const Key &key)
{
- this->add_new__impl(key, m_hash(key));
+ this->add_new__impl(key, hash_(key));
}
void add_new(Key &&key)
{
- this->add_new__impl(std::move(key), m_hash(key));
+ this->add_new__impl(std::move(key), hash_(key));
}
/**
@@ -263,13 +289,9 @@ class VectorSet {
{
return this->add_as(std::move(key));
}
-
- /**
- * Same as `add`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool add_as(ForwardKey &&key)
{
- return this->add__impl(std::forward<ForwardKey>(key), m_hash(key));
+ return this->add__impl(std::forward<ForwardKey>(key), hash_(key));
}
/**
@@ -295,13 +317,9 @@ class VectorSet {
{
return this->contains_as(key);
}
-
- /**
- * Same as `contains`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool contains_as(const ForwardKey &key) const
{
- return this->contains__impl(key, m_hash(key));
+ return this->contains__impl(key, hash_(key));
}
/**
@@ -314,13 +332,9 @@ class VectorSet {
{
return this->remove_as(key);
}
-
- /**
- * Same as `remove`, but accepts other key types that are supported by the hash function.
- */
template<typename ForwardKey> bool remove_as(const ForwardKey &key)
{
- return this->remove__impl(key, m_hash(key));
+ return this->remove__impl(key, hash_(key));
}
/**
@@ -331,14 +345,9 @@ class VectorSet {
{
this->remove_contained_as(key);
}
-
- /**
- * Same as `remove_contained`, but accepts other key types that are supported by the hash
- * function.
- */
template<typename ForwardKey> void remove_contained_as(const ForwardKey &key)
{
- this->remove_contained__impl(key, m_hash(key));
+ this->remove_contained__impl(key, hash_(key));
}
/**
@@ -354,34 +363,26 @@ class VectorSet {
* Return the location of the key in the vector. It is assumed, that the key is in the vector
* set. If this is not necessarily the case, use `index_of_try`.
*/
- uint32_t index_of(const Key &key) const
+ int64_t index_of(const Key &key) const
{
return this->index_of_as(key);
}
-
- /**
- * Same as `index_of`, but accepts other key types that are supported by the hash function.
- */
- template<typename ForwardKey> uint32_t index_of_as(const ForwardKey &key) const
+ template<typename ForwardKey> int64_t index_of_as(const ForwardKey &key) const
{
- return this->index_of__impl(key, m_hash(key));
+ return this->index_of__impl(key, hash_(key));
}
/**
* Return the location of the key in the vector. If the key is not in the set, -1 is returned.
* If you know for sure that the key is in the set, it is better to use `index_of` instead.
*/
- int32_t index_of_try(const Key &key) const
+ int64_t index_of_try(const Key &key) const
{
- return (int32_t)this->index_of_try_as(key);
+ return this->index_of_try_as(key);
}
-
- /**
- * Same as `index_of_try`, but accepts other key types that are supported by the hash function.
- */
- template<typename ForwardKey> int32_t index_of_try_as(const ForwardKey &key) const
+ template<typename ForwardKey> int64_t index_of_try_as(const ForwardKey &key) const
{
- return this->index_of_try__impl(key, m_hash(key));
+ return this->index_of_try__impl(key, hash_(key));
}
/**
@@ -389,42 +390,17 @@ class VectorSet {
*/
const Key *data() const
{
- return m_keys;
+ return keys_;
}
const Key *begin() const
{
- return m_keys;
+ return keys_;
}
const Key *end() const
{
- return m_keys + this->size();
- }
-
- /**
- * Get the key stored at the given position in the vector.
- */
- const Key &operator[](uint32_t index) const
- {
- BLI_assert(index <= this->size());
- return m_keys[index];
- }
-
- operator Span<Key>() const
- {
- return Span<Key>(m_keys, this->size());
- }
-
- /**
- * Get an Span referencing the keys vector. The referenced memory buffer is only valid as
- * long as the vector set is not changed.
- *
- * The keys must not be changed, because this would change their hash value.
- */
- Span<Key> as_span() const
- {
- return *this;
+ return keys_ + this->size();
}
/**
@@ -433,15 +409,15 @@ class VectorSet {
void print_stats(StringRef name = "") const
{
HashTableStats stats(*this, this->as_span());
- stats.print();
+ stats.print(name);
}
/**
* Returns the number of keys stored in the vector set.
*/
- uint32_t size() const
+ int64_t size() const
{
- return m_occupied_and_removed_slots - m_removed_slots;
+ return occupied_and_removed_slots_ - removed_slots_;
}
/**
@@ -449,29 +425,29 @@ class VectorSet {
*/
bool is_empty() const
{
- return m_occupied_and_removed_slots == m_removed_slots;
+ return occupied_and_removed_slots_ == removed_slots_;
}
/**
* Returns the number of available slots. This is mostly for debugging purposes.
*/
- uint32_t capacity() const
+ int64_t capacity() const
{
- return m_slots.size();
+ return slots_.size();
}
/**
* Returns the amount of removed slots in the set. This is mostly for debugging purposes.
*/
- uint32_t removed_amount() const
+ int64_t removed_amount() const
{
- return m_removed_slots;
+ return removed_slots_;
}
/**
* Returns the bytes required per element. This is mostly for debugging purposes.
*/
- uint32_t size_per_element() const
+ int64_t size_per_element() const
{
return sizeof(Slot) + sizeof(Key);
}
@@ -480,17 +456,17 @@ class VectorSet {
* Returns the approximate memory requirements of the set in bytes. This is more correct for
* larger sets.
*/
- uint32_t size_in_bytes() const
+ int64_t size_in_bytes() const
{
- return sizeof(Slot) * m_slots.size() + sizeof(Key) * m_usable_slots;
+ return (int64_t)(sizeof(Slot) * slots_.size() + sizeof(Key) * usable_slots_);
}
/**
* Potentially resize the vector set such that it can hold n elements without doing another grow.
*/
- void reserve(uint32_t n)
+ void reserve(const int64_t n)
{
- if (m_usable_slots < n) {
+ if (usable_slots_ < n) {
this->realloc_and_reinsert(n);
}
}
@@ -499,60 +475,61 @@ class VectorSet {
* Get the number of collisions that the probing strategy has to go through to find the key or
* determine that it is not in the set.
*/
- uint32_t count_collisions(const Key &key) const
+ int64_t count_collisions(const Key &key) const
{
- return this->count_collisions__impl(key, m_hash(key));
+ return this->count_collisions__impl(key, hash_(key));
}
private:
- BLI_NOINLINE void realloc_and_reinsert(uint32_t min_usable_slots)
+ BLI_NOINLINE void realloc_and_reinsert(const int64_t min_usable_slots)
{
- uint32_t total_slots, usable_slots;
- m_max_load_factor.compute_total_and_usable_slots(
+ int64_t total_slots, usable_slots;
+ max_load_factor_.compute_total_and_usable_slots(
SlotArray::inline_buffer_capacity(), min_usable_slots, &total_slots, &usable_slots);
- uint32_t new_slot_mask = total_slots - 1;
+ BLI_assert(total_slots >= 1);
+ const uint64_t new_slot_mask = (uint64_t)total_slots - 1;
/* Optimize the case when the set was empty beforehand. We can avoid some copies here. */
if (this->size() == 0) {
- m_slots.~Array();
- new (&m_slots) SlotArray(total_slots);
- m_removed_slots = 0;
- m_occupied_and_removed_slots = 0;
- m_usable_slots = usable_slots;
- m_slot_mask = new_slot_mask;
- m_keys = this->allocate_keys_array(usable_slots);
+ slots_.~Array();
+ new (&slots_) SlotArray(total_slots);
+ removed_slots_ = 0;
+ occupied_and_removed_slots_ = 0;
+ usable_slots_ = usable_slots;
+ slot_mask_ = new_slot_mask;
+ keys_ = this->allocate_keys_array(usable_slots);
return;
}
SlotArray new_slots(total_slots);
- for (Slot &slot : m_slots) {
+ for (Slot &slot : slots_) {
if (slot.is_occupied()) {
this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask);
}
}
Key *new_keys = this->allocate_keys_array(usable_slots);
- uninitialized_relocate_n(m_keys, this->size(), new_keys);
- this->deallocate_keys_array(m_keys);
+ uninitialized_relocate_n(keys_, this->size(), new_keys);
+ this->deallocate_keys_array(keys_);
/* All occupied slots have been destructed already and empty/removed slots are assumed to be
* trivially destructible. */
- m_slots.clear_without_destruct();
- m_slots = std::move(new_slots);
- m_keys = new_keys;
- m_occupied_and_removed_slots -= m_removed_slots;
- m_usable_slots = usable_slots;
- m_removed_slots = 0;
- m_slot_mask = new_slot_mask;
+ slots_.clear_without_destruct();
+ slots_ = std::move(new_slots);
+ keys_ = new_keys;
+ occupied_and_removed_slots_ -= removed_slots_;
+ usable_slots_ = usable_slots;
+ removed_slots_ = 0;
+ slot_mask_ = new_slot_mask;
}
void add_after_grow_and_destruct_old(Slot &old_slot,
SlotArray &new_slots,
- uint32_t new_slot_mask)
+ const uint64_t new_slot_mask)
{
- const Key &key = m_keys[old_slot.index()];
- uint32_t hash = old_slot.get_hash(key, Hash());
+ const Key &key = keys_[old_slot.index()];
+ const uint64_t hash = old_slot.get_hash(key, Hash());
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
@@ -564,20 +541,21 @@ class VectorSet {
SLOT_PROBING_END();
}
- template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint32_t hash) const
+ template<typename ForwardKey>
+ bool contains__impl(const ForwardKey &key, const uint64_t hash) const
{
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
return false;
}
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
return true;
}
}
VECTOR_SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> void add_new__impl(ForwardKey &&key, uint32_t hash)
+ template<typename ForwardKey> void add_new__impl(ForwardKey &&key, const uint64_t hash)
{
BLI_assert(!this->contains_as(key));
@@ -585,41 +563,42 @@ class VectorSet {
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- uint32_t index = this->size();
- new (m_keys + index) Key(std::forward<ForwardKey>(key));
+ int64_t index = this->size();
+ new (keys_ + index) Key(std::forward<ForwardKey>(key));
slot.occupy(index, hash);
- m_occupied_and_removed_slots++;
+ occupied_and_removed_slots_++;
return;
}
}
VECTOR_SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> bool add__impl(ForwardKey &&key, uint32_t hash)
+ template<typename ForwardKey> bool add__impl(ForwardKey &&key, const uint64_t hash)
{
this->ensure_can_add();
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- uint32_t index = this->size();
- new (m_keys + index) Key(std::forward<ForwardKey>(key));
- m_occupied_and_removed_slots++;
+ int64_t index = this->size();
+ new (keys_ + index) Key(std::forward<ForwardKey>(key));
+ occupied_and_removed_slots_++;
slot.occupy(index, hash);
return true;
}
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
return false;
}
}
VECTOR_SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> uint32_t index_of__impl(const ForwardKey &key, uint32_t hash) const
+ template<typename ForwardKey>
+ int64_t index_of__impl(const ForwardKey &key, const uint64_t hash) const
{
BLI_assert(this->contains_as(key));
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
return slot.index();
}
}
@@ -627,11 +606,11 @@ class VectorSet {
}
template<typename ForwardKey>
- int32_t index_of_try__impl(const ForwardKey &key, uint32_t hash) const
+ int64_t index_of_try__impl(const ForwardKey &key, const uint64_t hash) const
{
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
- return (int32_t)slot.index();
+ if (slot.contains(key, is_equal_, hash, keys_)) {
+ return slot.index();
}
if (slot.is_empty()) {
return -1;
@@ -644,12 +623,12 @@ class VectorSet {
{
BLI_assert(this->size() > 0);
- uint32_t index_to_pop = this->size() - 1;
- Key key = std::move(m_keys[index_to_pop]);
- m_keys[index_to_pop].~Key();
- uint32_t hash = m_hash(key);
+ const int64_t index_to_pop = this->size() - 1;
+ Key key = std::move(keys_[index_to_pop]);
+ keys_[index_to_pop].~Key();
+ const uint64_t hash = hash_(key);
- m_removed_slots++;
+ removed_slots_++;
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.has_index(index_to_pop)) {
@@ -660,10 +639,10 @@ class VectorSet {
VECTOR_SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey> bool remove__impl(const ForwardKey &key, const uint64_t hash)
{
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
this->remove_key_internal(slot);
return true;
}
@@ -674,12 +653,13 @@ class VectorSet {
VECTOR_SET_SLOT_PROBING_END();
}
- template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint32_t hash)
+ template<typename ForwardKey>
+ void remove_contained__impl(const ForwardKey &key, const uint64_t hash)
{
BLI_assert(this->contains_as(key));
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
this->remove_key_internal(slot);
return;
}
@@ -689,24 +669,24 @@ class VectorSet {
void remove_key_internal(Slot &slot)
{
- uint32_t index_to_remove = slot.index();
- uint32_t size = this->size();
- uint32_t last_element_index = size - 1;
+ int64_t index_to_remove = slot.index();
+ int64_t size = this->size();
+ int64_t last_element_index = size - 1;
if (index_to_remove < last_element_index) {
- m_keys[index_to_remove] = std::move(m_keys[last_element_index]);
- this->update_slot_index(m_keys[index_to_remove], last_element_index, index_to_remove);
+ keys_[index_to_remove] = std::move(keys_[last_element_index]);
+ this->update_slot_index(keys_[index_to_remove], last_element_index, index_to_remove);
}
- m_keys[last_element_index].~Key();
+ keys_[last_element_index].~Key();
slot.remove();
- m_removed_slots++;
+ removed_slots_++;
return;
}
- void update_slot_index(const Key &key, uint32_t old_index, uint32_t new_index)
+ void update_slot_index(const Key &key, const int64_t old_index, const int64_t new_index)
{
- uint32_t hash = m_hash(key);
+ uint64_t hash = hash_(key);
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.has_index(old_index)) {
slot.update_index(new_index);
@@ -717,12 +697,12 @@ class VectorSet {
}
template<typename ForwardKey>
- uint32_t count_collisions__impl(const ForwardKey &key, uint32_t hash) const
+ int64_t count_collisions__impl(const ForwardKey &key, const uint64_t hash) const
{
- uint32_t collisions = 0;
+ int64_t collisions = 0;
VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
- if (slot.contains(key, m_is_equal, hash, m_keys)) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
return collisions;
}
if (slot.is_empty()) {
@@ -735,23 +715,34 @@ class VectorSet {
void ensure_can_add()
{
- if (m_occupied_and_removed_slots >= m_usable_slots) {
+ if (occupied_and_removed_slots_ >= usable_slots_) {
this->realloc_and_reinsert(this->size() + 1);
- BLI_assert(m_occupied_and_removed_slots < m_usable_slots);
+ BLI_assert(occupied_and_removed_slots_ < usable_slots_);
}
}
- Key *allocate_keys_array(uint32_t size)
+ Key *allocate_keys_array(const int64_t size)
{
- return (Key *)m_slots.allocator().allocate((uint32_t)sizeof(Key) * size, alignof(Key), AT);
+ return (Key *)slots_.allocator().allocate(sizeof(Key) * (size_t)size, alignof(Key), AT);
}
void deallocate_keys_array(Key *keys)
{
- m_slots.allocator().deallocate(keys);
+ slots_.allocator().deallocate(keys);
}
};
+/**
+ * Same as a normal VectorSet, but does not use Blender's guarded allocator. This is useful when
+ * allocating memory with static storage duration.
+ */
+template<typename Key,
+ typename ProbingStrategy = DefaultProbingStrategy,
+ typename Hash = DefaultHash<Key>,
+ typename IsEqual = DefaultEquality,
+ typename Slot = typename DefaultVectorSetSlot<Key>::type>
+using RawVectorSet = VectorSet<Key, ProbingStrategy, Hash, IsEqual, Slot, RawAllocator>;
+
} // namespace blender
#endif /* __BLI_VECTOR_SET_HH__ */
diff --git a/source/blender/blenlib/BLI_vector_set_slots.hh b/source/blender/blenlib/BLI_vector_set_slots.hh
index 25148866b6c..49e6d4daedb 100644
--- a/source/blender/blenlib/BLI_vector_set_slots.hh
+++ b/source/blender/blenlib/BLI_vector_set_slots.hh
@@ -53,7 +53,7 @@ template<typename Key> class SimpleVectorSetSlot {
/**
* After the default constructor has run, the slot has to be in the empty state.
*/
- int32_t m_state = s_is_empty;
+ int64_t state_ = s_is_empty;
public:
/**
@@ -61,7 +61,7 @@ template<typename Key> class SimpleVectorSetSlot {
*/
bool is_occupied() const
{
- return m_state >= 0;
+ return state_ >= 0;
}
/**
@@ -69,16 +69,16 @@ template<typename Key> class SimpleVectorSetSlot {
*/
bool is_empty() const
{
- return m_state == s_is_empty;
+ return state_ == s_is_empty;
}
/**
* Return the stored index. It is assumed that the slot is occupied.
*/
- uint32_t index() const
+ int64_t index() const
{
BLI_assert(this->is_occupied());
- return (uint32_t)m_state;
+ return state_;
}
/**
@@ -88,11 +88,11 @@ template<typename Key> class SimpleVectorSetSlot {
template<typename ForwardKey, typename IsEqual>
bool contains(const ForwardKey &key,
const IsEqual &is_equal,
- uint32_t UNUSED(hash),
+ uint64_t UNUSED(hash),
const Key *keys) const
{
- if (m_state >= 0) {
- return is_equal(key, keys[m_state]);
+ if (state_ >= 0) {
+ return is_equal(key, keys[state_]);
}
return false;
}
@@ -102,31 +102,31 @@ template<typename Key> class SimpleVectorSetSlot {
* we can avoid a comparison with the state, since we know the slot is occupied. For this
* specific slot implementation, this does not make a difference.
*/
- void relocate_occupied_here(SimpleVectorSetSlot &other, uint32_t UNUSED(hash))
+ void relocate_occupied_here(SimpleVectorSetSlot &other, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
BLI_assert(other.is_occupied());
- m_state = other.m_state;
+ state_ = other.state_;
}
/**
* Change the state of this slot from empty/removed to occupied. The hash can be used by other
* slot implementations.
*/
- void occupy(uint32_t index, uint32_t UNUSED(hash))
+ void occupy(int64_t index, uint64_t UNUSED(hash))
{
BLI_assert(!this->is_occupied());
- m_state = (int32_t)index;
+ state_ = index;
}
/**
* The key has changed its position in the vector, so the index has to be updated. This method
* can assume that the slot is currently occupied.
*/
- void update_index(uint32_t index)
+ void update_index(int64_t index)
{
BLI_assert(this->is_occupied());
- m_state = (int32_t)index;
+ state_ = index;
}
/**
@@ -135,22 +135,22 @@ template<typename Key> class SimpleVectorSetSlot {
void remove()
{
BLI_assert(this->is_occupied());
- m_state = s_is_removed;
+ state_ = s_is_removed;
}
/**
* Return true if this slot is currently occupied and its corresponding key has the given index.
*/
- bool has_index(uint32_t index) const
+ bool has_index(int64_t index) const
{
- return (uint32_t)m_state == index;
+ return state_ == index;
}
/**
* Return the hash of the currently stored key. In this simple set slot implementation, we just
* compute the hash here. Other implementations might store the hash in the slot instead.
*/
- template<typename Hash> uint32_t get_hash(const Key &key, const Hash &hash) const
+ template<typename Hash> uint64_t get_hash(const Key &key, const Hash &hash) const
{
BLI_assert(this->is_occupied());
return hash(key);
diff --git a/source/blender/blenlib/BLI_voxel.h b/source/blender/blenlib/BLI_voxel.h
index 220ab9b3705..82854c57928 100644
--- a/source/blender/blenlib/BLI_voxel.h
+++ b/source/blender/blenlib/BLI_voxel.h
@@ -35,10 +35,13 @@ extern "C" {
(int64_t)(z) * (int64_t)(res)[0] * (int64_t)(res)[1])
/* all input coordinates must be in bounding box 0.0 - 1.0 */
-float BLI_voxel_sample_nearest(float *data, const int res[3], const float co[3]);
-float BLI_voxel_sample_trilinear(float *data, const int res[3], const float co[3]);
-float BLI_voxel_sample_triquadratic(float *data, const int res[3], const float co[3]);
-float BLI_voxel_sample_tricubic(float *data, const int res[3], const float co[3], int bspline);
+float BLI_voxel_sample_nearest(const float *data, const int res[3], const float co[3]);
+float BLI_voxel_sample_trilinear(const float *data, const int res[3], const float co[3]);
+float BLI_voxel_sample_triquadratic(const float *data, const int res[3], const float co[3]);
+float BLI_voxel_sample_tricubic(const float *data,
+ const int res[3],
+ const float co[3],
+ int bspline);
#ifdef __cplusplus
}
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 69df0505dfe..4997917a93f 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -86,6 +86,7 @@ set(SRC
intern/listbase.c
intern/math_base.c
intern/math_base_inline.c
+ intern/math_base_safe_inline.c
intern/math_bits_inline.c
intern/math_color.c
intern/math_color_blend_inline.c
@@ -105,7 +106,7 @@ set(SRC
intern/polyfill_2d.c
intern/polyfill_2d_beautify.c
intern/quadric.c
- intern/rand.c
+ intern/rand.cc
intern/rct.c
intern/scanfill.c
intern/scanfill_utils.c
@@ -124,7 +125,7 @@ set(SRC
intern/task_pool.cc
intern/task_range.cc
intern/task_scheduler.cc
- intern/threads.c
+ intern/threads.cc
intern/time.c
intern/timecode.c
intern/timeit.cc
@@ -163,6 +164,7 @@ set(SRC
BLI_convexhull_2d.h
BLI_delaunay_2d.h
BLI_dial_2d.h
+ BLI_disjoint_set.hh
BLI_dlrbTree.h
BLI_dot_export.hh
BLI_dot_export_attribute_enums.hh
@@ -197,6 +199,7 @@ set(SRC
BLI_kdtree.h
BLI_kdtree_impl.h
BLI_lasso_2d.h
+ BLI_linear_allocator.hh
BLI_link_utils.h
BLI_linklist.h
BLI_linklist_lockfree.h
@@ -207,6 +210,7 @@ set(SRC
BLI_map_slots.hh
BLI_math.h
BLI_math_base.h
+ BLI_math_base_safe.h
BLI_math_bits.h
BLI_math_color.h
BLI_math_color_blend.h
@@ -225,14 +229,15 @@ 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
BLI_probing_strategies.hh
BLI_quadric.h
BLI_rand.h
+ BLI_rand.hh
BLI_rect.h
+ BLI_resource_collector.hh
BLI_scanfill.h
BLI_set.hh
BLI_set_slots.hh
@@ -262,6 +267,7 @@ set(SRC
BLI_utility_mixins.hh
BLI_uvproject.h
BLI_vector.hh
+ BLI_vector_adaptor.hh
BLI_vector_set.hh
BLI_vector_set_slots.hh
BLI_vfontdata.h
@@ -325,6 +331,7 @@ endif()
# no need to compile object files for inline headers.
set_source_files_properties(
intern/math_base_inline.c
+ intern/math_base_safe_inline.c
intern/math_bits_inline.c
intern/math_color_blend_inline.c
intern/math_color_inline.c
@@ -334,3 +341,29 @@ set_source_files_properties(
)
blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/BLI_array_test.cc
+ tests/BLI_disjoint_set_test.cc
+ tests/BLI_edgehash_test.cc
+ tests/BLI_index_mask_test.cc
+ tests/BLI_index_range_test.cc
+ tests/BLI_linear_allocator_test.cc
+ tests/BLI_map_test.cc
+ tests/BLI_math_base_safe_test.cc
+ tests/BLI_memory_utils_test.cc
+ tests/BLI_multi_value_map_test.cc
+ tests/BLI_set_test.cc
+ tests/BLI_span_test.cc
+ tests/BLI_stack_cxx_test.cc
+ tests/BLI_string_ref_test.cc
+ tests/BLI_vector_set_test.cc
+ tests/BLI_vector_test.cc
+ )
+ set(TEST_LIB
+ bf_blenlib
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_bli_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
diff --git a/source/blender/blenlib/intern/BLI_ghash_utils.c b/source/blender/blenlib/intern/BLI_ghash_utils.c
index 83bf0373ae7..d6a4b24682f 100644
--- a/source/blender/blenlib/intern/BLI_ghash_utils.c
+++ b/source/blender/blenlib/intern/BLI_ghash_utils.c
@@ -86,25 +86,6 @@ bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b)
return (memcmp(a, b, sizeof(uint[4])) != 0);
}
-uint BLI_ghashutil_uinthash_v2(const uint key[2])
-{
- uint hash;
- hash = key[0];
- hash *= 37;
- hash += key[1];
- return hash;
-}
-
-uint BLI_ghashutil_uinthash_v2_murmur(const uint key[2])
-{
- return BLI_hash_mm2((const unsigned char *)key, sizeof(int) * 2 /* sizeof(key) */, 0);
-}
-
-bool BLI_ghashutil_uinthash_v2_cmp(const void *a, const void *b)
-{
- return (memcmp(a, b, sizeof(uint[2])) != 0);
-}
-
uint BLI_ghashutil_uinthash(uint key)
{
key += ~(key << 16);
diff --git a/source/blender/blenlib/intern/BLI_index_range.cc b/source/blender/blenlib/intern/BLI_index_range.cc
index 910e418a29b..43c6265a17d 100644
--- a/source/blender/blenlib/intern/BLI_index_range.cc
+++ b/source/blender/blenlib/intern/BLI_index_range.cc
@@ -24,28 +24,28 @@
namespace blender {
-static Vector<Array<uint, 0, RawAllocator>, 1, RawAllocator> arrays;
-static uint current_array_size = 0;
-static uint *current_array = nullptr;
+static RawVector<RawArray<int64_t, 0>> arrays;
+static int64_t current_array_size = 0;
+static int64_t *current_array = nullptr;
static std::mutex current_array_mutex;
-Span<uint> IndexRange::as_span() const
+Span<int64_t> IndexRange::as_span() const
{
- uint min_required_size = m_start + m_size;
+ int64_t min_required_size = start_ + size_;
if (min_required_size <= current_array_size) {
- return Span<uint>(current_array + m_start, m_size);
+ return Span<int64_t>(current_array + start_, size_);
}
std::lock_guard<std::mutex> lock(current_array_mutex);
if (min_required_size <= current_array_size) {
- return Span<uint>(current_array + m_start, m_size);
+ return Span<int64_t>(current_array + start_, size_);
}
- uint new_size = std::max<uint>(1000, power_of_2_max_u(min_required_size));
- Array<uint, 0, RawAllocator> new_array(new_size);
- for (uint i = 0; i < new_size; i++) {
+ int64_t new_size = std::max<int64_t>(1000, power_of_2_max_u(min_required_size));
+ RawArray<int64_t, 0> new_array(new_size);
+ for (int64_t i = 0; i < new_size; i++) {
new_array[i] = i;
}
arrays.append(std::move(new_array));
@@ -54,7 +54,7 @@ Span<uint> IndexRange::as_span() const
std::atomic_thread_fence(std::memory_order_seq_cst);
current_array_size = new_size;
- return Span<uint>(current_array + m_start, m_size);
+ return Span<int64_t>(current_array + start_, size_);
}
} // namespace blender
diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c
index da67baf0ead..a3f93ccc753 100644
--- a/source/blender/blenlib/intern/BLI_kdopbvh.c
+++ b/source/blender/blenlib/intern/BLI_kdopbvh.c
@@ -94,7 +94,7 @@ typedef struct BVHNode {
struct BVHTree {
BVHNode **nodes;
BVHNode *nodearray; /* pre-alloc branch nodes */
- BVHNode **nodechild; /* pre-alloc childs for nodes */
+ BVHNode **nodechild; /* pre-alloc children for nodes */
float *nodebv; /* pre-alloc bounding-volumes for nodes */
float epsilon; /* epslion is used for inflation of the k-dop */
int totleaf; /* leafs */
@@ -169,6 +169,12 @@ typedef struct BVHNearestProjectedData {
} BVHNearestProjectedData;
+typedef struct BVHIntersectPlaneData {
+ const BVHTree *tree;
+ float plane[4];
+ struct BLI_Stack *intersect; /* Store indexes. */
+} BVHIntersectPlaneData;
+
/** \} */
/**
@@ -769,7 +775,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 +783,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
@@ -1032,12 +1038,14 @@ bool BLI_bvhtree_update_node(
return true;
}
-/* call BLI_bvhtree_update_node() first for every node/point/triangle */
+/**
+ * Call #BLI_bvhtree_update_node() first for every node/point/triangle.
+ */
void BLI_bvhtree_update_tree(BVHTree *tree)
{
/* Update bottom=>top
- * TRICKY: the way we build the tree all the childs have an index greater than the parent
- * This allows us todo a bottom up update by starting on the bigger numbered branch */
+ * TRICKY: the way we build the tree all the children have an index greater than the parent
+ * This allows us todo a bottom up update by starting on the bigger numbered branch. */
BVHNode **root = tree->nodes + tree->totleaf;
BVHNode **index = tree->nodes + tree->totleaf + tree->totbranch - 1;
@@ -1391,6 +1399,71 @@ BVHTreeOverlap *BLI_bvhtree_overlap(
/** \} */
/* -------------------------------------------------------------------- */
+/** \name BLI_bvhtree_intersect_plane
+ * \{ */
+
+static bool tree_intersect_plane_test(const float *bv, const float plane[4])
+{
+ /* TODO(germano): Support other kdop geometries. */
+ const float bb_min[3] = {bv[0], bv[2], bv[4]};
+ const float bb_max[3] = {bv[1], bv[3], bv[5]};
+ float bb_near[3], bb_far[3];
+ aabb_get_near_far_from_plane(plane, bb_min, bb_max, bb_near, bb_far);
+ if ((plane_point_side_v3(plane, bb_near) > 0.0f) !=
+ (plane_point_side_v3(plane, bb_far) > 0.0f)) {
+ return true;
+ }
+
+ return false;
+}
+
+static void bvhtree_intersect_plane_dfs_recursive(BVHIntersectPlaneData *__restrict data,
+ const BVHNode *node)
+{
+ if (tree_intersect_plane_test(node->bv, data->plane)) {
+ /* check if node is a leaf */
+ if (!node->totnode) {
+ int *intersect = BLI_stack_push_r(data->intersect);
+ *intersect = node->index;
+ }
+ else {
+ for (int j = 0; j < data->tree->tree_type; j++) {
+ if (node->children[j]) {
+ bvhtree_intersect_plane_dfs_recursive(data, node->children[j]);
+ }
+ }
+ }
+ }
+}
+
+int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_tot)
+{
+ int *intersect = NULL;
+ size_t total = 0;
+
+ if (tree->totleaf) {
+ BVHIntersectPlaneData data;
+ data.tree = tree;
+ copy_v4_v4(data.plane, plane);
+ data.intersect = BLI_stack_new(sizeof(int), __func__);
+
+ BVHNode *root = tree->nodes[tree->totleaf];
+ bvhtree_intersect_plane_dfs_recursive(&data, root);
+
+ total = BLI_stack_count(data.intersect);
+ if (total) {
+ intersect = MEM_mallocN(sizeof(int) * total, __func__);
+ BLI_stack_pop_n(data.intersect, intersect, (uint)total);
+ }
+ BLI_stack_free(data.intersect);
+ }
+ *r_intersect_tot = (uint)total;
+ return intersect;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name BLI_bvhtree_find_nearest
* \{ */
@@ -2309,7 +2382,7 @@ static bool bvhtree_walk_dfs_recursive(BVHTree_WalkData *walk_data, const BVHNod
}
/**
- * This is a generic function to perform a depth first search on the BVHTree
+ * This is a generic function to perform a depth first search on the #BVHTree
* where the search order and nodes traversed depend on callbacks passed in.
*
* \param tree: Tree to walk.
@@ -2317,7 +2390,7 @@ static bool bvhtree_walk_dfs_recursive(BVHTree_WalkData *walk_data, const BVHNod
* \param walk_leaf_cb: Callback to test leaf nodes, callback must store its own result,
* returning false exits early.
* \param walk_order_cb: Callback that indicates which direction to search,
- * either from the node with the lower or higher k-dop axis value.
+ * either from the node with the lower or higher K-DOP axis value.
* \param userdata: Argument passed to all callbacks.
*/
void BLI_bvhtree_walk_dfs(BVHTree *tree,
diff --git a/source/blender/blenlib/intern/BLI_linklist.c b/source/blender/blenlib/intern/BLI_linklist.c
index 5020e06d0b3..dc5d20ece99 100644
--- a/source/blender/blenlib/intern/BLI_linklist.c
+++ b/source/blender/blenlib/intern/BLI_linklist.c
@@ -74,6 +74,16 @@ LinkNode *BLI_linklist_find(LinkNode *list, int index)
return NULL;
}
+LinkNode *BLI_linklist_find_last(LinkNode *list)
+{
+ if (list) {
+ while (list->next) {
+ list = list->next;
+ }
+ }
+ return list;
+}
+
void BLI_linklist_reverse(LinkNode **listp)
{
LinkNode *rhead = NULL, *cur = *listp;
diff --git a/source/blender/blenlib/intern/bitmap.c b/source/blender/blenlib/intern/bitmap.c
index d24047397fb..54edcaec2c8 100644
--- a/source/blender/blenlib/intern/bitmap.c
+++ b/source/blender/blenlib/intern/bitmap.c
@@ -20,7 +20,7 @@
/** \file
* \ingroup bli
*
- * Utility functions for variable size bitmasks.
+ * Utility functions for variable size bit-masks.
*/
#include <limits.h>
diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c
index 4e0cd3a78dc..5f663dcb2e1 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 .
* / \ / \
@@ -951,7 +952,7 @@ static void initial_triangulation(CDT_state *cdt)
}
#endif
- /* Now dedup according to user-defined epsilon.
+ /* Now de-duplicate according to user-defined epsilon.
* We will merge a vertex into an earlier-indexed vertex
* that is within epsilon (Euclidean distance).
* Merges may cascade. So we may end up merging two things
@@ -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,
@@ -4317,7 +4320,7 @@ static void exactinit(void)
*/
static int fast_expansion_sum_zeroelim(
- int elen, double *e, int flen, double *f, double *h) /* h cannot be e or f. */
+ int elen, const double *e, int flen, const double *f, double *h) /* h cannot be e or f. */
{
double Q;
INEXACT double Qnew;
@@ -4402,7 +4405,7 @@ static int fast_expansion_sum_zeroelim(
*/
static int scale_expansion_zeroelim(int elen,
- double *e,
+ const double *e,
double b,
double *h) /* e and h cannot be the same. */
{
@@ -4448,7 +4451,7 @@ static int scale_expansion_zeroelim(int elen,
* See either version of my paper for details.
*/
-static double estimate(int elen, double *e)
+static double estimate(int elen, const double *e)
{
double Q;
int eindex;
diff --git a/source/blender/blenlib/intern/dot_export.cc b/source/blender/blenlib/intern/dot_export.cc
index a2cf843c473..48b6dc826d0 100644
--- a/source/blender/blenlib/intern/dot_export.cc
+++ b/source/blender/blenlib/intern/dot_export.cc
@@ -18,8 +18,7 @@
#include "BLI_dot_export.hh"
-namespace blender {
-namespace DotExport {
+namespace blender::dot {
/* Graph Building
************************************************/
@@ -27,8 +26,8 @@ namespace DotExport {
Node &Graph::new_node(StringRef label)
{
Node *node = new Node(*this);
- m_nodes.append(std::unique_ptr<Node>(node));
- m_top_level_nodes.add_new(node);
+ nodes_.append(std::unique_ptr<Node>(node));
+ top_level_nodes_.add_new(node);
node->set_attribute("label", label);
return *node;
}
@@ -36,8 +35,8 @@ Node &Graph::new_node(StringRef label)
Cluster &Graph::new_cluster(StringRef label)
{
Cluster *cluster = new Cluster(*this);
- m_clusters.append(std::unique_ptr<Cluster>(cluster));
- m_top_level_clusters.add_new(cluster);
+ clusters_.append(std::unique_ptr<Cluster>(cluster));
+ top_level_clusters_.add_new(cluster);
cluster->set_attribute("label", label);
return *cluster;
}
@@ -45,55 +44,55 @@ Cluster &Graph::new_cluster(StringRef label)
UndirectedEdge &UndirectedGraph::new_edge(NodePort a, NodePort b)
{
UndirectedEdge *edge = new UndirectedEdge(a, b);
- m_edges.append(std::unique_ptr<UndirectedEdge>(edge));
+ edges_.append(std::unique_ptr<UndirectedEdge>(edge));
return *edge;
}
DirectedEdge &DirectedGraph::new_edge(NodePort from, NodePort to)
{
DirectedEdge *edge = new DirectedEdge(from, to);
- m_edges.append(std::unique_ptr<DirectedEdge>(edge));
+ edges_.append(std::unique_ptr<DirectedEdge>(edge));
return *edge;
}
void Cluster::set_parent_cluster(Cluster *new_parent)
{
- if (m_parent == new_parent) {
+ if (parent_ == new_parent) {
return;
}
- else if (m_parent == nullptr) {
- m_graph.m_top_level_clusters.remove(this);
- new_parent->m_children.add_new(this);
+ else if (parent_ == nullptr) {
+ graph_.top_level_clusters_.remove(this);
+ new_parent->children_.add_new(this);
}
else if (new_parent == nullptr) {
- m_parent->m_children.remove(this);
- m_graph.m_top_level_clusters.add_new(this);
+ parent_->children_.remove(this);
+ graph_.top_level_clusters_.add_new(this);
}
else {
- m_parent->m_children.remove(this);
- new_parent->m_children.add_new(this);
+ parent_->children_.remove(this);
+ new_parent->children_.add_new(this);
}
- m_parent = new_parent;
+ parent_ = new_parent;
}
void Node::set_parent_cluster(Cluster *cluster)
{
- if (m_cluster == cluster) {
+ if (cluster_ == cluster) {
return;
}
- else if (m_cluster == nullptr) {
- m_graph.m_top_level_nodes.remove(this);
- cluster->m_nodes.add_new(this);
+ else if (cluster_ == nullptr) {
+ graph_.top_level_nodes_.remove(this);
+ cluster->nodes_.add_new(this);
}
else if (cluster == nullptr) {
- m_cluster->m_nodes.remove(this);
- m_graph.m_top_level_nodes.add_new(this);
+ cluster_->nodes_.remove(this);
+ graph_.top_level_nodes_.add_new(this);
}
else {
- m_cluster->m_nodes.remove(this);
- cluster->m_nodes.add_new(this);
+ cluster_->nodes_.remove(this);
+ cluster->nodes_.add_new(this);
}
- m_cluster = cluster;
+ cluster_ = cluster;
}
/* Utility methods
@@ -101,7 +100,7 @@ void Node::set_parent_cluster(Cluster *cluster)
void Graph::set_random_cluster_bgcolors()
{
- for (Cluster *cluster : m_top_level_clusters) {
+ for (Cluster *cluster : top_level_clusters_) {
cluster->set_random_cluster_bgcolors();
}
}
@@ -113,7 +112,7 @@ void Cluster::set_random_cluster_bgcolors()
float value = 0.8f;
this->set_attribute("bgcolor", color_attr_from_hsv(hue, staturation, value));
- for (Cluster *cluster : m_children) {
+ for (Cluster *cluster : children_) {
cluster->set_random_cluster_bgcolors();
}
}
@@ -128,7 +127,7 @@ std::string DirectedGraph::to_dot_string() const
this->export__declare_nodes_and_clusters(ss);
ss << "\n";
- for (const std::unique_ptr<DirectedEdge> &edge : m_edges) {
+ for (const std::unique_ptr<DirectedEdge> &edge : edges_) {
edge->export__as_edge_statement(ss);
ss << "\n";
}
@@ -144,7 +143,7 @@ std::string UndirectedGraph::to_dot_string() const
this->export__declare_nodes_and_clusters(ss);
ss << "\n";
- for (const std::unique_ptr<UndirectedEdge> &edge : m_edges) {
+ for (const std::unique_ptr<UndirectedEdge> &edge : edges_) {
edge->export__as_edge_statement(ss);
ss << "\n";
}
@@ -156,14 +155,14 @@ std::string UndirectedGraph::to_dot_string() const
void Graph::export__declare_nodes_and_clusters(std::stringstream &ss) const
{
ss << "graph ";
- m_attributes.export__as_bracket_list(ss);
+ attributes_.export__as_bracket_list(ss);
ss << "\n\n";
- for (Node *node : m_top_level_nodes) {
+ for (Node *node : top_level_nodes_) {
node->export__as_declaration(ss);
}
- for (Cluster *cluster : m_top_level_clusters) {
+ for (Cluster *cluster : top_level_clusters_) {
cluster->export__declare_nodes_and_clusters(ss);
}
}
@@ -173,14 +172,14 @@ void Cluster::export__declare_nodes_and_clusters(std::stringstream &ss) const
ss << "subgraph cluster_" << (uintptr_t)this << " {\n";
ss << "graph ";
- m_attributes.export__as_bracket_list(ss);
+ attributes_.export__as_bracket_list(ss);
ss << "\n\n";
- for (Node *node : m_nodes) {
+ for (Node *node : nodes_) {
node->export__as_declaration(ss);
}
- for (Cluster *cluster : m_children) {
+ for (Cluster *cluster : children_) {
cluster->export__declare_nodes_and_clusters(ss);
}
@@ -189,26 +188,26 @@ void Cluster::export__declare_nodes_and_clusters(std::stringstream &ss) const
void DirectedEdge::export__as_edge_statement(std::stringstream &ss) const
{
- m_a.to_dot_string(ss);
+ a_.to_dot_string(ss);
ss << " -> ";
- m_b.to_dot_string(ss);
+ b_.to_dot_string(ss);
ss << " ";
- m_attributes.export__as_bracket_list(ss);
+ attributes_.export__as_bracket_list(ss);
}
void UndirectedEdge::export__as_edge_statement(std::stringstream &ss) const
{
- m_a.to_dot_string(ss);
+ a_.to_dot_string(ss);
ss << " -- ";
- m_b.to_dot_string(ss);
+ b_.to_dot_string(ss);
ss << " ";
- m_attributes.export__as_bracket_list(ss);
+ attributes_.export__as_bracket_list(ss);
}
void AttributeList::export__as_bracket_list(std::stringstream &ss) const
{
ss << "[";
- m_attributes.foreach_item([&](StringRef key, StringRef value) {
+ attributes_.foreach_item([&](StringRef key, StringRef value) {
if (StringRef(value).startswith("<")) {
/* Don't draw the quotes, this is an html-like value. */
ss << key << "=" << value << ", ";
@@ -229,15 +228,15 @@ void Node::export__as_declaration(std::stringstream &ss) const
{
this->export__as_id(ss);
ss << " ";
- m_attributes.export__as_bracket_list(ss);
+ attributes_.export__as_bracket_list(ss);
ss << "\n";
}
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();
+ node_->export__as_id(ss);
+ if (port_name_.has_value()) {
+ ss << ":" << *port_name_;
}
}
@@ -252,7 +251,7 @@ NodeWithSocketsRef::NodeWithSocketsRef(Node &node,
StringRef name,
Span<std::string> input_names,
Span<std::string> output_names)
- : m_node(&node)
+ : node_(&node)
{
std::stringstream ss;
@@ -264,8 +263,8 @@ NodeWithSocketsRef::NodeWithSocketsRef(Node &node,
ss << "</b></td></tr>";
/* Sockets */
- uint socket_max_amount = std::max(input_names.size(), output_names.size());
- for (uint i = 0; i < socket_max_amount; i++) {
+ int socket_max_amount = std::max(input_names.size(), output_names.size());
+ for (int i = 0; i < socket_max_amount; i++) {
ss << "<tr>";
if (i < input_names.size()) {
StringRef name = input_names[i];
@@ -297,9 +296,8 @@ NodeWithSocketsRef::NodeWithSocketsRef(Node &node,
ss << "</table>>";
- m_node->set_attribute("label", ss.str());
- m_node->set_shape(Attr_shape::Rectangle);
+ node_->set_attribute("label", ss.str());
+ node_->set_shape(Attr_shape::Rectangle);
}
-} // namespace DotExport
-} // namespace blender
+} // namespace blender::dot
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/expr_pylike_eval.c b/source/blender/blenlib/intern/expr_pylike_eval.c
index d1d84dab3f7..f8618c54ea4 100644
--- a/source/blender/blenlib/intern/expr_pylike_eval.c
+++ b/source/blender/blenlib/intern/expr_pylike_eval.c
@@ -72,6 +72,8 @@ typedef enum eOpCode {
OPCODE_FUNC1,
/* 2 argument function call: (a b -> func2(a,b)) */
OPCODE_FUNC2,
+ /* 3 argument function call: (a b c -> func3(a,b,c)) */
+ OPCODE_FUNC3,
/* Parameter access: (-> params[ival]) */
OPCODE_PARAMETER,
/* Minimum of multiple inputs: (a b c... -> min); ival = arg count */
@@ -92,6 +94,7 @@ typedef enum eOpCode {
typedef double (*UnaryOpFunc)(double);
typedef double (*BinaryOpFunc)(double, double);
+typedef double (*TernaryOpFunc)(double, double, double);
typedef struct ExprOp {
eOpCode opcode;
@@ -104,6 +107,7 @@ typedef struct ExprOp {
void *ptr;
UnaryOpFunc func1;
BinaryOpFunc func2;
+ TernaryOpFunc func3;
} arg;
} ExprOp;
@@ -216,6 +220,11 @@ eExprPyLike_EvalStatus BLI_expr_pylike_eval(ExprPyLike_Parsed *expr,
stack[sp - 2] = ops[pc].arg.func2(stack[sp - 2], stack[sp - 1]);
sp--;
break;
+ case OPCODE_FUNC3:
+ FAIL_IF(sp < 3);
+ stack[sp - 3] = ops[pc].arg.func3(stack[sp - 3], stack[sp - 2], stack[sp - 1]);
+ sp -= 2;
+ break;
case OPCODE_MIN:
FAIL_IF(sp < ops[pc].arg.ival);
for (int j = 1; j < ops[pc].arg.ival; j++, sp--) {
@@ -326,6 +335,35 @@ static double op_degrees(double arg)
return arg * 180.0 / M_PI;
}
+static double op_log2(double a, double b)
+{
+ return log(a) / log(b);
+}
+
+static double op_lerp(double a, double b, double x)
+{
+ return a * (1.0 - x) + b * x;
+}
+
+static double op_clamp(double arg)
+{
+ CLAMP(arg, 0.0, 1.0);
+ return arg;
+}
+
+static double op_clamp3(double arg, double minv, double maxv)
+{
+ CLAMP(arg, minv, maxv);
+ return arg;
+}
+
+static double op_smoothstep(double a, double b, double x)
+{
+ double t = (x - a) / (b - a);
+ CLAMP(t, 0.0, 1.0);
+ return t * t * (3.0 - 2.0 * t);
+}
+
static double op_not(double a)
{
return a ? 0.0 : 1.0;
@@ -390,6 +428,7 @@ static BuiltinOpDef builtin_ops[] = {
{"floor", OPCODE_FUNC1, floor},
{"ceil", OPCODE_FUNC1, ceil},
{"trunc", OPCODE_FUNC1, trunc},
+ {"round", OPCODE_FUNC1, round},
{"int", OPCODE_FUNC1, trunc},
{"sin", OPCODE_FUNC1, sin},
{"cos", OPCODE_FUNC1, cos},
@@ -400,9 +439,14 @@ static BuiltinOpDef builtin_ops[] = {
{"atan2", OPCODE_FUNC2, atan2},
{"exp", OPCODE_FUNC1, exp},
{"log", OPCODE_FUNC1, log},
+ {"log", OPCODE_FUNC2, op_log2},
{"sqrt", OPCODE_FUNC1, sqrt},
{"pow", OPCODE_FUNC2, pow},
{"fmod", OPCODE_FUNC2, fmod},
+ {"lerp", OPCODE_FUNC3, op_lerp},
+ {"clamp", OPCODE_FUNC1, op_clamp},
+ {"clamp", OPCODE_FUNC3, op_clamp3},
+ {"smoothstep", OPCODE_FUNC3, op_smoothstep},
{NULL, OPCODE_CONST, NULL},
};
@@ -514,6 +558,22 @@ static void parse_set_jump(ExprParseState *state, int jump)
state->ops[jump - 1].jmp_offset = state->ops_count - jump;
}
+/* Returns the required argument count of the given function call code. */
+static int opcode_arg_count(eOpCode code)
+{
+ switch (code) {
+ case OPCODE_FUNC1:
+ return 1;
+ case OPCODE_FUNC2:
+ return 2;
+ case OPCODE_FUNC3:
+ return 3;
+ default:
+ BLI_assert(!"unexpected opcode");
+ return -1;
+ }
+}
+
/* Add a function call operation, applying constant folding when possible. */
static bool parse_add_func(ExprParseState *state, eOpCode code, int args, void *funcptr)
{
@@ -560,6 +620,27 @@ static bool parse_add_func(ExprParseState *state, eOpCode code, int args, void *
}
break;
+ case OPCODE_FUNC3:
+ CHECK_ERROR(args == 3);
+
+ if (jmp_gap >= 3 && prev_ops[-3].opcode == OPCODE_CONST &&
+ prev_ops[-2].opcode == OPCODE_CONST && prev_ops[-1].opcode == OPCODE_CONST) {
+ TernaryOpFunc func = funcptr;
+
+ /* volatile because some compilers overly aggressive optimize this call out.
+ * see D6012 for details. */
+ volatile double result = func(
+ prev_ops[-3].arg.dval, prev_ops[-2].arg.dval, prev_ops[-1].arg.dval);
+
+ if (fetestexcept(FE_DIVBYZERO | FE_INVALID) == 0) {
+ prev_ops[-3].arg.dval = result;
+ state->ops_count -= 2;
+ state->stack_ptr -= 2;
+ return true;
+ }
+ }
+ break;
+
default:
BLI_assert(false);
return false;
@@ -755,6 +836,17 @@ static bool parse_unary(ExprParseState *state)
if (STREQ(state->tokenbuf, builtin_ops[i].name)) {
int args = parse_function_args(state);
+ /* Search for other arg count versions if necessary. */
+ if (args != opcode_arg_count(builtin_ops[i].op)) {
+ for (int j = i + 1; builtin_ops[j].name; j++) {
+ if (opcode_arg_count(builtin_ops[j].op) == args &&
+ STREQ(builtin_ops[j].name, builtin_ops[i].name)) {
+ i = j;
+ break;
+ }
+ }
+ }
+
return parse_add_func(state, builtin_ops[i].op, args, builtin_ops[i].funcptr);
}
}
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index b9133edcae3..e61cbd318fc 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -177,8 +177,9 @@ size_t BLI_gzip_mem_to_file_at_pos(
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm, compression_level);
- if (ret != Z_OK)
+ if (ret != Z_OK) {
return 0;
+ }
strm.avail_in = len;
strm.next_in = (Bytef *)buf;
@@ -224,8 +225,9 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
- if (ret != Z_OK)
+ if (ret != Z_OK) {
return 0;
+ }
do {
strm.avail_in = fread(in, 1, chunk, file);
@@ -558,7 +560,7 @@ int BLI_move(const char *file, const char *to)
/* windows doesn't support moving to a directory
* it has to be 'mv filename filename' and not
- * 'mv filename destdir' */
+ * 'mv filename destination_directory' */
BLI_strncpy(str, to, sizeof(str));
/* points 'to' to a directory ? */
diff --git a/source/blender/blenlib/intern/math_base_safe_inline.c b/source/blender/blenlib/intern/math_base_safe_inline.c
new file mode 100644
index 00000000000..5600382e395
--- /dev/null
+++ b/source/blender/blenlib/intern/math_base_safe_inline.c
@@ -0,0 +1,79 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __MATH_BASE_SAFE_INLINE_C__
+#define __MATH_BASE_SAFE_INLINE_C__
+
+#include "BLI_math_base_safe.h"
+#include "BLI_utildefines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MINLINE float safe_divide(float a, float b)
+{
+ return (b != 0.0f) ? a / b : 0.0f;
+}
+
+MINLINE float safe_modf(float a, float b)
+{
+ return (b != 0.0f) ? fmodf(a, b) : 0.0f;
+}
+
+MINLINE float safe_logf(float a, float base)
+{
+ if (UNLIKELY(a <= 0.0f || base <= 0.0f)) {
+ return 0.0f;
+ }
+ return safe_divide(logf(a), logf(base));
+}
+
+MINLINE float safe_sqrtf(float a)
+{
+ return sqrtf(MAX2(a, 0.0f));
+}
+
+MINLINE float safe_inverse_sqrtf(float a)
+{
+ return (a > 0.0f) ? 1.0f / sqrtf(a) : 0.0f;
+}
+
+MINLINE float safe_asinf(float a)
+{
+ CLAMP(a, -1.0f, 1.0f);
+ return asinf(a);
+}
+
+MINLINE float safe_acosf(float a)
+{
+ CLAMP(a, -1.0f, 1.0f);
+ return acosf(a);
+}
+
+MINLINE float safe_powf(float base, float exponent)
+{
+ if (UNLIKELY(base < 0.0f && exponent != (int)exponent)) {
+ return 0.0f;
+ }
+ return powf(base, exponent);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MATH_BASE_SAFE_INLINE_C__ */
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_color.c b/source/blender/blenlib/intern/math_color.c
index 625849c01df..651a062e3d5 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -503,8 +503,12 @@ int constrain_rgb(float *r, float *g, float *b)
/* ********************** lift/gamma/gain / ASC-CDL conversion ********************************* */
-void lift_gamma_gain_to_asc_cdl(
- float *lift, float *gamma, float *gain, float *offset, float *slope, float *power)
+void lift_gamma_gain_to_asc_cdl(const float *lift,
+ const float *gamma,
+ const float *gain,
+ float *offset,
+ float *slope,
+ float *power)
{
int c;
for (c = 0; c < 3; c++) {
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index e7c1fc8c2d9..937bf8b1ae6 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -630,7 +630,7 @@ float dist_squared_ray_to_seg_v3(const float ray_origin[3],
float *r_depth)
{
float lambda, depth;
- if (isect_ray_seg_v3(ray_origin, ray_direction, v0, v1, &lambda)) {
+ if (isect_ray_line_v3(ray_origin, ray_direction, v0, v1, &lambda)) {
if (lambda <= 0.0f) {
copy_v3_v3(r_point, v0);
}
@@ -2129,11 +2129,11 @@ bool isect_ray_seg_v2(const float ray_origin[2],
return false;
}
-bool isect_ray_seg_v3(const float ray_origin[3],
- const float ray_direction[3],
- const float v0[3],
- const float v1[3],
- float *r_lambda)
+bool isect_ray_line_v3(const float ray_origin[3],
+ const float ray_direction[3],
+ const float v0[3],
+ const float v1[3],
+ float *r_lambda)
{
float a[3], t[3], n[3];
sub_v3_v3v3(a, v1, v0);
@@ -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])
@@ -4168,8 +4176,8 @@ int interp_sparse_array(float *array, const int list_size, const float skipval)
#define DIR_V2_SET(d_len, va, vb) \
{ \
- sub_v2_v2v2((d_len)->dir, va, vb); \
- (d_len)->len = len_v2((d_len)->dir); \
+ sub_v2db_v2fl_v2fl((d_len)->dir, va, vb); \
+ (d_len)->len = len_v2_db((d_len)->dir); \
} \
(void)0
@@ -4177,8 +4185,8 @@ struct Float3_Len {
float dir[3], len;
};
-struct Float2_Len {
- float dir[2], len;
+struct Double2_Len {
+ double dir[2], len;
};
/* Mean value weights - smooth interpolation weights for polygons with
@@ -4201,21 +4209,30 @@ static float mean_value_half_tan_v3(const struct Float3_Len *d_curr,
return 0.0f;
}
-static float mean_value_half_tan_v2(const struct Float2_Len *d_curr,
- const struct Float2_Len *d_next)
+/**
+ * Mean value weights - same as #mean_value_half_tan_v3 but for 2D vectors.
+ *
+ * \note When interpolating a 2D polygon, a point can be considered "outside"
+ * the polygon's bounds. Thus, when the point is very distant and the vectors
+ * have relatively close values, the precision problems are evident since they
+ * do not indicate a point "inside" the polygon.
+ * To resolve this, doubles are used.
+ */
+static double mean_value_half_tan_v2_db(const struct Double2_Len *d_curr,
+ const struct Double2_Len *d_next)
{
- /* different from the 3d version but still correct */
- const float area = cross_v2v2(d_curr->dir, d_next->dir);
+ /* Different from the 3d version but still correct. */
+ const double area = cross_v2v2_db(d_curr->dir, d_next->dir);
/* Compare against zero since 'FLT_EPSILON' can be too large, see: T73348. */
- if (LIKELY(area != 0.0f)) {
- const float dot = dot_v2v2(d_curr->dir, d_next->dir);
- const float len = d_curr->len * d_next->len;
- const float result = (len - dot) / area;
+ if (LIKELY(area != 0.0)) {
+ const double dot = dot_v2v2_db(d_curr->dir, d_next->dir);
+ const double len = d_curr->len * d_next->len;
+ const double result = (len - dot) / area;
if (isfinite(result)) {
return result;
}
}
- return 0.0f;
+ return 0.0;
}
void interp_weights_poly_v3(float *w, float v[][3], const int n, const float co[3])
@@ -4257,7 +4274,7 @@ void interp_weights_poly_v3(float *w, float v[][3], const int n, const float co[
* to borders of face.
* In that case, do simple linear interpolation between the two edge vertices */
- /* 'd_next.len' is infact 'd_curr.len', just avoid copy to begin with */
+ /* 'd_next.len' is in fact 'd_curr.len', just avoid copy to begin with */
if (UNLIKELY(d_next.len < eps)) {
ix_flag = IS_POINT_IX;
break;
@@ -4320,11 +4337,11 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[
const float eps_sq = eps * eps;
const float *v_curr, *v_next;
- float ht_prev, ht; /* half tangents */
+ double ht_prev, ht; /* half tangents */
float totweight = 0.0f;
int i_curr, i_next;
char ix_flag = 0;
- struct Float2_Len d_curr, d_next;
+ struct Double2_Len d_curr, d_next;
/* loop over 'i_next' */
i_curr = n - 1;
@@ -4335,14 +4352,14 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[
DIR_V2_SET(&d_curr, v_curr - 2 /* v[n - 2] */, co);
DIR_V2_SET(&d_next, v_curr /* v[n - 1] */, co);
- ht_prev = mean_value_half_tan_v2(&d_curr, &d_next);
+ ht_prev = mean_value_half_tan_v2_db(&d_curr, &d_next);
while (i_next < n) {
/* Mark Mayer et al algorithm that is used here does not operate well if vertex is close
* to borders of face. In that case,
* do simple linear interpolation between the two edge vertices */
- /* 'd_next.len' is infact 'd_curr.len', just avoid copy to begin with */
+ /* 'd_next.len' is in fact 'd_curr.len', just avoid copy to begin with */
if (UNLIKELY(d_next.len < eps)) {
ix_flag = IS_POINT_IX;
break;
@@ -4354,8 +4371,8 @@ void interp_weights_poly_v2(float *w, float v[][2], const int n, const float co[
d_curr = d_next;
DIR_V2_SET(&d_next, v_next, co);
- ht = mean_value_half_tan_v2(&d_curr, &d_next);
- w[i_curr] = (ht_prev + ht) / d_curr.len;
+ ht = mean_value_half_tan_v2_db(&d_curr, &d_next);
+ w[i_curr] = (float)((ht_prev + ht) / d_curr.len);
totweight += w[i_curr];
/* step */
@@ -4872,6 +4889,37 @@ void projmat_dimensions(const float projmat[4][4],
}
}
+void projmat_dimensions_db(const float projmat_fl[4][4],
+ double *r_left,
+ double *r_right,
+ double *r_bottom,
+ double *r_top,
+ double *r_near,
+ double *r_far)
+{
+ double projmat[4][4];
+ copy_m4d_m4(projmat, projmat_fl);
+
+ bool is_persp = projmat[3][3] == 0.0f;
+
+ if (is_persp) {
+ *r_left = (projmat[2][0] - 1.0) / projmat[0][0];
+ *r_right = (projmat[2][0] + 1.0) / projmat[0][0];
+ *r_bottom = (projmat[2][1] - 1.0) / projmat[1][1];
+ *r_top = (projmat[2][1] + 1.0) / projmat[1][1];
+ *r_near = projmat[3][2] / (projmat[2][2] - 1.0);
+ *r_far = projmat[3][2] / (projmat[2][2] + 1.0);
+ }
+ else {
+ *r_left = (-projmat[3][0] - 1.0) / projmat[0][0];
+ *r_right = (-projmat[3][0] + 1.0) / projmat[0][0];
+ *r_bottom = (-projmat[3][1] - 1.0) / projmat[1][1];
+ *r_top = (-projmat[3][1] + 1.0) / projmat[1][1];
+ *r_near = (projmat[3][2] + 1.0) / projmat[2][2];
+ *r_far = (projmat[3][2] - 1.0) / projmat[2][2];
+ }
+}
+
/**
* Creates a projection matrix for a small region of the viewport.
*
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 9e398239bc7..fadd7d83444 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -248,7 +248,7 @@ void swap_m4m4(float m1[4][4], float m2[4][4])
}
}
-void shuffle_m4(float R[4][4], int index[4])
+void shuffle_m4(float R[4][4], const int index[4])
{
zero_m4(R);
for (int k = 0; k < 4; k++) {
@@ -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/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index 6ec7c960d6b..7f1840228e2 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -307,7 +307,7 @@ void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], const uint nbr)
/**
* Specialized function for calculating normals.
- * fastpath for:
+ * Fast-path for:
*
* \code{.c}
* add_v3_v3v3(r, a, b);
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index ca405907bdd..1b47832589e 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -509,6 +509,12 @@ MINLINE void sub_v3_v3v3_db(double r[3], const double a[3], const double b[3])
r[2] = a[2] - b[2];
}
+MINLINE void sub_v2db_v2fl_v2fl(double r[2], const float a[2], const float b[2])
+{
+ r[0] = (double)a[0] - (double)b[0];
+ r[1] = (double)a[1] - (double)b[1];
+}
+
MINLINE void sub_v3db_v3fl_v3fl(double r[3], const float a[3], const float b[3])
{
r[0] = (double)a[0] - (double)b[0];
@@ -917,6 +923,11 @@ MINLINE float cross_v2v2(const float a[2], const float b[2])
return a[0] * b[1] - a[1] * b[0];
}
+MINLINE double cross_v2v2_db(const double a[2], const double b[2])
+{
+ return a[0] * b[1] - a[1] * b[0];
+}
+
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
{
BLI_assert(r != a && r != b);
@@ -997,6 +1008,11 @@ MINLINE float len_v2(const float v[2])
return sqrtf(v[0] * v[0] + v[1] * v[1]);
}
+MINLINE double len_v2_db(const double v[2])
+{
+ return sqrt(v[0] * v[0] + v[1] * v[1]);
+}
+
MINLINE float len_v2v2(const float v1[2], const float v2[2])
{
float x, y;
diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c
index 42b5ba28f5a..1ae1c91a3bd 100644
--- a/source/blender/blenlib/intern/noise.c
+++ b/source/blender/blenlib/intern/noise.c
@@ -27,7 +27,7 @@
#include "BLI_noise.h"
/* local */
-static float noise3_perlin(float vec[3]);
+static float noise3_perlin(const float vec[3]);
// static float turbulence_perlin(const float point[3], float lofreq, float hifreq);
// static float turbulencep(float noisesize, float x, float y, float z, int nr);
@@ -779,7 +779,7 @@ static const float g_perlin_data_v3[512 + 2][3] = {
} \
(void)0
-static float noise3_perlin(float vec[3])
+static float noise3_perlin(const float vec[3])
{
const char *p = g_perlin_data_ub;
const float(*g)[3] = g_perlin_data_v3;
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 2f51b66725b..d912cb8d464 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -242,7 +242,7 @@ void BLI_path_normalize(const char *relabase, char *path)
/* Note: previous version of following call used an offset of 3 instead of 4,
* which meant that the "/../home/me" example actually became "home/me".
- * Using offset of 3 gives behavior consistent with the abovementioned
+ * Using offset of 3 gives behavior consistent with the aforementioned
* Python routine. */
memmove(path, path + 3, strlen(path + 3) + 1);
}
diff --git a/source/blender/blenlib/intern/rand.c b/source/blender/blenlib/intern/rand.cc
index ab7a972e010..9bafc422db5 100644
--- a/source/blender/blenlib/intern/rand.c
+++ b/source/blender/blenlib/intern/rand.cc
@@ -30,6 +30,7 @@
#include "BLI_math.h"
#include "BLI_rand.h"
+#include "BLI_rand.hh"
#include "BLI_threads.h"
/* defines BLI_INLINE */
@@ -38,29 +39,22 @@
#include "BLI_strict_flags.h"
#include "BLI_sys_types.h"
-#define MULTIPLIER 0x5DEECE66Dll
-#define MASK 0x0000FFFFFFFFFFFFll
-#define MASK_BYTES 2
-
-#define ADDEND 0xB
-#define LOWSEED 0x330E
-
-extern unsigned char BLI_noise_hash_uchar_512[512]; /* noise.c */
+extern "C" unsigned char BLI_noise_hash_uchar_512[512]; /* noise.c */
#define hash BLI_noise_hash_uchar_512
/**
* Random Number Generator.
*/
struct RNG {
- uint64_t X;
+ blender::RandomNumberGenerator rng;
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("RNG")
};
RNG *BLI_rng_new(unsigned int seed)
{
- RNG *rng = MEM_mallocN(sizeof(*rng), "rng");
-
- BLI_rng_seed(rng, seed);
-
+ RNG *rng = new RNG();
+ rng->rng.seed(seed);
return rng;
}
@@ -69,26 +63,24 @@ RNG *BLI_rng_new(unsigned int seed)
*/
RNG *BLI_rng_new_srandom(unsigned int seed)
{
- RNG *rng = MEM_mallocN(sizeof(*rng), "rng");
-
- BLI_rng_srandom(rng, seed);
-
+ RNG *rng = new RNG();
+ rng->rng.seed_random(seed);
return rng;
}
RNG *BLI_rng_copy(RNG *rng)
{
- return MEM_dupallocN(rng);
+ return new RNG(*rng);
}
void BLI_rng_free(RNG *rng)
{
- MEM_freeN(rng);
+ delete rng;
}
void BLI_rng_seed(RNG *rng, unsigned int seed)
{
- rng->X = (((uint64_t)seed) << 16) | LOWSEED;
+ rng->rng.seed(seed);
}
/**
@@ -96,67 +88,22 @@ void BLI_rng_seed(RNG *rng, unsigned int seed)
*/
void BLI_rng_srandom(RNG *rng, unsigned int seed)
{
- BLI_rng_seed(rng, seed + hash[seed & 255]);
- seed = BLI_rng_get_uint(rng);
- BLI_rng_seed(rng, seed + hash[seed & 255]);
- seed = BLI_rng_get_uint(rng);
- BLI_rng_seed(rng, seed + hash[seed & 255]);
-}
-
-BLI_INLINE void rng_step(RNG *rng)
-{
- rng->X = (MULTIPLIER * rng->X + ADDEND) & MASK;
+ rng->rng.seed_random(seed);
}
void BLI_rng_get_char_n(RNG *rng, char *bytes, size_t bytes_len)
{
- size_t last_len = 0;
- size_t trim_len = bytes_len;
-
-#define RAND_STRIDE (sizeof(rng->X) - MASK_BYTES)
-
- if (trim_len > RAND_STRIDE) {
- last_len = trim_len % RAND_STRIDE;
- trim_len = trim_len - last_len;
- }
- else {
- trim_len = 0;
- last_len = bytes_len;
- }
-
- const char *data_src = (void *)&(rng->X);
- size_t i = 0;
- while (i != trim_len) {
- BLI_assert(i < trim_len);
-#ifdef __BIG_ENDIAN__
- for (size_t j = (RAND_STRIDE + MASK_BYTES) - 1; j != MASK_BYTES - 1; j--)
-#else
- for (size_t j = 0; j != RAND_STRIDE; j++)
-#endif
- {
- bytes[i++] = data_src[j];
- }
- rng_step(rng);
- }
- if (last_len) {
- for (size_t j = 0; j != last_len; j++) {
- bytes[i++] = data_src[j];
- }
- }
-
-#undef RAND_STRIDE
+ rng->rng.get_bytes(blender::MutableSpan(bytes, (int64_t)bytes_len));
}
int BLI_rng_get_int(RNG *rng)
{
- rng_step(rng);
- return (int)(rng->X >> 17);
+ return rng->rng.get_int32();
}
unsigned int BLI_rng_get_uint(RNG *rng)
{
- rng_step(rng);
- return (unsigned int)(rng->X >> 17);
+ return rng->rng.get_uint32();
}
/**
@@ -164,7 +111,7 @@ unsigned int BLI_rng_get_uint(RNG *rng)
*/
double BLI_rng_get_double(RNG *rng)
{
- return (double)BLI_rng_get_int(rng) / 0x80000000;
+ return rng->rng.get_double();
}
/**
@@ -172,29 +119,17 @@ double BLI_rng_get_double(RNG *rng)
*/
float BLI_rng_get_float(RNG *rng)
{
- return (float)BLI_rng_get_int(rng) / 0x80000000;
+ return rng->rng.get_float();
}
void BLI_rng_get_float_unit_v2(RNG *rng, float v[2])
{
- float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng);
- v[0] = cosf(a);
- v[1] = sinf(a);
+ copy_v2_v2(v, rng->rng.get_unit_float2());
}
void BLI_rng_get_float_unit_v3(RNG *rng, float v[3])
{
- float r;
- v[2] = (2.0f * BLI_rng_get_float(rng)) - 1.0f;
- if ((r = 1.0f - (v[2] * v[2])) > 0.0f) {
- float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng);
- r = sqrtf(r);
- v[0] = r * cosf(a);
- v[1] = r * sinf(a);
- }
- else {
- v[2] = 1.0f;
- }
+ copy_v3_v3(v, rng->rng.get_unit_float3());
}
/**
@@ -203,27 +138,12 @@ void BLI_rng_get_float_unit_v3(RNG *rng, float v[3])
void BLI_rng_get_tri_sample_float_v2(
RNG *rng, const float v1[2], const float v2[2], const float v3[2], float r_pt[2])
{
- float u = BLI_rng_get_float(rng);
- float v = BLI_rng_get_float(rng);
-
- float side_u[2], side_v[2];
-
- if ((u + v) > 1.0f) {
- u = 1.0f - u;
- v = 1.0f - v;
- }
-
- sub_v2_v2v2(side_u, v2, v1);
- sub_v2_v2v2(side_v, v3, v1);
-
- copy_v2_v2(r_pt, v1);
- madd_v2_v2fl(r_pt, side_u, u);
- madd_v2_v2fl(r_pt, side_v, v);
+ copy_v2_v2(r_pt, rng->rng.get_triangle_sample(v1, v2, v3));
}
void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsigned int elem_tot)
{
- const size_t elem_size = (size_t)elem_size_i;
+ const uint elem_size = elem_size_i;
unsigned int i = elem_tot;
void *temp;
@@ -254,9 +174,7 @@ void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsig
*/
void BLI_rng_skip(RNG *rng, int n)
{
- while (n--) {
- rng_step(rng);
- }
+ rng->rng.skip((uint)n);
}
/***/
@@ -326,7 +244,8 @@ struct RNG_THREAD_ARRAY {
RNG_THREAD_ARRAY *BLI_rng_threaded_new(void)
{
unsigned int i;
- RNG_THREAD_ARRAY *rngarr = MEM_mallocN(sizeof(RNG_THREAD_ARRAY), "random_array");
+ RNG_THREAD_ARRAY *rngarr = (RNG_THREAD_ARRAY *)MEM_mallocN(sizeof(RNG_THREAD_ARRAY),
+ "random_array");
for (i = 0; i < BLENDER_MAX_THREADS; i++) {
BLI_rng_srandom(&rngarr->rng_tab[i], (unsigned int)clock());
@@ -382,7 +301,7 @@ void BLI_halton_1d(unsigned int prime, double offset, int n, double *r)
}
}
-void BLI_halton_2d(unsigned int prime[2], double offset[2], int n, double *r)
+void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r)
{
const double invprimes[2] = {1.0 / (double)prime[0], 1.0 / (double)prime[1]};
@@ -395,7 +314,7 @@ void BLI_halton_2d(unsigned int prime[2], double offset[2], int n, double *r)
}
}
-void BLI_halton_3d(unsigned int prime[3], double offset[3], int n, double *r)
+void BLI_halton_3d(const unsigned int prime[3], double offset[3], int n, double *r)
{
const double invprimes[3] = {
1.0 / (double)prime[0], 1.0 / (double)prime[1], 1.0 / (double)prime[2]};
@@ -409,7 +328,7 @@ void BLI_halton_3d(unsigned int prime[3], double offset[3], int n, double *r)
}
}
-void BLI_halton_2d_sequence(unsigned int prime[2], double offset[2], int n, double *r)
+void BLI_halton_2d_sequence(const unsigned int prime[2], double offset[2], int n, double *r)
{
const double invprimes[2] = {1.0 / (double)prime[0], 1.0 / (double)prime[1]};
@@ -426,7 +345,7 @@ BLI_INLINE double radical_inverse(unsigned int n)
{
double u = 0;
- /* This reverse the bitwise representation
+ /* This reverse the bit-wise representation
* around the decimal point. */
for (double p = 0.5; n; p *= 0.5, n >>= 1) {
if (n & 1) {
@@ -449,3 +368,99 @@ void BLI_hammersley_2d_sequence(unsigned int n, double *r)
r[s * 2 + 1] = radical_inverse(s);
}
}
+
+namespace blender {
+
+/**
+ * Set a randomized hash of the value as seed.
+ */
+void RandomNumberGenerator::seed_random(uint32_t seed)
+{
+ this->seed(seed + hash[seed & 255]);
+ seed = this->get_uint32();
+ this->seed(seed + hash[seed & 255]);
+ seed = this->get_uint32();
+ this->seed(seed + hash[seed & 255]);
+}
+
+float2 RandomNumberGenerator::get_unit_float2()
+{
+ float a = (float)(M_PI * 2.0) * this->get_float();
+ return {cosf(a), sinf(a)};
+}
+
+float3 RandomNumberGenerator::get_unit_float3()
+{
+ float z = (2.0f * this->get_float()) - 1.0f;
+ float r = 1.0f - z * z;
+ if (r > 0.0f) {
+ float a = (float)(M_PI * 2.0) * this->get_float();
+ r = sqrtf(r);
+ float x = r * cosf(a);
+ float y = r * sinf(a);
+ return {x, y, z};
+ }
+ return {0.0f, 0.0f, 1.0f};
+}
+
+/**
+ * Generate a random point inside the given triangle.
+ */
+float2 RandomNumberGenerator::get_triangle_sample(float2 v1, float2 v2, float2 v3)
+{
+ float u = this->get_float();
+ float v = this->get_float();
+
+ if (u + v > 1.0f) {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ }
+
+ float2 side_u = v2 - v1;
+ float2 side_v = v3 - v1;
+
+ float2 sample = v1;
+ sample += side_u * u;
+ sample += side_v * v;
+ return sample;
+}
+
+void RandomNumberGenerator::get_bytes(MutableSpan<char> r_bytes)
+{
+ constexpr int64_t mask_bytes = 2;
+ constexpr int64_t rand_stride = (int64_t)sizeof(x_) - mask_bytes;
+
+ int64_t last_len = 0;
+ int64_t trim_len = r_bytes.size();
+
+ if (trim_len > rand_stride) {
+ last_len = trim_len % rand_stride;
+ trim_len = trim_len - last_len;
+ }
+ else {
+ trim_len = 0;
+ last_len = r_bytes.size();
+ }
+
+ const char *data_src = (const char *)&x_;
+ int64_t i = 0;
+ while (i != trim_len) {
+ BLI_assert(i < trim_len);
+#ifdef __BIG_ENDIAN__
+ for (int64_t j = (rand_stride + mask_bytes) - 1; j != mask_bytes - 1; j--)
+#else
+ for (int64_t j = 0; j != rand_stride; j++)
+#endif
+ {
+ r_bytes[i++] = data_src[j];
+ }
+ this->step();
+ }
+ if (last_len) {
+ for (int64_t j = 0; j != last_len; j++) {
+ r_bytes[i++] = data_src[j];
+ }
+ }
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c
index ad8443683f8..bf3c8730b01 100644
--- a/source/blender/blenlib/intern/rct.c
+++ b/source/blender/blenlib/intern/rct.c
@@ -635,6 +635,14 @@ void BLI_rcti_resize(rcti *rect, int x, int y)
rect->ymax = rect->ymin + y;
}
+void BLI_rcti_pad(rcti *rect, int pad_x, int pad_y)
+{
+ rect->xmin -= pad_x;
+ rect->ymin -= pad_y;
+ rect->xmax += pad_x;
+ rect->ymax += pad_y;
+}
+
void BLI_rctf_resize(rctf *rect, float x, float y)
{
rect->xmin = BLI_rctf_cent_x(rect) - (x * 0.5f);
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index fbfb258693b..d3191148c90 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -53,9 +53,9 @@
# include "BLI_string_utf8.h"
# include "BLI_winstuff.h"
# include "utfconv.h"
+# include <ShObjIdl.h>
# include <direct.h>
# include <io.h>
-# include <shobjidl_core.h>
# include <stdbool.h>
#else
# include <pwd.h>
@@ -275,25 +275,26 @@ eFileAttributes BLI_file_attributes(const char *path)
ret |= FILE_ATTR_REPARSE_POINT;
}
-# endif
+# else
-# ifdef __linux__
UNUSED_VARS(path);
/* TODO:
* If Immutable set FILE_ATTR_READONLY
* If Archived set FILE_ATTR_ARCHIVE
*/
-
# endif
-
return ret;
}
#endif
/* Return alias/shortcut file target. Apple version is defined in storage_apple.mm */
#ifndef __APPLE__
-bool BLI_file_alias_target(char target[FILE_MAXDIR], const char *filepath)
+bool BLI_file_alias_target(
+ /* This parameter can only be const on non-windows platforms.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ char target[FILE_MAXDIR],
+ const char *filepath)
{
# ifdef WIN32
if (!BLI_path_extension_check(filepath, ".lnk")) {
@@ -330,9 +331,7 @@ bool BLI_file_alias_target(char target[FILE_MAXDIR], const char *filepath)
}
return (success && target[0]);
-# endif
-
-# ifdef __linux__
+# else
UNUSED_VARS(target, filepath);
/* File-based redirection not supported. */
return false;
@@ -529,7 +528,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/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index ef137c4459e..abdae06acd5 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -409,7 +409,7 @@ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict
/**
* string with all instances of substr_old replaced with substr_new,
- * Returns a copy of the cstring \a str into a newly mallocN'd
+ * Returns a copy of the c-string \a str into a newly #MEM_mallocN'd
* and returns it.
*
* \note A rather wasteful string-replacement utility, though this shall do for now...
@@ -430,53 +430,49 @@ char *BLI_str_replaceN(const char *__restrict str,
BLI_assert(substr_old[0] != '\0');
- /* while we can still find a match for the old substring that we're searching for,
- * keep dicing and replacing
- */
+ /* While we can still find a match for the old sub-string that we're searching for,
+ * keep dicing and replacing. */
while ((match = strstr(str, substr_old))) {
/* the assembly buffer only gets created when we actually need to rebuild the string */
if (ds == NULL) {
ds = BLI_dynstr_new();
}
- /* if the match position does not match the current position in the string,
- * copy the text up to this position and advance the current position in the string
- */
+ /* If the match position does not match the current position in the string,
+ * copy the text up to this position and advance the current position in the string. */
if (str != match) {
- /* add the segment of the string from str to match to the buffer,
- * then restore the value at match */
+ /* Add the segment of the string from `str` to match to the buffer,
+ * then restore the value at match. */
BLI_dynstr_nappend(ds, str, (match - str));
/* now our current position should be set on the start of the match */
str = match;
}
- /* add the replacement text to the accumulation buffer */
+ /* Add the replacement text to the accumulation buffer. */
BLI_dynstr_append(ds, substr_new);
- /* advance the current position of the string up to the end of the replaced segment */
+ /* Advance the current position of the string up to the end of the replaced segment. */
str += len_old;
}
- /* finish off and return a new string that has had all occurrences of */
+ /* Finish off and return a new string that has had all occurrences of. */
if (ds) {
char *str_new;
- /* add what's left of the string to the assembly buffer
- * - we've been adjusting str to point at the end of the replaced segments
- */
+ /* Add what's left of the string to the assembly buffer
+ * - we've been adjusting `str` to point at the end of the replaced segments. */
BLI_dynstr_append(ds, str);
- /* convert to new c-string (MEM_malloc'd), and free the buffer */
+ /* Convert to new c-string (MEM_malloc'd), and free the buffer. */
str_new = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return str_new;
}
else {
- /* just create a new copy of the entire string - we avoid going through the assembly buffer
- * for what should be a bit more efficiency...
- */
+ /* Just create a new copy of the entire string - we avoid going through the assembly buffer
+ * for what should be a bit more efficiency. */
return BLI_strdup(str);
}
}
diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c
index 53db49aa59c..20edbb97561 100644
--- a/source/blender/blenlib/intern/system.c
+++ b/source/blender/blenlib/intern/system.c
@@ -111,7 +111,11 @@ void BLI_system_backtrace(FILE *fp)
/* NOTE: The code for CPU brand string is adopted from Cycles. */
#if !defined(_WIN32) || defined(FREE_WINDOWS)
-static void __cpuid(int data[4], int selector)
+static void __cpuid(
+ /* Cannot be const, because it is modified below.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ int data[4],
+ int selector)
{
# if defined(__x86_64__)
asm("cpuid" : "=a"(data[0]), "=b"(data[1]), "=c"(data[2]), "=d"(data[3]) : "a"(selector));
diff --git a/source/blender/blenlib/intern/task_range.cc b/source/blender/blenlib/intern/task_range.cc
index 67d8960434e..27e0fb0ed07 100644
--- a/source/blender/blenlib/intern/task_range.cc
+++ b/source/blender/blenlib/intern/task_range.cc
@@ -62,7 +62,7 @@ struct RangeTask {
}
/* Splitting constructor for parallel reduce. */
- RangeTask(RangeTask &other, tbb::split)
+ RangeTask(RangeTask &other, tbb::split /* unused */)
: func(other.func), userdata(other.userdata), settings(other.settings)
{
init_chunk(settings->userdata_chunk);
diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.cc
index be43c27e945..a8333d0c696 100644
--- a/source/blender/blenlib/intern/threads.c
+++ b/source/blender/blenlib/intern/threads.cc
@@ -47,6 +47,10 @@
# include <unistd.h>
#endif
+#ifdef WITH_TBB
+# include <tbb/spin_mutex.h>
+#endif
+
#include "atomic_ops.h"
#include "numaapi.h"
@@ -151,7 +155,7 @@ void BLI_threadpool_init(ListBase *threadbase, void *(*do_thread)(void *), int t
{
int a;
- if (threadbase != NULL && tot > 0) {
+ if (threadbase != nullptr && tot > 0) {
BLI_listbase_clear(threadbase);
if (tot > RE_MAX_THREAD) {
@@ -162,7 +166,7 @@ void BLI_threadpool_init(ListBase *threadbase, void *(*do_thread)(void *), int t
}
for (a = 0; a < tot; a++) {
- ThreadSlot *tslot = MEM_callocN(sizeof(ThreadSlot), "threadslot");
+ ThreadSlot *tslot = static_cast<ThreadSlot *>(MEM_callocN(sizeof(ThreadSlot), "threadslot"));
BLI_addtail(threadbase, tslot);
tslot->do_thread = do_thread;
tslot->avail = 1;
@@ -172,9 +176,9 @@ void BLI_threadpool_init(ListBase *threadbase, void *(*do_thread)(void *), int t
unsigned int level = atomic_fetch_and_add_u(&thread_levels, 1);
if (level == 0) {
#ifdef USE_APPLE_OMP_FIX
- /* workaround for Apple gcc 4.2.1 omp vs background thread bug,
- * we copy gomp thread local storage pointer to setting it again
- * inside the thread that we start */
+ /* Workaround for Apple gcc 4.2.1 OMP vs background thread bug,
+ * we copy GOMP thread local storage pointer to setting it again
+ * inside the thread that we start. */
thread_tls_data = pthread_getspecific(gomp_tls_key);
#endif
}
@@ -183,28 +187,29 @@ void BLI_threadpool_init(ListBase *threadbase, void *(*do_thread)(void *), int t
/* amount of available threads */
int BLI_available_threads(ListBase *threadbase)
{
- ThreadSlot *tslot;
int counter = 0;
- for (tslot = threadbase->first; tslot; tslot = tslot->next) {
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
if (tslot->avail) {
counter++;
}
}
+
return counter;
}
/* returns thread number, for sample patterns or threadsafe tables */
int BLI_threadpool_available_thread_index(ListBase *threadbase)
{
- ThreadSlot *tslot;
int counter = 0;
- for (tslot = threadbase->first; tslot; tslot = tslot->next, counter++) {
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
if (tslot->avail) {
return counter;
}
+ ++counter;
}
+
return 0;
}
@@ -213,8 +218,8 @@ static void *tslot_thread_start(void *tslot_p)
ThreadSlot *tslot = (ThreadSlot *)tslot_p;
#ifdef USE_APPLE_OMP_FIX
- /* workaround for Apple gcc 4.2.1 omp vs background thread bug,
- * set gomp thread local storage pointer which was copied beforehand */
+ /* Workaround for Apple gcc 4.2.1 OMP vs background thread bug,
+ * set GOMP thread local storage pointer which was copied beforehand */
pthread_setspecific(gomp_tls_key, thread_tls_data);
#endif
@@ -228,13 +233,11 @@ int BLI_thread_is_main(void)
void BLI_threadpool_insert(ListBase *threadbase, void *callerdata)
{
- ThreadSlot *tslot;
-
- for (tslot = threadbase->first; tslot; tslot = tslot->next) {
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
if (tslot->avail) {
tslot->avail = 0;
tslot->callerdata = callerdata;
- pthread_create(&tslot->pthread, NULL, tslot_thread_start, tslot);
+ pthread_create(&tslot->pthread, nullptr, tslot_thread_start, tslot);
return;
}
}
@@ -243,12 +246,10 @@ void BLI_threadpool_insert(ListBase *threadbase, void *callerdata)
void BLI_threadpool_remove(ListBase *threadbase, void *callerdata)
{
- ThreadSlot *tslot;
-
- for (tslot = threadbase->first; tslot; tslot = tslot->next) {
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
if (tslot->callerdata == callerdata) {
- pthread_join(tslot->pthread, NULL);
- tslot->callerdata = NULL;
+ pthread_join(tslot->pthread, nullptr);
+ tslot->callerdata = nullptr;
tslot->avail = 1;
}
}
@@ -256,27 +257,25 @@ void BLI_threadpool_remove(ListBase *threadbase, void *callerdata)
void BLI_threadpool_remove_index(ListBase *threadbase, int index)
{
- ThreadSlot *tslot;
int counter = 0;
- for (tslot = threadbase->first; tslot; tslot = tslot->next, counter++) {
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
if (counter == index && tslot->avail == 0) {
- pthread_join(tslot->pthread, NULL);
- tslot->callerdata = NULL;
+ pthread_join(tslot->pthread, nullptr);
+ tslot->callerdata = nullptr;
tslot->avail = 1;
break;
}
+ ++counter;
}
}
void BLI_threadpool_clear(ListBase *threadbase)
{
- ThreadSlot *tslot;
-
- for (tslot = threadbase->first; tslot; tslot = tslot->next) {
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
if (tslot->avail == 0) {
- pthread_join(tslot->pthread, NULL);
- tslot->callerdata = NULL;
+ pthread_join(tslot->pthread, nullptr);
+ tslot->callerdata = nullptr;
tslot->avail = 1;
}
}
@@ -284,19 +283,20 @@ void BLI_threadpool_clear(ListBase *threadbase)
void BLI_threadpool_end(ListBase *threadbase)
{
- ThreadSlot *tslot;
/* only needed if there's actually some stuff to end
* this way we don't end up decrementing thread_levels on an empty threadbase
* */
- if (threadbase && (BLI_listbase_is_empty(threadbase) == false)) {
- for (tslot = threadbase->first; tslot; tslot = tslot->next) {
- if (tslot->avail == 0) {
- pthread_join(tslot->pthread, NULL);
- }
+ if (threadbase == nullptr || BLI_listbase_is_empty(threadbase)) {
+ return;
+ }
+
+ LISTBASE_FOREACH (ThreadSlot *, tslot, threadbase) {
+ if (tslot->avail == 0) {
+ pthread_join(tslot->pthread, nullptr);
}
- BLI_freelistN(threadbase);
}
+ BLI_freelistN(threadbase);
}
/* System Information */
@@ -326,7 +326,7 @@ int BLI_system_thread_count(void)
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
len = sizeof(t);
- sysctl(mib, 2, &t, &len, NULL, 0);
+ sysctl(mib, 2, &t, &len, nullptr, 0);
# else
t = (int)sysconf(_SC_NPROCESSORS_ONLN);
# endif
@@ -377,7 +377,7 @@ static ThreadMutex *global_mutex_from_type(const int type)
return &_view3d_lock;
default:
BLI_assert(0);
- return NULL;
+ return nullptr;
}
}
@@ -395,7 +395,7 @@ void BLI_thread_unlock(int type)
void BLI_mutex_init(ThreadMutex *mutex)
{
- pthread_mutex_init(mutex, NULL);
+ pthread_mutex_init(mutex, nullptr);
}
void BLI_mutex_lock(ThreadMutex *mutex)
@@ -420,7 +420,7 @@ void BLI_mutex_end(ThreadMutex *mutex)
ThreadMutex *BLI_mutex_alloc(void)
{
- ThreadMutex *mutex = MEM_callocN(sizeof(ThreadMutex), "ThreadMutex");
+ ThreadMutex *mutex = static_cast<ThreadMutex *>(MEM_callocN(sizeof(ThreadMutex), "ThreadMutex"));
BLI_mutex_init(mutex);
return mutex;
}
@@ -433,10 +433,24 @@ void BLI_mutex_free(ThreadMutex *mutex)
/* Spin Locks */
+#ifdef WITH_TBB
+static tbb::spin_mutex *tbb_spin_mutex_cast(SpinLock *spin)
+{
+ static_assert(sizeof(SpinLock) >= sizeof(tbb::spin_mutex),
+ "SpinLock must match tbb::spin_mutex");
+ static_assert(alignof(SpinLock) % alignof(tbb::spin_mutex) == 0,
+ "SpinLock must be aligned same as tbb::spin_mutex");
+ return reinterpret_cast<tbb::spin_mutex *>(spin);
+}
+#endif
+
void BLI_spin_init(SpinLock *spin)
{
-#if defined(__APPLE__)
- *spin = OS_SPINLOCK_INIT;
+#ifdef WITH_TBB
+ tbb::spin_mutex *spin_mutex = tbb_spin_mutex_cast(spin);
+ new (spin_mutex) tbb::spin_mutex();
+#elif defined(__APPLE__)
+ BLI_mutex_init(spin);
#elif defined(_MSC_VER)
*spin = 0;
#else
@@ -446,8 +460,11 @@ void BLI_spin_init(SpinLock *spin)
void BLI_spin_lock(SpinLock *spin)
{
-#if defined(__APPLE__)
- OSSpinLockLock(spin);
+#ifdef WITH_TBB
+ tbb::spin_mutex *spin_mutex = tbb_spin_mutex_cast(spin);
+ spin_mutex->lock();
+#elif defined(__APPLE__)
+ BLI_mutex_lock(spin);
#elif defined(_MSC_VER)
while (InterlockedExchangeAcquire(spin, 1)) {
while (*spin) {
@@ -462,8 +479,11 @@ void BLI_spin_lock(SpinLock *spin)
void BLI_spin_unlock(SpinLock *spin)
{
-#if defined(__APPLE__)
- OSSpinLockUnlock(spin);
+#ifdef WITH_TBB
+ tbb::spin_mutex *spin_mutex = tbb_spin_mutex_cast(spin);
+ spin_mutex->unlock();
+#elif defined(__APPLE__)
+ BLI_mutex_unlock(spin);
#elif defined(_MSC_VER)
_ReadWriteBarrier();
*spin = 0;
@@ -472,22 +492,25 @@ void BLI_spin_unlock(SpinLock *spin)
#endif
}
-#if defined(__APPLE__) || defined(_MSC_VER)
-void BLI_spin_end(SpinLock *UNUSED(spin))
-{
-}
-#else
void BLI_spin_end(SpinLock *spin)
{
+#ifdef WITH_TBB
+ tbb::spin_mutex *spin_mutex = tbb_spin_mutex_cast(spin);
+ spin_mutex->~spin_mutex();
+#elif defined(__APPLE__)
+ BLI_mutex_end(spin);
+#elif defined(_MSC_VER)
+ /* Nothing to do, spin is a simple integer type. */
+#else
pthread_spin_destroy(spin);
-}
#endif
+}
/* Read/Write Mutex Lock */
void BLI_rw_mutex_init(ThreadRWMutex *mutex)
{
- pthread_rwlock_init(mutex, NULL);
+ pthread_rwlock_init(mutex, nullptr);
}
void BLI_rw_mutex_lock(ThreadRWMutex *mutex, int mode)
@@ -512,7 +535,8 @@ void BLI_rw_mutex_end(ThreadRWMutex *mutex)
ThreadRWMutex *BLI_rw_mutex_alloc(void)
{
- ThreadRWMutex *mutex = MEM_callocN(sizeof(ThreadRWMutex), "ThreadRWMutex");
+ ThreadRWMutex *mutex = static_cast<ThreadRWMutex *>(
+ MEM_callocN(sizeof(ThreadRWMutex), "ThreadRWMutex"));
BLI_rw_mutex_init(mutex);
return mutex;
}
@@ -533,10 +557,11 @@ struct TicketMutex {
TicketMutex *BLI_ticket_mutex_alloc(void)
{
- TicketMutex *ticket = MEM_callocN(sizeof(TicketMutex), "TicketMutex");
+ TicketMutex *ticket = static_cast<TicketMutex *>(
+ MEM_callocN(sizeof(TicketMutex), "TicketMutex"));
- pthread_cond_init(&ticket->cond, NULL);
- pthread_mutex_init(&ticket->mutex, NULL);
+ pthread_cond_init(&ticket->cond, nullptr);
+ pthread_mutex_init(&ticket->mutex, nullptr);
return ticket;
}
@@ -576,7 +601,7 @@ void BLI_ticket_mutex_unlock(TicketMutex *ticket)
void BLI_condition_init(ThreadCondition *cond)
{
- pthread_cond_init(cond, NULL);
+ pthread_cond_init(cond, nullptr);
}
void BLI_condition_wait(ThreadCondition *cond, ThreadMutex *mutex)
@@ -619,12 +644,12 @@ ThreadQueue *BLI_thread_queue_init(void)
{
ThreadQueue *queue;
- queue = MEM_callocN(sizeof(ThreadQueue), "ThreadQueue");
+ queue = static_cast<ThreadQueue *>(MEM_callocN(sizeof(ThreadQueue), "ThreadQueue"));
queue->queue = BLI_gsqueue_new(sizeof(void *));
- pthread_mutex_init(&queue->mutex, NULL);
- pthread_cond_init(&queue->push_cond, NULL);
- pthread_cond_init(&queue->finish_cond, NULL);
+ pthread_mutex_init(&queue->mutex, nullptr);
+ pthread_cond_init(&queue->push_cond, nullptr);
+ pthread_cond_init(&queue->finish_cond, nullptr);
return queue;
}
@@ -654,7 +679,7 @@ void BLI_thread_queue_push(ThreadQueue *queue, void *work)
void *BLI_thread_queue_pop(ThreadQueue *queue)
{
- void *work = NULL;
+ void *work = nullptr;
/* wait until there is work */
pthread_mutex_lock(&queue->mutex);
@@ -691,7 +716,7 @@ static void wait_timeout(struct timespec *timeout, int ms)
#else
{
struct timeval now;
- gettimeofday(&now, NULL);
+ gettimeofday(&now, nullptr);
sec = now.tv_sec;
usec = now.tv_usec;
}
@@ -714,7 +739,7 @@ static void wait_timeout(struct timespec *timeout, int ms)
void *BLI_thread_queue_pop_timeout(ThreadQueue *queue, int ms)
{
double t;
- void *work = NULL;
+ void *work = nullptr;
struct timespec timeout;
t = PIL_check_seconds_timer();
@@ -805,7 +830,7 @@ static bool check_is_threadripper2_alike_topology(void)
}
is_initialized = true;
char *cpu_brand = BLI_cpu_brand_string();
- if (cpu_brand == NULL) {
+ if (cpu_brand == nullptr) {
return false;
}
if (strstr(cpu_brand, "Threadripper")) {
diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c
index 510b9651961..9586da941a4 100644
--- a/source/blender/blenlib/intern/timecode.c
+++ b/source/blender/blenlib/intern/timecode.c
@@ -36,7 +36,7 @@
#include "BLI_strict_flags.h"
/**
- * Generate timecode/frame number string and store in \a str
+ * Generate time-code/frame number string and store in \a str
*
* \param str: destination string
* \param maxncpy: maximum number of characters to copy ``sizeof(str)``
@@ -44,7 +44,7 @@
* used to specify how detailed we need to be
* \param time_seconds: time total time in seconds
* \param fps: frames per second, typically from the #FPS macro
- * \param timecode_style: enum from eTimecodeStyles
+ * \param timecode_style: enum from #eTimecodeStyles
* \return length of \a str
*/
diff --git a/source/blender/blenlib/intern/timeit.cc b/source/blender/blenlib/intern/timeit.cc
index 7938784da67..9e07e44ca12 100644
--- a/source/blender/blenlib/intern/timeit.cc
+++ b/source/blender/blenlib/intern/timeit.cc
@@ -16,8 +16,7 @@
#include "BLI_timeit.hh"
-namespace blender {
-namespace Timeit {
+namespace blender::timeit {
void print_duration(Nanoseconds duration)
{
@@ -32,5 +31,4 @@ void print_duration(Nanoseconds duration)
}
}
-} // namespace Timeit
-} // namespace blender
+} // namespace blender::timeit
diff --git a/source/blender/blenlib/intern/voronoi_2d.c b/source/blender/blenlib/intern/voronoi_2d.c
index 59270c58341..bc11a2c7a1c 100644
--- a/source/blender/blenlib/intern/voronoi_2d.c
+++ b/source/blender/blenlib/intern/voronoi_2d.c
@@ -213,7 +213,7 @@ static void voronoiParabola_setRight(VoronoiParabola *parabola, VoronoiParabola
right->parent = parabola;
}
-static float voronoi_getY(VoronoiProcess *process, float p[2], float x)
+static float voronoi_getY(VoronoiProcess *process, const float p[2], float x)
{
float ly = process->current_y;
diff --git a/source/blender/blenlib/intern/voxel.c b/source/blender/blenlib/intern/voxel.c
index c7c794957c2..2c8eb9f5a13 100644
--- a/source/blender/blenlib/intern/voxel.c
+++ b/source/blender/blenlib/intern/voxel.c
@@ -26,7 +26,7 @@
#include "BLI_strict_flags.h"
-BLI_INLINE float D(float *data, const int res[3], int x, int y, int z)
+BLI_INLINE float D(const float *data, const int res[3], int x, int y, int z)
{
CLAMP(x, 0, res[0] - 1);
CLAMP(y, 0, res[1] - 1);
@@ -36,7 +36,7 @@ BLI_INLINE float D(float *data, const int res[3], int x, int y, int z)
/* *** nearest neighbor *** */
/* input coordinates must be in bounding box 0.0 - 1.0 */
-float BLI_voxel_sample_nearest(float *data, const int res[3], const float co[3])
+float BLI_voxel_sample_nearest(const float *data, const int res[3], const float co[3])
{
int xi, yi, zi;
@@ -65,7 +65,7 @@ BLI_INLINE int64_t _clamp(int a, int b, int c)
return (a < b) ? b : ((a > c) ? c : a);
}
-float BLI_voxel_sample_trilinear(float *data, const int res[3], const float co[3])
+float BLI_voxel_sample_trilinear(const float *data, const int res[3], const float co[3])
{
if (data) {
@@ -106,7 +106,7 @@ float BLI_voxel_sample_trilinear(float *data, const int res[3], const float co[3
return 0.f;
}
-float BLI_voxel_sample_triquadratic(float *data, const int res[3], const float co[3])
+float BLI_voxel_sample_triquadratic(const float *data, const int res[3], const float co[3])
{
if (data) {
@@ -161,7 +161,10 @@ float BLI_voxel_sample_triquadratic(float *data, const int res[3], const float c
return 0.f;
}
-float BLI_voxel_sample_tricubic(float *data, const int res[3], const float co[3], int bspline)
+float BLI_voxel_sample_tricubic(const float *data,
+ const int res[3],
+ const float co[3],
+ int bspline)
{
if (data) {
diff --git a/tests/gtests/blenlib/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc
index fc3c15fe813..7348a6f93f3 100644
--- a/tests/gtests/blenlib/BLI_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_array_test.cc
@@ -1,8 +1,10 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_array.hh"
#include "BLI_strict_flags.h"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
TEST(array, DefaultConstructor)
{
@@ -70,7 +72,7 @@ TEST(array, MoveConstructor)
Array<int> array = {5, 6, 7, 8};
Array<int> new_array(std::move(array));
- EXPECT_EQ(array.size(), 0);
+ EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(new_array.size(), 4);
EXPECT_EQ(new_array[0], 5);
EXPECT_EQ(new_array[1], 6);
@@ -99,7 +101,7 @@ TEST(array, MoveAssignment)
EXPECT_EQ(new_array.size(), 1);
new_array = std::move(array);
EXPECT_EQ(new_array.size(), 3);
- EXPECT_EQ(array.size(), 0);
+ EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(new_array[0], 1);
EXPECT_EQ(new_array[1], 2);
EXPECT_EQ(new_array[2], 3);
@@ -124,3 +126,51 @@ TEST(array, TrivialTypeSizeConstructor)
EXPECT_EQ(*ptr, magic);
delete array;
}
+
+struct ConstructibleType {
+ char value;
+
+ ConstructibleType()
+ {
+ value = 42;
+ }
+};
+
+TEST(array, NoInitializationSizeConstructor)
+{
+ using MyArray = Array<ConstructibleType>;
+
+ TypedBuffer<MyArray> buffer;
+ memset((void *)&buffer, 100, sizeof(MyArray));
+
+ /* Doing this to avoid some compiler optimization. */
+ for (int64_t i : IndexRange(sizeof(MyArray))) {
+ EXPECT_EQ(((char *)buffer.ptr())[i], 100);
+ }
+
+ {
+ MyArray &array = *new (buffer) MyArray(1, NoInitialization());
+ EXPECT_EQ(array[0].value, 100);
+ array.clear_without_destruct();
+ array.~Array();
+ }
+ {
+ MyArray &array = *new (buffer) MyArray(1);
+ EXPECT_EQ(array[0].value, 42);
+ array.~Array();
+ }
+}
+
+TEST(array, Fill)
+{
+ Array<int> array(5);
+ array.fill(3);
+ EXPECT_EQ(array.size(), 5u);
+ EXPECT_EQ(array[0], 3);
+ EXPECT_EQ(array[1], 3);
+ EXPECT_EQ(array[2], 3);
+ EXPECT_EQ(array[3], 3);
+ EXPECT_EQ(array[4], 3);
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_disjoint_set_test.cc b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc
new file mode 100644
index 00000000000..f30ee610b2a
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc
@@ -0,0 +1,36 @@
+/* Apache License, Version 2.0 */
+
+#include "BLI_disjoint_set.hh"
+#include "BLI_strict_flags.h"
+
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+TEST(disjoint_set, Test)
+{
+ DisjointSet disjoint_set(6);
+ EXPECT_FALSE(disjoint_set.in_same_set(1, 2));
+ EXPECT_FALSE(disjoint_set.in_same_set(5, 3));
+ EXPECT_TRUE(disjoint_set.in_same_set(2, 2));
+ EXPECT_EQ(disjoint_set.find_root(3), 3);
+
+ disjoint_set.join(1, 2);
+
+ EXPECT_TRUE(disjoint_set.in_same_set(1, 2));
+ EXPECT_FALSE(disjoint_set.in_same_set(0, 1));
+
+ disjoint_set.join(3, 4);
+
+ EXPECT_FALSE(disjoint_set.in_same_set(2, 3));
+ EXPECT_TRUE(disjoint_set.in_same_set(3, 4));
+
+ disjoint_set.join(1, 4);
+
+ EXPECT_TRUE(disjoint_set.in_same_set(1, 4));
+ EXPECT_TRUE(disjoint_set.in_same_set(1, 3));
+ EXPECT_TRUE(disjoint_set.in_same_set(2, 4));
+ EXPECT_FALSE(disjoint_set.in_same_set(0, 4));
+}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_edgehash_test.cc b/source/blender/blenlib/tests/BLI_edgehash_test.cc
index 982f5b35565..7106033df36 100644
--- a/tests/gtests/blenlib/BLI_edgehash_test.cc
+++ b/source/blender/blenlib/tests/BLI_edgehash_test.cc
@@ -2,12 +2,11 @@
#include "testing/testing.h"
#include <algorithm>
+#include <random>
#include <vector>
-extern "C" {
#include "BLI_edgehash.h"
#include "BLI_utildefines.h"
-}
#define VALUE_1 POINTER_FROM_INT(1)
#define VALUE_2 POINTER_FROM_INT(2)
@@ -320,7 +319,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;
@@ -333,10 +332,12 @@ TEST(edgehash, StressTest)
/* check if the right ones have been removed */
for (int i = 0; i < shuffled.size(); i++) {
bool haskey = BLI_edgehash_haskey(eh, shuffled[i].v1, shuffled[i].v2);
- if (i < remove_until)
+ if (i < remove_until) {
ASSERT_FALSE(haskey);
- else
+ }
+ else {
ASSERT_TRUE(haskey);
+ }
}
/* reinsert all edges */
diff --git a/tests/gtests/blenlib/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc
index 63b528c91b1..4d6060e51c9 100644
--- a/tests/gtests/blenlib/BLI_index_mask_test.cc
+++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc
@@ -1,7 +1,9 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_index_mask.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
TEST(index_mask, DefaultConstructor)
{
@@ -32,8 +34,10 @@ TEST(index_mask, RangeConstructor)
EXPECT_TRUE(mask.is_range());
EXPECT_EQ(mask.as_range().first(), 3);
EXPECT_EQ(mask.as_range().last(), 7);
- Span<uint> indices = mask.indices();
+ Span<int64_t> indices = mask.indices();
EXPECT_EQ(indices[0], 3);
EXPECT_EQ(indices[1], 4);
EXPECT_EQ(indices[2], 5);
}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc
index da51b7a66c0..d472ded0f18 100644
--- a/tests/gtests/blenlib/BLI_index_range_test.cc
+++ b/source/blender/blenlib/tests/BLI_index_range_test.cc
@@ -1,17 +1,19 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_index_range.hh"
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
TEST(index_range, DefaultConstructor)
{
IndexRange range;
EXPECT_EQ(range.size(), 0);
- Vector<uint> vector;
- for (uint value : range) {
+ Vector<int64_t> vector;
+ for (int64_t value : range) {
vector.append(value);
}
EXPECT_EQ(vector.size(), 0);
@@ -23,8 +25,8 @@ TEST(index_range, SingleElementRange)
EXPECT_EQ(range.size(), 1);
EXPECT_EQ(*range.begin(), 4);
- Vector<uint> vector;
- for (uint value : range) {
+ Vector<int64_t> vector;
+ for (int64_t value : range) {
vector.append(value);
}
@@ -37,13 +39,13 @@ TEST(index_range, MultipleElementRange)
IndexRange range(6, 4);
EXPECT_EQ(range.size(), 4);
- Vector<uint> vector;
- for (uint value : range) {
+ Vector<int64_t> vector;
+ for (int64_t value : range) {
vector.append(value);
}
EXPECT_EQ(vector.size(), 4);
- for (uint i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
EXPECT_EQ(vector[i], i + 6);
}
}
@@ -130,10 +132,12 @@ TEST(index_range, SliceRange)
TEST(index_range, AsSpan)
{
IndexRange range = IndexRange(4, 6);
- Span<uint> span = range.as_span();
+ Span<int64_t> 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);
}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
index 0d926ca7931..44b70d1f55d 100644
--- a/tests/gtests/blenlib/BLI_linear_allocator_test.cc
+++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
@@ -1,8 +1,10 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_linear_allocator.hh"
#include "BLI_strict_flags.h"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
static bool is_aligned(void *ptr, uint alignment)
{
@@ -112,3 +114,5 @@ TEST(linear_allocator, ConstructArrayCopy)
EXPECT_EQ(span1[1], 2);
EXPECT_EQ(span2[2], 3);
}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc
index b14ba40ed7c..fe7b0f01279 100644
--- a/tests/gtests/blenlib/BLI_map_test.cc
+++ b/source/blender/blenlib/tests/BLI_map_test.cc
@@ -1,3 +1,5 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_map.hh"
#include "BLI_rand.h"
#include "BLI_set.hh"
@@ -6,7 +8,7 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
TEST(map, DefaultConstructor)
{
@@ -88,13 +90,13 @@ TEST(map, PopTry)
map.add(1, 5);
map.add(2, 7);
EXPECT_EQ(map.size(), 2);
- Optional<int> value = map.pop_try(4);
+ std::optional<int> value = map.pop_try(4);
EXPECT_EQ(map.size(), 2);
EXPECT_FALSE(value.has_value());
value = map.pop_try(2);
EXPECT_EQ(map.size(), 1);
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);
}
@@ -141,7 +143,7 @@ TEST(map, ValueIterator)
blender::Set<float> values;
- uint iterations = 0;
+ int iterations = 0;
for (float value : map.values()) {
values.add(value);
iterations++;
@@ -162,7 +164,7 @@ TEST(map, KeyIterator)
blender::Set<int> keys;
- uint iterations = 0;
+ int iterations = 0;
for (int key : map.keys()) {
keys.add(key);
iterations++;
@@ -184,8 +186,9 @@ TEST(map, ItemIterator)
blender::Set<int> keys;
blender::Set<float> values;
- uint iterations = 0;
- for (auto item : map.items()) {
+ int iterations = 0;
+ const Map<int, float> &const_map = map;
+ for (auto item : const_map.items()) {
keys.add(item.key);
values.add(item.value);
iterations++;
@@ -228,6 +231,26 @@ TEST(map, MutableItemIterator)
EXPECT_EQ(map.lookup(2), 3.0f);
}
+TEST(map, MutableItemToItemConversion)
+{
+ Map<int, int> map;
+ map.add(3, 6);
+ map.add(2, 1);
+
+ Vector<int> keys, values;
+ for (Map<int, int>::Item item : map.items()) {
+ keys.append(item.key);
+ values.append(item.value);
+ }
+
+ EXPECT_EQ(keys.size(), 2);
+ EXPECT_EQ(values.size(), 2);
+ EXPECT_TRUE(keys.contains(3));
+ EXPECT_TRUE(keys.contains(2));
+ EXPECT_TRUE(values.contains(6));
+ EXPECT_TRUE(values.contains(1));
+}
+
static float return_42()
{
return 42.0f;
@@ -312,7 +335,7 @@ TEST(map, MoveConstructorSmall)
EXPECT_EQ(map2.size(), 2);
EXPECT_EQ(map2.lookup(1), 2.0f);
EXPECT_EQ(map2.lookup(4), 1.0f);
- EXPECT_EQ(map1.size(), 0);
+ EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(map1.lookup_ptr(4), nullptr);
}
@@ -326,7 +349,7 @@ TEST(map, MoveConstructorLarge)
EXPECT_EQ(map2.size(), 100);
EXPECT_EQ(map2.lookup(1), 1);
EXPECT_EQ(map2.lookup(4), 4);
- EXPECT_EQ(map1.size(), 0);
+ EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(map1.lookup_ptr(4), nullptr);
}
@@ -340,7 +363,7 @@ TEST(map, MoveAssignment)
EXPECT_EQ(map2.size(), 2);
EXPECT_EQ(map2.lookup(1), 2.0f);
EXPECT_EQ(map2.lookup(4), 1.0f);
- EXPECT_EQ(map1.size(), 0);
+ EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(map1.lookup_ptr(4), nullptr);
}
@@ -428,16 +451,44 @@ TEST(map, PointerKeys)
EXPECT_TRUE(map.is_empty());
}
+TEST(map, ConstKeysAndValues)
+{
+ Map<const std::string, const std::string> map;
+ map.reserve(10);
+ map.add("45", "643");
+ EXPECT_TRUE(map.contains("45"));
+ 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(), 2);
+ EXPECT_EQ(values.size(), 2);
+ 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.
*/
#if 0
template<typename MapT>
-BLI_NOINLINE void benchmark_random_ints(StringRef name, uint amount, uint factor)
+BLI_NOINLINE void benchmark_random_ints(StringRef name, int amount, int factor)
{
RNG *rng = BLI_rng_new(0);
Vector<int> values;
- for (uint i = 0; i < amount; i++) {
+ for (int i = 0; i < amount; i++) {
values.append(BLI_rng_get_int(rng) * factor);
}
BLI_rng_free(rng);
@@ -469,12 +520,12 @@ BLI_NOINLINE void benchmark_random_ints(StringRef name, uint amount, uint factor
TEST(map, Benchmark)
{
- for (uint i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
benchmark_random_ints<blender::Map<int, int>>("blender::Map ", 1000000, 1);
benchmark_random_ints<blender::StdUnorderedMapWrapper<int, int>>("std::unordered_map", 1000000, 1);
}
std::cout << "\n";
- for (uint i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
uint32_t factor = (3 << 10);
benchmark_random_ints<blender::Map<int, int>>("blender::Map ", 1000000, factor);
benchmark_random_ints<blender::StdUnorderedMapWrapper<int, int>>(
@@ -535,3 +586,5 @@ TEST(map, Benchmark)
*/
#endif /* Benchmark */
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_math_base_safe_test.cc b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc
new file mode 100644
index 00000000000..2e3e083cf92
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc
@@ -0,0 +1,37 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_math_base_safe.h"
+
+TEST(math_base, SafePowf)
+{
+ EXPECT_FLOAT_EQ(safe_powf(4.0f, 3.0f), 64.0f);
+ EXPECT_FLOAT_EQ(safe_powf(3.2f, 5.6f), 674.2793796f);
+ EXPECT_FLOAT_EQ(safe_powf(4.0f, -2.0f), 0.0625f);
+ EXPECT_FLOAT_EQ(safe_powf(6.0f, -3.2f), 0.003235311f);
+ EXPECT_FLOAT_EQ(safe_powf(-4.0f, 6), 4096.0f);
+ EXPECT_FLOAT_EQ(safe_powf(-3.0f, 5.5), 0.0f);
+ EXPECT_FLOAT_EQ(safe_powf(-2.5f, -4.0f), 0.0256f);
+ EXPECT_FLOAT_EQ(safe_powf(-3.7f, -4.5f), 0.0f);
+}
+
+TEST(math_base, SafeModf)
+{
+ EXPECT_FLOAT_EQ(safe_modf(3.4, 2.2f), 1.2f);
+ EXPECT_FLOAT_EQ(safe_modf(3.4, -2.2f), 1.2f);
+ EXPECT_FLOAT_EQ(safe_modf(-3.4, -2.2f), -1.2f);
+ EXPECT_FLOAT_EQ(safe_modf(-3.4, 0.0f), 0.0f);
+ EXPECT_FLOAT_EQ(safe_modf(0.0f, 3.0f), 0.0f);
+ EXPECT_FLOAT_EQ(safe_modf(55.0f, 10.0f), 5.0f);
+}
+
+TEST(math_base, SafeLogf)
+{
+ EXPECT_FLOAT_EQ(safe_logf(3.3f, 2.5f), 1.302995247f);
+ EXPECT_FLOAT_EQ(safe_logf(0.0f, 3.0f), 0.0f);
+ EXPECT_FLOAT_EQ(safe_logf(3.0f, 0.0f), 0.0f);
+ EXPECT_FLOAT_EQ(safe_logf(-2.0f, 4.3f), 0.0f);
+ EXPECT_FLOAT_EQ(safe_logf(2.0f, -4.3f), 0.0f);
+ EXPECT_FLOAT_EQ(safe_logf(-2.0f, -4.3f), 0.0f);
+}
diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
new file mode 100644
index 00000000000..f3cb02b63d7
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
@@ -0,0 +1,159 @@
+/* Apache License, Version 2.0 */
+
+#include "BLI_float3.hh"
+#include "BLI_memory_utils.hh"
+#include "BLI_strict_flags.h"
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+struct MyValue {
+ static inline int alive = 0;
+
+ MyValue()
+ {
+ if (alive == 15) {
+ throw std::exception();
+ }
+
+ alive++;
+ }
+
+ MyValue(const MyValue &UNUSED(other))
+ {
+ if (alive == 15) {
+ throw std::exception();
+ }
+
+ alive++;
+ }
+
+ ~MyValue()
+ {
+ alive--;
+ }
+};
+
+TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor)
+{
+ constexpr int amount = 10;
+ TypedBuffer<MyValue, amount> buffer;
+
+ EXPECT_EQ(MyValue::alive, 0);
+ default_construct_n(buffer.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, amount);
+ destruct_n(buffer.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, 0);
+}
+
+TEST(memory_utils, DefaultConstructN_StrongExceptionSafety)
+{
+ constexpr int amount = 20;
+ TypedBuffer<MyValue, amount> buffer;
+
+ EXPECT_EQ(MyValue::alive, 0);
+ EXPECT_THROW(default_construct_n(buffer.ptr(), amount), std::exception);
+ EXPECT_EQ(MyValue::alive, 0);
+}
+
+TEST(memory_utils, UninitializedCopyN_ActuallyCopies)
+{
+ constexpr int amount = 5;
+ TypedBuffer<MyValue, amount> buffer1;
+ TypedBuffer<MyValue, amount> buffer2;
+
+ EXPECT_EQ(MyValue::alive, 0);
+ default_construct_n(buffer1.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, amount);
+ uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr());
+ EXPECT_EQ(MyValue::alive, 2 * amount);
+ destruct_n(buffer1.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, amount);
+ destruct_n(buffer2.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, 0);
+}
+
+TEST(memory_utils, UninitializedCopyN_StrongExceptionSafety)
+{
+ constexpr int amount = 10;
+ TypedBuffer<MyValue, amount> buffer1;
+ TypedBuffer<MyValue, amount> buffer2;
+
+ EXPECT_EQ(MyValue::alive, 0);
+ default_construct_n(buffer1.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, amount);
+ EXPECT_THROW(uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()), std::exception);
+ EXPECT_EQ(MyValue::alive, amount);
+ destruct_n(buffer1.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, 0);
+}
+
+TEST(memory_utils, UninitializedFillN_ActuallyCopies)
+{
+ constexpr int amount = 10;
+ TypedBuffer<MyValue, amount> buffer;
+
+ EXPECT_EQ(MyValue::alive, 0);
+ {
+ MyValue value;
+ EXPECT_EQ(MyValue::alive, 1);
+ uninitialized_fill_n(buffer.ptr(), amount, value);
+ EXPECT_EQ(MyValue::alive, 1 + amount);
+ destruct_n(buffer.ptr(), amount);
+ EXPECT_EQ(MyValue::alive, 1);
+ }
+ EXPECT_EQ(MyValue::alive, 0);
+}
+
+TEST(memory_utils, UninitializedFillN_StrongExceptionSafety)
+{
+ constexpr int amount = 20;
+ TypedBuffer<MyValue, amount> buffer;
+
+ EXPECT_EQ(MyValue::alive, 0);
+ {
+ MyValue value;
+ EXPECT_EQ(MyValue::alive, 1);
+ EXPECT_THROW(uninitialized_fill_n(buffer.ptr(), amount, value), std::exception);
+ EXPECT_EQ(MyValue::alive, 1);
+ }
+ EXPECT_EQ(MyValue::alive, 0);
+}
+
+class TestBaseClass {
+ virtual void mymethod(){};
+};
+
+class TestChildClass : public TestBaseClass {
+ void mymethod() override
+ {
+ }
+};
+
+static_assert(is_convertible_pointer_v<int *, int *>);
+static_assert(is_convertible_pointer_v<int *, const int *>);
+static_assert(is_convertible_pointer_v<int *, int *const>);
+static_assert(is_convertible_pointer_v<int *, const int *const>);
+static_assert(!is_convertible_pointer_v<const int *, int *>);
+static_assert(!is_convertible_pointer_v<int, int *>);
+static_assert(!is_convertible_pointer_v<int *, int>);
+static_assert(is_convertible_pointer_v<TestBaseClass *, const TestBaseClass *>);
+static_assert(!is_convertible_pointer_v<const TestBaseClass *, TestBaseClass *>);
+static_assert(is_convertible_pointer_v<TestChildClass *, TestBaseClass *>);
+static_assert(!is_convertible_pointer_v<TestBaseClass *, TestChildClass *>);
+static_assert(is_convertible_pointer_v<const TestChildClass *, const TestBaseClass *>);
+static_assert(!is_convertible_pointer_v<TestBaseClass, const TestChildClass *>);
+static_assert(!is_convertible_pointer_v<float3, float *>);
+static_assert(!is_convertible_pointer_v<float *, float3>);
+static_assert(!is_convertible_pointer_v<int **, int *>);
+static_assert(!is_convertible_pointer_v<int *, int **>);
+static_assert(is_convertible_pointer_v<int **, int **>);
+static_assert(is_convertible_pointer_v<const int **, const int **>);
+static_assert(!is_convertible_pointer_v<const int **, int **>);
+static_assert(!is_convertible_pointer_v<int *const *, int **>);
+static_assert(!is_convertible_pointer_v<int *const *const, int **>);
+static_assert(is_convertible_pointer_v<int **, int **const>);
+static_assert(is_convertible_pointer_v<int **, int *const *>);
+static_assert(is_convertible_pointer_v<int **, int const *const *>);
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_multi_value_map_test.cc b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc
new file mode 100644
index 00000000000..7501fbe0d87
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc
@@ -0,0 +1,109 @@
+/* Apache License, Version 2.0 */
+
+#include "BLI_multi_value_map.hh"
+#include "BLI_vector.hh"
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+TEST(multi_value_map, LookupNotExistant)
+{
+ MultiValueMap<int, int> map;
+ EXPECT_EQ(map.lookup(5).size(), 0);
+ map.add(2, 5);
+ EXPECT_EQ(map.lookup(5).size(), 0);
+}
+
+TEST(multi_value_map, LookupExistant)
+{
+ MultiValueMap<int, int> map;
+ map.add(2, 4);
+ map.add(2, 5);
+ map.add(3, 6);
+
+ EXPECT_EQ(map.lookup(2).size(), 2);
+ EXPECT_EQ(map.lookup(2)[0], 4);
+ EXPECT_EQ(map.lookup(2)[1], 5);
+
+ EXPECT_EQ(map.lookup(3).size(), 1);
+ EXPECT_EQ(map.lookup(3)[0], 6);
+}
+
+TEST(multi_value_map, AddMultiple)
+{
+ MultiValueMap<int, int> map;
+ map.add_multiple(2, {4, 5, 6});
+ map.add_multiple(2, {1, 2});
+ map.add_multiple(5, {7, 5, 3});
+
+ EXPECT_EQ(map.lookup(2).size(), 5);
+ EXPECT_EQ(map.lookup(2)[0], 4);
+ EXPECT_EQ(map.lookup(2)[1], 5);
+ EXPECT_EQ(map.lookup(2)[2], 6);
+ EXPECT_EQ(map.lookup(2)[3], 1);
+ EXPECT_EQ(map.lookup(2)[4], 2);
+
+ EXPECT_EQ(map.lookup(5).size(), 3);
+ EXPECT_EQ(map.lookup(5)[0], 7);
+ EXPECT_EQ(map.lookup(5)[1], 5);
+ EXPECT_EQ(map.lookup(5)[2], 3);
+}
+
+TEST(multi_value_map, Keys)
+{
+ MultiValueMap<int, int> map;
+ map.add(5, 7);
+ map.add(5, 7);
+ map.add_multiple(2, {6, 7, 8});
+
+ Vector<int> keys;
+ for (int key : map.keys()) {
+ keys.append(key);
+ }
+
+ EXPECT_EQ(keys.size(), 2);
+ EXPECT_TRUE(keys.contains(5));
+ EXPECT_TRUE(keys.contains(2));
+}
+
+TEST(multi_value_map, Values)
+{
+ MultiValueMap<int, int> map;
+ map.add(3, 5);
+ map.add_multiple(3, {1, 2});
+ map.add(6, 1);
+
+ Vector<Span<int>> values;
+ for (Span<int> value_span : map.values()) {
+ values.append(value_span);
+ }
+
+ EXPECT_EQ(values.size(), 2);
+}
+
+TEST(multi_value_map, Items)
+{
+ MultiValueMap<int, int> map;
+ map.add_multiple(4, {1, 2, 3});
+
+ for (auto &&item : map.items()) {
+ int key = item.key;
+ Span<int> values = item.value;
+ EXPECT_EQ(key, 4);
+ EXPECT_EQ(values.size(), 3);
+ EXPECT_EQ(values[0], 1);
+ EXPECT_EQ(values[1], 2);
+ EXPECT_EQ(values[2], 3);
+ }
+}
+
+TEST(multi_value_map, UniquePtr)
+{
+ /* Mostly testing if it compiles here. */
+ MultiValueMap<std::unique_ptr<int>, std::unique_ptr<int>> map;
+ map.add(std::make_unique<int>(4), std::make_unique<int>(6));
+ map.add(std::make_unique<int>(4), std::make_unique<int>(7));
+ EXPECT_EQ(map.lookup(std::make_unique<int>(10)).size(), 0);
+}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc
index 8e77a446a97..7bd0b258df8 100644
--- a/tests/gtests/blenlib/BLI_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_set_test.cc
@@ -1,3 +1,5 @@
+/* Apache License, Version 2.0 */
+
#include <set>
#include <unordered_set>
@@ -9,7 +11,8 @@
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender {
+namespace tests {
TEST(set, DefaultConstructor)
{
@@ -79,7 +82,7 @@ TEST(set, MoveConstructor)
Set<int> set = {1, 2, 3};
EXPECT_EQ(set.size(), 3);
Set<int> set2(std::move(set));
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(set2.size(), 3);
}
@@ -104,7 +107,7 @@ TEST(set, MoveAssignment)
EXPECT_EQ(set.size(), 3);
Set<int> set2;
set2 = std::move(set);
- EXPECT_EQ(set.size(), 0);
+ EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(set2.size(), 3);
}
@@ -277,31 +280,32 @@ struct Type2 {
uint32_t value;
};
-bool operator==(const Type1 &a, const Type1 &b)
+static bool operator==(const Type1 &a, const Type1 &b)
{
return a.value == b.value;
}
-bool operator==(const Type1 &a, const Type2 &b)
-{
- return a.value == b.value;
-}
-bool operator==(const Type2 &a, const Type1 &b)
+static bool operator==(const Type2 &a, const Type1 &b)
{
return a.value == b.value;
}
-template<> struct blender::DefaultHash<Type1> {
- uint32_t operator()(const Type1 &value) const
+} // namespace tests
+
+/* This has to be defined in ::blender namespace. */
+template<> struct DefaultHash<tests::Type1> {
+ uint32_t operator()(const tests::Type1 &value) const
{
return value.value;
}
- uint32_t operator()(const Type2 &value) const
+ uint32_t operator()(const tests::Type2 &value) const
{
return value.value;
}
};
+namespace tests {
+
TEST(set, ContainsAs)
{
Set<Type1> set;
@@ -361,7 +365,7 @@ template<uint N> struct EqualityIntModN {
};
template<uint N> struct HashIntModN {
- uint32_t operator()(uint value) const
+ uint64_t operator()(uint value) const
{
return value % N;
}
@@ -403,16 +407,61 @@ TEST(set, IntrusiveIntKey)
EXPECT_TRUE(set.remove(4));
}
+struct MyKeyType {
+ uint32_t key;
+ int32_t attached_data;
+
+ uint64_t hash() const
+ {
+ return key;
+ }
+
+ friend bool operator==(const MyKeyType &a, const MyKeyType &b)
+ {
+ return a.key == b.key;
+ }
+};
+
+TEST(set, LookupKey)
+{
+ Set<MyKeyType> set;
+ set.add({1, 10});
+ set.add({2, 20});
+ EXPECT_EQ(set.lookup_key({1, 30}).attached_data, 10);
+ EXPECT_EQ(set.lookup_key({2, 0}).attached_data, 20);
+}
+
+TEST(set, LookupKeyDefault)
+{
+ Set<MyKeyType> set;
+ set.add({1, 10});
+ set.add({2, 20});
+
+ MyKeyType fallback{5, 50};
+ EXPECT_EQ(set.lookup_key_default({1, 66}, fallback).attached_data, 10);
+ EXPECT_EQ(set.lookup_key_default({4, 40}, fallback).attached_data, 50);
+}
+
+TEST(set, LookupKeyPtr)
+{
+ Set<MyKeyType> set;
+ set.add({1, 10});
+ set.add({2, 20});
+ EXPECT_EQ(set.lookup_key_ptr({1, 50})->attached_data, 10);
+ EXPECT_EQ(set.lookup_key_ptr({2, 50})->attached_data, 20);
+ EXPECT_EQ(set.lookup_key_ptr({3, 50}), nullptr);
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
#if 0
template<typename SetT>
-BLI_NOINLINE void benchmark_random_ints(StringRef name, uint amount, uint factor)
+BLI_NOINLINE void benchmark_random_ints(StringRef name, int amount, int factor)
{
RNG *rng = BLI_rng_new(0);
Vector<int> values;
- for (uint i = 0; i < amount; i++) {
+ for (int i = 0; i < amount; i++) {
values.append(BLI_rng_get_int(rng) * factor);
}
BLI_rng_free(rng);
@@ -444,12 +493,12 @@ BLI_NOINLINE void benchmark_random_ints(StringRef name, uint amount, uint factor
TEST(set, Benchmark)
{
- for (uint i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
benchmark_random_ints<blender::Set<int>>("blender::Set ", 100000, 1);
benchmark_random_ints<blender::StdUnorderedSetWrapper<int>>("std::unordered_set", 100000, 1);
}
std::cout << "\n";
- for (uint i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
uint32_t factor = (3 << 10);
benchmark_random_ints<blender::Set<int>>("blender::Set ", 100000, factor);
benchmark_random_ints<blender::StdUnorderedSetWrapper<int>>("std::unordered_set", 100000, factor);
@@ -511,3 +560,6 @@ TEST(set, Benchmark)
*/
#endif /* Benchmark */
+
+} // namespace tests
+} // namespace blender
diff --git a/tests/gtests/blenlib/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc
index b122e8efdb4..587497624f4 100644
--- a/tests/gtests/blenlib/BLI_span_test.cc
+++ b/source/blender/blenlib/tests/BLI_span_test.cc
@@ -1,9 +1,11 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_span.hh"
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
TEST(span, FromSmallVector)
{
@@ -195,7 +197,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(), (int64_t)sizeof(a));
EXPECT_EQ(a_span.size_in_bytes(), 40);
}
@@ -282,3 +284,28 @@ TEST(span, CastLargerSize)
EXPECT_EQ(a_span.size(), 4);
EXPECT_EQ(new_a_span.size(), 2);
}
+
+TEST(span, VoidPointerSpan)
+{
+ int a;
+ float b;
+ double c;
+
+ auto func1 = [](Span<void *> span) { EXPECT_EQ(span.size(), 3); };
+ func1({&a, &b, &c});
+}
+
+TEST(span, CopyFrom)
+{
+ std::array<int, 4> src = {5, 6, 7, 8};
+ std::array<int, 4> dst = {1, 2, 3, 4};
+
+ EXPECT_EQ(dst[2], 3);
+ MutableSpan(dst).copy_from(src);
+ EXPECT_EQ(dst[0], 5);
+ EXPECT_EQ(dst[1], 6);
+ EXPECT_EQ(dst[2], 7);
+ EXPECT_EQ(dst[3], 8);
+}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
index 2fb4c11ca83..3572e751b88 100644
--- a/tests/gtests/blenlib/BLI_stack_cxx_test.cc
+++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
@@ -1,9 +1,11 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_stack.hh"
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
#include "testing/testing.h"
-using namespace blender;
+namespace blender::tests {
TEST(stack, DefaultConstructor)
{
@@ -43,7 +45,7 @@ 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(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(stack2.size(), 7);
for (int i = 7; i >= 1; i--) {
EXPECT_EQ(stack2.pop(), i);
@@ -73,7 +75,7 @@ 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(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(stack2.size(), 7);
for (int i = 7; i >= 1; i--) {
EXPECT_EQ(stack2.pop(), i);
@@ -106,19 +108,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));
}
}
@@ -182,3 +184,5 @@ TEST(stack, OveralignedValues)
EXPECT_EQ((uintptr_t)&stack.peek() % 512, 0);
}
}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc
index 5bb0c396288..d08c8a77455 100644
--- a/tests/gtests/blenlib/BLI_string_ref_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc
@@ -1,11 +1,11 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_strict_flags.h"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "testing/testing.h"
-using blender::StringRef;
-using blender::StringRefNull;
-using blender::Vector;
+namespace blender::tests {
TEST(string_ref_null, DefaultConstructor)
{
@@ -273,3 +273,5 @@ TEST(string_ref, Copy)
EXPECT_EQ(dst[6], 0xFF);
EXPECT_EQ(ref, dst);
}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc
index 1f8a830d134..8f3db8d8403 100644
--- a/tests/gtests/blenlib/BLI_vector_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc
@@ -1,8 +1,10 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_strict_flags.h"
#include "BLI_vector_set.hh"
#include "testing/testing.h"
-using blender::VectorSet;
+namespace blender::tests {
TEST(vector_set, DefaultConstructor)
{
@@ -55,7 +57,7 @@ TEST(vector_set, Move)
{
VectorSet<int> set1 = {1, 2, 3};
VectorSet<int> set2 = std::move(set1);
- EXPECT_EQ(set1.size(), 0);
+ EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(set2.size(), 3);
}
@@ -64,7 +66,7 @@ TEST(vector_set, MoveAssignment)
VectorSet<int> set1 = {1, 2, 3};
VectorSet<int> set2 = {};
set2 = std::move(set1);
- EXPECT_EQ(set1.size(), 0);
+ EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(set2.size(), 3);
}
@@ -158,3 +160,5 @@ TEST(vector_set, Remove)
EXPECT_FALSE(set.remove(5));
EXPECT_FALSE(set.contains(5));
}
+
+} // namespace blender::tests
diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 4a04d002580..f72dfc5deb8 100644
--- a/tests/gtests/blenlib/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -1,9 +1,11 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_strict_flags.h"
#include "BLI_vector.hh"
#include "testing/testing.h"
#include <forward_list>
-using namespace blender;
+namespace blender::tests {
TEST(vector, DefaultConstructor)
{
@@ -57,6 +59,18 @@ TEST(vector, InitializerListConstructor)
EXPECT_EQ(vec[3], 6);
}
+TEST(vector, ConvertingConstructor)
+{
+ std::array<float, 5> values = {5.4f, 7.3f, -8.1f, 5.0f, 0.0f};
+ Vector<int> vec = values;
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ(vec[0], 5);
+ EXPECT_EQ(vec[1], 7);
+ EXPECT_EQ(vec[2], -8);
+ EXPECT_EQ(vec[3], 5);
+ EXPECT_EQ(vec[4], 0);
+}
+
struct TestListValue {
TestListValue *next, *prev;
int value;
@@ -153,7 +167,7 @@ TEST(vector, MoveConstructor)
Vector<int> vec1 = {1, 2, 3, 4};
Vector<int> vec2(std::move(vec1));
- EXPECT_EQ(vec1.size(), 0);
+ EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(vec2.size(), 4);
EXPECT_EQ(vec2[0], 1);
EXPECT_EQ(vec2[1], 2);
@@ -166,7 +180,7 @@ 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(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(vec2.size(), 4);
EXPECT_EQ(vec2[0], 1);
EXPECT_EQ(vec2[1], 2);
@@ -179,7 +193,7 @@ 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(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(vec2.size(), 4);
EXPECT_EQ(vec2[2], 3);
}
@@ -189,7 +203,7 @@ 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(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */
EXPECT_EQ(vec2.size(), 4);
EXPECT_EQ(vec2[3], 4);
}
@@ -265,29 +279,6 @@ TEST(vector, ExtendNonDuplicates)
EXPECT_EQ(vec.size(), 5);
}
-TEST(vector, Fill)
-{
- Vector<int> vec(5);
- vec.fill(3);
- EXPECT_EQ(vec.size(), 5);
- EXPECT_EQ(vec[0], 3);
- EXPECT_EQ(vec[1], 3);
- EXPECT_EQ(vec[2], 3);
- EXPECT_EQ(vec[3], 3);
- EXPECT_EQ(vec[4], 3);
-}
-
-TEST(vector, FillIndices)
-{
- Vector<int> vec(5, 0);
- vec.fill_indices({1, 2}, 4);
- EXPECT_EQ(vec[0], 0);
- EXPECT_EQ(vec[1], 4);
- EXPECT_EQ(vec[2], 4);
- EXPECT_EQ(vec[3], 0);
- EXPECT_EQ(vec[4], 0);
-}
-
TEST(vector, Iterator)
{
Vector<int> vec({1, 4, 9, 16});
@@ -305,8 +296,8 @@ TEST(vector, BecomeLarge)
vec.append(i * 5);
}
EXPECT_EQ(vec.size(), 100);
- for (uint i = 0; i < 100; i++) {
- EXPECT_EQ(vec[i], i * 5);
+ for (int i = 0; i < 100; i++) {
+ EXPECT_EQ(vec[i], static_cast<int>(i * 5));
}
}
@@ -339,19 +330,6 @@ TEST(vector, VectorOfVectors_Append)
EXPECT_EQ(vec[1][1], 8);
}
-TEST(vector, VectorOfVectors_Fill)
-{
- Vector<Vector<int>> vec(3);
- vec.fill({4, 5});
-
- EXPECT_EQ(vec[0][0], 4);
- EXPECT_EQ(vec[0][1], 5);
- EXPECT_EQ(vec[1][0], 4);
- EXPECT_EQ(vec[1][1], 5);
- EXPECT_EQ(vec[2][0], 4);
- EXPECT_EQ(vec[2][1], 5);
-}
-
TEST(vector, RemoveLast)
{
Vector<int> vec = {5, 6};
@@ -495,11 +473,11 @@ class TypeConstructMock {
{
}
- TypeConstructMock(const TypeConstructMock &other) : copy_constructed(true)
+ TypeConstructMock(const TypeConstructMock &UNUSED(other)) : copy_constructed(true)
{
}
- TypeConstructMock(TypeConstructMock &&other) : move_constructed(true)
+ TypeConstructMock(TypeConstructMock &&UNUSED(other)) noexcept : move_constructed(true)
{
}
@@ -513,7 +491,7 @@ class TypeConstructMock {
return *this;
}
- TypeConstructMock &operator=(TypeConstructMock &&other)
+ TypeConstructMock &operator=(TypeConstructMock &&other) noexcept
{
if (this == &other) {
return *this;
@@ -636,3 +614,26 @@ TEST(vector, OveralignedValues)
EXPECT_EQ((uintptr_t)&vec.last() % 512, 0);
}
}
+
+TEST(vector, ConstructVoidPointerVector)
+{
+ int a;
+ float b;
+ double c;
+ Vector<void *> vec = {&a, &b, &c};
+ EXPECT_EQ(vec.size(), 3);
+}
+
+TEST(vector, Fill)
+{
+ Vector<int> vec(5);
+ vec.fill(3);
+ EXPECT_EQ(vec.size(), 5u);
+ EXPECT_EQ(vec[0], 3);
+ EXPECT_EQ(vec[1], 3);
+ EXPECT_EQ(vec[2], 3);
+ EXPECT_EQ(vec[3], 3);
+ EXPECT_EQ(vec[4], 3);
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h
index 59116b4eefc..7c9738b67c5 100644
--- a/source/blender/blenloader/BLO_read_write.h
+++ b/source/blender/blenloader/BLO_read_write.h
@@ -180,7 +180,7 @@ bool BLO_write_is_undo(BlendWriter *writer);
void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address);
#define BLO_read_data_address(reader, ptr_p) \
- *(ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p))
+ *((void **)ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p))
typedef void (*BlendReadListFn)(BlendDataReader *reader, void *data);
void BLO_read_list_cb(BlendDataReader *reader, struct ListBase *list, BlendReadListFn callback);
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 8495caa91b5..e4908eb7257 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -48,6 +48,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 */
@@ -69,12 +81,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;
@@ -108,6 +114,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);
@@ -119,7 +131,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/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index 09e2f4bf417..7eab0651d97 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -63,8 +63,8 @@ set(SRC
BLO_blend_defs.h
BLO_blend_validate.h
- BLO_readfile.h
BLO_read_write.h
+ BLO_readfile.h
BLO_undofile.h
BLO_writefile.h
intern/readfile.h
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/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c
index 1309b3e3c33..cb2094d050f 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.c
@@ -56,7 +56,7 @@
#endif
/* local prototypes --------------------- */
-void BLO_blendhandle_print_sizes(BlendHandle *, void *);
+void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp);
/* Access routines used by filesel. */
@@ -390,39 +390,16 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
blo_make_old_idmap_from_main(fd, old_mainlist.first);
}
- /* makes lookup of existing images in old main */
- blo_make_image_pointer_map(fd, oldmain);
-
- /* makes lookup of existing light caches in old main */
- blo_make_scene_pointer_map(fd, oldmain);
-
- /* makes lookup of existing video clips in old main */
- blo_make_movieclip_pointer_map(fd, oldmain);
-
- /* make lookups of existing sound data in old main */
- blo_make_sound_pointer_map(fd, oldmain);
-
- /* make lookups of existing volume data in old main */
- blo_make_volume_pointer_map(fd, oldmain);
-
/* removed packed data from this trick - it's internal data that needs saves */
- bfd = blo_read_file_internal(fd, filename);
-
- /* ensures relinked light caches are not freed */
- blo_end_scene_pointer_map(fd, oldmain);
-
- /* ensures relinked images are not freed */
- blo_end_image_pointer_map(fd, oldmain);
+ /* Store all existing ID caches pointers into a mapping, to allow restoring them into newly
+ * read IDs whenever possible. */
+ blo_cache_storage_init(fd, oldmain);
- /* ensures relinked movie clips are not freed */
- blo_end_movieclip_pointer_map(fd, oldmain);
-
- /* ensures relinked sounds are not freed */
- blo_end_sound_pointer_map(fd, oldmain);
+ bfd = blo_read_file_internal(fd, filename);
- /* ensures relinked volumes are not freed */
- blo_end_volume_pointer_map(fd, oldmain);
+ /* Ensure relinked caches are not freed together with their old IDs. */
+ blo_cache_storage_old_bmain_clear(fd, oldmain);
/* Still in-use libraries have already been moved from oldmain to new mainlist,
* but oldmain itself shall *never* be 'transferred' to new mainlist! */
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 79705385538..b37029726f4 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -105,6 +105,7 @@
#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
+#include "BLI_memarena.h"
#include "BLI_mempool.h"
#include "BLI_threads.h"
@@ -118,6 +119,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"
@@ -126,6 +128,7 @@
#include "BKE_hair.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
+#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
@@ -258,10 +261,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;
@@ -310,7 +313,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>";
}
/* -------------------------------------------------------------------- */
@@ -673,7 +676,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) {
@@ -696,8 +699,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;
@@ -1610,21 +1613,6 @@ void blo_filedata_free(FileData *fd)
if (fd->globmap) {
oldnewmap_free(fd->globmap);
}
- if (fd->imamap) {
- oldnewmap_free(fd->imamap);
- }
- if (fd->movieclipmap) {
- oldnewmap_free(fd->movieclipmap);
- }
- if (fd->scenemap) {
- oldnewmap_free(fd->scenemap);
- }
- if (fd->soundmap) {
- oldnewmap_free(fd->soundmap);
- }
- if (fd->volumemap) {
- oldnewmap_free(fd->volumemap);
- }
if (fd->packedmap) {
oldnewmap_free(fd->packedmap);
}
@@ -1634,6 +1622,7 @@ void blo_filedata_free(FileData *fd)
if (fd->old_idmap != NULL) {
BKE_main_idmap_destroy(fd->old_idmap);
}
+ blo_cache_storage_end(fd);
if (fd->bheadmap) {
MEM_freeN(fd->bheadmap);
}
@@ -1800,51 +1789,6 @@ static void *newglobadr(FileData *fd, const void *adr)
return oldnewmap_lookup_and_inc(fd->globmap, adr, true);
}
-/* used to restore image data after undo */
-static void *newimaadr(FileData *fd, const void *adr)
-{
- if (fd->imamap && adr) {
- return oldnewmap_lookup_and_inc(fd->imamap, adr, true);
- }
- return NULL;
-}
-
-/* used to restore scene data after undo */
-static void *newsceadr(FileData *fd, const void *adr)
-{
- if (fd->scenemap && adr) {
- return oldnewmap_lookup_and_inc(fd->scenemap, adr, true);
- }
- return NULL;
-}
-
-/* used to restore movie clip data after undo */
-static void *newmclipadr(FileData *fd, const void *adr)
-{
- if (fd->movieclipmap && adr) {
- return oldnewmap_lookup_and_inc(fd->movieclipmap, adr, true);
- }
- return NULL;
-}
-
-/* used to restore sound data after undo */
-static void *newsoundadr(FileData *fd, const void *adr)
-{
- if (fd->soundmap && adr) {
- return oldnewmap_lookup_and_inc(fd->soundmap, adr, true);
- }
- return NULL;
-}
-
-/* used to restore volume data after undo */
-static void *newvolumeadr(FileData *fd, const void *adr)
-{
- if (fd->volumemap && adr) {
- return oldnewmap_lookup_and_inc(fd->volumemap, adr, true);
- }
- return NULL;
-}
-
/* used to restore packed data after undo */
static void *newpackedadr(FileData *fd, const void *adr)
{
@@ -1922,266 +1866,6 @@ void blo_clear_proxy_pointers_from_lib(Main *oldmain)
}
}
-void blo_make_scene_pointer_map(FileData *fd, Main *oldmain)
-{
- Scene *sce = oldmain->scenes.first;
-
- fd->scenemap = oldnewmap_new();
-
- for (; sce; sce = sce->id.next) {
- if (sce->eevee.light_cache_data) {
- struct LightCache *light_cache = sce->eevee.light_cache_data;
- oldnewmap_insert(fd->scenemap, light_cache, light_cache, 0);
- }
- }
-}
-
-void blo_end_scene_pointer_map(FileData *fd, Main *oldmain)
-{
- OldNew *entry = fd->scenemap->entries;
- Scene *sce = oldmain->scenes.first;
- int i;
-
- /* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->scenemap->nentries; i++, entry++) {
- if (entry->nr > 0) {
- entry->newp = NULL;
- }
- }
-
- for (; sce; sce = sce->id.next) {
- sce->eevee.light_cache_data = newsceadr(fd, sce->eevee.light_cache_data);
- }
-}
-
-void blo_make_image_pointer_map(FileData *fd, Main *oldmain)
-{
- Image *ima = oldmain->images.first;
- Scene *sce = oldmain->scenes.first;
- int a;
-
- fd->imamap = oldnewmap_new();
-
- for (; ima; ima = ima->id.next) {
- if (ima->cache) {
- oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0);
- }
- for (int eye = 0; eye < 2; eye++) {
- for (a = 0; a < TEXTARGET_COUNT; a++) {
- if (ima->gputexture[a][eye] != NULL) {
- oldnewmap_insert(fd->imamap, ima->gputexture[a][eye], ima->gputexture[a][eye], 0);
- }
- }
- }
- if (ima->rr) {
- oldnewmap_insert(fd->imamap, ima->rr, ima->rr, 0);
- }
- LISTBASE_FOREACH (RenderSlot *, slot, &ima->renderslots) {
- if (slot->render) {
- oldnewmap_insert(fd->imamap, slot->render, slot->render, 0);
- }
- }
- }
- for (; sce; sce = sce->id.next) {
- if (sce->nodetree && sce->nodetree->previews) {
- bNodeInstanceHashIterator iter;
- NODE_INSTANCE_HASH_ITER (iter, sce->nodetree->previews) {
- bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter);
- oldnewmap_insert(fd->imamap, preview, preview, 0);
- }
- }
- }
-}
-
-/* set old main image ibufs to zero if it has been restored */
-/* this works because freeing old main only happens after this call */
-void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
-{
- OldNew *entry = fd->imamap->entries;
- Image *ima = oldmain->images.first;
- Scene *sce = oldmain->scenes.first;
- int i;
-
- /* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->imamap->nentries; i++, entry++) {
- if (entry->nr > 0) {
- entry->newp = NULL;
- }
- }
-
- for (; ima; ima = ima->id.next) {
- ima->cache = newimaadr(fd, ima->cache);
- if (ima->cache == NULL) {
- ima->gpuflag = 0;
- ima->gpuframenr = INT_MAX;
- for (int eye = 0; eye < 2; eye++) {
- for (i = 0; i < TEXTARGET_COUNT; i++) {
- ima->gputexture[i][eye] = NULL;
- }
- }
- ima->rr = NULL;
- }
- LISTBASE_FOREACH (RenderSlot *, slot, &ima->renderslots) {
- slot->render = newimaadr(fd, slot->render);
- }
-
- for (int eye = 0; eye < 2; eye++) {
- for (i = 0; i < TEXTARGET_COUNT; i++) {
- ima->gputexture[i][eye] = newimaadr(fd, ima->gputexture[i][eye]);
- }
- }
- ima->rr = newimaadr(fd, ima->rr);
- }
- for (; sce; sce = sce->id.next) {
- if (sce->nodetree && sce->nodetree->previews) {
- bNodeInstanceHash *new_previews = BKE_node_instance_hash_new("node previews");
- bNodeInstanceHashIterator iter;
-
- /* reconstruct the preview hash, only using remaining pointers */
- NODE_INSTANCE_HASH_ITER (iter, sce->nodetree->previews) {
- bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter);
- if (preview) {
- bNodePreview *new_preview = newimaadr(fd, preview);
- if (new_preview) {
- bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter);
- BKE_node_instance_hash_insert(new_previews, key, new_preview);
- }
- }
- }
- BKE_node_instance_hash_free(sce->nodetree->previews, NULL);
- sce->nodetree->previews = new_previews;
- }
- }
-}
-
-void blo_make_movieclip_pointer_map(FileData *fd, Main *oldmain)
-{
- MovieClip *clip = oldmain->movieclips.first;
- Scene *sce = oldmain->scenes.first;
-
- fd->movieclipmap = oldnewmap_new();
-
- for (; clip; clip = clip->id.next) {
- if (clip->cache) {
- oldnewmap_insert(fd->movieclipmap, clip->cache, clip->cache, 0);
- }
-
- if (clip->tracking.camera.intrinsics) {
- oldnewmap_insert(
- fd->movieclipmap, clip->tracking.camera.intrinsics, clip->tracking.camera.intrinsics, 0);
- }
- }
-
- for (; sce; sce = sce->id.next) {
- if (sce->nodetree) {
- bNode *node;
- for (node = sce->nodetree->nodes.first; node; node = node->next) {
- if (node->type == CMP_NODE_MOVIEDISTORTION) {
- oldnewmap_insert(fd->movieclipmap, node->storage, node->storage, 0);
- }
- }
- }
- }
-}
-
-/* set old main movie clips caches to zero if it has been restored */
-/* this works because freeing old main only happens after this call */
-void blo_end_movieclip_pointer_map(FileData *fd, Main *oldmain)
-{
- OldNew *entry = fd->movieclipmap->entries;
- MovieClip *clip = oldmain->movieclips.first;
- Scene *sce = oldmain->scenes.first;
- int i;
-
- /* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->movieclipmap->nentries; i++, entry++) {
- if (entry->nr > 0) {
- entry->newp = NULL;
- }
- }
-
- for (; clip; clip = clip->id.next) {
- clip->cache = newmclipadr(fd, clip->cache);
- clip->tracking.camera.intrinsics = newmclipadr(fd, clip->tracking.camera.intrinsics);
- BLI_freelistN(&clip->runtime.gputextures);
- }
-
- for (; sce; sce = sce->id.next) {
- if (sce->nodetree) {
- bNode *node;
- for (node = sce->nodetree->nodes.first; node; node = node->next) {
- if (node->type == CMP_NODE_MOVIEDISTORTION) {
- node->storage = newmclipadr(fd, node->storage);
- }
- }
- }
- }
-}
-
-void blo_make_sound_pointer_map(FileData *fd, Main *oldmain)
-{
- bSound *sound = oldmain->sounds.first;
-
- fd->soundmap = oldnewmap_new();
-
- for (; sound; sound = sound->id.next) {
- if (sound->waveform) {
- oldnewmap_insert(fd->soundmap, sound->waveform, sound->waveform, 0);
- }
- }
-}
-
-/* set old main sound caches to zero if it has been restored */
-/* this works because freeing old main only happens after this call */
-void blo_end_sound_pointer_map(FileData *fd, Main *oldmain)
-{
- OldNew *entry = fd->soundmap->entries;
- bSound *sound = oldmain->sounds.first;
- int i;
-
- /* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->soundmap->nentries; i++, entry++) {
- if (entry->nr > 0) {
- entry->newp = NULL;
- }
- }
-
- for (; sound; sound = sound->id.next) {
- sound->waveform = newsoundadr(fd, sound->waveform);
- }
-}
-
-void blo_make_volume_pointer_map(FileData *fd, Main *oldmain)
-{
- fd->volumemap = oldnewmap_new();
-
- Volume *volume = oldmain->volumes.first;
- for (; volume; volume = volume->id.next) {
- if (volume->runtime.grids) {
- oldnewmap_insert(fd->volumemap, volume->runtime.grids, volume->runtime.grids, 0);
- }
- }
-}
-
-/* set old main volume caches to zero if it has been restored */
-/* this works because freeing old main only happens after this call */
-void blo_end_volume_pointer_map(FileData *fd, Main *oldmain)
-{
- OldNew *entry = fd->volumemap->entries;
- Volume *volume = oldmain->volumes.first;
- int i;
-
- /* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->volumemap->nentries; i++, entry++) {
- if (entry->nr > 0)
- entry->newp = NULL;
- }
-
- for (; volume; volume = volume->id.next) {
- volume->runtime.grids = newvolumeadr(fd, volume->runtime.grids);
- }
-}
-
/* XXX disabled this feature - packed files also belong in temp saves and quit.blend,
* to make restore work. */
@@ -2315,6 +1999,149 @@ void blo_make_old_idmap_from_main(FileData *fd, Main *bmain)
fd->old_idmap = BKE_main_idmap_create(bmain, false, NULL, MAIN_IDMAP_TYPE_UUID);
}
+typedef struct BLOCacheStorage {
+ GHash *cache_map;
+ MemArena *memarena;
+} BLOCacheStorage;
+
+/** Register a cache data entry to be preserved when reading some undo memfile. */
+static void blo_cache_storage_entry_register(ID *id,
+ const IDCacheKey *key,
+ void **UNUSED(cache_p),
+ eIDTypeInfoCacheCallbackFlags UNUSED(flags),
+ void *cache_storage_v)
+{
+ BLI_assert(key->id_session_uuid == id->session_uuid);
+ UNUSED_VARS_NDEBUG(id);
+
+ BLOCacheStorage *cache_storage = cache_storage_v;
+ BLI_assert(!BLI_ghash_haskey(cache_storage->cache_map, key));
+
+ IDCacheKey *storage_key = BLI_memarena_alloc(cache_storage->memarena, sizeof(*storage_key));
+ *storage_key = *key;
+ BLI_ghash_insert(cache_storage->cache_map, storage_key, POINTER_FROM_UINT(0));
+}
+
+/** Restore a cache data entry from old ID into new one, when reading some undo memfile. */
+static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id),
+ const IDCacheKey *key,
+ void **cache_p,
+ eIDTypeInfoCacheCallbackFlags flags,
+ void *cache_storage_v)
+{
+ BLOCacheStorage *cache_storage = cache_storage_v;
+
+ if (cache_storage == NULL) {
+ /* In non-undo case, only clear the pointer if it is a purely runtime one.
+ * If it may be stored in a persistent way in the .blend file, direct_link code is responsible
+ * to properly deal with it. */
+ if ((flags & IDTYPE_CACHE_CB_FLAGS_PERSISTENT) == 0) {
+ *cache_p = NULL;
+ }
+ return;
+ }
+
+ void **value = BLI_ghash_lookup_p(cache_storage->cache_map, key);
+ if (value == NULL) {
+ *cache_p = NULL;
+ return;
+ }
+ *value = POINTER_FROM_UINT(POINTER_AS_UINT(*value) + 1);
+ *cache_p = key->cache_v;
+}
+
+/** Clear as needed a cache data entry from old ID, when reading some undo memfile. */
+static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id),
+ const IDCacheKey *key,
+ void **cache_p,
+ eIDTypeInfoCacheCallbackFlags UNUSED(flags),
+ void *cache_storage_v)
+{
+ BLOCacheStorage *cache_storage = cache_storage_v;
+
+ void **value = BLI_ghash_lookup_p(cache_storage->cache_map, key);
+ if (value == NULL) {
+ *cache_p = NULL;
+ return;
+ }
+ /* If that cache has been restored into some new ID, we want to remove it from old one, otherwise
+ * keep it there so that it gets properly freed together with its ID. */
+ *cache_p = POINTER_AS_UINT(*value) != 0 ? NULL : key->cache_v;
+}
+
+void blo_cache_storage_init(FileData *fd, Main *bmain)
+{
+ if (fd->memfile != NULL) {
+ BLI_assert(fd->cache_storage == NULL);
+ fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__);
+ fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ fd->cache_storage->cache_map = BLI_ghash_new(
+ BKE_idtype_cache_key_hash, BKE_idtype_cache_key_cmp, __func__);
+
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ ID *id = lb->first;
+ if (id == NULL) {
+ continue;
+ }
+
+ const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id);
+ if (type_info->foreach_cache == NULL) {
+ continue;
+ }
+
+ FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
+ if (ID_IS_LINKED(id)) {
+ continue;
+ }
+ BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_register, fd->cache_storage);
+ }
+ FOREACH_MAIN_LISTBASE_ID_END;
+ }
+ FOREACH_MAIN_LISTBASE_END;
+ }
+ else {
+ fd->cache_storage = NULL;
+ }
+}
+
+void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old)
+{
+ if (fd->cache_storage != NULL) {
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain_old, lb) {
+ ID *id = lb->first;
+ if (id == NULL) {
+ continue;
+ }
+
+ const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id);
+ if (type_info->foreach_cache == NULL) {
+ continue;
+ }
+
+ FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
+ if (ID_IS_LINKED(id)) {
+ continue;
+ }
+ BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_clear_in_old, fd->cache_storage);
+ }
+ FOREACH_MAIN_LISTBASE_ID_END;
+ }
+ FOREACH_MAIN_LISTBASE_END;
+ }
+}
+
+void blo_cache_storage_end(FileData *fd)
+{
+ if (fd->cache_storage != NULL) {
+ BLI_ghash_free(fd->cache_storage->cache_map, NULL, NULL);
+ BLI_memarena_free(fd->cache_storage->memarena);
+ MEM_freeN(fd->cache_storage);
+ fd->cache_storage = NULL;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -2451,7 +2278,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)
{
@@ -2587,7 +2414,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;
@@ -2596,7 +2423,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);
}
@@ -2607,14 +2434,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;
}
@@ -2654,45 +2481,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)
@@ -2881,58 +2709,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;
@@ -2954,7 +2747,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);
@@ -2975,39 +2768,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);
}
}
@@ -3021,7 +2814,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))
{
}
@@ -3032,7 +2825,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))
{
}
@@ -3071,11 +2864,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);
}
}
}
@@ -3095,17 +2888,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);
}
}
}
@@ -3123,12 +2916,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);
}
}
@@ -3138,7 +2931,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;
@@ -3147,7 +2940,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;
}
@@ -3155,7 +2948,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;
@@ -3174,7 +2967,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;
@@ -3185,7 +2978,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);
}
}
@@ -3285,20 +3078,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);
}
}
}
@@ -3329,29 +3122,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);
}
}
@@ -3393,7 +3186,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;
@@ -3401,7 +3194,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);
}
}
}
@@ -3426,23 +3219,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)
@@ -3480,9 +3273,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))
{
}
@@ -3504,26 +3295,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);
}
}
}
@@ -3560,10 +3351,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);
}
/** \} */
@@ -3572,19 +3366,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:
@@ -3604,33 +3398,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
@@ -3640,7 +3434,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);
}
@@ -3648,9 +3442,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)
@@ -3703,7 +3497,8 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
}
if (node->type == CMP_NODE_MOVIEDISTORTION) {
- node->storage = newmclipadr(reader->fd, node->storage);
+ /* Do nothing, this is runtime cache and hence handled by generic code using
+ * `IDTypeInfo.foreach_cache` callback. */
}
else {
BLO_read_data_address(reader, &node->storage);
@@ -3720,7 +3515,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: {
@@ -3802,28 +3597,8 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree)
BLO_read_data_address(reader, &link->tosock);
}
-#if 0
- if (ntree->previews) {
- bNodeInstanceHash* new_previews = BKE_node_instance_hash_new("node previews");
- bNodeInstanceHashIterator iter;
-
- NODE_INSTANCE_HASH_ITER(iter, ntree->previews) {
- bNodePreview* preview = BKE_node_instance_hash_iterator_get_value(&iter);
- if (preview) {
- bNodePreview* new_preview = newimaadr(fd, preview);
- if (new_preview) {
- bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter);
- BKE_node_instance_hash_insert(new_previews, key, new_preview);
- }
- }
- }
- BKE_node_instance_hash_free(ntree->previews, NULL);
- ntree->previews = new_previews;
- }
-#else
- /* XXX TODO */
+ /* TODO, should be dealt by new generic cache handling of IDs... */
ntree->previews = NULL;
-#endif
/* type verification is in lib-link */
}
@@ -3836,7 +3611,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 */
@@ -3846,10 +3621,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;
@@ -3862,7 +3637,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) {
@@ -3871,7 +3646,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);
@@ -3934,7 +3709,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;
@@ -3945,7 +3720,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;
}
@@ -3967,13 +3742,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;
}
@@ -3986,24 +3761,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);
}
}
@@ -4056,16 +3831,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);
}
}
@@ -4088,9 +3863,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)
@@ -4100,7 +3875,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);
@@ -4122,12 +3897,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)
@@ -4185,13 +3960,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)
@@ -4218,9 +3993,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)
@@ -4238,7 +4013,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))
{
}
@@ -4255,7 +4030,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))
{
}
@@ -4263,7 +4038,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;
@@ -4300,55 +4075,26 @@ 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 *ima)
{
+ /* Images have some kind of 'main' cache, when NULL we should also clear all others. */
+ /* Needs to be done *after* cache pointers are restored (call to
+ * `foreach_cache`/`blo_cache_storage_entry_restore_in_new`), easier for now to do it in
+ * lib_link... */
+ if (ima->cache == NULL) {
+ BKE_image_free_buffers(ima);
+ }
}
static void direct_link_image(BlendDataReader *reader, Image *ima)
{
ImagePackedFile *imapf;
- /* for undo system, pointers could be restored */
- if (reader->fd->imamap) {
- ima->cache = newimaadr(reader->fd, ima->cache);
- }
- else {
- ima->cache = NULL;
- }
-
BLO_read_list(reader, &ima->tiles);
- /* if not restored, we keep the binded opengl index */
- if (!ima->cache) {
- ima->gpuflag = 0;
- ima->gpuframenr = INT_MAX;
- for (int eye = 0; eye < 2; eye++) {
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- ima->gputexture[i][eye] = NULL;
- }
- }
- ima->rr = NULL;
- }
- else {
- for (int eye = 0; eye < 2; eye++) {
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- ima->gputexture[i][eye] = newimaadr(reader->fd, ima->gputexture[i][eye]);
- }
- }
- ima->rr = newimaadr(reader->fd, ima->rr);
- }
-
- /* undo system, try to restore render buffers */
BLO_read_list(reader, &(ima->renderslots));
- if (reader->fd->imamap) {
- LISTBASE_FOREACH (RenderSlot *, slot, &ima->renderslots) {
- slot->render = newimaadr(reader->fd, slot->render);
- }
- }
- else {
- LISTBASE_FOREACH (RenderSlot *, slot, &ima->renderslots) {
- slot->render = NULL;
- }
+ if (reader->fd->memfile == NULL) {
+ /* We reset this last render slot index only when actually reading a file, not for undo. */
ima->last_render_slot = ima->render_slot;
}
@@ -4369,7 +4115,7 @@ static void direct_link_image(BlendDataReader *reader, Image *ima)
ima->preview = direct_link_preview_image(reader, ima->preview);
BLO_read_data_address(reader, &ima->stereo3d_format);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- tile->ok = 1;
+ tile->ok = IMA_OK;
}
}
@@ -4379,22 +4125,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)
@@ -4416,7 +4162,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu)
direct_link_animdata(reader, cu->adt);
/* Protect against integer overflow vulnerability. */
- CLAMP(cu->len_wchar, 0, INT_MAX - 4);
+ CLAMP(cu->len_char32, 0, INT_MAX - 4);
BLO_read_pointer_array(reader, (void **)&cu->mat);
@@ -4473,10 +4219,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)
@@ -4498,18 +4244,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);
}
}
}
@@ -4620,31 +4366,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);
@@ -4652,7 +4398,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 {
@@ -4669,12 +4415,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;
}
}
@@ -4685,8 +4431,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);
}
}
}
@@ -4712,15 +4458,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);
@@ -4751,30 +4497,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;
}
}
@@ -4876,7 +4625,6 @@ static void direct_link_particlesystems(BlendDataReader *reader, ListBase *parti
psys->orig_psys = NULL;
psys->batch_cache = NULL;
}
- return;
}
/** \} */
@@ -4885,21 +4633,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)
@@ -5114,10 +4862,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)
@@ -5142,17 +4890,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) {
@@ -5162,9 +4910,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) {
@@ -5175,9 +4923,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) {
@@ -5187,28 +4935,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"),
@@ -5219,7 +4967,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) {
@@ -5227,7 +4975,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);
@@ -5238,14 +4986,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);
@@ -5270,7 +5018,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
@@ -5280,26 +5028,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);
}
}
@@ -5308,53 +5056,52 @@ 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
}
}
{
- FluidModifierData *mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob,
+ FluidModifierData *fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob,
eModifierType_Fluid);
- if (mmd && (mmd->type == MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
+ if (fmd && (fmd->type == MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
/* Flag for refreshing the simulation after loading */
- mmd->domain->flags |= FLUID_DOMAIN_FILE_LOAD;
+ fmd->domain->flags |= FLUID_DOMAIN_FILE_LOAD;
}
- else if (mmd && (mmd->type == MOD_FLUID_TYPE_FLOW) && mmd->flow) {
- mmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
+ else if (fmd && (fmd->type == MOD_FLUID_TYPE_FLOW) && fmd->flow) {
+ fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
}
- else if (mmd && (mmd->type == MOD_FLUID_TYPE_EFFEC) && mmd->effector) {
- mmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
+ else if (fmd && (fmd->type == MOD_FLUID_TYPE_EFFEC) && fmd->effector) {
+ fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
}
}
/* 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;
@@ -5363,7 +5110,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");
}
}
@@ -5579,24 +5326,17 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
md = modifier_replace_with_fluid(reader->fd, ob, lb, md);
is_allocated = true;
}
+
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+
/* if modifiers disappear, or for upward compatibility */
- if (NULL == BKE_modifier_get_info(md->type)) {
+ if (mti == NULL) {
md->type = eModifierType_None;
}
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->prevCos = NULL;
- }
else if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
@@ -5626,42 +5366,42 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
}
else if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
-
- if (mmd->type == MOD_FLUID_TYPE_DOMAIN) {
- mmd->flow = NULL;
- mmd->effector = NULL;
- BLO_read_data_address(reader, &mmd->domain);
- mmd->domain->mmd = mmd;
-
- mmd->domain->fluid = NULL;
- mmd->domain->fluid_mutex = BLI_rw_mutex_alloc();
- mmd->domain->tex_density = NULL;
- mmd->domain->tex_color = NULL;
- mmd->domain->tex_shadow = NULL;
- mmd->domain->tex_flame = NULL;
- mmd->domain->tex_flame_coba = NULL;
- mmd->domain->tex_coba = NULL;
- mmd->domain->tex_field = NULL;
- mmd->domain->tex_velocity_x = NULL;
- mmd->domain->tex_velocity_y = NULL;
- mmd->domain->tex_velocity_z = NULL;
- mmd->domain->tex_wt = NULL;
- mmd->domain->mesh_velocities = NULL;
- BLO_read_data_address(reader, &mmd->domain->coba);
-
- BLO_read_data_address(reader, &mmd->domain->effector_weights);
- if (!mmd->domain->effector_weights) {
- mmd->domain->effector_weights = BKE_effector_add_weights(NULL);
+ FluidModifierData *fmd = (FluidModifierData *)md;
+
+ if (fmd->type == MOD_FLUID_TYPE_DOMAIN) {
+ fmd->flow = NULL;
+ fmd->effector = NULL;
+ BLO_read_data_address(reader, &fmd->domain);
+ fmd->domain->fmd = fmd;
+
+ fmd->domain->fluid = NULL;
+ fmd->domain->fluid_mutex = BLI_rw_mutex_alloc();
+ fmd->domain->tex_density = NULL;
+ fmd->domain->tex_color = NULL;
+ fmd->domain->tex_shadow = NULL;
+ fmd->domain->tex_flame = NULL;
+ fmd->domain->tex_flame_coba = NULL;
+ fmd->domain->tex_coba = NULL;
+ fmd->domain->tex_field = NULL;
+ fmd->domain->tex_velocity_x = NULL;
+ fmd->domain->tex_velocity_y = NULL;
+ fmd->domain->tex_velocity_z = NULL;
+ fmd->domain->tex_wt = NULL;
+ fmd->domain->mesh_velocities = NULL;
+ BLO_read_data_address(reader, &fmd->domain->coba);
+
+ BLO_read_data_address(reader, &fmd->domain->effector_weights);
+ if (!fmd->domain->effector_weights) {
+ fmd->domain->effector_weights = BKE_effector_add_weights(NULL);
}
direct_link_pointcache_list(
- reader, &(mmd->domain->ptcaches[0]), &(mmd->domain->point_cache[0]), 1);
+ reader, &(fmd->domain->ptcaches[0]), &(fmd->domain->point_cache[0]), 1);
/* Manta sim uses only one cache from now on, so store pointer convert */
- if (mmd->domain->ptcaches[1].first || mmd->domain->point_cache[1]) {
- if (mmd->domain->point_cache[1]) {
- PointCache *cache = BLO_read_get_new_data_address(reader, mmd->domain->point_cache[1]);
+ if (fmd->domain->ptcaches[1].first || fmd->domain->point_cache[1]) {
+ if (fmd->domain->point_cache[1]) {
+ PointCache *cache = BLO_read_get_new_data_address(reader, fmd->domain->point_cache[1]);
if (cache->flag & PTCACHE_FAKE_SMOKE) {
/* Manta-sim/smoke was already saved in "new format" and this cache is a fake one. */
}
@@ -5672,35 +5412,35 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object
}
BKE_ptcache_free(cache);
}
- BLI_listbase_clear(&mmd->domain->ptcaches[1]);
- mmd->domain->point_cache[1] = NULL;
+ BLI_listbase_clear(&fmd->domain->ptcaches[1]);
+ fmd->domain->point_cache[1] = NULL;
}
}
- else if (mmd->type == MOD_FLUID_TYPE_FLOW) {
- mmd->domain = NULL;
- mmd->effector = NULL;
- BLO_read_data_address(reader, &mmd->flow);
- mmd->flow->mmd = mmd;
- mmd->flow->mesh = NULL;
- mmd->flow->verts_old = NULL;
- mmd->flow->numverts = 0;
- BLO_read_data_address(reader, &mmd->flow->psys);
- }
- else if (mmd->type == MOD_FLUID_TYPE_EFFEC) {
- mmd->flow = NULL;
- mmd->domain = NULL;
- BLO_read_data_address(reader, &mmd->effector);
- if (mmd->effector) {
- mmd->effector->mmd = mmd;
- mmd->effector->verts_old = NULL;
- mmd->effector->numverts = 0;
- mmd->effector->mesh = NULL;
+ else if (fmd->type == MOD_FLUID_TYPE_FLOW) {
+ fmd->domain = NULL;
+ fmd->effector = NULL;
+ BLO_read_data_address(reader, &fmd->flow);
+ fmd->flow->fmd = fmd;
+ fmd->flow->mesh = NULL;
+ fmd->flow->verts_old = NULL;
+ fmd->flow->numverts = 0;
+ BLO_read_data_address(reader, &fmd->flow->psys);
+ }
+ else if (fmd->type == MOD_FLUID_TYPE_EFFEC) {
+ fmd->flow = NULL;
+ fmd->domain = NULL;
+ BLO_read_data_address(reader, &fmd->effector);
+ if (fmd->effector) {
+ fmd->effector->fmd = fmd;
+ fmd->effector->verts_old = NULL;
+ fmd->effector->numverts = 0;
+ fmd->effector->mesh = NULL;
}
else {
- mmd->type = 0;
- mmd->flow = NULL;
- mmd->domain = NULL;
- mmd->effector = NULL;
+ fmd->type = 0;
+ fmd->flow = NULL;
+ fmd->domain = NULL;
+ fmd->effector = NULL;
}
}
}
@@ -5736,153 +5476,9 @@ 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_LaplacianDeform) {
- LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md;
-
- BLO_read_float3_array(reader, lmd->total_verts, &lmd->vertexco);
- lmd->cache_system = NULL;
- }
- 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);
}
}
}
@@ -5910,7 +5506,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) {
@@ -5918,7 +5514,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);
}
@@ -5928,7 +5524,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);
}
}
@@ -5937,7 +5533,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);
}
}
@@ -5945,7 +5541,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);
}
}
@@ -5953,7 +5549,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);
}
}
@@ -5961,7 +5557,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);
}
}
@@ -6195,7 +5791,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);
}
}
@@ -6242,39 +5838,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. */
@@ -6288,12 +5884,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);
}
/** \} */
@@ -6313,15 +5909,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
@@ -6352,11 +5948,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);
@@ -6364,25 +5960,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);
}
-
- BKE_collection_parent_relations_rebuild(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);
}
/** \} */
@@ -6403,29 +5997,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);
}
}
}
@@ -6506,76 +6100,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;
@@ -6586,24 +6176,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;
@@ -6611,7 +6202,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);
@@ -6620,17 +6211,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);
}
}
@@ -6638,13 +6229,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);
}
}
@@ -6653,30 +6244,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
@@ -6726,7 +6317,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);
@@ -6768,12 +6359,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);
}
}
}
@@ -6836,23 +6427,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);
}
}
@@ -7051,7 +6642,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 */
@@ -7068,13 +6659,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce)
}
if (reader->fd->memfile) {
- /* If it's undo try to recover the cache. */
- if (reader->fd->scenemap) {
- sce->eevee.light_cache_data = newsceadr(reader->fd, sce->eevee.light_cache_data);
- }
- else {
- sce->eevee.light_cache_data = NULL;
- }
+ /* If it's undo do nothing here, caches are handled by higher-level generic calling code. */
}
else {
/* else try to read the cache from file. */
@@ -7098,18 +6683,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]);
}
}
@@ -7201,6 +6786,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);
}
}
@@ -7516,9 +7102,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));
@@ -7527,11 +7113,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;
}
@@ -7540,14 +7126,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;
}
@@ -7560,23 +7146,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: {
@@ -7585,7 +7171,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: {
@@ -7593,22 +7179,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);
}
@@ -7617,7 +7203,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;
@@ -7625,7 +7211,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 */
@@ -7637,14 +7223,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) {
@@ -7652,7 +7242,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) {
@@ -7681,8 +7271,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:
@@ -7730,10 +7320,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);
}
/** \} */
@@ -7819,21 +7408,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);
}
}
@@ -7845,17 +7434,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);
}
}
@@ -8391,12 +7980,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); */
@@ -8418,12 +8007,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);
@@ -8438,7 +8027,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))
{
}
@@ -8455,8 +8044,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));
}
}
}
@@ -8465,9 +8054,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);
}
}
}
@@ -8479,9 +8068,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)
@@ -8496,9 +8085,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)
@@ -8530,13 +8119,9 @@ static void direct_link_sound(BlendDataReader *reader, bSound *sound)
sound->cache = NULL;
}
- if (reader->fd->soundmap) {
- sound->waveform = newsoundadr(reader->fd, sound->waveform);
+ if (reader->fd->memfile != NULL) {
sound->tags |= SOUND_TAGS_WAVEFORM_NO_RELOAD;
}
- else {
- sound->waveform = NULL;
- }
sound->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock");
BLI_spin_init(sound->spinlock);
@@ -8548,9 +8133,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
}
/** \} */
@@ -8601,20 +8187,6 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip)
BLO_read_data_address(reader, &clip->adt);
- if (reader->fd->movieclipmap) {
- clip->cache = newmclipadr(reader->fd, clip->cache);
- }
- else {
- clip->cache = NULL;
- }
-
- if (reader->fd->movieclipmap) {
- clip->tracking.camera.intrinsics = newmclipadr(reader->fd, clip->tracking.camera.intrinsics);
- }
- else {
- clip->tracking.camera.intrinsics = NULL;
- }
-
direct_link_movieTracks(reader, &tracking->tracks);
direct_link_moviePlaneTracks(reader, &tracking->plane_tracks);
direct_link_movieReconstruction(reader, &tracking->reconstruction);
@@ -8626,6 +8198,10 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip)
clip->tracking_context = NULL;
clip->tracking.stats = NULL;
+ /* TODO we could store those in undo cache storage as well, and preserve them instead of
+ * re-creating them... */
+ BLI_listbase_clear(&clip->runtime.gputextures);
+
/* Needed for proper versioning, will be NULL for all newer files anyway. */
BLO_read_data_address(reader, &clip->tracking.stabilization.rot_track);
@@ -8642,36 +8218,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);
}
}
@@ -8739,12 +8317,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;
@@ -8756,10 +8334,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;
}
@@ -8772,7 +8350,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;
@@ -8781,7 +8359,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;
}
}
@@ -8791,7 +8369,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;
}
}
@@ -8801,7 +8379,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;
}
}
@@ -8809,8 +8387,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);
}
}
}
@@ -8871,51 +8449,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;
}
}
@@ -8929,47 +8507,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;
}
}
@@ -9014,10 +8592,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]);
}
}
@@ -9032,7 +8610,7 @@ static void direct_link_hair(BlendDataReader *reader, Hair *hair)
BKE_hair_update_customdata_pointers(hair);
/* Materials */
- BLO_read_pointer_array(reader, (void **)hair->mat);
+ BLO_read_pointer_array(reader, (void **)&hair->mat);
}
/** \} */
@@ -9041,10 +8619,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]);
}
}
@@ -9067,10 +8645,15 @@ 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)
{
+ /* Needs to be done *after* cache pointers are restored (call to
+ * `foreach_cache`/`blo_cache_storage_entry_restore_in_new`), easier for now to do it in
+ * lib_link... */
+ BKE_volume_init_grids(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]);
}
}
@@ -9080,11 +8663,7 @@ static void direct_link_volume(BlendDataReader *reader, Volume *volume)
direct_link_animdata(reader, volume->adt);
volume->packedfile = direct_link_packedfile(reader, volume->packedfile);
- volume->runtime.grids = (reader->fd->volumemap) ?
- newvolumeadr(reader->fd, volume->runtime.grids) :
- NULL;
volume->runtime.frame = 0;
- BKE_volume_init_grids(volume);
/* materials */
BLO_read_pointer_array(reader, (void **)&volume->mat);
@@ -9096,10 +8675,11 @@ 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 *reader, Simulation *simulation)
{
+ LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
+ BLO_read_id_address(reader, simulation->id.lib, &dependency->id);
+ }
}
static void direct_link_simulation(BlendDataReader *reader, Simulation *simulation)
@@ -9109,16 +8689,15 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati
BLO_read_list(reader, &simulation->states);
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
- switch ((eSimulationStateType)state->type) {
- case SIM_STATE_TYPE_PARTICLES: {
- ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
- direct_link_customdata(reader, &particle_state->attributes, particle_state->tot_particles);
- direct_link_pointcache_list(
- reader, &particle_state->ptcaches, &particle_state->point_cache, 0);
- break;
- };
+ BLO_read_data_address(reader, &state->name);
+ BLO_read_data_address(reader, &state->type);
+ if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) {
+ ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
+ direct_link_customdata(reader, &particle_state->attributes, particle_state->tot_particles);
}
}
+
+ BLO_read_list(reader, &simulation->dependencies);
}
/** \} */
@@ -9260,6 +8839,8 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
return true;
}
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+
/* XXX Very weakly handled currently, see comment in read_libblock() before trying to
* use it for anything new. */
bool success = true;
@@ -9387,6 +8968,12 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
break;
}
+ /* try to restore (when undoing) or clear ID's cache pointers. */
+ if (id_type->foreach_cache != NULL) {
+ BKE_idtype_id_foreach_cache(
+ id, blo_cache_storage_entry_restore_in_new, reader.fd->cache_storage);
+ }
+
return success;
}
@@ -9396,18 +8983,23 @@ static BHead *read_data_into_datamap(FileData *fd, BHead *bhead, const char *all
bhead = blo_bhead_next(fd, bhead);
while (bhead && bhead->code == DATA) {
- void *data;
+ /* The code below is useful for debugging leaks in data read from the blend file.
+ * Without this the messages only tell us what ID-type the memory came from,
+ * eg: `Data from OB len 64`, see #dataname.
+ * With the code below we get the struct-name to help tracking down the leak.
+ * This is kept disabled as the #malloc for the text always leaks memory. */
#if 0
- /* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */
- short* sp = fd->filesdna->structs[bhead->SDNAnr];
- char* tmp = malloc(100);
- allocname = fd->filesdna->types[sp[0]];
- strcpy(tmp, allocname);
- data = read_struct(fd, bhead, tmp);
-#else
- data = read_struct(fd, bhead, allocname);
+ {
+ const short *sp = fd->filesdna->structs[bhead->SDNAnr];
+ allocname = fd->filesdna->types[sp[0]];
+ size_t allocname_size = strlen(allocname) + 1;
+ char *allocname_buf = malloc(allocname_size);
+ memcpy(allocname_buf, allocname, allocname_size);
+ allocname = allocname_buf;
+ }
#endif
+ void *data = read_struct(fd, bhead, allocname);
if (data) {
oldnewmap_insert(fd->datamap, bhead->old, data, 0);
}
@@ -9483,7 +9075,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) {
@@ -9920,8 +9512,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;
@@ -9946,6 +9538,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) {
@@ -9966,7 +9560,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.
@@ -9974,129 +9568,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;
}
@@ -10111,18 +9705,9 @@ static void lib_link_all(FileData *fd, Main *bmain)
* so simpler to just use it directly in this single call. */
BLO_main_validate_shapekeys(bmain, NULL);
- if (fd->memfile != NULL) {
- /* When doing redo, we perform a tremendous amount of esoteric magic tricks to avoid having to
- * re-read all library data-blocks.
- * Unfortunately, that means that we do not clear Collections' parents lists, which then get
- * improperly extended in some cases by lib_link_scene() and lib_link_collection() calls above
- * (when one local collection is parent of linked ones).
- * I do not really see a way to address that issue, besides brute force call below which
- * invalidates and re-creates all parenting relationships between collections. Yet another
- * example of why it is such a bad idea to keep that kind of double-linked relationships info
- * 'permanently' in our data structures... */
- BKE_main_collections_parent_relations_rebuild(bmain);
- }
+ /* We have to rebuild that runtime information *after* all data-blocks have been properly linked.
+ */
+ BKE_main_collections_parent_relations_rebuild(bmain);
#ifndef NDEBUG
/* Double check we do not have any 'need link' tag remaining, this should never be the case once
@@ -10580,7 +10165,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);
@@ -10589,7 +10174,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;
}
@@ -10600,7 +10185,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;
@@ -10637,7 +10222,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
}
@@ -10675,26 +10260,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;
@@ -10704,7 +10289,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;
}
@@ -10712,7 +10297,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;
@@ -10725,54 +10310,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;
@@ -10780,84 +10365,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;
@@ -10865,39 +10450,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) {
@@ -10908,58 +10493,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:
@@ -10980,155 +10565,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;
@@ -11137,25 +10712,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);
}
}
@@ -11164,298 +10739,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;
@@ -11468,98 +11013,101 @@ 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);
+ }
+ LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
+ BLO_expand(expander, dependency->id);
}
}
@@ -11588,6 +11136,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;
@@ -11596,104 +11146,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;
@@ -12041,10 +11591,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(
@@ -12083,13 +11629,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)
{
@@ -12123,7 +11669,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,
@@ -12149,10 +11700,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);
@@ -12204,7 +11755,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);
@@ -12299,7 +11850,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));
}
@@ -12317,7 +11868,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...). */
@@ -12420,22 +11971,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) {
@@ -12472,7 +12023,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;
@@ -12504,7 +12055,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. */
@@ -12554,10 +12105,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) {
@@ -12698,7 +12249,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/readfile.h b/source/blender/blenloader/intern/readfile.h
index f698d642e33..f8c91c77634 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -30,6 +30,7 @@
#include "DNA_windowmanager_types.h" /* for ReportType */
#include "zlib.h"
+struct BLOCacheStorage;
struct GSet;
struct IDNameLib_Map;
struct Key;
@@ -115,12 +116,8 @@ typedef struct FileData {
struct OldNewMap *datamap;
struct OldNewMap *globmap;
struct OldNewMap *libmap;
- struct OldNewMap *imamap;
- struct OldNewMap *movieclipmap;
- struct OldNewMap *scenemap;
- struct OldNewMap *soundmap;
- struct OldNewMap *volumemap;
struct OldNewMap *packedmap;
+ struct BLOCacheStorage *cache_storage;
struct BHeadSort *bheadmap;
int tot_bheadmap;
@@ -152,21 +149,15 @@ FileData *blo_filedata_from_memfile(struct MemFile *memfile,
struct ReportList *reports);
void blo_clear_proxy_pointers_from_lib(struct Main *oldmain);
-void blo_make_image_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_end_image_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_make_scene_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_end_scene_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_make_movieclip_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_end_movieclip_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_make_sound_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_end_sound_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_make_volume_pointer_map(FileData *fd, struct Main *oldmain);
-void blo_end_volume_pointer_map(FileData *fd, struct Main *oldmain);
void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain);
void blo_end_packed_pointer_map(FileData *fd, struct Main *oldmain);
void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd);
void blo_make_old_idmap_from_main(FileData *fd, struct Main *bmain);
+void blo_cache_storage_init(FileData *fd, struct Main *bmain);
+void blo_cache_storage_old_bmain_clear(FileData *fd, struct Main *bmain_old);
+void blo_cache_storage_end(FileData *fd);
+
void blo_filedata_free(FileData *fd);
BHead *blo_bhead_first(FileData *fd);
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index eaeef0d52c1..3ed59a0baa1 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;
@@ -1793,18 +1793,18 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
- mmd->domain->vorticity = 2.0f;
- mmd->domain->time_scale = 1.0f;
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
+ fmd->domain->vorticity = 2.0f;
+ fmd->domain->time_scale = 1.0f;
- if (!(mmd->domain->flags & (1 << 4))) {
+ if (!(fmd->domain->flags & (1 << 4))) {
continue;
}
/* delete old MOD_SMOKE_INITVELOCITY flag */
- mmd->domain->flags &= ~(1 << 4);
+ fmd->domain->flags &= ~(1 << 4);
/* for now just add it to all flow objects in the scene */
{
@@ -1813,18 +1813,18 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
ModifierData *md2;
for (md2 = ob2->modifiers.first; md2; md2 = md2->next) {
if (md2->type == eModifierType_Fluid) {
- FluidModifierData *mmd2 = (FluidModifierData *)md2;
+ FluidModifierData *fmd2 = (FluidModifierData *)md2;
- if ((mmd2->type & MOD_FLUID_TYPE_FLOW) && mmd2->flow) {
- mmd2->flow->flags |= FLUID_FLOW_INITVELOCITY;
+ if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
+ fmd2->flow->flags |= FLUID_FLOW_INITVELOCITY;
}
}
}
}
}
}
- else if ((mmd->type & MOD_FLUID_TYPE_FLOW) && mmd->flow) {
- mmd->flow->vel_multi = 1.0f;
+ else if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) {
+ fmd->flow->vel_multi = 1.0f;
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 98e10bef470..5e91fea3e20 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -1310,11 +1310,11 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
- int maxres = max_iii(mmd->domain->res[0], mmd->domain->res[1], mmd->domain->res[2]);
- mmd->domain->scale = mmd->domain->dx * maxres;
- mmd->domain->dx = 1.0f / mmd->domain->scale;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
+ int maxres = max_iii(fmd->domain->res[0], fmd->domain->res[1], fmd->domain->res[2]);
+ fmd->domain->scale = fmd->domain->dx * maxres;
+ fmd->domain->dx = 1.0f / fmd->domain->scale;
}
}
}
@@ -1628,30 +1628,30 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
/* keep branch saves if possible */
- if (!mmd->domain->flame_max_temp) {
- mmd->domain->burning_rate = 0.75f;
- mmd->domain->flame_smoke = 1.0f;
- mmd->domain->flame_vorticity = 0.5f;
- mmd->domain->flame_ignition = 1.25f;
- mmd->domain->flame_max_temp = 1.75f;
- mmd->domain->adapt_threshold = 0.02f;
- mmd->domain->adapt_margin = 4;
- mmd->domain->flame_smoke_color[0] = 0.7f;
- mmd->domain->flame_smoke_color[1] = 0.7f;
- mmd->domain->flame_smoke_color[2] = 0.7f;
+ if (!fmd->domain->flame_max_temp) {
+ fmd->domain->burning_rate = 0.75f;
+ fmd->domain->flame_smoke = 1.0f;
+ fmd->domain->flame_vorticity = 0.5f;
+ fmd->domain->flame_ignition = 1.25f;
+ fmd->domain->flame_max_temp = 1.75f;
+ fmd->domain->adapt_threshold = 0.02f;
+ fmd->domain->adapt_margin = 4;
+ fmd->domain->flame_smoke_color[0] = 0.7f;
+ fmd->domain->flame_smoke_color[1] = 0.7f;
+ fmd->domain->flame_smoke_color[2] = 0.7f;
}
}
- else if ((mmd->type & MOD_FLUID_TYPE_FLOW) && mmd->flow) {
- if (!mmd->flow->texture_size) {
- mmd->flow->fuel_amount = 1.0;
- mmd->flow->surface_distance = 1.5;
- mmd->flow->color[0] = 0.7f;
- mmd->flow->color[1] = 0.7f;
- mmd->flow->color[2] = 0.7f;
- mmd->flow->texture_size = 1.0f;
+ else if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) {
+ if (!fmd->flow->texture_size) {
+ fmd->flow->fuel_amount = 1.0;
+ fmd->flow->surface_distance = 1.5;
+ fmd->flow->color[0] = 0.7f;
+ fmd->flow->color[1] = 0.7f;
+ fmd->flow->color[2] = 0.7f;
+ fmd->flow->texture_size = 1.0f;
}
}
}
@@ -2158,13 +2158,13 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
- if (mmd->domain->flags & FLUID_DOMAIN_USE_HIGH_SMOOTH) {
- mmd->domain->highres_sampling = SM_HRES_LINEAR;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
+ if (fmd->domain->flags & FLUID_DOMAIN_USE_HIGH_SMOOTH) {
+ fmd->domain->highres_sampling = SM_HRES_LINEAR;
}
else {
- mmd->domain->highres_sampling = SM_HRES_NEAREST;
+ fmd->domain->highres_sampling = SM_HRES_NEAREST;
}
}
}
@@ -2225,10 +2225,10 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if ((mmd->type & MOD_FLUID_TYPE_FLOW) && mmd->flow) {
- if (!mmd->flow->particle_size) {
- mmd->flow->particle_size = 1.0f;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if ((fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) {
+ if (!fmd->flow->particle_size) {
+ fmd->flow->particle_size = 1.0f;
}
}
}
@@ -2520,7 +2520,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (cu = bmain->curves.first; cu; cu = cu->id.next) {
if (cu->str) {
- cu->len_wchar = BLI_strlen_utf8(cu->str);
+ cu->len_char32 = BLI_strlen_utf8(cu->str);
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 2a468026ac5..521fc4b9b82 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;
}
}
@@ -1470,11 +1470,11 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if (mmd->domain) {
- mmd->domain->slice_per_voxel = 5.0f;
- mmd->domain->slice_depth = 0.5f;
- mmd->domain->display_thickness = 1.0f;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if (fmd->domain) {
+ fmd->domain->slice_per_voxel = 5.0f;
+ fmd->domain->slice_depth = 0.5f;
+ fmd->domain->display_thickness = 1.0f;
}
}
}
@@ -1640,9 +1640,9 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if (mmd->domain) {
- mmd->domain->clipping = 1e-3f;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if (fmd->domain) {
+ fmd->domain->clipping = 1e-3f;
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 6211c58d7d4..111ac728cc3 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;
+ }
+ }
}
}
@@ -3474,7 +3492,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) {
@@ -5075,6 +5093,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..b6caa018756 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -21,19 +21,23 @@
#define DNA_DEPRECATED_ALLOW
#include "BLI_listbase.h"
+#include "BLI_math.h"
#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"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_node.h"
#include "BLO_readfile.h"
#include "readfile.h"
@@ -192,6 +196,14 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
* \note Keep this message at the bottom of the function.
*/
{
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_cycles_fix(bmain, collection)) {
+ printf(
+ "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n"
+ "You may have to reconstruct your View Layers...\n",
+ collection->id.name);
+ }
+ }
/* Keep this block, even when empty. */
}
}
@@ -253,6 +265,125 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Initialize parameters of the new Nishita sky model. */
+ if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_size")) {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_SHADER) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == SH_NODE_TEX_SKY && node->storage) {
+ NodeTexSky *tex = (NodeTexSky *)node->storage;
+ tex->sun_disc = true;
+ tex->sun_size = DEG2RADF(0.545);
+ tex->sun_elevation = M_PI_2;
+ tex->sun_rotation = 0.0f;
+ tex->altitude = 0.0f;
+ tex->air_density = 1.0f;
+ tex->dust_density = 1.0f;
+ tex->ozone_density = 1.0f;
+ }
+ }
+ }
+ }
+ FOREACH_NODETREE_END;
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 290, 6)) {
+ /* 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) {
+ if (md->mode & eModifierMode_Expanded_DEPRECATED) {
+ md->ui_expand_flag = 1;
+ }
+ else {
+ md->ui_expand_flag = 0;
+ }
+ }
+ }
+ }
+
+ /* 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;
+ }
+ }
+ }
+ }
+
+ /* Change ocean modifier values from [0, 10] to [0, 1] ranges. */
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Ocean) {
+ OceanModifierData *omd = (OceanModifierData *)md;
+ omd->wave_alignment *= 0.1f;
+ omd->sharpen_peak_jonswap *= 0.1f;
+ }
+ }
+ }
}
/**
@@ -267,14 +398,31 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Keep this block, even when empty. */
- if (!DNA_struct_elem_find(fd->filesdna, "ModifierData", "short", "ui_expand_flag")) {
+ /* Initialize additional parameter of the Nishita sky model and change altitude unit. */
+ if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_intensity")) {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_SHADER) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == SH_NODE_TEX_SKY && node->storage) {
+ NodeTexSky *tex = (NodeTexSky *)node->storage;
+ tex->sun_intensity = 1.0f;
+ tex->altitude *= 0.001f;
+ }
+ }
+ }
+ }
+ FOREACH_NODETREE_END;
+ }
+
+ /* Refactor bevel affect type to use an enum. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BevelModifierData", "char", "affect_type")) {
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->mode & eModifierMode_Expanded_DEPRECATED) {
- md->ui_expand_flag = 1;
- }
- else {
- md->ui_expand_flag = 0;
+ if (md->type == eModifierType_Bevel) {
+ BevelModifierData *bmd = (BevelModifierData *)md;
+ const bool use_vertex_bevel = bmd->flags & MOD_BEVEL_VERT_DEPRECATED;
+ bmd->affect_type = use_vertex_bevel ? MOD_BEVEL_AFFECT_VERTICES :
+ MOD_BEVEL_AFFECT_EDGES;
}
}
}
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..7f75c0100b8 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -315,7 +315,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
copy_v2_fl2(scene->safe_areas.title, 0.1f, 0.05f);
copy_v2_fl2(scene->safe_areas.action, 0.035f, 0.035f);
- /* Change default cubemap quality. */
+ /* Change default cube-map quality. */
scene->eevee.gi_filter_quality = 3.0f;
/* Enable Soft Shadows by default. */
@@ -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..44c7c35e47d 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -579,7 +579,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
}
if (bmain->versionfile <= 109) {
- /* new variable: gridlines */
+ /* New variable: `gridlines`. */
bScreen *screen = bmain->screens.first;
while (screen) {
ScrArea *area = screen->areabase.first;
@@ -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);
}
}
}
@@ -1339,7 +1339,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
arm = blo_do_versions_newlibadr(fd, lib, ob->data);
enum { ARM_DRAWXRAY = (1 << 1) };
if (arm->flag & ARM_DRAWXRAY) {
- ob->dtx |= OB_DRAWXRAY;
+ ob->dtx |= OB_DRAW_IN_FRONT;
}
}
else if (ob->type == OB_MESH) {
@@ -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/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 1b0e41ec54a..e2dc27d7e88 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -366,7 +366,7 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef)
}
if (!USER_VERSION_ATLEAST(250, 0)) {
/* adjust grease-pencil distances */
- userdef->gp_manhattendist = 1;
+ userdef->gp_manhattandist = 1;
userdef->gp_euclideandist = 2;
/* adjust default interpolation for new IPO-curves */
@@ -753,6 +753,10 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef)
userdef->transopts &= ~USER_DOTRANSLATE_DEPRECATED;
}
+ if (!USER_VERSION_ATLEAST(290, 7)) {
+ userdef->statusbar_flag = STATUSBAR_SHOW_VERSION;
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index a3102200411..3db2fada85f 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
@@ -967,26 +969,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) {
@@ -1087,7 +1069,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;
@@ -1101,11 +1083,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 */
@@ -1376,6 +1358,7 @@ static const char *ptcache_data_struct[] = {
static const char *ptcache_extra_struct[] = {
"",
"ParticleSpring",
+ "vec3f",
};
static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches)
{
@@ -1435,13 +1418,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) {
@@ -1678,16 +1661,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);
@@ -1696,37 +1670,37 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase)
write_pointcaches(writer, &clmd->ptcaches);
}
else if (md->type == eModifierType_Fluid) {
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
- BLO_write_struct(writer, FluidDomainSettings, mmd->domain);
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ BLO_write_struct(writer, FluidDomainSettings, fmd->domain);
- if (mmd->domain) {
- write_pointcaches(writer, &(mmd->domain->ptcaches[0]));
+ if (fmd->domain) {
+ write_pointcaches(writer, &(fmd->domain->ptcaches[0]));
/* create fake pointcache so that old blender versions can read it */
- mmd->domain->point_cache[1] = BKE_ptcache_add(&mmd->domain->ptcaches[1]);
- mmd->domain->point_cache[1]->flag |= PTCACHE_DISK_CACHE | PTCACHE_FAKE_SMOKE;
- mmd->domain->point_cache[1]->step = 1;
+ fmd->domain->point_cache[1] = BKE_ptcache_add(&fmd->domain->ptcaches[1]);
+ fmd->domain->point_cache[1]->flag |= PTCACHE_DISK_CACHE | PTCACHE_FAKE_SMOKE;
+ fmd->domain->point_cache[1]->step = 1;
- write_pointcaches(writer, &(mmd->domain->ptcaches[1]));
+ write_pointcaches(writer, &(fmd->domain->ptcaches[1]));
- if (mmd->domain->coba) {
- BLO_write_struct(writer, ColorBand, mmd->domain->coba);
+ if (fmd->domain->coba) {
+ BLO_write_struct(writer, ColorBand, fmd->domain->coba);
}
/* cleanup the fake pointcache */
- BKE_ptcache_free_list(&mmd->domain->ptcaches[1]);
- mmd->domain->point_cache[1] = NULL;
+ BKE_ptcache_free_list(&fmd->domain->ptcaches[1]);
+ fmd->domain->point_cache[1] = NULL;
- BLO_write_struct(writer, EffectorWeights, mmd->domain->effector_weights);
+ BLO_write_struct(writer, EffectorWeights, fmd->domain->effector_weights);
}
}
- else if (mmd->type & MOD_FLUID_TYPE_FLOW) {
- BLO_write_struct(writer, FluidFlowSettings, mmd->flow);
+ else if (fmd->type & MOD_FLUID_TYPE_FLOW) {
+ BLO_write_struct(writer, FluidFlowSettings, fmd->flow);
}
- else if (mmd->type & MOD_FLUID_TYPE_EFFEC) {
- BLO_write_struct(writer, FluidEffectorSettings, mmd->effector);
+ else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
+ BLO_write_struct(writer, FluidEffectorSettings, fmd->effector);
}
}
else if (md->type == eModifierType_Fluidsim) {
@@ -1769,74 +1743,9 @@ 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_LaplacianDeform) {
- LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md;
-
- BLO_write_float3_array(writer, lmd->total_verts, lmd->vertexco);
- }
- 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);
}
}
}
@@ -1861,21 +1770,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) {
@@ -1884,25 +1793,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);
}
}
}
@@ -2098,7 +2007,7 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address)
if (cu->vfont) {
BLO_write_raw(writer, cu->len + 1, cu->str);
- BLO_write_struct_array(writer, CharInfo, cu->len_wchar + 1, cu->strinfo);
+ BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo);
BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb);
}
else {
@@ -2457,7 +2366,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. */
@@ -2514,12 +2423,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 {
@@ -2531,7 +2440,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);
}
}
@@ -2545,7 +2454,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);
}
@@ -2662,19 +2571,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);
@@ -2816,7 +2725,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);
@@ -3230,8 +3139,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)) {
@@ -3320,38 +3229,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) {
@@ -3612,28 +3521,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;
}
}
@@ -3681,25 +3592,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;
}
}
@@ -3918,28 +3832,37 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const
}
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
- switch ((eSimulationStateType)state->type) {
- case SIM_STATE_TYPE_PARTICLES: {
- ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
- BLO_write_struct(writer, ParticleSimulationState, particle_state);
-
- CustomDataLayer *layers = NULL;
- CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_file_write_prepare(
- &particle_state->attributes, &layers, layers_buff, ARRAY_SIZE(layers_buff));
-
- write_customdata(writer,
- &simulation->id,
- particle_state->tot_particles,
- &particle_state->attributes,
- layers,
- CD_MASK_ALL);
-
- write_pointcaches(writer, &particle_state->ptcaches);
- break;
+ BLO_write_string(writer, state->name);
+ BLO_write_string(writer, state->type);
+ /* TODO: Decentralize this part. */
+ if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) {
+ ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
+ BLO_write_struct(writer, ParticleSimulationState, particle_state);
+
+ CustomDataLayer *layers = NULL;
+ CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_file_write_prepare(
+ &particle_state->attributes, &layers, layers_buff, ARRAY_SIZE(layers_buff));
+
+ write_customdata(writer,
+ &simulation->id,
+ particle_state->tot_particles,
+ &particle_state->attributes,
+ layers,
+ CD_MASK_ALL);
+
+ if (layers != NULL && layers != layers_buff) {
+ MEM_freeN(layers);
}
}
+ else if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER)) {
+ ParticleMeshEmitterSimulationState *emitter_state = (ParticleMeshEmitterSimulationState *)
+ state;
+ BLO_write_struct(writer, ParticleMeshEmitterSimulationState, emitter_state);
+ }
}
+
+ BLO_write_struct_list(writer, SimulationDependency, &simulation->dependencies);
}
}
@@ -3993,7 +3916,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);
}
}
@@ -4008,7 +3931,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);
@@ -4095,6 +4018,7 @@ static bool write_file_handle(Main *mainvar,
MemFile *compare,
MemFile *current,
int write_flags,
+ bool use_userdef,
const BlendThumbnail *thumb)
{
BHead bhead;
@@ -4155,7 +4079,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);
@@ -4348,7 +4273,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);
}
@@ -4421,14 +4346,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);
@@ -4458,7 +4389,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));
@@ -4468,29 +4407,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);
@@ -4508,7 +4467,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 @)");
@@ -4534,9 +4493,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/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 7a389c63abd..fca411e594f 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -118,6 +118,8 @@ set(SRC
intern/bmesh_query.c
intern/bmesh_query.h
intern/bmesh_query_inline.h
+ intern/bmesh_query_uv.c
+ intern/bmesh_query_uv.h
intern/bmesh_structure.c
intern/bmesh_structure.h
intern/bmesh_structure_inline.h
@@ -151,6 +153,10 @@ set(SRC
tools/bmesh_path.h
tools/bmesh_path_region.c
tools/bmesh_path_region.h
+ tools/bmesh_path_uv.c
+ tools/bmesh_path_uv.h
+ tools/bmesh_path_region_uv.c
+ tools/bmesh_path_region_uv.h
tools/bmesh_region_match.c
tools/bmesh_region_match.h
tools/bmesh_separate.c
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index c0791e6fdbc..c1f4b9daf27 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -223,6 +223,7 @@ extern "C" {
#include "intern/bmesh_polygon.h"
#include "intern/bmesh_polygon_edgenet.h"
#include "intern/bmesh_query.h"
+#include "intern/bmesh_query_uv.h"
#include "intern/bmesh_walkers.h"
#include "intern/bmesh_inline.h"
diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h
index d0e91d033fb..4593f34792b 100644
--- a/source/blender/bmesh/bmesh_tools.h
+++ b/source/blender/bmesh/bmesh_tools.h
@@ -36,6 +36,8 @@ extern "C" {
#include "tools/bmesh_edgesplit.h"
#include "tools/bmesh_path.h"
#include "tools/bmesh_path_region.h"
+#include "tools/bmesh_path_region_uv.h"
+#include "tools/bmesh_path_uv.h"
#include "tools/bmesh_region_match.h"
#include "tools/bmesh_separate.h"
#include "tools/bmesh_triangulate.h"
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index 8f55c14c681..4e9775bcfa7 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -101,6 +101,7 @@ BMVert *BM_vert_create(BMesh *bm,
/* may add to middle of the pool */
bm->elem_index_dirty |= BM_VERT;
bm->elem_table_dirty |= BM_VERT;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
bm->totvert++;
@@ -190,6 +191,7 @@ BMEdge *BM_edge_create(
/* may add to middle of the pool */
bm->elem_index_dirty |= BM_EDGE;
bm->elem_table_dirty |= BM_EDGE;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
bm->totedge++;
@@ -259,6 +261,7 @@ static BMLoop *bm_loop_create(BMesh *bm,
/* may add to middle of the pool */
bm->elem_index_dirty |= BM_LOOP;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
bm->totloop++;
@@ -402,6 +405,7 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm)
/* may add to middle of the pool */
bm->elem_index_dirty |= BM_FACE;
bm->elem_table_dirty |= BM_FACE;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
bm->totface++;
@@ -748,6 +752,7 @@ static void bm_kill_only_vert(BMesh *bm, BMVert *v)
bm->totvert--;
bm->elem_index_dirty |= BM_VERT;
bm->elem_table_dirty |= BM_VERT;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BM_select_history_remove(bm, v);
@@ -770,6 +775,7 @@ static void bm_kill_only_edge(BMesh *bm, BMEdge *e)
bm->totedge--;
bm->elem_index_dirty |= BM_EDGE;
bm->elem_table_dirty |= BM_EDGE;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BM_select_history_remove(bm, (BMElem *)e);
@@ -796,6 +802,7 @@ static void bm_kill_only_face(BMesh *bm, BMFace *f)
bm->totface--;
bm->elem_index_dirty |= BM_FACE;
bm->elem_table_dirty |= BM_FACE;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BM_select_history_remove(bm, (BMElem *)f);
@@ -817,6 +824,8 @@ static void bm_kill_only_loop(BMesh *bm, BMLoop *l)
{
bm->totloop--;
bm->elem_index_dirty |= BM_LOOP;
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+
if (l->head.data) {
CustomData_bmesh_free_block(&bm->ldata, &l->head.data);
}
@@ -1826,117 +1835,116 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
if (BM_verts_in_edge(v_kill, v_target, e_old)) {
return NULL;
}
- else {
- BMEdge *e_splice;
- BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
- BMLoop *l_kill_next;
+
+ BMEdge *e_splice;
+ BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
+ BMLoop *l_kill_next;
#ifndef NDEBUG
- /* For verification later, count valence of 'v_old' and 'v_target' */
- valence1 = bmesh_disk_count(v_old);
- valence2 = bmesh_disk_count(v_target);
+ /* For verification later, count valence of 'v_old' and 'v_target' */
+ valence1 = bmesh_disk_count(v_old);
+ valence2 = bmesh_disk_count(v_target);
#endif
- if (check_edge_double) {
- e_splice = BM_edge_exists(v_target, v_old);
- }
+ if (check_edge_double) {
+ e_splice = BM_edge_exists(v_target, v_old);
+ }
- bmesh_disk_vert_replace(e_old, v_target, v_kill);
+ bmesh_disk_vert_replace(e_old, v_target, v_kill);
- /* remove e_kill from 'v_target's disk cycle */
- bmesh_disk_edge_remove(e_kill, v_target);
+ /* remove e_kill from 'v_target's disk cycle */
+ bmesh_disk_edge_remove(e_kill, v_target);
#ifndef NDEBUG
- /* deal with radial cycle of e_kill */
- radlen = bmesh_radial_length(e_kill->l);
+ /* deal with radial cycle of e_kill */
+ radlen = bmesh_radial_length(e_kill->l);
#endif
- if (e_kill->l) {
+ if (e_kill->l) {
- /* fix the neighboring loops of all loops in e_kill's radial cycle */
- l_kill = e_kill->l;
- do {
- /* relink loops and fix vertex pointer */
- if (l_kill->next->v == v_kill) {
- l_kill->next->v = v_target;
- }
+ /* fix the neighboring loops of all loops in e_kill's radial cycle */
+ l_kill = e_kill->l;
+ do {
+ /* relink loops and fix vertex pointer */
+ if (l_kill->next->v == v_kill) {
+ l_kill->next->v = v_target;
+ }
- l_kill->next->prev = l_kill->prev;
- l_kill->prev->next = l_kill->next;
- if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
- BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
- }
+ l_kill->next->prev = l_kill->prev;
+ l_kill->prev->next = l_kill->next;
+ if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
+ BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
+ }
- /* fix len attribute of face */
- l_kill->f->len--;
- if (kill_degenerate_faces) {
- if (l_kill->f->len < 3) {
- BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
- }
+ /* fix len attribute of face */
+ l_kill->f->len--;
+ if (kill_degenerate_faces) {
+ if (l_kill->f->len < 3) {
+ BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
}
- l_kill_next = l_kill->radial_next;
+ }
+ l_kill_next = l_kill->radial_next;
- bm_kill_only_loop(bm, l_kill);
+ bm_kill_only_loop(bm, l_kill);
- } while ((l_kill = l_kill_next) != e_kill->l);
- /* `e_kill->l` is invalid but the edge is freed next. */
+ } while ((l_kill = l_kill_next) != e_kill->l);
+ /* `e_kill->l` is invalid but the edge is freed next. */
#ifndef NDEBUG
- /* Validate radial cycle of e_old */
- edok = bmesh_radial_validate(radlen, e_old->l);
- BMESH_ASSERT(edok != false);
+ /* Validate radial cycle of e_old */
+ edok = bmesh_radial_validate(radlen, e_old->l);
+ BMESH_ASSERT(edok != false);
#endif
- }
- /* deallocate edge */
- bm_kill_only_edge(bm, e_kill);
+ }
+ /* deallocate edge */
+ bm_kill_only_edge(bm, e_kill);
- /* deallocate vertex */
- if (do_del) {
- bm_kill_only_vert(bm, v_kill);
- }
- else {
- v_kill->e = NULL;
- }
+ /* deallocate vertex */
+ if (do_del) {
+ bm_kill_only_vert(bm, v_kill);
+ }
+ else {
+ v_kill->e = NULL;
+ }
#ifndef NDEBUG
- /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */
- edok = bmesh_disk_validate(valence1, v_old->e, v_old);
+ /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */
+ edok = bmesh_disk_validate(valence1, v_old->e, v_old);
+ BMESH_ASSERT(edok != false);
+ edok = bmesh_disk_validate(valence2, v_target->e, v_target);
+ BMESH_ASSERT(edok != false);
+
+ /* Validate loop cycle of all faces attached to 'e_old' */
+ for (i = 0, l = e_old->l; i < radlen; i++, l = l->radial_next) {
+ BMESH_ASSERT(l->e == e_old);
+ edok = BM_verts_in_edge(l->v, l->next->v, e_old);
BMESH_ASSERT(edok != false);
- edok = bmesh_disk_validate(valence2, v_target->e, v_target);
+ edok = bmesh_loop_validate(l->f);
BMESH_ASSERT(edok != false);
- /* Validate loop cycle of all faces attached to 'e_old' */
- for (i = 0, l = e_old->l; i < radlen; i++, l = l->radial_next) {
- BMESH_ASSERT(l->e == e_old);
- edok = BM_verts_in_edge(l->v, l->next->v, e_old);
- BMESH_ASSERT(edok != false);
- edok = bmesh_loop_validate(l->f);
- BMESH_ASSERT(edok != false);
-
- BM_CHECK_ELEMENT(l);
- BM_CHECK_ELEMENT(l->v);
- BM_CHECK_ELEMENT(l->e);
- BM_CHECK_ELEMENT(l->f);
- }
+ BM_CHECK_ELEMENT(l);
+ BM_CHECK_ELEMENT(l->v);
+ BM_CHECK_ELEMENT(l->e);
+ BM_CHECK_ELEMENT(l->f);
+ }
#endif
- if (check_edge_double) {
- if (e_splice) {
- /* removes e_splice */
- BM_edge_splice(bm, e_old, e_splice);
- }
+ if (check_edge_double) {
+ if (e_splice) {
+ /* removes e_splice */
+ BM_edge_splice(bm, e_old, e_splice);
}
+ }
- if (kill_degenerate_faces) {
- BMFace *f_kill;
- while ((f_kill = BLI_SMALLSTACK_POP(faces_degenerate))) {
- BM_face_kill(bm, f_kill);
- }
+ if (kill_degenerate_faces) {
+ BMFace *f_kill;
+ while ((f_kill = BLI_SMALLSTACK_POP(faces_degenerate))) {
+ BM_face_kill(bm, f_kill);
}
+ }
- BM_CHECK_ELEMENT(v_old);
- BM_CHECK_ELEMENT(v_target);
- BM_CHECK_ELEMENT(e_old);
+ BM_CHECK_ELEMENT(v_old);
+ BM_CHECK_ELEMENT(v_target);
+ BM_CHECK_ELEMENT(e_old);
- return e_old;
- }
+ return e_old;
}
return NULL;
}
@@ -2373,23 +2381,22 @@ void bmesh_kernel_vert_separate(
* if 'edges' were alloc'd it'd be freed here. */
break;
}
- else {
- BMVert *v_new;
- v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
- if (copy_select) {
- BM_elem_select_copy(bm, v_new, v);
- }
+ BMVert *v_new;
- while ((e = BLI_SMALLSTACK_POP(edges))) {
- bmesh_edge_vert_swap(e, v_new, v);
- }
+ v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
+ if (copy_select) {
+ BM_elem_select_copy(bm, v_new, v);
+ }
- if (r_vout) {
- BLI_SMALLSTACK_PUSH(verts_new, v_new);
- }
- verts_num += 1;
+ while ((e = BLI_SMALLSTACK_POP(edges))) {
+ bmesh_edge_vert_swap(e, v_new, v);
+ }
+
+ if (r_vout) {
+ BLI_SMALLSTACK_PUSH(verts_new, v_new);
}
+ verts_num += 1;
}
#undef EDGE_VISIT
diff --git a/source/blender/bmesh/intern/bmesh_edgeloop.c b/source/blender/bmesh/intern/bmesh_edgeloop.c
index e4312877fc6..ec97ea51047 100644
--- a/source/blender/bmesh/intern/bmesh_edgeloop.c
+++ b/source/blender/bmesh/intern/bmesh_edgeloop.c
@@ -651,9 +651,7 @@ bool BM_edgeloop_calc_normal(BMesh *UNUSED(bm), BMEdgeLoopStore *el_store)
el_store->no[2] = 1.0f; /* other axis set to 0.0 */
return false;
}
- else {
- return true;
- }
+ return true;
}
/**
@@ -693,9 +691,7 @@ bool BM_edgeloop_calc_normal_aligned(BMesh *UNUSED(bm),
el_store->no[2] = 1.0f; /* other axis set to 0.0 */
return false;
}
- else {
- return true;
- }
+ return true;
}
void BM_edgeloop_flip(BMesh *UNUSED(bm), BMEdgeLoopStore *el_store)
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index 27c03f0a84f..e8298f4dffc 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -160,13 +160,12 @@ void BM_data_interp_face_vert_edge(BMesh *bm,
if (!l_v1 || !l_v2) {
return;
}
- else {
- const void *src[2];
- src[0] = l_v1->head.data;
- src[1] = l_v2->head.data;
- CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, l_v->head.data);
- }
+ const void *src[2];
+ src[0] = l_v1->head.data;
+ src[1] = l_v2->head.data;
+
+ CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, l_v->head.data);
} while ((l_iter = l_iter->radial_next) != e->l);
}
@@ -744,9 +743,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_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c
index c4dd53b0497..b68bae9ee04 100644
--- a/source/blender/bmesh/intern/bmesh_iterators.c
+++ b/source/blender/bmesh/intern/bmesh_iterators.c
@@ -206,10 +206,9 @@ void *BM_iter_as_arrayN(BMesh *bm,
}
return array;
}
- else {
- *r_len = 0;
- return NULL;
- }
+
+ *r_len = 0;
+ return NULL;
}
void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
@@ -244,10 +243,9 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS],
*r_len = i;
return array;
}
- else {
- *r_len = 0;
- return NULL;
- }
+
+ *r_len = 0;
+ return NULL;
}
int BM_iter_mesh_bitmap_from_filter(const char itype,
diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c
index 6c0585a7759..69d73f3d647 100644
--- a/source/blender/bmesh/intern/bmesh_log.c
+++ b/source/blender/bmesh/intern/bmesh_log.c
@@ -183,9 +183,7 @@ static float vert_mask_get(BMVert *v, const int cd_vert_mask_offset)
if (cd_vert_mask_offset != -1) {
return BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
}
- else {
- return 0.0f;
- }
+ return 0.0f;
}
/* Set a vertex's paint-mask value
diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c
index 177599656b6..5b8fe1f492e 100644
--- a/source/blender/bmesh/intern/bmesh_marking.c
+++ b/source/blender/bmesh/intern/bmesh_marking.c
@@ -743,7 +743,7 @@ BMFace *BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_s
if (bm->act_face && (!is_selected || BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT))) {
return bm->act_face;
}
- else if (is_sloppy) {
+ if (is_sloppy) {
BMIter iter;
BMFace *f = NULL;
BMEditSelection *ese;
@@ -953,9 +953,7 @@ bool _bm_select_history_remove(BMesh *bm, BMHeader *ele)
BLI_freelinkN(&bm->selected, ese);
return true;
}
- else {
- return false;
- }
+ return false;
}
void _bm_select_history_store_notest(BMesh *bm, BMHeader *ele)
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index fe7c9dba08e..8eed3141c7f 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -650,7 +650,7 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
return false;
}
/* Smooth loop/edge... */
- else if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
+ if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
if (lfan_pivot_next == l_curr) {
/* We walked around a whole cyclic smooth fan
* without finding any already-processed loop,
@@ -660,10 +660,8 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
/* ... already checked in some previous looping, we can abort. */
return false;
}
- else {
- /* ... we can skip it in future, and keep checking the smooth fan. */
- BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
- }
+ /* ... we can skip it in future, and keep checking the smooth fan. */
+ BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
}
}
@@ -2379,6 +2377,27 @@ BMFace *BM_face_at_index_find(BMesh *bm, const int index)
return BLI_mempool_findelem(bm->fpool, index);
}
+BMLoop *BM_loop_at_index_find(BMesh *bm, const int index)
+{
+ BMIter iter;
+ BMFace *f;
+ int i = index;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (i < f->len) {
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ if (i == 0) {
+ return l_iter;
+ }
+ i -= 1;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ i -= f->len;
+ }
+ return NULL;
+}
+
/**
* Use lookup table when available, else use slower find functions.
*
@@ -2389,9 +2408,7 @@ BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index)
if ((bm->elem_table_dirty & BM_VERT) == 0) {
return (index < bm->totvert) ? bm->vtable[index] : NULL;
}
- else {
- return BM_vert_at_index_find(bm, index);
- }
+ return BM_vert_at_index_find(bm, index);
}
BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index)
@@ -2399,9 +2416,7 @@ BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index)
if ((bm->elem_table_dirty & BM_EDGE) == 0) {
return (index < bm->totedge) ? bm->etable[index] : NULL;
}
- else {
- return BM_edge_at_index_find(bm, index);
- }
+ return BM_edge_at_index_find(bm, index);
}
BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index)
@@ -2409,9 +2424,7 @@ BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index)
if ((bm->elem_table_dirty & BM_FACE) == 0) {
return (index < bm->totface) ? bm->ftable[index] : NULL;
}
- else {
- return BM_face_at_index_find(bm, index);
- }
+ return BM_face_at_index_find(bm, index);
}
/**
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index 4ba0d948499..0d665f1d391 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -118,6 +118,7 @@ BLI_INLINE BMFace *BM_face_at_index(BMesh *bm, const int index)
BMVert *BM_vert_at_index_find(BMesh *bm, const int index);
BMEdge *BM_edge_at_index_find(BMesh *bm, const int index);
BMFace *BM_face_at_index_find(BMesh *bm, const int index);
+BMLoop *BM_loop_at_index_find(BMesh *bm, const int index);
BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index);
BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index);
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_mods.c b/source/blender/bmesh/intern/bmesh_mods.c
index 7b5df536192..5d2745f8a96 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -65,32 +65,26 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
BM_vert_kill(bm, v); /* will kill edges too */
return true;
}
- else if (!BM_vert_is_manifold(v)) {
+ if (!BM_vert_is_manifold(v)) {
if (!v->e) {
BM_vert_kill(bm, v);
return true;
}
- else if (!v->e->l) {
+ if (!v->e->l) {
if (len == 2) {
return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
}
- else {
- /* used to kill the vertex here, but it may be connected to faces.
- * so better do nothing */
- return false;
- }
- }
- else {
+ /* used to kill the vertex here, but it may be connected to faces.
+ * so better do nothing */
return false;
}
+ return false;
}
- else if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
+ if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
/* boundary vertex on a face */
return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
}
- else {
- return BM_disk_dissolve(bm, v);
- }
+ return BM_disk_dissolve(bm, v);
}
/**
@@ -139,13 +133,13 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true))) {
return false;
}
- else if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true))) {
+ if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true))) {
return false;
}
#endif
return true;
}
- else if (keepedge == NULL && len == 2) {
+ if (keepedge == NULL && len == 2) {
/* collapse the vertex */
e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true);
@@ -855,9 +849,7 @@ bool BM_edge_rotate_check(BMEdge *e)
return true;
}
- else {
- return false;
- }
+ return false;
}
/**
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 04cdc0020d9..4117ad67dd3 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},
};
@@ -1748,6 +1755,12 @@ static BMO_FlagSet bmo_enum_bevel_vmesh_method[] = {
{0, NULL},
};
+static BMO_FlagSet bmo_enum_bevel_affect_type[] = {
+ {BEVEL_AFFECT_VERTICES, "VERTICES"},
+ {BEVEL_AFFECT_EDGES, "EDGES"},
+ {0, NULL},
+};
+
/*
* Bevel.
*
@@ -1759,10 +1772,13 @@ 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 */
+ {"affect", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
+ bmo_enum_bevel_affect_type}, /* Whether to bevel vertices or edges. */
{"clamp_overlap", BMO_OP_SLOT_BOOL}, /* do not allow beveled edges/vertices to overlap each other */
{"material", BMO_OP_SLOT_INT}, /* material for bevel faces, -1 means get from adjacent faces */
{"loop_slide", BMO_OP_SLOT_BOOL}, /* prefer to slide along edges to having even widths */
@@ -1777,9 +1793,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.c b/source/blender/bmesh/intern/bmesh_operators.c
index 6454079a5dc..19e42b33c80 100644
--- a/source/blender/bmesh/intern/bmesh_operators.c
+++ b/source/blender/bmesh/intern/bmesh_operators.c
@@ -1501,7 +1501,7 @@ void *BMO_iter_step(BMOIter *iter)
return ele;
}
- else if (slot->slot_type == BMO_OP_SLOT_MAPPING) {
+ if (slot->slot_type == BMO_OP_SLOT_MAPPING) {
void *ret;
if (BLI_ghashIterator_done(&iter->giter) == false) {
@@ -1517,9 +1517,7 @@ void *BMO_iter_step(BMOIter *iter)
return ret;
}
- else {
- BLI_assert(0);
- }
+ BLI_assert(0);
return NULL;
}
diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h
index 9f0107db693..c0e59758120 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,
@@ -132,6 +139,12 @@ enum {
BEVEL_VMESH_CUTOFF,
};
+/* Bevel affect option. */
+enum {
+ BEVEL_AFFECT_VERTICES = 0,
+ BEVEL_AFFECT_EDGES = 1,
+};
+
/* Normal Face Strength values */
enum {
FACE_STRENGTH_WEAK = -16384,
diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c
index a40c293f1aa..06bab8c8cc1 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.c
+++ b/source/blender/bmesh/intern/bmesh_polygon.c
@@ -163,7 +163,7 @@ void BM_face_calc_tessellation(const BMFace *f,
float(*projverts)[2] = BLI_array_alloca(projverts, f->len);
int j;
- axis_dominant_v3_to_m3(axis_mat, f->no);
+ axis_dominant_v3_to_m3_negate(axis_mat, f->no);
j = 0;
l_iter = l_first;
@@ -174,7 +174,7 @@ void BM_face_calc_tessellation(const BMFace *f,
} while ((l_iter = l_iter->next) != l_first);
/* complete the loop */
- BLI_polyfill_calc(projverts, f->len, -1, r_index);
+ BLI_polyfill_calc(projverts, f->len, 1, r_index);
}
}
@@ -743,9 +743,7 @@ bool BM_vert_calc_normal_ex(const BMVert *v, const char hflag, float r_no[3])
normalize_v3(r_no);
return true;
}
- else {
- return false;
- }
+ return false;
}
bool BM_vert_calc_normal(const BMVert *v, float r_no[3])
@@ -773,9 +771,7 @@ bool BM_vert_calc_normal(const BMVert *v, float r_no[3])
normalize_v3(r_no);
return true;
}
- else {
- return false;
- }
+ return false;
}
void BM_vert_normal_update_all(BMVert *v)
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index 284f5265b90..39395cb9222 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -449,9 +449,7 @@ static bool bm_face_split_edgenet_find_loop(BMVert *v_init,
*r_face_verts_len = i;
return (i > 2) ? true : false;
}
- else {
- return false;
- }
+ return false;
}
/**
@@ -1217,9 +1215,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) {
return true;
}
- else {
- return false;
- }
+ return false;
}
#endif /* USE_PARTIAL_CONNECT */
@@ -1242,7 +1238,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/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c
index e000b253000..80634618f6f 100644
--- a/source/blender/bmesh/intern/bmesh_query.c
+++ b/source/blender/bmesh/intern/bmesh_query.c
@@ -104,20 +104,16 @@ BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
if (l_iter->prev->v == v_prev) {
return l_iter->next;
}
- else if (l_iter->next->v == v_prev) {
+ if (l_iter->next->v == v_prev) {
return l_iter->prev;
}
- else {
- /* invalid args */
- BLI_assert(0);
- return NULL;
- }
- }
- else {
/* invalid args */
BLI_assert(0);
return NULL;
}
+ /* invalid args */
+ BLI_assert(0);
+ return NULL;
}
/**
@@ -147,27 +143,52 @@ BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v)
if (l->prev->v == v_prev) {
return l->next;
}
- else {
- BLI_assert(l->next->v == v_prev);
+ BLI_assert(l->next->v == v_prev);
- return l->prev;
- }
+ return l->prev;
}
- else {
- BLI_assert(l->v == v_prev);
+ BLI_assert(l->v == v_prev);
- if (l->prev->v == v) {
- return l->prev->prev;
- }
- else {
- BLI_assert(l->next->v == v);
- return l->next->next;
- }
+ if (l->prev->v == v) {
+ return l->prev->prev;
}
+ BLI_assert(l->next->v == v);
+ return l->next->next;
#endif
}
/**
+ * Return the other loop that uses this edge.
+ *
+ * In this case the loop defines the vertex,
+ * the edge passed in defines the direction to step.
+ *
+ * <pre>
+ * +----------+ <-- Return the face-loop of this vertex.
+ * | |
+ * | e | <-- This edge defines the direction.
+ * | |
+ * +----------+ <-- This loop defines the face and vertex..
+ * l
+ * </pre>
+ *
+ */
+BMLoop *BM_loop_other_vert_loop_by_edge(BMLoop *l, BMEdge *e)
+{
+ BLI_assert(BM_vert_in_edge(e, l->v));
+ if (l->e == e) {
+ return l->next;
+ }
+ else if (l->prev->e == e) {
+ return l->prev;
+ }
+ else {
+ BLI_assert(0);
+ return NULL;
+ }
+}
+
+/**
* Check if verts share a face.
*/
bool BM_vert_pair_share_face_check(BMVert *v_a, BMVert *v_b)
@@ -302,9 +323,7 @@ static float bm_face_calc_split_dot(BMLoop *l_a, BMLoop *l_b)
(BM_face_calc_normal_subset(l_b, l_a, no[1]) != 0.0f)) {
return dot_v3v3(no[0], no[1]);
}
- else {
- return -1.0f;
- }
+ return -1.0f;
}
/**
@@ -634,9 +653,7 @@ BMLoop *BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step)
if (BM_edge_is_manifold(e_next)) {
return BM_edge_other_loop((*e_step = e_next), l);
}
- else {
- return NULL;
- }
+ return NULL;
}
/**
@@ -722,11 +739,10 @@ bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
*r_fb = lb->f;
return true;
}
- else {
- *r_fa = NULL;
- *r_fb = NULL;
- return false;
- }
+
+ *r_fa = NULL;
+ *r_fb = NULL;
+ return false;
}
/**
@@ -744,11 +760,10 @@ bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
*r_lb = lb;
return true;
}
- else {
- *r_la = NULL;
- *r_lb = NULL;
- return false;
- }
+
+ *r_la = NULL;
+ *r_lb = NULL;
+ return false;
}
/**
@@ -916,9 +931,7 @@ bool BM_vert_is_wire(const BMVert *v)
return true;
}
- else {
- return false;
- }
+ return false;
}
/**
@@ -1167,9 +1180,7 @@ bool BM_vert_is_boundary(const BMVert *v)
return false;
}
- else {
- return false;
- }
+ return false;
}
/**
@@ -1359,12 +1370,10 @@ BMVert *BM_edge_share_vert(BMEdge *e1, BMEdge *e2)
if (BM_vert_in_edge(e2, e1->v1)) {
return e1->v1;
}
- else if (BM_vert_in_edge(e2, e1->v2)) {
+ if (BM_vert_in_edge(e2, e1->v2)) {
return e1->v2;
}
- else {
- return NULL;
- }
+ return NULL;
}
/**
@@ -1381,9 +1390,7 @@ BMLoop *BM_edge_vert_share_loop(BMLoop *l, BMVert *v)
if (l->v == v) {
return l;
}
- else {
- return l->next;
- }
+ return l->next;
}
/**
@@ -1561,10 +1568,8 @@ float BM_loop_calc_face_normal_safe_ex(const BMLoop *l, const float epsilon_sq,
cross_v3_v3v3(r_normal, v1, v2);
return normalize_v3(r_normal);
}
- else {
- copy_v3_v3(r_normal, l->f->no);
- return 0.0f;
- }
+ copy_v3_v3(r_normal, l->f->no);
+ return 0.0f;
}
/**
@@ -1596,10 +1601,8 @@ float BM_loop_calc_face_normal_safe_vcos_ex(const BMLoop *l,
cross_v3_v3v3(r_normal, v1, v2);
return normalize_v3(r_normal);
}
- else {
- copy_v3_v3(r_normal, normal_fallback);
- return 0.0f;
- }
+ copy_v3_v3(r_normal, normal_fallback);
+ return 0.0f;
}
/**
@@ -1721,9 +1724,7 @@ float BM_edge_calc_face_angle_ex(const BMEdge *e, const float fallback)
const BMLoop *l2 = e->l->radial_next;
return angle_normalized_v3v3(l1->f->no, l2->f->no);
}
- else {
- return fallback;
- }
+ return fallback;
}
float BM_edge_calc_face_angle(const BMEdge *e)
{
@@ -1757,9 +1758,7 @@ float BM_edge_calc_face_angle_with_imat3_ex(const BMEdge *e,
return angle_normalized_v3v3(no1, no2);
}
- else {
- return fallback;
- }
+ return fallback;
}
float BM_edge_calc_face_angle_with_imat3(const BMEdge *e, const float imat3[3][3])
{
@@ -1782,9 +1781,7 @@ float BM_edge_calc_face_angle_signed_ex(const BMEdge *e, const float fallback)
const float angle = angle_normalized_v3v3(l1->f->no, l2->f->no);
return BM_edge_is_convex(e) ? angle : -angle;
}
- else {
- return fallback;
- }
+ return fallback;
}
float BM_edge_calc_face_angle_signed(const BMEdge *e)
{
@@ -1839,9 +1836,7 @@ float BM_vert_calc_edge_angle_ex(const BMVert *v, const float fallback)
return (float)M_PI - angle_v3v3v3(v1->co, v->co, v2->co);
}
- else {
- return fallback;
- }
+ return fallback;
}
float BM_vert_calc_edge_angle(const BMVert *v)
@@ -1869,9 +1864,7 @@ float BM_vert_calc_shell_factor(const BMVert *v)
if (accum_angle != 0.0f) {
return accum_shell / accum_angle;
}
- else {
- return 1.0f;
- }
+ return 1.0f;
}
/* alternate version of #BM_vert_calc_shell_factor which only
* uses 'hflag' faces, but falls back to all if none found. */
@@ -1896,16 +1889,12 @@ float BM_vert_calc_shell_factor_ex(const BMVert *v, const float no[3], const cha
if (accum_angle != 0.0f) {
return accum_shell / accum_angle;
}
- else {
- /* other main difference from BM_vert_calc_shell_factor! */
- if (tot != 0 && tot_sel == 0) {
- /* none selected, so use all */
- return BM_vert_calc_shell_factor(v);
- }
- else {
- return 1.0f;
- }
+ /* other main difference from BM_vert_calc_shell_factor! */
+ if (tot != 0 && tot_sel == 0) {
+ /* none selected, so use all */
+ return BM_vert_calc_shell_factor(v);
}
+ return 1.0f;
}
/**
@@ -1929,9 +1918,7 @@ float BM_vert_calc_median_tagged_edge_length(const BMVert *v)
if (tot) {
return length / (float)tot;
}
- else {
- return 0.0f;
- }
+ return 0.0f;
}
/**
diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h
index 7e07059d4d8..0d95efb778f 100644
--- a/source/blender/bmesh/intern/bmesh_query.h
+++ b/source/blender/bmesh/intern/bmesh_query.h
@@ -48,6 +48,8 @@ BMLoop *BM_face_other_edge_loop(BMFace *f, BMEdge *e, BMVert *v) ATTR_WARN_UNUSE
BMLoop *BM_loop_other_edge_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
+BMLoop *BM_loop_other_vert_loop_by_edge(BMLoop *l, BMEdge *e) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
BMLoop *BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
@@ -171,7 +173,6 @@ float BM_edge_calc_face_angle_with_imat3(const BMEdge *e,
float BM_edge_calc_face_angle_signed(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
void BM_edge_calc_face_tangent(const BMEdge *e, const BMLoop *e_loop, float r_tangent[3])
ATTR_NONNULL();
-
float BM_vert_calc_edge_angle(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
float BM_vert_calc_edge_angle_ex(const BMVert *v, const float fallback) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
diff --git a/source/blender/bmesh/intern/bmesh_query_uv.c b/source/blender/bmesh/intern/bmesh_query_uv.c
new file mode 100644
index 00000000000..b9ea51f0c4d
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_query_uv.c
@@ -0,0 +1,169 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bmesh
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_utildefines_stack.h"
+
+#include "BKE_customdata.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "bmesh.h"
+#include "intern/bmesh_private.h"
+
+static void uv_aspect(const BMLoop *l,
+ const float aspect[2],
+ const int cd_loop_uv_offset,
+ float r_uv[2])
+{
+ const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv;
+ r_uv[0] = uv[0] * aspect[0];
+ r_uv[1] = uv[1] * aspect[1];
+}
+
+/**
+ * Typically we avoid hiding arguments,
+ * make this an exception since it reads poorly with so many repeated arguments.
+ */
+#define UV_ASPECT(l, r_uv) uv_aspect(l, aspect, cd_loop_uv_offset, r_uv)
+
+/**
+ * Computes the UV center of a face, using the mean average weighted by edge length.
+ *
+ * See #BM_face_calc_center_median_weighted for matching spatial functionality.
+ *
+ * \param aspect: Calculate the center scaling by these values, and finally dividing.
+ * Since correct weighting depends on having the correct aspect.
+ */
+void BM_face_uv_calc_center_median_weighted(const BMFace *f,
+ const float aspect[2],
+ const int cd_loop_uv_offset,
+ float r_cent[2])
+{
+ const BMLoop *l_iter;
+ const BMLoop *l_first;
+ float totw = 0.0f;
+ float w_prev;
+
+ zero_v2(r_cent);
+
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+
+ float uv_prev[2], uv_curr[2];
+ UV_ASPECT(l_iter->prev, uv_prev);
+ UV_ASPECT(l_iter, uv_curr);
+ w_prev = len_v2v2(uv_prev, uv_curr);
+ do {
+ float uv_next[2];
+ UV_ASPECT(l_iter->next, uv_next);
+ const float w_curr = len_v2v2(uv_curr, uv_next);
+ const float w = (w_curr + w_prev);
+ madd_v2_v2fl(r_cent, uv_curr, w);
+ totw += w;
+ w_prev = w_curr;
+ copy_v2_v2(uv_curr, uv_next);
+ } while ((l_iter = l_iter->next) != l_first);
+
+ if (totw != 0.0f) {
+ mul_v2_fl(r_cent, 1.0f / (float)totw);
+ }
+ /* Reverse aspect. */
+ r_cent[0] /= aspect[0];
+ r_cent[1] /= aspect[1];
+}
+
+#undef UV_ASPECT
+
+/**
+ * Calculate the UV cross product (use the sign to check the winding).
+ */
+float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset)
+{
+ float(*uvs)[2] = BLI_array_alloca(uvs, f->len);
+ const BMLoop *l_iter;
+ const BMLoop *l_first;
+ int i = 0;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ copy_v2_v2(uvs[i++], luv->uv);
+ } while ((l_iter = l_iter->next) != l_first);
+ return cross_poly_v2(uvs, f->len);
+}
+
+/**
+ * Check if two loops that share an edge also have the same UV coordinates.
+ */
+bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
+{
+ BLI_assert(l_a->e == l_b->e);
+ MLoopUV *luv_a_curr = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
+ MLoopUV *luv_a_next = BM_ELEM_CD_GET_VOID_P(l_a->next, cd_loop_uv_offset);
+ MLoopUV *luv_b_curr = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
+ MLoopUV *luv_b_next = BM_ELEM_CD_GET_VOID_P(l_b->next, cd_loop_uv_offset);
+ if (l_a->v != l_b->v) {
+ SWAP(MLoopUV *, luv_b_curr, luv_b_next);
+ }
+ return (equals_v2v2(luv_a_curr->uv, luv_b_curr->uv) &&
+ equals_v2v2(luv_a_next->uv, luv_b_next->uv));
+}
+
+/**
+ * Check if two loops that share a vertex also have the same UV coordinates.
+ */
+bool BM_loop_uv_share_vert_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
+{
+ BLI_assert(l_a->v == l_b->v);
+ const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
+ const MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
+ if (!equals_v2v2(luv_a->uv, luv_b->uv)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Check if two loops that share a vertex also have the same UV coordinates.
+ */
+bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
+{
+ BLI_assert(l_a->v == l_b->v);
+ if (!BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) {
+ return false;
+ }
+
+ /* No need for NULL checks, these will always succeed. */
+ const BMLoop *l_other_a = BM_loop_other_vert_loop_by_edge(l_a, e);
+ const BMLoop *l_other_b = BM_loop_other_vert_loop_by_edge(l_b, e);
+
+ {
+ const MLoopUV *luv_other_a = BM_ELEM_CD_GET_VOID_P(l_other_a, cd_loop_uv_offset);
+ const MLoopUV *luv_other_b = BM_ELEM_CD_GET_VOID_P(l_other_b, cd_loop_uv_offset);
+ if (!equals_v2v2(luv_other_a->uv, luv_other_b->uv)) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/source/blender/bmesh/intern/bmesh_query_uv.h b/source/blender/bmesh/intern/bmesh_query_uv.h
new file mode 100644
index 00000000000..2558f814f32
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_query_uv.h
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BMESH_QUERY_UV_H__
+#define __BMESH_QUERY_UV_H__
+
+/** \file
+ * \ingroup bmesh
+ */
+
+float BM_loop_uv_calc_edge_length_squared(const BMLoop *l,
+ const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+float BM_loop_uv_calc_edge_length(const BMLoop *l,
+ const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+
+void BM_face_uv_calc_center_median_weighted(const BMFace *f,
+ const float aspect[2],
+ const int cd_loop_uv_offset,
+ float r_cent[2]) ATTR_NONNULL();
+
+float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+
+bool BM_loop_uv_share_edge_check_with_limit(BMLoop *l_a,
+ BMLoop *l_b,
+ const float limit[2],
+ const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+
+bool BM_loop_uv_share_edge_check(BMLoop *l_a,
+ BMLoop *l_b,
+ const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+
+bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
+ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+
+bool BM_loop_uv_share_vert_check(BMLoop *l_a,
+ BMLoop *l_b,
+ const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+
+#endif /* __BMESH_QUERY_UV_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c
index f317c59b8d1..88ddac425ee 100644
--- a/source/blender/bmesh/intern/bmesh_walkers_impl.c
+++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c
@@ -45,12 +45,10 @@ static bool bmw_mask_check_vert(BMWalker *walker, BMVert *v)
if ((walker->flag & BMW_FLAG_TEST_HIDDEN) && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
return false;
}
- else if (walker->mask_vert && !BMO_vert_flag_test(walker->bm, v, walker->mask_vert)) {
+ if (walker->mask_vert && !BMO_vert_flag_test(walker->bm, v, walker->mask_vert)) {
return false;
}
- else {
- return true;
- }
+ return true;
}
static bool bmw_mask_check_edge(BMWalker *walker, BMEdge *e)
@@ -58,12 +56,10 @@ static bool bmw_mask_check_edge(BMWalker *walker, BMEdge *e)
if ((walker->flag & BMW_FLAG_TEST_HIDDEN) && BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
return false;
}
- else if (walker->mask_edge && !BMO_edge_flag_test(walker->bm, e, walker->mask_edge)) {
+ if (walker->mask_edge && !BMO_edge_flag_test(walker->bm, e, walker->mask_edge)) {
return false;
}
- else {
- return true;
- }
+ return true;
}
static bool bmw_mask_check_face(BMWalker *walker, BMFace *f)
@@ -71,12 +67,10 @@ static bool bmw_mask_check_face(BMWalker *walker, BMFace *f)
if ((walker->flag & BMW_FLAG_TEST_HIDDEN) && BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
return false;
}
- else if (walker->mask_face && !BMO_face_flag_test(walker->bm, f, walker->mask_face)) {
+ if (walker->mask_face && !BMO_face_flag_test(walker->bm, f, walker->mask_face)) {
return false;
}
- else {
- return true;
- }
+ return true;
}
/** \} */
@@ -94,13 +88,9 @@ static bool bmw_edge_is_wire(const BMWalker *walker, const BMEdge *e)
if (BM_edge_is_wire(e)) {
return true;
}
- else {
- return BM_edge_is_all_face_flag_test(e, BM_ELEM_HIDDEN, false);
- }
- }
- else {
- return BM_edge_is_wire(e);
+ return BM_edge_is_all_face_flag_test(e, BM_ELEM_HIDDEN, false);
}
+ return BM_edge_is_wire(e);
}
/** \} */
@@ -501,16 +491,15 @@ static void *bmw_LoopShellWireWalker_step(BMWalker *walker)
return l;
}
- else {
- BMEdge *e = (BMEdge *)swalk->curelem;
- BLI_assert(e->head.htype == BM_EDGE);
+ BMEdge *e = (BMEdge *)swalk->curelem;
- bmw_LoopShellWireWalker_visitVert(walker, e->v1, e);
- bmw_LoopShellWireWalker_visitVert(walker, e->v2, e);
+ BLI_assert(e->head.htype == BM_EDGE);
- return e;
- }
+ bmw_LoopShellWireWalker_visitVert(walker, e->v1, e);
+ bmw_LoopShellWireWalker_visitVert(walker, e->v2, e);
+
+ return e;
}
/** \} */
@@ -718,7 +707,7 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker)
if (l == owalk.curloop) {
return NULL;
}
- else if (BLI_gset_haskey(walker->visit_set, l)) {
+ if (BLI_gset_haskey(walker->visit_set, l)) {
return owalk.curloop;
}
@@ -1246,9 +1235,7 @@ static void bmw_EdgeringWalker_begin(BMWalker *walker, void *data)
lwalk->wireedge = e;
return;
}
- else {
- lwalk->wireedge = NULL;
- }
+ lwalk->wireedge = NULL;
BLI_gset_insert(walker->visit_set, lwalk->l->e);
@@ -1285,9 +1272,7 @@ static void *bmw_EdgeringWalker_yield(BMWalker *walker)
if (lwalk->l) {
return lwalk->l->e;
}
- else {
- return lwalk->wireedge;
- }
+ return lwalk->wireedge;
}
static void *bmw_EdgeringWalker_step(BMWalker *walker)
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
index 18193beff58..4e708b595e0 100644
--- a/source/blender/bmesh/operators/bmo_bevel.c
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -33,8 +33,9 @@ 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 int affect_type = BMO_slot_int_get(op->slots_in, "affect");
const float profile = BMO_slot_float_get(op->slots_in, "profile");
const bool clamp_overlap = BMO_slot_bool_get(op->slots_in, "clamp_overlap");
const int material = BMO_slot_int_get(op->slots_in, "material");
@@ -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,9 +76,10 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
BM_mesh_bevel(bm,
offset,
offset_type,
+ profile_type,
seg,
profile,
- vonly,
+ affect_type,
false,
clamp_overlap,
NULL,
@@ -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/operators/bmo_connect_concave.c b/source/blender/bmesh/operators/bmo_connect_concave.c
index e7b86f102a2..bc1111676a3 100644
--- a/source/blender/bmesh/operators/bmo_connect_concave.c
+++ b/source/blender/bmesh/operators/bmo_connect_concave.c
@@ -61,23 +61,20 @@ static int bm_edge_length_cmp(const void *a_, const void *b_)
if (e_a_concave < e_b_concave) {
return -1;
}
- else if (e_a_concave > e_b_concave) {
+ if (e_a_concave > e_b_concave) {
return 1;
}
- else {
- /* otherwise shortest edges last */
- const float e_a_len = BM_edge_calc_length_squared(e_a);
- const float e_b_len = BM_edge_calc_length_squared(e_b);
- if (e_a_len < e_b_len) {
- return 1;
- }
- else if (e_a_len > e_b_len) {
- return -1;
- }
- else {
- return 0;
- }
+
+ /* otherwise shortest edges last */
+ const float e_a_len = BM_edge_calc_length_squared(e_a);
+ const float e_b_len = BM_edge_calc_length_squared(e_b);
+ if (e_a_len < e_b_len) {
+ return 1;
+ }
+ if (e_a_len > e_b_len) {
+ return -1;
}
+ return 0;
}
static bool bm_face_split_by_concave(BMesh *bm,
diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c
index ba8a93ff819..087cd2cd01f 100644
--- a/source/blender/bmesh/operators/bmo_connect_pair.c
+++ b/source/blender/bmesh/operators/bmo_connect_pair.c
@@ -155,16 +155,14 @@ static int min_dist_dir_test(MinDistDir *mddir, const float dist_dir[3], const f
if (mddir->dist_min[0] == FLT_MAX) {
return 0;
}
- else {
- if (dot_v3v3(dist_dir, mddir->dir) > 0.0f) {
- if (dist_sq < mddir->dist_min[0]) {
- return 0;
- }
+ if (dot_v3v3(dist_dir, mddir->dir) > 0.0f) {
+ if (dist_sq < mddir->dist_min[0]) {
+ return 0;
}
- else {
- if (dist_sq < mddir->dist_min[1]) {
- return 1;
- }
+ }
+ else {
+ if (dist_sq < mddir->dist_min[1]) {
+ return 1;
}
}
@@ -191,9 +189,7 @@ static int state_isect_co_pair(const PathContext *pc, const float co_a[3], const
if ((test_a && test_b) && (test_a != test_b)) {
return 1; /* on either side */
}
- else {
- return 0;
- }
+ return 0;
}
static int state_isect_co_exact(const PathContext *pc, const float co[3])
diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c
index c6813a864a8..0789000969e 100644
--- a/source/blender/bmesh/operators/bmo_create.c
+++ b/source/blender/bmesh/operators/bmo_create.c
@@ -280,7 +280,7 @@ void bmo_contextual_create_exec(BMesh *bm, BMOperator *op)
* last resort when all else fails.
*/
if (totv > 2) {
- /* TODO, some of these vertes may be connected by edges,
+ /* TODO, some of these vertices may be connected by edges,
* this connectivity could be used rather than treating
* them as a bunch of isolated verts. */
diff --git a/source/blender/bmesh/operators/bmo_edgenet.c b/source/blender/bmesh/operators/bmo_edgenet.c
index 16bbbd5df88..3ed2e8369e0 100644
--- a/source/blender/bmesh/operators/bmo_edgenet.c
+++ b/source/blender/bmesh/operators/bmo_edgenet.c
@@ -194,10 +194,8 @@ void bmo_edgenet_prepare_exec(BMesh *bm, BMOperator *op)
BLI_array_free(edges2);
return;
}
- else {
- edges1 = edges2;
- edges2 = NULL;
- }
+ edges1 = edges2;
+ edges2 = NULL;
}
if (edges2 && BLI_array_len(edges2) > 2 &&
diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c
index 3c63f4a60d6..eee31969971 100644
--- a/source/blender/bmesh/operators/bmo_extrude.c
+++ b/source/blender/bmesh/operators/bmo_extrude.c
@@ -572,6 +572,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
f = BM_face_create_verts(bm, f_verts, 4, NULL, BM_CREATE_NOP, true);
#endif
+ bm_extrude_copy_face_loop_attributes(bm, f);
if (join_face) {
BMVert *v1 = e->v1;
BMVert *v2 = e->v2;
@@ -583,11 +584,11 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
BMO_elem_flag_enable(bm, v2, EXT_TAG);
dissolve_verts[dissolve_verts_len++] = v2;
}
+ /* Tag the edges that can collapse. */
+ BMO_elem_flag_enable(bm, f_edges[0], EXT_TAG);
+ BMO_elem_flag_enable(bm, f_edges[1], EXT_TAG);
bmesh_kernel_join_face_kill_edge(bm, join_face, f, e);
}
- else {
- bm_extrude_copy_face_loop_attributes(bm, f);
- }
}
/* link isolated vert */
@@ -613,7 +614,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v);
if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) {
/* Lose edge or BMVert is edge pair. */
- BM_edge_collapse(bm, e, v, true, false);
+ BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, false);
}
else {
BLI_assert(!BM_vert_is_edge_pair(v));
diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c
index 097a7aca383..74a5e365dd5 100644
--- a/source/blender/bmesh/operators/bmo_inset.c
+++ b/source/blender/bmesh/operators/bmo_inset.c
@@ -506,9 +506,7 @@ static BMLoop *bm_edge_is_mixed_face_tag(BMLoop *l)
return ((tot_tag == 1) && (tot_untag >= 1)) ? l_tag : NULL;
}
- else {
- return NULL;
- }
+ return NULL;
}
static float bm_edge_info_average_length(BMVert *v, SplitEdgeInfo *edge_info)
@@ -530,9 +528,7 @@ static float bm_edge_info_average_length(BMVert *v, SplitEdgeInfo *edge_info)
if (tot != 0) {
return len / (float)tot;
}
- else {
- return -1.0f;
- }
+ return -1.0f;
}
/**.
@@ -663,9 +659,7 @@ static float bm_edge_info_average_length_with_fallback(
if (length != -1.0f) {
return length;
}
- else {
- return bm_edge_info_average_length_fallback(v, edge_info, bm, vert_lengths_p);
- }
+ return bm_edge_info_average_length_fallback(v, edge_info, bm, vert_lengths_p);
}
void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c
index bf63261fd4d..5e901df3959 100644
--- a/source/blender/bmesh/operators/bmo_subdivide.c
+++ b/source/blender/bmesh/operators/bmo_subdivide.c
@@ -1276,7 +1276,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op)
continue;
}
- else if (!pat) {
+ if (!pat) {
continue;
}
diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
index b261f018e3e..d72960adf89 100644
--- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c
+++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
@@ -907,9 +907,7 @@ static void bm_edgering_pair_order(BMesh *bm,
if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) {
break;
}
- else {
- v_other = NULL;
- }
+ v_other = NULL;
}
}
BLI_assert(v_other != NULL);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 00b647555cf..39174a49283 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.
*/
@@ -247,7 +266,7 @@ typedef struct BevVert {
int selcount;
/** Count of wire edges. */
int wirecount;
- /** Offset for this vertex, if vertex_only bevel. */
+ /** Offset for this vertex, if vertex only bevel. */
float offset;
/** Any seams on attached edges? */
bool any_seam;
@@ -298,18 +317,22 @@ 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;
+ /** Bevel vertices only or edges. */
+ int affect_type;
/** Number of segments in beveled edge profile. */
int seg;
/** User profile setting. */
float profile;
/** Superellipse parameter for edge profile. */
float pro_super_r;
- /** Bevel vertices only. */
- bool vertex_only;
/** Bevel amount affected by weights on edges or verts. */
bool use_weights;
/** Should bevel prefer to slide along edges rather than keep widths spec? */
@@ -324,14 +347,12 @@ 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. */
+ /** Vertex group array, maybe set if vertex only. */
const struct MDeformVert *dvert;
- /** Vertex group index, maybe set if vertex_only. */
+ /** Vertex group index, maybe set if vertex only. */
int vertex_group;
/** If >= 0, material number for bevel; else material comes from adjacent faces. */
int mat_nr;
@@ -513,7 +534,7 @@ static EdgeHalf *find_other_end_edge_half(BevelParams *bp, EdgeHalf *e, BevVert
BLI_assert(eother != NULL);
return eother;
}
- else if (r_bvother) {
+ if (r_bvother) {
*r_bvother = NULL;
}
return NULL;
@@ -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,53 +775,225 @@ 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;
BMLoop *lv1f1, *lv1f2, *lv2f1, *lv2f2;
BMVert *v1, *v2;
+ UNUSED_VARS_NDEBUG(v1, v2);
int i;
if (bm->ldata.totlayer == 0) {
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;
+}
- if (lef2->v == v1) {
- lv1f2 = lef2;
- lv2f2 = BM_face_other_edge_loop(f2, e, v2);
+/*
+ * 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;
+ bool *in_stack;
+ BMIter eiter, fiter;
+
+ 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;
+
+ /* Use an array as a stack. Stack size can't exceed total faces if keep track of what is in
+ * stack. */
+ stack = MEM_malloc_arrayN(totface, sizeof(BMFace *), __func__);
+ in_stack = MEM_malloc_arrayN(totface, sizeof(bool), __func__);
+
+ /* Set all component ids by DFS from faces with unassigned components. */
+ for (f = 0; f < totface; f++) {
+ face_component[f] = -1;
+ in_stack[f] = false;
+ }
+ current_component = -1;
+ for (f = 0; f < totface; f++) {
+ if (face_component[f] == -1 && !in_stack[f]) {
+ stack_top = 0;
+ current_component++;
+ BLI_assert(stack_top < totface);
+ stack[stack_top] = BM_face_at_index(bm, f);
+ in_stack[f] = true;
+ while (stack_top >= 0) {
+ bmf = stack[stack_top];
+ stack_top--;
+ bmf_index = BM_elem_index_get(bmf);
+ in_stack[bmf_index] = false;
+ 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 || in_stack[bmf_other_index]) {
+ continue;
+ }
+ if (contig_ldata_across_edge(bm, bme, bmf, bmf_other)) {
+ stack_top++;
+ BLI_assert(stack_top < totface);
+ stack[stack_top] = bmf_other;
+ in_stack[bmf_other_index] = true;
+ }
+ }
+ }
+ }
+ }
}
}
- return true;
+ MEM_freeN(stack);
+ MEM_freeN(in_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
+ * segment (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 possibleface_component choices.
+ * If we just choose randomly, the resulting UV maps or material
+ * assignment can look ugly/inconsistent.
+ * Allow for the case when arguments 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.
@@ -910,13 +1104,11 @@ static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_
*ret_closer_v = e->e->v1;
return true;
}
- else if (lambda >= (1.0f + BEVEL_EPSILON_BIG) * lenu) {
+ if (lambda >= (1.0f + BEVEL_EPSILON_BIG) * lenu) {
*ret_closer_v = e->e->v2;
return true;
}
- else {
- return false;
- }
+ return false;
}
/* Return whether the angle is less than, equal to, or larger than 180 degrees. */
@@ -947,12 +1139,10 @@ static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v)
if (fabsf(dot) < BEVEL_EPSILON_BIG) {
return ANGLE_STRAIGHT;
}
- else if (dot < 0.0f) {
+ if (dot < 0.0f) {
return ANGLE_LARGER;
}
- else {
- return ANGLE_SMALLER;
- }
+ return ANGLE_SMALLER;
}
/* co should be approximately on the plane between e1 and e2, which share common vert v and common
@@ -1171,8 +1361,9 @@ static void offset_meet(EdgeHalf *e1,
/**
* Calculate the meeting point between e1 and e2 (one of which should have zero offsets),
- * where e1 precedes e2 in CCW order around their common vertex v (viewed from normal side).
- * If r_angle is provided, return the angle between e and emeet in *r_angle.
+ * where \a e1 precedes \a e2 in CCW order around their common vertex \a v
+ * (viewed from normal side).
+ * If \a r_angle is provided, return the angle between \a e and \a meetco in `*r_angle`.
* If the angle is 0, or it is 180 degrees or larger, there will be no meeting point;
* return false in that case, else true.
*/
@@ -1421,7 +1612,7 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
zero_v3(pro->proj_dir);
do_linear_interp = false;
}
- else if (bp->vertex_only) {
+ else if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
copy_v3_v3(pro->start, start);
copy_v3_v3(pro->middle, bv->v->co);
copy_v3_v3(pro->end, end);
@@ -1660,9 +1851,7 @@ static double superellipse_co(double x, float r, bool rbig)
if (rbig) {
return pow((1.0 - pow(x, r)), (1.0 / r));
}
- else {
- return 1.0 - pow((1.0 - pow(1.0 - x, r)), (1.0 / r));
- }
+ return 1.0 - pow((1.0 - pow(1.0 - x, r)), (1.0 / r));
}
/**
@@ -1737,7 +1926,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 +1935,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 +1972,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 +2516,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 */
@@ -2352,7 +2534,7 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr
BoundVert *v;
float co[3];
- BLI_assert(bp->vertex_only);
+ BLI_assert(bp->affect_type == BEVEL_AFFECT_VERTICES);
e = efirst = &bv->edges[0];
do {
@@ -2464,7 +2646,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 +2678,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];
@@ -2625,7 +2807,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
return;
}
- if (bp->vertex_only) {
+ if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
build_boundary_vertex_only(bp, bv, construct);
return;
}
@@ -2771,7 +2953,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
bool betodd = (between % 2) == 1;
int i = 0;
/* Put first half of in-between edges at index 0, second half at index bp->seg.
- * If between is odd, put middle one at midindex. */
+ * If between is odd, put middle one at mid-index. */
for (e3 = e->next; e3 != e2; e3 = e3->next) {
v1->elast = e3;
if (i < bet2) {
@@ -3113,16 +3295,13 @@ static EdgeHalf *next_edgehalf_bev(BevelParams *bp,
if ((next_edge != NULL) && compare_ff(best_dot, second_best_dot, BEVEL_SMALL_ANG_DOT)) {
return NULL;
}
- else {
- return next_edge;
- }
- }
- else {
- /* Case 2: The next EdgeHalf is the other side of the BMEdge.
- * It's part of the same BMEdge, so we know the other EdgeHalf is also beveled. */
- next_edge = find_other_end_edge_half(bp, start_edge, r_bv);
return next_edge;
}
+
+ /* Case 2: The next EdgeHalf is the other side of the BMEdge.
+ * It's part of the same BMEdge, so we know the other EdgeHalf is also beveled. */
+ next_edge = find_other_end_edge_half(bp, start_edge, r_bv);
+ return next_edge;
}
/**
@@ -3562,9 +3741,8 @@ static bool is_canon(VMesh *vm, int i, int j, int k)
if (vm->seg % 2 == 1) { /* Odd. */
return (j <= ns2 && k <= ns2);
}
- else { /* Even. */
- return ((j < ns2 && k <= ns2) || (j == ns2 && k == ns2 && i == 0));
- }
+ /* Even. */
+ return ((j < ns2 && k <= ns2) || (j == ns2 && k == ns2 && i == 0));
}
/* Copy the vertex data to all of vm verts from canonical ones. */
@@ -3628,7 +3806,7 @@ static float sabin_gamma(int n)
if (n < 3) {
return 0.0f;
}
- else if (n == 3) {
+ if (n == 3) {
ans = 0.065247584f;
}
else if (n == 4) {
@@ -3831,7 +4009,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 +4028,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,11 +4256,11 @@ 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);
}
- else if (r == PRO_SQUARE_IN_R) {
+ if (r == PRO_SQUARE_IN_R) {
return make_cube_corner_square_in(mem_arena, nseg);
}
}
@@ -4162,7 +4340,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->affect_type == BEVEL_AFFECT_VERTICES || bp->profile_type == BEVEL_PROFILE_CUSTOM) {
return -1;
}
if (bv->vmesh->count != 3) {
@@ -4281,7 +4459,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 +4537,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 +4555,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) {
@@ -4450,9 +4628,7 @@ static BMEdge *find_closer_edge(float *co, BMEdge *e1, BMEdge *e2)
if (dsq1 < dsq2) {
return e1;
}
- else {
- return e2;
- }
+ return e2;
}
/* Snap co to the closest edge of face f. Return the edge in *r_snap_e,
@@ -4480,6 +4656,106 @@ 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 \a bv, snapping to rep \a frep,
+ * will uv poly have zero area?
+ * The uv poly is made by snapping all `outside-of-frep` vertices to the closest edge in \a frep.
+ * Sometimes this results in a zero or very small area polygon, which translates to a zero
+ * or very small area polygon 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 \a 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 {
@@ -4525,10 +4801,12 @@ static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_n
BLI_array_free(ve);
}
-/* Special case of bevel_build_rings when tri-corner and profile is 0.
+/**
+ * Special case of #bevel_build_rings when triangle-corner and profile is 0.
* There is no corner mesh except, if nseg odd, for a center poly.
* Boundary verts merge with previous ones according to pattern:
- * (i, 0, k) merged with (i+1, 0, ns-k) for k <= ns/2. */
+ * (i, 0, k) merged with (i+1, 0, ns-k) for k <= ns/2.
+ */
static void build_square_in_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv, VMesh *vm1)
{
int n, ns, ns2, odd, i, k;
@@ -4562,7 +4840,9 @@ static void build_square_in_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv, VMesh
}
}
-/* Copy whichever of a and b is closer to v into r. */
+/**
+ * Copy whichever of \a a and \a b is closer to v into \a r.
+ */
static void closer_v3_v3v3v3(float r[3], float a[3], float b[3], float v[3])
{
if (len_squared_v3v3(a, v) <= len_squared_v3v3(b, v)) {
@@ -4722,7 +5002,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
bndv = bndv->next;
}
- /* Fill in rest of centerlines by interpolation. */
+ /* Fill in rest of center-lines by interpolation. */
copy_v3_v3(co2, bv->v->co);
bndv = vm->boundstart;
for (i = 0; i < n_bndv; i++) {
@@ -4773,7 +5053,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
}
vmesh_copy_equiv_verts(vm);
- /* Fill in interior points by interpolation from edges to centerlines. */
+ /* Fill in interior points by interpolation from edges to center-lines. */
bndv = vm->boundstart;
for (i = 0; i < n_bndv; i++) {
im1 = (i == 0) ? n_bndv - 1 : i - 1;
@@ -4811,9 +5091,9 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
}
/**
- * Given that the boundary is built and the boundary BMVerts have been made,
+ * Given that the boundary is built and the boundary #BMVert's have been made,
* calculate the positions of the interior mesh points for the M_ADJ pattern,
- * using cubic subdivision, then make the BMVerts and the new faces.
+ * using cubic subdivision, then make the #BMVert's and the new faces.
*/
static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert *vpipe)
{
@@ -4821,7 +5101,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 +5113,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 +5124,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,7 +5156,15 @@ 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);
- if (bp->vertex_only) {
+ fchoices[0] = f;
+ fchoices[1] = f2;
+ if (odd) {
+ fc = choose_rep_face(bp, fchoices, 2);
+ }
+ else {
+ fc = NULL;
+ }
+ if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
e = bndv->efirst;
}
else {
@@ -4884,7 +5174,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;
@@ -4892,7 +5185,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
bmv3 = mesh_vert(vm, i, j + 1, k + 1)->v;
bmv4 = mesh_vert(vm, i, j + 1, k)->v;
BLI_assert(bmv1 && bmv2 && bmv3 && bmv4);
- if (bp->vertex_only) {
+ if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
if (j < k) {
if (k == ns2 && j == ns2 - 1) {
r_f = bev_create_quad_ex(bm,
@@ -4908,6 +5201,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
NULL,
bndv->next->efirst->e,
bme,
+ f2,
mat_nr);
}
else {
@@ -4918,14 +5212,14 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
}
else { /* j == k */
- /* Only one edge attached to v, since vertex_only. */
+ /* 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 +5228,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 +5247,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);
@@ -4975,7 +5270,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
}
} while ((bndv = bndv->next) != vm->boundstart);
bmv1 = mesh_vert(vm, 0, ns2, ns2)->v;
- if (bp->vertex_only || count_bound_vert_seams(bv) <= 1) {
+ if (bp->affect_type == BEVEL_AFFECT_VERTICES || count_bound_vert_seams(bv) <= 1) {
bev_merge_uvs(bm, bmv1);
}
}
@@ -5021,7 +5316,7 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
i = bndv->index;
/* Find the "down" direction for this side of the cutoff face. */
- /* Find the direction along the intersection of the two adjecent profile normals. */
+ /* Find the direction along the intersection of the two adjacent profile normals. */
cross_v3_v3v3(down_direction, bndv->profile.plane_no, bndv->prev->profile.plane_no);
if (dot_v3v3(down_direction, bv->v->no) > 0.0f) {
negate_v3(down_direction);
@@ -5150,43 +5445,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 +5460,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 {
@@ -5322,7 +5580,7 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
BoundVert *bndv;
int ns, k;
- BLI_assert(vm->count == 2 && bp->vertex_only);
+ BLI_assert(vm->count == 2 && bp->affect_type == BEVEL_AFFECT_VERTICES);
v1 = mesh_vert(vm, 0, 0, 0)->v;
v2 = mesh_vert(vm, 1, 0, 0)->v;
@@ -5446,7 +5704,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);
}
@@ -5483,7 +5741,7 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
switch (vm->mesh_kind) {
case M_NONE:
- if (n == 2 && bp->vertex_only) {
+ if (n == 2 && bp->affect_type == BEVEL_AFFECT_VERTICES) {
bevel_vert_two_edges(bp, bm, bv);
}
break;
@@ -5509,9 +5767,7 @@ static float edge_face_angle(EdgeHalf *e)
/* Angle between faces is supplement of angle between face normals. */
return (float)M_PI - angle_normalized_v3v3(e->fprev->no, e->fnext->no);
}
- else {
- return 0.0f;
- }
+ return 0.0f;
}
/* Take care, this flag isn't cleared before use, it just so happens that its not set. */
@@ -5771,9 +6027,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,27 +6040,27 @@ 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);
+ if (BM_elem_flag_test(bme, BM_ELEM_TAG) && bp->affect_type != BEVEL_AFFECT_VERTICES) {
+ 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->affect_type == BEVEL_AFFECT_VERTICES) {
+ 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) {
+ if (bp->affect_type != BEVEL_AFFECT_VERTICES) {
BM_BEVEL_EDGE_TAG_ENABLE(bme);
}
}
@@ -5814,7 +6069,8 @@ 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->affect_type != BEVEL_AFFECT_VERTICES) ||
+ (tot_edges < 2 && bp->affect_type == BEVEL_AFFECT_VERTICES)) {
/* Signal this vert isn't being beveled. */
BM_elem_flag_disable(v, BM_ELEM_TAG);
return NULL;
@@ -5822,13 +6078,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,10 +6097,10 @@ 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) {
+ if (BM_elem_flag_test(bme, BM_ELEM_TAG) && bp->affect_type != BEVEL_AFFECT_VERTICES) {
e->is_bev = true;
e->seg = bp->seg;
}
@@ -5864,27 +6120,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. */
+ if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
+ /* 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 +6152,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 +6161,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 +6194,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 +6204,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) {
@@ -5963,7 +6229,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
e->offset_r_spec *= weight;
}
}
- else if (bp->vertex_only) {
+ else if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
/* Weight has already been applied to bv->offset, if present.
* Transfer to e->offset_[lr]_spec according to offset_type. */
float edge_dir[3];
@@ -6000,6 +6266,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 +6287,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 +6677,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 +6738,25 @@ 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};
+ 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 the loops whose verts
+ * are in the non-chosen face to bme for interpolation purposes.
+ */
+ if (f_choice == f1) {
+ edges[0] = edges[1] = NULL;
+ edges[2] = edges[3] = bme;
+ }
+ else {
+ edges[0] = edges[1] = bme;
+ edges[2] = edges[3] = NULL;
+ }
+ 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 +7101,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. */
@@ -6851,7 +7132,7 @@ static float find_profile_fullness(BevelParams *bp)
/**
* Fills the ProfileSpacing struct with the 2D coordinates for the profile's vertices.
- * The superellipse used for multisegment profiles does not have a closed-form way
+ * The superellipse used for multi-segment profiles does not have a closed-form way
* to generate evenly spaced points along an arc. We use an expensive search procedure
* to find the parameter values that lead to bp->seg even chords.
* We also want spacing for a number of segments that is a power of 2 >= bp->seg (but at least 4).
@@ -7099,7 +7380,7 @@ static void bevel_limit_offset(BevelParams *bp, BMesh *bm)
}
for (i = 0; i < bv->edgecount; i++) {
eh = &bv->edges[i];
- if (bp->vertex_only) {
+ if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
collision_offset = vertex_collide_offset(bp, eh);
if (collision_offset < limited_offset) {
limited_offset = collision_offset;
@@ -7156,9 +7437,10 @@ 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,
+ const bool affect_type,
const bool use_weights,
const bool limit_offset,
const struct MDeformVert *dvert,
@@ -7173,7 +7455,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)
{
@@ -7190,11 +7471,12 @@ void BM_mesh_bevel(BMesh *bm,
bp.seg = segments;
bp.profile = profile;
bp.pro_super_r = -logf(2.0) / logf(sqrtf(profile)); /* Convert to superellipse exponent. */
- bp.vertex_only = vertex_only;
+ bp.affect_type = affect_type;
bp.use_weights = use_weights;
bp.loop_slide = loop_slide;
bp.limit_offset = limit_offset;
- bp.offset_adjust = true;
+ bp.offset_adjust = bp.affect_type != BEVEL_AFFECT_VERTICES &&
+ !ELEM(offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE);
bp.dvert = dvert;
bp.vertex_group = vertex_group;
bp.mat_nr = mat;
@@ -7207,12 +7489,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 +7523,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 +7539,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 +7567,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);
@@ -7306,7 +7591,7 @@ void BM_mesh_bevel(BMesh *bm,
}
/* Build polygons for edges. */
- if (!bp.vertex_only) {
+ if (bp.affect_type != BEVEL_AFFECT_VERTICES) {
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
bevel_build_edge_polygons(bm, &bp, e);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h
index 8562e584ec9..317ca05b68a 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.h
+++ b/source/blender/bmesh/tools/bmesh_bevel.h
@@ -27,9 +27,10 @@ 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,
+ const bool affect_type,
const bool use_weights,
const bool limit_offset,
const struct MDeformVert *dvert,
@@ -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/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c
index 7b6a820ea33..85de065daff 100644
--- a/source/blender/bmesh/tools/bmesh_bisect_plane.c
+++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c
@@ -57,12 +57,10 @@ static short plane_point_test_v3(const float plane[4],
if (f <= -eps) {
return -1;
}
- else if (f >= eps) {
+ if (f >= eps) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
/* -------------------------------------------------------------------- */
@@ -135,12 +133,10 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
if (val_a > val_b) {
return 1;
}
- else if (val_a < val_b) {
+ if (val_a < val_b) {
return -1;
}
- else {
- return 0;
- }
+ return 0;
}
static void bm_face_bisect_verts(
diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
index 0fc0fb130fc..a2a949b5567 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -148,11 +148,10 @@ static void bm_decim_calc_target_co_db(BMEdge *e, double optimize_co[3], const Q
/* all is good */
return;
}
- else {
- optimize_co[0] = 0.5 * ((double)e->v1->co[0] + (double)e->v2->co[0]);
- optimize_co[1] = 0.5 * ((double)e->v1->co[1] + (double)e->v2->co[1]);
- optimize_co[2] = 0.5 * ((double)e->v1->co[2] + (double)e->v2->co[2]);
- }
+
+ optimize_co[0] = 0.5 * ((double)e->v1->co[0] + (double)e->v2->co[0]);
+ optimize_co[1] = 0.5 * ((double)e->v1->co[1] + (double)e->v2->co[1]);
+ optimize_co[2] = 0.5 * ((double)e->v1->co[2] + (double)e->v2->co[2]);
}
static void bm_decim_calc_target_co_fl(BMEdge *e, float optimize_co[3], const Quadric *vquadrics)
@@ -1059,7 +1058,7 @@ static bool bm_edge_collapse(BMesh *bm,
return true;
}
- else if (BM_edge_is_boundary(e_clear)) {
+ if (BM_edge_is_boundary(e_clear)) {
/* same as above but only one triangle */
BMLoop *l_a;
BMEdge *e_a_other[2];
@@ -1115,9 +1114,7 @@ static bool bm_edge_collapse(BMesh *bm,
return true;
}
- else {
- return false;
- }
+ return false;
}
/**
@@ -1273,11 +1270,9 @@ static bool bm_decim_edge_collapse(BMesh *bm,
return true;
#endif
}
- else {
- /* add back with a high cost */
- bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
- return false;
- }
+ /* add back with a high cost */
+ bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
+ return false;
}
/* Main Decimate Function
diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
index 0f23165f9af..3ed27ea580e 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
@@ -278,9 +278,7 @@ static bool bm_vert_collapse_is_degenerate(BMVert *v)
}
return false;
}
- else {
- return true;
- }
+ return true;
}
#endif /* USE_DEGENERATE_CHECK */
diff --git a/source/blender/bmesh/tools/bmesh_edgenet.c b/source/blender/bmesh/tools/bmesh_edgenet.c
index 70b39393a4f..9f4327fd1e3 100644
--- a/source/blender/bmesh/tools/bmesh_edgenet.c
+++ b/source/blender/bmesh/tools/bmesh_edgenet.c
@@ -143,9 +143,7 @@ static bool bm_edgenet_path_check_overlap(BMVert *v1, BMVert *v2, VertNetInfo *v
return BM_face_exists_overlap_subset(vert_arr, (int)v_ls_tot);
}
- else {
- return false;
- }
+ return false;
}
/**
@@ -331,11 +329,10 @@ static LinkNode *bm_edgenet_path_calc(BMEdge *e,
*r_path_cost = path_cost_accum;
return path;
}
- else {
- /* check if a change was made */
- if (v_ls_next_old != v_ls_next) {
- found = true;
- }
+
+ /* check if a change was made */
+ if (v_ls_next_old != v_ls_next) {
+ found = true;
}
}
BLI_assert(v_ls_prev == NULL);
@@ -379,49 +376,49 @@ static LinkNode *bm_edgenet_path_calc_best(BMEdge *e,
if (path == NULL) {
return NULL;
}
- else if (path_cost < 1) {
+ if (path_cost < 1) {
/* any face that takes 1 iteration to find we consider valid */
return path;
}
- else {
- /* Check every edge to see if any can give a better path.
- * This avoids very strange/long paths from being created. */
- const uint path_len = *r_path_len;
- uint i, i_prev;
- BMVert **vert_arr = BLI_array_alloca(vert_arr, path_len);
- LinkNode *v_lnk;
+ /* Check every edge to see if any can give a better path.
+ * This avoids very strange/long paths from being created. */
- for (v_lnk = path, i = 0; v_lnk; v_lnk = v_lnk->next, i++) {
- vert_arr[i] = v_lnk->link;
- }
+ const uint path_len = *r_path_len;
+ uint i, i_prev;
+ BMVert **vert_arr = BLI_array_alloca(vert_arr, path_len);
+ LinkNode *v_lnk;
- i_prev = path_len - 1;
- for (i = 0; i < path_len; i++) {
- BMEdge *e_other = BM_edge_exists(vert_arr[i], vert_arr[i_prev]);
- if (e_other != e) {
- LinkNode *path_test;
- uint path_len_test;
- uint path_cost_test;
-
- path_test = bm_edgenet_path_calc(
- e_other, *pass_nr, path_cost, &path_len_test, &path_cost_test, vnet_info, path_pool);
- (*pass_nr)++;
-
- if (path_test) {
- BLI_assert(path_cost_test < path_cost);
-
- BLI_linklist_free_pool(path, NULL, path_pool);
- path = path_test;
- *r_path_len = path_len_test;
- *r_path_cost = path_cost_test;
- path_cost = path_cost_test;
- }
- }
+ for (v_lnk = path, i = 0; v_lnk; v_lnk = v_lnk->next, i++) {
+ vert_arr[i] = v_lnk->link;
+ }
- i_prev = i;
+ i_prev = path_len - 1;
+ for (i = 0; i < path_len; i++) {
+ BMEdge *e_other = BM_edge_exists(vert_arr[i], vert_arr[i_prev]);
+ if (e_other != e) {
+ LinkNode *path_test;
+ uint path_len_test;
+ uint path_cost_test;
+
+ path_test = bm_edgenet_path_calc(
+ e_other, *pass_nr, path_cost, &path_len_test, &path_cost_test, vnet_info, path_pool);
+ (*pass_nr)++;
+
+ if (path_test) {
+ BLI_assert(path_cost_test < path_cost);
+
+ BLI_linklist_free_pool(path, NULL, path_pool);
+ path = path_test;
+ *r_path_len = path_len_test;
+ *r_path_cost = path_cost_test;
+ path_cost = path_cost_test;
+ }
}
+
+ i_prev = i;
}
+
return path;
}
diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c
index c73b80d57f7..6426fe9c687 100644
--- a/source/blender/bmesh/tools/bmesh_intersect.c
+++ b/source/blender/bmesh/tools/bmesh_intersect.c
@@ -99,9 +99,7 @@ static BMEdge *bm_vert_other_edge(BMVert *v, BMEdge *e)
if (v->e != e) {
return v->e;
}
- else {
- return BM_DISK_EDGE_NEXT(v->e, v);
- }
+ return BM_DISK_EDGE_NEXT(v->e, v);
}
#endif
@@ -1341,10 +1339,8 @@ bool BM_mesh_intersect(BMesh *bm,
splice_pair[1] = v_next;
break;
}
- else {
- e_next = bm_vert_other_edge(v_next, e_step);
- }
+ e_next = bm_vert_other_edge(v_next, e_step);
e_step = e_next;
v_step = v_next;
BM_elem_flag_enable(e_step, BM_ELEM_TAG);
diff --git a/source/blender/bmesh/tools/bmesh_intersect_edges.c b/source/blender/bmesh/tools/bmesh_intersect_edges.c
index 3f0492aebb6..52231033fd3 100644
--- a/source/blender/bmesh/tools/bmesh_intersect_edges.c
+++ b/source/blender/bmesh/tools/bmesh_intersect_edges.c
@@ -127,12 +127,10 @@ static bool bm_vert_pair_share_splittable_face_cb(BMFace *UNUSED(f),
if (IN_RANGE(lambda_b, range_min, range_max)) {
return true;
}
- else {
- copy_v3_v3(co, l_b->prev->v->co);
- sub_v3_v3v3(dir, l_b->next->v->co, co);
- if (isect_ray_ray_v3(v_a_co, v_a_b_dir, co, dir, NULL, &lambda_b)) {
- return IN_RANGE(lambda_b, range_min, range_max);
- }
+ copy_v3_v3(co, l_b->prev->v->co);
+ sub_v3_v3v3(dir, l_b->next->v->co, co);
+ if (isect_ray_ray_v3(v_a_co, v_a_b_dir, co, dir, NULL, &lambda_b)) {
+ return IN_RANGE(lambda_b, range_min, range_max);
}
}
return false;
@@ -476,9 +474,7 @@ static int sort_cmp_by_lambda_cb(const void *index1_v, const void *index2_v, voi
if (pair_flat[index1].lambda > pair_flat[index2].lambda) {
return 1;
}
- else {
- return -1;
- }
+ return -1;
}
/* -------------------------------------------------------------------- */
@@ -837,15 +833,37 @@ bool BM_mesh_intersect_edges(
}
if (pair_array) {
+ BMVert *v_key, *v_val;
pair_iter = &pair_array[0];
for (i = 0; i < pair_len; i++, pair_iter++) {
BLI_assert((*pair_iter)[0].elem->head.htype == BM_VERT);
BLI_assert((*pair_iter)[1].elem->head.htype == BM_VERT);
BLI_assert((*pair_iter)[0].elem != (*pair_iter)[1].elem);
- BMVert *v_key, *v_val;
v_key = (*pair_iter)[0].vert;
v_val = (*pair_iter)[1].vert;
BLI_ghash_insert(r_targetmap, v_key, v_val);
+ }
+
+ /**
+ * The weld_verts operator works best when all keys in the same group of
+ * collapsed vertices point to the same vertex.
+ * That is, if the pairs of vertices are:
+ * [1, 2], [2, 3] and [3, 4],
+ * They are better adjusted to:
+ * [1, 4], [2, 4] and [3, 4].
+ */
+ pair_iter = &pair_array[0];
+ for (i = 0; i < pair_len; i++, pair_iter++) {
+ v_key = (*pair_iter)[0].vert;
+ v_val = (*pair_iter)[1].vert;
+ BMVert *v_target;
+ while ((v_target = BLI_ghash_lookup(r_targetmap, v_val))) {
+ v_val = v_target;
+ }
+ if (v_val != (*pair_iter)[1].vert) {
+ BMVert **v_val_p = (BMVert **)BLI_ghash_lookup_p(r_targetmap, v_key);
+ *v_val_p = (*pair_iter)[1].vert = v_val;
+ }
if (split_faces) {
/* The vertex index indicates its position in the pair_array flat. */
BM_elem_index_set(v_key, i * 2);
diff --git a/source/blender/bmesh/tools/bmesh_path.c b/source/blender/bmesh/tools/bmesh_path.c
index 713a68969e5..cb75f47acf3 100644
--- a/source/blender/bmesh/tools/bmesh_path.c
+++ b/source/blender/bmesh/tools/bmesh_path.c
@@ -18,6 +18,8 @@
* \ingroup bmesh
*
* Find a path between 2 elements.
+ *
+ * \note All 3 functions are similar, changes to one most likely apply to another.
*/
#include "MEM_guardedalloc.h"
@@ -29,8 +31,11 @@
#include "bmesh.h"
#include "bmesh_path.h" /* own include */
+#define COST_INIT_MAX FLT_MAX
+
/* -------------------------------------------------------------------- */
-/* Generic Helpers */
+/** \name Generic Helpers
+ * \{ */
/**
* Use skip options when we want to start measuring from a boundary.
@@ -40,16 +45,15 @@ static float step_cost_3_v3_ex(
{
float d1[3], d2[3];
- /* The cost is based on the simple sum of the length of the two edgees... */
+ /* The cost is based on the simple sum of the length of the two edges. */
sub_v3_v3v3(d1, v2, v1);
sub_v3_v3v3(d2, v3, v2);
const float cost_12 = normalize_v3(d1);
const float cost_23 = normalize_v3(d2);
const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23));
- /* but is biased to give higher values to sharp turns, so that it will take
- * paths with fewer "turns" when selecting between equal-weighted paths between
- * the two edges */
+ /* But is biased to give higher values to sharp turns, so that it will take paths with
+ * fewer "turns" when selecting between equal-weighted paths between the two edges. */
return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2)))));
}
@@ -58,8 +62,11 @@ static float step_cost_3_v3(const float v1[3], const float v2[3], const float v3
return step_cost_3_v3_ex(v1, v2, v3, false, false);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BM_mesh_calc_path_vert */
+/** \name BM_mesh_calc_path_vert
+ * \{ */
static void verttag_add_adjacent(HeapSimple *heap,
BMVert *v_a,
@@ -72,11 +79,11 @@ static void verttag_add_adjacent(HeapSimple *heap,
{
BMIter eiter;
BMEdge *e;
- /* loop over faces of face, but do so by first looping over loops */
+ /* Loop over faces of face, but do so by first looping over loops. */
BM_ITER_ELEM (e, &eiter, v_a, BM_EDGES_OF_VERT) {
BMVert *v_b = BM_edge_other_vert(e, v_a);
if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
- /* we know 'v_b' is not visited, check it out! */
+ /* We know 'v_b' is not visited, check it out! */
const int v_b_index = BM_elem_index_get(v_b);
const float cost_cut = params->use_topology_distance ? 1.0f : len_v3v3(v_a->co, v_b->co);
const float cost_new = cost[v_a_index] + cost_cut;
@@ -93,15 +100,15 @@ static void verttag_add_adjacent(HeapSimple *heap,
if (params->use_step_face) {
BMIter liter;
BMLoop *l;
- /* loop over faces of face, but do so by first looping over loops */
+ /* Loop over faces of face, but do so by first looping over loops. */
BM_ITER_ELEM (l, &liter, v_a, BM_LOOPS_OF_VERT) {
if (l->f->len > 3) {
- /* skip loops on adjacent edges */
+ /* Skip loops on adjacent edges. */
BMLoop *l_iter = l->next->next;
do {
BMVert *v_b = l_iter->v;
if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
- /* we know 'v_b' is not visited, check it out! */
+ /* We know 'v_b' is not visited, check it out! */
const int v_b_index = BM_elem_index_get(v_b);
const float cost_cut = params->use_topology_distance ? 1.0f :
len_v3v3(v_a->co, v_b->co);
@@ -127,7 +134,7 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
void *user_data)
{
LinkNode *path = NULL;
- /* BM_ELEM_TAG flag is used to store visited edges */
+ /* #BM_ELEM_TAG flag is used to store visited edges. */
BMVert *v;
BMIter viter;
HeapSimple *heap;
@@ -135,7 +142,7 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
BMVert **verts_prev;
int i, totvert;
- /* note, would pass BM_EDGE except we are looping over all faces anyway */
+ /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */
// BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
@@ -144,25 +151,25 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
}
bm->elem_index_dirty &= ~BM_VERT;
- /* alloc */
+ /* Allocate. */
totvert = bm->totvert;
verts_prev = MEM_callocN(sizeof(*verts_prev) * totvert, __func__);
cost = MEM_mallocN(sizeof(*cost) * totvert, __func__);
- copy_vn_fl(cost, totvert, 1e20f);
+ copy_vn_fl(cost, totvert, COST_INIT_MAX);
/*
* Arrays are now filled as follows:
*
- * As the search continues, verts_prev[n] will be the previous verts on the shortest
- * path found so far to face n. BM_ELEM_TAG is used to tag elements we have visited,
- * cost[n] will contain the length of the shortest
+ * As the search continues, `verts_prev[n]` will be the previous verts on the shortest
+ * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
+ * `cost[n]` will contain the length of the shortest
* path to face n found so far, Finally, heap is a priority heap which is built on the
- * the same data as the cost array, but inverted: it is a worklist of faces prioritized
+ * the same data as the cost array, but inverted: it is a work-list of faces prioritized
* by the shortest path found so far to the face.
*/
- /* regular dijkstra shortest path, but over faces instead of vertices */
+ /* Regular dijkstra shortest path, but over faces instead of vertices. */
heap = BLI_heapsimple_new();
BLI_heapsimple_insert(heap, 0.0f, v_src);
cost[BM_elem_index_get(v_src)] = 0.0f;
@@ -193,8 +200,11 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
return path;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BM_mesh_calc_path_edge */
+/** \name BM_mesh_calc_path_edge
+ * \{ */
static float edgetag_cut_cost_vert(BMEdge *e_a, BMEdge *e_b, BMVert *v)
{
@@ -223,8 +233,8 @@ static void edgetag_add_adjacent(HeapSimple *heap,
{
const int e_a_index = BM_elem_index_get(e_a);
- /* unlike vert/face, stepping faces disables scanning connected edges
- * and only steps over faces (selecting a ring of edges instead of a loop) */
+ /* Unlike vert/face, stepping faces disables scanning connected edges
+ * and only steps over faces (selecting a ring of edges instead of a loop). */
if (params->use_step_face == false || e_a->l == NULL) {
BMIter viter;
BMVert *v;
@@ -234,14 +244,14 @@ static void edgetag_add_adjacent(HeapSimple *heap,
BM_ITER_ELEM (v, &viter, e_a, BM_VERTS_OF_EDGE) {
- /* don't walk over previous vertex */
+ /* Don't walk over previous vertex. */
if ((edges_prev[e_a_index]) && (BM_vert_in_edge(edges_prev[e_a_index], v))) {
continue;
}
BM_ITER_ELEM (e_b, &eiter, v, BM_EDGES_OF_VERT) {
if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
- /* we know 'e_b' is not visited, check it out! */
+ /* We know 'e_b' is not visited, check it out! */
const int e_b_index = BM_elem_index_get(e_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -267,7 +277,7 @@ static void edgetag_add_adjacent(HeapSimple *heap,
l_cycle_iter = l_iter->next;
l_cycle_end = l_iter;
- /* good, but we need to allow this otherwise paths may fail to connect at all */
+ /* Good, but we need to allow this otherwise paths may fail to connect at all. */
#if 0
if (l_iter->f->len > 3) {
l_cycle_iter = l_cycle_iter->next;
@@ -278,7 +288,7 @@ static void edgetag_add_adjacent(HeapSimple *heap,
do {
BMEdge *e_b = l_cycle_iter->e;
if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
- /* we know 'e_b' is not visited, check it out! */
+ /* We know 'e_b' is not visited, check it out! */
const int e_b_index = BM_elem_index_get(e_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -304,7 +314,7 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
void *user_data)
{
LinkNode *path = NULL;
- /* BM_ELEM_TAG flag is used to store visited edges */
+ /* #BM_ELEM_TAG flag is used to store visited edges. */
BMEdge *e;
BMIter eiter;
HeapSimple *heap;
@@ -312,7 +322,7 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
BMEdge **edges_prev;
int i, totedge;
- /* note, would pass BM_EDGE except we are looping over all edges anyway */
+ /* Note, would pass #BM_EDGE except we are looping over all edges anyway. */
BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */);
BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
@@ -321,25 +331,25 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
}
bm->elem_index_dirty &= ~BM_EDGE;
- /* alloc */
+ /* Allocate. */
totedge = bm->totedge;
- edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, "SeamPathPrevious");
- cost = MEM_mallocN(sizeof(*cost) * totedge, "SeamPathCost");
+ edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totedge, __func__);
- copy_vn_fl(cost, totedge, 1e20f);
+ copy_vn_fl(cost, totedge, COST_INIT_MAX);
/*
* Arrays are now filled as follows:
*
- * As the search continues, prevedge[n] will be the previous edge on the shortest
- * path found so far to edge n. BM_ELEM_TAG is used to tag elements we have visited,
- * cost[n] will contain the length of the shortest
+ * As the search continues, `edges_prev[n]` will be the previous edge on the shortest
+ * path found so far to edge `n`. #BM_ELEM_TAG is used to tag elements we have visited,
+ * `cost[n]` will contain the length of the shortest
* path to edge n found so far, Finally, heap is a priority heap which is built on the
- * the same data as the cost array, but inverted: it is a worklist of edges prioritized
+ * the same data as the cost array, but inverted: it is a work-list of edges prioritized
* by the shortest path found so far to the edge.
*/
- /* regular dijkstra shortest path, but over edges instead of vertices */
+ /* Regular dijkstra shortest path, but over edges instead of vertices. */
heap = BLI_heapsimple_new();
BLI_heapsimple_insert(heap, 0.0f, e_src);
cost[BM_elem_index_get(e_src)] = 0.0f;
@@ -370,8 +380,11 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
return path;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BM_mesh_calc_path_face */
+/** \name BM_mesh_calc_path_face
+ * \{ */
static float facetag_cut_cost_edge(BMFace *f_a,
BMFace *f_b,
@@ -387,15 +400,15 @@ static float facetag_cut_cost_edge(BMFace *f_a,
#if 0
mid_v3_v3v3(e_cent, e->v1->co, e->v2->co);
#else
- /* for triangle fans it gives better results to pick a point on the edge */
+ /* For triangle fans it gives better results to pick a point on the edge. */
{
- float ix_e[3], ix_f[3], f;
+ float ix_e[3], ix_f[3];
isect_line_line_v3(e->v1->co, e->v2->co, f_a_cent, f_b_cent, ix_e, ix_f);
- f = line_point_factor_v3(ix_e, e->v1->co, e->v2->co);
- if (f < 0.0f) {
+ const float factor = line_point_factor_v3(ix_e, e->v1->co, e->v2->co);
+ if (factor < 0.0f) {
copy_v3_v3(e_cent, e->v1->co);
}
- else if (f > 1.0f) {
+ else if (factor > 1.0f) {
copy_v3_v3(e_cent, e->v2->co);
}
else {
@@ -432,7 +445,7 @@ static void facetag_add_adjacent(HeapSimple *heap,
{
const int f_a_index = BM_elem_index_get(f_a);
- /* loop over faces of face, but do so by first looping over loops */
+ /* Loop over faces of face, but do so by first looping over loops. */
{
BMIter liter;
BMLoop *l_a;
@@ -444,7 +457,7 @@ static void facetag_add_adjacent(HeapSimple *heap,
do {
BMFace *f_b = l_iter->f;
if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
- /* we know 'f_b' is not visited, check it out! */
+ /* We know 'f_b' is not visited, check it out! */
const int f_b_index = BM_elem_index_get(f_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -472,7 +485,7 @@ static void facetag_add_adjacent(HeapSimple *heap,
if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
BMFace *f_b = l_b->f;
if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
- /* we know 'f_b' is not visited, check it out! */
+ /* We know 'f_b' is not visited, check it out! */
const int f_b_index = BM_elem_index_get(f_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -499,7 +512,7 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
void *user_data)
{
LinkNode *path = NULL;
- /* BM_ELEM_TAG flag is used to store visited edges */
+ /* #BM_ELEM_TAG flag is used to store visited edges. */
BMFace *f;
BMIter fiter;
HeapSimple *heap;
@@ -510,7 +523,7 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
/* Start measuring face path at the face edges, ignoring their centers. */
const void *const f_endpoints[2] = {f_src, f_dst};
- /* note, would pass BM_EDGE except we are looping over all faces anyway */
+ /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */
// BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
@@ -519,25 +532,25 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
}
bm->elem_index_dirty &= ~BM_FACE;
- /* alloc */
+ /* Allocate. */
totface = bm->totface;
faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__);
cost = MEM_mallocN(sizeof(*cost) * totface, __func__);
- copy_vn_fl(cost, totface, 1e20f);
+ copy_vn_fl(cost, totface, COST_INIT_MAX);
/*
* Arrays are now filled as follows:
*
- * As the search continues, faces_prev[n] will be the previous face on the shortest
- * path found so far to face n. BM_ELEM_TAG is used to tag elements we have visited,
- * cost[n] will contain the length of the shortest
+ * As the search continues, `faces_prev[n]` will be the previous face on the shortest
+ * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
+ * `cost[n]` will contain the length of the shortest
* path to face n found so far, Finally, heap is a priority heap which is built on the
- * the same data as the cost array, but inverted: it is a worklist of faces prioritized
+ * the same data as the cost array, but inverted: it is a work-list of faces prioritized
* by the shortest path found so far to the face.
*/
- /* regular dijkstra shortest path, but over faces instead of vertices */
+ /* Regular dijkstra shortest path, but over faces instead of vertices. */
heap = BLI_heapsimple_new();
BLI_heapsimple_insert(heap, 0.0f, f_src);
cost[BM_elem_index_get(f_src)] = 0.0f;
@@ -567,3 +580,4 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
return path;
}
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_path_region.c b/source/blender/bmesh/tools/bmesh_path_region.c
index f3061704884..f8e981bbd58 100644
--- a/source/blender/bmesh/tools/bmesh_path_region.c
+++ b/source/blender/bmesh/tools/bmesh_path_region.c
@@ -91,9 +91,9 @@ static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int
if (bm_vert_region_test(v, depths, pass)) {
return true;
}
- else if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair) &&
- bm_vert_region_test(v_end_pair[0], depths, pass) &&
- bm_vert_region_test(v_end_pair[1], depths, pass)) {
+ if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair) &&
+ bm_vert_region_test(v_end_pair[0], depths, pass) &&
+ bm_vert_region_test(v_end_pair[1], depths, pass)) {
return true;
}
diff --git a/source/blender/bmesh/tools/bmesh_path_region_uv.c b/source/blender/bmesh/tools/bmesh_path_region_uv.c
new file mode 100644
index 00000000000..d036c20d0e4
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_path_region_uv.c
@@ -0,0 +1,502 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * Find the region defined by the path(s) between 2 UV elements.
+ * (path isn't ordered).
+ *
+ * \note This uses the same behavior as bmesh_path_region.c
+ * however walking UV's causes enough differences that it's
+ * impractical to share the code.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_utildefines_stack.h"
+
+#include "bmesh.h"
+#include "bmesh_path_region_uv.h" /* own include */
+
+/**
+ * Special handling of vertices with 2 edges
+ * (act as if the edge-chain is a single edge).
+ *
+ * \note Regarding manifold edge stepping: #BM_vert_is_edge_pair_manifold usage.
+ * Logic to skip a chain of vertices is not applied at boundaries because it gives
+ * strange behavior from a user perspective especially with boundary quads, see: T52701
+ *
+ * Restrict walking over a vertex chain to cases where the edges share the same faces.
+ * This is more typical of what a user would consider a vertex chain.
+ */
+#define USE_EDGE_CHAIN
+
+#ifdef USE_EDGE_CHAIN
+/**
+ * Takes a vertex with 2 edge users and assigns the vertices at each end-point,
+ *
+ * \return Success when \a l_end_pair values are set or false if the edges loop back on themselves.
+ */
+static bool bm_loop_pair_ends(BMLoop *l_pivot, BMLoop *l_end_pair[2])
+{
+ int j;
+ for (j = 0; j < 2; j++) {
+ BMLoop *l_other = j ? l_pivot->next : l_pivot->prev;
+ while (BM_vert_is_edge_pair_manifold(l_other->v)) {
+ l_other = j ? l_other->next : l_other->prev;
+ if (l_other == l_pivot) {
+ return false;
+ }
+ }
+ l_end_pair[j] = l_other;
+ }
+ BLI_assert(j == 2);
+ return true;
+}
+#endif /* USE_EDGE_CHAIN */
+
+/** \name Loop Vertex in Region Checks
+ * \{ */
+
+static bool bm_loop_region_test(BMLoop *l, int *const depths[2], const int pass)
+{
+ const int index = BM_elem_index_get(l);
+ return (((depths[0][index] != -1) && (depths[1][index] != -1)) &&
+ ((depths[0][index] + depths[1][index]) < pass));
+}
+
+#ifdef USE_EDGE_CHAIN
+static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
+{
+ BMLoop *l_end_pair[2];
+ if (bm_loop_region_test(l, depths, pass)) {
+ return true;
+ }
+ if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair) &&
+ bm_loop_region_test(l_end_pair[0], depths, pass) &&
+ bm_loop_region_test(l_end_pair[1], depths, pass)) {
+ return true;
+ }
+
+ return false;
+}
+#else
+static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
+{
+ return bm_loop_region_test(l, depths, pass);
+}
+#endif
+
+/** \} */
+
+/**
+ * Main logic for calculating region between 2 elements.
+ *
+ * This method works walking (breadth first) over all vertices,
+ * keeping track of topological distance from the source.
+ *
+ * This is done in both directions, after that each vertices 'depth' is added to check
+ * if its less than the number of passes needed to complete the search.
+ * When it is, we know the path is one of possible paths
+ * that have the minimum topological distance.
+ *
+ * \note Only verts without BM_ELEM_TAG will be walked over.
+ */
+static LinkNode *mesh_calc_path_region_elem(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ const char path_htype)
+{
+ int ele_loops_len[2];
+ BMLoop **ele_loops[2];
+
+ /* Get vertices from any `ele_src/ele_dst` elements. */
+ for (int side = 0; side < 2; side++) {
+ BMElem *ele = side ? ele_dst : ele_src;
+ int j = 0;
+
+ if (ele->head.htype == BM_FACE) {
+ BMFace *f = (BMFace *)ele;
+ ele_loops[side] = BLI_array_alloca(ele_loops[side], f->len);
+
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ ele_loops[side][j++] = l_iter;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ else if (ele->head.htype == BM_LOOP) {
+ if (path_htype == BM_EDGE) {
+ BMLoop *l = (BMLoop *)ele;
+ ele_loops[side] = BLI_array_alloca(ele_loops[side], 2);
+ ele_loops[side][j++] = l;
+ ele_loops[side][j++] = l->next;
+ }
+ else if (path_htype == BM_VERT) {
+ BMLoop *l = (BMLoop *)ele;
+ ele_loops[side] = BLI_array_alloca(ele_loops[side], 1);
+
+ ele_loops[side][j++] = l;
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ else {
+ BLI_assert(0);
+ }
+ ele_loops_len[side] = j;
+ }
+
+ int *depths[2] = {NULL};
+ int pass = 0;
+
+ BMLoop **stack = MEM_mallocN(sizeof(*stack) * bm->totloop, __func__);
+ BMLoop **stack_other = MEM_mallocN(sizeof(*stack_other) * bm->totloop, __func__);
+
+ STACK_DECLARE(stack);
+ STACK_INIT(stack, bm->totloop);
+
+ STACK_DECLARE(stack_other);
+ STACK_INIT(stack_other, bm->totloop);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
+ * otherwise, exit early. */
+ bool found_all = false;
+
+ for (int side = 0; side < 2; side++) {
+ const int side_other = !side;
+
+ /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
+ depths[side] = MEM_mallocN(sizeof(*depths[side]) * bm->totloop, __func__);
+ copy_vn_i(depths[side], bm->totloop, -1);
+
+ /* needed for second side */
+ STACK_CLEAR(stack);
+ STACK_CLEAR(stack_other);
+
+ for (int i = 0; i < ele_loops_len[side]; i++) {
+ BMLoop *l = ele_loops[side][i];
+ depths[side][BM_elem_index_get(l)] = 0;
+ if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
+ STACK_PUSH(stack, l);
+ }
+ }
+
+#ifdef USE_EDGE_CHAIN
+ /* Expand initial state to end-point vertices when they only have 2x edges,
+ * this prevents odd behavior when source or destination are in the middle
+ * of a long chain of edges. */
+ if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
+ for (int i = 0; i < ele_loops_len[side]; i++) {
+ BMLoop *l = ele_loops[side][i];
+ BMLoop *l_end_pair[2];
+ if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair)) {
+ for (int j = 0; j < 2; j++) {
+ const int l_end_index = BM_elem_index_get(l_end_pair[j]);
+ if (depths[side][l_end_index] == -1) {
+ depths[side][l_end_index] = 0;
+ if (!BM_elem_flag_test(l_end_pair[j], BM_ELEM_TAG)) {
+ STACK_PUSH(stack, l_end_pair[j]);
+ }
+ }
+ }
+ }
+ }
+ }
+#endif /* USE_EDGE_CHAIN */
+
+ /* Keep walking over connected geometry until we find all the vertices in
+ * `ele_loops[side_other]`, or exit the loop when there's no connection. */
+ found_all = false;
+ for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
+ while (STACK_SIZE(stack) != 0) {
+ BMLoop *l_a = STACK_POP(stack);
+ const int l_a_index = BM_elem_index_get(l_a);
+
+ BMIter iter;
+ BMLoop *l_iter;
+
+ BM_ITER_ELEM (l_iter, &iter, l_a->v, BM_LOOPS_OF_VERT) {
+ if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
+ continue;
+ }
+ if (!BM_loop_uv_share_vert_check(l_a, l_iter, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ /* Flush the depth to connected loops (only needed for UV's). */
+ if (depths[side][BM_elem_index_get(l_iter)] == -1) {
+ depths[side][BM_elem_index_get(l_iter)] = depths[side][l_a_index];
+ }
+
+ for (int j = 0; j < 2; j++) {
+ BMLoop *l_b = j ? l_iter->next : l_iter->prev;
+ int l_b_index = BM_elem_index_get(l_b);
+ if (depths[side][l_b_index] == -1) {
+
+#ifdef USE_EDGE_CHAIN
+ /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
+ {
+ while (BM_vert_is_edge_pair_manifold(l_b->v) &&
+ ((depths[side][l_b_index] == -1) &&
+ /* Don't walk back to the beginning */
+ (l_b != (j ? l_iter->prev : l_iter->next)))) {
+ depths[side][l_b_index] = pass;
+
+ l_b = j ? l_b->next : l_b->prev;
+ l_b_index = BM_elem_index_get(l_b);
+ }
+ }
+#endif /* USE_EDGE_CHAIN */
+
+ /* Add the other vertex to the stack, to be traversed in the next pass. */
+ if (depths[side][l_b_index] == -1) {
+#ifdef USE_EDGE_CHAIN
+ BLI_assert(!BM_vert_is_edge_pair_manifold(l_b->v));
+#endif
+ BLI_assert(pass == depths[side][BM_elem_index_get(l_a)] + 1);
+ depths[side][l_b_index] = pass;
+ if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
+ STACK_PUSH(stack_other, l_b);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Stop searching once there's none left.
+ * Note that this looks in-efficient, however until the target elements reached,
+ * it will exit immediately.
+ * After that, it takes as many passes as the element has edges to finish off. */
+ found_all = true;
+ for (int i = 0; i < ele_loops_len[side_other]; i++) {
+ if (depths[side][BM_elem_index_get(ele_loops[side_other][i])] == -1) {
+ found_all = false;
+ break;
+ }
+ }
+ if (found_all == true) {
+ pass++;
+ break;
+ }
+
+ STACK_SWAP(stack, stack_other);
+ }
+
+ /* if we have nothing left, and didn't find all elements on the other side,
+ * exit early and don't continue */
+ if (found_all == false) {
+ break;
+ }
+ }
+
+ MEM_freeN(stack);
+ MEM_freeN(stack_other);
+
+ /* Now we have depths recorded from both sides,
+ * select elements that use tagged verts. */
+ LinkNode *path = NULL;
+
+ if (found_all == false) {
+ /* fail! (do nothing) */
+ }
+ else if (path_htype == BM_FACE) {
+ BMIter fiter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ /* check all verts in face are tagged */
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ bool ok = true;
+#if 0
+ do {
+ if (!bm_loop_region_test_chain(l_iter->v, depths, pass)) {
+ ok = false;
+ break;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+#else
+ /* Allowing a single failure on a face gives fewer 'gaps'.
+ * While correct, in practice they're often part of what
+ * a user would consider the 'region'. */
+ int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
+ do {
+ if (!bm_loop_region_test_chain(l_iter, depths, pass)) {
+ if (ok_tests == 0) {
+ ok = false;
+ break;
+ }
+ ok_tests--;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+#endif
+
+ if (ok) {
+ BLI_linklist_prepend(&path, f);
+ }
+ }
+ }
+ }
+ else if (path_htype == BM_EDGE) {
+ BMIter fiter;
+ BMFace *f;
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l;
+ /* Check the current and next loop vertices are in the region. */
+ bool l_in_chain_next = bm_loop_region_test_chain(BM_FACE_FIRST_LOOP(f), depths, pass);
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ const bool l_in_chain = l_in_chain_next;
+ l_in_chain_next = bm_loop_region_test_chain(l->next, depths, pass);
+ if (l_in_chain && l_in_chain_next) {
+ BLI_linklist_prepend(&path, l);
+ }
+ }
+ }
+ }
+ else if (path_htype == BM_VERT) {
+ BMIter fiter;
+ BMFace *f;
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (bm_loop_region_test_chain(l, depths, pass)) {
+ BLI_linklist_prepend(&path, l);
+ }
+ }
+ }
+ }
+
+ for (int side = 0; side < 2; side++) {
+ if (depths[side]) {
+ MEM_freeN(depths[side]);
+ }
+ }
+
+ return path;
+}
+
+#undef USE_EDGE_CHAIN
+
+/** \name Main Functions (exposed externally).
+ * \{ */
+
+LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ bool (*filter_fn)(BMLoop *, void *user_data),
+ void *user_data)
+{
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited verts */
+ BMFace *f;
+ BMIter fiter;
+ int i = 0;
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data));
+ BM_elem_index_set(l, i); /* set_inline */
+ i += 1;
+ }
+ }
+ bm->elem_index_dirty &= ~BM_LOOP;
+
+ path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_VERT);
+
+ return path;
+}
+
+LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ bool (*filter_fn)(BMLoop *, void *user_data),
+ void *user_data)
+{
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited verts */
+ BMFace *f;
+ BMIter fiter;
+ int i = 0;
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data));
+ BM_elem_index_set(l, i); /* set_inline */
+ i += 1;
+ }
+ }
+ bm->elem_index_dirty &= ~BM_LOOP;
+
+ path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_EDGE);
+
+ return path;
+}
+
+LinkNode *BM_mesh_calc_path_uv_region_face(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ bool (*filter_fn)(BMFace *, void *user_data),
+ void *user_data)
+{
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited verts */
+ BMFace *f;
+ BMIter fiter;
+ int i;
+
+ /* flush flag to verts */
+ BM_mesh_elem_hflag_enable_all(bm, BM_VERT, BM_ELEM_TAG, false);
+
+ BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
+ bool test;
+ BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
+
+ /* flush tag to verts */
+ if (test == false) {
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+
+ path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_FACE);
+
+ return path;
+}
+
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_path_region_uv.h b/source/blender/bmesh/tools/bmesh_path_region_uv.h
new file mode 100644
index 00000000000..16e627ab461
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_path_region_uv.h
@@ -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 __BMESH_PATH_REGION_UV_H__
+#define __BMESH_PATH_REGION_UV_H__
+
+/** \file
+ * \ingroup bmesh
+ */
+
+struct LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ bool (*test_fn)(BMLoop *, void *user_data),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3);
+
+struct LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ bool (*test_fn)(BMLoop *, void *user_data),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3);
+
+struct LinkNode *BM_mesh_calc_path_uv_region_face(BMesh *bm,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const uint cd_loop_uv_offset,
+ bool (*test_fn)(BMFace *, void *user_data),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3);
+
+#endif /* __BMESH_PATH_REGION_UV_H__ */
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.c b/source/blender/bmesh/tools/bmesh_path_uv.c
new file mode 100644
index 00000000000..57a70645187
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_path_uv.c
@@ -0,0 +1,433 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * Find a path between 2 elements in UV space.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_heap_simple.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "bmesh.h"
+#include "bmesh_path_uv.h" /* own include */
+#include "intern/bmesh_query.h"
+#include "intern/bmesh_query_uv.h"
+
+#define COST_INIT_MAX FLT_MAX
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Helpers
+ * \{ */
+
+/**
+ * Use skip options when we want to start measuring from a boundary.
+ *
+ * See #step_cost_3_v3_ex in bmesh_path.c which follows the same logic.
+ */
+static float step_cost_3_v2_ex(
+ const float v1[2], const float v2[2], const float v3[2], bool skip_12, bool skip_23)
+{
+ float d1[2], d2[2];
+
+ /* The cost is based on the simple sum of the length of the two edges. */
+ sub_v2_v2v2(d1, v2, v1);
+ sub_v2_v2v2(d2, v3, v2);
+ const float cost_12 = normalize_v2(d1);
+ const float cost_23 = normalize_v2(d2);
+ const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23));
+
+ /* But is biased to give higher values to sharp turns, so that it will take paths with
+ * fewer "turns" when selecting between equal-weighted paths between the two edges. */
+ return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v2v2(d1, d2)))));
+}
+
+static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2],
+ const float v2[2],
+ const float v3[2])
+{
+ return step_cost_3_v2_ex(v1, v2, v3, false, false);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BM_mesh_calc_path_uv_vert
+ * \{ */
+
+static void looptag_add_adjacent_uv(HeapSimple *heap,
+ BMLoop *l_a,
+ BMLoop **loops_prev,
+ float *cost,
+ const struct BMCalcPathUVParams *params)
+{
+ BLI_assert(params->aspect_y != 0.0f);
+ const uint cd_loop_uv_offset = params->cd_loop_uv_offset;
+ const int l_a_index = BM_elem_index_get(l_a);
+ const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
+ const float uv_a[2] = {luv_a->uv[0], luv_a->uv[1] / params->aspect_y};
+
+ {
+ BMIter liter;
+ BMLoop *l;
+ /* Loop over faces of face, but do so by first looping over loops. */
+ BM_ITER_ELEM (l, &liter, l_a->v, BM_LOOPS_OF_VERT) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_a->uv, luv->uv)) {
+ /* 'l_a' is already tagged, tag all adjacent. */
+ BM_elem_flag_enable(l, BM_ELEM_TAG);
+ BMLoop *l_b = l->next;
+ do {
+ if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
+ const MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
+ const float uv_b[2] = {luv_b->uv[0], luv_b->uv[1] / params->aspect_y};
+ /* We know 'l_b' is not visited, check it out! */
+ const int l_b_index = BM_elem_index_get(l_b);
+ const float cost_cut = params->use_topology_distance ? 1.0f : len_v2v2(uv_a, uv_b);
+ const float cost_new = cost[l_a_index] + cost_cut;
+
+ if (cost[l_b_index] > cost_new) {
+ cost[l_b_index] = cost_new;
+ loops_prev[l_b_index] = l_a;
+ BLI_heapsimple_insert(heap, cost_new, l_b);
+ }
+ }
+ /* This means we only step onto `l->prev` & `l->next`. */
+ if (params->use_step_face == false) {
+ if (l_b == l->next) {
+ l_b = l->prev->prev;
+ }
+ }
+ } while ((l_b = l_b->next) != l);
+ }
+ }
+ }
+}
+
+struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMLoop *, void *),
+ void *user_data)
+{
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited edges */
+ BMIter viter;
+ HeapSimple *heap;
+ float *cost;
+ BMLoop **loops_prev;
+ int i = 0, totloop;
+ BMFace *f;
+
+ /* Note, would pass BM_EDGE except we are looping over all faces anyway. */
+ // BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG
+
+ BM_ITER_MESH (f, &viter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter = l_first;
+ do {
+ BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data));
+ BM_elem_index_set(l_iter, i); /* set_inline */
+ i += 1;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~BM_LOOP;
+
+ /* Allocate. */
+ totloop = bm->totloop;
+ loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totloop, __func__);
+
+ copy_vn_fl(cost, totloop, COST_INIT_MAX);
+
+ /* Regular dijkstra shortest path, but over UV loops instead of vertices. */
+ heap = BLI_heapsimple_new();
+ BLI_heapsimple_insert(heap, 0.0f, l_src);
+ cost[BM_elem_index_get(l_src)] = 0.0f;
+
+ BMLoop *l = NULL;
+ while (!BLI_heapsimple_is_empty(heap)) {
+ l = BLI_heapsimple_pop_min(heap);
+
+ if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) {
+ break;
+ }
+
+ if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
+ /* Adjacent loops are tagged while stepping to avoid 2x loops. */
+ BM_elem_flag_enable(l, BM_ELEM_TAG);
+ looptag_add_adjacent_uv(heap, l, loops_prev, cost, params);
+ }
+ }
+
+ if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) {
+ do {
+ BLI_linklist_prepend(&path, l);
+ } while ((l = loops_prev[BM_elem_index_get(l)]));
+ }
+
+ MEM_freeN(loops_prev);
+ MEM_freeN(cost);
+ BLI_heapsimple_free(heap, NULL);
+
+ return path;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BM_mesh_calc_path_uv_edge
+ * \{ */
+/* TODO(campbell): not very urgent, since the operator fakes this using vertex path. */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BM_mesh_calc_path_uv_face
+ * \{ */
+
+static float facetag_cut_cost_edge_uv(BMFace *f_a,
+ BMFace *f_b,
+ BMLoop *l_edge,
+ const void *const f_endpoints[2],
+ const float aspect_v2[2],
+ const int cd_loop_uv_offset)
+{
+ float f_a_cent[2];
+ float f_b_cent[2];
+ float e_cent[2];
+
+ BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent);
+ BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent);
+
+ const float *co_v1 = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_edge, cd_loop_uv_offset))->uv;
+ const float *co_v2 =
+ ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_edge->next, cd_loop_uv_offset))->uv;
+
+#if 0
+ mid_v2_v2v2(e_cent, co_v1, co_v2);
+#else
+ /* For triangle fans it gives better results to pick a point on the edge. */
+ {
+ float ix_e[2];
+ isect_line_line_v2_point(co_v1, co_v2, f_a_cent, f_b_cent, ix_e);
+ const float factor = line_point_factor_v2(ix_e, co_v1, co_v2);
+ if (factor < 0.0f) {
+ copy_v2_v2(e_cent, co_v1);
+ }
+ else if (factor > 1.0f) {
+ copy_v2_v2(e_cent, co_v2);
+ }
+ else {
+ copy_v2_v2(e_cent, ix_e);
+ }
+ }
+#endif
+
+ /* Apply aspect before calculating cost. */
+ mul_v2_v2(f_a_cent, aspect_v2);
+ mul_v2_v2(f_b_cent, aspect_v2);
+ mul_v2_v2(e_cent, aspect_v2);
+
+ return step_cost_3_v2_ex(
+ f_a_cent, e_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
+}
+
+static float facetag_cut_cost_vert_uv(BMFace *f_a,
+ BMFace *f_b,
+ BMLoop *l_vert,
+ const void *const f_endpoints[2],
+ const float aspect_v2[2],
+ const int cd_loop_uv_offset)
+{
+ float f_a_cent[2];
+ float f_b_cent[2];
+ float v_cent[2];
+
+ BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent);
+ BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent);
+
+ copy_v2_v2(v_cent, ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_vert, cd_loop_uv_offset))->uv);
+
+ mul_v2_v2(f_a_cent, aspect_v2);
+ mul_v2_v2(f_b_cent, aspect_v2);
+ mul_v2_v2(v_cent, aspect_v2);
+
+ return step_cost_3_v2_ex(
+ f_a_cent, v_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
+}
+
+static void facetag_add_adjacent_uv(HeapSimple *heap,
+ BMFace *f_a,
+ BMFace **faces_prev,
+ float *cost,
+ const void *const f_endpoints[2],
+ const float aspect_v2[2],
+ const struct BMCalcPathUVParams *params)
+{
+ const uint cd_loop_uv_offset = params->cd_loop_uv_offset;
+ const int f_a_index = BM_elem_index_get(f_a);
+
+ /* Loop over faces of face, but do so by first looping over loops. */
+ {
+ BMIter liter;
+ BMLoop *l_a;
+
+ BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
+ BMLoop *l_first, *l_iter;
+
+ /* Check there is an adjacent face to loop over. */
+ if (l_a != l_a->radial_next) {
+ l_iter = l_first = l_a->radial_next;
+ do {
+ BMFace *f_b = l_iter->f;
+ if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
+ if (BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) {
+ /* We know 'f_b' is not visited, check it out! */
+ const int f_b_index = BM_elem_index_get(f_b);
+ const float cost_cut =
+ params->use_topology_distance ?
+ 1.0f :
+ facetag_cut_cost_edge_uv(
+ f_a, f_b, l_iter, f_endpoints, aspect_v2, cd_loop_uv_offset);
+ const float cost_new = cost[f_a_index] + cost_cut;
+
+ if (cost[f_b_index] > cost_new) {
+ cost[f_b_index] = cost_new;
+ faces_prev[f_b_index] = f_a;
+ BLI_heapsimple_insert(heap, cost_new, f_b);
+ }
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ }
+ }
+
+ if (params->use_step_face) {
+ BMIter liter;
+ BMLoop *l_a;
+
+ BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
+ BMIter litersub;
+ BMLoop *l_b;
+ BM_ITER_ELEM (l_b, &litersub, l_a->v, BM_LOOPS_OF_VERT) {
+ if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
+ BMFace *f_b = l_b->f;
+ if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
+ if (BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) {
+ /* We know 'f_b' is not visited, check it out! */
+ const int f_b_index = BM_elem_index_get(f_b);
+ const float cost_cut =
+ params->use_topology_distance ?
+ 1.0f :
+ facetag_cut_cost_vert_uv(
+ f_a, f_b, l_a, f_endpoints, aspect_v2, cd_loop_uv_offset);
+ const float cost_new = cost[f_a_index] + cost_cut;
+
+ if (cost[f_b_index] > cost_new) {
+ cost[f_b_index] = cost_new;
+ faces_prev[f_b_index] = f_a;
+ BLI_heapsimple_insert(heap, cost_new, f_b);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm,
+ BMFace *f_src,
+ BMFace *f_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMFace *, void *),
+ void *user_data)
+{
+ const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y};
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited edges */
+ BMIter fiter;
+ HeapSimple *heap;
+ float *cost;
+ BMFace **faces_prev;
+ int i = 0, totface;
+
+ /* Start measuring face path at the face edges, ignoring their centers. */
+ const void *const f_endpoints[2] = {f_src, f_dst};
+
+ /* Note, would pass BM_EDGE except we are looping over all faces anyway. */
+ // BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG
+
+ {
+ BMFace *f;
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_set(f, BM_ELEM_TAG, !filter_fn(f, user_data));
+ BM_elem_index_set(f, i); /* set_inline */
+ i += 1;
+ }
+ bm->elem_index_dirty &= ~BM_FACE;
+ }
+
+ /* Allocate. */
+ totface = bm->totface;
+ faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totface, __func__);
+
+ copy_vn_fl(cost, totface, COST_INIT_MAX);
+
+ /* Regular dijkstra shortest path, but over UV faces instead of vertices. */
+ heap = BLI_heapsimple_new();
+ BLI_heapsimple_insert(heap, 0.0f, f_src);
+ cost[BM_elem_index_get(f_src)] = 0.0f;
+
+ BMFace *f = NULL;
+ while (!BLI_heapsimple_is_empty(heap)) {
+ f = BLI_heapsimple_pop_min(heap);
+
+ if (f == f_dst) {
+ break;
+ }
+
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ /* Adjacent loops are tagged while stepping to avoid 2x loops. */
+ BM_elem_flag_enable(f, BM_ELEM_TAG);
+ facetag_add_adjacent_uv(heap, f, faces_prev, cost, f_endpoints, aspect_v2, params);
+ }
+ }
+
+ if (f == f_dst) {
+ do {
+ BLI_linklist_prepend(&path, f);
+ } while ((f = faces_prev[BM_elem_index_get(f)]));
+ }
+
+ MEM_freeN(faces_prev);
+ MEM_freeN(cost);
+ BLI_heapsimple_free(heap, NULL);
+
+ return path;
+}
+
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.h b/source/blender/bmesh/tools/bmesh_path_uv.h
new file mode 100644
index 00000000000..c7c5768f7d0
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_path_uv.h
@@ -0,0 +1,47 @@
+/*
+ * 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 __BMESH_PATH_UV_H__
+#define __BMESH_PATH_UV_H__
+
+/** \file
+ * \ingroup bmesh
+ */
+
+struct BMCalcPathUVParams {
+ uint use_topology_distance : 1;
+ uint use_step_face : 1;
+ uint cd_loop_uv_offset;
+ float aspect_y;
+};
+
+struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMLoop *, void *),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3, 5);
+
+struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm,
+ BMFace *f_src,
+ BMFace *f_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMFace *, void *),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3, 5);
+
+#endif /* __BMESH_PATH_UV_H__ */
diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c
index 44761b9f37e..d222ea214c4 100644
--- a/source/blender/bmesh/tools/bmesh_region_match.c
+++ b/source/blender/bmesh/tools/bmesh_region_match.c
@@ -139,9 +139,7 @@ BLI_INLINE bool bm_uuidwalk_face_test(UUIDWalk *uuidwalk, BMFace *f)
if (uuidwalk->use_face_isolate) {
return BM_elem_flag_test_bool(f, BM_ELEM_TAG);
}
- else {
- return true;
- }
+ return true;
}
BLI_INLINE bool bm_uuidwalk_vert_lookup(UUIDWalk *uuidwalk, BMVert *v, UUID_Int *r_uuid)
@@ -152,9 +150,7 @@ BLI_INLINE bool bm_uuidwalk_vert_lookup(UUIDWalk *uuidwalk, BMVert *v, UUID_Int
*r_uuid = (UUID_Int)(*ret);
return true;
}
- else {
- return false;
- }
+ return false;
}
BLI_INLINE bool bm_uuidwalk_face_lookup(UUIDWalk *uuidwalk, BMFace *f, UUID_Int *r_uuid)
@@ -165,9 +161,7 @@ BLI_INLINE bool bm_uuidwalk_face_lookup(UUIDWalk *uuidwalk, BMFace *f, UUID_Int
*r_uuid = (UUID_Int)(*ret);
return true;
}
- else {
- return false;
- }
+ return false;
}
static uint ghashutil_bmelem_indexhash(const void *key)
@@ -566,12 +560,10 @@ static int bm_face_len_cmp(const void *v1, const void *v2)
if (f1->len > f2->len) {
return 1;
}
- else if (f1->len < f2->len) {
+ if (f1->len < f2->len) {
return -1;
}
- else {
- return 0;
- }
+ return 0;
}
static uint bm_uuidwalk_init_from_edge(UUIDWalk *uuidwalk, BMEdge *e)
@@ -937,10 +929,8 @@ static bool bm_edge_is_region_boundary(BMEdge *e)
} while ((l_iter = l_iter->radial_next) != e->l);
return false;
}
- else {
- /* boundary */
- return true;
- }
+ /* boundary */
+ return true;
}
static void bm_face_region_pivot_edge_use_best(GHash *gh,
@@ -1309,7 +1299,9 @@ static UUIDFashMatch *bm_vert_fasthash_create(BMesh *bm, const uint depth)
return id_curr;
}
-static void bm_vert_fasthash_edge_order(UUIDFashMatch *fm, const BMEdge *e, UUIDFashMatch e_fm[2])
+static void bm_vert_fasthash_edge_order(const UUIDFashMatch *fm,
+ const BMEdge *e,
+ UUIDFashMatch e_fm[2])
{
e_fm[0] = fm[BM_elem_index_get(e->v1)];
e_fm[1] = fm[BM_elem_index_get(e->v2)];
diff --git a/source/blender/bmesh/tools/bmesh_wireframe.c b/source/blender/bmesh/tools/bmesh_wireframe.c
index 2240c64807c..1c820db74f4 100644
--- a/source/blender/bmesh/tools/bmesh_wireframe.c
+++ b/source/blender/bmesh/tools/bmesh_wireframe.c
@@ -142,13 +142,13 @@ static bool bm_loop_is_radial_boundary(BMLoop *l_first)
if (l == l_first) {
return true; /* a real boundary */
}
- else {
- do {
- if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
- return false;
- }
- } while ((l = l->radial_next) != l_first);
- }
+
+ do {
+ if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
+ return false;
+ }
+ } while ((l = l->radial_next) != l_first);
+
return true;
}
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/intern/COM_WorkScheduler.cpp b/source/blender/compositor/intern/COM_WorkScheduler.cpp
index 92e64c33d4d..f4da16efd22 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.cpp
+++ b/source/blender/compositor/intern/COM_WorkScheduler.cpp
@@ -184,7 +184,7 @@ bool WorkScheduler::hasGPUDevices()
{
#if COM_CURRENT_THREADING_MODEL == COM_TM_QUEUE
# ifdef COM_OPENCL_ENABLED
- return g_gpudevices.size() > 0;
+ return !g_gpudevices.empty();
# else
return 0;
# endif
@@ -210,7 +210,7 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads)
if (g_cpudevices.size() != num_cpu_threads) {
Device *device;
- while (g_cpudevices.size() > 0) {
+ while (!g_cpudevices.empty()) {
device = g_cpudevices.back();
g_cpudevices.pop_back();
device->deinitialize();
@@ -331,7 +331,7 @@ void WorkScheduler::deinitialize()
/* deinitialize CPU threads */
if (g_cpuInitialized) {
Device *device;
- while (g_cpudevices.size() > 0) {
+ while (!g_cpudevices.empty()) {
device = g_cpudevices.back();
g_cpudevices.pop_back();
device->deinitialize();
@@ -345,7 +345,7 @@ void WorkScheduler::deinitialize()
/* deinitialize OpenCL GPU's */
if (g_openclInitialized) {
Device *device;
- while (g_gpudevices.size() > 0) {
+ while (!g_gpudevices.empty()) {
device = g_gpudevices.back();
g_gpudevices.pop_back();
device->deinitialize();
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/compositor/operations/COM_ColorCorrectionOperation.cpp b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cpp
index 39ffb690328..893c052831c 100644
--- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cpp
+++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cpp
@@ -38,6 +38,15 @@ void ColorCorrectionOperation::initExecution()
this->m_inputMask = this->getInputSocketReader(1);
}
+/* Calculate x^y if the function is defined. Otherwise return the given fallback value. */
+BLI_INLINE float color_correct_powf_safe(const float x, const float y, const float fallback_value)
+{
+ if (x < 0) {
+ return fallback_value;
+ }
+ return powf(x, y);
+}
+
void ColorCorrectionOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -116,9 +125,9 @@ void ColorCorrectionOperation::executePixelSampled(float output[4],
b = 0.5f + ((b - 0.5f) * contrast);
/* Check for negative values to avoid nan. */
- r = (r > 0.0f) ? powf(r * gain + lift, invgamma) : r;
- g = (g > 0.0f) ? powf(g * gain + lift, invgamma) : g;
- b = (b > 0.0f) ? powf(b * gain + lift, invgamma) : b;
+ r = color_correct_powf_safe(r * gain + lift, invgamma, r);
+ g = color_correct_powf_safe(g * gain + lift, invgamma, g);
+ b = color_correct_powf_safe(b * gain + lift, invgamma, b);
// mix with mask
r = mvalue * inputImageColor[0] + value * r;
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp
index ae6f49bffcd..84f7fe2d225 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp
@@ -26,8 +26,8 @@
// this part has been copied from the double edge mask
static void do_adjacentKeepBorders(unsigned int t,
unsigned int rw,
- unsigned int *limask,
- unsigned int *lomask,
+ const unsigned int *limask,
+ const unsigned int *lomask,
unsigned int *lres,
float *res,
unsigned int *rsize)
@@ -196,8 +196,8 @@ static void do_adjacentKeepBorders(unsigned int t,
static void do_adjacentBleedBorders(unsigned int t,
unsigned int rw,
- unsigned int *limask,
- unsigned int *lomask,
+ const unsigned int *limask,
+ const unsigned int *lomask,
unsigned int *lres,
float *res,
unsigned int *rsize)
@@ -417,8 +417,8 @@ static void do_adjacentBleedBorders(unsigned int t,
static void do_allKeepBorders(unsigned int t,
unsigned int rw,
- unsigned int *limask,
- unsigned int *lomask,
+ const unsigned int *limask,
+ const unsigned int *lomask,
unsigned int *lres,
float *res,
unsigned int *rsize)
@@ -579,8 +579,8 @@ static void do_allKeepBorders(unsigned int t,
static void do_allBleedBorders(unsigned int t,
unsigned int rw,
- unsigned int *limask,
- unsigned int *lomask,
+ const unsigned int *limask,
+ const unsigned int *lomask,
unsigned int *lres,
float *res,
unsigned int *rsize)
@@ -793,8 +793,8 @@ static void do_allBleedBorders(unsigned int t,
static void do_allEdgeDetection(unsigned int t,
unsigned int rw,
- unsigned int *limask,
- unsigned int *lomask,
+ const unsigned int *limask,
+ const unsigned int *lomask,
unsigned int *lres,
float *res,
unsigned int *rsize,
@@ -863,8 +863,8 @@ static void do_allEdgeDetection(unsigned int t,
static void do_adjacentEdgeDetection(unsigned int t,
unsigned int rw,
- unsigned int *limask,
- unsigned int *lomask,
+ const unsigned int *limask,
+ const unsigned int *lomask,
unsigned int *lres,
float *res,
unsigned int *rsize,
@@ -935,7 +935,7 @@ static void do_adjacentEdgeDetection(unsigned int t,
static void do_createEdgeLocationBuffer(unsigned int t,
unsigned int rw,
- unsigned int *lres,
+ const unsigned int *lres,
float *res,
unsigned short *gbuf,
unsigned int *innerEdgeOffset,
@@ -1060,7 +1060,7 @@ static void do_createEdgeLocationBuffer(unsigned int t,
static void do_fillGradientBuffer(unsigned int rw,
float *res,
- unsigned short *gbuf,
+ const unsigned short *gbuf,
unsigned int isz,
unsigned int osz,
unsigned int gsz,
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp
index b43b94af06a..0087c720ac0 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp
@@ -198,7 +198,7 @@ static void FHT2D(
//------------------------------------------------------------------------------
/* 2D convolution calc, d1 *= d2, M/N - > log2 of width/height */
-static void fht_convolve(fREAL *d1, fREAL *d2, unsigned int M, unsigned int N)
+static void fht_convolve(fREAL *d1, const fREAL *d2, unsigned int M, unsigned int N)
{
fREAL a, b;
unsigned int i, j, k, L, mj, mL;
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cpp b/source/blender/compositor/operations/COM_InpaintOperation.cpp
index 0967984899d..b8329b1cb29 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.cpp
+++ b/source/blender/compositor/operations/COM_InpaintOperation.cpp
@@ -34,7 +34,7 @@ InpaintSimpleOperation::InpaintSimpleOperation() : NodeOperation()
this->setComplex(true);
this->m_inputImageProgram = NULL;
this->m_pixelorder = NULL;
- this->m_manhatten_distance = NULL;
+ this->m_manhattan_distance = NULL;
this->m_cached_buffer = NULL;
this->m_cached_buffer_ready = false;
}
@@ -43,7 +43,7 @@ void InpaintSimpleOperation::initExecution()
this->m_inputImageProgram = this->getInputSocketReader(0);
this->m_pixelorder = NULL;
- this->m_manhatten_distance = NULL;
+ this->m_manhattan_distance = NULL;
this->m_cached_buffer = NULL;
this->m_cached_buffer_ready = false;
@@ -85,7 +85,7 @@ int InpaintSimpleOperation::mdist(int x, int y)
ASSERT_XY_RANGE(x, y);
- return this->m_manhatten_distance[y * width + x];
+ return this->m_manhattan_distance[y * width + x];
}
bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters)
@@ -108,11 +108,11 @@ bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters)
return true;
}
-void InpaintSimpleOperation::calc_manhatten_distance()
+void InpaintSimpleOperation::calc_manhattan_distance()
{
int width = this->getWidth();
int height = this->getHeight();
- short *m = this->m_manhatten_distance = (short *)MEM_mallocN(sizeof(short) * width * height,
+ short *m = this->m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height,
__func__);
int *offsets;
@@ -223,7 +223,7 @@ void *InpaintSimpleOperation::initializeTileData(rcti *rect)
MemoryBuffer *buf = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect);
this->m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer());
- this->calc_manhatten_distance();
+ this->calc_manhattan_distance();
int curr = 0;
int x, y;
@@ -258,9 +258,9 @@ void InpaintSimpleOperation::deinitExecution()
this->m_pixelorder = NULL;
}
- if (this->m_manhatten_distance) {
- MEM_freeN(this->m_manhatten_distance);
- this->m_manhatten_distance = NULL;
+ if (this->m_manhattan_distance) {
+ MEM_freeN(this->m_manhattan_distance);
+ this->m_manhattan_distance = NULL;
}
this->m_cached_buffer_ready = false;
}
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h
index 2fef7c590ea..76c1ae81b28 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.h
+++ b/source/blender/compositor/operations/COM_InpaintOperation.h
@@ -34,7 +34,7 @@ class InpaintSimpleOperation : public NodeOperation {
int *m_pixelorder;
int m_area_size;
- short *m_manhatten_distance;
+ short *m_manhattan_distance;
public:
InpaintSimpleOperation();
@@ -65,7 +65,7 @@ class InpaintSimpleOperation : public NodeOperation {
rcti *output);
private:
- void calc_manhatten_distance();
+ void calc_manhattan_distance();
void clamp_xy(int &x, int &y);
float *get_pixel(int x, int y);
int mdist(int x, int y);
diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cpp b/source/blender/compositor/operations/COM_NormalizeOperation.cpp
index f7e689fa008..55faa480b83 100644
--- a/source/blender/compositor/operations/COM_NormalizeOperation.cpp
+++ b/source/blender/compositor/operations/COM_NormalizeOperation.cpp
@@ -53,9 +53,7 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data)
void NormalizeOperation::deinitExecution()
{
this->m_imageReader = NULL;
- if (this->m_cachedInstance) {
- delete this->m_cachedInstance;
- }
+ delete this->m_cachedInstance;
NodeOperation::deinitMutex();
}
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cpp b/source/blender/compositor/operations/COM_PreviewOperation.cpp
index 30fe2ca824d..43d20271141 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.cpp
+++ b/source/blender/compositor/operations/COM_PreviewOperation.cpp
@@ -126,14 +126,27 @@ void PreviewOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
NodeOperation::determineResolution(resolution, preferredResolution);
- int width = resolution[0];
- int height = resolution[1];
+
+ /* If resolution is 0 there are two possible scenarios:
+ * - Either node is not connected at all
+ * - It is connected to input which doesn't have own resolution (i.e. color input).
+ *
+ * In the former case we rely on the execution system to not evaluate this node.
+ *
+ * For the latter case we use 1 pixel preview, so that it's possible to see preview color in the
+ * preview. This is how final F12 render will behave (flood-fill final frame with the color).
+ *
+ * Having things consistent in terms that node preview is scaled down F12 render is a very
+ * natural thing to do. */
+ int width = max_ii(1, resolution[0]);
+ int height = max_ii(1, resolution[1]);
+
this->m_divider = 0.0f;
if (width > height) {
- this->m_divider = COM_PREVIEW_SIZE / (width - 1);
+ this->m_divider = (float)COM_PREVIEW_SIZE / (width);
}
else {
- this->m_divider = COM_PREVIEW_SIZE / (height - 1);
+ this->m_divider = (float)COM_PREVIEW_SIZE / (height);
}
width = width * this->m_divider;
height = height * this->m_divider;
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cpp b/source/blender/compositor/operations/COM_TonemapOperation.cpp
index 417fe8713ed..59354ff7581 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cpp
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cpp
@@ -85,9 +85,7 @@ void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y,
void TonemapOperation::deinitExecution()
{
this->m_imageReader = NULL;
- if (this->m_cachedInstance) {
- delete this->m_cachedInstance;
- }
+ delete this->m_cachedInstance;
NodeOperation::deinitMutex();
}
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cpp b/source/blender/compositor/operations/COM_VectorBlurOperation.cpp
index ee1bb0739b9..56e0ab9b93f 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.cpp
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cpp
@@ -138,7 +138,6 @@ void VectorBlurOperation::generateVectorBlur(float *data,
inputImage->getBuffer(),
inputSpeed->getBuffer(),
inputZ->getBuffer());
- return;
}
/* ****************** Spans ******************************* */
@@ -515,7 +514,7 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove)
/* we make this into 3 points, center point is (0, 0) */
/* and offset the center point just enough to make curve go through midpoint */
-static void quad_bezier_2d(float *result, float *v1, float *v2, float *ipodata)
+static void quad_bezier_2d(float *result, const float *v1, const float *v2, const float *ipodata)
{
float p1[2], p2[2], p3[2];
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 355d2536e1a..51fce738700 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -46,6 +46,7 @@ set(SRC
intern/builder/deg_builder_nodes_view_layer.cc
intern/builder/deg_builder_pchanmap.cc
intern/builder/deg_builder_relations.cc
+ intern/builder/deg_builder_relations_drivers.cc
intern/builder/deg_builder_relations_keys.cc
intern/builder/deg_builder_relations_rig.cc
intern/builder/deg_builder_relations_scene.cc
@@ -100,6 +101,7 @@ set(SRC
intern/builder/deg_builder.h
intern/builder/deg_builder_cache.h
intern/builder/deg_builder_cycle.h
+ intern/builder/deg_builder_relations_drivers.h
intern/builder/deg_builder_map.h
intern/builder/deg_builder_nodes.h
intern/builder/deg_builder_pchanmap.h
@@ -145,3 +147,14 @@ set(LIB
)
blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ intern/builder/deg_builder_rna_test.cc
+ )
+ set(TEST_LIB
+ bf_depsgraph
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_depsgraph_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB}")
+endif()
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..750bccf0e52 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. */
@@ -71,11 +72,11 @@ bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b)
return a.data == b.data && a.property_rna == b.property_rna;
}
-uint32_t AnimatedPropertyID::hash() const
+uint64_t AnimatedPropertyID::hash() const
{
uintptr_t ptr1 = (uintptr_t)data;
uintptr_t ptr2 = (uintptr_t)property_rna;
- return (uint32_t)(((ptr1 >> 4) * 33) ^ (ptr2 >> 4));
+ return (uint64_t)(((ptr1 >> 4) * 33) ^ (ptr2 >> 4));
}
namespace {
@@ -155,21 +156,16 @@ DepsgraphBuilderCache::DepsgraphBuilderCache()
DepsgraphBuilderCache::~DepsgraphBuilderCache()
{
- for (AnimatedPropertyStorageMap::value_type &iter : animated_property_storage_map_) {
- AnimatedPropertyStorage *animated_property_storage = iter.second;
- OBJECT_GUARDED_DELETE(animated_property_storage, AnimatedPropertyStorage);
+ for (AnimatedPropertyStorage *animated_property_storage :
+ animated_property_storage_map_.values()) {
+ delete animated_property_storage;
}
}
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 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..e04ae3a3727 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
@@ -23,6 +23,8 @@
#pragma once
+#include "MEM_guardedalloc.h"
+
#include "intern/depsgraph_type.h"
#include "RNA_access.h"
@@ -31,7 +33,8 @@ struct ID;
struct PointerRNA;
struct PropertyRNA;
-namespace DEG {
+namespace blender {
+namespace deg {
class DepsgraphBuilderCache;
@@ -44,14 +47,14 @@ class AnimatedPropertyID {
AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name);
AnimatedPropertyID(ID *id, StructRNA *type, void *data, const char *property_name);
- uint32_t hash() const;
-
- bool operator<(const AnimatedPropertyID &other) const;
+ uint64_t hash() const;
friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b);
/* Corresponds to PointerRNA.data. */
void *data;
const PropertyRNA *property_rna;
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyID");
};
class AnimatedPropertyStorage {
@@ -71,9 +74,9 @@ class AnimatedPropertyStorage {
/* indexed by PointerRNA.data. */
Set<AnimatedPropertyID> animated_properties_set;
-};
-typedef map<ID *, AnimatedPropertyStorage *> AnimatedPropertyStorageMap;
+ MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyStorage");
+};
/* Cached data which can be re-used by multiple builders. */
class DepsgraphBuilderCache {
@@ -100,7 +103,10 @@ class DepsgraphBuilderCache {
return animated_property_storage->isPropertyAnimated(args...);
}
- AnimatedPropertyStorageMap animated_property_storage_map_;
+ Map<ID *, AnimatedPropertyStorage *> animated_property_storage_map_;
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("DepsgraphBuilderCache");
};
-} // 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..6fb11c5b91e 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 {
@@ -76,22 +77,22 @@ struct CyclesSolverState {
int num_cycles;
};
-BLI_INLINE void set_node_visited_state(Node *node, eCyclicCheckVisitedState state)
+inline void set_node_visited_state(Node *node, eCyclicCheckVisitedState state)
{
node->custom_flags = (node->custom_flags & ~0x3) | (int)state;
}
-BLI_INLINE eCyclicCheckVisitedState get_node_visited_state(Node *node)
+inline eCyclicCheckVisitedState get_node_visited_state(Node *node)
{
return (eCyclicCheckVisitedState)(node->custom_flags & 0x3);
}
-BLI_INLINE void set_node_num_visited_children(Node *node, int num_children)
+inline void set_node_num_visited_children(Node *node, int num_children)
{
node->custom_flags = (node->custom_flags & 0x3) | (num_children << 2);
}
-BLI_INLINE int get_node_num_visited_children(Node *node)
+inline int get_node_num_visited_children(Node *node)
{
return node->custom_flags >> 2;
}
@@ -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..390e996b63f 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);
@@ -1110,6 +1126,12 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene)
if (object->type != OB_MESH) {
continue;
}
+ if (object->rigidbody_object == nullptr) {
+ continue;
+ }
+ if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) {
+ continue;
+ }
/* Create operation for flushing results. */
/* Object's transform component - where the rigidbody operation
* lives. */
@@ -1450,6 +1472,18 @@ void DepsgraphNodeBuilder::build_light(Light *lamp)
function_bind(BKE_light_eval, _1, lamp_cow));
}
+void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
+{
+ build_idproperties(socket->prop);
+
+ if (socket->type == SOCK_OBJECT) {
+ build_id((ID *)((bNodeSocketValueObject *)socket->default_value)->value);
+ }
+ else if (socket->type == SOCK_IMAGE) {
+ build_id((ID *)((bNodeSocketValueImage *)socket->default_value)->value);
+ }
+}
+
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
{
if (ntree == nullptr) {
@@ -1478,10 +1512,10 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
build_idproperties(bnode->prop);
LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) {
- build_idproperties(socket->prop);
+ build_nodetree_socket(socket);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) {
- build_idproperties(socket->prop);
+ build_nodetree_socket(socket);
}
ID *id = bnode->id;
@@ -1764,8 +1798,10 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation)
return;
}
add_id_node(&simulation->id);
+ build_idproperties(simulation->id.properties);
build_animdata(&simulation->id);
build_parameters(&simulation->id);
+ build_nodetree(simulation->nodetree);
Simulation *simulation_cow = get_cow_datablock(simulation);
Scene *scene_cow = get_cow_datablock(scene_);
@@ -1815,6 +1851,10 @@ void DepsgraphNodeBuilder::build_scene_audio(Scene *scene)
return;
}
+ OperationNode *audio_entry_node = add_operation_node(
+ &scene->id, NodeType::AUDIO, OperationCode::AUDIO_ENTRY);
+ audio_entry_node->set_as_entry();
+
add_operation_node(&scene->id, NodeType::AUDIO, OperationCode::SOUND_EVAL);
Scene *scene_cow = get_cow_datablock(scene);
@@ -1884,4 +1924,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..40f42705a52 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -31,6 +31,7 @@
#include "DEG_depsgraph.h"
+struct bNodeSocket;
struct CacheFile;
struct Camera;
struct Collection;
@@ -64,7 +65,8 @@ struct bNodeTree;
struct bPoseChannel;
struct bSound;
-namespace DEG {
+namespace blender {
+namespace deg {
struct ComponentNode;
struct Depsgraph;
@@ -168,6 +170,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,13 +205,14 @@ 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);
virtual void build_camera(Camera *camera);
virtual void build_light(Light *lamp);
virtual void build_nodetree(bNodeTree *ntree);
+ virtual void build_nodetree_socket(bNodeSocket *socket);
virtual void build_material(Material *ma);
virtual void build_materials(Material **materials, int num_materials);
virtual void build_freestyle_lineset(FreestyleLineSet *fls);
@@ -287,4 +293,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..5ae71dd1792 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -104,6 +104,7 @@
#include "intern/builder/deg_builder.h"
#include "intern/builder/deg_builder_pchanmap.h"
+#include "intern/builder/deg_builder_relations_drivers.h"
#include "intern/debug/deg_debug.h"
#include "intern/depsgraph_physics.h"
#include "intern/depsgraph_tag.h"
@@ -118,7 +119,8 @@
#include "intern/depsgraph_relation.h"
#include "intern/depsgraph_type.h"
-namespace DEG {
+namespace blender {
+namespace deg {
/* ***************** */
/* Relations Builder */
@@ -321,7 +323,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 +336,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 +498,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 +600,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 +623,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 +643,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 +735,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 +747,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)
@@ -1686,6 +1698,22 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
if (object->type != OB_MESH) {
continue;
}
+ if (object->rigidbody_object == nullptr) {
+ continue;
+ }
+ if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) {
+ continue;
+ }
+
+ if (object->parent != nullptr && object->parent->rigidbody_object != nullptr &&
+ object->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) {
+ /* If we are a child of a compound shape object, the transforms and sim evaluation will be
+ * handled by the parent compound shape object. Do not add any evaluation triggers
+ * for the child objects.
+ */
+ continue;
+ }
+
OperationKey rb_transform_copy_key(
&object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
/* Rigid body synchronization depends on the actual simulation. */
@@ -1729,19 +1757,24 @@ 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);
- OperationKey ob1_key(
- &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
- OperationKey ob2_key(
- &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
- /* Constrained-objects sync depends on the constraint-holder. */
- add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1");
- add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2");
+ if (rbc->ob1->rigidbody_object->type == RBO_TYPE_ACTIVE) {
+ OperationKey ob1_key(
+ &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
+ /* Constrained-objects sync depends on the constraint-holder. */
+ add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1");
+ }
+ if (rbc->ob2->rigidbody_object->type == RBO_TYPE_ACTIVE) {
+ OperationKey ob2_key(
+ &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY);
+ /* Constrained-objects sync depends on the constraint-holder. */
+ add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2");
+ }
/* Ensure that sim depends on this constraint's transform. */
add_relation(trans_key, rb_simulate_key, "RigidBodyConstraint Transform -> RB Simulation");
}
@@ -1820,7 +1853,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 +1865,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 +2156,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 +2265,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");
@@ -2264,6 +2297,24 @@ void DepsgraphRelationBuilder::build_light(Light *lamp)
add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters");
}
+void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
+{
+ build_idproperties(socket->prop);
+
+ if (socket->type == SOCK_OBJECT) {
+ Object *object = ((bNodeSocketValueObject *)socket->default_value)->value;
+ if (object != nullptr) {
+ build_object(object);
+ }
+ }
+ else if (socket->type == SOCK_IMAGE) {
+ Image *image = ((bNodeSocketValueImage *)socket->default_value)->value;
+ if (image != nullptr) {
+ build_image(image);
+ }
+ }
+}
+
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
{
if (ntree == nullptr) {
@@ -2280,10 +2331,10 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
build_idproperties(bnode->prop);
LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) {
- build_idproperties(socket->prop);
+ build_nodetree_socket(socket);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) {
- build_idproperties(socket->prop);
+ build_nodetree_socket(socket);
}
ID *id = bnode->id;
@@ -2307,7 +2358,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 +2374,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) {
@@ -2590,13 +2641,39 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation)
if (built_map_.checkIsBuiltAndTag(simulation)) {
return;
}
+ build_idproperties(simulation->id.properties);
build_animdata(&simulation->id);
build_parameters(&simulation->id);
- OperationKey simulation_update_key(
+ build_nodetree(simulation->nodetree);
+ build_nested_nodetree(&simulation->id, simulation->nodetree);
+
+ OperationKey simulation_eval_key(
&simulation->id, NodeType::SIMULATION, OperationCode::SIMULATION_EVAL);
TimeSourceKey time_src_key;
- add_relation(time_src_key, simulation_update_key, "TimeSrc -> Simulation");
+ add_relation(time_src_key, simulation_eval_key, "TimeSrc -> Simulation");
+
+ OperationKey nodetree_key(
+ &simulation->nodetree->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT);
+ add_relation(nodetree_key, simulation_eval_key, "NodeTree -> Simulation", 0);
+
+ LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
+ if (dependency->id == nullptr) {
+ continue;
+ }
+ build_id(dependency->id);
+ if (GS(dependency->id->name) == ID_OB) {
+ Object *object = (Object *)dependency->id;
+ if (dependency->flag & SIM_DEPENDS_ON_TRANSFORM) {
+ ComponentKey object_transform_key(&object->id, NodeType::TRANSFORM);
+ add_relation(object_transform_key, simulation_eval_key, "Object Transform -> Simulation");
+ }
+ if (dependency->flag & SIM_DEPENDS_ON_GEOMETRY) {
+ ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY);
+ add_relation(object_geometry_key, simulation_eval_key, "Object Geometry -> Simulation");
+ }
+ }
+ }
}
void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene)
@@ -2644,8 +2721,10 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene)
void DepsgraphRelationBuilder::build_scene_audio(Scene *scene)
{
+ OperationKey scene_audio_entry_key(&scene->id, NodeType::AUDIO, OperationCode::AUDIO_ENTRY);
OperationKey scene_audio_volume_key(&scene->id, NodeType::AUDIO, OperationCode::AUDIO_VOLUME);
OperationKey scene_sound_eval_key(&scene->id, NodeType::AUDIO, OperationCode::SOUND_EVAL);
+ add_relation(scene_audio_entry_key, scene_audio_volume_key, "Audio Entry -> Volume");
add_relation(scene_audio_volume_key, scene_sound_eval_key, "Audio Volume -> Sound");
if (scene->audio.flag & AUDIO_VOLUME_ANIMATED) {
@@ -2661,7 +2740,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);
}
}
@@ -2830,123 +2909,6 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node)
#endif
}
-static bool is_reachable(const Node *const from, const Node *const to)
-{
- if (from == to) {
- return true;
- }
-
- // Perform a graph walk from 'to' towards its incoming connections.
- // Walking from 'from' towards its outgoing connections is 10x slower on the Spring rig.
- deque<const Node *> queue;
- Set<const Node *> seen;
- queue.push_back(to);
- while (!queue.empty()) {
- // Visit the next node to inspect.
- const Node *visit = queue.back();
- queue.pop_back();
-
- if (visit == from) {
- return true;
- }
-
- // Queue all incoming relations that we haven't seen before.
- for (Relation *relation : visit->inlinks) {
- const Node *prev_node = relation->from;
- if (seen.add(prev_node)) {
- queue.push_back(prev_node);
- }
- }
- }
- return false;
-}
-
-void DepsgraphRelationBuilder::build_driver_relations()
-{
- for (IDNode *id_node : graph_->id_nodes) {
- build_driver_relations(id_node);
- }
-}
-
-void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node)
-{
- /* Add relations between drivers that write to the same datablock.
- *
- * This prevents threading issues when two separate RNA properties write to
- * the same memory address. For example:
- * - Drivers on individual array elements, as the animation system will write
- * the whole array back to RNA even when changing individual array value.
- * - Drivers on RNA properties that map to a single bit flag. Changing the RNA
- * value will write the entire int containing the bit, in a non-thread-safe
- * way.
- */
- ID *id_orig = id_node->id_orig;
- AnimData *adt = BKE_animdata_from_id(id_orig);
- if (adt == nullptr) {
- return;
- }
-
- // Mapping from RNA prefix -> set of driver evaluation nodes:
- typedef Vector<Node *> DriverGroup;
- typedef map<string, DriverGroup> DriverGroupMap;
- DriverGroupMap driver_groups;
-
- LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
- if (fcu->rna_path == nullptr) {
- continue;
- }
- // Get the RNA path except the part after the last dot.
- char *last_dot = strrchr(fcu->rna_path, '.');
- string rna_prefix;
- if (last_dot != nullptr) {
- rna_prefix = string(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);
- }
-
- for (pair<string, DriverGroup> prefix_group : driver_groups) {
- // 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();
- 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];
-
- // 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];
-
- // Investigate whether this relation would create a dependency cycle.
- // Example graph:
- // A -> B -> C
- // and investigating a potential connection C->A. Because A->C is an
- // existing transitive connection, adding C->A would create a cycle.
- if (is_reachable(op_to, op_from)) {
- continue;
- }
-
- // No need to directly connect this node if there is already a transitive connection.
- if (is_reachable(op_from, op_to)) {
- break;
- }
-
- add_operation_relation(
- op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialisation");
- break;
- }
- }
- }
-} // namespace DEG
-
/* **** ID traversal callbacks functions **** */
void DepsgraphRelationBuilder::modifier_walk(void *user_data,
@@ -2975,4 +2937,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..04f2a3f911d 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -46,6 +46,7 @@
#include "intern/node/deg_node_operation.h"
struct Base;
+struct bNodeSocket;
struct CacheFile;
struct Camera;
struct Collection;
@@ -84,7 +85,8 @@ struct bSound;
struct PropertyRNA;
-namespace DEG {
+namespace blender {
+namespace deg {
struct ComponentNode;
struct DepsNodeHandle;
@@ -210,10 +212,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);
@@ -274,6 +276,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_camera(Camera *camera);
virtual void build_light(Light *lamp);
virtual void build_nodetree(bNodeTree *ntree);
+ virtual void build_nodetree_socket(bNodeSocket *socket);
virtual void build_material(Material *ma);
virtual void build_materials(Material **materials, int num_materials);
virtual void build_freestyle_lineset(FreestyleLineSet *fls);
@@ -385,6 +388,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_drivers.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.cc
new file mode 100644
index 00000000000..717c8300f9b
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.cc
@@ -0,0 +1,258 @@
+/*
+ * 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) 2013 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup depsgraph
+ *
+ * Methods for constructing depsgraph relations for drivers.
+ */
+
+#include "intern/builder/deg_builder_relations_drivers.h"
+
+#include <cstring>
+
+#include "DNA_anim_types.h"
+
+#include "BKE_anim_data.h"
+
+#include "intern/builder/deg_builder_relations.h"
+#include "intern/depsgraph_relation.h"
+#include "intern/node/deg_node.h"
+
+namespace blender {
+namespace deg {
+
+DriverDescriptor::DriverDescriptor(PointerRNA *id_ptr, FCurve *fcu)
+ : rna_prefix(),
+ rna_suffix(),
+ id_ptr_(id_ptr),
+ fcu_(fcu),
+ driver_relations_needed_(false),
+ pointer_rna_(),
+ property_rna_(nullptr),
+ is_array_(false)
+{
+ driver_relations_needed_ = determine_relations_needed();
+ split_rna_path();
+}
+
+bool DriverDescriptor::determine_relations_needed()
+{
+ if (fcu_->array_index > 0) {
+ /* Drivers on array elements always need relations. */
+ is_array_ = true;
+ return true;
+ }
+
+ if (!resolve_rna()) {
+ /* Properties that don't exist can't cause threading issues either. */
+ return false;
+ }
+
+ if (RNA_property_array_check(property_rna_)) {
+ /* Drivers on array elements always need relations. */
+ is_array_ = true;
+ return true;
+ }
+
+ /* Drivers on Booleans and Enums (when used as bitflags) can write to the same memory location,
+ * so they need relations between each other. */
+ return ELEM(RNA_property_type(property_rna_), PROP_BOOLEAN, PROP_ENUM);
+}
+
+bool DriverDescriptor::driver_relations_needed() const
+{
+ return driver_relations_needed_;
+}
+
+bool DriverDescriptor::is_array() const
+{
+ return is_array_;
+}
+
+/* Assumes that 'other' comes from the same RNA group, that is, has the same RNA path prefix. */
+bool DriverDescriptor::is_same_array_as(const DriverDescriptor &other) const
+{
+ if (!is_array_ || !other.is_array_) {
+ return false;
+ }
+ return rna_suffix == other.rna_suffix;
+}
+
+OperationKey DriverDescriptor::depsgraph_key() const
+{
+ return OperationKey(id_ptr_->owner_id,
+ NodeType::PARAMETERS,
+ OperationCode::DRIVER,
+ fcu_->rna_path,
+ fcu_->array_index);
+}
+
+void DriverDescriptor::split_rna_path()
+{
+ const char *last_dot = strrchr(fcu_->rna_path, '.');
+ if (last_dot == nullptr || last_dot[1] == '\0') {
+ rna_prefix = StringRef();
+ rna_suffix = StringRef(fcu_->rna_path);
+ return;
+ }
+
+ rna_prefix = StringRef(fcu_->rna_path, last_dot);
+ rna_suffix = StringRef(last_dot + 1);
+}
+
+bool DriverDescriptor::resolve_rna()
+{
+ return RNA_path_resolve_property(id_ptr_, fcu_->rna_path, &pointer_rna_, &property_rna_);
+}
+
+static bool is_reachable(const Node *const from, const Node *const to)
+{
+ if (from == to) {
+ return true;
+ }
+
+ // Perform a graph walk from 'to' towards its incoming connections.
+ // Walking from 'from' towards its outgoing connections is 10x slower on the Spring rig.
+ deque<const Node *> queue;
+ Set<const Node *> seen;
+ queue.push_back(to);
+ while (!queue.empty()) {
+ // Visit the next node to inspect.
+ const Node *visit = queue.back();
+ queue.pop_back();
+
+ if (visit == from) {
+ return true;
+ }
+
+ // Queue all incoming relations that we haven't seen before.
+ for (Relation *relation : visit->inlinks) {
+ const Node *prev_node = relation->from;
+ if (seen.add(prev_node)) {
+ queue.push_back(prev_node);
+ }
+ }
+ }
+ return false;
+}
+
+/* **** DepsgraphRelationBuilder functions **** */
+
+void DepsgraphRelationBuilder::build_driver_relations()
+{
+ for (IDNode *id_node : graph_->id_nodes) {
+ build_driver_relations(id_node);
+ }
+}
+
+void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node)
+{
+ /* Add relations between drivers that write to the same datablock.
+ *
+ * This prevents threading issues when two separate RNA properties write to
+ * the same memory address. For example:
+ * - Drivers on individual array elements, as the animation system will write
+ * the whole array back to RNA even when changing individual array value.
+ * - Drivers on RNA properties that map to a single bit flag. Changing the RNA
+ * value will write the entire int containing the bit, in a non-thread-safe
+ * way.
+ */
+ ID *id_orig = id_node->id_orig;
+ AnimData *adt = BKE_animdata_from_id(id_orig);
+ if (adt == nullptr) {
+ return;
+ }
+
+ // Mapping from RNA prefix -> set of driver descriptors:
+ Map<string, Vector<DriverDescriptor>> driver_groups;
+
+ PointerRNA id_ptr;
+ RNA_id_pointer_create(id_orig, &id_ptr);
+
+ LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
+ if (fcu->rna_path == nullptr) {
+ continue;
+ }
+
+ DriverDescriptor driver_desc(&id_ptr, fcu);
+ if (!driver_desc.driver_relations_needed()) {
+ continue;
+ }
+
+ driver_groups.lookup_or_add_default_as(driver_desc.rna_prefix).append(driver_desc);
+ }
+
+ for (Span<DriverDescriptor> 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.size();
+ if (num_drivers < 2) {
+ // A relation requires two drivers.
+ continue;
+ }
+ for (int from_index = 0; from_index < num_drivers; ++from_index) {
+ const DriverDescriptor &driver_from = prefix_group[from_index];
+ Node *op_from = get_node(driver_from.depsgraph_key());
+
+ // Start by trying the next node in the group.
+ for (int to_offset = 1; to_offset < num_drivers; ++to_offset) {
+ const int to_index = (from_index + to_offset) % num_drivers;
+ const DriverDescriptor &driver_to = prefix_group[to_index];
+ Node *op_to = get_node(driver_to.depsgraph_key());
+
+ // Duplicate drivers can exist (see T78615), but cannot be distinguished by OperationKey
+ // and thus have the same depsgraph node. Relations between those drivers should not be
+ // created. This not something that is expected to happen (both the UI and the Python API
+ // prevent duplicate drivers), it did happen in a file and it is easy to deal with here.
+ if (op_from == op_to) {
+ continue;
+ }
+
+ if (from_index < to_index && driver_from.is_same_array_as(driver_to)) {
+ // This is for adding a relation like `color[0]` -> `color[1]`.
+ // When the search for another driver wraps around, we cannot blindly add relations any
+ // more.
+ }
+ else {
+ // Investigate whether this relation would create a dependency cycle.
+ // Example graph:
+ // A -> B -> C
+ // and investigating a potential connection C->A. Because A->C is an
+ // existing transitive connection, adding C->A would create a cycle.
+ if (is_reachable(op_to, op_from)) {
+ continue;
+ }
+
+ // No need to directly connect this node if there is already a transitive connection.
+ if (is_reachable(op_from, op_to)) {
+ break;
+ }
+ }
+
+ add_operation_relation(
+ op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialization");
+ break;
+ }
+ }
+ }
+}
+
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h
new file mode 100644
index 00000000000..c80c69be9e2
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h
@@ -0,0 +1,76 @@
+/*
+ * 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) 2013 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "RNA_types.h"
+
+#include "intern/builder/deg_builder_relations.h"
+
+struct FCurve;
+
+namespace blender {
+namespace deg {
+
+/* Helper class for determining which relations are needed between driver evaluation nodes. */
+class DriverDescriptor {
+ public:
+ /* Drivers are grouped by their RNA prefix. The prefix is the part of the RNA
+ * path up to the last dot, the suffix is the remainder of the RNA path:
+ *
+ * fcu->rna_path rna_prefix rna_suffix
+ * ------------------------------- ---------------------- ----------
+ * 'color' '' 'color'
+ * 'rigidbody_world.time_scale' 'rigidbody_world' 'time_scale'
+ * 'pose.bones["master"].location' 'pose.bones["master"]' 'location'
+ */
+ StringRef rna_prefix;
+ StringRef rna_suffix;
+
+ public:
+ DriverDescriptor(PointerRNA *id_ptr, FCurve *fcu);
+
+ bool driver_relations_needed() const;
+ bool is_array() const;
+ /* Assumes that 'other' comes from the same RNA group, that is, has the same RNA path prefix. */
+ bool is_same_array_as(const DriverDescriptor &other) const;
+ OperationKey depsgraph_key() const;
+
+ private:
+ PointerRNA *id_ptr_;
+ FCurve *fcu_;
+ bool driver_relations_needed_;
+
+ PointerRNA pointer_rna_;
+ PropertyRNA *property_rna_;
+ bool is_array_;
+
+ bool determine_relations_needed();
+ void split_rna_path();
+ bool resolve_rna();
+};
+
+} // namespace deg
+} // namespace blender
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..8d51fa1422a 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)
{
@@ -67,7 +68,7 @@ void deg_graph_remove_unused_noops(Depsgraph *graph)
/* Remove the relation. */
rel_in->unlink();
- OBJECT_GUARDED_DELETE(rel_in, Relation);
+ delete rel_in;
num_removed_relations++;
/* Queue parent no-op node that has now become unused. */
@@ -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..e3b667427a2 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 ******************************** */
@@ -148,6 +149,25 @@ Node *RNANodeQuery::find_node(const PointerRNA *ptr,
node_identifier.operation_name_tag);
}
+bool RNANodeQuery::contains(const char *prop_identifier, const char *rna_path_component)
+{
+ const char *substr = strstr(prop_identifier, rna_path_component);
+ if (substr == nullptr) {
+ return false;
+ }
+
+ // If substr != prop_identifier, it means that the substring is found further in prop_identifier,
+ // and that thus index -1 is a valid memory location.
+ const bool start_ok = substr == prop_identifier || substr[-1] == '.';
+ if (!start_ok) {
+ return false;
+ }
+
+ const size_t component_len = strlen(rna_path_component);
+ const bool end_ok = ELEM(substr[component_len], '\0', '.', '[');
+ return end_ok;
+}
+
RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
const PropertyRNA *prop,
RNAPointerSource source)
@@ -282,12 +302,20 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
if (prop != nullptr) {
const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop);
/* TODO(sergey): How to optimize this? */
- if (strstr(prop_identifier, "location") || strstr(prop_identifier, "rotation") ||
- strstr(prop_identifier, "scale") || strstr(prop_identifier, "matrix_")) {
+ if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") ||
+ contains(prop_identifier, "matrix_channel") ||
+ contains(prop_identifier, "matrix_inverse") ||
+ contains(prop_identifier, "matrix_local") ||
+ contains(prop_identifier, "matrix_parent_inverse") ||
+ contains(prop_identifier, "matrix_world") ||
+ contains(prop_identifier, "rotation_axis_angle") ||
+ contains(prop_identifier, "rotation_euler") ||
+ contains(prop_identifier, "rotation_mode") ||
+ contains(prop_identifier, "rotation_quaternion") || contains(prop_identifier, "scale")) {
node_identifier.type = NodeType::TRANSFORM;
return node_identifier;
}
- else if (strstr(prop_identifier, "data")) {
+ else if (contains(prop_identifier, "data")) {
/* We access object.data, most likely a geometry.
* Might be a bone tho. */
node_identifier.type = NodeType::GEOMETRY;
@@ -365,8 +393,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..d03903d508c 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;
@@ -92,6 +93,20 @@ class RNANodeQuery {
/* Make sure ID data exists for the given ID, and returns it. */
RNANodeQueryIDData *ensure_id_data(const ID *id);
+
+ /* Check whether prop_identifier contains rna_path_component.
+ *
+ * This checks more than a sub-string:
+ *
+ * prop_identifier contains(prop_identifier, "location")
+ * ------------------------ -------------------------------------
+ * location true
+ * ["test_location"] false
+ * pose["bone"].location true
+ * pose["bone"].location.x true
+ */
+ static bool contains(const char *prop_identifier, const char *rna_path_component);
};
-} // namespace DEG
+} // namespace deg
+} // namespace blender
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna_test.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna_test.cc
new file mode 100644
index 00000000000..c91dda87190
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna_test.cc
@@ -0,0 +1,60 @@
+/*
+ * 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 depsgraph
+ */
+
+#include "intern/builder/deg_builder_rna.h"
+
+#include "testing/testing.h"
+
+namespace blender {
+namespace deg {
+namespace tests {
+
+class TestableRNANodeQuery : public RNANodeQuery {
+ public:
+ static bool contains(const char *prop_identifier, const char *rna_path_component)
+ {
+ return RNANodeQuery::contains(prop_identifier, rna_path_component);
+ }
+};
+
+TEST(deg_builder_rna, contains)
+{
+ EXPECT_TRUE(TestableRNANodeQuery::contains("location", "location"));
+ EXPECT_TRUE(TestableRNANodeQuery::contains("location.x", "location"));
+ EXPECT_TRUE(TestableRNANodeQuery::contains("pose.bone[\"blork\"].location", "location"));
+ EXPECT_TRUE(TestableRNANodeQuery::contains("pose.bone[\"blork\"].location.x", "location"));
+ EXPECT_TRUE(TestableRNANodeQuery::contains("pose.bone[\"blork\"].location[0]", "location"));
+
+ EXPECT_FALSE(TestableRNANodeQuery::contains("", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("locatio", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("locationnn", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("test_location", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("location_test", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("test_location_test", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("pose.bone[\"location\"].scale", "location"));
+ EXPECT_FALSE(TestableRNANodeQuery::contains("pose.bone[\"location\"].scale[0]", "location"));
+}
+
+} // namespace tests
+} // 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..bd6a364e08a 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 {
/* -------------------------------------------------- */
@@ -101,7 +102,7 @@ void deg_graph_transitive_reduction(Depsgraph *graph)
}
for (Relation *rel : relations_to_remove) {
rel->unlink();
- OBJECT_GUARDED_DELETE(rel, Relation);
+ delete rel;
}
num_removed_relations += relations_to_remove.size();
relations_to_remove.clear();
@@ -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..515b53297b9 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 {
@@ -52,9 +55,8 @@ struct StatsEntry {
};
/* TODO(sergey): De-duplicate with graphviz relation debugger. */
-static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
- ATTR_PRINTF_FORMAT(2, 3);
-static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
+void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3);
+void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
@@ -62,7 +64,7 @@ static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
va_end(args);
}
-BLI_INLINE double get_node_time(const DebugContext & /*ctx*/, const Node *node)
+inline double get_node_time(const DebugContext & /*ctx*/, const Node *node)
{
// TODO(sergey): Figure out a nice way to define which exact time
// we want to show.
@@ -147,7 +149,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 +160,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..4a9c840dd9f 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),
@@ -80,9 +83,7 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati
Depsgraph::~Depsgraph()
{
clear_id_nodes();
- if (time_source != nullptr) {
- OBJECT_GUARDED_DELETE(time_source, TimeSourceNode);
- }
+ delete time_source;
BLI_spin_end(&lock);
}
@@ -154,7 +155,7 @@ void Depsgraph::clear_id_nodes()
clear_id_nodes_conditional([](ID_Type id_type) { return id_type != ID_PA; });
for (IDNode *id_node : id_nodes) {
- OBJECT_GUARDED_DELETE(id_node, IDNode);
+ delete id_node;
}
/* Clear containers. */
id_hash.clear();
@@ -185,7 +186,7 @@ Relation *Depsgraph::add_new_relation(Node *from, Node *to, const char *descript
#endif
/* Create new relation, and add it to the graph. */
- rel = OBJECT_GUARDED_NEW(Relation, from, to, description);
+ rel = new Relation(from, to, description);
rel->flag |= flags;
return rel;
}
@@ -226,10 +227,8 @@ void Depsgraph::add_entry_tag(OperationNode *node)
void Depsgraph::clear_all_nodes()
{
clear_id_nodes();
- if (time_source != nullptr) {
- OBJECT_GUARDED_DELETE(time_source, TimeSourceNode);
- time_source = nullptr;
- }
+ delete time_source;
+ time_source = nullptr;
}
ID *Depsgraph::get_cow_id(const ID *id_orig) const
@@ -258,7 +257,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 +266,8 @@ 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 = new deg::Depsgraph(bmain, scene, view_layer, mode);
+ deg::register_graph(deg_depsgraph);
return reinterpret_cast<Depsgraph *>(deg_depsgraph);
}
@@ -279,11 +278,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 +290,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 +300,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);
- OBJECT_GUARDED_DELETE(deg_depsgraph, Depsgraph);
+ using deg::Depsgraph;
+ deg::Depsgraph *deg_depsgraph = reinterpret_cast<deg::Depsgraph *>(graph);
+ deg::unregister_graph(deg_depsgraph);
+ delete deg_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 +321,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..ea579a4958e 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -31,6 +31,8 @@
#include <stdlib.h>
+#include "MEM_guardedalloc.h"
+
#include "DNA_ID.h" /* for ID_Type */
#include "BKE_main.h" /* for MAX_LIBARRAY */
@@ -47,7 +49,8 @@ struct ID;
struct Scene;
struct ViewLayer;
-namespace DEG {
+namespace blender {
+namespace deg {
struct IDNode;
struct Node;
@@ -167,6 +170,9 @@ struct Depsgraph {
/* Cached list of colliders/effectors for collections and the scene
* created along with relations, for fast lookup during evaluation. */
Map<const ID *, ListBase *> *physics_relations[DEG_PHYSICS_RELATIONS_NUM];
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph");
};
-} // 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..814f467e3d5 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 {
@@ -96,7 +98,7 @@ void verify_id_properties_freed(DEGObjectIterData *data)
temp_dupli_object->id.properties = nullptr;
}
-static bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject *dob)
+bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject *dob)
{
/* Automatic hiding if this object is being instanced on verts/faces/frames
* by its parent. Ideally this should not be needed, but due to the wrong
@@ -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..623702ee3ae 100644
--- a/source/blender/depsgraph/intern/depsgraph_registry.cc
+++ b/source/blender/depsgraph/intern/depsgraph_registry.cc
@@ -27,35 +27,43 @@
#include "intern/depsgraph.h"
-namespace DEG {
+namespace blender {
+namespace deg {
-static Map<Main *, VectorSet<Depsgraph *>> g_graph_registry;
+using GraphRegistry = Map<Main *, VectorSet<Depsgraph *>>;
+static GraphRegistry &get_graph_registry()
+{
+ static GraphRegistry graph_registry;
+ return graph_registry;
+}
void register_graph(Depsgraph *depsgraph)
{
Main *bmain = depsgraph->bmain;
- g_graph_registry.lookup_or_add_default(bmain).add_new(depsgraph);
+ get_graph_registry().lookup_or_add_default(bmain).add_new(depsgraph);
}
void unregister_graph(Depsgraph *depsgraph)
{
Main *bmain = depsgraph->bmain;
- VectorSet<Depsgraph *> &graphs = g_graph_registry.lookup(bmain);
+ GraphRegistry &graph_registry = get_graph_registry();
+ VectorSet<Depsgraph *> &graphs = graph_registry.lookup(bmain);
graphs.remove(depsgraph);
// If this was the last depsgraph associated with the main, remove the main entry as well.
if (graphs.is_empty()) {
- g_graph_registry.remove(bmain);
+ graph_registry.remove(bmain);
}
}
Span<Depsgraph *> get_all_registered_graphs(Main *bmain)
{
- VectorSet<Depsgraph *> *graphs = g_graph_registry.lookup_ptr(bmain);
+ VectorSet<Depsgraph *> *graphs = get_graph_registry().lookup_ptr(bmain);
if (graphs != nullptr) {
return *graphs;
}
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..7154710e07e 100644
--- a/source/blender/depsgraph/intern/depsgraph_relation.h
+++ b/source/blender/depsgraph/intern/depsgraph_relation.h
@@ -23,7 +23,10 @@
#pragma once
-namespace DEG {
+#include "MEM_guardedalloc.h"
+
+namespace blender {
+namespace deg {
struct Node;
@@ -58,6 +61,9 @@ struct Relation {
/* relationship attributes */
const char *name; /* label for debugging */
int flag; /* Bitmask of RelationFlag) */
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("Relation");
};
-} // 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 0ee86088e43..1863a333930 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;
@@ -237,6 +240,8 @@ void depsgraph_tag_to_component_opcode(const ID *id,
case ID_RECALC_PSYS_ALL:
BLI_assert(!"Should not happen");
break;
+ case ID_RECALC_TAG_FOR_UNDO:
+ break; /* Must be ignored by depsgraph. */
}
}
@@ -357,12 +362,12 @@ void deg_graph_id_tag_legacy_compat(
}
}
-static void graph_id_tag_update_single_flag(Main *bmain,
- Depsgraph *graph,
- ID *id,
- IDNode *id_node,
- IDRecalcFlag tag,
- eUpdateSource update_source)
+void graph_id_tag_update_single_flag(Main *bmain,
+ Depsgraph *graph,
+ ID *id,
+ IDNode *id_node,
+ IDRecalcFlag tag,
+ eUpdateSource update_source)
{
if (tag == ID_RECALC_EDITORS) {
if (graph != nullptr && graph->is_active) {
@@ -508,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);
@@ -523,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) {
@@ -617,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);
}
@@ -682,7 +687,8 @@ void graph_id_tag_update(
}
}
-} // namespace DEG
+} // namespace deg
+} // namespace blender
const char *DEG_update_tag_as_string(IDRecalcFlag flag)
{
@@ -737,6 +743,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag)
return "SOURCE";
case ID_RECALC_ALL:
return "ALL";
+ case ID_RECALC_TAG_FOR_UNDO:
+ return "TAG_FOR_UNDO";
}
return nullptr;
}
@@ -755,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,
@@ -763,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. */
@@ -778,15 +786,16 @@ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type)
DEG_graph_id_type_tag(depsgraph, ID_LA);
DEG_graph_id_type_tag(depsgraph, ID_WO);
DEG_graph_id_type_tag(depsgraph, ID_SCE);
+ DEG_graph_id_type_tag(depsgraph, ID_SIM);
}
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);
}
}
@@ -794,13 +803,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);
}
}
@@ -817,7 +826,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)
@@ -833,14 +842,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..82cb311ec45 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) \
@@ -119,6 +120,7 @@ union NestedIDHackTempStorage {
Scene scene;
Tex tex;
World world;
+ Simulation simulation;
};
/* Set nested owned ID pointers to nullptr. */
@@ -136,6 +138,7 @@ void nested_id_hack_discard_pointers(ID *id_cow)
SPECIAL_CASE(ID_MA, Material, nodetree)
SPECIAL_CASE(ID_TE, Tex, nodetree)
SPECIAL_CASE(ID_WO, World, nodetree)
+ SPECIAL_CASE(ID_SIM, Simulation, nodetree)
SPECIAL_CASE(ID_CU, Curve, key)
SPECIAL_CASE(ID_LT, Lattice, key)
@@ -184,6 +187,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage
SPECIAL_CASE(ID_MA, Material, nodetree, material)
SPECIAL_CASE(ID_TE, Tex, nodetree, tex)
SPECIAL_CASE(ID_WO, World, nodetree, world)
+ SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation)
SPECIAL_CASE(ID_CU, Curve, key, curve)
SPECIAL_CASE(ID_LT, Lattice, key, lattice)
@@ -223,6 +227,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id)
SPECIAL_CASE(ID_SCE, Scene, nodetree)
SPECIAL_CASE(ID_TE, Tex, nodetree)
SPECIAL_CASE(ID_WO, World, nodetree)
+ SPECIAL_CASE(ID_SIM, Simulation, nodetree)
SPECIAL_CASE(ID_CU, Curve, key)
SPECIAL_CASE(ID_LT, Lattice, key)
@@ -260,6 +265,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow)
SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree)
SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree)
SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree)
+ SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree)
SPECIAL_CASE(ID_CU, Curve, key, Key)
SPECIAL_CASE(ID_LT, Lattice, key, Key)
@@ -495,7 +501,7 @@ void update_scene_orig_pointers(const Scene *scene_orig, Scene *scene_cow)
}
/* Check whether given ID is expanded or still a shallow copy. */
-BLI_INLINE bool check_datablock_expanded(const ID *id_cow)
+inline bool check_datablock_expanded(const ID *id_cow)
{
return (id_cow->name[0] != '\0');
}
@@ -710,13 +716,6 @@ void update_modifiers_orig_pointers(const Object *object_orig, Object *object_co
&object_orig->modifiers, &object_cow->modifiers, &ModifierData::orig_modifier_data);
}
-void update_simulation_states_orig_pointers(const Simulation *simulation_orig,
- Simulation *simulation_cow)
-{
- update_list_orig_pointers(
- &simulation_orig->states, &simulation_cow->states, &SimulationState::orig_state);
-}
-
void update_nla_strips_orig_pointers(const ListBase *strips_orig, ListBase *strips_cow)
{
NlaStrip *strip_orig = reinterpret_cast<NlaStrip *>(strips_orig->first);
@@ -816,12 +815,6 @@ void update_id_after_copy(const Depsgraph *depsgraph,
update_scene_orig_pointers(scene_orig, scene_cow);
break;
}
- case ID_SIM: {
- Simulation *simulation_cow = (Simulation *)id_cow;
- const Simulation *simulation_orig = (const Simulation *)id_orig;
- update_simulation_states_orig_pointers(simulation_orig, simulation_cow);
- break;
- }
default:
break;
}
@@ -945,7 +938,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 +962,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 +1082,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 +1130,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 ee543dcf25d..a74ec485d88 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,
@@ -96,7 +99,7 @@ void flush_init_id_node_func(void *__restrict data_v,
}
}
-BLI_INLINE void flush_prepare(Depsgraph *graph)
+inline void flush_prepare(Depsgraph *graph)
{
for (OperationNode *node : graph->operations) {
node->scheduled = false;
@@ -111,7 +114,7 @@ BLI_INLINE void flush_prepare(Depsgraph *graph)
}
}
-BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
+inline void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
{
for (OperationNode *op_node : graph->entry_tags) {
queue->push_back(op_node);
@@ -123,15 +126,15 @@ BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
}
}
-BLI_INLINE void flush_handle_id_node(IDNode *id_node)
+inline void flush_handle_id_node(IDNode *id_node)
{
id_node->custom_flags = ID_STATE_MODIFIED;
}
/* TODO(sergey): We can reduce number of arguments here. */
-BLI_INLINE void flush_handle_component_node(IDNode *id_node,
- ComponentNode *comp_node,
- FlushQueue *queue)
+inline void flush_handle_component_node(IDNode *id_node,
+ ComponentNode *comp_node,
+ FlushQueue *queue)
{
/* We only handle component once. */
if (comp_node->custom_flags == COMPONENT_STATE_DONE) {
@@ -166,7 +169,7 @@ BLI_INLINE void flush_handle_component_node(IDNode *id_node,
* return value, so it can start being handled right away, without building too
* much of a queue.
*/
-BLI_INLINE OperationNode *flush_schedule_children(OperationNode *op_node, FlushQueue *queue)
+inline OperationNode *flush_schedule_children(OperationNode *op_node, FlushQueue *queue)
{
if (op_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
IDNode *id_node = op_node->owner->owner;
@@ -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,10 +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);
- }
- /* ID may need to get its auto-override operations refreshed. */
- if (ID_IS_OVERRIDE_LIBRARY_AUTO(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);
@@ -337,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;
@@ -390,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..934403674a9 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
+uint64_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..a5bdf2359ee 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);
+
+ uint64_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..9e386f13888 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)
{
@@ -301,7 +302,7 @@ Node::~Node()
* when we're trying to free same link from both it's sides. We don't have
* dangling links so this is not a problem from memory leaks point of view. */
for (Relation *rel : inlinks) {
- OBJECT_GUARDED_DELETE(rel, Relation);
+ delete rel;
}
}
@@ -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..188022fa2ae 100644
--- a/source/blender/depsgraph/intern/node/deg_node.h
+++ b/source/blender/depsgraph/intern/node/deg_node.h
@@ -23,6 +23,8 @@
#pragma once
+#include "MEM_guardedalloc.h"
+
#include "intern/depsgraph_type.h"
#include "BLI_utildefines.h"
@@ -32,7 +34,8 @@
struct ID;
struct Scene;
-namespace DEG {
+namespace blender {
+namespace deg {
struct Depsgraph;
struct OperationNode;
@@ -203,6 +206,8 @@ struct Node {
}
virtual NodeClass get_class() const;
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("Node");
};
/* Macros for common static typeinfo. */
@@ -212,4 +217,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..cd82b7be050 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 */
@@ -71,7 +72,7 @@ bool ComponentNode::OperationIDKey::operator==(const OperationIDKey &other) cons
return (opcode == other.opcode) && (STREQ(name, other.name)) && (name_tag == other.name_tag);
}
-uint32_t ComponentNode::OperationIDKey::hash() const
+uint64_t ComponentNode::OperationIDKey::hash() const
{
const int opcode_as_int = static_cast<int>(opcode);
return BLI_ghashutil_combine_hash(
@@ -97,9 +98,7 @@ void ComponentNode::init(const ID * /*id*/, const char * /*subdata*/)
ComponentNode::~ComponentNode()
{
clear_operations();
- if (operations_map != nullptr) {
- delete operations_map;
- }
+ delete operations_map;
}
string ComponentNode::identifier() const
@@ -220,12 +219,12 @@ void ComponentNode::clear_operations()
{
if (operations_map != nullptr) {
for (OperationNode *op_node : operations_map->values()) {
- OBJECT_GUARDED_DELETE(op_node, OperationNode);
+ delete op_node;
}
operations_map->clear();
}
for (OperationNode *op_node : operations) {
- OBJECT_GUARDED_DELETE(op_node, OperationNode);
+ delete op_node;
}
operations.clear();
}
@@ -377,4 +376,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..06582c88d8b 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;
@@ -53,7 +54,7 @@ struct ComponentNode : public Node {
string identifier() const;
bool operator==(const OperationIDKey &other) const;
- uint32_t hash() const;
+ uint64_t hash() const;
};
/* Typedef for container of operations */
@@ -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..0fc7e496ed9 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
{
@@ -49,7 +50,7 @@ Node *DepsNodeFactoryImpl<ModeObjectType>::create_node(const ID *id,
const char *subdata,
const char *name) const
{
- Node *node = OBJECT_GUARDED_NEW(ModeObjectType);
+ Node *node = new ModeObjectType();
/* Populate base node settings. */
node->type = type();
/* Set name if provided, or use default type name. */
@@ -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..8d19949adc8 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)
{
@@ -66,7 +67,7 @@ bool IDNode::ComponentIDKey::operator==(const ComponentIDKey &other) const
return type == other.type && STREQ(name, other.name);
}
-uint32_t IDNode::ComponentIDKey::hash() const
+uint64_t IDNode::ComponentIDKey::hash() const
{
const int type_as_int = static_cast<int>(type);
return BLI_ghashutil_combine_hash(BLI_ghashutil_uinthash(type_as_int),
@@ -132,7 +133,7 @@ void IDNode::destroy()
}
for (ComponentNode *comp_node : components.values()) {
- OBJECT_GUARDED_DELETE(comp_node, ComponentNode);
+ delete comp_node;
}
/* Free memory used by this CoW ID. */
@@ -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..04a9006ac10 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;
@@ -50,7 +51,7 @@ const char *linkedStateAsString(eDepsNode_LinkedState_Type linked_state);
struct IDNode : public Node {
struct ComponentIDKey {
ComponentIDKey(NodeType type, const char *name = "");
- uint32_t hash() const;
+ uint64_t hash() const;
bool operator==(const ComponentIDKey &other) const;
NodeType type;
@@ -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..a32a43e2905 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)
{
@@ -60,11 +61,17 @@ const char *operationCodeAsString(OperationCode opcode)
/* Scene related. */
case OperationCode::SCENE_EVAL:
return "SCENE_EVAL";
+ case OperationCode::AUDIO_ENTRY:
+ return "AUDIO_ENTRY";
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 +273,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..52b6c9a753b 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;
@@ -60,10 +61,13 @@ enum class OperationCode {
/* Scene related. ------------------------------------------------------- */
SCENE_EVAL,
+ AUDIO_ENTRY,
AUDIO_VOLUME,
/* Object related. ------------------------------------------------------ */
+ OBJECT_FROM_LAYER_ENTRY,
OBJECT_BASE_FLAGS,
+ OBJECT_FROM_LAYER_EXIT,
DIMENSIONS,
/* Transform. ----------------------------------------------------------- */
@@ -274,4 +278,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..8bd4832e446 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -64,6 +64,7 @@ set(SRC
intern/draw_color_management.c
intern/draw_common.c
intern/draw_debug.c
+ intern/draw_fluid.c
intern/draw_hair.c
intern/draw_instance_data.c
intern/draw_manager.c
@@ -143,12 +144,12 @@ set(SRC
engines/overlay/overlay_outline.c
engines/overlay/overlay_paint.c
engines/overlay/overlay_particle.c
- engines/overlay/overlay_pointcloud.c
engines/overlay/overlay_sculpt.c
engines/overlay/overlay_shader.c
engines/overlay/overlay_wireframe.c
DRW_engine.h
+ DRW_engine_types.h
DRW_select_buffer.h
intern/DRW_render.h
intern/draw_cache.h
@@ -186,11 +187,10 @@ set(LIB
)
data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC)
-data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC)
-data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC)
-data_to_c_simple(engines/eevee/shaders/concentric_samples_lib.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/closure_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/common_uniforms_lib.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/common_utiltex_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lights_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC)
@@ -205,8 +205,8 @@ data_to_c_simple(engines/eevee/shaders/lightprobe_grid_display_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_grid_fill_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_vert.glsl SRC)
-data_to_c_simple(engines/eevee/shaders/lit_surface_frag.glsl SRC)
-data_to_c_simple(engines/eevee/shaders/lit_surface_vert.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/lookdev_world_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/closure_lit_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_bloom_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_frag.glsl SRC)
@@ -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,10 +225,11 @@ 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)
-
data_to_c_simple(engines/eevee/shaders/shadow_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_lut_frag.glsl SRC)
@@ -238,9 +240,14 @@ data_to_c_simple(engines/eevee/shaders/octahedron_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/cubemap_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/renderpass_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/renderpass_postprocess_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/surface_frag.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/surface_geom.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/surface_lib.glsl SRC)
+data_to_c_simple(engines/eevee/shaders/surface_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/update_noise_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_accum_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_lib.glsl SRC)
@@ -268,6 +275,7 @@ data_to_c_simple(engines/workbench/shaders/workbench_material_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_merge_infront_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_hair_vert.glsl SRC)
+data_to_c_simple(engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_prepass_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shader_interface_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_shadow_caps_geom.glsl SRC)
@@ -282,8 +290,11 @@ data_to_c_simple(engines/workbench/shaders/workbench_world_light_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_colormanagement_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC)
+data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC)
+data_to_c_simple(intern/shaders/common_math_lib.glsl SRC)
+data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_view_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_fxaa_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_smaa_lib.glsl SRC)
@@ -310,6 +321,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)
@@ -382,8 +394,6 @@ data_to_c_simple(engines/overlay/shaders/paint_weight_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/paint_wire_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/particle_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/particle_frag.glsl SRC)
-data_to_c_simple(engines/overlay/shaders/pointcloud_vert.glsl SRC)
-data_to_c_simple(engines/overlay/shaders/pointcloud_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/sculpt_mask_vert.glsl SRC)
data_to_c_simple(engines/overlay/shaders/sculpt_mask_frag.glsl SRC)
data_to_c_simple(engines/overlay/shaders/volume_velocity_vert.glsl SRC)
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index 6c835c6d7ae..bc4efd82a03 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -43,6 +43,7 @@ struct GPUViewport;
struct ID;
struct Main;
struct Object;
+struct Render;
struct RenderEngine;
struct RenderEngineType;
struct Scene;
@@ -137,6 +138,9 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
struct DRWInstanceDataList *DRW_instance_data_list_create(void);
void DRW_instance_data_list_free(struct DRWInstanceDataList *idatalist);
+void DRW_render_context_enable(struct Render *render);
+void DRW_render_context_disable(struct Render *render);
+
void DRW_opengl_context_create(void);
void DRW_opengl_context_destroy(void);
void DRW_opengl_context_enable(void);
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..94c380b3b50 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;
@@ -165,7 +165,7 @@ static void basic_cache_populate(void *vedata, Object *ob)
return;
}
- bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0;
+ bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const DRWContextState *draw_ctx = DRW_context_state_get();
if (ob != draw_ctx->object_edit) {
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index a19af77124f..87948f403a0 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;
@@ -63,6 +205,7 @@ void EEVEE_view_layer_data_free(void *storage)
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_color);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit);
+ DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment);
if (sldata->material_cache) {
BLI_memblock_destroy(sldata->material_cache, NULL);
@@ -105,6 +248,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_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
index 4a3cc36ddef..05cd6426911 100644
--- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c
+++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
@@ -54,24 +54,30 @@ extern char datatoc_common_view_lib_glsl[];
static void eevee_create_shader_depth_of_field(const bool use_alpha)
{
- char *frag = BLI_string_joinN(datatoc_common_view_lib_glsl, datatoc_effect_dof_frag_glsl);
- e_data.dof_downsample_sh[use_alpha] = DRW_shader_create_fullscreen(
- frag,
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
+
+ e_data.dof_downsample_sh[use_alpha] = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_dof_frag_glsl,
+ lib,
use_alpha ? "#define USE_ALPHA_DOF\n"
"#define STEP_DOWNSAMPLE\n" :
"#define STEP_DOWNSAMPLE\n");
- e_data.dof_scatter_sh[use_alpha] = DRW_shader_create(datatoc_effect_dof_vert_glsl,
- NULL,
- frag,
- use_alpha ? "#define USE_ALPHA_DOF\n"
- "#define STEP_SCATTER\n" :
- "#define STEP_SCATTER\n");
- e_data.dof_resolve_sh[use_alpha] = DRW_shader_create_fullscreen(frag,
- use_alpha ?
- "#define USE_ALPHA_DOF\n"
- "#define STEP_RESOLVE\n" :
- "#define STEP_RESOLVE\n");
- MEM_freeN(frag);
+
+ e_data.dof_scatter_sh[use_alpha] = DRW_shader_create_with_shaderlib(
+ datatoc_effect_dof_vert_glsl,
+ NULL,
+ datatoc_effect_dof_frag_glsl,
+ lib,
+ use_alpha ? "#define USE_ALPHA_DOF\n"
+ "#define STEP_SCATTER\n" :
+ "#define STEP_SCATTER\n");
+
+ e_data.dof_resolve_sh[use_alpha] = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_dof_frag_glsl,
+ lib,
+ use_alpha ? "#define USE_ALPHA_DOF\n"
+ "#define STEP_RESOLVE\n" :
+ "#define STEP_RESOLVE\n");
}
int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata),
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index ab846fe0f11..f6e74c6822c 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);
}
}
@@ -479,7 +486,7 @@ void EEVEE_downsample_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int le
}
/**
- * Simple down-sampling algorithm for cubemap. Reconstruct mip chain up to mip level.
+ * Simple down-sampling algorithm for cube-map. Reconstruct mip chain up to mip level.
*/
void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
{
@@ -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..7bf4c78d870 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -32,6 +32,8 @@
#include "DNA_world_types.h"
+#include "IMB_imbuf.h"
+
#include "eevee_private.h"
#include "eevee_engine.h" /* own include */
@@ -166,7 +168,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 +217,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 +359,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 +385,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 +396,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 +406,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 +421,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 +437,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)
@@ -476,7 +603,7 @@ RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
EEVEE_ENGINE,
N_("Eevee"),
- RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT,
+ RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
NULL,
&DRW_render_to_image,
NULL,
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index 4cdd166f09c..78fcd28eb5d 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -53,9 +53,6 @@
#if defined(IRRADIANCE_SH_L2)
# define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */
# define IRRADIANCE_SAMPLE_SIZE_Y 4 /* 3 in reality */
-#elif defined(IRRADIANCE_CUBEMAP)
-# define IRRADIANCE_SAMPLE_SIZE_X 8
-# define IRRADIANCE_SAMPLE_SIZE_Y 8
#elif defined(IRRADIANCE_HL2)
# define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */
# define IRRADIANCE_SAMPLE_SIZE_Y 2
@@ -113,7 +110,7 @@ typedef struct EEVEE_LightBake {
float samples_ct, invsamples_ct;
/** Sampling bias during convolution step. */
float lod_factor;
- /** Max cubemap LOD to sample when convolving. */
+ /** Max cube-map LOD to sample when convolving. */
float lod_max;
/** Number of probes to render + world probe. */
int cube_len, grid_len;
@@ -121,7 +118,7 @@ typedef struct EEVEE_LightBake {
/* Irradiance grid */
/** Current probe being rendered (UBO data). */
EEVEE_LightGrid *grid;
- /** Target cubemap at MIP 0. */
+ /** Target cube-map at MIP 0. */
int irr_cube_res;
/** Size of the irradiance texture. */
int irr_size[3];
@@ -145,7 +142,7 @@ typedef struct EEVEE_LightBake {
/* Reflection probe */
/** Current probe being rendered (UBO data). */
EEVEE_LightProbe *cube;
- /** Target cubemap at MIP 0. */
+ /** Target cube-map at MIP 0. */
int ref_cube_res;
/** Index of the current cube. */
int cube_offset;
@@ -849,6 +846,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..a4c07584b86 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -179,8 +179,6 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
if (!sldata->fallback_lightcache) {
#if defined(IRRADIANCE_SH_L2)
int grid_res = 4;
-#elif defined(IRRADIANCE_CUBEMAP)
- int grid_res = 8;
#elif defined(IRRADIANCE_HL2)
int grid_res = 4;
#endif
@@ -328,38 +326,28 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat
{
DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
- struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRWShadingGroup *grp = NULL;
+ EEVEE_lookdev_cache_init(vedata, sldata, psl->probe_background, pinfo, &grp);
- Scene *scene = draw_ctx->scene;
- World *wo = scene->world;
-
- /* LookDev */
- EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->probe_background, wo, pinfo);
+ if (grp == NULL) {
+ Scene *scene = draw_ctx->scene;
+ World *world = (scene->world) ? scene->world : EEVEE_world_default_get();
- if (!grp && wo) {
- struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_PROBE);
+ const int options = VAR_WORLD_BACKGROUND | VAR_WORLD_PROBE;
+ struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options);
grp = DRW_shgroup_material_create(gpumat, psl->probe_background);
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
- /* TODO (fclem): remove those (need to clean the GLSL files). */
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_call(grp, geom, NULL);
}
- /* Fallback if shader fails or if not using nodetree. */
- if (grp == NULL) {
- grp = DRW_shgroup_create(EEVEE_shaders_probe_default_sh_get(), psl->probe_background);
- DRW_shgroup_uniform_vec3(grp, "color", G_draw.block.colorBackground, 1);
- DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
- DRW_shgroup_call(grp, geom, NULL);
- }
+ DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
+ DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
+ DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
+ DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
+ DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
+ DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
+ DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo);
+ DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
if (DRW_state_draw_support()) {
@@ -957,8 +945,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);
@@ -1114,9 +1102,6 @@ void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata,
/* NOTE : Keep in sync with load_irradiance_cell() */
#if defined(IRRADIANCE_SH_L2)
int size[2] = {3, 3};
-#elif defined(IRRADIANCE_CUBEMAP)
- int size[2] = {8, 8};
- pinfo->samples_len = 1024.0f;
#elif defined(IRRADIANCE_HL2)
int size[2] = {3, 2};
pinfo->samples_len = 1024.0f;
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c
index 18365d69514..403a8e2af55 100644
--- a/source/blender/draw/engines/eevee/eevee_lookdev.c
+++ b/source/blender/draw/engines/eevee/eevee_lookdev.c
@@ -97,19 +97,18 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD
void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
- DRWShadingGroup **r_grp,
DRWPass *pass,
- World *UNUSED(world),
- EEVEE_LightProbesInfo *pinfo)
+ EEVEE_LightProbesInfo *pinfo,
+ DRWShadingGroup **r_shgrp)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
- View3D *v3d = draw_ctx->v3d;
- View3DShading *shading = &v3d->shading;
- Scene *scene = draw_ctx->scene;
+ /* The view will be NULL when rendering previews. */
+ const View3D *v3d = draw_ctx->v3d;
+ const Scene *scene = draw_ctx->scene;
const bool probe_render = pinfo != NULL;
@@ -150,93 +149,91 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
}
if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) {
+ const View3DShading *shading = &v3d->shading;
StudioLight *sl = BKE_studiolight_find(shading->lookdev_light,
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
- if (sl && (sl->flag & STUDIOLIGHT_TYPE_WORLD)) {
- GPUShader *shader = probe_render ? EEVEE_shaders_default_studiolight_sh_get() :
- EEVEE_shaders_background_studiolight_sh_get();
+ if (sl == NULL || (sl->flag & STUDIOLIGHT_TYPE_WORLD) == 0) {
+ return;
+ }
+
+ GPUShader *shader = probe_render ? EEVEE_shaders_studiolight_probe_sh_get() :
+ EEVEE_shaders_studiolight_background_sh_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
- int cube_res = scene_eval->eevee.gi_cubemap_resolution;
+ const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
+ int cube_res = scene_eval->eevee.gi_cubemap_resolution;
- /* If one of the component is missing we start from scratch. */
- if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) ||
- (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) ||
- (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) {
- eevee_lookdev_lightcache_delete(vedata);
- }
+ /* If one of the component is missing we start from scratch. */
+ if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) ||
+ (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) ||
+ (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) {
+ eevee_lookdev_lightcache_delete(vedata);
+ }
- if (stl->lookdev_lightcache == NULL) {
+ if (stl->lookdev_lightcache == NULL) {
#if defined(IRRADIANCE_SH_L2)
- int grid_res = 4;
-#elif defined(IRRADIANCE_CUBEMAP)
- int grid_res = 8;
+ int grid_res = 4;
#elif defined(IRRADIANCE_HL2)
- int grid_res = 4;
+ int grid_res = 4;
#endif
- stl->lookdev_lightcache = EEVEE_lightcache_create(
- 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1});
-
- /* XXX: Fix memleak. TODO find out why. */
- MEM_SAFE_FREE(stl->lookdev_cube_mips);
-
- /* We do this to use a special light cache for lookdev.
- * This light-cache needs to be per viewport. But we need to
- * have correct freeing when the viewport is closed. So we
- * need to reference all textures to the txl and the memblocks
- * to the stl. */
- stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data;
- stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data;
- stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips;
- txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex;
- txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex;
- }
-
- g_data->light_cache = stl->lookdev_lightcache;
-
- DRWShadingGroup *grp = *r_grp = DRW_shgroup_create(shader, pass);
- axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z);
- DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix);
-
- if (probe_render) {
- DRW_shgroup_uniform_float_copy(
- grp, "studioLightIntensity", shading->studiolight_intensity);
- BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
- DRW_shgroup_uniform_texture(grp, "image", sl->equirect_radiance_gputexture);
- /* Do not fadeout when doing probe rendering, only when drawing the background */
- DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
- }
- else {
- float background_alpha = g_data->background_alpha * shading->studiolight_background;
- float studiolight_blur = powf(shading->studiolight_blur, 2.5f);
- DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha);
- DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur);
- DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- }
-
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
-
- /* Do we need to recalc the lightprobes? */
- if (g_data->studiolight_index != sl->index ||
- g_data->studiolight_rot_z != shading->studiolight_rot_z ||
- g_data->studiolight_intensity != shading->studiolight_intensity ||
- g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution ||
- g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp ||
- g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) {
- stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD;
- g_data->studiolight_index = sl->index;
- g_data->studiolight_rot_z = shading->studiolight_rot_z;
- g_data->studiolight_intensity = shading->studiolight_intensity;
- g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution;
- g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp;
- g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality;
- }
+ stl->lookdev_lightcache = EEVEE_lightcache_create(
+ 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1});
+
+ /* XXX: Fix memleak. TODO find out why. */
+ MEM_SAFE_FREE(stl->lookdev_cube_mips);
+
+ /* We do this to use a special light cache for lookdev.
+ * This light-cache needs to be per viewport. But we need to
+ * have correct freeing when the viewport is closed. So we
+ * need to reference all textures to the txl and the memblocks
+ * to the stl. */
+ stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data;
+ stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data;
+ stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips;
+ txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex;
+ txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex;
+ }
+
+ g_data->light_cache = stl->lookdev_lightcache;
+
+ DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
+ axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z);
+ DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix);
+
+ if (probe_render) {
+ /* Avoid artifact with equirectangular mapping. */
+ eGPUSamplerState state = (GPU_SAMPLER_FILTER | GPU_SAMPLER_REPEAT_S);
+ DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity);
+ BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
+ DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state);
+ /* Do not fadeout when doing probe rendering, only when drawing the background */
+ DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
+ }
+ else {
+ float background_alpha = g_data->background_alpha * shading->studiolight_background;
+ float studiolight_blur = powf(shading->studiolight_blur, 2.5f);
+ DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha);
+ DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur);
+ DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx);
+ }
+
+ /* Common UBOs are setup latter. */
+ *r_shgrp = grp;
+
+ /* Do we need to recalc the lightprobes? */
+ if (g_data->studiolight_index != sl->index ||
+ g_data->studiolight_rot_z != shading->studiolight_rot_z ||
+ g_data->studiolight_intensity != shading->studiolight_intensity ||
+ g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution ||
+ g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp ||
+ g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) {
+ stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD;
+ g_data->studiolight_index = sl->index;
+ g_data->studiolight_rot_z = shading->studiolight_rot_z;
+ g_data->studiolight_intensity = shading->studiolight_intensity;
+ g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution;
+ g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp;
+ g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality;
}
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_lut_gen.c b/source/blender/draw/engines/eevee/eevee_lut_gen.c
index 5f20d6fbfb8..8c3fb29e941 100644
--- a/source/blender/draw/engines/eevee/eevee_lut_gen.c
+++ b/source/blender/draw/engines/eevee/eevee_lut_gen.c
@@ -31,6 +31,8 @@
#include "BLI_rand.h"
#include "BLI_string_utils.h"
+#include "eevee_private.h"
+
extern char datatoc_bsdf_lut_frag_glsl[];
extern char datatoc_btdf_lut_frag_glsl[];
extern char datatoc_bsdf_common_lib_glsl[];
@@ -45,15 +47,13 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h))
static float samples_len = 8192.0f;
static float inv_samples_len = 1.0f / 8192.0f;
- char *lib_str = BLI_string_joinN(datatoc_bsdf_common_lib_glsl, datatoc_bsdf_sampling_lib_glsl);
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
- struct GPUShader *sh = DRW_shader_create_with_lib(datatoc_lightprobe_vert_glsl,
- datatoc_lightprobe_geom_glsl,
- datatoc_bsdf_lut_frag_glsl,
- lib_str,
- "#define HAMMERSLEY_SIZE 8192\n"
- "#define BRDF_LUT_SIZE 64\n"
- "#define NOISE_SIZE 64\n");
+ struct GPUShader *sh = DRW_shader_create_with_shaderlib(datatoc_lightprobe_vert_glsl,
+ datatoc_lightprobe_geom_glsl,
+ datatoc_bsdf_lut_frag_glsl,
+ lib,
+ "#define HAMMERSLEY_SIZE 8192\n");
DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, pass);
@@ -76,8 +76,7 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h))
DRW_draw_pass(pass);
float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
- glReadBuffer(GL_COLOR_ATTACHMENT0);
- glReadPixels(0, 0, w, h, GL_RGB, GL_FLOAT, data);
+ GPU_framebuffer_read_color(fb, 0, 0, w, h, 3, 0, GPU_DATA_FLOAT, data);
printf("{");
for (int i = 0; i < w * h * 3; i += 3) {
@@ -106,16 +105,10 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h)
static float a2 = 0.0f;
static float inv_samples_len = 1.0f / 8192.0f;
- char *frag_str = BLI_string_joinN(
- datatoc_bsdf_common_lib_glsl, datatoc_bsdf_sampling_lib_glsl, datatoc_btdf_lut_frag_glsl);
-
- struct GPUShader *sh = DRW_shader_create_fullscreen(frag_str,
- "#define HAMMERSLEY_SIZE 8192\n"
- "#define BRDF_LUT_SIZE 64\n"
- "#define NOISE_SIZE 64\n"
- "#define LUT_SIZE 64\n");
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
- MEM_freeN(frag_str);
+ struct GPUShader *sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_btdf_lut_frag_glsl, lib, "#define HAMMERSLEY_SIZE 8192\n");
DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, pass);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index cfc70baaf01..59e8c1407e6 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -56,37 +56,6 @@ static struct {
float noise_offsets[3];
} e_data = {NULL}; /* Engine data */
-extern char datatoc_lights_lib_glsl[];
-extern char datatoc_lightprobe_lib_glsl[];
-extern char datatoc_ambient_occlusion_lib_glsl[];
-extern char datatoc_prepass_frag_glsl[];
-extern char datatoc_prepass_vert_glsl[];
-extern char datatoc_default_frag_glsl[];
-extern char datatoc_default_world_frag_glsl[];
-extern char datatoc_ltc_lib_glsl[];
-extern char datatoc_bsdf_common_lib_glsl[];
-extern char datatoc_bsdf_sampling_lib_glsl[];
-extern char datatoc_common_uniforms_lib_glsl[];
-extern char datatoc_common_hair_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-extern char datatoc_irradiance_lib_glsl[];
-extern char datatoc_octahedron_lib_glsl[];
-extern char datatoc_cubemap_lib_glsl[];
-extern char datatoc_lit_surface_frag_glsl[];
-extern char datatoc_lit_surface_vert_glsl[];
-extern char datatoc_raytrace_lib_glsl[];
-extern char datatoc_ssr_lib_glsl[];
-extern char datatoc_shadow_vert_glsl[];
-extern char datatoc_lightprobe_geom_glsl[];
-extern char datatoc_lightprobe_vert_glsl[];
-extern char datatoc_background_vert_glsl[];
-extern char datatoc_update_noise_frag_glsl[];
-extern char datatoc_volumetric_vert_glsl[];
-extern char datatoc_volumetric_geom_glsl[];
-extern char datatoc_volumetric_frag_glsl[];
-extern char datatoc_volumetric_lib_glsl[];
-extern char datatoc_gpu_shader_uniform_color_frag_glsl[];
-
typedef struct EeveeMaterialCache {
struct DRWShadingGroup *depth_grp;
struct DRWShadingGroup *shading_grp;
@@ -114,8 +83,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
GPUMaterial *gpumat,
EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
- int *ssr_id,
- float *refract_depth,
+ const int *ssr_id,
+ const float *refract_depth,
bool use_ssrefraction,
bool use_alpha_blend)
{
@@ -238,46 +207,6 @@ void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const d
DRW_draw_pass(psl->update_noise_pass);
}
-void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4])
-{
- /* view vectors for the corners of the view frustum.
- * Can be used to recreate the world space position easily */
- float view_vecs[4][4] = {
- {-1.0f, -1.0f, -1.0f, 1.0f},
- {1.0f, -1.0f, -1.0f, 1.0f},
- {-1.0f, 1.0f, -1.0f, 1.0f},
- {-1.0f, -1.0f, 1.0f, 1.0f},
- };
-
- /* convert the view vectors to view space */
- const bool is_persp = (winmat[3][3] == 0.0f);
- for (int i = 0; i < 4; i++) {
- mul_project_m4_v3(invproj, view_vecs[i]);
- /* normalized trick see:
- * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
- if (is_persp) {
- /* Divide XY by Z. */
- mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]);
- }
- }
-
- /**
- * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and
- * view_vecs[1] is the vector going from the near-bottom-left corner to
- * the far-top-right corner.
- * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner
- * when Z = 1, and top-left corner if Z = 1.
- * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed)
- * distance from the near plane to the far clip plane.
- */
- copy_v4_v4(r_viewvecs[0], view_vecs[0]);
-
- /* we need to store the differences */
- r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0];
- r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1];
- r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2];
-}
-
void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
EEVEE_StorageList *stl,
@@ -305,15 +234,6 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
}
{
- /* Update view_vecs */
- float invproj[4][4], winmat[4][4];
- DRW_view_winmat_get(NULL, winmat, false);
- DRW_view_winmat_get(NULL, invproj, true);
-
- EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs);
- }
-
- {
/* Update noise Framebuffer. */
GPU_framebuffer_ensure_config(
&fbl->update_noise_fb,
@@ -325,27 +245,31 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
if (sldata->renderpass_ubo.combined == NULL) {
sldata->renderpass_ubo.combined = DRW_uniformbuffer_create(
sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, true, true, true, true, false});
+ &(const EEVEE_RenderPassData){true, true, true, true, true, false, false});
sldata->renderpass_ubo.diff_color = DRW_uniformbuffer_create(
sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, false, false, false, false, true});
+ &(const EEVEE_RenderPassData){true, false, false, false, false, true, false});
sldata->renderpass_ubo.diff_light = DRW_uniformbuffer_create(
sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){true, true, false, false, false, false});
+ &(const EEVEE_RenderPassData){true, true, false, false, false, false, false});
sldata->renderpass_ubo.spec_color = DRW_uniformbuffer_create(
sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){false, false, true, false, false, false});
+ &(const EEVEE_RenderPassData){false, false, true, false, false, false, false});
sldata->renderpass_ubo.spec_light = DRW_uniformbuffer_create(
sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){false, false, true, true, false, false});
+ &(const EEVEE_RenderPassData){false, false, true, true, false, false, false});
sldata->renderpass_ubo.emit = DRW_uniformbuffer_create(
sizeof(EEVEE_RenderPassData),
- &(const EEVEE_RenderPassData){false, false, false, false, true, false});
+ &(const EEVEE_RenderPassData){false, false, false, false, true, false, false});
+
+ sldata->renderpass_ubo.environment = DRW_uniformbuffer_create(
+ sizeof(EEVEE_RenderPassData),
+ &(const EEVEE_RenderPassData){true, true, true, true, true, false, true});
}
/* Used combined pass by default. */
@@ -387,39 +311,28 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
DRW_PASS_CREATE(psl->background_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
- struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRWShadingGroup *grp = NULL;
+ EEVEE_lookdev_cache_init(vedata, sldata, psl->background_ps, NULL, &grp);
- Scene *scene = draw_ctx->scene;
- World *wo = scene->world;
-
- EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->background_ps, wo, NULL);
+ if (grp == NULL) {
+ Scene *scene = draw_ctx->scene;
+ World *world = (scene->world) ? scene->world : EEVEE_world_default_get();
- if (!grp && wo) {
- struct GPUMaterial *gpumat = EEVEE_material_get(
- vedata, scene, NULL, wo, VAR_WORLD_BACKGROUND);
+ const int options = VAR_WORLD_BACKGROUND;
+ struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options);
grp = DRW_shgroup_material_create(gpumat, psl->background_ps);
DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
- /* TODO (fclem): remove those (need to clean the GLSL files). */
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_call(grp, geom, NULL);
}
- /* Fallback if shader fails or if not using nodetree. */
- if (grp == NULL) {
- GPUShader *sh = EEVEE_shaders_default_background_sh_get();
- grp = DRW_shgroup_create(sh, psl->background_ps);
- DRW_shgroup_uniform_vec3(grp, "color", G_draw.block.colorBackground, 1);
- DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
- DRW_shgroup_call(grp, geom, NULL);
- }
+ DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
+ DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
+ DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
+ DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
+ DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
+ DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
+ DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo);
+ DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
#define EEVEE_PASS_CREATE(pass, state) \
@@ -801,6 +714,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 +766,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 +815,9 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
}
}
}
+
+ /* Motion Blur Vectors. */
+ EEVEE_motion_blur_cache_populate(sldata, vedata, ob);
}
/* Volumetrics */
@@ -946,17 +863,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 +930,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});
@@ -1071,7 +986,7 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRWPass *material_accum_ps = psl->material_accum_ps;
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
material_renderpass_accumulate(
- fbl, psl->background_accum_ps, pd, txl->env_accum, sldata->renderpass_ubo.combined);
+ fbl, psl->background_accum_ps, pd, txl->env_accum, sldata->renderpass_ubo.environment);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
material_renderpass_accumulate(
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
index 7b942784ee9..d2f3a13eb7c 100644
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ b/source/blender/draw/engines/eevee/eevee_mist.c
@@ -56,14 +56,10 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
if (e_data.mist_sh == NULL) {
- char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_effect_mist_frag_glsl);
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
- e_data.mist_sh = DRW_shader_create_fullscreen(frag_str, "#define FIRST_PASS\n");
-
- MEM_freeN(frag_str);
+ e_data.mist_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_mist_frag_glsl, lib, "#define FIRST_PASS\n");
}
/* Create FrameBuffer. */
@@ -75,7 +71,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);
}
@@ -98,11 +94,11 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
}
}
else {
- float near = -sldata->common_data.view_vecs[0][2];
- float range = sldata->common_data.view_vecs[1][2];
+ float near = DRW_view_near_distance_get(NULL);
+ float far = DRW_view_far_distance_get(NULL);
/* Fallback */
g_data->mist_start = near;
- g_data->mist_inv_dist = 1.0f / fabsf(range);
+ g_data->mist_inv_dist = 1.0f / fabsf(far - near);
g_data->mist_falloff = 1.0f;
}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index a6e6b30a6b1..0e8a2493b01 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,513 @@
#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)
+{
+#define TILE_SIZE_STR "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
+ e_data.motion_blur_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_motion_blur_frag_glsl, lib, TILE_SIZE_STR);
+ e_data.motion_blur_object_sh = DRW_shader_create_with_shaderlib(
+ datatoc_object_motion_vert_glsl, NULL, datatoc_object_motion_frag_glsl, lib, NULL);
+
+ e_data.motion_blur_hair_sh = DRW_shader_create_with_shaderlib(datatoc_object_motion_vert_glsl,
+ NULL,
+ datatoc_object_motion_frag_glsl,
+ lib,
+ "#define HAIR\n");
+
+ e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(datatoc_effect_velocity_tile_frag_glsl,
+ "#define TILE_GATHER\n" TILE_SIZE_STR);
+ e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen(
+ datatoc_effect_velocity_tile_frag_glsl, "#define TILE_EXPANSION\n" TILE_SIZE_STR);
+}
- /* 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 +564,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 +597,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..1929bbb9b98 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -53,17 +53,14 @@ extern char datatoc_effect_gtao_frag_glsl[];
static void eevee_create_shader_occlusion(void)
{
- char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_ambient_occlusion_lib_glsl,
- datatoc_effect_gtao_frag_glsl);
-
- e_data.gtao_sh = DRW_shader_create_fullscreen(frag_str, NULL);
- e_data.gtao_layer_sh = DRW_shader_create_fullscreen(frag_str, "#define LAYERED_DEPTH\n");
- e_data.gtao_debug_sh = DRW_shader_create_fullscreen(frag_str, "#define DEBUG_AO\n");
-
- MEM_freeN(frag_str);
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
+
+ e_data.gtao_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_gtao_frag_glsl, lib, NULL);
+ e_data.gtao_layer_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_gtao_frag_glsl, lib, "#define LAYERED_DEPTH\n");
+ e_data.gtao_debug_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_gtao_frag_glsl, lib, "#define DEBUG_AO\n");
}
int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
@@ -155,7 +152,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..c202bbbb754 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;
@@ -51,14 +53,11 @@ extern struct DrawEngineType draw_engine_eevee_type;
/* Only define one of these. */
// #define IRRADIANCE_SH_L2
-// #define IRRADIANCE_CUBEMAP
#define IRRADIANCE_HL2
#define HAMMERSLEY_SIZE 1024
#if defined(IRRADIANCE_SH_L2)
# define SHADER_IRRADIANCE "#define IRRADIANCE_SH_L2\n"
-#elif defined(IRRADIANCE_CUBEMAP)
-# define SHADER_IRRADIANCE "#define IRRADIANCE_CUBEMAP\n"
#elif defined(IRRADIANCE_HL2)
# define SHADER_IRRADIANCE "#define IRRADIANCE_HL2\n"
#endif
@@ -164,7 +163,7 @@ enum {
VAR_MAT_MESH = (1 << 0),
VAR_MAT_VOLUME = (1 << 1),
VAR_MAT_HAIR = (1 << 2),
- VAR_MAT_PROBE = (1 << 3),
+ /* VAR_MAT_PROBE = (1 << 3), UNUSED */
VAR_MAT_BLEND = (1 << 4),
VAR_MAT_LOOKDEV = (1 << 5),
VAR_MAT_HOLDOUT = (1 << 6),
@@ -256,7 +255,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 +331,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;
@@ -408,7 +414,8 @@ typedef struct EEVEE_RenderPassData {
int renderPassGlossyLight;
int renderPassEmit;
int renderPassSSSColor;
- int _pad[2];
+ int renderPassEnvironment;
+ int _pad[1];
} EEVEE_RenderPassData;
/* ************ LIGHT UBO ************* */
@@ -556,6 +563,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[8]; /* 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 +666,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 +677,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];
@@ -678,7 +747,6 @@ typedef struct EEVEE_EffectsInfo {
* - sizeof(bool) == sizeof(int) in GLSL so use int in C */
typedef struct EEVEE_CommonUniformBuffer {
float prev_persmat[4][4]; /* mat4 */
- float view_vecs[2][4]; /* vec4[2] */
float mip_ratio[10][4]; /* vec2[10] */
/* Ambient Occlusion */
/* -- 16 byte aligned -- */
@@ -764,6 +832,7 @@ typedef struct EEVEE_ViewLayerData {
/* Material Render passes */
struct {
struct GPUUniformBuffer *combined;
+ struct GPUUniformBuffer *environment;
struct GPUUniformBuffer *diff_color;
struct GPUUniformBuffer *diff_light;
struct GPUUniformBuffer *spec_color;
@@ -805,6 +874,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 +950,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 +976,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,
@@ -918,7 +1000,6 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_materials_free(void);
void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]);
-void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4]);
void EEVEE_material_renderpasses_init(EEVEE_Data *vedata);
void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
@@ -926,8 +1007,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
struct GPUMaterial *gpumat,
EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
- int *ssr_id,
- float *refract_depth,
+ const int *ssr_id,
+ const float *refract_depth,
bool use_ssrefraction,
bool use_alpha_blend);
/* eevee_lights.c */
@@ -974,15 +1055,14 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]);
/* eevee_shaders.c */
void EEVEE_shaders_lightprobe_shaders_init(void);
void EEVEE_shaders_material_shaders_init(void);
+struct DRWShaderLibrary *EEVEE_shader_lib_get(void);
struct GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_default_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void);
-struct GPUShader *EEVEE_shaders_default_studiolight_sh_get(void);
-struct GPUShader *EEVEE_shaders_default_background_sh_get(void);
-struct GPUShader *EEVEE_shaders_background_studiolight_sh_get(void);
+struct GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void);
+struct GPUShader *EEVEE_shaders_studiolight_background_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void);
struct GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void);
@@ -994,6 +1074,7 @@ struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo);
Material *EEVEE_material_default_diffuse_get(void);
Material *EEVEE_material_default_glossy_get(void);
Material *EEVEE_material_default_error_get(void);
+World *EEVEE_world_default_get(void);
struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options);
struct GPUMaterial *EEVEE_material_get(
EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options);
@@ -1112,8 +1193,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 +1217,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 +1231,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 +1275,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 +1287,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);
@@ -1198,10 +1298,9 @@ void EEVEE_render_update_passes(struct RenderEngine *engine,
/** eevee_lookdev.c */
void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
- DRWShadingGroup **grp,
DRWPass *pass,
- struct World *world,
- EEVEE_LightProbesInfo *pinfo);
+ EEVEE_LightProbesInfo *pinfo,
+ DRWShadingGroup **r_shgrp);
void EEVEE_lookdev_draw(EEVEE_Data *vedata);
/** eevee_engine.c */
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 89a5ad2198a..787fc16a7da 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);
@@ -250,6 +267,7 @@ static void eevee_render_color_result(RenderLayer *rl,
BLI_rcti_size_y(rect),
num_channels,
0,
+ GPU_DATA_FLOAT,
rp->rect);
}
@@ -478,27 +496,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 +511,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 +617,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 +640,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..089d8b7a287 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,49 @@ 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) {
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
+
+ e_data.postprocess_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_renderpass_postprocess_frag_glsl, lib, NULL);
+ }
+
+ 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..a1755e60c06 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -48,30 +48,12 @@ static struct {
struct GPUTexture *depth_src;
} e_data = {{NULL}}; /* Engine data */
-extern char datatoc_ambient_occlusion_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-extern char datatoc_common_uniforms_lib_glsl[];
-extern char datatoc_bsdf_common_lib_glsl[];
-extern char datatoc_bsdf_sampling_lib_glsl[];
-extern char datatoc_octahedron_lib_glsl[];
-extern char datatoc_cubemap_lib_glsl[];
extern char datatoc_effect_ssr_frag_glsl[];
-extern char datatoc_lightprobe_lib_glsl[];
-extern char datatoc_raytrace_lib_glsl[];
static struct GPUShader *eevee_effects_screen_raytrace_shader_get(int options)
{
if (e_data.ssr_sh[options] == NULL) {
- char *ssr_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_bsdf_sampling_lib_glsl,
- datatoc_ambient_occlusion_lib_glsl,
- datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_lightprobe_lib_glsl,
- datatoc_raytrace_lib_glsl,
- datatoc_effect_ssr_frag_glsl);
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
DynStr *ds_defines = BLI_dynstr_new();
BLI_dynstr_append(ds_defines, SHADER_DEFINES);
@@ -91,9 +73,9 @@ static struct GPUShader *eevee_effects_screen_raytrace_shader_get(int options)
char *ssr_define_str = BLI_dynstr_get_cstring(ds_defines);
BLI_dynstr_free(ds_defines);
- e_data.ssr_sh[options] = DRW_shader_create_fullscreen(ssr_shader_str, ssr_define_str);
+ e_data.ssr_sh[options] = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_ssr_frag_glsl, lib, ssr_define_str);
- MEM_freeN(ssr_shader_str);
MEM_freeN(ssr_define_str);
}
@@ -358,7 +340,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_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index 09e74c84948..5f125d395d3 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -28,6 +28,8 @@
#include "BLI_dynstr.h"
#include "BLI_string_utils.h"
+#include "DNA_world_types.h"
+
#include "MEM_guardedalloc.h"
#include "GPU_material.h"
@@ -40,19 +42,17 @@
static const char *filter_defines = "#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n"
#if defined(IRRADIANCE_SH_L2)
- "#define IRRADIANCE_SH_L2\n"
-#elif defined(IRRADIANCE_CUBEMAP)
- "#define IRRADIANCE_CUBEMAP\n"
+ "#define IRRADIANCE_SH_L2\n";
#elif defined(IRRADIANCE_HL2)
- "#define IRRADIANCE_HL2\n"
+ "#define IRRADIANCE_HL2\n";
#endif
- "#define NOISE_SIZE 64\n";
static struct {
+ /* Lookdev */
+ struct GPUShader *studiolight_probe_sh;
+ struct GPUShader *studiolight_background_sh;
+
/* Probes */
- struct GPUShader *probe_default_sh;
- struct GPUShader *probe_default_studiolight_sh;
- struct GPUShader *probe_background_studiolight_sh;
struct GPUShader *probe_grid_display_sh;
struct GPUShader *probe_cube_display_sh;
struct GPUShader *probe_planar_display_sh;
@@ -70,17 +70,16 @@ static struct {
struct GPUShader *taa_resolve_reproject_sh;
/* General purpose Shaders. */
- struct GPUShader *default_background;
+ struct GPUShader *lookdev_background;
struct GPUShader *update_noise_sh;
/* Shader strings */
- char *frag_shader_lib;
- char *vert_shader_str;
- char *vert_shadow_shader_str;
- char *vert_background_shader_str;
- char *vert_volume_shader_str;
- char *geom_volume_shader_str;
- char *volume_shader_lib;
+ char *closure_lit_lib;
+ char *surface_lit_frag;
+ char *surface_prepass_frag;
+ char *surface_geom_barycentric;
+
+ DRWShaderLibrary *lib;
/* LookDev Materials */
Material *glossy_mat;
@@ -88,6 +87,8 @@ static struct {
Material *error_mat;
+ World *default_world;
+
/* Default Material */
struct {
bNodeTree *ntree;
@@ -103,16 +104,39 @@ static struct {
} world;
} e_data = {NULL}; /* Engine data */
-extern char datatoc_bsdf_common_lib_glsl[];
-extern char datatoc_bsdf_sampling_lib_glsl[];
-extern char datatoc_common_uniforms_lib_glsl[];
+extern char datatoc_common_hair_lib_glsl[];
+extern char datatoc_common_math_lib_glsl[];
+extern char datatoc_common_math_geom_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
+extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
extern char datatoc_ambient_occlusion_lib_glsl[];
extern char datatoc_background_vert_glsl[];
-extern char datatoc_common_hair_lib_glsl[];
+extern char datatoc_bsdf_common_lib_glsl[];
+extern char datatoc_bsdf_lut_frag_glsl[];
+extern char datatoc_bsdf_sampling_lib_glsl[];
+extern char datatoc_btdf_lut_frag_glsl[];
+extern char datatoc_closure_lib_glsl[];
+extern char datatoc_common_uniforms_lib_glsl[];
+extern char datatoc_common_utiltex_lib_glsl[];
extern char datatoc_cubemap_lib_glsl[];
-extern char datatoc_default_world_frag_glsl[];
+extern char datatoc_default_frag_glsl[];
+extern char datatoc_lookdev_world_frag_glsl[];
+extern char datatoc_effect_bloom_frag_glsl[];
+extern char datatoc_effect_dof_frag_glsl[];
+extern char datatoc_effect_dof_vert_glsl[];
+extern char datatoc_effect_downsample_cube_frag_glsl[];
+extern char datatoc_effect_downsample_frag_glsl[];
+extern char datatoc_effect_gtao_frag_glsl[];
+extern char datatoc_effect_minmaxz_frag_glsl[];
+extern char datatoc_effect_mist_frag_glsl[];
+extern char datatoc_effect_motion_blur_frag_glsl[];
+extern char datatoc_effect_ssr_frag_glsl[];
+extern char datatoc_effect_subsurface_frag_glsl[];
+extern char datatoc_effect_temporal_aa_glsl[];
+extern char datatoc_effect_translucency_frag_glsl[];
+extern char datatoc_effect_velocity_resolve_frag_glsl[];
+extern char datatoc_effect_velocity_tile_frag_glsl[];
extern char datatoc_irradiance_lib_glsl[];
extern char datatoc_lightprobe_cube_display_frag_glsl[];
extern char datatoc_lightprobe_cube_display_vert_glsl[];
@@ -131,72 +155,111 @@ extern char datatoc_lightprobe_planar_downsample_geom_glsl[];
extern char datatoc_lightprobe_planar_downsample_vert_glsl[];
extern char datatoc_lightprobe_vert_glsl[];
extern char datatoc_lights_lib_glsl[];
-extern char datatoc_lit_surface_frag_glsl[];
-extern char datatoc_lit_surface_vert_glsl[];
+extern char datatoc_closure_lit_lib_glsl[];
extern char datatoc_ltc_lib_glsl[];
+extern char datatoc_object_motion_frag_glsl[];
+extern char datatoc_object_motion_vert_glsl[];
extern char datatoc_octahedron_lib_glsl[];
extern char datatoc_prepass_frag_glsl[];
+extern char datatoc_prepass_vert_glsl[];
extern char datatoc_raytrace_lib_glsl[];
+extern char datatoc_renderpass_lib_glsl[];
+extern char datatoc_renderpass_postprocess_frag_glsl[];
+extern char datatoc_shadow_accum_frag_glsl[];
+extern char datatoc_shadow_frag_glsl[];
extern char datatoc_shadow_vert_glsl[];
extern char datatoc_ssr_lib_glsl[];
+extern char datatoc_surface_frag_glsl[];
+extern char datatoc_surface_geom_glsl[];
+extern char datatoc_surface_lib_glsl[];
+extern char datatoc_surface_vert_glsl[];
extern char datatoc_update_noise_frag_glsl[];
+extern char datatoc_volumetric_accum_frag_glsl[];
extern char datatoc_volumetric_frag_glsl[];
extern char datatoc_volumetric_geom_glsl[];
+extern char datatoc_volumetric_integration_frag_glsl[];
extern char datatoc_volumetric_lib_glsl[];
+extern char datatoc_volumetric_resolve_frag_glsl[];
+extern char datatoc_volumetric_scatter_frag_glsl[];
extern char datatoc_volumetric_vert_glsl[];
-/* Velocity Resolve */
-extern char datatoc_effect_velocity_resolve_frag_glsl[];
-
-/* Temporal Sampling */
-extern char datatoc_effect_temporal_aa_glsl[];
-
/* *********** FUNCTIONS *********** */
+static void eevee_shader_library_ensure(void)
+{
+ if (e_data.lib == NULL) {
+ e_data.lib = DRW_shader_library_create();
+ /* NOTE: Theses needs to be ordered by dependencies. */
+ DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_utiltex_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, bsdf_sampling_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, cubemap_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, raytrace_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, ambient_occlusion_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, octahedron_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, irradiance_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, lightprobe_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, ltc_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, lights_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, surface_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, closure_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib);
+
+ /* Add one for each Closure */
+ e_data.closure_lit_lib = BLI_string_joinN(datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl,
+ datatoc_closure_lit_lib_glsl);
+
+ DRW_shader_library_add_file(e_data.lib, e_data.closure_lit_lib, "closure_lit_lib.glsl");
+
+ e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
+ datatoc_surface_frag_glsl);
+
+ e_data.surface_prepass_frag = DRW_shader_library_create_shader_string(
+ e_data.lib, datatoc_prepass_frag_glsl);
+
+ e_data.surface_geom_barycentric = DRW_shader_library_create_shader_string(
+ e_data.lib, datatoc_surface_geom_glsl);
+ }
+}
+
void EEVEE_shaders_lightprobe_shaders_init(void)
{
BLI_assert(e_data.probe_filter_glossy_sh == NULL);
- char *shader_str = NULL;
-
- shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_bsdf_sampling_lib_glsl,
- datatoc_lightprobe_filter_glossy_frag_glsl);
-
- e_data.probe_filter_glossy_sh = DRW_shader_create(
- datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str, filter_defines);
-
- e_data.probe_default_sh = DRW_shader_create_with_lib(datatoc_background_vert_glsl,
- NULL,
- datatoc_default_world_frag_glsl,
- datatoc_common_view_lib_glsl,
- NULL);
- MEM_freeN(shader_str);
+ eevee_shader_library_ensure();
- shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_bsdf_sampling_lib_glsl,
- datatoc_lightprobe_filter_diffuse_frag_glsl);
+ e_data.probe_filter_glossy_sh = DRW_shader_create_with_shaderlib(
+ datatoc_lightprobe_vert_glsl,
+ datatoc_lightprobe_geom_glsl,
+ datatoc_lightprobe_filter_glossy_frag_glsl,
+ e_data.lib,
+ filter_defines);
- e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen(shader_str, filter_defines);
+ e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_lightprobe_filter_diffuse_frag_glsl, e_data.lib, filter_defines);
- MEM_freeN(shader_str);
+ e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_lightprobe_filter_visibility_frag_glsl, e_data.lib, filter_defines);
- shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_bsdf_sampling_lib_glsl,
- datatoc_lightprobe_filter_visibility_frag_glsl);
-
- e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen(shader_str, filter_defines);
-
- MEM_freeN(shader_str);
-
- e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen(datatoc_lightprobe_grid_fill_frag_glsl,
- filter_defines);
+ e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_lightprobe_grid_fill_frag_glsl, e_data.lib, filter_defines);
e_data.probe_planar_downsample_sh = DRW_shader_create(
datatoc_lightprobe_planar_downsample_vert_glsl,
@@ -207,70 +270,18 @@ void EEVEE_shaders_lightprobe_shaders_init(void)
void EEVEE_shaders_material_shaders_init(void)
{
- e_data.frag_shader_lib = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_bsdf_sampling_lib_glsl,
- datatoc_ambient_occlusion_lib_glsl,
- datatoc_raytrace_lib_glsl,
- datatoc_ssr_lib_glsl,
- datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_irradiance_lib_glsl,
- datatoc_lightprobe_lib_glsl,
- datatoc_ltc_lib_glsl,
- datatoc_lights_lib_glsl,
- /* Add one for each Closure */
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_lit_surface_frag_glsl,
- datatoc_volumetric_lib_glsl);
-
- e_data.volume_shader_lib = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_ambient_occlusion_lib_glsl,
- datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_irradiance_lib_glsl,
- datatoc_lightprobe_lib_glsl,
- datatoc_ltc_lib_glsl,
- datatoc_lights_lib_glsl,
- datatoc_volumetric_lib_glsl,
- datatoc_volumetric_frag_glsl);
-
- e_data.vert_shader_str = BLI_string_joinN(
- datatoc_common_view_lib_glsl, datatoc_common_hair_lib_glsl, datatoc_lit_surface_vert_glsl);
-
- e_data.vert_shadow_shader_str = BLI_string_joinN(
- datatoc_common_view_lib_glsl, datatoc_common_hair_lib_glsl, datatoc_shadow_vert_glsl);
-
- e_data.vert_background_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_background_vert_glsl);
-
- e_data.vert_volume_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_volumetric_vert_glsl);
-
- e_data.geom_volume_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_volumetric_geom_glsl);
+ eevee_shader_library_ensure();
}
-GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void)
+DRWShaderLibrary *EEVEE_shader_lib_get(void)
{
- return e_data.probe_filter_glossy_sh;
+ eevee_shader_library_ensure();
+ return e_data.lib;
}
-GPUShader *EEVEE_shaders_probe_default_sh_get(void)
+GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void)
{
- return e_data.probe_default_sh;
+ return e_data.probe_filter_glossy_sh;
}
GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void)
@@ -293,59 +304,40 @@ GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void)
return e_data.probe_planar_downsample_sh;
}
-GPUShader *EEVEE_shaders_default_studiolight_sh_get(void)
+GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void)
{
- if (e_data.probe_default_studiolight_sh == NULL) {
- e_data.probe_default_studiolight_sh = DRW_shader_create_with_lib(
- datatoc_background_vert_glsl,
- NULL,
- datatoc_default_world_frag_glsl,
- datatoc_common_view_lib_glsl,
- "#define LOOKDEV\n");
- }
- return e_data.probe_default_studiolight_sh;
+ if (e_data.studiolight_probe_sh == NULL) {
+ e_data.studiolight_probe_sh = DRW_shader_create_with_shaderlib(datatoc_background_vert_glsl,
+ NULL,
+ datatoc_lookdev_world_frag_glsl,
+ e_data.lib,
+ SHADER_DEFINES);
+ }
+ return e_data.studiolight_probe_sh;
}
-GPUShader *EEVEE_shaders_background_studiolight_sh_get(void)
+GPUShader *EEVEE_shaders_studiolight_background_sh_get(void)
{
- if (e_data.probe_background_studiolight_sh == NULL) {
- char *frag_str = BLI_string_joinN(datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_lightprobe_lib_glsl,
- datatoc_default_world_frag_glsl);
-
- e_data.probe_background_studiolight_sh = DRW_shader_create_with_lib(
+ if (e_data.studiolight_background_sh == NULL) {
+ e_data.studiolight_background_sh = DRW_shader_create_with_shaderlib(
datatoc_background_vert_glsl,
NULL,
- frag_str,
- datatoc_common_view_lib_glsl,
+ datatoc_lookdev_world_frag_glsl,
+ e_data.lib,
"#define LOOKDEV_BG\n" SHADER_DEFINES);
-
- MEM_freeN(frag_str);
}
- return e_data.probe_background_studiolight_sh;
+ return e_data.studiolight_background_sh;
}
GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void)
{
if (e_data.probe_cube_display_sh == NULL) {
- char *shader_str = BLI_string_joinN(datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_lightprobe_lib_glsl,
- datatoc_lightprobe_cube_display_frag_glsl);
-
- char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_lightprobe_cube_display_vert_glsl);
-
- e_data.probe_cube_display_sh = DRW_shader_create(vert_str, NULL, shader_str, SHADER_DEFINES);
-
- MEM_freeN(vert_str);
- MEM_freeN(shader_str);
+ e_data.probe_cube_display_sh = DRW_shader_create_with_shaderlib(
+ datatoc_lightprobe_cube_display_vert_glsl,
+ NULL,
+ datatoc_lightprobe_cube_display_frag_glsl,
+ e_data.lib,
+ SHADER_DEFINES);
}
return e_data.probe_cube_display_sh;
}
@@ -353,22 +345,12 @@ GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void)
GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void)
{
if (e_data.probe_grid_display_sh == NULL) {
- char *shader_str = BLI_string_joinN(datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_irradiance_lib_glsl,
- datatoc_lightprobe_lib_glsl,
- datatoc_lightprobe_grid_display_frag_glsl);
-
- char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_lightprobe_grid_display_vert_glsl);
-
- e_data.probe_grid_display_sh = DRW_shader_create(vert_str, NULL, shader_str, filter_defines);
-
- MEM_freeN(vert_str);
- MEM_freeN(shader_str);
+ e_data.probe_grid_display_sh = DRW_shader_create_with_shaderlib(
+ datatoc_lightprobe_grid_display_vert_glsl,
+ NULL,
+ datatoc_lightprobe_grid_display_frag_glsl,
+ e_data.lib,
+ filter_defines);
}
return e_data.probe_grid_display_sh;
}
@@ -376,16 +358,12 @@ GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void)
GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void)
{
if (e_data.probe_planar_display_sh == NULL) {
- char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_lightprobe_planar_display_vert_glsl);
-
- char *shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_lightprobe_planar_display_frag_glsl);
-
- e_data.probe_planar_display_sh = DRW_shader_create(vert_str, NULL, shader_str, NULL);
-
- MEM_freeN(vert_str);
- MEM_freeN(shader_str);
+ e_data.probe_planar_display_sh = DRW_shader_create_with_shaderlib(
+ datatoc_lightprobe_planar_display_vert_glsl,
+ NULL,
+ datatoc_lightprobe_planar_display_frag_glsl,
+ e_data.lib,
+ NULL);
}
return e_data.probe_planar_display_sh;
}
@@ -393,34 +371,17 @@ GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void)
GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void)
{
if (e_data.velocity_resolve_sh == NULL) {
- char *frag_str = BLI_string_joinN(datatoc_common_uniforms_lib_glsl,
- datatoc_common_view_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_effect_velocity_resolve_frag_glsl);
-
- e_data.velocity_resolve_sh = DRW_shader_create_fullscreen(frag_str, NULL);
-
- MEM_freeN(frag_str);
+ e_data.velocity_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_velocity_resolve_frag_glsl, e_data.lib, NULL);
}
return e_data.velocity_resolve_sh;
}
-GPUShader *EEVEE_shaders_default_background_sh_get(void)
-{
- if (e_data.default_background == NULL) {
- e_data.default_background = DRW_shader_create_with_lib(datatoc_background_vert_glsl,
- NULL,
- datatoc_default_world_frag_glsl,
- datatoc_common_view_lib_glsl,
- NULL);
- }
- return e_data.default_background;
-}
-
GPUShader *EEVEE_shaders_update_noise_sh_get(void)
{
if (e_data.update_noise_sh == NULL) {
- e_data.update_noise_sh = DRW_shader_create_fullscreen(datatoc_update_noise_frag_glsl, NULL);
+ e_data.update_noise_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_update_noise_frag_glsl, e_data.lib, NULL);
}
return e_data.update_noise_sh;
}
@@ -437,13 +398,8 @@ GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects)
sh = &e_data.taa_resolve_sh;
}
if (*sh == NULL) {
- char *frag_str = BLI_string_joinN(datatoc_common_uniforms_lib_glsl,
- datatoc_common_view_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_effect_temporal_aa_glsl);
-
- *sh = DRW_shader_create_fullscreen(frag_str, define);
- MEM_freeN(frag_str);
+ *sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_temporal_aa_glsl, e_data.lib, define);
}
return *sh;
@@ -583,6 +539,18 @@ struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo)
return e_data.world.ntree;
}
+World *EEVEE_world_default_get(void)
+{
+ if (e_data.default_world == NULL) {
+ e_data.default_world = BKE_id_new_nomain(ID_WO, "EEVEEE default world");
+ copy_v3_fl(&e_data.default_world->horr, 0.0f);
+ e_data.default_world->use_nodes = 0;
+ e_data.default_world->nodetree = NULL;
+ BLI_listbase_clear(&e_data.default_world->gpumaterial);
+ }
+ return e_data.default_world;
+}
+
static char *eevee_get_defines(int options)
{
char *str = NULL;
@@ -605,7 +573,7 @@ static char *eevee_get_defines(int options)
if ((options & VAR_MAT_HAIR) != 0) {
BLI_dynstr_append(ds, "#define HAIR_SHADER\n");
}
- if ((options & (VAR_MAT_PROBE | VAR_WORLD_PROBE)) != 0) {
+ if ((options & VAR_WORLD_PROBE) != 0) {
BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n");
}
if ((options & VAR_MAT_HASH) != 0) {
@@ -635,13 +603,13 @@ static char *eevee_get_vert(int options)
char *str = NULL;
if ((options & VAR_MAT_VOLUME) != 0) {
- str = BLI_strdup(e_data.vert_volume_shader_str);
+ str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl);
}
else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) {
- str = BLI_strdup(e_data.vert_background_shader_str);
+ str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl);
}
else {
- str = BLI_strdup(e_data.vert_shader_str);
+ str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl);
}
return str;
@@ -652,7 +620,7 @@ static char *eevee_get_geom(int options)
char *str = NULL;
if ((options & VAR_MAT_VOLUME) != 0) {
- str = BLI_strdup(e_data.geom_volume_shader_str);
+ str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_geom_glsl);
}
return str;
@@ -663,18 +631,36 @@ static char *eevee_get_frag(int options)
char *str = NULL;
if ((options & VAR_MAT_VOLUME) != 0) {
- str = BLI_strdup(e_data.volume_shader_lib);
+ str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_frag_glsl);
}
else if ((options & VAR_MAT_DEPTH) != 0) {
- str = BLI_string_joinN(e_data.frag_shader_lib, datatoc_prepass_frag_glsl);
+ str = BLI_strdup(e_data.surface_prepass_frag);
}
else {
- str = BLI_strdup(e_data.frag_shader_lib);
+ str = BLI_strdup(e_data.surface_lit_frag);
}
return str;
}
+static void eevee_material_post_eval(GPUMaterial *mat,
+ int options,
+ const char **UNUSED(vert_code),
+ const char **geom_code,
+ const char **UNUSED(frag_lib),
+ const char **UNUSED(defines))
+{
+ const bool is_hair = (options & VAR_MAT_HAIR) != 0;
+ const bool is_mesh = (options & VAR_MAT_MESH) != 0;
+
+ /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used.
+ * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */
+ if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) &&
+ *geom_code == NULL) {
+ *geom_code = e_data.surface_geom_barycentric;
+ }
+}
+
static struct GPUMaterial *eevee_material_get_ex(
struct Scene *scene, Material *ma, World *wo, int options, bool deferred)
{
@@ -702,14 +688,16 @@ static struct GPUMaterial *eevee_material_get_ex(
char *frag = eevee_get_frag(options);
if (ma) {
+ GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval;
+
bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma);
mat = DRW_shader_create_from_material(
- scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred);
+ scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn);
}
else {
bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo);
mat = DRW_shader_create_from_world(
- scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred);
+ scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL);
}
MEM_SAFE_FREE(defines);
@@ -764,30 +752,31 @@ struct GPUMaterial *EEVEE_material_get(
void EEVEE_shaders_free(void)
{
- MEM_SAFE_FREE(e_data.frag_shader_lib);
- MEM_SAFE_FREE(e_data.vert_shader_str);
- MEM_SAFE_FREE(e_data.vert_shadow_shader_str);
- MEM_SAFE_FREE(e_data.vert_background_shader_str);
- MEM_SAFE_FREE(e_data.vert_volume_shader_str);
- MEM_SAFE_FREE(e_data.geom_volume_shader_str);
- MEM_SAFE_FREE(e_data.volume_shader_lib);
- DRW_SHADER_FREE_SAFE(e_data.default_background);
+ MEM_SAFE_FREE(e_data.closure_lit_lib);
+ MEM_SAFE_FREE(e_data.surface_prepass_frag);
+ MEM_SAFE_FREE(e_data.surface_lit_frag);
+ MEM_SAFE_FREE(e_data.surface_geom_barycentric);
+ DRW_SHADER_FREE_SAFE(e_data.lookdev_background);
DRW_SHADER_FREE_SAFE(e_data.update_noise_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_default_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_default_studiolight_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_background_studiolight_sh);
+ DRW_SHADER_FREE_SAFE(e_data.studiolight_probe_sh);
+ DRW_SHADER_FREE_SAFE(e_data.studiolight_background_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh);
DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh);
DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh);
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh);
+ DRW_SHADER_LIB_FREE_SAFE(e_data.lib);
+ if (e_data.default_world) {
+ BKE_id_free(NULL, e_data.default_world);
+ e_data.default_world = NULL;
+ }
if (e_data.glossy_mat) {
BKE_id_free(NULL, e_data.glossy_mat);
e_data.glossy_mat = NULL;
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 84c50a22ae6..0da356b75ac 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -42,11 +42,6 @@ static struct {
extern char datatoc_shadow_vert_glsl[];
extern char datatoc_shadow_frag_glsl[];
extern char datatoc_shadow_accum_frag_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-extern char datatoc_common_uniforms_lib_glsl[];
-extern char datatoc_bsdf_common_lib_glsl[];
-extern char datatoc_lights_lib_glsl[];
-extern char datatoc_raytrace_lib_glsl[];
void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh)
{
@@ -65,23 +60,13 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata)
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if (!e_data.shadow_sh) {
- e_data.shadow_sh = DRW_shader_create_with_lib(datatoc_shadow_vert_glsl,
- NULL,
- datatoc_shadow_frag_glsl,
- datatoc_common_view_lib_glsl,
- NULL);
- }
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
- if (!e_data.shadow_accum_sh) {
- char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_raytrace_lib_glsl,
- datatoc_lights_lib_glsl,
- datatoc_shadow_accum_frag_glsl);
+ e_data.shadow_sh = DRW_shader_create_with_shaderlib(
+ datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, lib, NULL);
- e_data.shadow_accum_sh = DRW_shader_create_fullscreen(frag_str, SHADER_DEFINES);
- MEM_freeN(frag_str);
+ e_data.shadow_accum_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_shadow_accum_frag_glsl, lib, SHADER_DEFINES);
}
if (!sldata->lights) {
@@ -260,10 +245,8 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
}
if (!sldata->shadow_cube_pool) {
- /* TODO shadowcube array. */
- int cube_size = linfo->shadow_cube_size + ((true) ? 2 : 0);
- sldata->shadow_cube_pool = DRW_texture_create_2d_array(cube_size,
- cube_size,
+ sldata->shadow_cube_pool = DRW_texture_create_2d_array(linfo->shadow_cube_size,
+ linfo->shadow_cube_size,
max_ii(1, linfo->num_cube_layer * 6),
shadow_pool_format,
DRW_TEX_FILTER | DRW_TEX_COMPARE,
@@ -412,7 +395,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..637c5201afc 100644
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.c
@@ -38,55 +38,23 @@ static struct {
struct GPUShader *sss_sh[3];
} e_data = {{NULL}}; /* Engine data */
-extern char datatoc_common_view_lib_glsl[];
-extern char datatoc_common_uniforms_lib_glsl[];
-extern char datatoc_lights_lib_glsl[];
-extern char datatoc_raytrace_lib_glsl[];
-extern char datatoc_octahedron_lib_glsl[];
-extern char datatoc_cubemap_lib_glsl[];
-extern char datatoc_bsdf_sampling_lib_glsl[];
-extern char datatoc_bsdf_common_lib_glsl[];
extern char datatoc_effect_subsurface_frag_glsl[];
extern char datatoc_effect_translucency_frag_glsl[];
static void eevee_create_shader_subsurface(void)
{
- char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_effect_subsurface_frag_glsl);
-
- /* TODO(fclem) remove some of these dependencies. */
- char *frag_translucent_str = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_bsdf_sampling_lib_glsl,
- datatoc_raytrace_lib_glsl,
- datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_lights_lib_glsl,
- datatoc_effect_translucency_frag_glsl);
-
- e_data.sss_sh[0] = DRW_shader_create_fullscreen(frag_str, "#define FIRST_PASS\n");
- e_data.sss_sh[1] = DRW_shader_create_fullscreen(frag_str, "#define SECOND_PASS\n");
- e_data.sss_sh[2] = DRW_shader_create_fullscreen(frag_translucent_str,
- "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES);
-
- MEM_freeN(frag_translucent_str);
- MEM_freeN(frag_str);
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
+
+ e_data.sss_sh[0] = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_subsurface_frag_glsl, lib, "#define FIRST_PASS\n");
+ e_data.sss_sh[1] = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_subsurface_frag_glsl, lib, "#define SECOND_PASS\n");
+ e_data.sss_sh[2] = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_effect_translucency_frag_glsl, lib, "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES);
}
-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 +165,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..997f9a5be9d 100644
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
@@ -96,7 +96,7 @@ static void invert_cdf(const float cdf[FILTER_CDF_TABLE_SIZE],
}
/* Evaluate a discrete function table with linear interpolation. */
-static float eval_table(float *table, float x)
+static float eval_table(const float *table, float x)
{
CLAMP(x, 0.0f, 1.0f);
x = x * (FILTER_CDF_TABLE_SIZE - 1);
@@ -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);
@@ -234,14 +240,13 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
}
- effects->taa_total_sample = EEVEE_renderpasses_only_first_sample_pass_active(vedata) ?
- 1 :
- scene_eval->eevee.taa_samples;
+ const bool first_sample_only = EEVEE_renderpasses_only_first_sample_pass_active(vedata);
+ view_is_valid = view_is_valid && !first_sample_only;
+ effects->taa_total_sample = first_sample_only ? 1 : scene_eval->eevee.taa_samples;
MAX2(effects->taa_total_sample, 0);
DRW_view_persmat_get(NULL, persmat, false);
view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
- 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..57d5e54290e 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -44,16 +44,12 @@
#include "DEG_depsgraph_query.h"
-#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_material.h"
#include "GPU_texture.h"
#include "eevee_private.h"
static struct {
- char *volumetric_common_lib;
- char *volumetric_common_lights_lib;
-
struct GPUShader *volumetric_clear_sh;
struct GPUShader *scatter_sh;
struct GPUShader *scatter_with_lights_sh;
@@ -97,57 +93,48 @@ extern char datatoc_common_fullscreen_vert_glsl[];
static void eevee_create_shader_volumes(void)
{
- e_data.volumetric_common_lib = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_volumetric_lib_glsl);
-
- e_data.volumetric_common_lights_lib = BLI_string_joinN(datatoc_common_view_lib_glsl,
- datatoc_common_uniforms_lib_glsl,
- datatoc_bsdf_common_lib_glsl,
- datatoc_octahedron_lib_glsl,
- datatoc_cubemap_lib_glsl,
- datatoc_irradiance_lib_glsl,
- datatoc_lights_lib_glsl,
- datatoc_volumetric_lib_glsl);
-
- e_data.volumetric_clear_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_frag_glsl,
- e_data.volumetric_common_lib,
- "#define VOLUMETRICS\n"
- "#define CLEAR\n");
- e_data.scatter_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_scatter_frag_glsl,
- e_data.volumetric_common_lights_lib,
- SHADER_DEFINES
- "#define VOLUMETRICS\n"
- "#define VOLUME_SHADOW\n");
- e_data.scatter_with_lights_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_scatter_frag_glsl,
- e_data.volumetric_common_lights_lib,
- SHADER_DEFINES
- "#define VOLUMETRICS\n"
- "#define VOLUME_LIGHTING\n"
- "#define VOLUME_SHADOW\n");
- e_data.volumetric_integration_sh = DRW_shader_create_with_lib(
+ DRWShaderLibrary *lib = EEVEE_shader_lib_get();
+
+ e_data.volumetric_clear_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl,
+ datatoc_volumetric_geom_glsl,
+ datatoc_volumetric_frag_glsl,
+ lib,
+ SHADER_DEFINES
+ "#define VOLUMETRICS\n"
+ "#define CLEAR\n");
+
+ e_data.scatter_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl,
+ datatoc_volumetric_geom_glsl,
+ datatoc_volumetric_scatter_frag_glsl,
+ lib,
+ SHADER_DEFINES
+ "#define VOLUMETRICS\n"
+ "#define VOLUME_SHADOW\n");
+
+ e_data.scatter_with_lights_sh = DRW_shader_create_with_shaderlib(
+ datatoc_volumetric_vert_glsl,
+ datatoc_volumetric_geom_glsl,
+ datatoc_volumetric_scatter_frag_glsl,
+ lib,
+ SHADER_DEFINES
+ "#define VOLUMETRICS\n"
+ "#define VOLUME_LIGHTING\n"
+ "#define VOLUME_SHADOW\n");
+
+ e_data.volumetric_integration_sh = DRW_shader_create_with_shaderlib(
datatoc_volumetric_vert_glsl,
datatoc_volumetric_geom_glsl,
datatoc_volumetric_integration_frag_glsl,
- e_data.volumetric_common_lib,
+ lib,
USE_VOLUME_OPTI ? "#extension GL_ARB_shader_image_load_store: enable\n"
"#extension GL_ARB_shading_language_420pack: enable\n"
- "#define USE_VOLUME_OPTI\n" :
- NULL);
- e_data.volumetric_resolve_sh = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl,
- NULL,
- datatoc_volumetric_resolve_frag_glsl,
- e_data.volumetric_common_lib,
- NULL);
- e_data.volumetric_accum_sh = DRW_shader_create_fullscreen(datatoc_volumetric_accum_frag_glsl,
- NULL);
+ "#define USE_VOLUME_OPTI\n" SHADER_DEFINES :
+ SHADER_DEFINES);
+
+ e_data.volumetric_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_volumetric_resolve_frag_glsl, lib, SHADER_DEFINES);
+ e_data.volumetric_accum_sh = DRW_shader_create_fullscreen_with_shaderlib(
+ datatoc_volumetric_accum_frag_glsl, lib, SHADER_DEFINES);
const float density[4] = {1.0f, 1.0f, 1.0f, 1.0f};
e_data.dummy_density = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, density);
@@ -259,17 +246,11 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
common_data->vol_shadow_steps = 0;
}
- /* Update view_vecs */
- float invproj[4][4], winmat[4][4];
- DRW_view_winmat_get(NULL, winmat, false);
- DRW_view_winmat_get(NULL, invproj, true);
- EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs);
-
if (DRW_view_is_persp_get(NULL)) {
float sample_distribution = scene_eval->eevee.volumetric_sample_distribution;
sample_distribution = 4.0f * (max_ff(1.0f - sample_distribution, 1e-2f));
- const float clip_start = common_data->view_vecs[0][2];
+ const float clip_start = DRW_view_near_distance_get(NULL);
/* Negate */
float near = integration_start = min_ff(-integration_start, clip_start - 1e-4f);
float far = integration_end = min_ff(-integration_end, near - 1e-4f);
@@ -280,8 +261,8 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
common_data->vol_depth_param[2] = sample_distribution;
}
else {
- const float clip_start = common_data->view_vecs[0][2];
- const float clip_end = clip_start + common_data->view_vecs[1][2];
+ const float clip_start = DRW_view_near_distance_get(NULL);
+ const float clip_end = DRW_view_far_distance_get(NULL);
integration_start = min_ff(integration_end, clip_start);
integration_end = max_ff(-integration_end, clip_end);
@@ -487,11 +468,11 @@ static bool eevee_volume_object_mesh_init(Scene *scene,
if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
(BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) &&
((FluidModifierData *)md)->domain != NULL) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ FluidDomainSettings *fds = fmd->domain;
/* Don't try to show liquid domains here. */
- if (!mds->fluid || !(mds->type == FLUID_DOMAIN_TYPE_GAS)) {
+ if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
return false;
}
@@ -500,33 +481,28 @@ static bool eevee_volume_object_mesh_init(Scene *scene,
#if 0
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool show_smoke = ((int)DEG_get_ctime(draw_ctx->depsgraph) >=
- *mds->point_cache[0]->startframe);
+ *fds->point_cache[0]->startframe);
#endif
- if (mds->fluid && (mds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) {
- if (!(mds->flags & FLUID_DOMAIN_USE_NOISE)) {
- GPU_create_smoke(mmd, 0);
- }
- else if (mds->flags & FLUID_DOMAIN_USE_NOISE) {
- GPU_create_smoke(mmd, 1);
- }
- BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(mmd));
+ if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) {
+ DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
+ BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd));
}
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
if (STREQ(gpu_grid->name, "density")) {
DRW_shgroup_uniform_texture_ref(grp,
gpu_grid->sampler_name,
- mds->tex_density ? &mds->tex_density :
+ fds->tex_density ? &fds->tex_density :
&e_data.dummy_density);
}
else if (STREQ(gpu_grid->name, "color")) {
DRW_shgroup_uniform_texture_ref(
- grp, gpu_grid->sampler_name, mds->tex_color ? &mds->tex_color : &e_data.dummy_density);
+ grp, gpu_grid->sampler_name, fds->tex_color ? &fds->tex_color : &e_data.dummy_density);
}
else if (STREQ(gpu_grid->name, "flame") || STREQ(gpu_grid->name, "temperature")) {
DRW_shgroup_uniform_texture_ref(
- grp, gpu_grid->sampler_name, mds->tex_flame ? &mds->tex_flame : &e_data.dummy_flame);
+ grp, gpu_grid->sampler_name, fds->tex_flame ? &fds->tex_flame : &e_data.dummy_flame);
}
else {
DRW_shgroup_uniform_texture_ref(grp, gpu_grid->sampler_name, &e_data.dummy_density);
@@ -534,14 +510,14 @@ static bool eevee_volume_object_mesh_init(Scene *scene,
}
/* Constant Volume color. */
- bool use_constant_color = ((mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
- (mds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
+ bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
+ (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
DRW_shgroup_uniform_vec3(
- grp, "volumeColor", (use_constant_color) ? mds->active_color : white, 1);
+ grp, "volumeColor", (use_constant_color) ? fds->active_color : white, 1);
/* Output is such that 0..1 maps to 0..1000K */
- DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &mds->flame_ignition, 1);
+ DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &fds->flame_ignition, 1);
}
else {
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
@@ -840,17 +816,14 @@ void EEVEE_volumes_free_smoke_textures(void)
{
/* Free Smoke Textures after rendering */
LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) {
- FluidModifierData *mmd = (FluidModifierData *)link->data;
- GPU_free_smoke(mmd);
+ FluidModifierData *fmd = (FluidModifierData *)link->data;
+ DRW_smoke_free(fmd);
}
BLI_freelistN(&e_data.smoke_domains);
}
void EEVEE_volumes_free(void)
{
- MEM_SAFE_FREE(e_data.volumetric_common_lib);
- MEM_SAFE_FREE(e_data.volumetric_common_lights_lib);
-
DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter);
DRW_TEXTURE_FREE_SAFE(e_data.dummy_transmit);
@@ -894,7 +867,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/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 57b16418696..2f6f8327f58 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -1,4 +1,7 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
+
/* Based on Practical Realtime Strategies for Accurate Indirect Occlusion
* http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf
* http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx
@@ -24,12 +27,6 @@
#define MAX_SEARCH_ITER 32
#define MAX_LOD 6.0
-#ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-#endif /* UTIL_TEX */
-
uniform sampler2D horizonBuffer;
/* aoSettings flags */
@@ -243,6 +240,11 @@ float gtao_multibounce(float visibility, vec3 albedo)
return max(x, ((x * a + b) * x + c) * x);
}
+float specular_occlusion(float NV, float AO, float roughness)
+{
+ return saturate(pow(NV + AO, roughness) - 1.0 + AO);
+}
+
/* Use the right occlusion */
float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal)
{
diff --git a/source/blender/draw/engines/eevee/shaders/background_vert.glsl b/source/blender/draw/engines/eevee/shaders/background_vert.glsl
index aff8e0857f6..ab5d9a7ebe4 100644
--- a/source/blender/draw/engines/eevee/shaders/background_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/background_vert.glsl
@@ -1,17 +1,17 @@
-in vec2 pos;
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
-out vec3 viewPosition;
+in vec2 pos;
-#ifndef VOLUMETRICS
-/* necessary for compilation*/
-out vec3 worldPosition;
-out vec3 worldNormal;
-out vec3 viewNormal;
-#endif
+RESOURCE_ID_VARYING
void main()
{
+ GPU_INTEL_VERTEX_SHADER_WORKAROUND
+
+ PASS_RESOURCE_ID
+
gl_Position = vec4(pos, 1.0, 1.0);
viewPosition = vec3(pos, -1.0);
@@ -22,6 +22,6 @@ void main()
#endif
#ifdef USE_ATTR
- pass_attr(viewPosition);
+ pass_attr(viewPosition, NormalMatrix, ModelMatrixInverse);
#endif
}
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..deedde64194 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -1,486 +1,7 @@
-#define M_PI 3.14159265358979323846 /* pi */
-#define M_2PI 6.28318530717958647692 /* 2*pi */
-#define M_PI_2 1.57079632679489661923 /* pi/2 */
-#define M_1_PI 0.318309886183790671538 /* 1/pi */
-#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */
-#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */
-#define FLT_MAX 3.402823e+38
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#define LUT_SIZE 64
-
-/* Buffers */
-uniform sampler2D colorBuffer;
-uniform sampler2D depthBuffer;
-uniform sampler2D maxzBuffer;
-uniform sampler2D minzBuffer;
-uniform sampler2DArray planarDepth;
-
-#define cameraForward ViewMatrixInverse[2].xyz
-#define cameraPos ViewMatrixInverse[3].xyz
-#define cameraVec \
- ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - worldPosition) : cameraForward)
-#define viewCameraVec \
- ((ProjectionMatrix[3][3] == 0.0) ? normalize(-viewPosition) : vec3(0.0, 0.0, 1.0))
-
-/* ------- Structures -------- */
-
-/* ------ Lights ----- */
-struct LightData {
- vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */
- vec4 color_spec; /* w : Spec Intensity */
- vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
- vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
- vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
- vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */
-};
-
-/* convenience aliases */
-#define l_color color_spec.rgb
-#define l_spec color_spec.a
-#define l_position position_influence.xyz
-#define l_influence position_influence.w
-#define l_sizex rightvec_sizex.w
-#define l_sizey upvec_sizey.w
-#define l_right rightvec_sizex.xyz
-#define l_up upvec_sizey.xyz
-#define l_forward forwardvec_type.xyz
-#define l_type forwardvec_type.w
-#define l_spot_size spotdata_radius_shadow.x
-#define l_spot_blend spotdata_radius_shadow.y
-#define l_radius spotdata_radius_shadow.z
-#define l_shadowid spotdata_radius_shadow.w
-
-/* ------ Shadows ----- */
-#ifndef MAX_CASCADE_NUM
-# define MAX_CASCADE_NUM 4
-#endif
-
-struct ShadowData {
- vec4 near_far_bias_id;
- vec4 contact_shadow_data;
-};
-
-struct ShadowCubeData {
- mat4 shadowmat;
- vec4 position;
-};
-
-struct ShadowCascadeData {
- mat4 shadowmat[MAX_CASCADE_NUM];
- vec4 split_start_distances;
- vec4 split_end_distances;
- vec4 shadow_vec_id;
-};
-
-/* convenience aliases */
-#define sh_near near_far_bias_id.x
-#define sh_far near_far_bias_id.y
-#define sh_bias near_far_bias_id.z
-#define sh_data_index near_far_bias_id.w
-#define sh_contact_dist contact_shadow_data.x
-#define sh_contact_offset contact_shadow_data.y
-#define sh_contact_spread contact_shadow_data.z
-#define sh_contact_thickness contact_shadow_data.w
-#define sh_shadow_vec shadow_vec_id.xyz
-#define sh_tex_index shadow_vec_id.w
-
-/* ------ Render Passes ----- */
-layout(std140) uniform renderpass_block
-{
- bool renderPassDiffuse;
- bool renderPassDiffuseLight;
- bool renderPassGlossy;
- bool renderPassGlossyLight;
- bool renderPassEmit;
- bool renderPassSSSColor;
-};
-
-vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light)
-{
- return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0);
-}
-
-vec3 render_pass_sss_mask(vec3 sss_color)
-{
- return renderPassSSSColor ? sss_color : vec3(0.0);
-}
-
-vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light)
-{
- return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0);
-}
-
-vec3 render_pass_emission_mask(vec3 emission_light)
-{
- return renderPassEmit ? emission_light : vec3(0.0);
-}
-
-/* ------- Convenience functions --------- */
-
-vec3 mul(mat3 m, vec3 v)
-{
- return m * v;
-}
-mat3 mul(mat3 m1, mat3 m2)
-{
- return m1 * m2;
-}
-vec3 transform_direction(mat4 m, vec3 v)
-{
- return mat3(m) * v;
-}
-vec3 transform_point(mat4 m, vec3 v)
-{
- return (m * vec4(v, 1.0)).xyz;
-}
-vec3 project_point(mat4 m, vec3 v)
-{
- vec4 tmp = m * vec4(v, 1.0);
- return tmp.xyz / tmp.w;
-}
-
-#define min3(a, b, c) min(a, min(b, c))
-#define min4(a, b, c, d) min(a, min3(b, c, d))
-#define min5(a, b, c, d, e) min(a, min4(b, c, d, e))
-#define min6(a, b, c, d, e, f) min(a, min5(b, c, d, e, f))
-#define min7(a, b, c, d, e, f, g) min(a, min6(b, c, d, e, f, g))
-#define min8(a, b, c, d, e, f, g, h) min(a, min7(b, c, d, e, f, g, h))
-#define min9(a, b, c, d, e, f, g, h, i) min(a, min8(b, c, d, e, f, g, h, i))
-
-#define max3(a, b, c) max(a, max(b, c))
-#define max4(a, b, c, d) max(a, max3(b, c, d))
-#define max5(a, b, c, d, e) max(a, max4(b, c, d, e))
-#define max6(a, b, c, d, e, f) max(a, max5(b, c, d, e, f))
-#define max7(a, b, c, d, e, f, g) max(a, max6(b, c, d, e, f, g))
-#define max8(a, b, c, d, e, f, g, h) max(a, max7(b, c, d, e, f, g, h))
-#define max9(a, b, c, d, e, f, g, h, i) max(a, max8(b, c, d, e, f, g, h, i))
-
-#define avg3(a, b, c) (a + b + c) * (1.0 / 3.0)
-#define avg4(a, b, c, d) (a + b + c + d) * (1.0 / 4.0)
-#define avg5(a, b, c, d, e) (a + b + c + d + e) * (1.0 / 5.0)
-#define avg6(a, b, c, d, e, f) (a + b + c + d + e + f) * (1.0 / 6.0)
-#define avg7(a, b, c, d, e, f, g) (a + b + c + d + e + f + g) * (1.0 / 7.0)
-#define avg8(a, b, c, d, e, f, g, h) (a + b + c + d + e + f + g + h) * (1.0 / 8.0)
-#define avg9(a, b, c, d, e, f, g, h, i) (a + b + c + d + e + f + g + h + i) * (1.0 / 9.0)
-
-float min_v2(vec2 v)
-{
- return min(v.x, v.y);
-}
-float min_v3(vec3 v)
-{
- return min(v.x, min(v.y, v.z));
-}
-float min_v4(vec4 v)
-{
- return min(min(v.x, v.y), min(v.z, v.w));
-}
-float max_v2(vec2 v)
-{
- return max(v.x, v.y);
-}
-float max_v3(vec3 v)
-{
- return max(v.x, max(v.y, v.z));
-}
-float max_v4(vec4 v)
-{
- return max(max(v.x, v.y), max(v.z, v.w));
-}
-
-float sum(vec2 v)
-{
- return dot(vec2(1.0), v);
-}
-float sum(vec3 v)
-{
- return dot(vec3(1.0), v);
-}
-float sum(vec4 v)
-{
- return dot(vec4(1.0), v);
-}
-
-float avg(vec2 v)
-{
- return dot(vec2(1.0 / 2.0), v);
-}
-float avg(vec3 v)
-{
- return dot(vec3(1.0 / 3.0), v);
-}
-float avg(vec4 v)
-{
- return dot(vec4(1.0 / 4.0), v);
-}
-
-float saturate(float a)
-{
- return clamp(a, 0.0, 1.0);
-}
-vec2 saturate(vec2 a)
-{
- return clamp(a, 0.0, 1.0);
-}
-vec3 saturate(vec3 a)
-{
- return clamp(a, 0.0, 1.0);
-}
-vec4 saturate(vec4 a)
-{
- return clamp(a, 0.0, 1.0);
-}
-
-float distance_squared(vec2 a, vec2 b)
-{
- a -= b;
- return dot(a, a);
-}
-float distance_squared(vec3 a, vec3 b)
-{
- a -= b;
- return dot(a, a);
-}
-float len_squared(vec3 a)
-{
- return dot(a, a);
-}
-
-float inverse_distance(vec3 V)
-{
- return max(1 / length(V), 1e-8);
-}
-
-vec2 mip_ratio_interp(float mip)
-{
- float low_mip = floor(mip);
- return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip);
-}
-
-/* ------- RNG ------- */
-
-float wang_hash_noise(uint s)
-{
- s = (s ^ 61u) ^ (s >> 16u);
- s *= 9u;
- s = s ^ (s >> 4u);
- s *= 0x27d4eb2du;
- s = s ^ (s >> 15u);
-
- return fract(float(s) / 4294967296.0);
-}
-
-/* ------- Fast Math ------- */
-
-/* [Drobot2014a] Low Level Optimizations for GCN */
-float fast_sqrt(float v)
-{
- return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
-}
-
-vec2 fast_sqrt(vec2 v)
-{
- return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
-}
-
-/* [Eberly2014] GPGPU Programming for Games and Science */
-float fast_acos(float v)
-{
- float res = -0.156583 * abs(v) + M_PI_2;
- res *= fast_sqrt(1.0 - abs(v));
- return (v >= 0) ? res : M_PI - res;
-}
-
-vec2 fast_acos(vec2 v)
-{
- vec2 res = -0.156583 * abs(v) + M_PI_2;
- res *= fast_sqrt(1.0 - abs(v));
- v.x = (v.x >= 0) ? res.x : M_PI - res.x;
- v.y = (v.y >= 0) ? res.y : M_PI - res.y;
- return v;
-}
-
-float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal)
-{
- return dot(planenormal, planeorigin - lineorigin);
-}
-
-float line_plane_intersect_dist(vec3 lineorigin,
- vec3 linedirection,
- vec3 planeorigin,
- vec3 planenormal)
-{
- return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
-}
-
-float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane)
-{
- vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz));
- vec3 h = lineorigin - plane_co;
- return -dot(plane.xyz, h) / dot(plane.xyz, linedirection);
-}
-
-vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
-{
- float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
- return lineorigin + linedirection * dist;
-}
-
-vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane)
-{
- float dist = line_plane_intersect_dist(lineorigin, linedirection, plane);
- return lineorigin + linedirection * dist;
-}
-
-float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
-{
- /* aligned plane normal */
- vec3 L = planeorigin - lineorigin;
- float diskdist = length(L);
- vec3 planenormal = -normalize(L);
- return -diskdist / dot(planenormal, linedirection);
-}
-
-vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
-{
- float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
- if (dist < 0) {
- /* if intersection is behind we fake the intersection to be
- * really far and (hopefully) not inside the radius of interest */
- dist = 1e16;
- }
- return lineorigin + linedirection * dist;
-}
-
-float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
-{
- float a = dot(linedirection, linedirection);
- float b = dot(linedirection, lineorigin);
- float c = dot(lineorigin, lineorigin) - 1;
-
- float dist = 1e15;
- float determinant = b * b - a * c;
- if (determinant >= 0) {
- dist = (sqrt(determinant) - b) / a;
- }
-
- return dist;
-}
-
-float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
-{
- /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
- */
- vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection;
- vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection;
- vec3 furthestplane = max(firstplane, secondplane);
-
- return min_v3(furthestplane);
-}
-
-/* Return texture coordinates to sample Surface LUT */
-vec2 lut_coords(float cosTheta, float roughness)
-{
- float theta = acos(cosTheta);
- vec2 coords = vec2(roughness, theta / M_PI_2);
-
- /* scale and bias coordinates, for correct filtered lookup */
- return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
-}
-
-vec2 lut_coords_ltc(float cosTheta, float roughness)
-{
- vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta));
-
- /* scale and bias coordinates, for correct filtered lookup */
- return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
-}
-
-/* -- Tangent Space conversion -- */
-vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B)
-{
- return T * vector.x + B * vector.y + N * vector.z;
-}
-
-vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B)
-{
- return vec3(dot(T, vector), dot(B, vector), dot(N, vector));
-}
-
-void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
-{
- vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
- T = normalize(cross(UpVector, N));
- B = cross(N, T);
-}
-
-/* ---- Opengl Depth conversion ---- */
-
-float linear_depth(bool is_persp, float z, float zf, float zn)
-{
- if (is_persp) {
- return (zn * zf) / (z * (zn - zf) + zf);
- }
- else {
- return (z * 2.0 - 1.0) * zf;
- }
-}
-
-float buffer_depth(bool is_persp, float z, float zf, float zn)
-{
- if (is_persp) {
- return (zf * (zn - z)) / (z * (zn - zf));
- }
- else {
- return (z / (zf * 2.0)) + 0.5;
- }
-}
-
-float get_view_z_from_depth(float depth)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- float d = 2.0 * depth - 1.0;
- return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]);
- }
- else {
- return viewVecs[0].z + depth * viewVecs[1].z;
- }
-}
-
-float get_depth_from_view_z(float z)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2];
- return d * 0.5 + 0.5;
- }
- else {
- return (z - viewVecs[0].z) / viewVecs[1].z;
- }
-}
-
-vec2 get_uvs_from_view(vec3 view)
-{
- vec3 ndc = project_point(ProjectionMatrix, view);
- return ndc.xy * 0.5 + 0.5;
-}
-
-vec3 get_view_space_from_depth(vec2 uvcoords, float depth)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- return vec3(viewVecs[0].xy + uvcoords * viewVecs[1].xy, 1.0) * get_view_z_from_depth(depth);
- }
- else {
- return viewVecs[0].xyz + vec3(uvcoords, depth) * viewVecs[1].xyz;
- }
-}
-
-vec3 get_world_space_from_depth(vec2 uvcoords, float depth)
-{
- return (ViewMatrixInverse * vec4(get_view_space_from_depth(uvcoords, depth), 1.0)).xyz;
-}
-
-vec3 get_specular_reflection_dominant_dir(vec3 N, vec3 V, float roughness)
+vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
{
vec3 R = -reflect(V, N);
float smoothness = 1.0 - roughness;
@@ -488,13 +9,6 @@ vec3 get_specular_reflection_dominant_dir(vec3 N, vec3 V, float roughness)
return normalize(mix(N, R, fac));
}
-float specular_occlusion(float NV, float AO, float roughness)
-{
- return saturate(pow(NV + AO, roughness) - 1.0 + AO);
-}
-
-/* --- Refraction utils --- */
-
float ior_from_f0(float f0)
{
float f = sqrt(f0);
@@ -507,7 +21,7 @@ float f0_from_ior(float eta)
return A * A;
}
-vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior)
+vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior)
{
/* TODO: This a bad approximation. Better approximation should fit
* the refracted vector and roughness into the best prefiltered reflection
@@ -526,128 +40,6 @@ vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float
return R;
}
-float get_btdf_lut(sampler2DArray btdf_lut_tex, float NV, float roughness, float ior)
-{
- const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE;
-
- vec3 coords;
- /* Try to compensate for the low resolution and interpolation error. */
- coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) +
- (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) :
- (0.9 + lut_scale_bias_texel_size.z) * ior * ior;
- coords.y = 1.0 - saturate(NV);
- coords.xy *= lut_scale_bias_texel_size.x;
- coords.xy += lut_scale_bias_texel_size.y;
-
- const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */
- const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */
-
- float mip = roughness * lut_lvl_scale;
- float mip_floor = floor(mip);
-
- coords.z = lut_lvl_ofs + mip_floor + 1.0;
- float btdf_high = textureLod(btdf_lut_tex, coords, 0.0).r;
-
- coords.z -= 1.0;
- float btdf_low = textureLod(btdf_lut_tex, coords, 0.0).r;
-
- float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z);
-
- return btdf;
-}
-
-/* ---- Encode / Decode Normal buffer data ---- */
-/* From http://aras-p.info/texts/CompactNormalStorage.html
- * Using Method #4: Spheremap Transform */
-vec2 normal_encode(vec3 n, vec3 view)
-{
- float p = sqrt(n.z * 8.0 + 8.0);
- return n.xy / p + 0.5;
-}
-
-vec3 normal_decode(vec2 enc, vec3 view)
-{
- vec2 fenc = enc * 4.0 - 2.0;
- float f = dot(fenc, fenc);
- float g = sqrt(1.0 - f / 4.0);
- vec3 n;
- n.xy = fenc * g;
- n.z = 1 - f / 2;
- return n;
-}
-
-/* ---- RGBM (shared multiplier) encoding ---- */
-/* From http://iwasbeingirony.blogspot.fr/2010/06/difference-between-rgbm-and-rgbd.html */
-
-/* Higher RGBM_MAX_RANGE gives imprecision issues in low intensity. */
-#define RGBM_MAX_RANGE 512.0
-
-vec4 rgbm_encode(vec3 rgb)
-{
- float maxRGB = max_v3(rgb);
- float M = maxRGB / RGBM_MAX_RANGE;
- M = ceil(M * 255.0) / 255.0;
- return vec4(rgb / (M * RGBM_MAX_RANGE), M);
-}
-
-vec3 rgbm_decode(vec4 data)
-{
- return data.rgb * (data.a * RGBM_MAX_RANGE);
-}
-
-/* ---- RGBE (shared exponent) encoding ---- */
-vec4 rgbe_encode(vec3 rgb)
-{
- float maxRGB = max_v3(rgb);
- float fexp = ceil(log2(maxRGB));
- return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0);
-}
-
-vec3 rgbe_decode(vec4 data)
-{
- float fexp = data.a * 255.0 - 128.0;
- return data.rgb * exp2(fexp);
-}
-
-#if 1
-# define irradiance_encode rgbe_encode
-# define irradiance_decode rgbe_decode
-#else /* No ecoding (when using floating point format) */
-# define irradiance_encode(X) (X).rgbb
-# define irradiance_decode(X) (X).rgb
-#endif
-
-/* Irradiance Visibility Encoding */
-#if 1
-vec4 visibility_encode(vec2 accum, float range)
-{
- accum /= range;
-
- vec4 data;
- data.x = fract(accum.x);
- data.y = floor(accum.x) / 255.0;
- data.z = fract(accum.y);
- data.w = floor(accum.y) / 255.0;
-
- return data;
-}
-
-vec2 visibility_decode(vec4 data, float range)
-{
- return (data.xz + data.yw * 255.0) * range;
-}
-#else /* No ecoding (when using floating point format) */
-vec4 visibility_encode(vec2 accum, float range)
-{
- return accum.xyxy;
-}
-
-vec2 visibility_decode(vec4 data, float range)
-{
- return data.xy;
-}
-#endif
-
/* Fresnel monochromatic, perfect mirror */
float F_eta(float eta, float cos_theta)
{
@@ -765,256 +157,3 @@ float cone_cosine(float r)
/* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion*/
return exp2(-3.32193 * r * r);
}
-
-/* --------- Closure ---------- */
-
-#ifdef VOLUMETRICS
-
-struct Closure {
- vec3 absorption;
- vec3 scatter;
- vec3 emission;
- float anisotropy;
-};
-
-Closure nodetree_exec(void); /* Prototype */
-
-# define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), vec3(0.0), 0.0)
-
-Closure closure_mix(Closure cl1, Closure cl2, float fac)
-{
- Closure cl;
- cl.absorption = mix(cl1.absorption, cl2.absorption, fac);
- cl.scatter = mix(cl1.scatter, cl2.scatter, fac);
- cl.emission = mix(cl1.emission, cl2.emission, fac);
- cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac);
- return cl;
-}
-
-Closure closure_add(Closure cl1, Closure cl2)
-{
- Closure cl;
- cl.absorption = cl1.absorption + cl2.absorption;
- cl.scatter = cl1.scatter + cl2.scatter;
- cl.emission = cl1.emission + cl2.emission;
- cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */
- return cl;
-}
-
-Closure closure_emission(vec3 rgb)
-{
- Closure cl = CLOSURE_DEFAULT;
- cl.emission = rgb;
- return cl;
-}
-
-#else /* VOLUMETRICS */
-
-struct Closure {
- vec3 radiance;
- vec3 transmittance;
- float holdout;
-# ifdef USE_SSS
- vec3 sss_irradiance;
- vec3 sss_albedo;
- float sss_radius;
-# endif
- vec4 ssr_data;
- vec2 ssr_normal;
- int flag;
-};
-
-Closure nodetree_exec(void); /* Prototype */
-
-# define FLAG_TEST(flag, val) (((flag) & (val)) != 0)
-
-# define CLOSURE_SSR_FLAG 1
-# define CLOSURE_SSS_FLAG 2
-# define CLOSURE_HOLDOUT_FLAG 4
-
-# ifdef USE_SSS
-# define CLOSURE_DEFAULT \
- Closure(vec3(0.0), vec3(0.0), 0.0, vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0)
-# else
-# define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0)
-# endif
-
-uniform int outputSsrId = 1;
-uniform int outputSssId = 1;
-
-void closure_load_ssr_data(
- vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl)
-{
- /* Still encode to avoid artifacts in the SSR pass. */
- vec3 vN = normalize(mat3(ViewMatrix) * N);
- cl.ssr_normal = normal_encode(vN, viewVec);
-
- if (ssr_id == outputSsrId) {
- cl.ssr_data = vec4(ssr_spec, roughness);
- cl.flag |= CLOSURE_SSR_FLAG;
- }
-}
-
-void closure_load_sss_data(
- float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl)
-{
-# ifdef USE_SSS
- if (sss_id == outputSssId) {
- cl.sss_irradiance = sss_irradiance;
- cl.sss_radius = radius;
- cl.sss_albedo = sss_albedo;
- cl.flag |= CLOSURE_SSS_FLAG;
- cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0));
- }
- else
-# endif
- {
- cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo);
- }
-}
-
-Closure closure_mix(Closure cl1, Closure cl2, float fac)
-{
- Closure cl;
- cl.holdout = mix(cl1.holdout, cl2.holdout, fac);
- cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac);
- cl.radiance = mix(cl1.radiance, cl2.radiance, fac);
- cl.flag = cl1.flag | cl2.flag;
- cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac);
- bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
- /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz).*/
- cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
- cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
-
-# ifdef USE_SSS
- cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac);
- bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
- /* It also does not make sense to mix SSS radius or irradiance. */
- cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
- cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
-# endif
- return cl;
-}
-
-Closure closure_add(Closure cl1, Closure cl2)
-{
- Closure cl;
- cl.transmittance = cl1.transmittance + cl2.transmittance;
- cl.radiance = cl1.radiance + cl2.radiance;
- cl.holdout = cl1.holdout + cl2.holdout;
- cl.flag = cl1.flag | cl2.flag;
- cl.ssr_data = cl1.ssr_data + cl2.ssr_data;
- bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
- /* When mixing SSR don't blend roughness and normals.*/
- cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
- cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
-
-# ifdef USE_SSS
- cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo;
- bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
- /* It also does not make sense to mix SSS radius or irradiance. */
- cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
- cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
-# endif
- return cl;
-}
-
-Closure closure_emission(vec3 rgb)
-{
- Closure cl = CLOSURE_DEFAULT;
- cl.radiance = rgb;
- return cl;
-}
-
-/* Breaking this across multiple lines causes issues for some older GLSL compilers. */
-/* clang-format off */
-# if defined(MESH_SHADER) && !defined(DEPTH_SHADER)
-/* clang-format on */
-# ifndef USE_ALPHA_BLEND
-layout(location = 0) out vec4 outRadiance;
-layout(location = 1) out vec2 ssrNormals;
-layout(location = 2) out vec4 ssrData;
-# ifdef USE_SSS
-layout(location = 3) out vec3 sssIrradiance;
-layout(location = 4) out float sssRadius;
-layout(location = 5) out vec3 sssAlbedo;
-# endif
-# else /* USE_ALPHA_BLEND */
-/* Use dual source blending to be able to make a whole range of effects. */
-layout(location = 0, index = 0) out vec4 outRadiance;
-layout(location = 0, index = 1) out vec4 outTransmittance;
-# endif /* USE_ALPHA_BLEND */
-
-# if defined(USE_ALPHA_BLEND)
-/* Prototype because this file is included before volumetric_lib.glsl */
-void volumetric_resolve(vec2 frag_uvs,
- float frag_depth,
- out vec3 transmittance,
- out vec3 scattering);
-# endif
-
-# define NODETREE_EXEC
-void main()
-{
- Closure cl = nodetree_exec();
-
- float holdout = 1.0 - saturate(cl.holdout);
- float transmit = saturate(avg(cl.transmittance));
- float alpha = 1.0 - transmit;
-
-# ifdef USE_ALPHA_BLEND
- vec2 uvs = gl_FragCoord.xy * volCoordScale.zw;
- vec3 vol_transmit, vol_scatter;
- volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter);
-
- /* Removes part of the volume scattering that have
- * already been added to the destination pixels.
- * 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);
-# else
- outRadiance = vec4(cl.radiance, holdout);
- ssrNormals = cl.ssr_normal;
- ssrData = cl.ssr_data;
-# ifdef USE_SSS
- sssIrradiance = cl.sss_irradiance;
- sssRadius = cl.sss_radius;
- sssAlbedo = cl.sss_albedo;
-# endif
-# endif
-
- /* For Probe capture */
-# ifdef USE_SSS
- float fac = float(!sssToggle);
-
- /* TODO(fclem) we shouldn't need this.
- * Just disable USE_SSS when USE_REFRACTION is enabled. */
-# ifdef USE_REFRACTION
- /* SSRefraction pass is done after the SSS pass.
- * In order to not loose the diffuse light totally we
- * need to merge the SSS radiance to the main radiance. */
- fac = 1.0;
-# endif
-
- outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac;
-# endif
-
-# ifdef LOOKDEV
- gl_FragDepth = 0.0;
-# endif
-
-# ifndef USE_ALPHA_BLEND
- float alpha_div = 1.0 / max(1e-8, alpha);
- outRadiance.rgb *= alpha_div;
- ssrData.rgb *= alpha_div;
-# ifdef USE_SSS
- sssAlbedo.rgb *= alpha_div;
-# endif
-# endif
-}
-
-# endif /* MESH_SHADER */
-
-#endif /* VOLUMETRICS */
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl
index f05b3396428..2b2da884fde 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl
@@ -1,3 +1,4 @@
+#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
out vec4 FragColor;
@@ -5,8 +6,8 @@ void main()
{
vec3 N, T, B, V;
- float NV = (1.0 - (clamp(gl_FragCoord.y / BRDF_LUT_SIZE, 1e-4, 0.9999)));
- float sqrtRoughness = clamp(gl_FragCoord.x / BRDF_LUT_SIZE, 1e-4, 0.9999);
+ float NV = (1.0 - (clamp(gl_FragCoord.y / b, 1e-4, 0.9999)));
+ float sqrtRoughness = clamp(gl_FragCoord.x / LUT_SIZE, 1e-4, 0.9999);
float a = sqrtRoughness * sqrtRoughness;
float a2 = a * a;
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl
index 5f2b719095e..066ea58e2bf 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl
@@ -1,6 +1,7 @@
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+
uniform sampler1D texHammersley;
-uniform sampler2D texJitter;
uniform float sampleCount;
uniform float invSampleCount;
@@ -8,8 +9,7 @@ vec2 jitternoise = vec2(0.0);
#ifndef UTIL_TEX
# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
+
#endif /* UTIL_TEX */
void setup_noise(void)
@@ -17,6 +17,11 @@ void setup_noise(void)
jitternoise = texelfetch_noise_tex(gl_FragCoord.xy).rg; /* Global variable */
}
+vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B)
+{
+ return T * vector.x + B * vector.y + N * vector.z;
+}
+
#ifdef HAMMERSLEY_SIZE
vec3 hammersley_3d(float i, float invsamplenbr)
{
diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl
index 1389a9763c0..d815d9d4e6b 100644
--- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl
@@ -1,3 +1,4 @@
+#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
uniform float a2;
@@ -7,8 +8,8 @@ void main()
{
vec3 N, T, B, V;
- float x = gl_FragCoord.x / BRDF_LUT_SIZE;
- float y = gl_FragCoord.y / BRDF_LUT_SIZE;
+ float x = gl_FragCoord.x / LUT_SIZE;
+ float y = gl_FragCoord.y / LUT_SIZE;
/* There is little variation if ior > 1.0 so we
* maximize LUT precision for ior < 1.0 */
x = x * 1.1;
diff --git a/source/blender/draw/engines/eevee/shaders/closure_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl
new file mode 100644
index 00000000000..0be638f3cbb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl
@@ -0,0 +1,181 @@
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
+
+#ifndef VOLUMETRICS
+
+uniform int outputSsrId = 1;
+uniform int outputSssId = 1;
+
+#endif
+
+struct Closure {
+#ifdef VOLUMETRICS
+ vec3 absorption;
+ vec3 scatter;
+ vec3 emission;
+ float anisotropy;
+
+#else /* SURFACE */
+ vec3 radiance;
+ vec3 transmittance;
+ float holdout;
+ vec4 ssr_data;
+ vec2 ssr_normal;
+ int flag;
+# ifdef USE_SSS
+ vec3 sss_irradiance;
+ vec3 sss_albedo;
+ float sss_radius;
+# endif
+
+#endif
+};
+
+/* Prototype */
+Closure nodetree_exec(void);
+
+/* clang-format off */
+/* Avoid multiline defines. */
+#ifdef VOLUMETRICS
+# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0)
+#elif !defined(USE_SSS)
+# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0)
+#else
+# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0)
+#endif
+/* clang-format on */
+
+#define FLAG_TEST(flag, val) (((flag) & (val)) != 0)
+
+#define CLOSURE_SSR_FLAG 1
+#define CLOSURE_SSS_FLAG 2
+#define CLOSURE_HOLDOUT_FLAG 4
+
+#ifdef VOLUMETRICS
+Closure closure_mix(Closure cl1, Closure cl2, float fac)
+{
+ Closure cl;
+ cl.absorption = mix(cl1.absorption, cl2.absorption, fac);
+ cl.scatter = mix(cl1.scatter, cl2.scatter, fac);
+ cl.emission = mix(cl1.emission, cl2.emission, fac);
+ cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac);
+ return cl;
+}
+
+Closure closure_add(Closure cl1, Closure cl2)
+{
+ Closure cl;
+ cl.absorption = cl1.absorption + cl2.absorption;
+ cl.scatter = cl1.scatter + cl2.scatter;
+ cl.emission = cl1.emission + cl2.emission;
+ cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */
+ return cl;
+}
+
+Closure closure_emission(vec3 rgb)
+{
+ Closure cl = CLOSURE_DEFAULT;
+ cl.emission = rgb;
+ return cl;
+}
+
+#else /* SURFACE */
+
+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;
+ cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac);
+ bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
+ /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz).*/
+ cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
+ cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
+
+# ifdef USE_SSS
+ cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac);
+ bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
+ /* It also does not make sense to mix SSS radius or irradiance. */
+ cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
+ cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
+# endif
+ return cl;
+}
+
+Closure closure_add(Closure cl1, Closure cl2)
+{
+ Closure cl;
+ cl.transmittance = cl1.transmittance + cl2.transmittance;
+ cl.radiance = cl1.radiance + cl2.radiance;
+ cl.holdout = cl1.holdout + cl2.holdout;
+ cl.flag = cl1.flag | cl2.flag;
+ cl.ssr_data = cl1.ssr_data + cl2.ssr_data;
+ bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
+ /* When mixing SSR don't blend roughness and normals.*/
+ cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
+ cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
+
+# ifdef USE_SSS
+ cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo;
+ bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
+ /* It also does not make sense to mix SSS radius or irradiance. */
+ cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
+ cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
+# endif
+ return cl;
+}
+
+Closure closure_emission(vec3 rgb)
+{
+ Closure cl = CLOSURE_DEFAULT;
+ cl.radiance = rgb;
+ return cl;
+}
+
+#endif
+
+#ifndef VOLUMETRICS
+
+void closure_load_ssr_data(
+ vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl)
+{
+ /* Still encode to avoid artifacts in the SSR pass. */
+ vec3 vN = normalize(mat3(ViewMatrix) * N);
+ cl.ssr_normal = normal_encode(vN, viewVec);
+
+ if (ssr_id == outputSsrId) {
+ cl.ssr_data = vec4(ssr_spec, roughness);
+ cl.flag |= CLOSURE_SSR_FLAG;
+ }
+}
+
+void closure_load_sss_data(
+ float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl)
+{
+# ifdef USE_SSS
+ if (sss_id == outputSssId) {
+ cl.sss_irradiance = sss_irradiance;
+ cl.sss_radius = radius;
+ cl.sss_albedo = sss_albedo;
+ cl.flag |= CLOSURE_SSS_FLAG;
+ cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0));
+ }
+ else
+# endif
+ {
+ cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo);
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl
index bc7879763c3..bf33caf9854 100644
--- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl
@@ -1,32 +1,8 @@
-#ifndef LIT_SURFACE_UNIFORM
-# define LIT_SURFACE_UNIFORM
-
-uniform float refractionDepth;
-
-# ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-# endif /* UTIL_TEX */
-
-in vec3 worldPosition;
-in vec3 viewPosition;
-
-in vec3 worldNormal;
-in vec3 viewNormal;
-
-# ifdef HAIR_SHADER
-in vec3 hairTangent; /* world space */
-in float hairThickTime;
-in float hairThickness;
-in float hairTime;
-flat in int hairStrandID;
-
-uniform int hairThicknessRes = 1;
-# endif
-
-#endif /* LIT_SURFACE_UNIFORM */
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
+#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
+#pragma BLENDER_REQUIRE(ssr_lib.glsl)
/**
* AUTO CONFIG
@@ -209,7 +185,7 @@ void CLOSURE_NAME(vec3 N
vec3 V = cameraVec;
- vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0);
+ vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
/* ---------------------------------------------------------------- */
/* -------------------- SCENE LIGHTS LIGHTING --------------------- */
@@ -328,11 +304,11 @@ void CLOSURE_NAME(vec3 N
# endif
# ifdef CLOSURE_GLOSSY
- vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared);
+ vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared);
# endif
# ifdef CLOSURE_CLEARCOAT
- vec3 C_spec_dir = get_specular_reflection_dominant_dir(C_N, V, C_roughnessSquared);
+ vec3 C_spec_dir = specular_dominant_dir(C_N, V, C_roughnessSquared);
# endif
# ifdef CLOSURE_REFRACTION
@@ -345,7 +321,7 @@ void CLOSURE_NAME(vec3 N
line_plane_intersect(
worldPosition, refr_V, worldPosition - N * refractionDepth, N) :
worldPosition;
- vec3 refr_dir = get_specular_refraction_dominant_dir(N, refr_V, roughness, final_ior);
+ vec3 refr_dir = refraction_dominant_dir(N, refr_V, roughness, final_ior);
# endif
# ifdef CLOSURE_REFRACTION
@@ -485,7 +461,7 @@ void CLOSURE_NAME(vec3 N
# endif
# ifdef CLOSURE_REFRACTION
- float btdf = get_btdf_lut(utilTex, NV, roughness, ior);
+ float btdf = get_btdf_lut(NV, roughness, ior);
out_refr += refr_accum.rgb * btdf;
# endif
diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
index 759b4098b37..a6c9eebaff2 100644
--- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
@@ -2,7 +2,6 @@
layout(std140) uniform common_block
{
mat4 pastViewProjectionMatrix;
- vec4 viewVecs[2];
vec2 mipRatio[10]; /* To correct mip level texel misalignment */
/* Ambient Occlusion */
vec4 aoParameters[2];
@@ -70,3 +69,9 @@ layout(std140) uniform common_block
#define ssrQuality ssrParameters.x
#define ssrThickness ssrParameters.y
#define ssrPixelSize ssrParameters.zw
+
+vec2 mip_ratio_interp(float mip)
+{
+ float low_mip = floor(mip);
+ return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
new file mode 100644
index 00000000000..7e959026987
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
@@ -0,0 +1,65 @@
+
+#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Utiltex
+ *
+ * Utiltex is a sampler2DArray that stores a number of useful small utilitary textures and lookup
+ * tables.
+ * \{ */
+
+uniform sampler2DArray utilTex;
+
+#define LUT_SIZE 64
+
+#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
+
+/* Return texture coordinates to sample Surface LUT */
+vec2 lut_coords(float cosTheta, float roughness)
+{
+ float theta = acos(cosTheta);
+ vec2 coords = vec2(roughness, theta / M_PI_2);
+
+ /* scale and bias coordinates, for correct filtered lookup */
+ return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
+}
+
+vec2 lut_coords_ltc(float cosTheta, float roughness)
+{
+ vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta));
+
+ /* scale and bias coordinates, for correct filtered lookup */
+ return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
+}
+
+float get_btdf_lut(float NV, float roughness, float ior)
+{
+ const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE;
+
+ vec3 coords;
+ /* Try to compensate for the low resolution and interpolation error. */
+ coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) +
+ (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) :
+ (0.9 + lut_scale_bias_texel_size.z) * ior * ior;
+ coords.y = 1.0 - saturate(NV);
+ coords.xy *= lut_scale_bias_texel_size.x;
+ coords.xy += lut_scale_bias_texel_size.y;
+
+ const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */
+ const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */
+
+ float mip = roughness * lut_lvl_scale;
+ float mip_floor = floor(mip);
+
+ coords.z = lut_lvl_ofs + mip_floor + 1.0;
+ float btdf_high = textureLod(utilTex, coords, 0.0).r;
+
+ coords.z -= 1.0;
+ float btdf_low = textureLod(utilTex, coords, 0.0).r;
+
+ float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z);
+
+ return btdf;
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/concentric_samples_lib.glsl b/source/blender/draw/engines/eevee/shaders/concentric_samples_lib.glsl
deleted file mode 100644
index 7e0e5945c54..00000000000
--- a/source/blender/draw/engines/eevee/shaders/concentric_samples_lib.glsl
+++ /dev/null
@@ -1,265 +0,0 @@
-/* Precomputed table of concentric samples.
- * Generated using this algorithm http://l2program.co.uk/900/concentric-disk-sampling
- * Sorted by radius then by rotation angle.
- * This way it's better for cache usage and for
- * easily restricting to a certain number of
- * sample while still having a circular kernel. */
-
-#define CONCENTRIC_SAMPLE_NUM 256
-const vec2 concentric[CONCENTRIC_SAMPLE_NUM] = vec2[CONCENTRIC_SAMPLE_NUM](
- vec2(0.0441941738242, 0.0441941738242),
- vec2(-0.0441941738242, -0.0441941738242),
- vec2(-0.0441941738242, 0.0441941738242),
- vec2(0.0441941738242, -0.0441941738242),
- vec2(0.181111092429, 0.0485285709567),
- vec2(0.132582521472, 0.132582521472),
- vec2(-0.181111092429, 0.0485285709567),
- vec2(0.0485285709567, 0.181111092429),
- vec2(-0.181111092429, -0.0485285709567),
- vec2(-0.0485285709567, 0.181111092429),
- vec2(-0.132582521472, -0.132582521472),
- vec2(-0.132582521472, 0.132582521472),
- vec2(-0.0485285709567, -0.181111092429),
- vec2(0.0485285709567, -0.181111092429),
- vec2(0.132582521472, -0.132582521472),
- vec2(0.181111092429, -0.0485285709567),
- vec2(0.308652606436, 0.0488857703251),
- vec2(0.278439538809, 0.141872031169),
- vec2(0.220970869121, 0.220970869121),
- vec2(-0.278439538809, 0.141872031169),
- vec2(0.141872031169, 0.278439538809),
- vec2(-0.308652606436, 0.0488857703251),
- vec2(0.0488857703251, 0.308652606436),
- vec2(-0.308652606436, -0.0488857703251),
- vec2(-0.0488857703251, 0.308652606436),
- vec2(-0.278439538809, -0.141872031169),
- vec2(-0.141872031169, 0.278439538809),
- vec2(-0.220970869121, -0.220970869121),
- vec2(-0.220970869121, 0.220970869121),
- vec2(-0.141872031169, -0.278439538809),
- vec2(-0.0488857703251, -0.308652606436),
- vec2(0.0488857703251, -0.308652606436),
- vec2(0.141872031169, -0.278439538809),
- vec2(0.220970869121, -0.220970869121),
- vec2(0.278439538809, -0.141872031169),
- vec2(0.308652606436, -0.0488857703251),
- vec2(0.434749091828, 0.0489844582952),
- vec2(0.41294895701, 0.144497089605),
- vec2(0.370441837162, 0.232764033475),
- vec2(0.309359216769, 0.309359216769),
- vec2(-0.370441837162, 0.232764033475),
- vec2(0.232764033475, 0.370441837162),
- vec2(-0.41294895701, 0.144497089605),
- vec2(0.144497089605, 0.41294895701),
- vec2(-0.434749091828, 0.0489844582952),
- vec2(0.0489844582952, 0.434749091828),
- vec2(-0.434749091828, -0.0489844582952),
- vec2(-0.0489844582952, 0.434749091828),
- vec2(-0.41294895701, -0.144497089605),
- vec2(-0.144497089605, 0.41294895701),
- vec2(-0.370441837162, -0.232764033475),
- vec2(-0.232764033475, 0.370441837162),
- vec2(-0.309359216769, -0.309359216769),
- vec2(-0.309359216769, 0.309359216769),
- vec2(-0.232764033475, -0.370441837162),
- vec2(-0.144497089605, -0.41294895701),
- vec2(-0.0489844582952, -0.434749091828),
- vec2(0.0489844582952, -0.434749091828),
- vec2(0.144497089605, -0.41294895701),
- vec2(0.232764033475, -0.370441837162),
- vec2(0.309359216769, -0.309359216769),
- vec2(0.370441837162, -0.232764033475),
- vec2(0.41294895701, -0.144497089605),
- vec2(0.434749091828, -0.0489844582952),
- vec2(0.560359517677, 0.0490251052956),
- vec2(0.543333277288, 0.14558571287),
- vec2(0.509798130208, 0.237722772229),
- vec2(0.460773024913, 0.322636745447),
- vec2(0.397747564417, 0.397747564417),
- vec2(-0.460773024913, 0.322636745447),
- vec2(0.322636745447, 0.460773024913),
- vec2(-0.509798130208, 0.237722772229),
- vec2(0.237722772229, 0.509798130208),
- vec2(-0.543333277288, 0.14558571287),
- vec2(0.14558571287, 0.543333277288),
- vec2(-0.560359517677, 0.0490251052956),
- vec2(0.0490251052956, 0.560359517677),
- vec2(-0.560359517677, -0.0490251052956),
- vec2(-0.0490251052956, 0.560359517677),
- vec2(-0.543333277288, -0.14558571287),
- vec2(-0.14558571287, 0.543333277288),
- vec2(-0.509798130208, -0.237722772229),
- vec2(-0.237722772229, 0.509798130208),
- vec2(-0.460773024913, -0.322636745447),
- vec2(-0.322636745447, 0.460773024913),
- vec2(-0.397747564417, -0.397747564417),
- vec2(-0.397747564417, 0.397747564417),
- vec2(-0.322636745447, -0.460773024913),
- vec2(-0.237722772229, -0.509798130208),
- vec2(-0.14558571287, -0.543333277288),
- vec2(-0.0490251052956, -0.560359517677),
- vec2(0.0490251052956, -0.560359517677),
- vec2(0.14558571287, -0.543333277288),
- vec2(0.237722772229, -0.509798130208),
- vec2(0.322636745447, -0.460773024913),
- vec2(0.397747564417, -0.397747564417),
- vec2(0.460773024913, -0.322636745447),
- vec2(0.509798130208, -0.237722772229),
- vec2(0.543333277288, -0.14558571287),
- vec2(0.560359517677, -0.0490251052956),
- vec2(0.685748328795, 0.0490456884495),
- vec2(0.671788470355, 0.146138636568),
- vec2(0.644152935937, 0.240256623474),
- vec2(0.603404305327, 0.32948367837),
- vec2(0.550372103135, 0.412003395727),
- vec2(0.486135912066, 0.486135912066),
- vec2(-0.550372103135, 0.412003395727),
- vec2(0.412003395727, 0.550372103135),
- vec2(-0.603404305327, 0.32948367837),
- vec2(0.32948367837, 0.603404305327),
- vec2(-0.644152935937, 0.240256623474),
- vec2(0.240256623474, 0.644152935937),
- vec2(-0.671788470355, 0.146138636568),
- vec2(0.146138636568, 0.671788470355),
- vec2(-0.685748328795, 0.0490456884495),
- vec2(0.0490456884495, 0.685748328795),
- vec2(-0.685748328795, -0.0490456884495),
- vec2(-0.0490456884495, 0.685748328795),
- vec2(-0.671788470355, -0.146138636568),
- vec2(-0.146138636568, 0.671788470355),
- vec2(-0.644152935937, -0.240256623474),
- vec2(-0.240256623474, 0.644152935937),
- vec2(-0.603404305327, -0.32948367837),
- vec2(-0.32948367837, 0.603404305327),
- vec2(-0.550372103135, -0.412003395727),
- vec2(-0.412003395727, 0.550372103135),
- vec2(-0.486135912066, -0.486135912066),
- vec2(-0.486135912066, 0.486135912066),
- vec2(-0.412003395727, -0.550372103135),
- vec2(-0.32948367837, -0.603404305327),
- vec2(-0.240256623474, -0.644152935937),
- vec2(-0.146138636568, -0.671788470355),
- vec2(-0.0490456884495, -0.685748328795),
- vec2(0.0490456884495, -0.685748328795),
- vec2(0.146138636568, -0.671788470355),
- vec2(0.240256623474, -0.644152935937),
- vec2(0.32948367837, -0.603404305327),
- vec2(0.412003395727, -0.550372103135),
- vec2(0.486135912066, -0.486135912066),
- vec2(0.550372103135, -0.412003395727),
- vec2(0.603404305327, -0.32948367837),
- vec2(0.644152935937, -0.240256623474),
- vec2(0.671788470355, -0.146138636568),
- vec2(0.685748328795, -0.0490456884495),
- vec2(0.811017637806, 0.0490575291556),
- vec2(0.799191174395, 0.146457218224),
- vec2(0.775710704038, 0.241721231257),
- vec2(0.740918624869, 0.33346040443),
- vec2(0.695322283745, 0.420336974019),
- vec2(0.639586577995, 0.501084084011),
- vec2(0.574524259714, 0.574524259714),
- vec2(-0.639586577995, 0.501084084011),
- vec2(0.501084084011, 0.639586577995),
- vec2(-0.695322283745, 0.420336974019),
- vec2(0.420336974019, 0.695322283745),
- vec2(-0.740918624869, 0.33346040443),
- vec2(0.33346040443, 0.740918624869),
- vec2(-0.775710704038, 0.241721231257),
- vec2(0.241721231257, 0.775710704038),
- vec2(-0.799191174395, 0.146457218224),
- vec2(0.146457218224, 0.799191174395),
- vec2(-0.811017637806, 0.0490575291556),
- vec2(0.0490575291556, 0.811017637806),
- vec2(-0.811017637806, -0.0490575291556),
- vec2(-0.0490575291556, 0.811017637806),
- vec2(-0.799191174395, -0.146457218224),
- vec2(-0.146457218224, 0.799191174395),
- vec2(-0.775710704038, -0.241721231257),
- vec2(-0.241721231257, 0.775710704038),
- vec2(-0.740918624869, -0.33346040443),
- vec2(-0.33346040443, 0.740918624869),
- vec2(-0.695322283745, -0.420336974019),
- vec2(-0.420336974019, 0.695322283745),
- vec2(-0.639586577995, -0.501084084011),
- vec2(-0.501084084011, 0.639586577995),
- vec2(-0.574524259714, -0.574524259714),
- vec2(-0.574524259714, 0.574524259714),
- vec2(-0.501084084011, -0.639586577995),
- vec2(-0.420336974019, -0.695322283745),
- vec2(-0.33346040443, -0.740918624869),
- vec2(-0.241721231257, -0.775710704038),
- vec2(-0.146457218224, -0.799191174395),
- vec2(-0.0490575291556, -0.811017637806),
- vec2(0.0490575291556, -0.811017637806),
- vec2(0.146457218224, -0.799191174395),
- vec2(0.241721231257, -0.775710704038),
- vec2(0.33346040443, -0.740918624869),
- vec2(0.420336974019, -0.695322283745),
- vec2(0.501084084011, -0.639586577995),
- vec2(0.574524259714, -0.574524259714),
- vec2(0.639586577995, -0.501084084011),
- vec2(0.695322283745, -0.420336974019),
- vec2(0.740918624869, -0.33346040443),
- vec2(0.775710704038, -0.241721231257),
- vec2(0.799191174395, -0.146457218224),
- vec2(0.811017637806, -0.0490575291556),
- vec2(0.936215188832, 0.0490649589778),
- vec2(0.925957819308, 0.146657310975),
- vec2(0.905555462146, 0.242642854784),
- vec2(0.875231649841, 0.335969952699),
- vec2(0.835318616427, 0.425616093506),
- vec2(0.786253657449, 0.510599095327),
- vec2(0.728574338866, 0.589987866609),
- vec2(0.662912607362, 0.662912607362),
- vec2(-0.728574338866, 0.589987866609),
- vec2(0.589987866609, 0.728574338866),
- vec2(-0.786253657449, 0.510599095327),
- vec2(0.510599095327, 0.786253657449),
- vec2(-0.835318616427, 0.425616093506),
- vec2(0.425616093506, 0.835318616427),
- vec2(-0.875231649841, 0.335969952699),
- vec2(0.335969952699, 0.875231649841),
- vec2(-0.905555462146, 0.242642854784),
- vec2(0.242642854784, 0.905555462146),
- vec2(-0.925957819308, 0.146657310975),
- vec2(0.146657310975, 0.925957819308),
- vec2(-0.936215188832, 0.0490649589778),
- vec2(0.0490649589778, 0.936215188832),
- vec2(-0.936215188832, -0.0490649589778),
- vec2(-0.0490649589778, 0.936215188832),
- vec2(-0.925957819308, -0.146657310975),
- vec2(-0.146657310975, 0.925957819308),
- vec2(-0.905555462146, -0.242642854784),
- vec2(-0.242642854784, 0.905555462146),
- vec2(-0.875231649841, -0.335969952699),
- vec2(-0.335969952699, 0.875231649841),
- vec2(-0.835318616427, -0.425616093506),
- vec2(-0.425616093506, 0.835318616427),
- vec2(-0.786253657449, -0.510599095327),
- vec2(-0.510599095327, 0.786253657449),
- vec2(-0.728574338866, -0.589987866609),
- vec2(-0.589987866609, 0.728574338866),
- vec2(-0.662912607362, -0.662912607362),
- vec2(-0.662912607362, 0.662912607362),
- vec2(-0.589987866609, -0.728574338866),
- vec2(-0.510599095327, -0.786253657449),
- vec2(-0.425616093506, -0.835318616427),
- vec2(-0.335969952699, -0.875231649841),
- vec2(-0.242642854784, -0.905555462146),
- vec2(-0.146657310975, -0.925957819308),
- vec2(-0.0490649589778, -0.936215188832),
- vec2(0.0490649589778, -0.936215188832),
- vec2(0.146657310975, -0.925957819308),
- vec2(0.242642854784, -0.905555462146),
- vec2(0.335969952699, -0.875231649841),
- vec2(0.425616093506, -0.835318616427),
- vec2(0.510599095327, -0.786253657449),
- vec2(0.589987866609, -0.728574338866),
- vec2(0.662912607362, -0.662912607362),
- vec2(0.728574338866, -0.589987866609),
- vec2(0.786253657449, -0.510599095327),
- vec2(0.835318616427, -0.425616093506),
- vec2(0.875231649841, -0.335969952699),
- vec2(0.905555462146, -0.242642854784),
- vec2(0.925957819308, -0.146657310975),
- vec2(0.936215188832, -0.0490649589778));
diff --git a/source/blender/draw/engines/eevee/shaders/default_frag.glsl b/source/blender/draw/engines/eevee/shaders/default_frag.glsl
deleted file mode 100644
index 1014b25033a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/default_frag.glsl
+++ /dev/null
@@ -1,51 +0,0 @@
-
-uniform vec3 basecol;
-uniform float metallic;
-uniform float specular;
-uniform float roughness;
-
-Closure nodetree_exec(void)
-{
-#ifdef HAIR_SHADER
- vec3 B = normalize(cross(worldNormal, hairTangent));
- float cos_theta;
- if (hairThicknessRes == 1) {
- vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0);
- /* Random cosine normal distribution on the hair surface. */
- cos_theta = rand.x * 2.0 - 1.0;
- }
- else {
- /* Shade as a cylinder. */
- cos_theta = hairThickTime / hairThickness;
- }
- float sin_theta = sqrt(max(0.0, 1.0f - cos_theta * cos_theta));
- vec3 N = normalize(worldNormal * sin_theta + B * cos_theta);
- vec3 vN = mat3(ViewMatrix) * N;
-#else
- vec3 N = normalize(gl_FrontFacing ? worldNormal : -worldNormal);
- vec3 vN = normalize(gl_FrontFacing ? viewNormal : -viewNormal);
-#endif
-
- vec3 dielectric = vec3(0.034) * specular * 2.0;
- vec3 albedo = mix(basecol, vec3(0.0), metallic);
- vec3 f0 = mix(dielectric, basecol, metallic);
- vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
- vec3 out_diff, out_spec, ssr_spec;
- eevee_closure_default(N, albedo, f0, f90, 1, roughness, 1.0, true, out_diff, out_spec, ssr_spec);
-
- Closure cl = CLOSURE_DEFAULT;
- cl.radiance = render_pass_glossy_mask(vec3(1.0), out_spec) +
- render_pass_diffuse_mask(albedo, out_diff * albedo);
- closure_load_ssr_data(ssr_spec, roughness, N, viewCameraVec, 1, cl);
-
-#ifdef LOOKDEV
- gl_FragDepth = 0.0;
-#endif
-
-#ifdef HOLDOUT
- cl = CLOSURE_DEFAULT;
- cl.holdout = 1.0;
-#endif
-
- return cl;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl
index d56890769a7..9c1ca17f87c 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl
@@ -1,4 +1,8 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
uniform sampler2D colorBuffer;
uniform sampler2D depthBuffer;
@@ -18,9 +22,6 @@ uniform vec4 bokehParams[2];
uniform vec2 nearFar; /* Near & far view depths values */
-#define M_PI 3.1415926535897932384626433832795
-#define M_2PI 6.2831853071795864769252868
-
/* -------------- Utils ------------- */
/* divide by sensor size to get the normalized size */
@@ -34,11 +35,6 @@ uniform vec2 nearFar; /* Near & far view depths values */
#define weighted_sum(a, b, c, d, e) \
(a * e.x + b * e.y + c * e.z + d * e.w) / max(1e-6, dot(e, vec4(1.0)));
-float max_v4(vec4 v)
-{
- return max(max(v.x, v.y), max(v.z, v.w));
-}
-
vec4 safe_color(vec4 c)
{
/* Clamp to avoid black square artifacts if a pixel goes NaN. */
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl
index d83b410125a..6e35d4a54ae 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
uniform vec4 bokehParams[2];
#define bokeh_rotation bokehParams[0].x
@@ -15,8 +17,6 @@ flat out float smoothFac;
flat out ivec2 edge;
out vec2 particlecoord;
-#define M_PI 3.1415926535897932384626433832795
-
/* Scatter pass, calculate a triangle covering the CoC. */
void main()
{
diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
index eea0ce0aae2..47fe21928b3 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
@@ -1,14 +1,34 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
+
/**
* This shader only compute maximum horizon angles for each directions.
* The final integration is done at the resolve stage with the shading normal.
*/
-uniform float rotationOffset;
-
out vec4 FragColor;
-#ifdef DEBUG_AO
uniform sampler2D normalBuffer;
+#ifdef LAYERED_DEPTH
+uniform sampler2DArray depthBufferLayered;
+uniform int layer;
+# define gtao_depthBuffer depthBufferLayered
+# define gtao_textureLod(a, b, c) textureLod(a, vec3(b, layer), c)
+
+#else
+uniform sampler2D depthBuffer;
+# define gtao_depthBuffer depthBuffer
+# define gtao_textureLod(a, b, c) textureLod(a, b, c)
+
+#endif
+
+uniform float rotationOffset;
+
+#ifdef DEBUG_AO
void main()
{
@@ -34,18 +54,6 @@ void main()
#else
-# ifdef LAYERED_DEPTH
-uniform sampler2DArray depthBufferLayered;
-uniform int layer;
-# define gtao_depthBuffer depthBufferLayered
-# define gtao_textureLod(a, b, c) textureLod(a, vec3(b, layer), c)
-
-# else
-# define gtao_depthBuffer depthBuffer
-# define gtao_textureLod(a, b, c) textureLod(a, b, c)
-
-# endif
-
void main()
{
vec2 uvs = saturate(gl_FragCoord.xy / vec2(textureSize(gtao_depthBuffer, 0).xy));
diff --git a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl
index edee55a07e0..7331f92ba6d 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl
@@ -1,5 +1,10 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
/* Convert depth to Mist factor */
uniform vec3 mistSettings;
+uniform sampler2D depthBuffer;
#define mistStart mistSettings.x
#define mistInvDistance mistSettings.y
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..3501a4448c5 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,232 @@
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+
+/*
+ * 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;
+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)
+{
+ 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;
+}
+
+vec4 sample_velocity(vec2 uv)
{
- vec3 ndc_pos;
- ndc_pos.xy = uvcoordsvar.xy;
- ndc_pos.z = texture(depthBuffer, uvcoordsvar.xy).x;
+ vec4 data = texture(velocityBuffer, uv);
+ return decode_velocity(data);
+}
- float inv_samples = 1.0 / float(samples);
- float noise = 2.0 * wang_hash_noise(0u) * inv_samples;
+vec2 sample_velocity(vec2 uv, const bool next)
+{
+ vec4 data = sample_velocity(uv);
+ data.xy = (next ? data.zw : data.xy);
+ return data.xy;
+}
- /* Normalize Device Coordinates are [-1, +1]. */
- ndc_pos = ndc_pos * 2.0 - 1.0;
+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;
+}
- vec4 p = currInvViewProjMatrix * vec4(ndc_pos, 1.0);
- vec3 world_pos = p.xyz / p.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;
+ }
- /* 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 */
+ if (max_motion_len < 0.5) {
+ return;
+ }
- vec2 motion = (ndc_pos.xy - old_ndc.xy) * 0.25; /* 0.25 fit cycles ref */
+ 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);
+ }
- float inc = 2.0 * inv_samples;
- float i = -1.0 + noise;
+ if (center_motion_len < 0.5) {
+ return;
+ }
- 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;
+ 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_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
index 598cc3e5183..f8dccb7511a 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
@@ -1,4 +1,11 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
+#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
+#pragma BLENDER_REQUIRE(ssr_lib.glsl)
+
/* Based on Stochastic Screen Space Reflections
* https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */
@@ -131,7 +138,7 @@ void main()
return;
}
- vec4 rand = texelFetch(utilTex, ivec3(halfres_texel % LUT_SIZE, 2), 0);
+ vec4 rand = texelfetch_noise_tex(halfres_texel);
/* Gives *perfect* reflection for very small roughness */
if (roughness < 0.04) {
diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
index e9da49c9eb9..2a53a4f119f 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
@@ -1,4 +1,9 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
+
/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
#define MAX_SSS_SAMPLES 65
@@ -14,36 +19,16 @@ uniform sampler2D sssIrradiance;
uniform sampler2D sssRadius;
uniform sampler2D sssAlbedo;
-#ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-#endif /* UTIL_TEX */
-
layout(location = 0) out vec4 sssRadiance;
-float get_view_z_from_depth(float depth)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- float d = 2.0 * depth - 1.0;
- return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]);
- }
- else {
- return viewVecs[0].z + depth * viewVecs[1].z;
- }
-}
-
-#define LUT_SIZE 64
-#define M_PI_2 1.5707963267948966 /* pi/2 */
-#define M_2PI 6.2831853071795865 /* 2*pi */
-
void main(void)
{
vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO precompute */
vec2 uvs = gl_FragCoord.xy * pixel_size;
vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb;
float sss_radius = texture(sssRadius, uvs).r;
- float depth_view = get_view_z_from_depth(texture(depthBuffer, uvs).r);
+ float depth = texture(depthBuffer, uvs).r;
+ float depth_view = get_view_z_from_depth(depth);
float rand = texelfetch_noise_tex(gl_FragCoord.xy).r;
#ifdef FIRST_PASS
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..28947e971d2 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,12 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+uniform sampler2D colorBuffer;
+uniform sampler2D depthBuffer;
uniform sampler2D colorHistoryBuffer;
-uniform sampler2D velocityBuffer;
+
+uniform mat4 prevViewProjectionMatrix;
out vec4 FragColor;
@@ -38,16 +44,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_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
index 6531ceb8dbe..c85eff92e37 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
@@ -1,11 +1,16 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(lights_lib.glsl)
+
in vec4 uvcoordsvar;
out vec4 FragColor;
+uniform sampler2D depthBuffer;
uniform sampler1D sssTexProfile;
uniform sampler2D sssRadius;
-
uniform sampler2DArray sssShadowCubes;
uniform sampler2DArray sssShadowCascades;
@@ -27,12 +32,6 @@ vec3 sss_profile(float s)
return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb;
}
-#ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-#endif /* UTIL_TEX */
-
float light_translucent_power_with_falloff(LightData ld, vec3 N, vec4 l_vector)
{
float power, falloff;
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..145939cefb2 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,13 @@
-uniform mat4 currPersinv;
-uniform mat4 pastPersmat;
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-out vec2 outData;
+uniform sampler2D depthBuffer;
+
+uniform mat4 prevViewProjMatrix;
+uniform mat4 currViewProjMatrixInv;
+uniform mat4 nextViewProjMatrix;
+
+out vec4 outData;
void main()
{
@@ -12,13 +17,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;
-
- outData = uv - uv_history;
+ 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;
- /* 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/irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl
index b1d4de1c682..2274bf8b950 100644
--- a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl
@@ -1,44 +1,76 @@
-uniform sampler2DArray irradianceGrid;
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
+#pragma BLENDER_REQUIRE(octahedron_lib.glsl)
#define IRRADIANCE_LIB
-#ifdef IRRADIANCE_CUBEMAP
-struct IrradianceData {
- vec3 color;
-};
-#elif defined(IRRADIANCE_SH_L2)
+/* ---------------------------------------------------------------------- */
+/** \name Structure
+ * \{ */
+
+#if defined(IRRADIANCE_SH_L2)
struct IrradianceData {
vec3 shcoefs[9];
};
+
#else /* defined(IRRADIANCE_HL2) */
struct IrradianceData {
vec3 cubesides[3];
};
+
#endif
-IrradianceData load_irradiance_cell(int cell, vec3 N)
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Resources
+ * \{ */
+
+uniform sampler2DArray irradianceGrid;
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+vec4 irradiance_encode(vec3 rgb)
{
- /* Keep in sync with diffuse_filter_probe() */
+ float maxRGB = max_v3(rgb);
+ float fexp = ceil(log2(maxRGB));
+ return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0);
+}
-#if defined(IRRADIANCE_CUBEMAP)
+vec3 irradiance_decode(vec4 data)
+{
+ float fexp = data.a * 255.0 - 128.0;
+ return data.rgb * exp2(fexp);
+}
-# define AMBIANT_CUBESIZE 8
- ivec2 cell_co = ivec2(AMBIANT_CUBESIZE);
- int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x;
- cell_co.x *= cell % cell_per_row;
- cell_co.y *= cell / cell_per_row;
+vec4 visibility_encode(vec2 accum, float range)
+{
+ accum /= range;
- vec2 texelSize = 1.0 / vec2(AMBIANT_CUBESIZE);
+ vec4 data;
+ data.x = fract(accum.x);
+ data.y = floor(accum.x) / 255.0;
+ data.z = fract(accum.y);
+ data.w = floor(accum.y) / 255.0;
- vec2 uvs = mapping_octahedron(N, texelSize);
- uvs *= vec2(AMBIANT_CUBESIZE) / vec2(textureSize(irradianceGrid, 0));
- uvs += vec2(cell_co) / vec2(textureSize(irradianceGrid, 0));
+ return data;
+}
- IrradianceData ir;
- ir.color = texture(irradianceGrid, vec3(uvs, 0.0)).rgb;
+vec2 visibility_decode(vec4 data, float range)
+{
+ return (data.xz + data.yw * 255.0) * range;
+}
-#elif defined(IRRADIANCE_SH_L2)
+IrradianceData load_irradiance_cell(int cell, vec3 N)
+{
+ /* Keep in sync with diffuse_filter_probe() */
+
+#if defined(IRRADIANCE_SH_L2)
ivec2 cell_co = ivec2(3, 3);
int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x;
@@ -164,9 +196,7 @@ vec3 hl2_basis(vec3 N, vec3 cubesides[3])
vec3 compute_irradiance(vec3 N, IrradianceData ird)
{
-#if defined(IRRADIANCE_CUBEMAP)
- return ird.color;
-#elif defined(IRRADIANCE_SH_L2)
+#if defined(IRRADIANCE_SH_L2)
return spherical_harmonics_L2(N, ird.shcoefs);
#else /* defined(IRRADIANCE_HL2) */
return hl2_basis(N, ird.cubesides);
@@ -178,3 +208,5 @@ vec3 irradiance_from_cell_get(int cell, vec3 ir_dir)
IrradianceData ir_data = load_irradiance_cell(cell, ir_dir);
return compute_irradiance(ir_dir, ir_data);
}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl
index 96fe94fc41e..a12069dc57b 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl
@@ -1,4 +1,7 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(cubemap_lib.glsl)
+
flat in int pid;
in vec2 quadCoord;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl
index db780714091..d06ad553ca4 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
/* XXX TODO fix code duplication */
struct CubeData {
vec4 position_type;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
index 296c1581545..bf45169ebaa 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
@@ -1,4 +1,8 @@
+#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
+
uniform samplerCube probeHdr;
uniform int probeSize;
uniform float lodFactor;
@@ -111,32 +115,7 @@ void main()
FragColor = vec4(sh, 1.0);
#else
-# if defined(IRRADIANCE_CUBEMAP)
- /* Downside: Need lots of memory for storage, distortion due to octahedral mapping */
- const vec2 map_size = vec2(16.0);
- const vec2 texelSize = 1.0 / map_size;
- vec2 uvs = mod(gl_FragCoord.xy, map_size) * texelSize;
- const float paddingSize = 1.0;
-
- /* Add a N pixel border to ensure filtering is correct
- * for N mipmap levels. */
- uvs = (uvs - texelSize * paddingSize) / (1.0 - 2.0 * texelSize * paddingSize);
-
- /* edge mirroring : only mirror if directly adjacent
- * (not diagonally adjacent) */
- vec2 m = abs(uvs - 0.5) + 0.5;
- vec2 f = floor(m);
- if (f.x - f.y != 0.0) {
- uvs = 1.0 - uvs;
- }
-
- /* clamp to [0-1] */
- uvs = fract(uvs);
-
- /* get cubemap vector */
- vec3 cubevec = octahedral_to_cubemap_proj(uvs);
-
-# elif defined(IRRADIANCE_HL2)
+# if defined(IRRADIANCE_HL2)
/* Downside: very very low resolution (6 texels), bleed lighting because of interpolation */
int x = int(gl_FragCoord.x) % 3;
int y = int(gl_FragCoord.y) % 2;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
index 00eb3c7e200..ccb77427ed2 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
@@ -1,4 +1,7 @@
+#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+
uniform samplerCube probeHdr;
uniform float roughnessSquared;
uniform float texelSize;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
index 5d8af21032a..8d7c58a93d5 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
@@ -1,4 +1,8 @@
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
+
uniform samplerCube probeDepth;
uniform int outputSize;
uniform float lodFactor;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl
index f8bc1703c66..009ccf6535e 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl
@@ -40,9 +40,6 @@ void main()
for (int v = 0; v < 3; v++) {
gl_Position = vPos[v];
worldPosition = x_axis[fFace] * vPos[v].x + y_axis[fFace] * vPos[v].y + maj_axes[fFace];
-#ifdef USE_ATTR
- pass_attr(v);
-#endif
EmitVertex();
}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl
index 4e500db827e..6fefe1319bd 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
uniform float sphere_size;
uniform int offset;
uniform ivec3 grid_resolution;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
index 7cab5146b09..71e4d6e2c4a 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
@@ -6,8 +6,6 @@ void main()
{
#if defined(IRRADIANCE_SH_L2)
const ivec2 data_size = ivec2(3, 3);
-#elif defined(IRRADIANCE_CUBEMAP)
- const ivec2 data_size = ivec2(8, 8);
#elif defined(IRRADIANCE_HL2)
const ivec2 data_size = ivec2(3, 2);
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl
index 6c6db88139b..a2e25b83532 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl
@@ -1,3 +1,12 @@
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
+#pragma BLENDER_REQUIRE(cubemap_lib.glsl)
+#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
+#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
+
/* ----------- Uniforms --------- */
uniform sampler2DArray probePlanars;
@@ -73,12 +82,6 @@ struct GridData {
# define MAX_PLANAR 1
#endif
-#ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-#endif /* UTIL_TEX */
-
layout(std140) uniform probe_block
{
CubeData probes_data[MAX_PROBE];
@@ -218,7 +221,7 @@ void fallback_cubemap(vec3 N,
inout vec4 spec_accum)
{
/* Specular probes */
- vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared);
+ vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared);
#ifdef SSR_AO
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
@@ -246,7 +249,6 @@ void fallback_cubemap(vec3 N,
}
}
-#ifdef IRRADIANCE_LIB
vec3 probe_evaluate_grid(GridData gd, vec3 W, vec3 N, vec3 localpos)
{
localpos = localpos * 0.5 + 0.5;
@@ -308,5 +310,3 @@ vec3 probe_evaluate_world_diff(vec3 N)
{
return irradiance_from_cell_get(0, N);
}
-
-#endif /* IRRADIANCE_LIB */
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl
index 2807488db6c..0a53abcb04a 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
uniform sampler2DArray probePlanars;
in vec3 worldPosition;
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl
index 65506e5c7b1..6759c060259 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
in vec3 pos;
in int probe_id;
diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl
index 3b9d0a8f2bc..949e4d8f04f 100644
--- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl
@@ -1,8 +1,76 @@
-uniform sampler2DArrayShadow shadowCubeTexture;
-uniform sampler2DArrayShadow shadowCascadeTexture;
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
+#pragma BLENDER_REQUIRE(ltc_lib.glsl)
+
+#ifndef MAX_CASCADE_NUM
+# define MAX_CASCADE_NUM 4
+#endif
+
+/* ---------------------------------------------------------------------- */
+/** \name Structure
+ * \{ */
+
+struct LightData {
+ vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */
+ vec4 color_spec; /* w : Spec Intensity */
+ vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
+ vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
+ vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
+ vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */
+};
+
+/* convenience aliases */
+#define l_color color_spec.rgb
+#define l_spec color_spec.a
+#define l_position position_influence.xyz
+#define l_influence position_influence.w
+#define l_sizex rightvec_sizex.w
+#define l_sizey upvec_sizey.w
+#define l_right rightvec_sizex.xyz
+#define l_up upvec_sizey.xyz
+#define l_forward forwardvec_type.xyz
+#define l_type forwardvec_type.w
+#define l_spot_size spotdata_radius_shadow.x
+#define l_spot_blend spotdata_radius_shadow.y
+#define l_radius spotdata_radius_shadow.z
+#define l_shadowid spotdata_radius_shadow.w
+
+struct ShadowData {
+ vec4 near_far_bias_id;
+ vec4 contact_shadow_data;
+};
+
+struct ShadowCubeData {
+ mat4 shadowmat;
+ vec4 position;
+};
+
+struct ShadowCascadeData {
+ mat4 shadowmat[MAX_CASCADE_NUM];
+ vec4 split_start_distances;
+ vec4 split_end_distances;
+ vec4 shadow_vec_id;
+};
+
+/* convenience aliases */
+#define sh_near near_far_bias_id.x
+#define sh_far near_far_bias_id.y
+#define sh_bias near_far_bias_id.z
+#define sh_data_index near_far_bias_id.w
+#define sh_contact_dist contact_shadow_data.x
+#define sh_contact_offset contact_shadow_data.y
+#define sh_contact_spread contact_shadow_data.z
+#define sh_contact_thickness contact_shadow_data.w
+#define sh_shadow_vec shadow_vec_id.xyz
+#define sh_tex_index shadow_vec_id.w
+
+/** \} */
-#define LAMPS_LIB
+/* ---------------------------------------------------------------------- */
+/** \name Resources
+ * \{ */
layout(std140) uniform shadow_block
{
@@ -16,6 +84,15 @@ layout(std140) uniform light_block
LightData lights_data[MAX_LIGHT];
};
+uniform sampler2DArrayShadow shadowCubeTexture;
+uniform sampler2DArrayShadow shadowCascadeTexture;
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Shadow Functions
+ * \{ */
+
/* type */
#define POINT 0.0
#define SUN 1.0
@@ -133,9 +210,11 @@ float sample_cascade_shadow(int shadow_id, vec3 W)
#undef scube
#undef scsmd
-/* ----------------------------------------------------------- */
-/* --------------------- Light Functions --------------------- */
-/* ----------------------------------------------------------- */
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Light Functions
+ * \{ */
/* From Frostbite PBR Course
* Distance based attenuation
@@ -258,7 +337,6 @@ float light_visibility(LightData ld,
l_atten);
}
-#ifdef USE_LTC
float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
{
if (ld.l_type == AREA_RECT) {
@@ -321,4 +399,5 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector)
return ltc_evaluate_disk(N, V, ltc_matrix(ltc_mat), points);
}
}
-#endif
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/default_world_frag.glsl b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl
index 8c876cf582c..9077b414026 100644
--- a/source/blender/draw/engines/eevee/shaders/default_world_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl
@@ -1,20 +1,17 @@
-uniform float backgroundAlpha;
-uniform vec3 color;
-
-out vec4 FragColor;
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
-#if defined(LOOKDEV_BG) || defined(LOOKDEV)
+uniform sampler2D studioLight;
+uniform float backgroundAlpha;
uniform mat3 StudioLightMatrix;
-uniform sampler2D image;
uniform float studioLightIntensity = 1.0;
uniform float studioLightBlur = 0.0;
-in vec3 viewPosition;
-# ifndef M_PI
-# define M_PI 3.14159265358979323846
-# endif
+out vec4 FragColor;
vec3 background_transform_to_world(vec3 viewvec)
{
@@ -35,36 +32,20 @@ vec4 node_tex_environment_equirectangular(vec3 co, sampler2D ima)
vec3 nco = normalize(co);
float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5;
float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5;
-
- /* Fix pole bleeding */
- float width = float(textureSize(ima, 0).x);
- float texel_width = 1.0 / width;
- v = clamp(v, texel_width, 1.0 - texel_width);
-
- /* Fix u = 0 seam */
- /* This is caused by texture filtering, since uv don't have smooth derivatives
- * at u = 0 or 2PI, hardware filtering is using the smallest mipmap for certain
- * texels. So we force the highest mipmap and don't do anisotropic filtering. */
return textureLod(ima, vec2(u, v), 0.0);
}
-#endif
void main()
{
- vec3 background_color;
+ vec3 worldvec = background_transform_to_world(viewPosition);
+ vec3 background_color;
#if defined(LOOKDEV_BG)
- vec3 worldvec = background_transform_to_world(viewPosition);
background_color = probe_evaluate_world_spec(worldvec, studioLightBlur).rgb;
- background_color *= studioLightIntensity;
-
-#elif defined(LOOKDEV)
- vec3 worldvec = background_transform_to_world(viewPosition);
- background_color = node_tex_environment_equirectangular(StudioLightMatrix * worldvec, image).rgb;
- background_color *= studioLightIntensity;
-
#else
- background_color = color;
+ worldvec = StudioLightMatrix * worldvec;
+ background_color = node_tex_environment_equirectangular(worldvec, studioLight).rgb;
+ background_color *= studioLightIntensity;
#endif
FragColor = vec4(clamp(background_color, vec3(0.0), vec3(1e10)), 1.0) * backgroundAlpha;
diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl
index dbfc7ad5a71..2750d42a74a 100644
--- a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl
@@ -8,12 +8,6 @@
#define USE_LTC
-#ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-#endif /* UTIL_TEX */
-
/* Diffuse *clipped* sphere integral. */
float diffuse_sphere_integral(float avg_dir_z, float form_factor)
{
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..ef96bcbaedb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
@@ -0,0 +1,59 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+
+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()
+{
+ GPU_INTEL_VERTEX_SHADER_WORKAROUND
+
+#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/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
index 9acd8f998f6..34999076f9c 100644
--- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
@@ -1,4 +1,14 @@
+/* Required by some nodes. */
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
+
#ifdef USE_ALPHA_HASH
/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */
@@ -56,8 +66,6 @@ float hashed_alpha_threshold(vec3 co)
#endif
-#define NODETREE_EXEC
-
void main()
{
#if defined(USE_ALPHA_HASH)
@@ -66,11 +74,9 @@ void main()
float opacity = saturate(1.0 - avg(cl.transmittance));
-# if defined(USE_ALPHA_HASH)
/* Hashed Alpha Testing */
if (opacity < hashed_alpha_threshold(worldPosition)) {
discard;
}
-# endif
#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
index 2c7e0aca3fb..f650e2eeb8c 100644
--- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
@@ -1,16 +1,14 @@
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
#ifndef HAIR_SHADER
in vec3 pos;
#endif
void main()
{
-#ifdef GPU_INTEL
- /* Due to some shader compiler bug, we somewhat
- * need to access gl_VertexID to make it work. even
- * if it's actually dead code. */
- gl_Position.x = float(gl_VertexID);
-#endif
+ GPU_INTEL_VERTEX_SHADER_WORKAROUND
#ifdef HAIR_SHADER
float time, thick_time, thickness;
@@ -34,5 +32,4 @@ void main()
#ifdef CLIP_PLANES
gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]);
#endif
- /* TODO motion vectors */
}
diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
index f88cfdf3787..39db39f8756 100644
--- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
@@ -1,3 +1,11 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
+
+uniform sampler2D maxzBuffer;
+uniform sampler2DArray planarDepth;
+
#define MAX_STEP 256
float sample_depth(vec2 uv, int index, float lod)
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
new file mode 100644
index 00000000000..36cf3cecf40
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
@@ -0,0 +1,43 @@
+
+/* ---------------------------------------------------------------------- */
+/** \name Resources
+ * \{ */
+
+layout(std140) uniform renderpass_block
+{
+ bool renderPassDiffuse;
+ bool renderPassDiffuseLight;
+ bool renderPassGlossy;
+ bool renderPassGlossyLight;
+ bool renderPassEmit;
+ bool renderPassSSSColor;
+ bool renderPassEnvironment;
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light)
+{
+ return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0);
+}
+
+vec3 render_pass_sss_mask(vec3 sss_color)
+{
+ return renderPassSSSColor ? sss_color : vec3(0.0);
+}
+
+vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light)
+{
+ return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0);
+}
+
+vec3 render_pass_emission_mask(vec3 emission_light)
+{
+ return renderPassEmit ? emission_light : vec3(0.0);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
index 361963d5ac3..89a411bc7cb 100644
--- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
@@ -1,3 +1,7 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+
#define PASS_POST_UNDEFINED 0
#define PASS_POST_ACCUMULATED_COLOR 1
#define PASS_POST_ACCUMULATED_LIGHT 2
@@ -9,6 +13,8 @@
uniform int postProcessType;
uniform int currentSample;
+
+uniform sampler2D depthBuffer;
uniform sampler2D inputBuffer;
uniform sampler2D inputSecondLightBuffer;
uniform sampler2D inputColorBuffer;
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl
index 860ec9e1727..e0b9d4a60db 100644
--- a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl
@@ -1,11 +1,11 @@
-out vec4 fragColor;
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(lights_lib.glsl)
+
+uniform sampler2D depthBuffer;
-#ifndef UTIL_TEX
-# define UTIL_TEX
-uniform sampler2DArray utilTex;
-# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-#endif /* UTIL_TEX */
+out vec4 fragColor;
void main()
{
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
index c42f905cf7e..0e342938396 100644
--- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
@@ -1,39 +1,23 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
+
in vec3 pos;
in vec3 nor;
-#ifdef MESH_SHADER
-out vec3 worldPosition;
-out vec3 viewPosition;
-out vec3 worldNormal;
-out vec3 viewNormal;
-#endif
-
-#ifdef HAIR_SHADER
-out vec3 hairTangent;
-out float hairThickTime;
-out float hairThickness;
-out float hairTime;
-flat out int hairStrandID;
-#endif
-
void main()
{
-#ifdef GPU_INTEL
- /* Due to some shader compiler bug, we somewhat
- * need to access gl_VertexID to make it work. even
- * if it's actually dead code. */
- gl_Position.x = float(gl_VertexID);
-#endif
+ GPU_INTEL_VERTEX_SHADER_WORKAROUND
#ifdef HAIR_SHADER
hairStrandID = hair_get_strand_id();
- vec3 world_pos, binor;
+ vec3 pos, binor;
hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
ModelMatrixInverse,
ViewMatrixInverse[3].xyz,
ViewMatrixInverse[2].xyz,
- world_pos,
+ pos,
hairTangent,
binor,
hairTime,
@@ -41,6 +25,7 @@ void main()
hairThickTime);
worldNormal = cross(hairTangent, binor);
+ vec3 world_pos = pos;
#else
vec3 world_pos = point_object_to_world(pos);
#endif
@@ -57,7 +42,10 @@ void main()
/* No need to normalize since this is just a rotation. */
viewNormal = normal_world_to_view(worldNormal);
# ifdef USE_ATTR
- pass_attr(pos);
+# ifdef HAIR_SHADER
+ pos = hair_get_strand_pos();
+# endif
+ pass_attr(pos, NormalMatrix, ModelMatrixInverse);
# endif
#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl
index 0591b247541..29495e98355 100644
--- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl
@@ -1,3 +1,10 @@
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
+#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
+
/* ------------ Refraction ------------ */
#define BTDF_BIAS 0.85
diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
new file mode 100644
index 00000000000..e746acfdfa3
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
@@ -0,0 +1,89 @@
+
+/* Required by some nodes. */
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+
+#pragma BLENDER_REQUIRE(closure_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
+#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
+
+#ifdef USE_ALPHA_BLEND
+/* Use dual source blending to be able to make a whole range of effects. */
+layout(location = 0, index = 0) out vec4 outRadiance;
+layout(location = 0, index = 1) out vec4 outTransmittance;
+
+#else /* OPAQUE */
+layout(location = 0) out vec4 outRadiance;
+layout(location = 1) out vec2 ssrNormals;
+layout(location = 2) out vec4 ssrData;
+# ifdef USE_SSS
+layout(location = 3) out vec3 sssIrradiance;
+layout(location = 4) out float sssRadius;
+layout(location = 5) out vec3 sssAlbedo;
+# endif
+
+#endif
+
+void main()
+{
+ Closure cl = nodetree_exec();
+
+ float holdout = saturate(1.0 - cl.holdout);
+ float transmit = saturate(avg(cl.transmittance));
+ float alpha = 1.0 - transmit;
+
+#ifdef USE_ALPHA_BLEND
+ vec2 uvs = gl_FragCoord.xy * volCoordScale.zw;
+ vec3 vol_transmit, vol_scatter;
+ volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter);
+
+ /* Removes part of the volume scattering that have
+ * already been added to the destination pixels.
+ * Since we do that using the blending pipeline we need to account for material transmittance. */
+ vol_scatter -= vol_scatter * cl.transmittance;
+
+ 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;
+ ssrData = cl.ssr_data;
+# ifdef USE_SSS
+ sssIrradiance = cl.sss_irradiance;
+ sssRadius = cl.sss_radius;
+ sssAlbedo = cl.sss_albedo;
+# endif
+#endif
+
+ /* For Probe capture */
+#ifdef USE_SSS
+ float fac = float(!sssToggle);
+
+ /* TODO(fclem) we shouldn't need this.
+ * Just disable USE_SSS when USE_REFRACTION is enabled. */
+# ifdef USE_REFRACTION
+ /* SSRefraction pass is done after the SSS pass.
+ * In order to not loose the diffuse light totally we
+ * need to merge the SSS radiance to the main radiance. */
+ fac = 1.0;
+# endif
+
+ outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac;
+#endif
+
+#ifndef USE_ALPHA_BLEND
+ float alpha_div = 1.0 / max(1e-8, alpha);
+ outRadiance.rgb *= alpha_div;
+ ssrData.rgb *= alpha_div;
+# ifdef USE_SSS
+ sssAlbedo.rgb *= alpha_div;
+# endif
+#endif
+
+#ifdef LOOKDEV
+ /* Lookdev spheres are rendered in front. */
+ gl_FragDepth = 0.0;
+#endif
+}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl
new file mode 100644
index 00000000000..ad437dca79a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl
@@ -0,0 +1,46 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+RESOURCE_ID_VARYING
+
+/* Only used to compute barycentric coordinates. */
+
+void main()
+{
+#ifdef DO_BARYCENTRIC_DISTANCES
+ dataAttrOut.barycentricDist = calc_barycentric_distances(
+ dataIn[0].worldPosition, dataIn[1].worldPosition, dataIn[2].worldPosition);
+#endif
+
+ PASS_RESOURCE_ID
+
+#ifdef USE_ATTR
+ pass_attr(0);
+#endif
+ PASS_SURFACE_INTERFACE(0);
+ gl_Position = gl_in[0].gl_Position;
+ gl_ClipDistance[0] = gl_in[0].gl_ClipDistance[0];
+ EmitVertex();
+
+#ifdef USE_ATTR
+ pass_attr(1);
+#endif
+ PASS_SURFACE_INTERFACE(1);
+ gl_Position = gl_in[1].gl_Position;
+ gl_ClipDistance[0] = gl_in[1].gl_ClipDistance[0];
+ EmitVertex();
+
+#ifdef USE_ATTR
+ pass_attr(2);
+#endif
+ PASS_SURFACE_INTERFACE(2);
+ gl_Position = gl_in[2].gl_Position;
+ gl_ClipDistance[0] = gl_in[2].gl_ClipDistance[0];
+ EmitVertex();
+
+ EndPrimitive();
+}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
new file mode 100644
index 00000000000..b93a3a23eff
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
@@ -0,0 +1,43 @@
+/** This describe the entire interface of the shader. */
+
+/* Samplers */
+uniform sampler2D colorBuffer;
+uniform sampler2D depthBuffer;
+
+/* Uniforms */
+uniform float refractionDepth;
+
+#define SURFACE_INTERFACE \
+ vec3 worldPosition; \
+ vec3 viewPosition; \
+ vec3 worldNormal; \
+ vec3 viewNormal;
+
+#ifdef GPU_GEOMETRY_SHADER
+in ShaderStageInterface{SURFACE_INTERFACE} dataIn[];
+
+out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
+
+# define PASS_SURFACE_INTERFACE(vert) \
+ dataOut.worldPosition = dataIn[vert].worldPosition; \
+ dataOut.viewPosition = dataIn[vert].viewPosition; \
+ dataOut.worldNormal = dataIn[vert].worldNormal; \
+ dataOut.viewNormal = dataIn[vert].viewNormal;
+
+#else
+
+IN_OUT ShaderStageInterface{SURFACE_INTERFACE};
+
+#endif
+
+#ifdef HAIR_SHADER
+IN_OUT ShaderHairInterface
+{
+ /* world space */
+ vec3 hairTangent;
+ float hairThickTime;
+ float hairThickness;
+ float hairTime;
+ flat int hairStrandID;
+};
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
index 1b94fc2bee1..0ad1393dd70 100644
--- a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
@@ -1,32 +1,20 @@
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(surface_lib.glsl)
+
#ifndef HAIR_SHADER
in vec3 pos;
in vec3 nor;
#endif
-#ifdef MESH_SHADER
-out vec3 worldPosition;
-out vec3 viewPosition;
-out vec3 worldNormal;
-out vec3 viewNormal;
-#endif
-
-#ifdef HAIR_SHADER
-out vec3 hairTangent;
-out float hairThickTime;
-out float hairThickness;
-out float hairTime;
-flat out int hairStrandID;
-#endif
+RESOURCE_ID_VARYING
void main()
{
-#ifdef GPU_INTEL
- /* Due to some shader compiler bug, we somewhat
- * need to access gl_VertexID to make it work. even
- * if it's actually dead code. */
- gl_Position.x = float(gl_VertexID);
-#endif
+ GPU_INTEL_VERTEX_SHADER_WORKAROUND
+
+ PASS_RESOURCE_ID
#ifdef HAIR_SHADER
hairStrandID = hair_get_strand_id();
@@ -63,7 +51,10 @@ void main()
/* No need to normalize since this is just a rotation. */
viewNormal = normal_world_to_view(worldNormal);
# ifdef USE_ATTR
- pass_attr(pos);
+# ifdef HAIR_SHADER
+ pos = hair_get_strand_pos();
+# endif
+ pass_attr(pos, NormalMatrix, ModelMatrixInverse);
# endif
#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl
index 02ad2170f06..0c01c46c2ba 100644
--- a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl
@@ -1,11 +1,11 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
uniform sampler2D blueNoise;
uniform vec3 offsets;
out vec4 FragColor;
-#define M_2PI 6.28318530717958647692
-
void main(void)
{
vec3 blue_noise = texelFetch(blueNoise, ivec2(gl_FragCoord.xy), 0).xyz;
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
index 312fc07054a..bac69ab0355 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
@@ -1,9 +1,10 @@
+#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_lib.glsl)
+
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
-#define NODETREE_EXEC
-
#ifdef MESH_SHADER
uniform vec3 volumeOrcoLoc;
uniform vec3 volumeOrcoSize;
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
index 96b891c929f..30cda401284 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
#ifdef MESH_SHADER
/* TODO tight slices */
layout(triangles) in;
@@ -12,12 +14,16 @@ in vec4 vPos[];
flat out int slice;
+RESOURCE_ID_VARYING
+
#ifdef MESH_SHADER
/* TODO tight slices */
void main()
{
gl_Layer = slice = int(vPos[0].z);
+ PASS_RESOURCE_ID
+
# ifdef USE_ATTR
pass_attr(0);
# endif
@@ -48,6 +54,8 @@ void main()
{
gl_Layer = slice = int(vPos[0].z);
+ PASS_RESOURCE_ID
+
# ifdef USE_ATTR
pass_attr(0);
# endif
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
index c3c442e7b69..f4276bd61bd 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
+
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
@@ -11,9 +13,11 @@ uniform sampler3D volumeExtinction;
#ifdef USE_VOLUME_OPTI
uniform layout(binding = 0, r11f_g11f_b10f) writeonly restrict image3D finalScattering_img;
uniform layout(binding = 1, r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img;
+
vec3 finalScattering;
vec3 finalTransmittance;
#else
+
flat in int slice;
layout(location = 0) out vec3 finalScattering;
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
index 40eb3da42d1..9b852a57ec4 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
@@ -1,4 +1,8 @@
+#pragma BLENDER_REQUIRE(lights_lib.glsl)
+#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
+#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
+
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
@@ -58,7 +62,6 @@ float phase_function(vec3 v, vec3 l, float g)
return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
}
-#ifdef LAMPS_LIB
vec3 light_volume(LightData ld, vec4 l_vector)
{
float power;
@@ -95,7 +98,7 @@ vec3 light_volume(LightData ld, vec4 l_vector)
return tint * lum;
}
-# define VOLUMETRIC_SHADOW_MAX_STEP 32.0
+#define VOLUMETRIC_SHADOW_MAX_STEP 32.0
vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction)
{
@@ -109,7 +112,7 @@ vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction)
vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction)
{
-# if defined(VOLUME_SHADOW)
+#if defined(VOLUME_SHADOW)
/* Heterogeneous volume shadows */
float dd = l_vector.w / volShadowSteps;
vec3 L = l_vector.xyz * l_vector.w;
@@ -120,27 +123,24 @@ vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D v
shadow *= exp(-s_extinction * dd);
}
return shadow;
-# else
+#else
return vec3(1.0);
-# endif /* VOLUME_SHADOW */
+#endif /* VOLUME_SHADOW */
}
-#endif
-#ifdef IRRADIANCE_LIB
vec3 irradiance_volumetric(vec3 wpos)
{
-# ifdef IRRADIANCE_HL2
+#ifdef IRRADIANCE_HL2
IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0));
vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
ir_data = load_irradiance_cell(0, vec3(-1.0));
irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
irradiance *= 0.16666666; /* 1/6 */
return irradiance;
-# else
+#else
return vec3(0.0);
-# endif
-}
#endif
+}
uniform sampler3D inScattering;
uniform sampler3D inTransmittance;
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl
index 1ff7e848c40..6ab21587c9a 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
+
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl
index 9621fa1cc0d..806f1b5b205 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
+
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
index b96360febb0..b70747ecec3 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
@@ -1,6 +1,10 @@
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
out vec4 vPos;
+RESOURCE_ID_VARYING
+
void main()
{
/* Generate Triangle : less memory fetches from a VBO */
@@ -25,7 +29,9 @@ void main()
vPos.z = float(t_id);
vPos.w = 1.0;
+ PASS_RESOURCE_ID
+
#ifdef USE_ATTR
- pass_attr(vec3(0.0));
+ pass_attr(vec3(0.0), mat3(1), mat4(1));
#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..8a4134ec8ea 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -106,7 +106,7 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob)
copy_v3_v3(tgp_ob->plane_mat[3], center);
/* Add to corresponding list if is in front. */
- if (ob->dtx & OB_DRAWXRAY) {
+ if (ob->dtx & OB_DRAW_IN_FRONT) {
BLI_LINKS_APPEND(&pd->tobjects_infront, tgp_ob);
}
else {
@@ -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;
@@ -258,15 +258,17 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
{
bGPdata *gpd = (bGPdata *)ob->data;
- const bool is_in_front = (ob->dtx & OB_DRAWXRAY);
+ const bool is_in_front = (ob->dtx & OB_DRAW_IN_FRONT);
const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0;
const bool overide_vertcol = (pd->v3d_color_type != -1);
const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) ||
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_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 6e6b35e19ca..ed443b2c73f 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -63,7 +63,7 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
if (ibuf != NULL && ibuf->rect != NULL) {
- gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, GL_TEXTURE_2D);
+ gpu_tex = BKE_image_get_gpu_texture(image, &iuser, ibuf);
*r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL);
}
BKE_image_release_ibuf(image, ibuf, lock);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 495de7ef10b..dbad226099e 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;
}
@@ -522,12 +525,6 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
DRW_shgroup_uniform_texture(iter->grp, "gpStrokeTexture", tex_stroke);
iter->tex_stroke = tex_stroke;
}
-
- /* TODO(fclem): This is a quick workaround but
- * ideally we should have this as a permanent bind. */
- const bool is_masked = iter->tgp_ob->layers.last->mask_bits != NULL;
- GPUTexture **mask_tex = (is_masked) ? &iter->pd->mask_tx : &iter->pd->dummy_tx;
- DRW_shgroup_uniform_texture_ref(iter->grp, "gpMaskTexture", mask_tex);
}
bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW);
@@ -537,7 +534,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 +544,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 +627,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_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c
index bb91bdbe396..4748858a6a8 100644
--- a/source/blender/draw/engines/gpencil/gpencil_render.c
+++ b/source/blender/draw/engines/gpencil/gpencil_render.c
@@ -235,6 +235,7 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl,
BLI_rcti_size_y(rect),
4,
0,
+ GPU_DATA_FLOAT,
rp->rect);
}
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/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
index 1e75f6dd5bb..36fe9df9e5a 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -137,24 +137,20 @@ void blend_mode_output(
}
}
-#ifdef GPU_VERTEX_SHADER
-# define IN_OUT out
-#else
-# define IN_OUT in
-#endif
-
-/* Shader interface. */
-IN_OUT vec4 finalColorMul;
-IN_OUT vec4 finalColorAdd;
-IN_OUT vec3 finalPos;
-IN_OUT vec2 finalUvs;
-noperspective IN_OUT float strokeThickness;
-noperspective IN_OUT float strokeHardeness;
-flat IN_OUT vec2 strokeAspect;
-flat IN_OUT vec2 strokePt1;
-flat IN_OUT vec2 strokePt2;
-flat IN_OUT int matFlag;
-flat IN_OUT float depth;
+IN_OUT ShaderStageInterface
+{
+ vec4 finalColorMul;
+ vec4 finalColorAdd;
+ vec3 finalPos;
+ vec2 finalUvs;
+ noperspective float strokeThickness;
+ noperspective float strokeHardeness;
+ flat vec2 strokeAspect;
+ flat vec2 strokePt1;
+ flat vec2 strokePt2;
+ flat int matFlag;
+ flat float depth;
+};
#ifdef GPU_FRAGMENT_SHADER
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 95fd918f8c1..960e5e424f2 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -178,6 +178,7 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
#define BUF_INSTANCE DRW_shgroup_call_buffer_instance
#define BUF_LINE(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_LINES)
+#define BUF_POINT(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_POINTS)
{
format = formats->instance_bone;
@@ -228,11 +229,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());
@@ -510,7 +512,7 @@ static void drw_shgroup_bone_envelope(ArmatureDrawContext *ctx,
/* Custom (geometry) */
extern void drw_batch_cache_validate(Object *custom);
-extern void drw_batch_cache_generate_requested(Object *custom);
+extern void drw_batch_cache_generate_requested_delayed(Object *custom);
BLI_INLINE DRWCallBuffer *custom_bone_instance_shgroup(ArmatureDrawContext *ctx,
DRWShadingGroup *grp,
@@ -567,7 +569,7 @@ static void drw_shgroup_bone_custom_solid(ArmatureDrawContext *ctx,
}
/* TODO(fclem) needs to be moved elsewhere. */
- drw_batch_cache_generate_requested(custom);
+ drw_batch_cache_generate_requested_delayed(custom);
}
static void drw_shgroup_bone_custom_wire(ArmatureDrawContext *ctx,
@@ -591,7 +593,7 @@ static void drw_shgroup_bone_custom_wire(ArmatureDrawContext *ctx,
}
/* TODO(fclem) needs to be moved elsewhere. */
- drw_batch_cache_generate_requested(custom);
+ drw_batch_cache_generate_requested_delayed(custom);
}
static void drw_shgroup_bone_custom_empty(ArmatureDrawContext *ctx,
@@ -2117,10 +2119,11 @@ static void armature_context_setup(ArmatureDrawContext *ctx,
const bool do_envelope_dist,
const bool is_edit_mode,
const bool is_pose_mode,
- float *const_color)
+ const float *const_color)
{
const bool is_object_mode = !do_envelope_dist;
- const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0 || (pd->armature.do_pose_xray && is_pose_mode);
+ const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0 ||
+ (pd->armature.do_pose_xray && is_pose_mode);
const bool draw_as_wire = (ob->dt < OB_SOLID);
const bool is_filled = (!pd->armature.transparent && !draw_as_wire) || !is_object_mode;
const bool is_transparent = pd->armature.transparent || (draw_as_wire && !is_object_mode);
@@ -2167,7 +2170,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_curve.c b/source/blender/draw/engines/overlay/overlay_edit_curve.c
index 9a79c78c996..6d907646817 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_curve.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_curve.c
@@ -78,7 +78,7 @@ void OVERLAY_edit_curve_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
bool draw_normals = (pd->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_NORMALS) != 0;
- bool do_xray = (ob->dtx & OB_DRAWXRAY) != 0;
+ bool do_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
Curve *cu = ob->data;
struct GPUBatch *geom;
diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
index 7fc1a7fdce6..ebc8a2f97ef 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
@@ -269,7 +269,8 @@ void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob)
OVERLAY_PrivateData *pd = vedata->stl->pd;
struct GPUBatch *geom = NULL;
- bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0;
+ bool draw_as_solid = (ob->dt > OB_WIRE);
+ bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
bool do_occlude_wire = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_OCCLUDE_WIRE) != 0;
bool do_show_mesh_analysis = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_STATVIS) != 0;
bool fnormals_do = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_FACE_NORMALS) != 0;
@@ -283,7 +284,7 @@ void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob)
}
}
- if (do_occlude_wire || do_in_front) {
+ if (do_occlude_wire || (do_in_front && draw_as_solid)) {
geom = DRW_cache_mesh_surface_get(ob);
DRW_shgroup_call_no_cull(pd->edit_mesh_depth_grp[do_in_front], geom, ob);
}
@@ -311,7 +312,7 @@ void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob)
overlay_edit_mesh_add_ob_to_pass(pd, ob, do_in_front);
}
- pd->edit_mesh.ghost_ob += (ob->dtx & OB_DRAWXRAY) ? 1 : 0;
+ pd->edit_mesh.ghost_ob += (ob->dtx & OB_DRAW_IN_FRONT) ? 1 : 0;
pd->edit_mesh.edit_ob += 1;
if (DRW_state_show_text() && (pd->edit_mesh.flag & OVERLAY_EDIT_TEXT)) {
diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c
index c4d020adc11..6ddd0e6d9be 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);
}
}
@@ -173,7 +182,7 @@ void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob)
OVERLAY_PrivateData *pd = vedata->stl->pd;
Curve *cu = ob->data;
struct GPUBatch *geom;
- bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0;
+ bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || cu->ext2 != 0.0f;
if ((cu->flag & CU_FAST) || !has_surface) {
@@ -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..bc96a03da31 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -182,7 +182,6 @@ static void OVERLAY_cache_init(void *vedata)
OVERLAY_motion_path_cache_init(vedata);
OVERLAY_outline_cache_init(vedata);
OVERLAY_particle_cache_init(vedata);
- OVERLAY_pointcloud_cache_init(vedata);
OVERLAY_wireframe_cache_init(vedata);
}
@@ -403,12 +402,6 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OVERLAY_particle_cache_populate(vedata, ob);
}
- /* TODO: these should not be overlays, just here for testing since it's
- * easier to implement than integrating it into eevee/workbench. */
- if (ob->type == OB_POINTCLOUD) {
- OVERLAY_pointcloud_cache_populate(vedata, ob);
- }
-
/* Relationship, object center, bounbox ... */
if (!pd->hide_overlays) {
OVERLAY_extra_cache_populate(vedata, ob);
@@ -423,7 +416,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();
@@ -482,7 +475,6 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_armature_draw(vedata);
OVERLAY_particle_draw(vedata);
OVERLAY_metaball_draw(vedata);
- OVERLAY_pointcloud_draw(vedata);
OVERLAY_gpencil_draw(vedata);
OVERLAY_extra_draw(vedata);
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 15bf26a5fa8..1e766b3cc39 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -53,8 +53,6 @@
#include "ED_view3d.h"
-#include "GPU_draw.h"
-
#include "overlay_private.h"
#include "draw_common.h"
@@ -199,6 +197,9 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata)
cb->extra_loose_points = grp = DRW_shgroup_create(sh, extra_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+
+ /* Buffer access for drawing isolated points, matching `extra_lines`. */
+ cb->extra_points = BUF_POINT(grp, formats->point_extra);
}
{
format = formats->pos;
@@ -230,6 +231,11 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata)
}
}
+void OVERLAY_extra_point(OVERLAY_ExtraCallBuffers *cb, const float point[3], const float color[4])
+{
+ DRW_buffer_add_entry(cb->extra_points, point, color);
+}
+
void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb,
const float start[3],
const float end[3],
@@ -250,7 +256,7 @@ void OVERLAY_extra_line(OVERLAY_ExtraCallBuffers *cb,
OVERLAY_ExtraCallBuffers *OVERLAY_extra_call_buffer_get(OVERLAY_Data *vedata, Object *ob)
{
- bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0;
+ bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
OVERLAY_PrivateData *pd = vedata->stl->pd;
return &pd->extra_call_buffers[do_in_front];
}
@@ -1276,6 +1282,19 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
OVERLAY_extra_line_dashed(cb, parent_pos, ob->obmat[3], relation_color);
}
+ /* Drawing the hook lines. */
+ for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Hook) {
+ HookModifierData *hmd = (HookModifierData *)md;
+ float center[3];
+ mul_v3_m4v3(center, ob->obmat, hmd->cent);
+ if (hmd->object) {
+ OVERLAY_extra_line_dashed(cb, hmd->object->obmat[3], center, relation_color);
+ }
+ OVERLAY_extra_point(cb, center, relation_color);
+ }
+ }
+
if (ob->rigidbody_constraint) {
Object *rbc_ob1 = ob->rigidbody_constraint->ob1;
Object *rbc_ob2 = ob->rigidbody_constraint->ob2;
@@ -1318,7 +1337,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;
@@ -1356,24 +1375,24 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb,
Object *ob,
ModifierData *md,
Scene *scene,
- float *color)
+ const float *color)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ FluidDomainSettings *fds = fmd->domain;
/* Don't show smoke before simulation starts, this could be made an option in the future. */
- const bool draw_velocity = (mds->draw_velocity && mds->fluid &&
- CFRA >= mds->point_cache[0]->startframe);
+ const bool draw_velocity = (fds->draw_velocity && fds->fluid &&
+ CFRA >= fds->point_cache[0]->startframe);
/* Small cube showing voxel size. */
{
float min[3];
- madd_v3fl_v3fl_v3fl_v3i(min, mds->p0, mds->cell_size, mds->res_min);
+ madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, fds->cell_size, fds->res_min);
float voxel_cubemat[4][4] = {{0.0f}};
/* scale small cube to voxel size */
- voxel_cubemat[0][0] = mds->cell_size[0] / 2.0f;
- voxel_cubemat[1][1] = mds->cell_size[1] / 2.0f;
- voxel_cubemat[2][2] = mds->cell_size[2] / 2.0f;
+ voxel_cubemat[0][0] = fds->cell_size[0] / 2.0f;
+ voxel_cubemat[1][1] = fds->cell_size[1] / 2.0f;
+ voxel_cubemat[2][2] = fds->cell_size[2] / 2.0f;
voxel_cubemat[3][3] = 1.0f;
/* translate small cube to corner */
copy_v3_v3(voxel_cubemat[3], min);
@@ -1385,38 +1404,38 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb,
}
if (draw_velocity) {
- const bool use_needle = (mds->vector_draw_type == VECTOR_DRAW_NEEDLE);
+ const bool use_needle = (fds->vector_draw_type == VECTOR_DRAW_NEEDLE);
int line_count = (use_needle) ? 6 : 1;
int slice_axis = -1;
- line_count *= mds->res[0] * mds->res[1] * mds->res[2];
+ line_count *= fds->res[0] * fds->res[1] * fds->res[2];
- if (mds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED &&
- mds->axis_slice_method == AXIS_SLICE_SINGLE) {
+ if (fds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED &&
+ fds->axis_slice_method == AXIS_SLICE_SINGLE) {
float viewinv[4][4];
DRW_view_viewmat_get(NULL, viewinv, true);
- const int axis = (mds->slice_axis == SLICE_AXIS_AUTO) ? axis_dominant_v3_single(viewinv[2]) :
- mds->slice_axis - 1;
+ const int axis = (fds->slice_axis == SLICE_AXIS_AUTO) ? axis_dominant_v3_single(viewinv[2]) :
+ fds->slice_axis - 1;
slice_axis = axis;
- line_count /= mds->res[axis];
+ line_count /= fds->res[axis];
}
- GPU_create_smoke_velocity(mmd);
+ DRW_smoke_ensure_velocity(fmd);
GPUShader *sh = OVERLAY_shader_volume_velocity(use_needle);
DRWShadingGroup *grp = DRW_shgroup_create(sh, data->psl->extra_ps[0]);
- DRW_shgroup_uniform_texture(grp, "velocityX", mds->tex_velocity_x);
- DRW_shgroup_uniform_texture(grp, "velocityY", mds->tex_velocity_y);
- DRW_shgroup_uniform_texture(grp, "velocityZ", mds->tex_velocity_z);
- DRW_shgroup_uniform_float_copy(grp, "displaySize", mds->vector_scale);
- DRW_shgroup_uniform_float_copy(grp, "slicePosition", mds->slice_depth);
- DRW_shgroup_uniform_vec3_copy(grp, "cellSize", mds->cell_size);
- DRW_shgroup_uniform_vec3_copy(grp, "domainOriginOffset", mds->p0);
- DRW_shgroup_uniform_ivec3_copy(grp, "adaptiveCellOffset", mds->res_min);
+ DRW_shgroup_uniform_texture(grp, "velocityX", fds->tex_velocity_x);
+ DRW_shgroup_uniform_texture(grp, "velocityY", fds->tex_velocity_y);
+ DRW_shgroup_uniform_texture(grp, "velocityZ", fds->tex_velocity_z);
+ DRW_shgroup_uniform_float_copy(grp, "displaySize", fds->vector_scale);
+ DRW_shgroup_uniform_float_copy(grp, "slicePosition", fds->slice_depth);
+ DRW_shgroup_uniform_vec3_copy(grp, "cellSize", fds->cell_size);
+ DRW_shgroup_uniform_vec3_copy(grp, "domainOriginOffset", fds->p0);
+ DRW_shgroup_uniform_ivec3_copy(grp, "adaptiveCellOffset", fds->res_min);
DRW_shgroup_uniform_int_copy(grp, "sliceAxis", slice_axis);
DRW_shgroup_call_procedural_lines(grp, ob, line_count);
- BLI_addtail(&data->stl->pd->smoke_domains, BLI_genericNodeN(mmd));
+ BLI_addtail(&data->stl->pd->smoke_domains, BLI_genericNodeN(fmd));
}
}
@@ -1430,8 +1449,8 @@ static void OVERLAY_volume_free_smoke_textures(OVERLAY_Data *data)
* all viewport in a redraw at least. */
LinkData *link;
while ((link = BLI_pophead(&data->stl->pd->smoke_domains))) {
- FluidModifierData *mmd = (FluidModifierData *)link->data;
- GPU_free_smoke_velocity(mmd);
+ FluidModifierData *fmd = (FluidModifierData *)link->data;
+ DRW_smoke_free_velocity(fmd);
MEM_freeN(link);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_facing.c b/source/blender/draw/engines/overlay/overlay_facing.c
index 4eb4b8ae85b..cfeaf8e1927 100644
--- a/source/blender/draw/engines/overlay/overlay_facing.c
+++ b/source/blender/draw/engines/overlay/overlay_facing.c
@@ -60,7 +60,7 @@ void OVERLAY_facing_cache_populate(OVERLAY_Data *vedata, Object *ob)
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
- const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
if (use_sculpt_pbvh) {
DRW_shgroup_call_sculpt(pd->facing_grp[is_xray], ob, false, false);
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_image.c b/source/blender/draw/engines/overlay/overlay_image.c
index be3510967b6..d456e147c15 100644
--- a/source/blender/draw/engines/overlay/overlay_image.c
+++ b/source/blender/draw/engines/overlay/overlay_image.c
@@ -61,11 +61,11 @@ void OVERLAY_image_cache_init(OVERLAY_Data *vedata)
state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
DRW_PASS_CREATE(psl->image_empties_ps, state | pd->clipping_state);
- state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA;
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA_PREMUL;
DRW_PASS_CREATE(psl->image_empties_back_ps, state | pd->clipping_state);
DRW_PASS_CREATE(psl->image_empties_blend_ps, state | pd->clipping_state);
- state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL;
DRW_PASS_CREATE(psl->image_empties_front_ps, state);
DRW_PASS_CREATE(psl->image_foreground_ps, state);
}
@@ -175,7 +175,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
}
width = ibuf->x;
height = ibuf->y;
- tex = GPU_texture_from_blender(image, iuser, ibuf, GL_TEXTURE_2D);
+ tex = BKE_image_get_gpu_texture(image, iuser, ibuf);
BKE_image_release_ibuf(image, ibuf, lock);
iuser->scene = NULL;
@@ -203,7 +203,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
}
BKE_movieclip_user_set_frame(&bgpic->cuser, ctime);
- tex = GPU_texture_from_movieclip(clip, &bgpic->cuser, GL_TEXTURE_2D);
+ tex = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser);
if (tex == NULL) {
return NULL;
}
@@ -232,7 +232,7 @@ static void OVERLAY_image_free_movieclips_textures(OVERLAY_Data *data)
LinkData *link;
while ((link = BLI_pophead(&data->stl->pd->bg_movie_clips))) {
MovieClip *clip = (MovieClip *)link->data;
- GPU_free_texture_movieclip(clip);
+ BKE_movieclip_free_gputexture(clip);
MEM_freeN(link);
}
}
@@ -342,7 +342,7 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob)
mul_m4_m4m4(mat, modelmat, mat);
const bool is_foreground = (bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != 0;
- float color_premult_alpha[4] = {bgpic->alpha, bgpic->alpha, bgpic->alpha, bgpic->alpha};
+ float color_premult_alpha[4] = {1.0f, 1.0f, 1.0f, bgpic->alpha};
DRWPass *pass = is_foreground ? psl->image_foreground_ps : psl->image_background_ps;
@@ -383,7 +383,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (ima != NULL) {
ImageUser iuser = *ob->iuser;
camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser);
- tex = GPU_texture_from_blender(ima, &iuser, NULL, GL_TEXTURE_2D);
+ tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
if (tex) {
size[0] = GPU_texture_orig_width(tex);
size[1] = GPU_texture_orig_height(tex);
@@ -405,7 +405,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* Use the actual depth if we are doing depth tests to determine the distance to the object */
char depth_mode = DRW_state_is_depth() ? OB_EMPTY_IMAGE_DEPTH_DEFAULT : ob->empty_image_depth;
DRWPass *pass = NULL;
- if ((ob->dtx & OB_DRAWXRAY) != 0) {
+ if ((ob->dtx & OB_DRAW_IN_FRONT) != 0) {
/* Object In Front overrides image empty depth mode. */
pass = psl->image_empties_front_ps;
}
diff --git a/source/blender/draw/engines/overlay/overlay_metaball.c b/source/blender/draw/engines/overlay/overlay_metaball.c
index 76ffbe4cc6b..c10c0a84247 100644
--- a/source/blender/draw/engines/overlay/overlay_metaball.c
+++ b/source/blender/draw/engines/overlay/overlay_metaball.c
@@ -69,7 +69,7 @@ static void metaball_instance_data_set(
void OVERLAY_edit_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
- const bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const bool is_select = DRW_state_is_select();
OVERLAY_PrivateData *pd = vedata->stl->pd;
MetaBall *mb = ob->data;
@@ -112,7 +112,7 @@ void OVERLAY_edit_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob)
void OVERLAY_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
- const bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
OVERLAY_PrivateData *pd = vedata->stl->pd;
MetaBall *mb = ob->data;
const DRWContextState *draw_ctx = DRW_context_state_get();
diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c
index 99d22fc380f..214322c4adc 100644
--- a/source/blender/draw/engines/overlay/overlay_outline.c
+++ b/source/blender/draw/engines/overlay/overlay_outline.c
@@ -138,6 +138,11 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata)
pd->outlines_grp = grp = DRW_shgroup_create(sh_geom, psl->outlines_prepass_ps);
DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
+ GPUShader *sh_geom_ptcloud = OVERLAY_shader_outline_prepass_pointcloud();
+
+ pd->outlines_ptcloud_grp = grp = DRW_shgroup_create(sh_geom_ptcloud, psl->outlines_prepass_ps);
+ DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
+
GPUShader *sh_gpencil = OVERLAY_shader_outline_prepass_gpencil();
pd->outlines_gpencil_grp = grp = DRW_shgroup_create(sh_gpencil, psl->outlines_prepass_ps);
@@ -178,10 +183,10 @@ typedef struct iterData {
float plane[4];
} iterData;
-static void gp_layer_cache_populate(bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *UNUSED(gps),
- void *thunk)
+static void gpencil_layer_cache_populate(bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *UNUSED(gps),
+ void *thunk)
{
iterData *iter = (iterData *)thunk;
bGPdata *gpd = (bGPdata *)iter->ob->data;
@@ -204,10 +209,10 @@ static void gp_layer_cache_populate(bGPDlayer *gpl,
DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane);
}
-static void gp_stroke_cache_populate(bGPDlayer *UNUSED(gpl),
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps,
- void *thunk)
+static void gpencil_stroke_cache_populate(bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps,
+ void *thunk)
{
iterData *iter = (iterData *)thunk;
@@ -258,8 +263,13 @@ static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
gpencil_depth_plane(ob, iter.plane);
}
- BKE_gpencil_visible_stroke_iter(
- NULL, ob, gp_layer_cache_populate, gp_stroke_cache_populate, &iter, false, pd->cfra);
+ BKE_gpencil_visible_stroke_iter(NULL,
+ ob,
+ gpencil_layer_cache_populate,
+ gpencil_stroke_cache_populate,
+ &iter,
+ false,
+ pd->cfra);
}
void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
@@ -283,6 +293,12 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
return;
}
+ if (ob->type == OB_POINTCLOUD && pd->wireframe_mode) {
+ /* Looks bad in this case. Could be relaxed if we draw a
+ * wireframe of some sort in the future. */
+ return;
+ }
+
if (dupli && !init_dupli) {
geom = dupli->outline_geom;
shgroup = dupli->outline_shgrp;
@@ -302,12 +318,18 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
}
if (geom) {
- shgroup = pd->outlines_grp;
+ shgroup = (ob->type == OB_POINTCLOUD) ? pd->outlines_ptcloud_grp : pd->outlines_grp;
}
}
if (shgroup && geom) {
- DRW_shgroup_call(shgroup, geom, ob);
+ if (ob->type == OB_POINTCLOUD) {
+ /* Draw range to avoid drawcall batching messing up the instance attrib. */
+ DRW_shgroup_call_instance_range(shgroup, ob, geom, 0, 0);
+ }
+ else {
+ DRW_shgroup_call(shgroup, geom, ob);
+ }
}
if (init_dupli) {
diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c
index 456c56f9c03..25b8a953ba7 100644
--- a/source/blender/draw/engines/overlay/overlay_paint.c
+++ b/source/blender/draw/engines/overlay/overlay_paint.c
@@ -22,6 +22,8 @@
#include "DRW_render.h"
+#include "BKE_image.h"
+
#include "DNA_mesh_types.h"
#include "DEG_depsgraph_query.h"
@@ -63,7 +65,7 @@ void OVERLAY_paint_init(OVERLAY_Data *vedata)
const DRWContextState *draw_ctx = DRW_context_state_get();
pd->painting.in_front = pd->use_in_front && draw_ctx->obact &&
- (draw_ctx->obact->dtx & OB_DRAWXRAY);
+ (draw_ctx->obact->dtx & OB_DRAW_IN_FRONT);
pd->painting.alpha_blending = paint_object_is_rendered_transparent(draw_ctx->v3d,
draw_ctx->obact);
}
@@ -136,7 +138,7 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state);
- GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, GL_TEXTURE_2D);
+ GPUTexture *tex = BKE_image_get_gpu_texture(imapaint->stencil, NULL, NULL);
const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL);
const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0;
@@ -254,11 +256,18 @@ void OVERLAY_paint_draw(OVERLAY_Data *vedata)
OVERLAY_PrivateData *pd = stl->pd;
OVERLAY_PassList *psl = vedata->psl;
+ OVERLAY_FramebufferList *fbl = vedata->fbl;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (DRW_state_is_fbo()) {
- /* Paint overlay needs final color because of multiply blend mode. */
- GPU_framebuffer_bind(pd->painting.in_front ? dfbl->in_front_fb : dfbl->default_fb);
+ if (pd->painting.alpha_blending) {
+ GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb :
+ fbl->overlay_default_fb);
+ }
+ else {
+ /* Paint overlay needs final color because of multiply blend mode. */
+ GPU_framebuffer_bind(pd->painting.in_front ? dfbl->in_front_fb : dfbl->default_fb);
+ }
}
if (psl->paint_depth_ps) {
diff --git a/source/blender/draw/engines/overlay/overlay_pointcloud.c b/source/blender/draw/engines/overlay/overlay_pointcloud.c
deleted file mode 100644
index b2a2d44bf73..00000000000
--- a/source/blender/draw/engines/overlay/overlay_pointcloud.c
+++ /dev/null
@@ -1,72 +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.
- *
- * Copyright 2020, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- */
-
-#include "DRW_render.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "DNA_pointcloud_types.h"
-
-#include "BKE_pointcache.h"
-
-#include "overlay_private.h"
-
-/* -------------------------------------------------------------------- */
-/** \name PointCloud
- * \{ */
-
-void OVERLAY_pointcloud_cache_init(OVERLAY_Data *vedata)
-{
- OVERLAY_PassList *psl = vedata->psl;
- OVERLAY_PrivateData *pd = vedata->stl->pd;
- GPUShader *sh;
- DRWShadingGroup *grp;
-
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
- DRW_PASS_CREATE(psl->pointcloud_ps, state | pd->clipping_state);
-
- sh = OVERLAY_shader_pointcloud_dot();
- pd->pointcloud_dots_grp = grp = DRW_shgroup_create(sh, psl->pointcloud_ps);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
-}
-
-void OVERLAY_pointcloud_cache_populate(OVERLAY_Data *vedata, Object *ob)
-{
- OVERLAY_PrivateData *pd = vedata->stl->pd;
-
- struct GPUBatch *geom = DRW_cache_pointcloud_get_dots(ob);
-
- const float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
-
- DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->pointcloud_dots_grp);
- DRW_shgroup_uniform_vec4_copy(grp, "color", color);
- DRW_shgroup_call(grp, geom, ob);
-}
-
-void OVERLAY_pointcloud_draw(OVERLAY_Data *vedata)
-{
- OVERLAY_PassList *psl = vedata->psl;
-
- DRW_draw_pass(psl->pointcloud_ps);
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index ed0a9cf6981..efb9594f499 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;
@@ -148,6 +149,7 @@ typedef struct OVERLAY_ExtraCallBuffers {
DRWCallBuffer *extra_dashed_lines;
DRWCallBuffer *extra_lines;
+ DRWCallBuffer *extra_points;
DRWCallBuffer *field_curve;
DRWCallBuffer *field_force;
@@ -242,6 +244,7 @@ typedef struct OVERLAY_PrivateData {
DRWShadingGroup *motion_path_lines_grp;
DRWShadingGroup *motion_path_points_grp;
DRWShadingGroup *outlines_grp;
+ DRWShadingGroup *outlines_ptcloud_grp;
DRWShadingGroup *outlines_gpencil_grp;
DRWShadingGroup *paint_depth_grp;
DRWShadingGroup *paint_surf_grp;
@@ -264,6 +267,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 +304,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;
@@ -382,6 +389,7 @@ typedef struct OVERLAY_InstanceFormats {
struct GPUVertFormat *pos;
struct GPUVertFormat *pos_color;
struct GPUVertFormat *wire_extra;
+ struct GPUVertFormat *point_extra;
} OVERLAY_InstanceFormats;
/* Pack data into the last row of the 4x4 matrix. It will be decoded by the vertex shader. */
@@ -475,6 +483,7 @@ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_speaker_cache_populate(OVERLAY_Data *vedata, Object *ob);
OVERLAY_ExtraCallBuffers *OVERLAY_extra_call_buffer_get(OVERLAY_Data *vedata, Object *ob);
+void OVERLAY_extra_point(OVERLAY_ExtraCallBuffers *cb, const float point[3], const float color[4]);
void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb,
const float start[3],
const float end[3],
@@ -545,10 +554,6 @@ void OVERLAY_particle_cache_init(OVERLAY_Data *vedata);
void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_particle_draw(OVERLAY_Data *vedata);
-void OVERLAY_pointcloud_cache_init(OVERLAY_Data *vedata);
-void OVERLAY_pointcloud_cache_populate(OVERLAY_Data *vedata, Object *ob);
-void OVERLAY_pointcloud_draw(OVERLAY_Data *vedata);
-
void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata);
void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_sculpt_draw(OVERLAY_Data *vedata);
@@ -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);
@@ -604,6 +610,7 @@ GPUShader *OVERLAY_shader_motion_path_vert(void);
GPUShader *OVERLAY_shader_uniform_color(void);
GPUShader *OVERLAY_shader_outline_prepass(bool use_wire);
GPUShader *OVERLAY_shader_outline_prepass_gpencil(void);
+GPUShader *OVERLAY_shader_outline_prepass_pointcloud(void);
GPUShader *OVERLAY_shader_extra_grid(void);
GPUShader *OVERLAY_shader_outline_detect(void);
GPUShader *OVERLAY_shader_paint_face(void);
@@ -614,7 +621,6 @@ GPUShader *OVERLAY_shader_paint_weight(void);
GPUShader *OVERLAY_shader_paint_wire(void);
GPUShader *OVERLAY_shader_particle_dot(void);
GPUShader *OVERLAY_shader_particle_shape(void);
-GPUShader *OVERLAY_shader_pointcloud_dot(void);
GPUShader *OVERLAY_shader_sculpt_mask(void);
GPUShader *OVERLAY_shader_volume_velocity(bool use_needle);
GPUShader *OVERLAY_shader_wireframe(bool custom_bias);
diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.c b/source/blender/draw/engines/overlay/overlay_sculpt.c
index 111fa6316ed..b36252ead30 100644
--- a/source/blender/draw/engines/overlay/overlay_sculpt.c
+++ b/source/blender/draw/engines/overlay/overlay_sculpt.c
@@ -62,10 +62,11 @@ void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob)
void OVERLAY_sculpt_draw(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (DRW_state_is_fbo()) {
- GPU_framebuffer_bind(dfbl->default_fb);
+ GPU_framebuffer_bind(pd->painting.in_front ? dfbl->in_front_fb : dfbl->default_fb);
}
DRW_draw_pass(psl->sculpt_mask_ps);
diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c
index 0610b8397a1..e3cb052890b 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[];
@@ -102,8 +103,6 @@ extern char datatoc_paint_weight_vert_glsl[];
extern char datatoc_paint_wire_vert_glsl[];
extern char datatoc_particle_vert_glsl[];
extern char datatoc_particle_frag_glsl[];
-extern char datatoc_pointcloud_vert_glsl[];
-extern char datatoc_pointcloud_frag_glsl[];
extern char datatoc_sculpt_mask_vert_glsl[];
extern char datatoc_sculpt_mask_frag_glsl[];
extern char datatoc_volume_velocity_vert_glsl[];
@@ -126,11 +125,13 @@ extern char datatoc_common_fullscreen_vert_glsl[];
extern char datatoc_common_fxaa_lib_glsl[];
extern char datatoc_common_smaa_lib_glsl[];
extern char datatoc_common_globals_lib_glsl[];
+extern char datatoc_common_pointcloud_lib_glsl[];
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;
@@ -179,6 +180,7 @@ typedef struct OVERLAY_Shaders {
GPUShader *motion_path_vert;
GPUShader *outline_prepass;
GPUShader *outline_prepass_gpencil;
+ GPUShader *outline_prepass_pointcloud;
GPUShader *outline_prepass_wire;
GPUShader *outline_detect;
GPUShader *paint_face;
@@ -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)
@@ -1112,6 +1135,33 @@ GPUShader *OVERLAY_shader_outline_prepass_gpencil(void)
return sh_data->outline_prepass_gpencil;
}
+GPUShader *OVERLAY_shader_outline_prepass_pointcloud(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->outline_prepass_pointcloud) {
+ sh_data->outline_prepass_pointcloud = GPU_shader_create_from_arrays({
+ .vert = (const char *[]){sh_cfg->lib,
+ datatoc_common_view_lib_glsl,
+ datatoc_common_pointcloud_lib_glsl,
+ datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_outline_prepass_vert_glsl,
+ NULL},
+ .frag = (const char *[]){datatoc_common_view_lib_glsl,
+ datatoc_gpencil_common_lib_glsl,
+ datatoc_outline_prepass_frag_glsl,
+ NULL},
+ .defs = (const char *[]){sh_cfg->def,
+ "#define POINTCLOUD\n",
+ "#define INSTANCED_ATTR\n",
+ "#define UNIFORM_RESOURCE_ID\n",
+ NULL},
+ });
+ }
+ return sh_data->outline_prepass_pointcloud;
+}
+
GPUShader *OVERLAY_shader_outline_detect(void)
{
OVERLAY_Shaders *sh_data = &e_data.sh_data[0];
@@ -1283,25 +1333,6 @@ GPUShader *OVERLAY_shader_particle_shape(void)
return sh_data->particle_shape;
}
-GPUShader *OVERLAY_shader_pointcloud_dot(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->pointcloud_dot) {
- sh_data->pointcloud_dot = GPU_shader_create_from_arrays({
- .vert = (const char *[]){sh_cfg->lib,
- datatoc_common_globals_lib_glsl,
- datatoc_common_view_lib_glsl,
- datatoc_pointcloud_vert_glsl,
- NULL},
- .frag = (const char *[]){datatoc_pointcloud_frag_glsl, NULL},
- .defs = (const char *[]){sh_cfg->def, "#define USE_DOTS\n", NULL},
- });
- }
- return sh_data->pointcloud_dot;
-}
-
GPUShader *OVERLAY_shader_sculpt_mask(void)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
@@ -1445,6 +1476,11 @@ OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void)
{"pos", DRW_ATTR_FLOAT, 3},
{"colorid", DRW_ATTR_INT, 1},
});
+ DRW_shgroup_instance_format(g_formats.point_extra,
+ {
+ {"pos", DRW_ATTR_FLOAT, 3},
+ {"colorid", DRW_ATTR_INT, 1},
+ });
DRW_shgroup_instance_format(g_formats.instance_bone,
{
{"inst_obmat", DRW_ATTR_FLOAT, 16},
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c
index eebfc88fdce..4129983a901 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;
@@ -101,12 +102,9 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_bool_copy(grp, "isHair", false);
pd->wires_all_grp[xray][use_coloring] = grp = DRW_shgroup_create(wires_sh, pass);
- DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tx);
DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 1.0f);
pd->wires_hair_grp[xray][use_coloring] = grp = DRW_shgroup_create(wires_sh, pass);
- /* TODO(fclem) texture ref persist */
- DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tx);
DRW_shgroup_uniform_bool_copy(grp, "isHair", true);
DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 10.0f);
}
@@ -133,7 +131,7 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
static void wireframe_hair_cache_populate(OVERLAY_Data *vedata, Object *ob, ParticleSystem *psys)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
- const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
Object *dupli_parent = DRW_object_get_dupli_parent(ob);
DupliObject *dupli_object = DRW_object_get_dupli(ob);
@@ -169,7 +167,7 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
OVERLAY_PrivateData *pd = vedata->stl->pd;
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool all_wires = (ob->dtx & OB_DRAW_ALL_EDGES) != 0;
- const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const bool is_mesh = ob->type == OB_MESH;
const bool is_mesh_verts_only = is_mesh && (((Mesh *)ob->data)->totedge == 0 &&
((Mesh *)ob->data)->totvert > 0);
@@ -234,10 +232,15 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
}
}
- if (use_wire && ob->type == OB_VOLUME) {
- /* Volume object as points exception. */
- Volume *volume = ob->data;
- if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) {
+ if (use_wire && ELEM(ob->type, OB_VOLUME, OB_POINTCLOUD)) {
+ bool draw_as_points = true;
+ if (ob->type == OB_VOLUME) {
+ /* Volume object as points exception. */
+ Volume *volume = ob->data;
+ draw_as_points = volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS;
+ }
+
+ if (draw_as_points) {
float *color;
OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob);
DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color);
diff --git a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl
index 0d01f67c6ea..2989e07691f 100644
--- a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl
@@ -1,4 +1,6 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
uniform sampler2D colorTex;
uniform sampler2D depthTex;
uniform sampler2D lineTex;
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/armature_sphere_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl
index 380708795e9..0925901a9c9 100644
--- a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl
@@ -14,19 +14,6 @@ layout(depth_greater) out float gl_FragDepth;
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 lineOutput;
-#define cameraPos ViewMatrixInverse[3].xyz
-
-float get_depth_from_view_z(float z)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- z = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2];
- }
- else {
- z = z * ProjectionMatrix[2][2] / (1.0 - ProjectionMatrix[3][2]);
- }
- return z * 0.5 + 0.5;
-}
-
void main()
{
const float sphere_radius = 0.05;
diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl
index 306fbb473ee..a400aadb052 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl
@@ -52,7 +52,7 @@ void main()
bool edge_selected = (((vertFlag[1] | vertFlag[0]) & VERT_SELECTED) != 0);
bool handle_selected = (showCurveHandles &&
- (((vertFlag[1] | vertFlag[0]) & HANDLE_SELECTED) != 0));
+ (((vertFlag[1] | vertFlag[0]) & VERT_SELECTED_BEZT_HANDLE) != 0));
/* If handle type is only selected and the edge is not selected, don't show. */
if ((curveHandleDisplay != CURVE_HANDLE_ALL) && (!handle_selected)) {
diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl
index b1e1c0879a5..a811fcca0d4 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl
@@ -37,7 +37,7 @@ void main()
#endif
bool show_handle = showCurveHandles;
- if ((curveHandleDisplay == CURVE_HANDLE_SELECTED) && ((data & HANDLE_SELECTED) == 0)) {
+ if ((curveHandleDisplay == CURVE_HANDLE_SELECTED) && ((data & VERT_SELECTED_BEZT_HANDLE) == 0)) {
show_handle = false;
}
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..d0b68df0625 100644
--- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl
@@ -14,8 +14,6 @@ uniform float meshSize;
uniform float lineKernel = 0.0;
uniform sampler2D depthBuffer;
-#define cameraPos (ViewMatrixInverse[3].xyz)
-
uniform int gridFlag;
#define STEPS_LEN 8
@@ -28,7 +26,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 +103,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/overlay/shaders/grid_vert.glsl b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl
index 496bb011c74..dd0e771ad93 100644
--- a/source/blender/draw/engines/overlay/shaders/grid_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl
@@ -7,8 +7,6 @@ uniform float meshSize;
uniform int gridFlag;
-#define cameraPos (ViewMatrixInverse[3].xyz)
-
#define PLANE_XY (1 << 4)
#define PLANE_XZ (1 << 5)
#define PLANE_YZ (1 << 6)
diff --git a/source/blender/draw/engines/overlay/shaders/image_frag.glsl b/source/blender/draw/engines/overlay/shaders/image_frag.glsl
index 298ba1e27ed..4d37f1599a3 100644
--- a/source/blender/draw/engines/overlay/shaders/image_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/image_frag.glsl
@@ -26,4 +26,7 @@ void main()
fragColor.a = 1.0;
}
}
+
+ /* Pre-multiplied blending. */
+ fragColor.rgb *= fragColor.a;
}
diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl
index a2021759196..582a7c6cae2 100644
--- a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl
@@ -1,7 +1,7 @@
uniform bool isTransform;
-#ifndef USE_GPENCIL
+#if !defined(USE_GPENCIL) && !defined(POINTCLOUD)
in vec3 pos;
#endif
@@ -56,7 +56,11 @@ void main()
# endif
#else
+# ifdef POINTCLOUD
+ vec3 world_pos = pointcloud_get_pos();
+# else
vec3 world_pos = point_object_to_world(pos);
+# endif
gl_Position = point_world_to_ndc(world_pos);
# ifdef USE_GEOM
vPos = point_world_to_view(world_pos);
diff --git a/source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl b/source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl
deleted file mode 100644
index 36928d0c776..00000000000
--- a/source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-in vec4 finalColor;
-
-out vec4 fragColor;
-
-void main()
-{
- float dist = length(gl_PointCoord - vec2(0.5));
-
- if (dist > 0.5) {
- discard;
- }
- /* Nice sphere falloff. */
- float intensity = sqrt(1.0 - dist * 2.0) * 0.5 + 0.5;
- fragColor = finalColor * vec4(intensity, intensity, intensity, 1.0);
-}
diff --git a/source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl b/source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl
deleted file mode 100644
index d71ccee5159..00000000000
--- a/source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl
+++ /dev/null
@@ -1,27 +0,0 @@
-
-uniform vec4 color;
-
-/* ---- Per instance Attrs ---- */
-in vec3 pointcloud_pos;
-in vec3 pointcloud_radius;
-
-out vec4 finalColor;
-
-void main()
-{
- vec3 world_pos = point_object_to_world(pointcloud_pos);
-
- vec3 world_size = abs(mat3(ModelMatrix) * vec3(pointcloud_radius));
- float world_radius = (world_size.x + world_size.y + world_size.z) / 3.0;
-
- gl_Position = point_world_to_ndc(world_pos);
- /* World sized points. */
- gl_PointSize = sizePixel * world_radius * ProjectionMatrix[1][1] * sizeViewport.y /
- gl_Position.w;
-
- finalColor = color;
-
-#ifdef USE_WORLD_CLIP_PLANES
- world_clip_planes_calc_clip_distance(world_pos);
-#endif
-}
diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c
index bb7c947a0b9..8cee6c4ee9f 100644
--- a/source/blender/draw/engines/select/select_engine.c
+++ b/source/blender/draw/engines/select/select_engine.c
@@ -378,7 +378,7 @@ RenderEngineType DRW_engine_viewport_select_type = {
NULL,
SELECT_ENGINE,
N_("Select ID"),
- RE_INTERNAL | RE_USE_STEREO_VIEWPORT,
+ RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
NULL,
NULL,
NULL,
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..d0d52c8485b 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl
@@ -23,12 +23,12 @@ 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;
}
- vec3 position = view_position_from_depth(screenco, depth, world_data.viewvecs, ProjectionMatrix);
+ vec3 position = get_view_space_from_depth(screenco, depth);
vec3 normal = workbench_normal_decode(texture(normalBuffer, screenco));
vec2 jitter_co = (screenco * world_data.viewport_size.xy) * world_data.cavity_jitter_scale;
@@ -68,8 +68,7 @@ void cavity_compute(vec2 screenco,
bool is_background = (s_depth == 1.0);
/* This trick provide good edge effect even if no neighbor is found. */
s_depth = (is_background) ? depth : s_depth;
- vec3 s_pos = view_position_from_depth(
- uvcoords, s_depth, world_data.viewvecs, ProjectionMatrix);
+ vec3 s_pos = get_view_space_from_depth(uvcoords, s_depth);
if (is_background) {
s_pos.z -= world_data.cavity_distance;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
index 25eaf003e07..eb61edca6c7 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
@@ -48,7 +48,7 @@ float workbench_float_pair_encode(float v1, float v2)
{
// const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS);
// const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS);
- /* Same as above because some compiler are dumb af. and think we use mediump int. */
+ /* Same as above because some compiler are very dumb and think we use medium int. */
const int v1_mask = 0x1F;
const int v2_mask = 0x7;
int iv1 = int(v1 * float(v1_mask));
@@ -60,38 +60,10 @@ void workbench_float_pair_decode(float data, out float v1, out float v2)
{
// const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS);
// const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS);
- /* Same as above because some compiler are dumb af. and think we use mediump int. */
+ /* Same as above because some compiler are very dumb and think we use medium int. */
const int v1_mask = 0x1F;
const int v2_mask = 0x7;
int idata = int(data);
v1 = float(idata & v1_mask) * (1.0 / float(v1_mask));
v2 = float(idata >> int(ROUGHNESS_BITS)) * (1.0 / float(v2_mask));
}
-
-vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvecs[3], mat4 proj_mat)
-{
- if (proj_mat[3][3] == 0.0) {
- return normalize(viewvecs[0].xyz + vec3(uv, 0.0) * viewvecs[1].xyz);
- }
- else {
- return vec3(0.0, 0.0, 1.0);
- }
-}
-
-vec3 view_position_from_depth(vec2 uvcoords, float depth, vec4 viewvecs[3], mat4 proj_mat)
-{
- if (proj_mat[3][3] == 0.0) {
- /* Perspective */
- float d = 2.0 * depth - 1.0;
-
- float zview = -proj_mat[3][2] / (d + proj_mat[2][2]);
-
- return zview * (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz);
- }
- else {
- /* Orthographic */
- vec3 offset = vec3(uvcoords, depth);
-
- return viewvecs[0].xyz + offset * viewvecs[1].xyz;
- }
-}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl
index cdb9823096c..6e10a656fc1 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl
@@ -14,7 +14,7 @@ out vec4 fragColor;
void main()
{
/* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */
- vec3 I = view_vector_from_screen_uv(uvcoordsvar.st, world_data.viewvecs, ProjectionMatrix);
+ vec3 I = get_view_vector_from_screen_uv(uvcoordsvar.st);
vec3 N = workbench_normal_decode(texture(normalBuffer, uvcoordsvar.st));
vec4 mat_data = texture(materialBuffer, uvcoordsvar.st);
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl
index 5f3283e1643..a76a14fa750 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl
@@ -5,7 +5,6 @@ struct LightData {
};
struct WorldData {
- vec4 viewvecs[3];
vec4 viewport_size;
vec4 object_outline_color;
vec4 shadow_direction_vs;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
index 51007a9f246..71816f6ff6e 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl
@@ -1,3 +1,6 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
/**
* Separable Hexagonal Bokeh Blur by Colin Barré-Brisebois
* https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited-part-1-basic-3-pass-version/
@@ -21,13 +24,6 @@ uniform sampler2D noiseTex;
#define dof_distance dofParams.y
#define dof_invsensorsize dofParams.z
-#define M_PI 3.1415926535897932 /* pi */
-
-float max_v4(vec4 v)
-{
- return max(max(v.x, v.y), max(v.z, v.w));
-}
-
#define weighted_sum(a, b, c, d, e, e_sum) \
((a)*e.x + (b)*e.y + (c)*e.z + (d)*e.w) / max(1e-6, e_sum);
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
new file mode 100644
index 00000000000..8e5c8a1b21f
--- /dev/null
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
@@ -0,0 +1,38 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl)
+#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl)
+#pragma BLENDER_REQUIRE(workbench_common_lib.glsl)
+#pragma BLENDER_REQUIRE(workbench_material_lib.glsl)
+#pragma BLENDER_REQUIRE(workbench_image_lib.glsl)
+
+void main()
+{
+ vec3 world_pos;
+ pointcloud_get_pos_and_nor(world_pos, normal_interp);
+
+ normal_interp = normalize(normal_world_to_view(normal_interp));
+
+ gl_Position = point_world_to_ndc(world_pos);
+
+#ifdef USE_WORLD_CLIP_PLANES
+ world_clip_planes_calc_clip_distance(world_pos);
+#endif
+
+ uv_interp = vec2(0.0);
+
+#ifdef OPAQUE_MATERIAL
+ float metallic, roughness;
+#endif
+ workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic);
+
+ if (materialIndex == 0) {
+ color_interp = vec3(1.0);
+ }
+
+#ifdef OPAQUE_MATERIAL
+ packed_rough_metal = workbench_float_pair_encode(roughness, metallic);
+#endif
+
+ object_id = int((uint(resource_id) + 1u) & 0xFFu);
+}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl
index 8e2f7ba4735..6bfa351aeb0 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl
@@ -1,10 +1,4 @@
-#ifdef GPU_VERTEX_SHADER
-# define IN_OUT out
-#else
-# define IN_OUT in
-#endif
-
IN_OUT ShaderStageInterface
{
vec3 normal_interp;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl
index 3c2d1a9c0c7..fd4d00d96dd 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl
@@ -15,7 +15,7 @@ layout(location = 1) out vec4 revealageAccum;
layout(location = 2) out uint objectId;
/* Special function only to be used with calculate_transparent_weight(). */
-float linear_zdepth(float depth, vec4 viewvecs[3], mat4 proj_mat)
+float linear_zdepth(float depth, vec4 viewvecs[2], mat4 proj_mat)
{
if (proj_mat[3][3] == 0.0) {
float d = 2.0 * depth - 1.0;
@@ -33,7 +33,7 @@ float linear_zdepth(float depth, vec4 viewvecs[3], mat4 proj_mat)
*/
float calculate_transparent_weight(void)
{
- float z = linear_zdepth(gl_FragCoord.z, world_data.viewvecs, ProjectionMatrix);
+ float z = linear_zdepth(gl_FragCoord.z, ViewVecs, ProjectionMatrix);
#if 0
/* Eq 10 : Good for surfaces with varying opacity (like particles) */
float a = min(1.0, alpha * 10.0) + 0.01;
@@ -57,7 +57,7 @@ void main()
{
/* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */
vec2 uv_viewport = gl_FragCoord.xy * world_data.viewport_size_inv;
- vec3 I = view_vector_from_screen_uv(uv_viewport, world_data.viewvecs, ProjectionMatrix);
+ vec3 I = get_view_vector_from_screen_uv(uv_viewport);
vec3 N = normalize(normal_interp);
vec3 color = color_interp;
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..aa938d80fa3 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
@@ -1,4 +1,5 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl)
#pragma BLENDER_REQUIRE(workbench_data_lib.glsl)
@@ -33,11 +34,6 @@ float phase_function_isotropic()
return 1.0 / (4.0 * M_PI);
}
-float max_v3(vec3 v)
-{
- return max(v.x, max(v.y, v.z));
-}
-
float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
{
/* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
@@ -107,7 +103,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 +122,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
}
@@ -195,10 +190,8 @@ void main()
float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
float depth_end = min(depth, gl_FragCoord.z);
- vec3 vs_ray_end = view_position_from_depth(
- screen_uv, depth_end, world_data.viewvecs, ProjectionMatrix);
- vec3 vs_ray_ori = view_position_from_depth(
- screen_uv, 0.0, world_data.viewvecs, ProjectionMatrix);
+ vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end);
+ vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0);
vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0);
vs_ray_dir /= abs(vs_ray_dir.z);
diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c
index 7b08e97ac31..0d7f4ee660b 100644
--- a/source/blender/draw/engines/workbench/workbench_data.c
+++ b/source/blender/draw/engines/workbench/workbench_data.c
@@ -86,43 +86,6 @@ static WORKBENCH_ViewLayerData *workbench_view_layer_data_ensure_ex(struct ViewL
/* \} */
-static void workbench_viewvecs_update(float r_viewvecs[3][4])
-{
- float invproj[4][4];
- const bool is_persp = DRW_view_is_persp_get(NULL);
- DRW_view_winmat_get(NULL, invproj, true);
-
- /* view vectors for the corners of the view frustum.
- * Can be used to recreate the world space position easily */
- copy_v4_fl4(r_viewvecs[0], -1.0f, -1.0f, -1.0f, 1.0f);
- copy_v4_fl4(r_viewvecs[1], 1.0f, -1.0f, -1.0f, 1.0f);
- copy_v4_fl4(r_viewvecs[2], -1.0f, 1.0f, -1.0f, 1.0f);
-
- /* convert the view vectors to view space */
- for (int i = 0; i < 3; i++) {
- mul_m4_v4(invproj, r_viewvecs[i]);
- /* normalized trick see:
- * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
- mul_v3_fl(r_viewvecs[i], 1.0f / r_viewvecs[i][3]);
- if (is_persp) {
- mul_v3_fl(r_viewvecs[i], 1.0f / r_viewvecs[i][2]);
- }
- r_viewvecs[i][3] = 1.0;
- }
-
- /* we need to store the differences */
- r_viewvecs[1][0] -= r_viewvecs[0][0];
- r_viewvecs[1][1] = r_viewvecs[2][1] - r_viewvecs[0][1];
-
- /* calculate a depth offset as well */
- if (!is_persp) {
- float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f};
- mul_m4_v4(invproj, vec_far);
- mul_v3_fl(vec_far, 1.0f / vec_far[3]);
- r_viewvecs[1][2] = vec_far[2] - r_viewvecs[0][2];
- }
-}
-
static void workbench_studiolight_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd)
{
StudioLight *studiolight = wpd->studio_light;
@@ -256,6 +219,8 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd)
}
else if (XRAY_ENABLED(v3d)) {
wpd->shading.xray_alpha = XRAY_ALPHA(v3d);
+ /* Disable shading options that aren't supported in transparency mode. */
+ wpd->shading.flag &= ~(V3D_SHADING_SHADOW | V3D_SHADING_CAVITY | V3D_SHADING_DEPTH_OF_FIELD);
}
else {
wpd->shading.xray_alpha = 1.0f;
@@ -309,7 +274,6 @@ void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd)
workbench_studiolight_data_update(wpd, &wd);
workbench_shadow_data_update(wpd, &wd);
workbench_cavity_data_update(wpd, &wd);
- workbench_viewvecs_update(wd.viewvecs);
DRW_uniformbuffer_update(wpd->world_ubo, &wd);
}
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 828a9127fb1..53119723fab 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -130,6 +130,17 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd,
}
}
+BLI_INLINE void workbench_object_drawcall(DRWShadingGroup *grp, struct GPUBatch *geom, Object *ob)
+{
+ if (ob->type == OB_POINTCLOUD) {
+ /* Draw range to avoid drawcall batching messing up the instance attrib. */
+ DRW_shgroup_call_instance_range(grp, ob, geom, 0, 0);
+ }
+ else {
+ DRW_shgroup_call(grp, geom, ob);
+ }
+}
+
static void workbench_cache_texpaint_populate(WORKBENCH_PrivateData *wpd, Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
@@ -145,7 +156,7 @@ static void workbench_cache_texpaint_populate(WORKBENCH_PrivateData *wpd, Object
SET_FLAG_FROM_TEST(state, imapaint->interp == IMAGEPAINT_INTERP_LINEAR, GPU_SAMPLER_FILTER);
DRWShadingGroup *grp = workbench_image_setup(wpd, ob, 0, ima, NULL, state);
- DRW_shgroup_call(grp, geom, ob);
+ workbench_object_drawcall(grp, geom, ob);
}
}
else {
@@ -157,7 +168,7 @@ static void workbench_cache_texpaint_populate(WORKBENCH_PrivateData *wpd, Object
continue;
}
DRWShadingGroup *grp = workbench_image_setup(wpd, ob, i + 1, NULL, NULL, 0);
- DRW_shgroup_call(grp, geoms[i], ob);
+ workbench_object_drawcall(grp, geoms[i], ob);
}
}
}
@@ -174,11 +185,27 @@ 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 {
+ if (U.experimental.use_sculpt_vertex_colors) {
+ geom = DRW_cache_mesh_surface_sculptcolors_get(ob);
+ }
+ else {
+ geom = DRW_cache_mesh_surface_vertpaint_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);
+ workbench_object_drawcall(grp, geom, ob);
}
}
else {
@@ -191,7 +218,7 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd,
continue;
}
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, i + 1, color_type, r_transp);
- DRW_shgroup_call(grp, geoms[i], ob);
+ workbench_object_drawcall(grp, geoms[i], ob);
}
}
}
@@ -240,15 +267,26 @@ 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 (U.experimental.use_sculpt_vertex_colors) {
+ if ((me == NULL) || !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
+ color_type = V3D_SHADING_OBJECT_COLOR;
+ }
+ }
+ else {
+ if ((me == NULL) || !CustomData_has_layer(&me->ldata, CD_MLOOPCOL)) {
+ color_type = V3D_SHADING_OBJECT_COLOR;
+ }
+ }
}
if (r_sculpt_pbvh) {
@@ -379,7 +417,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();
@@ -423,7 +461,7 @@ void workbench_cache_finish(void *ved)
/* TODO don't free reuse next redraw. */
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
- for (int k = 0; k < 2; k++) {
+ for (int k = 0; k < WORKBENCH_DATATYPE_MAX; k++) {
if (wpd->prepass[i][j][k].material_hash) {
BLI_ghash_free(wpd->prepass[i][j][k].material_hash, NULL, NULL);
wpd->prepass[i][j][k].material_hash = NULL;
@@ -603,7 +641,7 @@ RenderEngineType DRW_engine_viewport_workbench_type = {
NULL,
WORKBENCH_ENGINE,
N_("Workbench"),
- RE_INTERNAL | RE_USE_STEREO_VIEWPORT,
+ RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
NULL,
&DRW_render_to_image,
NULL,
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 0b2d508fee5..824c3dc5ff1 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};
@@ -162,13 +162,13 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
int mat_nr,
eV3DShadingColorType color_type,
- bool hair,
+ eWORKBENCH_DataType datatype,
bool *r_transp)
{
Image *ima = NULL;
ImageUser *iuser = NULL;
eGPUSamplerState sampler;
- const bool infront = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool infront = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
if (color_type == V3D_SHADING_TEXTURE_COLOR) {
workbench_material_get_image(ob, mat_nr, &ima, &iuser, &sampler);
@@ -180,7 +180,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
switch (color_type) {
case V3D_SHADING_TEXTURE_COLOR: {
- return workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, sampler, hair);
+ return workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, sampler, datatype);
}
case V3D_SHADING_MATERIAL_COLOR: {
/* For now, we use the same ubo for material and object coloring but with different indices.
@@ -191,7 +191,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
Material *ma = workbench_object_material_get(ob, mat_nr);
const bool transp = wpd->shading.xray_alpha < 1.0f || ma->a < 1.0f;
- WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][hair];
+ WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][datatype];
if (r_transp && transp) {
*r_transp = true;
@@ -216,7 +216,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
}
case V3D_SHADING_VERTEX_COLOR: {
const bool transp = wpd->shading.xray_alpha < 1.0f;
- DRWShadingGroup *grp = wpd->prepass[transp][infront][hair].vcol_shgrp;
+ DRWShadingGroup *grp = wpd->prepass[transp][infront][datatype].vcol_shgrp;
return grp;
}
default: {
@@ -231,7 +231,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
workbench_material_ubo_data(wpd, ob, NULL, &wpd->material_ubo_data_curr[mat_id], color_type);
const bool transp = wpd->shading.xray_alpha < 1.0f || ob->color[3] < 1.0f;
- DRWShadingGroup *grp = wpd->prepass[transp][infront][hair].common_shgrp;
+ DRWShadingGroup *grp = wpd->prepass[transp][infront][datatype].common_shgrp;
if (resource_changed) {
grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
@@ -251,7 +251,7 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
Image *ima,
ImageUser *iuser,
eGPUSamplerState sampler,
- bool hair)
+ eWORKBENCH_DataType datatype)
{
GPUTexture *tex = NULL, *tex_tile_data = NULL;
@@ -261,11 +261,11 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
if (ima) {
if (ima->source == IMA_SRC_TILED) {
- tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D_ARRAY);
- tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_1D_ARRAY);
+ tex = BKE_image_get_gpu_tiles(ima, iuser, NULL);
+ tex_tile_data = BKE_image_get_gpu_tilemap(ima, iuser, NULL);
}
else {
- tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D);
+ tex = BKE_image_get_gpu_texture(ima, iuser, NULL);
}
}
@@ -273,9 +273,9 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
tex = wpd->dummy_image_tx;
}
- const bool infront = (ob->dtx & OB_DRAWXRAY) != 0;
+ const bool infront = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const bool transp = wpd->shading.xray_alpha < 1.0f;
- WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][hair];
+ WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][datatype];
DRWShadingGroup **grp_tex = NULL;
/* A hashmap stores image shgroups to pack all similar drawcalls together. */
diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c
index 27d5b71f35c..738f4a67471 100644
--- a/source/blender/draw/engines/workbench/workbench_opaque.c
+++ b/source/blender/draw/engines/workbench/workbench_opaque.c
@@ -59,10 +59,10 @@ void workbench_opaque_engine_init(WORKBENCH_Data *data)
});
}
-void workbench_opaque_cache_init(WORKBENCH_Data *data)
+void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
{
- WORKBENCH_PassList *psl = data->psl;
- WORKBENCH_PrivateData *wpd = data->stl->wpd;
+ WORKBENCH_PassList *psl = vedata->psl;
+ WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
struct GPUShader *sh;
DRWShadingGroup *grp;
@@ -84,31 +84,31 @@ void workbench_opaque_cache_init(WORKBENCH_Data *data)
pass = psl->opaque_ps;
}
- for (int hair = 0; hair < 2; hair++) {
- wpd->prepass[opaque][infront][hair].material_hash = BLI_ghash_ptr_new(__func__);
+ for (eWORKBENCH_DataType data = 0; data < WORKBENCH_DATATYPE_MAX; data++) {
+ wpd->prepass[opaque][infront][data].material_hash = BLI_ghash_ptr_new(__func__);
- sh = workbench_shader_opaque_get(wpd, hair);
+ sh = workbench_shader_opaque_get(wpd, data);
- wpd->prepass[opaque][infront][hair].common_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
- wpd->prepass[opaque][infront][hair].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
- sh = workbench_shader_opaque_image_get(wpd, hair, false);
+ sh = workbench_shader_opaque_image_get(wpd, data, false);
- wpd->prepass[opaque][infront][hair].image_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
- sh = workbench_shader_opaque_image_get(wpd, hair, true);
+ sh = workbench_shader_opaque_image_get(wpd, data, true);
- wpd->prepass[opaque][infront][hair].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index 20b6d368ac0..244ac695e78 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -71,6 +71,14 @@ struct RenderEngine;
struct RenderLayer;
struct rcti;
+typedef enum eWORKBENCH_DataType {
+ WORKBENCH_DATATYPE_MESH = 0,
+ WORKBENCH_DATATYPE_HAIR,
+ WORKBENCH_DATATYPE_POINTCLOUD,
+
+ WORKBENCH_DATATYPE_MAX,
+} eWORKBENCH_DataType;
+
typedef struct WORKBENCH_FramebufferList {
struct GPUFrameBuffer *opaque_fb;
struct GPUFrameBuffer *opaque_infront_fb;
@@ -168,7 +176,6 @@ typedef struct WORKBENCH_UBO_Material {
} WORKBENCH_UBO_Material;
typedef struct WORKBENCH_UBO_World {
- float viewvecs[3][4];
float viewport_size[2], viewport_size_inv[2];
float object_outline_color[4];
float shadow_direction_vs[4];
@@ -293,8 +300,8 @@ typedef struct WORKBENCH_PrivateData {
/** Object IDs buffer for curvature & outline. */
struct GPUTexture *object_id_tx;
- /** Prepass infos for each draw types [transparent][infront][hair]. */
- WORKBENCH_Prepass prepass[2][2][2];
+ /** Pre-pass information for each draw types [transparent][infront][datatype]. */
+ WORKBENCH_Prepass prepass[2][2][WORKBENCH_DATATYPE_MAX];
/* Materials */
/** Copy of vldata->material_ubo for faster access. */
@@ -393,14 +400,16 @@ void workbench_shadow_cache_init(WORKBENCH_Data *data);
void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat);
/* workbench_shader.c */
-GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, bool hair);
-GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, bool hair, bool tiled);
+GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType data);
+GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd,
+ eWORKBENCH_DataType data,
+ bool tiled);
GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd);
GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *wpd);
-GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, bool hair);
+GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType data);
GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd,
- bool hair,
+ eWORKBENCH_DataType data,
bool tiled);
GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd);
@@ -455,7 +464,7 @@ DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
int mat_nr,
eV3DShadingColorType color_type,
- bool hair,
+ eWORKBENCH_DataType datatype,
bool *r_transp);
DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
Object *ob,
@@ -463,17 +472,20 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
Image *ima,
ImageUser *iuser,
eGPUSamplerState sampler,
- bool hair);
+ eWORKBENCH_DataType datatype);
+
+#define WORKBENCH_OBJECT_DATATYPE(ob) \
+ ((ob->type == OB_POINTCLOUD) ? WORKBENCH_DATATYPE_POINTCLOUD : WORKBENCH_DATATYPE_MESH)
#define workbench_material_setup(wpd, ob, mat_nr, color_type, r_transp) \
- workbench_material_setup_ex(wpd, ob, mat_nr, color_type, false, r_transp)
+ workbench_material_setup_ex(wpd, ob, mat_nr, color_type, WORKBENCH_OBJECT_DATATYPE(ob), r_transp)
#define workbench_image_setup(wpd, ob, mat_nr, ima, iuser, interp) \
- workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, false)
+ workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, WORKBENCH_OBJECT_DATATYPE(ob))
#define workbench_material_hair_setup(wpd, ob, mat_nr, color_type) \
- workbench_material_setup_ex(wpd, ob, mat_nr, color_type, true, 0)
+ workbench_material_setup_ex(wpd, ob, mat_nr, color_type, WORKBENCH_DATATYPE_HAIR, 0)
#define workbench_image_hair_setup(wpd, ob, mat_nr, ima, iuser, interp) \
- workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, true)
+ workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, WORKBENCH_DATATYPE_HAIR)
/* workbench_data.c */
void workbench_private_data_init(WORKBENCH_PrivateData *wpd);
diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c
index 9e66bcb07f4..77e16327a43 100644
--- a/source/blender/draw/engines/workbench/workbench_render.c
+++ b/source/blender/draw/engines/workbench/workbench_render.c
@@ -212,6 +212,7 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer
BLI_rcti_size_y(rect),
4,
0,
+ GPU_DATA_FLOAT,
rp->rect);
workbench_render_result_z(render_layer, viewname, rect);
diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c
index 99366779b22..aab3cef00e6 100644
--- a/source/blender/draw/engines/workbench/workbench_shader.c
+++ b/source/blender/draw/engines/workbench/workbench_shader.c
@@ -28,12 +28,16 @@
#include "workbench_engine.h"
#include "workbench_private.h"
+extern char datatoc_common_math_lib_glsl[];
+extern char datatoc_common_math_geom_lib_glsl[];
extern char datatoc_common_hair_lib_glsl[];
+extern char datatoc_common_pointcloud_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_common_smaa_lib_glsl[];
extern char datatoc_workbench_prepass_vert_glsl[];
extern char datatoc_workbench_prepass_hair_vert_glsl[];
+extern char datatoc_workbench_prepass_pointcloud_vert_glsl[];
extern char datatoc_workbench_prepass_frag_glsl[];
extern char datatoc_workbench_effect_cavity_frag_glsl[];
@@ -74,7 +78,6 @@ extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
/* Maximum number of variations. */
#define MAX_LIGHTING 3
#define MAX_COLOR 3
-#define MAX_GEOM 2
enum {
VOLUME_SH_SLICE = 0,
@@ -85,8 +88,9 @@ enum {
#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1))
static struct {
- struct GPUShader *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][MAX_GEOM][MAX_COLOR];
- struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][MAX_GEOM][MAX_LIGHTING][MAX_COLOR];
+ struct GPUShader *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX][MAX_COLOR];
+ struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][WORKBENCH_DATATYPE_MAX]
+ [MAX_LIGHTING][MAX_COLOR];
struct GPUShader *opaque_composite_sh[MAX_LIGHTING];
struct GPUShader *oit_resolve_sh;
@@ -117,8 +121,11 @@ void workbench_shader_library_ensure(void)
if (e_data.lib == NULL) {
e_data.lib = DRW_shader_library_create();
/* NOTE: Theses needs to be ordered by dependencies. */
+ DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib);
DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib);
DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, common_pointcloud_lib);
DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_shader_interface_lib);
DRW_SHADER_LIB_ADD(e_data.lib, workbench_common_lib);
@@ -177,15 +184,18 @@ static int workbench_color_index(WORKBENCH_PrivateData *UNUSED(wpd), bool textur
return (textured) ? (tiled ? 2 : 1) : 0;
}
-static GPUShader *workbench_shader_get_ex(
- WORKBENCH_PrivateData *wpd, bool transp, bool hair, bool textured, bool tiled)
+static GPUShader *workbench_shader_get_ex(WORKBENCH_PrivateData *wpd,
+ bool transp,
+ eWORKBENCH_DataType datatype,
+ bool textured,
+ bool tiled)
{
int color = workbench_color_index(wpd, textured, tiled);
int light = wpd->shading.light;
BLI_assert(light < MAX_LIGHTING);
struct GPUShader **shader =
- (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][hair][light][color] :
- &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][hair][color];
+ (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][datatype][light][color] :
+ &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][datatype][color];
if (*shader == NULL) {
char *defines = workbench_build_defines(wpd, textured, tiled, false, false);
@@ -194,8 +204,11 @@ static GPUShader *workbench_shader_get_ex(
datatoc_workbench_prepass_frag_glsl;
char *frag_src = DRW_shader_library_create_shader_string(e_data.lib, frag_file);
- char *vert_file = hair ? datatoc_workbench_prepass_hair_vert_glsl :
- datatoc_workbench_prepass_vert_glsl;
+ char *vert_file = (datatype == WORKBENCH_DATATYPE_HAIR) ?
+ datatoc_workbench_prepass_hair_vert_glsl :
+ ((datatype == WORKBENCH_DATATYPE_POINTCLOUD) ?
+ datatoc_workbench_prepass_pointcloud_vert_glsl :
+ datatoc_workbench_prepass_vert_glsl);
char *vert_src = DRW_shader_library_create_shader_string(e_data.lib, vert_file);
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[wpd->sh_cfg];
@@ -207,6 +220,10 @@ static GPUShader *workbench_shader_get_ex(
defines,
transp ? "#define TRANSPARENT_MATERIAL\n" :
"#define OPAQUE_MATERIAL\n",
+ (datatype == WORKBENCH_DATATYPE_POINTCLOUD) ?
+ "#define UNIFORM_RESOURCE_ID\n"
+ "#define INSTANCED_ATTR\n" :
+ NULL,
NULL},
});
@@ -217,26 +234,29 @@ static GPUShader *workbench_shader_get_ex(
return *shader;
}
-GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, bool hair)
+GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, eWORKBENCH_DataType datatype)
{
- return workbench_shader_get_ex(wpd, false, hair, false, false);
+ return workbench_shader_get_ex(wpd, false, datatype, false, false);
}
-GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, bool hair, bool tiled)
+GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd,
+ eWORKBENCH_DataType datatype,
+ bool tiled)
{
- return workbench_shader_get_ex(wpd, false, hair, true, tiled);
+ return workbench_shader_get_ex(wpd, false, datatype, true, tiled);
}
-GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, bool hair)
+GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd,
+ eWORKBENCH_DataType datatype)
{
- return workbench_shader_get_ex(wpd, true, hair, false, false);
+ return workbench_shader_get_ex(wpd, true, datatype, false, false);
}
GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd,
- bool hair,
+ eWORKBENCH_DataType datatype,
bool tiled)
{
- return workbench_shader_get_ex(wpd, true, hair, true, tiled);
+ return workbench_shader_get_ex(wpd, true, datatype, true, tiled);
}
GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd)
diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c
index 5fd8229304a..5eff056846c 100644
--- a/source/blender/draw/engines/workbench/workbench_transparent.c
+++ b/source/blender/draw/engines/workbench/workbench_transparent.c
@@ -83,10 +83,10 @@ static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd,
}
}
-void workbench_transparent_cache_init(WORKBENCH_Data *data)
+void workbench_transparent_cache_init(WORKBENCH_Data *vedata)
{
- WORKBENCH_PassList *psl = data->psl;
- WORKBENCH_PrivateData *wpd = data->stl->wpd;
+ WORKBENCH_PassList *psl = vedata->psl;
+ WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
struct GPUShader *sh;
DRWShadingGroup *grp;
@@ -105,30 +105,30 @@ void workbench_transparent_cache_init(WORKBENCH_Data *data)
pass = psl->transp_accum_ps;
}
- for (int hair = 0; hair < 2; hair++) {
- wpd->prepass[transp][infront][hair].material_hash = BLI_ghash_ptr_new(__func__);
+ for (eWORKBENCH_DataType data = 0; data < WORKBENCH_DATATYPE_MAX; data++) {
+ wpd->prepass[transp][infront][data].material_hash = BLI_ghash_ptr_new(__func__);
- sh = workbench_shader_transparent_get(wpd, hair);
+ sh = workbench_shader_transparent_get(wpd, data);
- wpd->prepass[transp][infront][hair].common_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[transp][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
workbench_transparent_lighting_uniforms(wpd, grp);
- wpd->prepass[transp][infront][hair].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[transp][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
- sh = workbench_shader_transparent_image_get(wpd, hair, false);
+ sh = workbench_shader_transparent_image_get(wpd, data, false);
- wpd->prepass[transp][infront][hair].image_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[transp][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
workbench_transparent_lighting_uniforms(wpd, grp);
- sh = workbench_shader_transparent_image_get(wpd, hair, true);
+ sh = workbench_shader_transparent_image_get(wpd, data, true);
- wpd->prepass[transp][infront][hair].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
+ wpd->prepass[transp][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr);
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
workbench_transparent_lighting_uniforms(wpd, grp);
@@ -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/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c
index 21cb567aaae..d3c4d51dbd4 100644
--- a/source/blender/draw/engines/workbench/workbench_volume.c
+++ b/source/blender/draw/engines/workbench/workbench_volume.c
@@ -38,8 +38,6 @@
#include "BKE_volume.h"
#include "BKE_volume_render.h"
-#include "GPU_draw.h"
-
void workbench_volume_engine_init(WORKBENCH_Data *vedata)
{
WORKBENCH_TextureList *txl = vedata->txl;
@@ -65,53 +63,50 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
Object *ob,
ModifierData *md)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
- FluidDomainSettings *mds = mmd->domain;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ FluidDomainSettings *fds = fmd->domain;
WORKBENCH_PrivateData *wpd = vedata->stl->wpd;
WORKBENCH_TextureList *txl = vedata->txl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRWShadingGroup *grp = NULL;
/* Don't try to show liquid domains here */
- if (!mds->fluid || !(mds->type == FLUID_DOMAIN_TYPE_GAS)) {
+ if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
return;
}
wpd->volumes_do = true;
- if (mds->use_coba) {
- GPU_create_smoke_coba_field(mmd);
- }
- else if (!(mds->flags & FLUID_DOMAIN_USE_NOISE)) {
- GPU_create_smoke(mmd, 0);
+ if (fds->use_coba) {
+ DRW_smoke_ensure_coba_field(fmd);
}
- else if (mds->flags & FLUID_DOMAIN_USE_NOISE) {
- GPU_create_smoke(mmd, 1);
+ else {
+ DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
}
- if ((!mds->use_coba && (mds->tex_density == NULL && mds->tex_color == NULL)) ||
- (mds->use_coba && mds->tex_field == NULL)) {
+ if ((!fds->use_coba && (fds->tex_density == NULL && fds->tex_color == NULL)) ||
+ (fds->use_coba && fds->tex_field == NULL)) {
return;
}
- const bool use_slice = (mds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED &&
- mds->axis_slice_method == AXIS_SLICE_SINGLE);
- const bool cubic_interp = (mds->interp_method == VOLUME_INTERP_CUBIC);
- GPUShader *sh = workbench_shader_volume_get(use_slice, mds->use_coba, cubic_interp, true);
+ const bool use_slice = (fds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED &&
+ fds->axis_slice_method == AXIS_SLICE_SINGLE);
+ const bool cubic_interp = (fds->interp_method == VOLUME_INTERP_CUBIC);
+ GPUShader *sh = workbench_shader_volume_get(use_slice, fds->use_coba, cubic_interp, true);
if (use_slice) {
float invviewmat[4][4];
DRW_view_viewmat_get(NULL, invviewmat, true);
- const int axis = (mds->slice_axis == SLICE_AXIS_AUTO) ?
+ const int axis = (fds->slice_axis == SLICE_AXIS_AUTO) ?
axis_dominant_v3_single(invviewmat[2]) :
- mds->slice_axis - 1;
+ fds->slice_axis - 1;
float dim[3];
BKE_object_dimensions_get(ob, dim);
/* 0.05f to achieve somewhat the same opacity as the full view. */
float step_length = max_ff(1e-16f, dim[axis] * 0.05f);
grp = DRW_shgroup_create(sh, vedata->psl->volume_ps);
- DRW_shgroup_uniform_float_copy(grp, "slicePosition", mds->slice_depth);
+ DRW_shgroup_uniform_float_copy(grp, "slicePosition", fds->slice_depth);
DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis);
DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length);
DRW_shgroup_state_disable(grp, DRW_STATE_CULL_FRONT);
@@ -120,8 +115,8 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
double noise_ofs;
BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs);
float dim[3], step_length, max_slice;
- float slice_ct[3] = {mds->res[0], mds->res[1], mds->res[2]};
- mul_v3_fl(slice_ct, max_ff(0.001f, mds->slice_per_voxel));
+ float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]};
+ mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel));
max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]);
BKE_object_dimensions_get(ob, dim);
invert_v3(slice_ct);
@@ -136,26 +131,26 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
DRW_shgroup_state_enable(grp, DRW_STATE_CULL_FRONT);
}
- if (mds->use_coba) {
- DRW_shgroup_uniform_texture(grp, "densityTexture", mds->tex_field);
- DRW_shgroup_uniform_texture(grp, "transferTexture", mds->tex_coba);
+ if (fds->use_coba) {
+ DRW_shgroup_uniform_texture(grp, "densityTexture", fds->tex_field);
+ DRW_shgroup_uniform_texture(grp, "transferTexture", fds->tex_coba);
}
else {
static float white[3] = {1.0f, 1.0f, 1.0f};
- bool use_constant_color = ((mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
- (mds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
+ bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
+ (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
DRW_shgroup_uniform_texture(
- grp, "densityTexture", (mds->tex_color) ? mds->tex_color : mds->tex_density);
- DRW_shgroup_uniform_texture(grp, "shadowTexture", mds->tex_shadow);
+ grp, "densityTexture", (fds->tex_color) ? fds->tex_color : fds->tex_density);
+ DRW_shgroup_uniform_texture(grp, "shadowTexture", fds->tex_shadow);
DRW_shgroup_uniform_texture(
- grp, "flameTexture", (mds->tex_flame) ? mds->tex_flame : txl->dummy_volume_tx);
+ grp, "flameTexture", (fds->tex_flame) ? fds->tex_flame : txl->dummy_volume_tx);
DRW_shgroup_uniform_texture(
- grp, "flameColorTexture", (mds->tex_flame) ? mds->tex_flame_coba : txl->dummy_coba_tx);
+ grp, "flameColorTexture", (fds->tex_flame) ? fds->tex_flame_coba : txl->dummy_coba_tx);
DRW_shgroup_uniform_vec3(
- grp, "activeColor", (use_constant_color) ? mds->active_color : white, 1);
+ grp, "activeColor", (use_constant_color) ? fds->active_color : white, 1);
}
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_uniform_float_copy(grp, "densityScale", 10.0f * mds->display_thickness);
+ DRW_shgroup_uniform_float_copy(grp, "densityScale", 10.0f * fds->display_thickness);
if (use_slice) {
DRW_shgroup_call(grp, DRW_cache_quad_get(), ob);
@@ -164,7 +159,7 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata,
DRW_shgroup_call(grp, DRW_cache_cube_get(), ob);
}
- BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(mmd));
+ BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(fmd));
}
static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd,
@@ -292,8 +287,8 @@ void workbench_volume_draw_finish(WORKBENCH_Data *vedata)
* modifier is not used for display. We should share them for
* all viewport in a redraw at least. */
LISTBASE_FOREACH (LinkData *, link, &wpd->smoke_domains) {
- FluidModifierData *mmd = (FluidModifierData *)link->data;
- GPU_free_smoke(mmd);
+ FluidModifierData *fmd = (FluidModifierData *)link->data;
+ DRW_smoke_free(fmd);
}
BLI_freelistN(&wpd->smoke_domains);
}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 89dd6fa210c..3551296cfc3 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -198,12 +198,28 @@ void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo);
} while (0)
/* Shaders */
+
+#ifndef __GPU_MATERIAL_H__
+/* FIXME: Meh avoid including all GPUMaterial. */
+typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat,
+ int options,
+ const char **vert_code,
+ const char **geom_code,
+ const char **frag_lib,
+ const char **defines);
+#endif
+
struct GPUShader *DRW_shader_create(const char *vert,
const char *geom,
const char *frag,
const char *defines);
struct GPUShader *DRW_shader_create_with_lib(
const char *vert, const char *geom, const char *frag, const char *lib, const char *defines);
+struct GPUShader *DRW_shader_create_with_shaderlib(const char *vert,
+ const char *geom,
+ const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines);
struct GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
const char *geom,
const char *defines,
@@ -211,6 +227,9 @@ struct GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
const char **varying_names,
const int varying_count);
struct GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines);
+struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib(const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines);
struct GPUMaterial *DRW_shader_find_from_world(struct World *wo,
const void *engine_type,
const int options,
@@ -229,7 +248,8 @@ struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
const char *geom,
const char *frag_lib,
const char *defines,
- bool deferred);
+ bool deferred,
+ GPUMaterialEvalCallbackFn callback);
struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
struct Material *ma,
struct bNodeTree *ntree,
@@ -240,7 +260,8 @@ struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
const char *geom,
const char *frag_lib,
const char *defines,
- bool deferred);
+ bool deferred,
+ GPUMaterialEvalCallbackFn callback);
void DRW_shader_free(struct GPUShader *shader);
#define DRW_SHADER_FREE_SAFE(shader) \
do { \
@@ -257,7 +278,8 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const ch
#define DRW_SHADER_LIB_ADD(lib, lib_name) \
DRW_shader_library_add_file(lib, datatoc_##lib_name##_glsl, STRINGIFY(lib_name) ".glsl")
-char *DRW_shader_library_create_shader_string(DRWShaderLibrary *lib, char *shader_code);
+char *DRW_shader_library_create_shader_string(const DRWShaderLibrary *lib,
+ const char *shader_code);
void DRW_shader_library_free(DRWShaderLibrary *lib);
#define DRW_SHADER_LIB_FREE_SAFE(lib) \
@@ -462,6 +484,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 +595,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);
@@ -619,13 +645,15 @@ void DRW_render_object_iter(void *vedata,
struct RenderEngine *engine,
struct Depsgraph *depsgraph));
void DRW_render_instance_buffer_finish(void);
-void DRW_render_viewport_size_set(int size[2]);
+void DRW_render_viewport_size_set(const int size[2]);
void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
struct Depsgraph *depsgraph,
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 c23ea3d7c82..20e346375a7 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -829,7 +829,7 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob)
case OB_HAIR:
return NULL;
case OB_POINTCLOUD:
- return NULL;
+ return DRW_pointcloud_batch_cache_get_dots(ob);
case OB_VOLUME:
return DRW_cache_volume_face_wireframe_get(ob);
case OB_GPENCIL: {
@@ -880,6 +880,32 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
case OB_HAIR:
return NULL;
case OB_POINTCLOUD:
+ return DRW_cache_pointcloud_surface_get(ob);
+ case OB_VOLUME:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+/* 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;
@@ -932,7 +958,7 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob,
case OB_HAIR:
return NULL;
case OB_POINTCLOUD:
- return NULL;
+ return DRW_cache_pointcloud_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
case OB_VOLUME:
return NULL;
default:
@@ -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);
@@ -3257,9 +3289,16 @@ GPUBatch *DRW_cache_lattice_vert_overlay_get(Object *ob)
GPUBatch *DRW_cache_pointcloud_get_dots(Object *object)
{
+ BLI_assert(object->type == OB_POINTCLOUD);
return DRW_pointcloud_batch_cache_get_dots(object);
}
+GPUBatch *DRW_cache_pointcloud_surface_get(Object *object)
+{
+ BLI_assert(object->type == OB_POINTCLOUD);
+ return DRW_pointcloud_batch_cache_get_surface(object);
+}
+
/* -------------------------------------------------------------------- */
/** \name Volume
* \{ */
@@ -3552,6 +3591,11 @@ void drw_batch_cache_generate_requested(Object *ob)
}
}
+void drw_batch_cache_generate_requested_delayed(Object *ob)
+{
+ BLI_gset_add(DST.delayed_extraction, ob);
+}
+
void DRW_batch_cache_free_old(Object *ob, int ctime)
{
struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 221fb89612f..1ea53c91cb3 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);
@@ -212,6 +215,7 @@ struct GPUBatch *DRW_cache_hair_edge_detection_get(struct Object *ob, bool *r_is
/* PointCloud */
struct GPUBatch *DRW_cache_pointcloud_get_dots(struct Object *obj);
+struct GPUBatch *DRW_cache_pointcloud_surface_get(struct Object *obj);
/* Volume */
typedef struct DRWVolumeGrid {
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index f203c2ff1ea..4156e2e79d8 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -53,16 +53,21 @@ 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
* modifiers could remove it. (see T68857) */
uint32_t edit_uv : 1;
} DRW_MeshCDMask;
+/* Keep `DRW_MeshCDMask` struct within an `uint64_t`.
+ * bit-wise and atomic operations are used to compare and update the struct.
+ * See `mesh_cd_layers_type_*` functions. */
+BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits")
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;
@@ -165,8 +170,7 @@ typedef enum DRWBatchFlag {
MBC_WIRE_EDGES = (1 << 23),
MBC_WIRE_LOOPS = (1 << 24),
MBC_WIRE_LOOPS_UVS = (1 << 25),
- MBC_SURF_PER_MAT = (1 << 26),
- MBC_SKIN_ROOTS = (1 << 27),
+ MBC_SKIN_ROOTS = (1 << 26),
} DRWBatchFlag;
#define MBC_EDITUV \
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index 06462d5b9c5..cca8ebcf2a8 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -128,12 +128,191 @@ 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];
int *lverts, *ledges;
} MeshRenderData;
+
+static void mesh_render_data_update_loose_geom(MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType UNUSED(data_flag))
+{
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
+ mr->vert_loose_len = 0;
+ mr->edge_loose_len = 0;
+
+ BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
+
+ mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__);
+ 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, 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));
+ }
+
+ mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
+ for (int v = 0; v < mr->vert_len; v++) {
+ if (!BLI_BITMAP_TEST(lvert_map, v)) {
+ mr->lverts[mr->vert_loose_len++] = v;
+ }
+ }
+ if (mr->vert_loose_len < mr->vert_len) {
+ mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
+ }
+
+ MEM_freeN(lvert_map);
+
+ mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
+ }
+ }
+ else {
+ /* #BMesh */
+ BMesh *bm = mr->bm;
+ if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
+ int elem_id;
+ BMIter iter;
+ BMVert *eve;
+ BMEdge *ede;
+ mr->vert_loose_len = 0;
+ mr->edge_loose_len = 0;
+
+ mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
+ if (eve->e == NULL) {
+ mr->lverts[mr->vert_loose_len++] = elem_id;
+ }
+ }
+ if (mr->vert_loose_len < mr->vert_len) {
+ mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
+ }
+
+ mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__);
+ BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
+ if (ede->l == NULL) {
+ mr->ledges[mr->edge_loose_len++] = elem_id;
+ }
+ }
+ if (mr->edge_loose_len < mr->edge_len) {
+ mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
+ }
+
+ mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
+ }
+ }
+}
+
+/**
+ * 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)
+{
+ Mesh *me = mr->me;
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+ mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
+ BKE_mesh_recalc_looptri(
+ me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri);
+ }
+ }
+ else {
+ /* #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));
+ }
+ }
+}
+
+static void mesh_render_data_update_normals(MeshRenderData *mr,
+ const eMRIterType UNUSED(iter_type),
+ const eMRDataType data_flag)
+{
+ Mesh *me = mr->me;
+ const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
+ const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
+ mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
+ BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
+ NULL,
+ mr->vert_len,
+ mr->mloop,
+ mr->mpoly,
+ mr->loop_len,
+ mr->poly_len,
+ mr->poly_normals,
+ true);
+ }
+ if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+ mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+ short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
+ BKE_mesh_normals_loop_split(mr->me->mvert,
+ mr->vert_len,
+ mr->me->medge,
+ mr->edge_len,
+ mr->me->mloop,
+ mr->loop_normals,
+ mr->loop_len,
+ mr->me->mpoly,
+ mr->poly_normals,
+ mr->poly_len,
+ is_auto_smooth,
+ split_angle,
+ NULL,
+ clnors,
+ NULL);
+ }
+ }
+ else {
+ /* #BMesh */
+ if (data_flag & MR_DATA_POLY_NOR) {
+ /* Use #BMFace.no instead. */
+ }
+ if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+
+ const float(*vert_coords)[3] = NULL;
+ const float(*vert_normals)[3] = NULL;
+ const float(*poly_normals)[3] = NULL;
+
+ if (mr->edit_data && mr->edit_data->vertexCos) {
+ vert_coords = mr->bm_vert_coords;
+ vert_normals = mr->bm_vert_normals;
+ poly_normals = mr->bm_poly_normals;
+ }
+
+ mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+ const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
+ BM_loops_calc_normal_vcos(mr->bm,
+ vert_coords,
+ vert_normals,
+ poly_normals,
+ is_auto_smooth,
+ split_angle,
+ mr->loop_normals,
+ NULL,
+ NULL,
+ clnors_offset,
+ false);
+ }
+ }
+}
+
static MeshRenderData *mesh_render_data_create(Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
@@ -141,7 +320,9 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
const bool do_final,
const bool do_uvedit,
const DRW_MeshCDMask *UNUSED(cd_used),
- const ToolSettings *ts)
+ const ToolSettings *ts,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag)
{
MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
mr->toolsettings = ts;
@@ -240,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;
@@ -249,165 +430,11 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->poly_len = bm->totface;
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
}
+ mesh_render_data_update_loose_geom(mr, iter_type, data_flag);
return mr;
}
-/* Part of the creation of the MeshRenderData that happens in a thread. */
-static void mesh_render_data_update(MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- Mesh *me = mr->me;
- const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
- const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
-
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
- mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
- BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
- NULL,
- mr->vert_len,
- mr->mloop,
- mr->mpoly,
- mr->loop_len,
- mr->poly_len,
- mr->poly_normals,
- true);
- }
- if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
- mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
- BKE_mesh_normals_loop_split(mr->me->mvert,
- mr->vert_len,
- mr->me->medge,
- mr->edge_len,
- mr->me->mloop,
- mr->loop_normals,
- mr->loop_len,
- mr->me->mpoly,
- mr->poly_normals,
- mr->poly_len,
- is_auto_smooth,
- split_angle,
- NULL,
- clnors,
- NULL);
- }
- if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
- BKE_mesh_recalc_looptri(mr->me->mloop,
- mr->me->mpoly,
- mr->me->mvert,
- mr->me->totloop,
- mr->me->totpoly,
- mr->mlooptri);
- }
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- mr->vert_loose_len = 0;
- mr->edge_loose_len = 0;
-
- BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, "lvert map");
-
- 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;
- }
- /* Tag verts as not loose. */
- BLI_BITMAP_ENABLE(lvert_map, medge->v1);
- BLI_BITMAP_ENABLE(lvert_map, medge->v2);
- }
- if (mr->edge_loose_len < mr->edge_len) {
- mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
- }
-
- mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
- for (int v = 0; v < mr->vert_len; v++) {
- if (!BLI_BITMAP_TEST(lvert_map, v)) {
- mr->lverts[mr->vert_loose_len++] = v;
- }
- }
- if (mr->vert_loose_len < mr->vert_len) {
- mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
- }
-
- MEM_freeN(lvert_map);
-
- mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
- }
- }
- else {
- /* BMesh */
- BMesh *bm = mr->bm;
- if (data_flag & MR_DATA_POLY_NOR) {
- /* Use bmface->no instead. */
- }
- if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
-
- const float(*vert_coords)[3] = NULL;
- const float(*vert_normals)[3] = NULL;
- const float(*poly_normals)[3] = NULL;
-
- if (mr->edit_data && mr->edit_data->vertexCos) {
- vert_coords = mr->bm_vert_coords;
- vert_normals = mr->bm_vert_normals;
- poly_normals = mr->bm_poly_normals;
- }
-
- mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
- BM_loops_calc_normal_vcos(mr->bm,
- vert_coords,
- vert_normals,
- poly_normals,
- is_auto_smooth,
- split_angle,
- mr->loop_normals,
- NULL,
- NULL,
- clnors_offset,
- false);
- }
- if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- /* Edit mode ensures this is valid, no need to calculate. */
- BLI_assert((bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
- }
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- int elem_id;
- BMIter iter;
- BMVert *eve;
- BMEdge *ede;
- mr->vert_loose_len = 0;
- mr->edge_loose_len = 0;
-
- mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
- if (eve->e == NULL) {
- mr->lverts[mr->vert_loose_len++] = elem_id;
- }
- }
- if (mr->vert_loose_len < mr->vert_len) {
- mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
- }
-
- mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__);
- BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
- if (ede->l == NULL) {
- mr->ledges[mr->edge_loose_len++] = elem_id;
- }
- }
- if (mr->edge_loose_len < mr->edge_len) {
- mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
- }
-
- mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
- }
- }
-}
-
static void mesh_render_data_free(MeshRenderData *mr)
{
MEM_SAFE_FREE(mr->mlooptri);
@@ -461,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;
}
}
@@ -480,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;
}
@@ -558,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++) {
@@ -583,51 +845,62 @@ 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. */
+ /* The `surface_per_mat` tests are there when object shading type is set to Wire or Bounds. In
+ * these cases there isn't a surface per material. */
if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) {
- 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 mat_start = data->tri_mat_start[i];
+ const int mat_end = data->tri_mat_end[i];
+ const int start = mat_start * 3;
+ const int len = (mat_end - mat_start) * 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);
}
}
@@ -637,18 +910,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,
};
/** \} */
@@ -666,71 +933,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)
@@ -740,18 +1049,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,
};
/** \} */
@@ -778,18 +1083,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,
};
/** \} */
@@ -805,77 +1106,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)
@@ -885,18 +1210,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,
};
/** \} */
@@ -912,34 +1235,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;
}
}
@@ -950,18 +1290,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,
};
/** \} */
@@ -984,66 +1318,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,
};
/** \} */
@@ -1065,7 +1393,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;
@@ -1082,7 +1410,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);
@@ -1093,7 +1421,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. */
@@ -1105,7 +1433,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);
@@ -1119,42 +1447,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)) {
@@ -1184,18 +1518,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,
};
/** \} */
@@ -1225,53 +1553,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,
};
/** \} */
@@ -1297,55 +1625,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,
};
/** \} */
@@ -1373,50 +1699,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,
};
/** \} */
@@ -1437,64 +1761,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,
};
/** \} */
@@ -1517,7 +1852,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");
@@ -1541,95 +1876,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)
@@ -1638,18 +2000,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,
};
/** \} */
@@ -1675,62 +2035,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,
};
/** \} */
@@ -1752,64 +2121,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,
};
/** \} */
@@ -1853,7 +2233,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. */
@@ -1879,20 +2259,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));
}
}
@@ -1903,18 +2284,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,
};
/** \} */
@@ -1962,7 +2334,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__);
@@ -1976,17 +2348,17 @@ 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);
if (tan_len != 0 || use_orco_tan) {
short tangent_mask = 0;
bool calc_active_tangent = false;
@@ -1998,7 +2370,7 @@ 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);
}
@@ -2016,7 +2388,7 @@ 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);
}
@@ -2024,7 +2396,7 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool
if (use_orco_tan) {
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_TANGENT, 0);
+ const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0);
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
@@ -2050,18 +2422,19 @@ 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(
+ &loop_data, 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(&loop_data, 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++;
}
}
@@ -2070,24 +2443,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(
+ &loop_data, 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(&loop_data, 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(&loop_data, mr->loop_len);
}
static void *extract_tan_init(const MeshRenderData *mr, void *buf)
@@ -2097,18 +2471,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,
};
/** \} */
@@ -2124,18 +2489,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,
};
/** \} */
@@ -2150,7 +2506,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)) {
@@ -2167,14 +2525,44 @@ 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 */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
+ /* 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 */
+ if (U.experimental.use_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);
@@ -2184,27 +2572,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]);
@@ -2212,22 +2603,45 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
}
}
}
+
+ if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) {
+ 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,
};
/** \} */
@@ -2246,7 +2660,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);
@@ -2261,33 +2675,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)
@@ -2296,18 +2714,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,
};
/** \} */
@@ -2334,7 +2746,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;
@@ -2358,9 +2770,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;
}
@@ -2376,81 +2788,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) {
@@ -2465,8 +2899,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);
@@ -2475,18 +2909,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,
};
/** \} */
@@ -2497,8 +2927,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)
@@ -2584,27 +3014,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)
@@ -2613,18 +3065,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,
};
/** \} */
@@ -2731,31 +3177,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;
}
@@ -2775,7 +3221,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");
}
@@ -2785,114 +3231,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,
};
/** \} */
@@ -2910,7 +3372,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");
}
@@ -2927,51 +3389,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),
@@ -2982,18 +3452,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,
};
/** \} */
@@ -3053,13 +3517,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 {
@@ -3072,8 +3536,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. */
@@ -3083,18 +3547,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];
}
}
}
@@ -3107,18 +3571,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,
};
/** \} */
@@ -3165,7 +3621,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. */
@@ -3183,7 +3639,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);
}
@@ -3195,7 +3651,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);
}
@@ -3208,94 +3664,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),
@@ -3306,18 +3763,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,
};
/** \} */
@@ -3389,28 +3840,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;
@@ -3502,12 +3955,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;
}
}
}
@@ -3548,12 +4001,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;
}
}
}
@@ -3604,8 +4057,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) {
@@ -3626,9 +4079,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;
}
}
}
@@ -3655,9 +4108,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;
}
}
}
@@ -3700,9 +4153,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) {
@@ -3714,7 +4167,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 {
@@ -3733,24 +4186,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,
@@ -3766,8 +4219,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;
}
}
}
@@ -3800,11 +4253,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);
@@ -3815,35 +4267,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;
@@ -3876,9 +4330,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);
}
}
@@ -3912,20 +4366,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,
};
/** \} */
@@ -3943,56 +4389,71 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf)
GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->poly_len);
- if (!mr->use_subsurf_fdots) {
- /* Clear so we can accumulate on it. */
- memset(vbo->data, 0x0, mr->poly_len * vbo->format.stride);
- }
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_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ float *co = center[f_index];
+ zero_v3(co);
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ add_v3_v3(co, bm_vert_co_get(mr, l_iter->v));
+ } while ((l_iter = l_iter->next) != l_first);
+ mul_v3_fl(co, 1.0f / (float)f->len);
+ }
+ EXTRACT_POLY_FOREACH_BM_END;
}
-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];
+ const MVert *mvert = mr->mvert;
+ const MLoop *mloop = mr->mloop;
+
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 (mv->flag & ME_VERT_FACEDOT) {
+ copy_v3_v3(center[mp_index], mv->co);
+ }
}
+ EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
else {
- float w = 1.0f / (float)mpoly->totloop;
- madd_v3_v3fl(center[p], mvert->co, w);
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ float *co = center[mp_index];
+ zero_v3(co);
+
+ const MLoop *ml = &mloop[mp->loopstart];
+ for (int i = 0; i < mp->totloop; i++, ml++) {
+ const MVert *mv = &mvert[ml->v];
+ add_v3_v3(center[mp_index], mv->co);
+ }
+ mul_v3_fl(co, 1.0f / (float)mp->totloop);
+ }
+ EXTRACT_POLY_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,
};
/** \} */
@@ -4065,18 +4526,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,
};
/** \} */
@@ -4120,30 +4573,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;
}
}
@@ -4155,18 +4620,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,
};
/** \} */
@@ -4195,31 +4654,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),
@@ -4230,18 +4693,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,
};
/** \} */
@@ -4292,18 +4749,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,
};
/** \} */
@@ -4325,157 +4773,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)
@@ -4491,37 +4965,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,
};
/** \} */
@@ -4533,10 +5013,16 @@ typedef struct ExtractUserData {
void *user_data;
} ExtractUserData;
+typedef enum ExtractTaskDataType {
+ EXTRACT_MESH_EXTRACT,
+ EXTRACT_LINES_LOOSE,
+} ExtractTaskDataType;
+
typedef struct ExtractTaskData {
void *next, *prev;
const MeshRenderData *mr;
const MeshExtract *extract;
+ ExtractTaskDataType tasktype;
eMRIterType iter_type;
int start, end;
/** Decremented each time a task is finished. */
@@ -4545,6 +5031,41 @@ typedef struct ExtractTaskData {
ExtractUserData *user_data;
} ExtractTaskData;
+static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr,
+ const MeshExtract *extract,
+ void *buf,
+ int32_t *task_counter)
+{
+ ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__);
+ taskdata->next = NULL;
+ taskdata->prev = NULL;
+ taskdata->tasktype = EXTRACT_MESH_EXTRACT;
+ taskdata->mr = mr;
+ 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 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. */
+ taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__);
+ taskdata->iter_type = mesh_extract_iter_type(extract);
+ taskdata->task_counter = task_counter;
+ taskdata->start = 0;
+ taskdata->end = INT_MAX;
+ return taskdata;
+}
+
+static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr)
+{
+ ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__);
+ taskdata->tasktype = EXTRACT_LINES_LOOSE;
+ taskdata->mr = mr;
+ return taskdata;
+}
+
static void extract_task_data_free(void *data)
{
ExtractTaskData *task_data = data;
@@ -4562,67 +5083,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;
}
@@ -4630,23 +5153,30 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
static void extract_init(ExtractTaskData *data)
{
- data->user_data->user_data = data->extract->init(data->mr, data->buf);
+ if (data->tasktype == EXTRACT_MESH_EXTRACT) {
+ data->user_data->user_data = data->extract->init(data->mr, data->buf);
+ }
}
static void extract_run(void *__restrict taskdata)
{
ExtractTaskData *data = (ExtractTaskData *)taskdata;
- mesh_extract_iter(data->mr,
- data->iter_type,
- data->start,
- data->end,
- data->extract,
- data->user_data->user_data);
-
- /* If this is the last task, we do the finish function. */
- int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
- if (remainin_tasks == 0 && data->extract->finish != NULL) {
- data->extract->finish(data->mr, data->buf, data->user_data->user_data);
+ if (data->tasktype == EXTRACT_MESH_EXTRACT) {
+ mesh_extract_iter(data->mr,
+ data->iter_type,
+ data->start,
+ data->end,
+ data->extract,
+ data->user_data->user_data);
+
+ /* If this is the last task, we do the finish function. */
+ int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
+ if (remainin_tasks == 0 && data->extract->finish != NULL) {
+ data->extract->finish(data->mr, data->buf, data->user_data->user_data);
+ }
+ }
+ else if (data->tasktype == EXTRACT_LINES_LOOSE) {
+ extract_lines_loose_subbuffer(data->mr);
}
}
@@ -4670,15 +5200,20 @@ typedef struct MeshRenderDataUpdateTaskData {
static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata)
{
BLI_assert(taskdata);
- mesh_render_data_free(taskdata->mr);
+ MeshRenderData *mr = taskdata->mr;
+ mesh_render_data_free(mr);
MEM_freeN(taskdata);
}
static void mesh_extract_render_data_node_exec(void *__restrict task_data)
{
MeshRenderDataUpdateTaskData *update_task_data = task_data;
- mesh_render_data_update(
- update_task_data->mr, update_task_data->iter_type, update_task_data->data_flag);
+ MeshRenderData *mr = update_task_data->mr;
+ const eMRIterType iter_type = update_task_data->iter_type;
+ const eMRDataType data_flag = update_task_data->data_flag;
+
+ mesh_render_data_update_normals(mr, iter_type, data_flag);
+ mesh_render_data_update_looptris(mr, iter_type, data_flag);
}
static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
@@ -4822,24 +5357,8 @@ static void extract_task_create(struct TaskGraph *task_graph,
}
/* Divide extraction of the VBO/IBO into sensible chunks of works. */
- ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), "ExtractTaskData");
- taskdata->next = NULL;
- taskdata->prev = NULL;
- taskdata->mr = mr;
- 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.
- *
- * This structure makes sure that when extract_init is called, that the user data of all
- * iterations are updated. */
- taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__);
- taskdata->iter_type = mesh_extract_iter_type(extract);
- taskdata->task_counter = task_counter;
- taskdata->start = 0;
- taskdata->end = INT_MAX;
+ ExtractTaskData *taskdata = extract_task_data_create_mesh_extract(
+ mr, extract, buf, task_counter);
/* Simple heuristic. */
const int chunk_size = 8192;
@@ -4853,10 +5372,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) {
@@ -4911,9 +5430,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
*
@@ -4980,14 +5499,26 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
TEST_ASSIGN(IBO, ibo, edituv_points);
TEST_ASSIGN(IBO, ibo, edituv_fdots);
+ if (do_lines_loose_subbuffer) {
+ iter_flag |= MR_ITER_LEDGE;
+ }
+
#undef TEST_ASSIGN
#ifdef DEBUG_TIME
double rdata_start = PIL_check_seconds_timer();
#endif
- MeshRenderData *mr = mesh_render_data_create(
- me, is_editmode, is_paint_mode, obmat, do_final, do_uvedit, cd_layer_used, ts);
+ MeshRenderData *mr = mesh_render_data_create(me,
+ is_editmode,
+ is_paint_mode,
+ obmat,
+ do_final,
+ do_uvedit,
+ cd_layer_used,
+ ts,
+ iter_flag,
+ data_flag);
mr->cache = cache; /* HACK */
mr->use_hide = use_hide;
mr->use_subsurf_fdots = use_subsurf_fdots;
@@ -5069,12 +5600,8 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
}
else {
if (do_lines_loose_subbuffer) {
- /* When `lines_loose` is requested without `lines` we can create the sub-buffer on the fly as
- * the `lines` buffer should then already be up-to-date.
- * (see `DRW_batch_requested(cache->batch.loose_edges, GPU_PRIM_LINES)` in
- * `DRW_mesh_batch_cache_create_requested`).
- */
- extract_lines_loose_subbuffer(mr);
+ ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr);
+ BLI_addtail(&single_threaded_task_data->task_datas, taskdata);
}
}
EXTRACT(ibo, points);
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 80649143537..96d351794e6 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -144,6 +144,10 @@ int DRW_hair_material_count_get(struct Hair *hair);
int DRW_pointcloud_material_count_get(struct PointCloud *pointcloud);
struct GPUBatch *DRW_pointcloud_batch_cache_get_dots(struct Object *ob);
+struct GPUBatch *DRW_pointcloud_batch_cache_get_surface(struct Object *ob);
+struct GPUBatch **DRW_cache_pointcloud_surface_shaded_get(struct Object *ob,
+ struct GPUMaterial **gpumat_array,
+ uint gpumat_array_len);
/* Volume */
int DRW_volume_material_count_get(struct Volume *volume);
@@ -170,6 +174,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,20 +204,26 @@ 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. */
+
/* Edit mesh bitflags (is this the right place?) */
enum {
VFLAG_VERT_ACTIVE = 1 << 0,
VFLAG_VERT_SELECTED = 1 << 1,
- VFLAG_EDGE_ACTIVE = 1 << 2,
- VFLAG_EDGE_SELECTED = 1 << 3,
- VFLAG_EDGE_SEAM = 1 << 4,
- VFLAG_EDGE_SHARP = 1 << 5,
- VFLAG_EDGE_FREESTYLE = 1 << 6,
- VFLAG_HANDLE_SELECTED = 1 << 7,
- /* Beware to not go over 1 << 7 (it's a byte flag)
- * (see gpu_shader_edit_mesh_overlay_geom.glsl) */
+ VFLAG_VERT_SELECTED_BEZT_HANDLE = 1 << 2,
+ VFLAG_EDGE_ACTIVE = 1 << 3,
+ VFLAG_EDGE_SELECTED = 1 << 4,
+ VFLAG_EDGE_SEAM = 1 << 5,
+ VFLAG_EDGE_SHARP = 1 << 6,
+ VFLAG_EDGE_FREESTYLE = 1 << 7,
+ /* Beware to not go over 1 << 7 (it's a byte flag). */
};
enum {
@@ -224,8 +235,7 @@ enum {
VFLAG_EDGE_UV_SELECT = 1 << 5,
VFLAG_FACE_UV_ACTIVE = 1 << 6,
VFLAG_FACE_UV_SELECT = 1 << 7,
- /* Beware to not go over 1 << 7 (it's a byte flag)
- * (see gpu_shader_edit_mesh_overlay_geom.glsl) */
+ /* Beware to not go over 1 << 7 (it's a byte flag). */
};
/* Particles */
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c
index c6112994b65..73e0ff7ef83 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.c
+++ b/source/blender/draw/intern/draw_cache_impl_curve.c
@@ -47,6 +47,7 @@
#include "draw_cache_impl.h" /* own include */
+/* See: edit_curve_point_vert.glsl for duplicate includes. */
#define SELECT 1
#define ACTIVE_NURB 1 << 2
#define BEZIER_HANDLE 1 << 3
@@ -698,7 +699,9 @@ static char beztriple_vflag_get(CurveRenderData *rdata,
SET_FLAG_FROM_TEST(vflag, (v_idx == rdata->actvert && nu_id == rdata->actnu), VFLAG_VERT_ACTIVE);
SET_FLAG_FROM_TEST(vflag, (nu_id == rdata->actnu), ACTIVE_NURB);
SET_FLAG_FROM_TEST(vflag, handle_point, BEZIER_HANDLE);
- SET_FLAG_FROM_TEST(vflag, handle_selected, VFLAG_HANDLE_SELECTED);
+ SET_FLAG_FROM_TEST(vflag, handle_selected, VFLAG_VERT_SELECTED_BEZT_HANDLE);
+ /* Setting flags that overlap with will cause the color id not to work properly. */
+ BLI_assert((vflag >> COLOR_SHIFT) == 0);
/* handle color id */
vflag |= col_id << COLOR_SHIFT;
return vflag;
@@ -711,6 +714,8 @@ static char bpoint_vflag_get(CurveRenderData *rdata, char flag, int v_idx, int n
SET_FLAG_FROM_TEST(vflag, (v_idx == rdata->actvert && nu_id == rdata->actnu), VFLAG_VERT_ACTIVE);
SET_FLAG_FROM_TEST(vflag, (nu_id == rdata->actnu), ACTIVE_NURB);
SET_FLAG_FROM_TEST(vflag, ((u % 2) == 0), EVEN_U_BIT);
+ /* Setting flags that overlap with will cause the color id not to work properly. */
+ BLI_assert((vflag >> COLOR_SHIFT) == 0);
vflag |= COLOR_NURB_ULINE_ID << COLOR_SHIFT;
return vflag;
}
@@ -898,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..d648245f232 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -150,7 +150,6 @@ void DRW_gpencil_batch_cache_free(bGPdata *gpd)
gpencil_batch_cache_clear(gpd->runtime.gpencil_cache);
MEM_SAFE_FREE(gpd->runtime.gpencil_cache);
gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
- return;
}
/** \} */
@@ -349,10 +348,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 +386,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 +440,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 +476,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_lattice.c b/source/blender/draw/intern/draw_cache_impl_lattice.c
index bb313b31deb..66a67d6b8fe 100644
--- a/source/blender/draw/intern/draw_cache_impl_lattice.c
+++ b/source/blender/draw/intern/draw_cache_impl_lattice.c
@@ -127,7 +127,7 @@ typedef struct LatticeRenderData {
int actbp;
- struct MDeformVert *dvert;
+ const struct MDeformVert *dvert;
} LatticeRenderData;
enum {
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 99e285a18f1..7cc10bd14e8 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -74,22 +74,30 @@ static void mesh_batch_cache_clear(Mesh *me);
/* Return true is all layers in _b_ are inside _a_. */
BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b)
{
- return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b);
+ return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b);
}
BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b)
{
- return *((uint32_t *)&a) == *((uint32_t *)&b);
+ return *((uint64_t *)&a) == *((uint64_t *)&b);
}
BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b)
{
- atomic_fetch_and_or_uint32((uint32_t *)a, *(uint32_t *)&b);
+ uint32_t *a_p = (uint32_t *)a;
+ uint32_t *b_p = (uint32_t *)&b;
+ atomic_fetch_and_or_uint32(a_p, *b_p);
+ atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1));
}
BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a)
{
- *((uint32_t *)a) = 0;
+ *((uint64_t *)a) = 0;
+}
+
+BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me)
+{
+ return (me->edit_mesh && me->edit_mesh->mesh_eval_final) ? me->edit_mesh->mesh_eval_final : me;
}
static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *cd_used)
@@ -112,9 +120,24 @@ 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;
+ const Mesh *me_final = editmesh_final_or_this(me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
if (layer != -1) {
@@ -124,7 +147,7 @@ static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used
static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
{
- const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
+ const Mesh *me_final = editmesh_final_or_this(me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
int layer = CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV);
if (layer != -1) {
@@ -134,8 +157,20 @@ 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 Mesh *me_final = editmesh_final_or_this(me);
+ 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 = editmesh_final_or_this(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);
@@ -146,8 +181,9 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
struct GPUMaterial **gpumat_array,
int gpumat_array_len)
{
- const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
+ const Mesh *me_final = editmesh_final_or_this(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;
@@ -172,9 +208,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
type = CD_MTFACE;
if (layer == -1) {
+ if (U.experimental.use_sculpt_vertex_colors) {
+ layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name);
+ type = CD_PROP_COLOR;
+ }
+ }
+
+ if (layer == -1) {
layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name);
type = CD_MCOL;
}
+
#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 +266,33 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
}
break;
}
+ case CD_PROP_COLOR: {
+ /* Sculpt Vertex Colors */
+ bool use_mloop_cols = false;
+ 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);
+ /* Fallback to 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);
+ use_mloop_cols = true;
+ }
+ }
+ if (layer != -1) {
+ if (use_mloop_cols) {
+ cd_used.vcol |= (1 << layer);
+ }
+ else {
+ 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 +300,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: {
@@ -461,14 +532,26 @@ static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache,
}
}
-static void mesh_batch_cache_discard_shaded_batches(MeshBatchCache *cache)
+static void mesh_batch_cache_request_surface_batches(MeshBatchCache *cache)
{
+ mesh_batch_cache_add_request(cache, MBC_SURFACE);
+ DRW_batch_request(&cache->batch.surface);
+ if (cache->surface_per_mat) {
+ for (int i = 0; i < cache->mat_len; i++) {
+ DRW_batch_request(&cache->surface_per_mat[i]);
+ }
+ }
+}
+
+static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache)
+{
+ GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
if (cache->surface_per_mat) {
for (int i = 0; i < cache->mat_len; i++) {
GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]);
}
}
- cache->batch_ready &= ~MBC_SURF_PER_MAT;
+ cache->batch_ready &= ~MBC_SURFACE;
}
static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
@@ -480,7 +563,7 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco);
}
- mesh_batch_cache_discard_shaded_batches(cache);
+ mesh_batch_cache_discard_surface_batches(cache);
mesh_cd_layers_type_clear(&cache->cd_used);
MEM_SAFE_FREE(cache->surface_per_mat);
@@ -520,10 +603,7 @@ static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache)
cache->cd_used.edit_uv = 0;
/* Discard other batches that uses vbo.uv */
- mesh_batch_cache_discard_shaded_batches(cache);
-
- GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
- cache->batch_ready &= ~MBC_SURFACE;
+ mesh_batch_cache_discard_surface_batches(cache);
}
static void mesh_batch_cache_discard_uvedit_select(MeshBatchCache *cache)
@@ -585,12 +665,8 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, int mode)
GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops);
GPU_BATCH_DISCARD_SAFE(cache->batch.wire_edges);
- if (cache->surface_per_mat) {
- for (int i = 0; i < cache->mat_len; i++) {
- GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]);
- }
- }
- cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SURF_PER_MAT);
+ mesh_batch_cache_discard_surface_batches(cache);
+ cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS);
break;
case BKE_MESH_BATCH_DIRTY_ALL:
cache->is_dirty = true;
@@ -679,10 +755,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);
}
@@ -704,8 +792,8 @@ GPUBatch *DRW_mesh_batch_cache_get_all_edges(Mesh *me)
GPUBatch *DRW_mesh_batch_cache_get_surface(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- mesh_batch_cache_add_request(cache, MBC_SURFACE);
- return DRW_batch_request(&cache->batch.surface);
+ mesh_batch_cache_request_surface_batches(cache);
+ return cache->batch.surface;
}
GPUBatch *DRW_mesh_batch_cache_get_loose_edges(Mesh *me)
@@ -763,23 +851,15 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me,
BLI_assert(gpumat_array_len == cache->mat_len);
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
-
- mesh_batch_cache_add_request(cache, MBC_SURF_PER_MAT);
-
- for (int i = 0; i < cache->mat_len; i++) {
- DRW_batch_request(&cache->surface_per_mat[i]);
- }
+ mesh_batch_cache_request_surface_batches(cache);
return cache->surface_per_mat;
}
GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- mesh_batch_cache_add_request(cache, MBC_SURF_PER_MAT);
texpaint_request_active_uv(cache, me);
- for (int i = 0; i < cache->mat_len; i++) {
- DRW_batch_request(&cache->surface_per_mat[i]);
- }
+ mesh_batch_cache_request_surface_batches(cache);
return cache->surface_per_mat;
}
@@ -787,16 +867,24 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
texpaint_request_active_uv(cache, me);
- mesh_batch_cache_add_request(cache, MBC_SURFACE);
- return DRW_batch_request(&cache->batch.surface);
+ mesh_batch_cache_request_surface_batches(cache);
+ return cache->batch.surface;
}
GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
texpaint_request_active_vcol(cache, me);
- mesh_batch_cache_add_request(cache, MBC_SURFACE);
- return DRW_batch_request(&cache->batch.surface);
+ mesh_batch_cache_request_surface_batches(cache);
+ return 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_request_surface_batches(cache);
+ return cache->batch.surface;
}
int DRW_mesh_material_count_get(Mesh *me)
@@ -810,6 +898,22 @@ 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_request_surface_batches(cache);
+
+ 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);
@@ -1019,6 +1123,40 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime)
mesh_cd_layers_type_clear(&cache->cd_used_over_time);
}
+#ifdef DEBUG
+/* Sanity check function to test if all requested batches are available. */
+static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, Mesh *me)
+{
+ MeshBatchCache *cache = mesh_batch_cache_get(me);
+ /* Make sure all requested batches have been setup. */
+ /* Note: The next line creates a different scheduling than during release builds what can lead to
+ * some issues (See T77867 where we needed to disable this function in order to debug what was
+ * happening in release builds). */
+ BLI_task_graph_work_and_wait(task_graph);
+ for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
+ BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
+ }
+ for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); i++) {
+ BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i]));
+ }
+ for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); i++) {
+ BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i]));
+ }
+ for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); i++) {
+ BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i]));
+ }
+ for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); i++) {
+ BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i]));
+ }
+ for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); i++) {
+ BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i]));
+ }
+ for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); i++) {
+ BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i]));
+ }
+}
+#endif
+
/* Can be called for any surface type. Mesh *me is the final mesh. */
void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
Object *ob,
@@ -1039,10 +1177,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
/* Early out */
if (cache->batch_requested == 0) {
#ifdef DEBUG
- goto check;
-#else
- return;
+ drw_mesh_batch_cache_check_available(task_graph, me);
#endif
+ return;
}
/* Sanity check. */
@@ -1067,30 +1204,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
}
}
- /* HACK: if MBC_SURF_PER_MAT is requested and ibo.tris is already available, it won't have it's
- * index ranges initialized. So discard ibo.tris in order to recreate it.
- * This needs to happen before saved_elem_ranges is populated. */
- if ((batch_requested & MBC_SURF_PER_MAT) != 0 && (cache->batch_ready & MBC_SURF_PER_MAT) == 0) {
- FOREACH_MESH_BUFFER_CACHE (cache, mbuffercache) {
- GPU_INDEXBUF_DISCARD_SAFE(mbuffercache->ibo.tris);
- }
- /* Clear all batches that reference ibo.tris. */
- GPU_BATCH_CLEAR_SAFE(cache->batch.surface);
- GPU_BATCH_CLEAR_SAFE(cache->batch.surface_weights);
- GPU_BATCH_CLEAR_SAFE(cache->batch.edit_mesh_analysis);
- GPU_BATCH_CLEAR_SAFE(cache->batch.edit_triangles);
- GPU_BATCH_CLEAR_SAFE(cache->batch.edit_lnor);
- GPU_BATCH_CLEAR_SAFE(cache->batch.edit_selection_faces);
- for (int i = 0; i < cache->mat_len; i++) {
- GPU_BATCH_CLEAR_SAFE(cache->surface_per_mat[i]);
- }
-
- cache->batch_ready &= ~(MBC_SURFACE | MBC_SURFACE_WEIGHTS | MBC_EDIT_MESH_ANALYSIS |
- MBC_EDIT_TRIANGLES | MBC_EDIT_LNOR | MBC_EDIT_SELECTION_FACES);
- }
-
if (batch_requested &
- (MBC_SURFACE | MBC_SURF_PER_MAT | MBC_WIRE_LOOPS_UVS | MBC_EDITUV_FACES_STRETCH_AREA |
+ (MBC_SURFACE | MBC_WIRE_LOOPS_UVS | MBC_EDITUV_FACES_STRETCH_AREA |
MBC_EDITUV_FACES_STRETCH_ANGLE | MBC_EDITUV_FACES | MBC_EDITUV_EDGES | MBC_EDITUV_VERTS)) {
/* Modifiers will only generate an orco layer if the mesh is deformed. */
if (cache->cd_needed.orco != 0) {
@@ -1120,7 +1235,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);
}
}
@@ -1141,7 +1258,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
GPU_BATCH_CLEAR_SAFE(cache->surface_per_mat[i]);
}
GPU_BATCH_CLEAR_SAFE(cache->batch.surface);
- cache->batch_ready &= ~(MBC_SURFACE | MBC_SURF_PER_MAT);
+ cache->batch_ready &= ~(MBC_SURFACE);
mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed);
}
@@ -1177,10 +1294,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
/* Second chance to early out */
if ((batch_requested & ~cache->batch_ready) == 0) {
#ifdef DEBUG
- goto check;
-#else
- return;
+ drw_mesh_batch_cache_check_available(task_graph, me);
#endif
+ return;
}
cache->batch_ready |= batch_requested;
@@ -1201,7 +1317,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 +1385,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) {
@@ -1432,32 +1548,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
ts,
use_hide);
#ifdef DEBUG
-check:
- /* Make sure all requested batches have been setup. */
- /* TODO(jbakker): we should move this to the draw_manager but that needs refactoring and
- * additional looping.*/
- BLI_task_graph_work_and_wait(task_graph);
- for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
- BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
- }
- for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); i++) {
- BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i]));
- }
- for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); i++) {
- BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i]));
- }
- for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); i++) {
- BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i]));
- }
- for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); i++) {
- BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i]));
- }
- for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); i++) {
- BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i]));
- }
- for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); i++) {
- BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i]));
- }
+ drw_mesh_batch_cache_check_available(task_graph, me);
#endif
}
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_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c
index 53939b35285..06cedb9f72c 100644
--- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c
+++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c
@@ -28,6 +28,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_math_base.h"
+#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "DNA_object_types.h"
@@ -45,11 +46,18 @@ static void pointcloud_batch_cache_clear(PointCloud *pointcloud);
/* PointCloud GPUBatch Cache */
typedef struct PointCloudBatchCache {
- GPUVertBuf *pos;
- GPUBatch *batch;
+ GPUVertBuf *pos; /* Position and radius. */
+ GPUVertBuf *geom; /* Instanced geometry for each point in the cloud (small sphere). */
+ GPUIndexBuf *geom_indices;
+
+ GPUBatch *dots;
+ GPUBatch *surface;
+ GPUBatch **surface_per_mat;
/* settings to determine if cache is invalid */
bool is_dirty;
+
+ int mat_len;
} PointCloudBatchCache;
/* GPUBatch cache management. */
@@ -57,7 +65,14 @@ typedef struct PointCloudBatchCache {
static bool pointcloud_batch_cache_valid(PointCloud *pointcloud)
{
PointCloudBatchCache *cache = pointcloud->batch_cache;
- return (cache && cache->is_dirty == false);
+
+ if (cache == NULL) {
+ return false;
+ }
+ if (cache->mat_len != DRW_pointcloud_material_count_get(pointcloud)) {
+ return false;
+ }
+ return cache->is_dirty == false;
}
static void pointcloud_batch_cache_init(PointCloud *pointcloud)
@@ -71,6 +86,10 @@ static void pointcloud_batch_cache_init(PointCloud *pointcloud)
memset(cache, 0, sizeof(*cache));
}
+ cache->mat_len = DRW_pointcloud_material_count_get(pointcloud);
+ cache->surface_per_mat = MEM_callocN(sizeof(GPUBatch *) * cache->mat_len,
+ "pointcloud suface_per_mat");
+
cache->is_dirty = false;
}
@@ -109,8 +128,18 @@ static void pointcloud_batch_cache_clear(PointCloud *pointcloud)
return;
}
- GPU_BATCH_DISCARD_SAFE(cache->batch);
+ GPU_BATCH_DISCARD_SAFE(cache->dots);
+ GPU_BATCH_DISCARD_SAFE(cache->surface);
GPU_VERTBUF_DISCARD_SAFE(cache->pos);
+ GPU_VERTBUF_DISCARD_SAFE(cache->geom);
+ GPU_INDEXBUF_DISCARD_SAFE(cache->geom_indices);
+
+ if (cache->surface_per_mat) {
+ for (int i = 0; i < cache->mat_len; i++) {
+ GPU_BATCH_DISCARD_SAFE(cache->surface_per_mat[i]);
+ }
+ }
+ MEM_SAFE_FREE(cache->surface_per_mat);
}
void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud)
@@ -126,35 +155,84 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *
}
PointCloud *pointcloud = ob->data;
+ const bool has_radius = pointcloud->radius != NULL;
static GPUVertFormat format = {0};
- static uint pos_id;
- static uint radius_id;
+ static GPUVertFormat format_no_radius = {0};
+ static uint pos;
if (format.attr_len == 0) {
/* initialize vertex format */
- pos_id = GPU_vertformat_attr_add(&format, "pointcloud_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- radius_id = GPU_vertformat_attr_add(
- &format, "pointcloud_radius", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ /* From the opengl wiki:
+ * Note that size does not have to exactly match the size used by the vertex shader. If the
+ * vertex shader has fewer components than the attribute provides, then the extras are ignored.
+ * If the vertex shader has more components than the array provides, the extras are given
+ * values from the vector (0, 0, 0, 1) for the missing XYZW components.
+ */
+ pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
- GPU_VERTBUF_DISCARD_SAFE(cache->pos);
- cache->pos = GPU_vertbuf_create_with_format(&format);
+ cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius);
GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint);
- GPU_vertbuf_attr_fill(cache->pos, pos_id, pointcloud->co);
- if (pointcloud->radius) {
- GPU_vertbuf_attr_fill(cache->pos, radius_id, pointcloud->radius);
- }
- else if (pointcloud->totpoint) {
- /* TODO: optimize for constant radius by not including in vertex buffer at all? */
- float *radius = MEM_malloc_arrayN(pointcloud->totpoint, sizeof(float), __func__);
+ if (has_radius) {
+ float(*vbo_data)[4] = (float(*)[4])cache->pos->data;
for (int i = 0; i < pointcloud->totpoint; i++) {
- /* TODO: add default radius to PointCloud data structure. */
- radius[i] = 0.01f;
+ copy_v3_v3(vbo_data[i], pointcloud->co[i]);
+ /* TODO(fclem) remove multiplication here. Here only for keeping the size correct for now. */
+ vbo_data[i][3] = pointcloud->radius[i] * 100.0f;
}
- GPU_vertbuf_attr_fill(cache->pos, radius_id, radius);
- MEM_freeN(radius);
}
+ else {
+ GPU_vertbuf_attr_fill(cache->pos, pos, pointcloud->co);
+ }
+}
+
+static const float half_octahedron_normals[5][3] = {
+ {0.0f, 0.0f, 1.0f},
+ {1.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f},
+ {-1.0f, 0.0f, 0.0f},
+ {0.0f, -1.0f, 0.0f},
+};
+
+static const uint half_octahedron_tris[4][3] = {
+ {0, 1, 2},
+ {0, 2, 3},
+ {0, 3, 4},
+ {0, 4, 1},
+};
+
+static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBatchCache *cache)
+{
+ if (cache->geom != NULL) {
+ return;
+ }
+
+ static GPUVertFormat format = {0};
+ static uint pos;
+ if (format.attr_len == 0) {
+ /* initialize vertex format */
+ pos = GPU_vertformat_attr_add(&format, "pos_inst", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_alias_add(&format, "nor");
+ }
+
+ cache->geom = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache->geom, ARRAY_SIZE(half_octahedron_normals));
+
+ GPU_vertbuf_attr_fill(cache->geom, pos, half_octahedron_normals);
+
+ GPUIndexBufBuilder builder;
+ GPU_indexbuf_init(&builder,
+ GPU_PRIM_TRIS,
+ ARRAY_SIZE(half_octahedron_tris),
+ ARRAY_SIZE(half_octahedron_normals));
+
+ for (int i = 0; i < ARRAY_SIZE(half_octahedron_tris); i++) {
+ GPU_indexbuf_add_tri_verts(&builder, UNPACK3(half_octahedron_tris[i]));
+ }
+
+ cache->geom_indices = GPU_indexbuf_build(&builder);
}
GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
@@ -162,12 +240,48 @@ GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
PointCloud *pointcloud = ob->data;
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
- if (cache->batch == NULL) {
+ if (cache->dots == NULL) {
pointcloud_batch_cache_ensure_pos(ob, cache);
- cache->batch = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL);
+ cache->dots = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL);
+ }
+
+ return cache->dots;
+}
+
+GPUBatch *DRW_pointcloud_batch_cache_get_surface(Object *ob)
+{
+ PointCloud *pointcloud = ob->data;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
+
+ if (cache->surface == NULL) {
+ pointcloud_batch_cache_ensure_pos(ob, cache);
+ pointcloud_batch_cache_ensure_geom(ob, cache);
+
+ cache->surface = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices);
+ GPU_batch_instbuf_add_ex(cache->surface, cache->pos, false);
+ }
+
+ return cache->surface;
+}
+
+GPUBatch **DRW_cache_pointcloud_surface_shaded_get(Object *ob,
+ struct GPUMaterial **UNUSED(gpumat_array),
+ uint gpumat_array_len)
+{
+ PointCloud *pointcloud = ob->data;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
+ BLI_assert(cache->mat_len == gpumat_array_len);
+ UNUSED_VARS(gpumat_array_len);
+
+ if (cache->surface_per_mat[0] == NULL) {
+ pointcloud_batch_cache_ensure_pos(ob, cache);
+ pointcloud_batch_cache_ensure_geom(ob, cache);
+
+ cache->surface_per_mat[0] = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices);
+ GPU_batch_instbuf_add_ex(cache->surface_per_mat[0], cache->pos, false);
}
- return cache->batch;
+ return cache->surface_per_mat;
}
int DRW_pointcloud_material_count_get(PointCloud *pointcloud)
diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c
index 9c2c075ab4f..e07f5b33d58 100644
--- a/source/blender/draw/intern/draw_cache_impl_volume.c
+++ b/source/blender/draw/intern/draw_cache_impl_volume.c
@@ -266,7 +266,7 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
NULL);
GPU_texture_bind(cache_grid->texture, 0);
- GPU_texture_swizzle_channel_auto(cache_grid->texture, channels);
+ GPU_texture_swizzle_set(cache_grid->texture, (channels == 3) ? "rgb1" : "rrr1");
GPU_texture_wrap_mode(cache_grid->texture, false, false);
GPU_texture_unbind(cache_grid->texture);
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.c b/source/blender/draw/intern/draw_common.c
index 2be0249a2cd..095e928aa74 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -22,6 +22,7 @@
#include "DRW_render.h"
+#include "GPU_matrix.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
@@ -31,8 +32,6 @@
#include "BKE_global.h"
#include "BKE_object.h"
-#include "BIF_glutil.h"
-
#include "draw_common.h"
#if 0
@@ -280,7 +279,7 @@ DRWView *DRW_view_create_with_zoffset(const DRWView *parent_view,
viewdist = 1.0f / max_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1]));
}
- winmat[3][2] -= bglPolygonOffsetCalc((float *)winmat, viewdist, offset);
+ winmat[3][2] -= GPU_polygon_offset_calc(winmat, viewdist, offset);
return DRW_view_create_sub(parent_view, viewmat, winmat);
}
@@ -509,5 +508,5 @@ static GPUTexture *DRW_create_weight_colorramp_texture(void)
pixels[i][3] = 1.0f;
}
- return GPU_texture_create_1d(256, GPU_RGBA8, pixels[0], error);
+ return GPU_texture_create_1d(256, GPU_SRGB8_A8, pixels[0], error);
}
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 656d72b2808..09f901c344f 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -27,8 +27,10 @@ struct DRWPass;
struct DRWShadingGroup;
struct GPUMaterial;
struct ModifierData;
+struct FluidModifierData;
struct Object;
struct ParticleSystem;
+struct RegionView3D;
struct ViewLayer;
#define UBO_FIRST_COLOR colorWire
@@ -159,14 +161,14 @@ void DRW_globals_update(void);
void DRW_globals_free(void);
struct DRWView *DRW_view_create_with_zoffset(const struct DRWView *parent_view,
- const RegionView3D *rv3d,
+ const struct RegionView3D *rv3d,
float offset);
int DRW_object_wire_theme_get(struct Object *ob, struct ViewLayer *view_layer, float **r_color);
float *DRW_color_background_blend_get(int theme_id);
-bool DRW_object_is_flat(Object *ob, int *r_axis);
-bool DRW_object_axis_orthogonal_to_view(Object *ob, int axis);
+bool DRW_object_is_flat(struct Object *ob, int *r_axis);
+bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis);
/* draw_hair.c */
@@ -176,11 +178,28 @@ 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);
void DRW_hair_free(void);
+/* draw_fluid.c */
+
+/* Fluid simulation. */
+void DRW_smoke_ensure(struct FluidModifierData *fmd, int highres);
+void DRW_smoke_ensure_coba_field(struct FluidModifierData *fmd);
+void DRW_smoke_ensure_velocity(struct FluidModifierData *fmd);
+
+void DRW_smoke_free(struct FluidModifierData *fmd);
+void DRW_smoke_free_velocity(struct FluidModifierData *fmd);
+
/* draw_common.c */
struct DRW_Global {
/** If needed, contains all global/Theme colors
diff --git a/source/blender/gpu/intern/gpu_draw_smoke.c b/source/blender/draw/intern/draw_fluid.c
index 80c59ed47c9..33311dc698f 100644
--- a/source/blender/gpu/intern/gpu_draw_smoke.c
+++ b/source/blender/draw/intern/draw_fluid.c
@@ -35,10 +35,10 @@
#include "BKE_colorband.h"
-#include "GPU_draw.h"
-#include "GPU_glew.h"
#include "GPU_texture.h"
+#include "draw_common.h" /* Own include. */
+
#ifdef WITH_FLUID
# include "manta_fluid_API.h"
#endif
@@ -62,7 +62,8 @@ static void create_flame_spectrum_texture(float *data)
# define MAX_FIRE_ALPHA 0.06f
# define FULL_ON_FIRE 100
- float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels");
+ float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float),
+ "spec_pixels");
blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000);
@@ -99,12 +100,13 @@ 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]);
}
}
static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba)
{
- float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__);
+ float *data = (float *)MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__);
switch (type) {
case TFUNC_FLAME_SPECTRUM:
@@ -115,7 +117,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);
@@ -127,110 +129,110 @@ static void swizzle_texture_channel_single(GPUTexture *tex)
/* Swizzle texture channels so that we get useful RGBA values when sampling
* a texture with fewer channels, e.g. when using density as color. */
GPU_texture_bind(tex, 0);
- GPU_texture_swizzle_channel_auto(tex, 1);
+ GPU_texture_swizzle_set(tex, "rrr1");
GPU_texture_unbind(tex);
}
-static GPUTexture *create_field_texture(FluidDomainSettings *mds)
+static GPUTexture *create_field_texture(FluidDomainSettings *fds)
{
float *field = NULL;
- switch (mds->coba_field) {
+ switch (fds->coba_field) {
case FLUID_DOMAIN_FIELD_DENSITY:
- field = manta_smoke_get_density(mds->fluid);
+ field = manta_smoke_get_density(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_HEAT:
- field = manta_smoke_get_heat(mds->fluid);
+ field = manta_smoke_get_heat(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FUEL:
- field = manta_smoke_get_fuel(mds->fluid);
+ field = manta_smoke_get_fuel(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_REACT:
- field = manta_smoke_get_react(mds->fluid);
+ field = manta_smoke_get_react(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FLAME:
- field = manta_smoke_get_flame(mds->fluid);
+ field = manta_smoke_get_flame(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_VELOCITY_X:
- field = manta_get_velocity_x(mds->fluid);
+ field = manta_get_velocity_x(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_VELOCITY_Y:
- field = manta_get_velocity_y(mds->fluid);
+ field = manta_get_velocity_y(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_VELOCITY_Z:
- field = manta_get_velocity_z(mds->fluid);
+ field = manta_get_velocity_z(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_COLOR_R:
- field = manta_smoke_get_color_r(mds->fluid);
+ field = manta_smoke_get_color_r(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_COLOR_G:
- field = manta_smoke_get_color_g(mds->fluid);
+ field = manta_smoke_get_color_g(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_COLOR_B:
- field = manta_smoke_get_color_b(mds->fluid);
+ field = manta_smoke_get_color_b(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FORCE_X:
- field = manta_get_force_x(mds->fluid);
+ field = manta_get_force_x(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FORCE_Y:
- field = manta_get_force_y(mds->fluid);
+ field = manta_get_force_y(fds->fluid);
break;
case FLUID_DOMAIN_FIELD_FORCE_Z:
- field = manta_get_force_z(mds->fluid);
+ field = manta_get_force_z(fds->fluid);
break;
default:
return NULL;
}
GPUTexture *tex = GPU_texture_create_nD(
- mds->res[0], mds->res[1], mds->res[2], 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL);
+ UNPACK3(fds->res), 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL);
swizzle_texture_channel_single(tex);
return tex;
}
-static GPUTexture *create_density_texture(FluidDomainSettings *mds, int highres)
+static GPUTexture *create_density_texture(FluidDomainSettings *fds, int highres)
{
- int *dim = (highres) ? mds->res_noise : mds->res;
+ int *dim = (highres) ? fds->res_noise : fds->res;
float *data;
if (highres) {
- data = manta_smoke_turbulence_get_density(mds->fluid);
+ data = manta_noise_get_density(fds->fluid);
}
else {
- data = manta_smoke_get_density(mds->fluid);
+ data = manta_smoke_get_density(fds->fluid);
}
GPUTexture *tex = GPU_texture_create_nD(
- dim[0], dim[1], dim[2], 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL);
+ UNPACK3(dim), 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL);
swizzle_texture_channel_single(tex);
return tex;
}
-static GPUTexture *create_color_texture(FluidDomainSettings *mds, int highres)
+static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres)
{
- const bool has_color = (highres) ? manta_smoke_turbulence_has_colors(mds->fluid) :
- manta_smoke_has_colors(mds->fluid);
+ const bool has_color = (highres) ? manta_noise_has_colors(fds->fluid) :
+ manta_smoke_has_colors(fds->fluid);
if (!has_color) {
return NULL;
}
- int cell_count = (highres) ? manta_smoke_turbulence_get_cells(mds->fluid) : mds->total_cells;
- int *dim = (highres) ? mds->res_noise : mds->res;
- float *data = MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture");
+ int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells;
+ int *dim = (highres) ? fds->res_noise : fds->res;
+ float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture");
if (data == NULL) {
return NULL;
}
if (highres) {
- manta_smoke_turbulence_get_rgba(mds->fluid, data, 0);
+ manta_noise_get_rgba(fds->fluid, data, 0);
}
else {
- manta_smoke_get_rgba(mds->fluid, data, 0);
+ manta_smoke_get_rgba(fds->fluid, data, 0);
}
GPUTexture *tex = GPU_texture_create_nD(
@@ -241,22 +243,22 @@ static GPUTexture *create_color_texture(FluidDomainSettings *mds, int highres)
return tex;
}
-static GPUTexture *create_flame_texture(FluidDomainSettings *mds, int highres)
+static GPUTexture *create_flame_texture(FluidDomainSettings *fds, int highres)
{
float *source = NULL;
- const bool has_fuel = (highres) ? manta_smoke_turbulence_has_fuel(mds->fluid) :
- manta_smoke_has_fuel(mds->fluid);
- int *dim = (highres) ? mds->res_noise : mds->res;
+ const bool has_fuel = (highres) ? manta_noise_has_fuel(fds->fluid) :
+ manta_smoke_has_fuel(fds->fluid);
+ int *dim = (highres) ? fds->res_noise : fds->res;
if (!has_fuel) {
return NULL;
}
if (highres) {
- source = manta_smoke_turbulence_get_flame(mds->fluid);
+ source = manta_noise_get_flame(fds->fluid);
}
else {
- source = manta_smoke_get_flame(mds->fluid);
+ source = manta_smoke_get_flame(fds->fluid);
}
GPUTexture *tex = GPU_texture_create_nD(
@@ -275,90 +277,88 @@ static GPUTexture *create_flame_texture(FluidDomainSettings *mds, int highres)
/** \name Public API
* \{ */
-void GPU_free_smoke(FluidModifierData *mmd)
+void DRW_smoke_free(FluidModifierData *fmd)
{
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) {
- if (mmd->domain->tex_density) {
- GPU_texture_free(mmd->domain->tex_density);
- mmd->domain->tex_density = NULL;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
+ if (fmd->domain->tex_density) {
+ GPU_texture_free(fmd->domain->tex_density);
+ fmd->domain->tex_density = NULL;
}
- if (mmd->domain->tex_color) {
- GPU_texture_free(mmd->domain->tex_color);
- mmd->domain->tex_color = NULL;
+ if (fmd->domain->tex_color) {
+ GPU_texture_free(fmd->domain->tex_color);
+ fmd->domain->tex_color = NULL;
}
- if (mmd->domain->tex_shadow) {
- GPU_texture_free(mmd->domain->tex_shadow);
- mmd->domain->tex_shadow = NULL;
+ if (fmd->domain->tex_shadow) {
+ GPU_texture_free(fmd->domain->tex_shadow);
+ fmd->domain->tex_shadow = NULL;
}
- if (mmd->domain->tex_flame) {
- GPU_texture_free(mmd->domain->tex_flame);
- mmd->domain->tex_flame = NULL;
+ if (fmd->domain->tex_flame) {
+ GPU_texture_free(fmd->domain->tex_flame);
+ fmd->domain->tex_flame = NULL;
}
- if (mmd->domain->tex_flame_coba) {
- GPU_texture_free(mmd->domain->tex_flame_coba);
- mmd->domain->tex_flame_coba = NULL;
+ if (fmd->domain->tex_flame_coba) {
+ GPU_texture_free(fmd->domain->tex_flame_coba);
+ fmd->domain->tex_flame_coba = NULL;
}
- if (mmd->domain->tex_coba) {
- GPU_texture_free(mmd->domain->tex_coba);
- mmd->domain->tex_coba = NULL;
+ if (fmd->domain->tex_coba) {
+ GPU_texture_free(fmd->domain->tex_coba);
+ fmd->domain->tex_coba = NULL;
}
- if (mmd->domain->tex_field) {
- GPU_texture_free(mmd->domain->tex_field);
- mmd->domain->tex_field = NULL;
+ if (fmd->domain->tex_field) {
+ GPU_texture_free(fmd->domain->tex_field);
+ fmd->domain->tex_field = NULL;
}
}
}
-void GPU_create_smoke_coba_field(FluidModifierData *mmd)
+void DRW_smoke_ensure_coba_field(FluidModifierData *fmd)
{
#ifndef WITH_FLUID
- UNUSED_VARS(mmd);
+ UNUSED_VARS(fmd);
#else
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
- FluidDomainSettings *mds = mmd->domain;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ FluidDomainSettings *fds = fmd->domain;
- if (!mds->tex_field) {
- mds->tex_field = create_field_texture(mds);
+ if (!fds->tex_field) {
+ fds->tex_field = create_field_texture(fds);
}
- if (!mds->tex_coba) {
- mds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, mds->coba);
+ if (!fds->tex_coba) {
+ fds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, fds->coba);
}
}
#endif
}
-void GPU_create_smoke(FluidModifierData *mmd, int highres)
+void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
{
#ifndef WITH_FLUID
- UNUSED_VARS(mmd, highres);
+ UNUSED_VARS(fmd, highres);
#else
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
- FluidDomainSettings *mds = mmd->domain;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ FluidDomainSettings *fds = fmd->domain;
- if (!mds->tex_density) {
- mds->tex_density = create_density_texture(mds, highres);
+ if (!fds->tex_density) {
+ fds->tex_density = create_density_texture(fds, highres);
}
- if (!mds->tex_color) {
- mds->tex_color = create_color_texture(mds, highres);
+ if (!fds->tex_color) {
+ fds->tex_color = create_color_texture(fds, highres);
}
- if (!mds->tex_flame) {
- mds->tex_flame = create_flame_texture(mds, highres);
+ if (!fds->tex_flame) {
+ fds->tex_flame = create_flame_texture(fds, highres);
}
- if (!mds->tex_flame_coba && mds->tex_flame) {
- mds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL);
+ if (!fds->tex_flame_coba && fds->tex_flame) {
+ fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL);
}
- if (!mds->tex_shadow) {
- mds->tex_shadow = GPU_texture_create_nD(mds->res[0],
- mds->res[1],
- mds->res[2],
+ if (!fds->tex_shadow) {
+ fds->tex_shadow = GPU_texture_create_nD(UNPACK3(fds->res),
3,
- manta_smoke_get_shadow(mds->fluid),
+ manta_smoke_get_shadow(fds->fluid),
GPU_R8,
GPU_DATA_FLOAT,
0,
@@ -369,53 +369,50 @@ void GPU_create_smoke(FluidModifierData *mmd, int highres)
#endif /* WITH_FLUID */
}
-void GPU_create_smoke_velocity(FluidModifierData *mmd)
+void DRW_smoke_ensure_velocity(FluidModifierData *fmd)
{
#ifndef WITH_FLUID
- UNUSED_VARS(mmd);
+ UNUSED_VARS(fmd);
#else
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN) {
- FluidDomainSettings *mds = mmd->domain;
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
+ FluidDomainSettings *fds = fmd->domain;
- const float *vel_x = manta_get_velocity_x(mds->fluid);
- const float *vel_y = manta_get_velocity_y(mds->fluid);
- const float *vel_z = manta_get_velocity_z(mds->fluid);
+ const float *vel_x = manta_get_velocity_x(fds->fluid);
+ const float *vel_y = manta_get_velocity_y(fds->fluid);
+ const float *vel_z = manta_get_velocity_z(fds->fluid);
if (ELEM(NULL, vel_x, vel_y, vel_z)) {
return;
}
- if (!mds->tex_velocity_x) {
- mds->tex_velocity_x = GPU_texture_create_3d(
- mds->res[0], mds->res[1], mds->res[2], GPU_R16F, vel_x, NULL);
- mds->tex_velocity_y = GPU_texture_create_3d(
- mds->res[0], mds->res[1], mds->res[2], GPU_R16F, vel_y, NULL);
- mds->tex_velocity_z = GPU_texture_create_3d(
- mds->res[0], mds->res[1], mds->res[2], GPU_R16F, vel_z, NULL);
+ if (!fds->tex_velocity_x) {
+ fds->tex_velocity_x = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_x, NULL);
+ fds->tex_velocity_y = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_y, NULL);
+ fds->tex_velocity_z = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_z, NULL);
}
}
#endif /* WITH_FLUID */
}
-/* TODO Unify with the other GPU_free_smoke. */
-void GPU_free_smoke_velocity(FluidModifierData *mmd)
+/* TODO Unify with the other DRW_smoke_free. */
+void DRW_smoke_free_velocity(FluidModifierData *fmd)
{
- if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) {
- if (mmd->domain->tex_velocity_x) {
- GPU_texture_free(mmd->domain->tex_velocity_x);
+ if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
+ if (fmd->domain->tex_velocity_x) {
+ GPU_texture_free(fmd->domain->tex_velocity_x);
}
- if (mmd->domain->tex_velocity_y) {
- GPU_texture_free(mmd->domain->tex_velocity_y);
+ if (fmd->domain->tex_velocity_y) {
+ GPU_texture_free(fmd->domain->tex_velocity_y);
}
- if (mmd->domain->tex_velocity_z) {
- GPU_texture_free(mmd->domain->tex_velocity_z);
+ if (fmd->domain->tex_velocity_z) {
+ GPU_texture_free(fmd->domain->tex_velocity_z);
}
- mmd->domain->tex_velocity_x = NULL;
- mmd->domain->tex_velocity_y = NULL;
- mmd->domain->tex_velocity_z = NULL;
+ fmd->domain->tex_velocity_x = NULL;
+ fmd->domain->tex_velocity_y = NULL;
+ fmd->domain->tex_velocity_z = NULL;
}
}
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index 2fdaf0d5345..cbdcbbf9090 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;
}
@@ -316,7 +343,7 @@ void DRW_hair_update(void)
DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp);
/* Readback result to main memory. */
- GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, data);
+ GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data);
/* Upload back to VBO. */
GPU_vertbuf_use(pr_call->vbo);
glBufferSubData(GL_ARRAY_BUFFER,
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index e7dff422105..712a93e8880 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -132,11 +132,17 @@ static void drw_task_graph_init(void)
{
BLI_assert(DST.task_graph == NULL);
DST.task_graph = BLI_task_graph_create();
+ DST.delayed_extraction = BLI_gset_ptr_new(__func__);
}
static void drw_task_graph_deinit(void)
{
BLI_task_graph_work_and_wait(DST.task_graph);
+
+ BLI_gset_free(DST.delayed_extraction, (void (*)(void *key))drw_batch_cache_generate_requested);
+ DST.delayed_extraction = NULL;
+ BLI_task_graph_work_and_wait(DST.task_graph);
+
BLI_task_graph_free(DST.task_graph);
DST.task_graph = NULL;
}
@@ -363,10 +369,12 @@ void DRW_engine_viewport_data_size_get(
}
/* WARNING: only use for custom pipeline. 99% of the time, you don't want to use this. */
-void DRW_render_viewport_size_set(int size[2])
+void DRW_render_viewport_size_set(const 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)
@@ -1467,6 +1475,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
/* Only iterate over objects for internal engines or when overlays are enabled */
if (do_populate_loop) {
+ DST.dupli_origin = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;
@@ -1485,6 +1494,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
drw_duplidata_free();
drw_engines_cache_finish();
+ drw_task_graph_deinit();
DRW_render_instance_buffer_finish();
#ifdef USE_PROFILE
@@ -1493,7 +1503,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
#endif
}
- drw_task_graph_deinit();
DRW_stats_begin();
GPU_framebuffer_bind(DST.default_framebuffer);
@@ -1628,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)
@@ -1658,24 +1660,8 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
RenderEngineType *engine_type = engine->type;
RenderData *r = &scene->r;
Render *render = engine->re;
- /* Changing Context */
- if (G.background && DST.gl_context == NULL) {
- WM_init_opengl(G_MAIN);
- }
- void *re_gl_context = RE_gl_context_get(render);
- void *re_gpu_context = NULL;
-
- /* Changing Context */
- if (re_gl_context != NULL) {
- DRW_opengl_render_context_enable(re_gl_context);
- /* We need to query gpu context after a gl context has been bound. */
- re_gpu_context = RE_gpu_context_get(render);
- DRW_gpu_render_context_enable(re_gpu_context);
- }
- else {
- DRW_opengl_context_enable();
- }
+ DRW_render_context_enable(render);
/* Reset before using it. */
drw_state_prepare_clean_for_draw(&DST);
@@ -1712,7 +1698,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);
}
@@ -1727,14 +1713,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph
/* Restore Drawing area. */
GPU_framebuffer_restore();
- /* Changing Context */
- if (re_gl_context != NULL) {
- DRW_gpu_render_context_disable(re_gpu_context);
- DRW_opengl_render_context_disable(re_gl_context);
- }
- else {
- DRW_opengl_context_disable();
- }
+ DRW_render_context_disable(render);
DST.buffer_finish_called = false;
}
@@ -1747,24 +1726,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
DrawEngineType *draw_engine_type = engine_type->draw_engine;
Render *render = engine->re;
- if (G.background && DST.gl_context == NULL) {
- WM_init_opengl(G_MAIN);
- }
-
- void *re_gl_context = RE_gl_context_get(render);
- void *re_gpu_context = NULL;
-
- /* Changing Context */
- if (re_gl_context != NULL) {
- DRW_opengl_render_context_enable(re_gl_context);
- /* We need to query gpu context after a gl context has been bound. */
- re_gpu_context = RE_gpu_context_get(render);
- DRW_gpu_render_context_enable(re_gpu_context);
- }
- else {
- DRW_opengl_context_enable();
- }
-
/* IMPORTANT: We don't support immediate mode in render mode!
* This shall remain in effect until immediate mode supports
* multiple threads. */
@@ -1791,9 +1752,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type);
- /* set default viewport */
- glViewport(0, 0, size[0], size[1]);
-
/* Main rendering. */
rctf view_rect;
rcti render_rect;
@@ -1807,12 +1765,15 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
/* Reset state before drawing */
DRW_state_reset();
+ /* set default viewport */
+ GPU_viewport(0, 0, size[0], size[1]);
+
/* Init render result. */
RenderResult *render_result = RE_engine_begin_result(engine,
0,
0,
- (int)size[0],
- (int)size[1],
+ size[0],
+ size[1],
view_layer->name,
/* RR_ALL_VIEWS */ NULL);
@@ -1820,7 +1781,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;
}
@@ -1840,15 +1801,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph)
/* Reset state after drawing */
DRW_state_reset();
-
- /* Changing Context */
- if (re_gl_context != NULL) {
- DRW_gpu_render_context_disable(re_gpu_context);
- DRW_opengl_render_context_disable(re_gl_context);
- }
- else {
- DRW_opengl_context_disable();
- }
}
void DRW_render_object_iter(
@@ -1864,6 +1816,7 @@ void DRW_render_object_iter(
const int object_type_exclude_viewport = draw_ctx->v3d ?
draw_ctx->v3d->object_type_exclude_viewport :
0;
+ DST.dupli_origin = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) == 0) {
DST.dupli_parent = data_.dupli_parent;
@@ -1941,6 +1894,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;
@@ -2121,6 +2096,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
const int object_type_exclude_select = (v3d->object_type_exclude_viewport |
v3d->object_type_exclude_select);
bool filter_exclude = false;
+ DST.dupli_origin = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
if (!BKE_object_is_visible_in_viewport(v3d, ob)) {
continue;
@@ -2271,6 +2247,7 @@ static void drw_draw_depth_loop_imp(struct Depsgraph *depsgraph,
drw_engines_world_update(DST.draw_ctx.scene);
const int object_type_exclude_viewport = v3d->object_type_exclude_viewport;
+ DST.dupli_origin = NULL;
DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (DST.draw_ctx.depsgraph, ob) {
if ((object_type_exclude_viewport & (1 << ob->type)) != 0) {
continue;
@@ -2288,9 +2265,9 @@ static void drw_draw_depth_loop_imp(struct Depsgraph *depsgraph,
drw_duplidata_free();
drw_engines_cache_finish();
+ drw_task_graph_deinit();
DRW_render_instance_buffer_finish();
}
- drw_task_graph_deinit();
/* Start Drawing */
DRW_state_reset();
@@ -2364,6 +2341,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);
@@ -2384,14 +2372,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);
@@ -2407,6 +2394,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
drw_engines_cache_finish();
+ drw_task_graph_deinit();
#if 0 /* This is a workaround to a nasty bug that seems to be a nasty driver bug. (See T69377) */
DRW_render_instance_buffer_finish();
#else
@@ -2415,7 +2403,6 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
drw_resource_buffer_finish(DST.vmempool);
#endif
}
- drw_task_graph_deinit();
/* Start Drawing */
DRW_state_reset();
@@ -2432,12 +2419,6 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
#endif
}
-/** See #DRW_shgroup_world_clip_planes_from_rv3d. */
-static void draw_world_clip_planes_from_rv3d(GPUBatch *batch, const float world_clip_planes[6][4])
-{
- GPU_batch_uniform_4fv_array(batch, "WorldClipPlanes", 6, world_clip_planes[0]);
-}
-
/**
* Clears the Depth Buffer and draws only the specified object.
*/
@@ -2460,7 +2441,7 @@ void DRW_draw_depth_object(
const float(*world_clip_planes)[4] = NULL;
if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- ED_view3d_clipping_set(rv3d);
+ GPU_clip_distances(6);
ED_view3d_clipping_local(rv3d, object->obmat);
world_clip_planes = rv3d->clip_local;
}
@@ -2488,7 +2469,7 @@ void DRW_draw_depth_object(
GPU_SHADER_CFG_DEFAULT;
GPU_batch_program_set_builtin_with_config(batch, GPU_SHADER_3D_DEPTH_ONLY, sh_cfg);
if (world_clip_planes != NULL) {
- draw_world_clip_planes_from_rv3d(batch, world_clip_planes);
+ GPU_batch_uniform_4fv_array(batch, "WorldClipPlanes", 6, world_clip_planes[0]);
}
GPU_batch_draw(batch);
@@ -2499,7 +2480,7 @@ void DRW_draw_depth_object(
}
if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- ED_view3d_clipping_disable();
+ GPU_clip_distances(0);
}
GPU_matrix_set(rv3d->viewmat);
@@ -2732,6 +2713,42 @@ void DRW_engines_free(void)
DRW_opengl_context_disable();
}
+void DRW_render_context_enable(Render *render)
+{
+ if (G.background && DST.gl_context == NULL) {
+ WM_init_opengl(G_MAIN);
+ }
+
+ void *re_gl_context = RE_gl_context_get(render);
+
+ /* Changing Context */
+ if (re_gl_context != NULL) {
+ DRW_opengl_render_context_enable(re_gl_context);
+ /* We need to query gpu context after a gl context has been bound. */
+ void *re_gpu_context = NULL;
+ re_gpu_context = RE_gpu_context_get(render);
+ DRW_gpu_render_context_enable(re_gpu_context);
+ }
+ else {
+ DRW_opengl_context_enable();
+ }
+}
+
+void DRW_render_context_disable(Render *render)
+{
+ void *re_gl_context = RE_gl_context_get(render);
+
+ if (re_gl_context != NULL) {
+ void *re_gpu_context = NULL;
+ re_gpu_context = RE_gpu_context_get(render);
+ DRW_gpu_render_context_disable(re_gpu_context);
+ DRW_opengl_render_context_disable(re_gl_context);
+ }
+ else {
+ DRW_opengl_context_disable();
+ }
+}
+
/** \} */
/** \name Init/Exit (DRW_opengl_ctx)
@@ -2872,8 +2889,8 @@ void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context))
* switch to it just to submit the final frame, which has notable performance impact.
*
* We could "inject" a context through DRW_opengl_render_context_enable(), but that would have to
- * work from the main thread, which is tricky to get working too. The preferable solution would be
- * using a separate thread for VR drawing where a single context can stay active. */
+ * work from the main thread, which is tricky to get working too. The preferable solution would
+ * be using a separate thread for VR drawing where a single context can stay active. */
void *DRW_xr_opengl_context_get(void)
{
return DST.gl_context;
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 6cae2a4f9f6..a448a94185a 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -380,6 +380,7 @@ typedef struct DRWViewUboStorage {
float wininv[4][4];
float clipplanes[6][4];
+ float viewvecs[2][4];
/* Should not be here. Not view dependent (only main view). */
float viewcamtexcofac[4];
} DRWViewUboStorage;
@@ -548,6 +549,8 @@ typedef struct DRWManager {
#endif
struct TaskGraph *task_graph;
+ /* Contains list of objects that needs to be extracted from other objects. */
+ struct GSet *delayed_extraction;
/* ---------- Nothing after this point is cleared after use ----------- */
@@ -581,10 +584,11 @@ void drw_state_set(DRWState state);
void drw_debug_draw(void);
void drw_debug_init(void);
-eDRWCommandType command_type_get(uint64_t *command_type_bits, int index);
+eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index);
void drw_batch_cache_validate(Object *ob);
void drw_batch_cache_generate_requested(struct Object *ob);
+void drw_batch_cache_generate_requested_delayed(Object *ob);
void drw_resource_buffer_finish(ViewportMemoryPool *vmempool);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 07fb97236fb..3184d6155d2 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,
@@ -610,7 +623,7 @@ static void command_type_set(uint64_t *command_type_bits, int index, eDRWCommand
command_type_bits[index / 16] |= ((uint64_t)type) << ((index % 16) * 4);
}
-eDRWCommandType command_type_get(uint64_t *command_type_bits, int index)
+eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index)
{
return ((command_type_bits[index / 16] >> ((index % 16) * 4)) & 0xF);
}
@@ -780,10 +793,10 @@ void DRW_shgroup_call_range(
drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct);
}
+/* A count of 0 instance will use the default number of instance in the batch. */
void DRW_shgroup_call_instance_range(
DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct)
{
- BLI_assert(i_ct > 0);
BLI_assert(geom != NULL);
if (G.f & G_FLAG_PICKSEL) {
drw_command_set_select_id(shgroup, NULL, DST.select_id);
@@ -1279,13 +1292,10 @@ static DRWShadingGroup *drw_shgroup_material_create_ex(GPUPass *gpupass, DRWPass
}
static void drw_shgroup_material_texture(DRWShadingGroup *grp,
- GPUMaterialTexture *tex,
+ GPUTexture *gputex,
const char *name,
- eGPUSamplerState state,
- int textarget)
+ eGPUSamplerState state)
{
- GPUTexture *gputex = GPU_texture_from_blender(tex->ima, tex->iuser, NULL, textarget);
-
DRW_shgroup_uniform_texture_ex(grp, name, gputex, state);
GPUTexture **gputex_ref = BLI_memblock_alloc(DST.vmempool->images);
@@ -1301,15 +1311,16 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial
LISTBASE_FOREACH (GPUMaterialTexture *, tex, &textures) {
if (tex->ima) {
/* Image */
+ GPUTexture *gputex;
if (tex->tiled_mapping_name[0]) {
- drw_shgroup_material_texture(
- grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D_ARRAY);
- drw_shgroup_material_texture(
- grp, tex, tex->tiled_mapping_name, tex->sampler_state, GL_TEXTURE_1D_ARRAY);
+ gputex = BKE_image_get_gpu_tiles(tex->ima, tex->iuser, NULL);
+ drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state);
+ gputex = BKE_image_get_gpu_tilemap(tex->ima, tex->iuser, NULL);
+ drw_shgroup_material_texture(grp, gputex, tex->tiled_mapping_name, tex->sampler_state);
}
else {
- drw_shgroup_material_texture(
- grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D);
+ gputex = BKE_image_get_gpu_texture(tex->ima, tex->iuser, NULL);
+ drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state);
}
}
else if (tex->colorband) {
@@ -1643,6 +1654,52 @@ static void draw_view_matrix_state_update(DRWViewUboStorage *storage,
mul_m4_m4m4(storage->persmat, winmat, viewmat);
invert_m4_m4(storage->persinv, storage->persmat);
+
+ const bool is_persp = (winmat[3][3] == 0.0f);
+
+ /* Near clip distance. */
+ storage->viewvecs[0][3] = (is_persp) ? -winmat[3][2] / (winmat[2][2] - 1.0f) :
+ -(winmat[3][2] + 1.0f) / winmat[2][2];
+
+ /* Far clip distance. */
+ storage->viewvecs[1][3] = (is_persp) ? -winmat[3][2] / (winmat[2][2] + 1.0f) :
+ -(winmat[3][2] - 1.0f) / winmat[2][2];
+
+ /* view vectors for the corners of the view frustum.
+ * Can be used to recreate the world space position easily */
+ float view_vecs[4][3] = {
+ {-1.0f, -1.0f, -1.0f},
+ {1.0f, -1.0f, -1.0f},
+ {-1.0f, 1.0f, -1.0f},
+ {-1.0f, -1.0f, 1.0f},
+ };
+
+ /* convert the view vectors to view space */
+ for (int i = 0; i < 4; i++) {
+ mul_project_m4_v3(storage->wininv, view_vecs[i]);
+ /* normalized trick see:
+ * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
+ if (is_persp) {
+ /* Divide XY by Z. */
+ mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]);
+ }
+ }
+
+ /**
+ * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and
+ * view_vecs[1] is the vector going from the near-bottom-left corner to
+ * the far-top-right corner.
+ * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner
+ * when Z = 1, and top-left corner if Z = 1.
+ * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed)
+ * distance from the near plane to the far clip plane.
+ */
+ copy_v3_v3(storage->viewvecs[0], view_vecs[0]);
+
+ /* we need to store the differences */
+ storage->viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0];
+ storage->viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1];
+ storage->viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2];
}
/* Create a view with culling. */
@@ -1786,6 +1843,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_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 59b4e9af14e..91cbc03e5a4 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -98,12 +98,7 @@ void drw_state_set(DRWState state)
{
int test;
if ((test = CHANGED_TO(DRW_STATE_WRITE_DEPTH))) {
- if (test == 1) {
- glDepthMask(GL_TRUE);
- }
- else {
- glDepthMask(GL_FALSE);
- }
+ GPU_depth_mask(test == 1);
}
}
@@ -142,10 +137,10 @@ void drw_state_set(DRWState state)
int test;
if ((test = CHANGED_TO(DRW_STATE_WRITE_COLOR))) {
if (test == 1) {
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(true, true, true, true);
}
else {
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ GPU_color_mask(false, false, false, false);
}
}
}
@@ -355,14 +350,10 @@ void drw_state_set(DRWState state)
int test;
if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) {
if (test == 1) {
- for (int i = 0; i < DST.view_active->clip_planes_len; i++) {
- glEnable(GL_CLIP_DISTANCE0 + i);
- }
+ GPU_clip_distances(DST.view_active->clip_planes_len);
}
else {
- for (int i = 0; i < MAX_CLIP_PLANES; i++) {
- glDisable(GL_CLIP_DISTANCE0 + i);
- }
+ GPU_clip_distances(0);
}
}
}
@@ -455,6 +446,7 @@ void DRW_state_reset(void)
DRW_state_reset_ex(DRW_STATE_DEFAULT);
GPU_texture_unbind_all();
+ GPU_uniformbuffer_unbind_all();
/* Should stay constant during the whole rendering. */
GPU_point_size(5);
@@ -678,8 +670,7 @@ BLI_INLINE void draw_geometry_bind(DRWShadingGroup *shgroup, GPUBatch *geom)
DST.batch = geom;
- GPU_batch_program_set_no_use(
- geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
+ GPU_batch_set_shader_no_bind(geom, shgroup->shader);
geom->program_in_use = true; /* XXX hacking #GPUBatch */
@@ -782,10 +773,11 @@ static bool ubo_bindings_validate(DRWShadingGroup *shgroup)
DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes,
&shgroup->pass_handle);
- printf("Pass : %s, Shader : %s, Block : %s\n",
+ printf("Pass : %s, Shader : %s, Block : %s, Binding %d\n",
parent_pass->name,
shgroup->shader->name,
- blockname);
+ blockname,
+ binding);
}
}
# endif
@@ -1115,6 +1107,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
/* Unbinding can be costly. Skip in normal condition. */
if (G.debug & G_DEBUG_GPU) {
GPU_texture_unbind_all();
+ GPU_uniformbuffer_unbind_all();
}
}
GPU_shader_bind(shgroup->shader);
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index 6304b707cb9..fec234c5015 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -91,10 +91,13 @@ static void drw_deferred_shader_queue_free(ListBase *queue)
}
}
-static void drw_deferred_shader_compilation_exec(void *custom_data,
- short *stop,
- short *do_update,
- float *progress)
+static void drw_deferred_shader_compilation_exec(
+ void *custom_data,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
DRWShaderCompiler *comp = (DRWShaderCompiler *)custom_data;
void *gl_context = comp->gl_context;
@@ -325,6 +328,26 @@ GPUShader *DRW_shader_create_with_lib(
return sh;
}
+GPUShader *DRW_shader_create_with_shaderlib(const char *vert,
+ const char *geom,
+ const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines)
+{
+ GPUShader *sh;
+ char *vert_with_lib = DRW_shader_library_create_shader_string(lib, vert);
+ char *frag_with_lib = DRW_shader_library_create_shader_string(lib, frag);
+ char *geom_with_lib = (geom) ? DRW_shader_library_create_shader_string(lib, geom) : NULL;
+
+ sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines, __func__);
+
+ MEM_SAFE_FREE(vert_with_lib);
+ MEM_SAFE_FREE(frag_with_lib);
+ MEM_SAFE_FREE(geom_with_lib);
+
+ return sh;
+}
+
GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
const char *geom,
const char *defines,
@@ -349,6 +372,22 @@ GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines)
datatoc_common_fullscreen_vert_glsl, frag, NULL, NULL, defines, __func__);
}
+GPUShader *DRW_shader_create_fullscreen_with_shaderlib(const char *frag,
+ const DRWShaderLibrary *lib,
+ const char *defines)
+{
+
+ GPUShader *sh;
+ char *vert = datatoc_common_fullscreen_vert_glsl;
+ char *frag_with_lib = DRW_shader_library_create_shader_string(lib, frag);
+
+ sh = GPU_shader_create(vert, frag_with_lib, NULL, NULL, defines, __func__);
+
+ MEM_SAFE_FREE(frag_with_lib);
+
+ return sh;
+}
+
GPUMaterial *DRW_shader_find_from_world(World *wo,
const void *engine_type,
const int options,
@@ -391,7 +430,8 @@ GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
const char *geom,
const char *frag_lib,
const char *defines,
- bool deferred)
+ bool deferred,
+ GPUMaterialEvalCallbackFn callback)
{
GPUMaterial *mat = NULL;
if (DRW_state_is_image_render() || !deferred) {
@@ -411,7 +451,8 @@ GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
geom,
frag_lib,
defines,
- wo->id.name);
+ wo->id.name,
+ callback);
}
if (GPU_material_status(mat) == GPU_MAT_QUEUED) {
@@ -431,7 +472,8 @@ GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
const char *geom,
const char *frag_lib,
const char *defines,
- bool deferred)
+ bool deferred,
+ GPUMaterialEvalCallbackFn callback)
{
GPUMaterial *mat = NULL;
if (DRW_state_is_image_render() || !deferred) {
@@ -451,7 +493,8 @@ GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
geom,
frag_lib,
defines,
- ma->id.name);
+ ma->id.name,
+ callback);
}
if (GPU_material_status(mat) == GPU_MAT_QUEUED) {
@@ -502,7 +545,7 @@ void DRW_shader_library_free(DRWShaderLibrary *lib)
MEM_SAFE_FREE(lib);
}
-static int drw_shader_library_search(DRWShaderLibrary *lib, const char *name)
+static int drw_shader_library_search(const DRWShaderLibrary *lib, const char *name)
{
for (int i = 0; i < MAX_LIB; i++) {
if (lib->libs[i]) {
@@ -518,18 +561,28 @@ static int drw_shader_library_search(DRWShaderLibrary *lib, const char *name)
}
/* Return bitmap of dependencies. */
-static uint32_t drw_shader_dependencies_get(DRWShaderLibrary *lib, char *lib_code)
+static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const char *lib_code)
{
/* Search dependencies. */
uint32_t deps = 0;
- char *haystack = lib_code;
+ const char *haystack = lib_code;
while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) {
haystack += 16;
int dep = drw_shader_library_search(lib, haystack);
if (dep == -1) {
+ char dbg_name[32];
+ int i = 0;
+ while ((haystack[0] != ')') && (i < 31)) {
+ dbg_name[i] = haystack[0];
+ haystack++;
+ i++;
+ }
+ dbg_name[i + 1] = '\0';
+
printf(
- "Error: Dependency not found.\n"
- "This might be due to bad lib ordering.\n");
+ "Error: Dependency not found: %s\n"
+ "This might be due to bad lib ordering.\n",
+ dbg_name);
BLI_assert(0);
}
else {
@@ -562,7 +615,7 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const ch
/* Return an allocN'ed string containing the shader code with its dependencies prepended.
* Caller must free the string with MEM_freeN after use. */
-char *DRW_shader_library_create_shader_string(DRWShaderLibrary *lib, char *shader_code)
+char *DRW_shader_library_create_shader_string(const DRWShaderLibrary *lib, const char *shader_code)
{
uint32_t deps = drw_shader_dependencies_get(lib, shader_code);
diff --git a/source/blender/draw/intern/draw_manager_text.c b/source/blender/draw/intern/draw_manager_text.c
index f4601fe4f48..b3c4c97715e 100644
--- a/source/blender/draw/intern/draw_manager_text.c
+++ b/source/blender/draw/intern/draw_manager_text.c
@@ -38,6 +38,7 @@
#include "DNA_view3d_types.h"
#include "GPU_matrix.h"
+#include "GPU_state.h"
#include "ED_screen.h"
#include "ED_view3d.h"
@@ -149,8 +150,9 @@ void DRW_text_cache_draw(DRWTextStore *dt, ARegion *region, struct View3D *v3d)
if (tot) {
int col_pack_prev = 0;
+ /* Disable clipping for text */
if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- ED_view3d_clipping_disable();
+ GPU_clip_distances(0);
}
float original_proj[4][4];
@@ -188,7 +190,7 @@ void DRW_text_cache_draw(DRWTextStore *dt, ARegion *region, struct View3D *v3d)
GPU_matrix_projection_set(original_proj);
if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- ED_view3d_clipping_enable();
+ GPU_clip_distances(6);
}
}
}
diff --git a/source/blender/draw/intern/draw_select_buffer.c b/source/blender/draw/intern/draw_select_buffer.c
index 81c9f400f6e..ee5561e1e38 100644
--- a/source/blender/draw/intern/draw_select_buffer.c
+++ b/source/blender/draw/intern/draw_select_buffer.c
@@ -84,14 +84,15 @@ uint *DRW_select_buffer_read(struct Depsgraph *depsgraph,
GPUFrameBuffer *select_id_fb = DRW_engine_select_framebuffer_get();
GPU_framebuffer_bind(select_id_fb);
- glReadBuffer(GL_COLOR_ATTACHMENT0);
- glReadPixels(rect_clamp.xmin,
- rect_clamp.ymin,
- BLI_rcti_size_x(&rect_clamp),
- BLI_rcti_size_y(&rect_clamp),
- GL_RED_INTEGER,
- GL_UNSIGNED_INT,
- r_buf);
+ GPU_framebuffer_read_color(select_id_fb,
+ rect_clamp.xmin,
+ rect_clamp.ymin,
+ BLI_rcti_size_x(&rect_clamp),
+ BLI_rcti_size_y(&rect_clamp),
+ 1,
+ 0,
+ GPU_DATA_UNSIGNED_INT,
+ r_buf);
if (!BLI_rcti_compare(rect, &rect_clamp)) {
/* The rect has been clamped so you need to realign the buffer and fill in the blanks */
@@ -122,7 +123,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 +165,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 +338,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,
@@ -395,7 +395,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph,
int center_x = width / 2;
int center_y = height / 2;
- /* Manhatten distance in keeping with other screen-based selection. */
+ /* Manhattan distance in keeping with other screen-based selection. */
*dist = (uint)(abs(hit_x - center_x) + abs(hit_y - center_y));
/* Indices start at 1 here. */
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index 06026d51faf..b42700b2c7e 100644
--- a/source/blender/draw/intern/draw_view.c
+++ b/source/blender/draw/intern/draw_view.c
@@ -103,9 +103,9 @@ void DRW_draw_cursor(void)
Scene *scene = draw_ctx->scene;
ViewLayer *view_layer = draw_ctx->view_layer;
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glDepthMask(GL_FALSE);
- glDisable(GL_DEPTH_TEST);
+ GPU_color_mask(true, true, true, true);
+ GPU_depth_mask(false);
+ GPU_depth_test(false);
if (is_cursor_visible(draw_ctx, scene, view_layer)) {
int co[2];
@@ -184,8 +184,7 @@ void DRW_draw_cursor(void)
GPUBatch *cursor_batch = DRW_cache_cursor_get(is_aligned);
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR);
- GPU_batch_program_set(
- cursor_batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader));
+ GPU_batch_set_shader(cursor_batch, shader);
GPU_batch_draw(cursor_batch);
@@ -217,5 +216,5 @@ void DRW_draw_gizmo_2d(void)
WM_gizmomap_draw(region->gizmo_map, draw_ctx->evil_C, WM_GIZMOMAP_DRAWSTEP_2D);
- glDepthMask(GL_TRUE);
+ GPU_depth_mask(true);
}
diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl
index a479a87e14b..40a527a6ba4 100644
--- a/source/blender/draw/intern/shaders/common_globals_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl
@@ -123,6 +123,8 @@ layout(std140) uniform globalsBlock
#define sizeViewportInv (sizeViewport.zw)
+/* See: 'draw_cache_impl.h' for matching includes. */
+
/* data[0] (1st byte flags) */
#define FACE_ACTIVE (1 << 0)
#define FACE_SELECTED (1 << 1)
@@ -135,9 +137,9 @@ layout(std140) uniform globalsBlock
/* data[1] (2st byte flags) */
#define VERT_ACTIVE (1 << 0)
#define VERT_SELECTED (1 << 1)
-#define EDGE_ACTIVE (1 << 2)
-#define EDGE_SELECTED (1 << 3)
-#define EDGE_SEAM (1 << 4)
-#define EDGE_SHARP (1 << 5)
-#define EDGE_FREESTYLE (1 << 6)
-#define HANDLE_SELECTED (1 << 7)
+#define VERT_SELECTED_BEZT_HANDLE (1 << 2)
+#define EDGE_ACTIVE (1 << 3)
+#define EDGE_SELECTED (1 << 4)
+#define EDGE_SEAM (1 << 5)
+#define EDGE_SHARP (1 << 6)
+#define EDGE_FREESTYLE (1 << 7)
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index ffff631e34b..8684d82f228 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -95,7 +95,7 @@ void hair_get_interp_attrs(
* For final drawing, the vertex index and the number of vertex per segment
*/
-#ifndef HAIR_PHASE_SUBDIV
+#if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER)
int hair_get_strand_id(void)
{
return gl_VertexID / (hairStrandsRes * hairThicknessRes);
@@ -206,4 +206,24 @@ vec3 hair_get_strand_pos(void)
return texelFetch(hairPointBuffer, id).point_position;
}
+vec2 hair_get_barycentric(void)
+{
+ /* To match cycles without breaking into individual segment we encode if we need to invert
+ * the first component into the second component. We invert if the barycentricTexCo.y
+ * is NOT 0.0 or 1.0. */
+ int id = hair_get_base_id();
+ return vec2(float((id % 2) == 1), float(((id % 4) % 3) > 0));
+}
+
#endif
+
+/* To be fed the result of hair_get_barycentric from vertex shader. */
+vec2 hair_resolve_barycentric(vec2 vert_barycentric)
+{
+ if (fract(vert_barycentric.y) != 0.0) {
+ return vec2(vert_barycentric.x, 0.0);
+ }
+ else {
+ return vec2(1.0 - vert_barycentric.x, 0.0);
+ }
+}
diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
new file mode 100644
index 00000000000..e337376d7c4
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
@@ -0,0 +1,119 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Math intersection & projection functions.
+ * \{ */
+
+float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal)
+{
+ return dot(planenormal, planeorigin - lineorigin);
+}
+
+float line_plane_intersect_dist(vec3 lineorigin,
+ vec3 linedirection,
+ vec3 planeorigin,
+ vec3 planenormal)
+{
+ return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
+}
+
+float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane)
+{
+ vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz));
+ vec3 h = lineorigin - plane_co;
+ return -dot(plane.xyz, h) / dot(plane.xyz, linedirection);
+}
+
+vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
+{
+ float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
+ return lineorigin + linedirection * dist;
+}
+
+vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane)
+{
+ float dist = line_plane_intersect_dist(lineorigin, linedirection, plane);
+ return lineorigin + linedirection * dist;
+}
+
+float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+{
+ /* aligned plane normal */
+ vec3 L = planeorigin - lineorigin;
+ float diskdist = length(L);
+ vec3 planenormal = -normalize(L);
+ return -diskdist / dot(planenormal, linedirection);
+}
+
+vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+{
+ float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
+ if (dist < 0) {
+ /* if intersection is behind we fake the intersection to be
+ * really far and (hopefully) not inside the radius of interest */
+ dist = 1e16;
+ }
+ return lineorigin + linedirection * dist;
+}
+
+float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
+{
+ float a = dot(linedirection, linedirection);
+ float b = dot(linedirection, lineorigin);
+ float c = dot(lineorigin, lineorigin) - 1;
+
+ float dist = 1e15;
+ float determinant = b * b - a * c;
+ if (determinant >= 0) {
+ dist = (sqrt(determinant) - b) / a;
+ }
+
+ return dist;
+}
+
+float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
+{
+ /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
+ */
+ vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection;
+ vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection;
+ vec3 furthestplane = max(firstplane, secondplane);
+
+ return min_v3(furthestplane);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Other useful functions.
+ * \{ */
+
+void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
+{
+ vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+ T = normalize(cross(UpVector, N));
+ B = cross(N, T);
+}
+
+/* ---- Encode / Decode Normal buffer data ---- */
+/* From http://aras-p.info/texts/CompactNormalStorage.html
+ * Using Method #4: Spheremap Transform */
+vec2 normal_encode(vec3 n, vec3 view)
+{
+ float p = sqrt(n.z * 8.0 + 8.0);
+ return n.xy / p + 0.5;
+}
+
+vec3 normal_decode(vec2 enc, vec3 view)
+{
+ vec2 fenc = enc * 4.0 - 2.0;
+ float f = dot(fenc, fenc);
+ float g = sqrt(1.0 - f / 4.0);
+ vec3 n;
+ n.xy = fenc * g;
+ n.z = 1 - f / 2;
+ return n;
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl
new file mode 100644
index 00000000000..a82e0b5a5e9
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -0,0 +1,130 @@
+
+/* ---------------------------------------------------------------------- */
+/** \name Common Math Utilities
+ * \{ */
+
+#define M_PI 3.14159265358979323846 /* pi */
+#define M_2PI 6.28318530717958647692 /* 2*pi */
+#define M_PI_2 1.57079632679489661923 /* pi/2 */
+#define M_1_PI 0.318309886183790671538 /* 1/pi */
+#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */
+#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */
+#define FLT_MAX 3.402823e+38
+
+vec3 mul(mat3 m, vec3 v)
+{
+ return m * v;
+}
+mat3 mul(mat3 m1, mat3 m2)
+{
+ return m1 * m2;
+}
+vec3 transform_direction(mat4 m, vec3 v)
+{
+ return mat3(m) * v;
+}
+vec3 transform_point(mat4 m, vec3 v)
+{
+ return (m * vec4(v, 1.0)).xyz;
+}
+vec3 project_point(mat4 m, vec3 v)
+{
+ vec4 tmp = m * vec4(v, 1.0);
+ return tmp.xyz / tmp.w;
+}
+
+#define min3(a, b, c) min(a, min(b, c))
+#define min4(a, b, c, d) min(a, min3(b, c, d))
+#define min5(a, b, c, d, e) min(a, min4(b, c, d, e))
+#define min6(a, b, c, d, e, f) min(a, min5(b, c, d, e, f))
+#define min7(a, b, c, d, e, f, g) min(a, min6(b, c, d, e, f, g))
+#define min8(a, b, c, d, e, f, g, h) min(a, min7(b, c, d, e, f, g, h))
+#define min9(a, b, c, d, e, f, g, h, i) min(a, min8(b, c, d, e, f, g, h, i))
+
+#define max3(a, b, c) max(a, max(b, c))
+#define max4(a, b, c, d) max(a, max3(b, c, d))
+#define max5(a, b, c, d, e) max(a, max4(b, c, d, e))
+#define max6(a, b, c, d, e, f) max(a, max5(b, c, d, e, f))
+#define max7(a, b, c, d, e, f, g) max(a, max6(b, c, d, e, f, g))
+#define max8(a, b, c, d, e, f, g, h) max(a, max7(b, c, d, e, f, g, h))
+#define max9(a, b, c, d, e, f, g, h, i) max(a, max8(b, c, d, e, f, g, h, i))
+
+#define avg3(a, b, c) (a + b + c) * (1.0 / 3.0)
+#define avg4(a, b, c, d) (a + b + c + d) * (1.0 / 4.0)
+#define avg5(a, b, c, d, e) (a + b + c + d + e) * (1.0 / 5.0)
+#define avg6(a, b, c, d, e, f) (a + b + c + d + e + f) * (1.0 / 6.0)
+#define avg7(a, b, c, d, e, f, g) (a + b + c + d + e + f + g) * (1.0 / 7.0)
+#define avg8(a, b, c, d, e, f, g, h) (a + b + c + d + e + f + g + h) * (1.0 / 8.0)
+#define avg9(a, b, c, d, e, f, g, h, i) (a + b + c + d + e + f + g + h + i) * (1.0 / 9.0)
+
+/* clang-format off */
+float min_v2(vec2 v) { return min(v.x, v.y); }
+float min_v3(vec3 v) { return min(v.x, min(v.y, v.z)); }
+float min_v4(vec4 v) { return min(min(v.x, v.y), min(v.z, v.w)); }
+float max_v2(vec2 v) { return max(v.x, v.y); }
+float max_v3(vec3 v) { return max(v.x, max(v.y, v.z)); }
+float max_v4(vec4 v) { return max(max(v.x, v.y), max(v.z, v.w)); }
+
+float sum(vec2 v) { return dot(vec2(1.0), v); }
+float sum(vec3 v) { return dot(vec3(1.0), v); }
+float sum(vec4 v) { return dot(vec4(1.0), v); }
+
+float avg(vec2 v) { return dot(vec2(1.0 / 2.0), v); }
+float avg(vec3 v) { return dot(vec3(1.0 / 3.0), v); }
+float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); }
+/* clang-format on */
+
+#define saturate(a) clamp(a, 0.0, 1.0)
+
+float distance_squared(vec2 a, vec2 b)
+{
+ a -= b;
+ return dot(a, a);
+}
+
+float distance_squared(vec3 a, vec3 b)
+{
+ a -= b;
+ return dot(a, a);
+}
+
+float len_squared(vec3 a)
+{
+ return dot(a, a);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Fast Math
+ * \{ */
+
+/* [Drobot2014a] Low Level Optimizations for GCN */
+float fast_sqrt(float v)
+{
+ return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
+}
+
+vec2 fast_sqrt(vec2 v)
+{
+ return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1));
+}
+
+/* [Eberly2014] GPGPU Programming for Games and Science */
+float fast_acos(float v)
+{
+ float res = -0.156583 * abs(v) + M_PI_2;
+ res *= fast_sqrt(1.0 - abs(v));
+ return (v >= 0) ? res : M_PI - res;
+}
+
+vec2 fast_acos(vec2 v)
+{
+ vec2 res = -0.156583 * abs(v) + M_PI_2;
+ res *= fast_sqrt(1.0 - abs(v));
+ v.x = (v.x >= 0) ? res.x : M_PI - res.x;
+ v.y = (v.y >= 0) ? res.y : M_PI - res.y;
+ return v;
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
new file mode 100644
index 00000000000..36b67f2bd60
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
@@ -0,0 +1,39 @@
+
+/* NOTE: To be used with UNIFORM_RESOURCE_ID and INSTANCED_ATTR as define. */
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+in vec4 pos; /* Position and radius. */
+
+/* ---- Instanced attribs ---- */
+
+in vec3 pos_inst;
+in vec3 nor;
+
+mat3 pointcloud_get_facing_matrix(vec3 p)
+{
+ mat3 facing_mat;
+ facing_mat[2] = normalize(ViewMatrixInverse[3].xyz - p);
+ facing_mat[1] = normalize(cross(ViewMatrixInverse[0].xyz, facing_mat[2]));
+ facing_mat[0] = cross(facing_mat[1], facing_mat[2]);
+ return facing_mat;
+}
+
+/* Return world position and normal. */
+void pointcloud_get_pos_and_nor(out vec3 outpos, out vec3 outnor)
+{
+ vec3 p = point_object_to_world(pos.xyz);
+ mat3 facing_mat = pointcloud_get_facing_matrix(p);
+
+ float radius = dot(abs(mat3(ModelMatrix) * pos.www), vec3(1.0 / 3.0));
+ /* TODO(fclem) remove multiplication here. Here only for keeping the size correct for now. */
+ radius *= 0.01;
+ outpos = p + (facing_mat * pos_inst) * radius;
+ outnor = facing_mat * nor;
+}
+
+vec3 pointcloud_get_pos(void)
+{
+ vec3 outpos, outnor;
+ pointcloud_get_pos_and_nor(outpos, outnor);
+ return outpos;
+} \ No newline at end of file
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index 1054f4d11c9..a55d2cd8c1a 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -14,10 +14,24 @@ layout(std140) uniform viewBlock
vec4 clipPlanes[6];
+ /* View frustum corners [NDC(-1.0, -1.0, -1.0) & NDC(1.0, 1.0, 1.0)].
+ * Fourth components are near and far values. */
+ vec4 ViewVecs[2];
+
/* TODO move it elsewhere. */
vec4 CameraTexCoFactors;
};
+#define ViewNear (ViewVecs[0].w)
+#define ViewFar (ViewVecs[1].w)
+
+#define cameraForward ViewMatrixInverse[2].xyz
+#define cameraPos ViewMatrixInverse[3].xyz
+#define cameraVec \
+ ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - worldPosition) : cameraForward)
+#define viewCameraVec \
+ ((ProjectionMatrix[3][3] == 0.0) ? normalize(-viewPosition) : vec3(0.0, 0.0, 1.0))
+
#ifdef world_clip_planes_calc_clip_distance
# undef world_clip_planes_calc_clip_distance
# define world_clip_planes_calc_clip_distance(p) \
@@ -100,10 +114,13 @@ uniform int resourceId;
/* Use this to declare and pass the value if
* the fragment shader uses the resource_id. */
-# define RESOURCE_ID_VARYING flat out int resourceIDFrag;
-# define RESOURCE_ID_VARYING_GEOM flat out int resourceIDGeom;
-# define PASS_RESOURCE_ID resourceIDFrag = resource_id;
-# define PASS_RESOURCE_ID_GEOM resourceIDGeom = resource_id;
+# ifdef USE_GEOMETRY_SHADER
+# define RESOURCE_ID_VARYING flat out int resourceIDGeom;
+# define PASS_RESOURCE_ID resourceIDGeom = resource_id;
+# else
+# define RESOURCE_ID_VARYING flat out int resourceIDFrag;
+# define PASS_RESOURCE_ID resourceIDFrag = resource_id;
+# endif
#endif
/* If used in a fragment / geometry shader, we pass
@@ -114,7 +131,7 @@ uniform int resourceId;
flat in int resourceIDGeom[];
# define resource_id resourceIDGeom
-# define PASS_RESOURCE_ID(i) resourceIDFrag = resource_id[i];
+# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0];
#endif
#ifdef GPU_FRAGMENT_SHADER
@@ -167,10 +184,14 @@ uniform mat4 ModelMatrixInverse;
* Note: This is only valid because we are only using the mat3 of the ViewMatrixInverse.
* ViewMatrix * transpose(ModelMatrixInverse)
**/
-#define normal_object_to_view(n) (mat3(ViewMatrix) * (transpose(mat3(ModelMatrixInverse)) * n))
-#define normal_object_to_world(n) (transpose(mat3(ModelMatrixInverse)) * n)
-#define normal_world_to_object(n) (transpose(mat3(ModelMatrix)) * n)
+#define NormalMatrix transpose(mat3(ModelMatrixInverse))
+#define NormalMatrixInverse transpose(mat3(ModelMatrix))
+
+#define normal_object_to_view(n) (mat3(ViewMatrix) * (NormalMatrix * n))
+#define normal_object_to_world(n) (NormalMatrix * n)
+#define normal_world_to_object(n) (NormalMatrixInverse * n)
#define normal_world_to_view(n) (mat3(ViewMatrix) * n)
+#define normal_view_to_world(n) (mat3(ViewMatrixInverse) * n)
#define point_object_to_ndc(p) (ViewProjectionMatrix * vec4((ModelMatrix * vec4(p, 1.0)).xyz, 1.0))
#define point_object_to_view(p) ((ViewMatrix * vec4((ModelMatrix * vec4(p, 1.0)).xyz, 1.0)).xyz)
@@ -194,3 +215,78 @@ uniform mat4 ModelMatrixInverse;
#define DRW_BASE_FROM_DUPLI (1 << 2)
#define DRW_BASE_FROM_SET (1 << 3)
#define DRW_BASE_ACTIVE (1 << 4)
+
+/* ---- Opengl Depth conversion ---- */
+
+float linear_depth(bool is_persp, float z, float zf, float zn)
+{
+ if (is_persp) {
+ return (zn * zf) / (z * (zn - zf) + zf);
+ }
+ else {
+ return (z * 2.0 - 1.0) * zf;
+ }
+}
+
+float buffer_depth(bool is_persp, float z, float zf, float zn)
+{
+ if (is_persp) {
+ return (zf * (zn - z)) / (z * (zn - zf));
+ }
+ else {
+ return (z / (zf * 2.0)) + 0.5;
+ }
+}
+
+float get_view_z_from_depth(float depth)
+{
+ if (ProjectionMatrix[3][3] == 0.0) {
+ float d = 2.0 * depth - 1.0;
+ return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]);
+ }
+ else {
+ return ViewVecs[0].z + depth * ViewVecs[1].z;
+ }
+}
+
+float get_depth_from_view_z(float z)
+{
+ if (ProjectionMatrix[3][3] == 0.0) {
+ float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2];
+ return d * 0.5 + 0.5;
+ }
+ else {
+ return (z - ViewVecs[0].z) / ViewVecs[1].z;
+ }
+}
+
+vec2 get_uvs_from_view(vec3 view)
+{
+ vec4 ndc = ProjectionMatrix * vec4(view, 1.0);
+ return (ndc.xy / ndc.w) * 0.5 + 0.5;
+}
+
+vec3 get_view_space_from_depth(vec2 uvcoords, float depth)
+{
+ if (ProjectionMatrix[3][3] == 0.0) {
+ return vec3(ViewVecs[0].xy + uvcoords * ViewVecs[1].xy, 1.0) * get_view_z_from_depth(depth);
+ }
+ else {
+ return ViewVecs[0].xyz + vec3(uvcoords, depth) * ViewVecs[1].xyz;
+ }
+}
+
+vec3 get_world_space_from_depth(vec2 uvcoords, float depth)
+{
+ return (ViewMatrixInverse * vec4(get_view_space_from_depth(uvcoords, depth), 1.0)).xyz;
+}
+
+vec3 get_view_vector_from_screen_uv(vec2 uv)
+{
+ if (ProjectionMatrix[3][3] == 0.0) {
+ return normalize(vec3(ViewVecs[0].xy + uv * ViewVecs[1].xy, 1.0));
+ }
+ else {
+ return vec3(0.0, 0.0, 1.0);
+ }
+}
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 0947023e071..53401e0c05a 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -322,9 +322,7 @@ static short acf_generic_basic_offset(bAnimContext *ac, bAnimListElem *ale)
if (acf && acf->get_indent_level) {
return acf->get_indent_level(ac, ale) * INDENT_STEP_SIZE;
}
- else {
- return 0;
- }
+ return 0;
}
/* offset based on nodetree type */
@@ -514,11 +512,10 @@ static int acf_summary_setting_flag(bAnimContext *UNUSED(ac),
*neg = true;
return ADS_FLAG_SUMMARY_COLLAPSED;
}
- else {
- /* unsupported */
- *neg = false;
- return 0;
- }
+
+ /* unsupported */
+ *neg = false;
+ return 0;
}
/* get pointer to the setting */
@@ -538,11 +535,10 @@ static void *acf_summary_setting_ptr(bAnimListElem *ale,
/* return pointer to DopeSheet's flag */
return GET_ACF_FLAG_PTR(ads->flag, type);
}
- else {
- /* can't return anything useful - unsupported */
- *type = 0;
- return NULL;
- }
+
+ /* can't return anything useful - unsupported */
+ *type = 0;
+ return NULL;
}
/* all animation summary (DopeSheet only) type define */
@@ -2846,8 +2842,9 @@ static void *acf_dshair_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se
case ACHANNEL_SETTING_SELECT: /* selected */
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
- if (hair->adt)
+ if (hair->adt) {
return GET_ACF_FLAG_PTR(hair->adt->flag, type);
+ }
return NULL;
default: /* unsupported */
@@ -2926,8 +2923,9 @@ static void *acf_dspointcloud_setting_ptr(bAnimListElem *ale,
case ACHANNEL_SETTING_SELECT: /* selected */
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
- if (pointcloud->adt)
+ if (pointcloud->adt) {
return GET_ACF_FLAG_PTR(pointcloud->adt->flag, type);
+ }
return NULL;
default: /* unsupported */
@@ -3006,8 +3004,9 @@ static void *acf_dsvolume_setting_ptr(bAnimListElem *ale,
case ACHANNEL_SETTING_SELECT: /* selected */
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
- if (volume->adt)
+ if (volume->adt) {
return GET_ACF_FLAG_PTR(volume->adt->flag, type);
+ }
return NULL;
default: /* unsupported */
@@ -3084,8 +3083,9 @@ static void *acf_dssimulation_setting_ptr(bAnimListElem *ale,
case ACHANNEL_SETTING_SELECT: /* selected */
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
- if (simulation->adt)
+ if (simulation->adt) {
return GET_ACF_FLAG_PTR(simulation->adt->flag, type);
+ }
return NULL;
default: /* unsupported */
@@ -3815,19 +3815,15 @@ static bool acf_nlatrack_setting_valid(bAnimContext *UNUSED(ac),
/* ok - we've got a solo track, and this is it */
return true;
}
- else {
- /* not ok - we've got a solo track, but this isn't it, so make it more obvious */
- return false;
- }
+ /* not ok - we've got a solo track, but this isn't it, so make it more obvious */
+ return false;
}
/* ok - no tracks are solo'd, and this isn't being tweaked */
return true;
}
- else {
- /* unsupported - this track is being tweaked */
- return false;
- }
+ /* unsupported - this track is being tweaked */
+ return false;
/* unsupported */
default:
@@ -3901,9 +3897,8 @@ static int acf_nlaaction_icon(bAnimListElem *ale)
if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
return ICON_ACTION_TWEAK;
}
- else {
- return ICON_ACTION;
- }
+
+ return ICON_ACTION;
}
/* Backdrop color for nla action channel
@@ -4157,9 +4152,8 @@ const bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale)
if ((ale->type >= 0) && (ale->type < ANIMTYPE_NUM_TYPES)) {
return animchannelTypeInfo[ale->type];
}
- else {
- return NULL;
- }
+
+ return NULL;
}
/* --------------------------- */
@@ -4227,9 +4221,7 @@ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChanne
if (negflag) {
return ((*val) & flag) == 0;
}
- else {
- return ((*val) & flag) != 0;
- }
+ return ((*val) & flag) != 0;
}
case sizeof(short): /* short pointer for setting */
{
@@ -4238,9 +4230,7 @@ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChanne
if (negflag) {
return ((*val) & flag) == 0;
}
- else {
- return ((*val) & flag) != 0;
- }
+ return ((*val) & flag) != 0;
}
case sizeof(char): /* char pointer for setting */
{
@@ -4249,9 +4239,7 @@ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChanne
if (negflag) {
return ((*val) & flag) == 0;
}
- else {
- return ((*val) & flag) != 0;
- }
+ return ((*val) & flag) != 0;
}
}
}
@@ -4720,6 +4708,7 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
ReportList *reports = CTX_wm_reports(C);
Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
ToolSettings *ts = scene->toolsettings;
ListBase nla_cache = {NULL, NULL};
PointerRNA id_ptr, ptr;
@@ -4732,8 +4721,10 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
RNA_id_pointer_create(id, &id_ptr);
/* Get NLA context for value remapping */
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ (float)CFRA);
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(
- &nla_cache, &id_ptr, adt, (float)CFRA, false);
+ &nla_cache, &id_ptr, adt, &anim_eval_context, false);
/* get current frame and apply NLA-mapping to it (if applicable) */
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
@@ -4750,7 +4741,7 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
/* insert a keyframe for this F-Curve */
done = insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, ts->keyframe_type, nla_context, flag);
+ reports, ptr, prop, fcu, &anim_eval_context, ts->keyframe_type, nla_context, flag);
if (done) {
if (adt->action != NULL) {
@@ -4774,23 +4765,26 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
ReportList *reports = CTX_wm_reports(C);
Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
ToolSettings *ts = scene->toolsettings;
ListBase nla_cache = {NULL, NULL};
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
eInsertKeyFlags flag = 0;
bool done = false;
- float cfra;
/* Get RNA pointer */
RNA_id_pointer_create((ID *)key, &id_ptr);
/* Get NLA context for value remapping */
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ (float)CFRA);
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(
- &nla_cache, &id_ptr, key->adt, (float)CFRA, false);
+ &nla_cache, &id_ptr, key->adt, &anim_eval_context, false);
/* get current frame and apply NLA-mapping to it (if applicable) */
- cfra = BKE_nla_tweakedit_remap(key->adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ const float remapped_frame = BKE_nla_tweakedit_remap(
+ key->adt, anim_eval_context.eval_time, NLATIME_CONVERT_UNMAP);
/* get flags for keyframing */
flag = ANIM_get_keyframing_flags(scene, true);
@@ -4803,13 +4797,21 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
FCurve *fcu = ED_action_fcurve_ensure(bmain, act, NULL, &ptr, rna_path, 0);
/* set the special 'replace' flag if on a keyframe */
- if (fcurve_frame_has_keyframe(fcu, cfra, 0)) {
+ if (fcurve_frame_has_keyframe(fcu, remapped_frame, 0)) {
flag |= INSERTKEY_REPLACE;
}
/* insert a keyframe for this F-Curve */
- done = insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, ts->keyframe_type, nla_context, flag);
+ const AnimationEvalContext remapped_anim_eval_context = BKE_animsys_eval_context_construct_at(
+ &anim_eval_context, remapped_frame);
+ done = insert_keyframe_direct(reports,
+ ptr,
+ prop,
+ fcu,
+ &remapped_anim_eval_context,
+ ts->keyframe_type,
+ nla_context,
+ flag);
if (done) {
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
@@ -4860,7 +4862,11 @@ static void achannel_setting_slider_nla_curve_cb(bContext *C,
}
/* insert a keyframe for this F-Curve */
- done = insert_keyframe_direct(reports, ptr, prop, fcu, cfra, ts->keyframe_type, NULL, flag);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
+ done = insert_keyframe_direct(
+ reports, ptr, prop, fcu, &anim_eval_context, ts->keyframe_type, NULL, flag);
if (done) {
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 1ca3452e8d8..d5f68e44ad8 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -505,9 +505,9 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac,
printf("ERROR: no channel matching the one changed was found\n");
return;
}
- else {
- const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
+ {
+ const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
if (acf == NULL) {
printf("ERROR: no channel info for the changed channel\n");
return;
@@ -571,9 +571,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac,
* finished, so skip until we get to the parent of this level
*/
}
- else {
- continue;
- }
+ continue;
}
}
}
@@ -2314,7 +2312,7 @@ static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op))
nla_empty = false;
break;
}
- else if (nlt->strips.first == NULL) {
+ if (nlt->strips.first == NULL) {
/* this track is empty, but another one may still have stuff in it, so can't break yet */
nla_empty = true;
}
@@ -2812,10 +2810,9 @@ static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const
if (rename_anim_channels(&ac, channel_index)) {
return OPERATOR_FINISHED;
}
- else {
- /* allow event to be handled by selectall operator */
- return OPERATOR_PASS_THROUGH;
- }
+
+ /* allow event to be handled by selectall operator */
+ return OPERATOR_PASS_THROUGH;
}
static void ANIM_OT_channels_rename(wmOperatorType *ot)
@@ -3372,10 +3369,9 @@ static int animchannels_channel_select_keys_invoke(bContext *C,
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
return OPERATOR_FINISHED;
}
- else {
- /* allow event to be handled by selectall operator */
- return OPERATOR_PASS_THROUGH;
- }
+
+ /* allow event to be handled by selectall operator */
+ return OPERATOR_PASS_THROUGH;
}
static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c
index a2a1f7eb1d2..4cbea0ded15 100644
--- a/source/blender/editors/animation/anim_draw.c
+++ b/source/blender/editors/animation/anim_draw.c
@@ -473,9 +473,7 @@ float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag
if (flag & ANIM_UNITCONV_RESTORE) {
return DEG2RADF(1.0f); /* degrees to radians */
}
- else {
- return RAD2DEGF(1.0f); /* radians to degrees */
- }
+ return RAD2DEGF(1.0f); /* radians to degrees */
}
}
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index bd83bdae31b..50733afe6fb 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -419,6 +419,7 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
ac->markers = ED_context_get_markers(C);
}
ac->view_layer = CTX_data_view_layer(C);
+ ac->depsgraph = CTX_data_depsgraph_pointer(C);
ac->obact = (ac->view_layer->basact) ? ac->view_layer->basact->object : NULL;
ac->area = area;
ac->region = region;
@@ -571,7 +572,7 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
channel_data, channel_type, owner_id, fcurve_owner_id, ale_statement) \
if (filter_mode & ANIMFILTER_TMP_PEEK) \
return 1; \
- else { \
+ { \
bAnimListElem *ale = make_new_animlistelem( \
channel_data, channel_type, (ID *)owner_id, fcurve_owner_id); \
if (ale) { \
@@ -1169,10 +1170,8 @@ static bool name_matches_dopesheet_filter(bDopeSheet *ads, char *name)
/* if we have a match somewhere, this returns true */
return found;
}
- else {
- /* fallback/default - just case insensitive, but starts from start of word */
- return BLI_strcasestr(name, ads->searchstr) != NULL;
- }
+ /* fallback/default - just case insensitive, but starts from start of word */
+ return BLI_strcasestr(name, ads->searchstr) != NULL;
}
/* (Display-)Name-based F-Curve filtering
@@ -2603,8 +2602,9 @@ static size_t animdata_filter_ds_obdata(
{
Hair *hair = (Hair *)ob->data;
- if (ads->filterflag2 & ADS_FILTER_NOHAIR)
+ if (ads->filterflag2 & ADS_FILTER_NOHAIR) {
return 0;
+ }
type = ANIMTYPE_DSHAIR;
expanded = FILTER_HAIR_OBJD(hair);
@@ -2614,8 +2614,9 @@ static size_t animdata_filter_ds_obdata(
{
PointCloud *pointcloud = (PointCloud *)ob->data;
- if (ads->filterflag2 & ADS_FILTER_NOPOINTCLOUD)
+ if (ads->filterflag2 & ADS_FILTER_NOPOINTCLOUD) {
return 0;
+ }
type = ANIMTYPE_DSPOINTCLOUD;
expanded = FILTER_POINTS_OBJD(pointcloud);
@@ -2625,8 +2626,9 @@ static size_t animdata_filter_ds_obdata(
{
Volume *volume = (Volume *)ob->data;
- if (ads->filterflag2 & ADS_FILTER_NOVOLUME)
+ if (ads->filterflag2 & ADS_FILTER_NOVOLUME) {
return 0;
+ }
type = ANIMTYPE_DSVOLUME;
expanded = FILTER_VOLUME_OBJD(volume);
@@ -3414,12 +3416,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/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c
index 3613ca9eeda..72103d68b05 100644
--- a/source/blender/editors/animation/anim_ipo_utils.c
+++ b/source/blender/editors/animation/anim_ipo_utils.c
@@ -59,7 +59,8 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
if (name == NULL) {
return icon;
}
- else if (ELEM(NULL, id, fcu, fcu->rna_path)) {
+
+ if (ELEM(NULL, id, fcu, fcu->rna_path)) {
if (fcu == NULL) {
strcpy(name, TIP_("<invalid>"));
}
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index 46566feea91..bcdbf4c74f0 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -110,9 +110,7 @@ ListBase *ED_animcontext_get_markers(const bAnimContext *ac)
if (ac) {
return context_get_markers(ac->scene, ac->area);
}
- else {
- return NULL;
- }
+ return NULL;
}
/* --------------------------------- */
@@ -307,7 +305,7 @@ static void add_marker_to_cfra_elem(ListBase *lb, TimeMarker *marker, short only
}
return;
}
- else if (ce->cfra > marker->frame) {
+ if (ce->cfra > marker->frame) {
break;
}
}
@@ -487,13 +485,11 @@ static int marker_get_icon_id(TimeMarker *marker, int flag)
(marker->flag & SELECT) ? ICON_PMARKER_SEL : ICON_PMARKER;
}
#ifdef DURIAN_CAMERA_SWITCH
- else if (marker->camera) {
+ if (marker->camera) {
return (marker->flag & SELECT) ? ICON_OUTLINER_OB_CAMERA : ICON_CAMERA_DATA;
}
#endif
- else {
- return (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER;
- }
+ return (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER;
}
static void draw_marker(
@@ -544,7 +540,7 @@ static void draw_markers_background(rctf *rect)
immUnbindProgram();
}
-static bool marker_is_in_frame_range(TimeMarker *marker, int frame_range[2])
+static bool marker_is_in_frame_range(TimeMarker *marker, const int frame_range[2])
{
if (marker->frame < frame_range[0]) {
return false;
@@ -1508,9 +1504,8 @@ static int ed_marker_rename_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+
+ return OPERATOR_CANCELLED;
}
static int ed_marker_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event)
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index 328e435877c..3b15cd794d8 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -1020,9 +1020,7 @@ static int add_driver_button_none(bContext *C, wmOperator *op, short mapping_typ
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static int add_driver_button_menu_exec(bContext *C, wmOperator *op)
@@ -1032,16 +1030,15 @@ static int add_driver_button_menu_exec(bContext *C, wmOperator *op)
/* Just create driver with no targets */
return add_driver_button_none(C, op, mapping_type);
}
- else {
- /* Create Driver using Eyedropper */
- wmOperatorType *ot = WM_operatortype_find("UI_OT_eyedropper_driver", true);
- /* XXX: We assume that it's fine to use the same set of properties,
- * since they're actually the same. */
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, op->ptr);
+ /* Create Driver using Eyedropper */
+ wmOperatorType *ot = WM_operatortype_find("UI_OT_eyedropper_driver", true);
- return OPERATOR_FINISHED;
- }
+ /* XXX: We assume that it's fine to use the same set of properties,
+ * since they're actually the same. */
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, op->ptr);
+
+ return OPERATOR_FINISHED;
}
/* Show menu or create drivers */
@@ -1054,12 +1051,11 @@ static int add_driver_button_menu_invoke(bContext *C, wmOperator *op, const wmEv
/* Mapping Type is Set - Directly go into creating drivers */
return add_driver_button_menu_exec(C, op);
}
- else {
- /* Show menu */
- // TODO: This should get filtered by the enum filter
- /* important to execute in the region we're currently in */
- return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_DEFAULT);
- }
+
+ /* Show menu */
+ // TODO: This should get filtered by the enum filter
+ /* important to execute in the region we're currently in */
+ return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_DEFAULT);
}
static void UNUSED_FUNCTION(ANIM_OT_driver_button_add_menu)(wmOperatorType *ot)
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index eadaa449b92..1ecdf5accb8 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -515,8 +515,7 @@ static void draw_modifier__cycles(uiLayout *layout,
RNA_pointer_create(fcurve_owner_id, &RNA_FModifierCycles, fcm, &ptr);
/* split into 2 columns
- * NOTE: the mode comboboxes shouldn't get labels, otherwise there isn't enough room
- */
+ * NOTE: the mode combination-boxes shouldn't get labels, otherwise there isn't enough room. */
split = uiLayoutSplit(layout, 0.5f, false);
/* before range */
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index b921ba039be..c69f3ce3e3a 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -84,9 +84,7 @@ short compare_ak_cfraPtr(void *node, void *data)
if (val < ak->cfra) {
return -1;
}
- else {
- return 1;
- }
+ return 1;
}
/* --------------- */
@@ -106,18 +104,16 @@ static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt)
if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
return KEYFRAME_HANDLE_AUTO_CLAMP;
}
- else if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
+ if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
return KEYFRAME_HANDLE_AUTO;
}
- else if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
+ if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
return KEYFRAME_HANDLE_VECTOR;
}
- else if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
+ if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
return KEYFRAME_HANDLE_FREE;
}
- else {
- return KEYFRAME_HANDLE_ALIGNED;
- }
+ return KEYFRAME_HANDLE_ALIGNED;
}
/* Determine if the keyframe is an extreme by comparing with neighbors.
@@ -337,9 +333,8 @@ static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt)
if (ELEM(NULL, keys, bezt)) {
return;
}
- else {
- BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
- }
+
+ BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
}
/* Add the given GPencil Frame to the given 'list' of Keyframes */
@@ -348,9 +343,8 @@ static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf)
if (ELEM(NULL, keys, gpf)) {
return;
}
- else {
- BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
- }
+
+ BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
}
/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
@@ -359,13 +353,12 @@ static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *mas
if (ELEM(NULL, keys, masklay_shape)) {
return;
}
- else {
- BLI_dlrbTree_add(keys,
- compare_ak_masklayshape,
- nalloc_ak_masklayshape,
- nupdate_ak_masklayshape,
- masklay_shape);
- }
+
+ BLI_dlrbTree_add(keys,
+ compare_ak_masklayshape,
+ nalloc_ak_masklayshape,
+ nupdate_ak_masklayshape,
+ masklay_shape);
}
/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index 2dae4e8b4c5..f608038a95a 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -569,9 +569,7 @@ static short ok_bezier_selected(KeyframeEditData *UNUSED(ked), BezTriple *bezt)
if (BEZT_ISSEL_ANY(bezt)) {
return KEYFRAME_OK_ALL;
}
- else {
- return 0;
- }
+ return 0;
}
static short ok_bezier_value(KeyframeEditData *ked, BezTriple *bezt)
@@ -617,9 +615,7 @@ static short ok_bezier_region(KeyframeEditData *ked, BezTriple *bezt)
/* return ok flags */
return ok;
}
- else {
- return 0;
- }
+ return 0;
}
/**
@@ -654,9 +650,7 @@ static short ok_bezier_region_lasso(KeyframeEditData *ked, BezTriple *bezt)
/* return ok flags */
return ok;
}
- else {
- return 0;
- }
+ return 0;
}
static short ok_bezier_channel_lasso(KeyframeEditData *ked, BezTriple *bezt)
@@ -718,9 +712,7 @@ static short ok_bezier_region_circle(KeyframeEditData *ked, BezTriple *bezt)
/* return ok flags */
return ok;
}
- else {
- return 0;
- }
+ return 0;
}
static short ok_bezier_channel_circle(KeyframeEditData *ked, BezTriple *bezt)
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index fc9ec870496..64065d6d633 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -83,7 +83,7 @@ void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc)
if (abs(index) >= fcu->totvert) {
return;
}
- else if (index < 0) {
+ if (index < 0) {
index += fcu->totvert;
}
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 2aa8d468e2d..8c2f4216aa9 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -659,20 +659,17 @@ static short new_key_needed(FCurve *fcu, float cFrame, float nValue)
if (IS_EQF(prevVal, nValue) && IS_EQF(beztVal, nValue) && IS_EQF(prevVal, beztVal)) {
return KEYNEEDED_DONTADD;
}
- else {
- float realVal;
- /* get real value of curve at that point */
- realVal = evaluate_fcurve(fcu, cFrame);
+ float realVal;
- /* compare whether it's the same as proposed */
- if (IS_EQF(realVal, nValue)) {
- return KEYNEEDED_DONTADD;
- }
- else {
- return KEYNEEDED_JUSTADD;
- }
+ /* get real value of curve at that point */
+ realVal = evaluate_fcurve(fcu, cFrame);
+
+ /* compare whether it's the same as proposed */
+ if (IS_EQF(realVal, nValue)) {
+ return KEYNEEDED_DONTADD;
}
+ return KEYNEEDED_JUSTADD;
}
/* new keyframe before prev beztriple? */
@@ -684,9 +681,8 @@ static short new_key_needed(FCurve *fcu, float cFrame, float nValue)
if (IS_EQF(prevVal, nValue) && IS_EQF(beztVal, nValue) && IS_EQF(prevVal, beztVal)) {
return KEYNEEDED_DELNEXT;
}
- else {
- return KEYNEEDED_JUSTADD;
- }
+
+ return KEYNEEDED_JUSTADD;
}
}
else {
@@ -728,9 +724,8 @@ static short new_key_needed(FCurve *fcu, float cFrame, float nValue)
if (IS_EQF(valA, nValue) && IS_EQF(valA, valB)) {
return KEYNEEDED_DELPREV;
}
- else {
- return KEYNEEDED_JUSTADD;
- }
+
+ return KEYNEEDED_JUSTADD;
}
/* ------------------ RNA Data-Access Functions ------------------ */
@@ -865,7 +860,8 @@ static bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop)
printf("%s failed: NULL identifier\n", __func__);
return false;
}
- else if (strstr(identifier, "location")) {
+
+ if (strstr(identifier, "location")) {
searchtype = VISUALKEY_LOC;
}
else if (strstr(identifier, "rotation")) {
@@ -1036,7 +1032,8 @@ static float *visualkey_get_values(
*r_count = 3;
return buffer;
}
- else if (strstr(identifier, "rotation_quaternion")) {
+
+ if (strstr(identifier, "rotation_quaternion")) {
float mat3[3][3];
copy_m3_m4(mat3, tmat);
@@ -1045,14 +1042,16 @@ static float *visualkey_get_values(
*r_count = 4;
return buffer;
}
- else if (strstr(identifier, "rotation_axis_angle")) {
+
+ if (strstr(identifier, "rotation_axis_angle")) {
/* w = 0, x,y,z = 1,2,3 */
mat4_to_axis_angle(buffer + 1, buffer, tmat);
*r_count = 4;
return buffer;
}
- else if (strstr(identifier, "scale")) {
+
+ if (strstr(identifier, "scale")) {
mat4_to_size(buffer, tmat);
*r_count = 3;
@@ -1114,7 +1113,7 @@ static bool insert_keyframe_value(ReportList *reports,
PointerRNA *ptr,
PropertyRNA *prop,
FCurve *fcu,
- float cfra,
+ const AnimationEvalContext *anim_eval_context,
float curval,
eBezTriple_KeyframeType keytype,
eInsertKeyFlags flag)
@@ -1131,13 +1130,15 @@ static bool insert_keyframe_value(ReportList *reports,
return false;
}
+ float cfra = anim_eval_context->eval_time;
+
/* adjust frame on which to add keyframe */
if ((flag & INSERTKEY_DRIVER) && (fcu->driver)) {
PathResolvedRNA anim_rna;
if (RNA_path_resolved_create(ptr, prop, fcu->array_index, &anim_rna)) {
/* for making it easier to add corrective drivers... */
- cfra = evaluate_driver(&anim_rna, fcu->driver, fcu->driver, cfra);
+ cfra = evaluate_driver(&anim_rna, fcu->driver, fcu->driver, anim_eval_context);
}
else {
cfra = 0.0f;
@@ -1181,10 +1182,9 @@ static bool insert_keyframe_value(ReportList *reports,
return true;
}
- else {
- /* just insert keyframe */
- return insert_vert_fcurve(fcu, cfra, curval, keytype, flag) >= 0;
- }
+
+ /* just insert keyframe */
+ return insert_vert_fcurve(fcu, cfra, curval, keytype, flag) >= 0;
}
/* Secondary Keyframing API call:
@@ -1204,7 +1204,7 @@ bool insert_keyframe_direct(ReportList *reports,
PointerRNA ptr,
PropertyRNA *prop,
FCurve *fcu,
- float cfra,
+ const AnimationEvalContext *anim_eval_context,
eBezTriple_KeyframeType keytype,
struct NlaKeyframingContext *nla_context,
eInsertKeyFlags flag)
@@ -1239,10 +1239,9 @@ bool insert_keyframe_direct(ReportList *reports,
fcu->rna_path);
return false;
}
- else {
- /* property found, so overwrite 'ptr' to make later code easier */
- ptr = tmp_ptr;
- }
+
+ /* property found, so overwrite 'ptr' to make later code easier */
+ ptr = tmp_ptr;
}
/* update F-Curve flags to ensure proper behavior for property type */
@@ -1277,7 +1276,7 @@ bool insert_keyframe_direct(ReportList *reports,
MEM_freeN(values);
}
- return insert_keyframe_value(reports, &ptr, prop, fcu, cfra, curval, keytype, flag);
+ return insert_keyframe_value(reports, &ptr, prop, fcu, anim_eval_context, curval, keytype, flag);
}
/* Find or create the FCurve based on the given path, and insert the specified value into it. */
@@ -1289,7 +1288,7 @@ static bool insert_keyframe_fcurve_value(Main *bmain,
const char group[],
const char rna_path[],
int array_index,
- float cfra,
+ const AnimationEvalContext *anim_eval_context,
float curval,
eBezTriple_KeyframeType keytype,
eInsertKeyFlags flag)
@@ -1323,11 +1322,33 @@ static bool insert_keyframe_fcurve_value(Main *bmain,
update_autoflags_fcurve_direct(fcu, prop);
/* insert keyframe */
- return insert_keyframe_value(reports, ptr, prop, fcu, cfra, curval, keytype, flag);
+ return insert_keyframe_value(
+ reports, ptr, prop, fcu, anim_eval_context, curval, keytype, flag);
}
- else {
- return false;
+
+ return false;
+}
+
+static AnimationEvalContext nla_time_remap(const AnimationEvalContext *anim_eval_context,
+ PointerRNA *id_ptr,
+ AnimData *adt,
+ bAction *act,
+ ListBase *nla_cache,
+ NlaKeyframingContext **r_nla_context)
+{
+ if (adt && adt->action == act) {
+ /* Get NLA context for value remapping. */
+ *r_nla_context = BKE_animsys_get_nla_keyframing_context(
+ nla_cache, id_ptr, adt, anim_eval_context, false);
+
+ /* Apply NLA-mapping to frame. */
+ const float remapped_frame = BKE_nla_tweakedit_remap(
+ adt, anim_eval_context->eval_time, NLATIME_CONVERT_UNMAP);
+ return BKE_animsys_eval_context_construct_at(anim_eval_context, remapped_frame);
}
+
+ *r_nla_context = NULL;
+ return *anim_eval_context;
}
/**
@@ -1350,7 +1371,7 @@ int insert_keyframe(Main *bmain,
const char group[],
const char rna_path[],
int array_index,
- float cfra,
+ const AnimationEvalContext *anim_eval_context,
eBezTriple_KeyframeType keytype,
ListBase *nla_cache,
eInsertKeyFlags flag)
@@ -1397,15 +1418,8 @@ int insert_keyframe(Main *bmain,
/* apply NLA-mapping to frame to use (if applicable) */
adt = BKE_animdata_from_id(id);
-
- if (adt && adt->action == act) {
- /* Get NLA context for value remapping. */
- nla_context = BKE_animsys_get_nla_keyframing_context(
- nla_cache ? nla_cache : &tmp_nla_cache, &id_ptr, adt, cfra, false);
-
- /* Apply NLA-mapping to frame. */
- cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
- }
+ const AnimationEvalContext remapped_context = nla_time_remap(
+ anim_eval_context, &id_ptr, adt, act, nla_cache ? nla_cache : &tmp_nla_cache, &nla_context);
/* Obtain values to insert. */
float value_buffer[RNA_MAX_ARRAY_LENGTH];
@@ -1439,7 +1453,7 @@ int insert_keyframe(Main *bmain,
group,
rna_path,
array_index,
- cfra,
+ &remapped_context,
values[array_index],
keytype,
flag)) {
@@ -1462,7 +1476,7 @@ int insert_keyframe(Main *bmain,
group,
rna_path,
array_index,
- cfra,
+ &remapped_context,
values[array_index],
keytype,
flag);
@@ -1481,7 +1495,7 @@ int insert_keyframe(Main *bmain,
group,
rna_path,
array_index,
- cfra,
+ &remapped_context,
values[array_index],
keytype,
flag);
@@ -1499,7 +1513,7 @@ int insert_keyframe(Main *bmain,
group,
rna_path,
array_index,
- cfra,
+ &remapped_context,
values[array_index],
keytype,
flag);
@@ -1851,7 +1865,8 @@ static int insert_key_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No suitable context info for active keying set");
return OPERATOR_CANCELLED;
}
- else if (num_channels > 0) {
+
+ if (num_channels > 0) {
/* if the appropriate properties have been set, make a note that we've inserted something */
if (RNA_boolean_get(op->ptr, "confirm_success")) {
BKE_reportf(op->reports,
@@ -1961,13 +1976,12 @@ static int insert_key_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UN
return OPERATOR_INTERFACE;
}
- else {
- /* just call the exec() on the active keyingset */
- RNA_enum_set(op->ptr, "type", 0);
- RNA_boolean_set(op->ptr, "confirm_success", true);
- return op->type->exec(C, op);
- }
+ /* just call the exec() on the active keyingset */
+ RNA_enum_set(op->ptr, "type", 0);
+ RNA_boolean_set(op->ptr, "confirm_success", true);
+
+ return op->type->exec(C, op);
}
void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot)
@@ -2067,7 +2081,8 @@ static int delete_key_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No suitable context info for active keying set");
return OPERATOR_CANCELLED;
}
- else if (num_channels > 0) {
+
+ if (num_channels > 0) {
/* if the appropriate properties have been set, make a note that we've inserted something */
if (RNA_boolean_get(op->ptr, "confirm_success")) {
BKE_reportf(op->reports,
@@ -2378,7 +2393,8 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
PropertyRNA *prop = NULL;
char *path;
uiBut *but;
- float cfra = (float)CFRA;
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ CTX_data_depsgraph_pointer(C), (float)CFRA);
bool changed = false;
int index;
const bool all = RNA_boolean_get(op->ptr, "all");
@@ -2404,7 +2420,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
if (fcu) {
changed = insert_keyframe_direct(
- op->reports, ptr, prop, fcu, cfra, ts->keyframe_type, NULL, 0);
+ op->reports, ptr, prop, fcu, &anim_eval_context, ts->keyframe_type, NULL, 0);
}
else {
BKE_report(op->reports,
@@ -2420,8 +2436,14 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
fcu = BKE_fcurve_find_by_rna_context_ui(C, &ptr, prop, index, NULL, NULL, &driven, &special);
if (fcu && driven) {
- changed = insert_keyframe_direct(
- op->reports, ptr, prop, fcu, cfra, ts->keyframe_type, NULL, INSERTKEY_DRIVER);
+ changed = insert_keyframe_direct(op->reports,
+ ptr,
+ prop,
+ fcu,
+ &anim_eval_context,
+ ts->keyframe_type,
+ NULL,
+ INSERTKEY_DRIVER);
}
}
else {
@@ -2464,7 +2486,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
group,
path,
index,
- cfra,
+ &anim_eval_context,
ts->keyframe_type,
NULL,
flag) != 0);
@@ -2727,17 +2749,16 @@ bool autokeyframe_cfra_can_key(const Scene *scene, ID *id)
*/
return id_frame_has_keyframe(id, cfra, ANIMFILTER_KEYS_LOCAL);
}
- else {
- /* Normal Mode (or treat as being normal mode):
- *
- * Just in case the flags aren't set properly (i.e. only on/off is set, without a mode)
- * let's set the "normal" flag too, so that it will all be sane everywhere...
- */
- scene->toolsettings->autokey_mode = AUTOKEY_MODE_NORMAL;
- /* Can insert anytime we like... */
- return true;
- }
+ /* Normal Mode (or treat as being normal mode):
+ *
+ * Just in case the flags aren't set properly (i.e. only on/off is set, without a mode)
+ * let's set the "normal" flag too, so that it will all be sane everywhere...
+ */
+ scene->toolsettings->autokey_mode = AUTOKEY_MODE_NORMAL;
+
+ /* Can insert anytime we like... */
+ return true;
}
/* ******************************************* */
@@ -2773,7 +2794,10 @@ bool fcurve_frame_has_keyframe(FCurve *fcu, float frame, short filter)
}
/* Returns whether the current value of a given property differs from the interpolated value. */
-bool fcurve_is_changed(PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float frame)
+bool fcurve_is_changed(PointerRNA ptr,
+ PropertyRNA *prop,
+ FCurve *fcu,
+ const AnimationEvalContext *anim_eval_context)
{
PathResolvedRNA anim_rna;
anim_rna.ptr = ptr;
@@ -2784,7 +2808,7 @@ bool fcurve_is_changed(PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, float fra
int count, index = fcu->array_index;
float *values = setting_get_rna_values(&ptr, prop, buffer, RNA_MAX_ARRAY_LENGTH, &count);
- float fcurve_val = calculate_fcurve(&anim_rna, fcu, frame);
+ float fcurve_val = calculate_fcurve(&anim_rna, fcu, anim_eval_context);
float cur_val = (index >= 0 && index < count) ? values[index] : 0.0f;
if (values != buffer) {
@@ -2950,9 +2974,7 @@ bool ED_autokeyframe_object(bContext *C, Scene *scene, Object *ob, KeyingSet *ks
return true;
}
- else {
- return false;
- }
+ return false;
}
bool ED_autokeyframe_pchan(
@@ -2977,14 +2999,13 @@ bool ED_autokeyframe_pchan(
return true;
}
- else {
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
- return false;
+ /* add unkeyed tags */
+ if (pchan->bone) {
+ pchan->bone->flag |= BONE_UNKEYED;
}
+
+ return false;
}
/**
@@ -2994,6 +3015,9 @@ bool ED_autokeyframe_property(
bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra)
{
Main *bmain = CTX_data_main(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
ID *id;
bAction *action;
FCurve *fcu;
@@ -3014,7 +3038,8 @@ bool ED_autokeyframe_property(
ReportList *reports = CTX_wm_reports(C);
ToolSettings *ts = scene->toolsettings;
- changed = insert_keyframe_direct(reports, *ptr, prop, fcu, cfra, ts->keyframe_type, NULL, 0);
+ changed = insert_keyframe_direct(
+ reports, *ptr, prop, fcu, &anim_eval_context, ts->keyframe_type, NULL, 0);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
}
@@ -3027,7 +3052,7 @@ bool ED_autokeyframe_property(
ToolSettings *ts = scene->toolsettings;
changed = insert_keyframe_direct(
- reports, *ptr, prop, fcu, cfra, ts->keyframe_type, NULL, INSERTKEY_DRIVER);
+ reports, *ptr, prop, fcu, &anim_eval_context, ts->keyframe_type, NULL, INSERTKEY_DRIVER);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
}
@@ -3051,7 +3076,7 @@ bool ED_autokeyframe_property(
((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path,
rnaindex,
- cfra,
+ &anim_eval_context,
ts->keyframe_type,
NULL,
flag) != 0;
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index 89c7860982b..876740b889a 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -96,9 +96,8 @@ static bool keyingset_poll_activePath_edit(bContext *C)
if (scene->active_keyingset <= 0) {
return 0;
}
- else {
- ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
- }
+
+ ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
/* there must be an active KeyingSet and an active path */
return ((ks) && (ks->paths.first) && (ks->active_path > 0));
@@ -158,13 +157,13 @@ static int remove_active_keyingset_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No active Keying Set to remove");
return OPERATOR_CANCELLED;
}
- else if (scene->active_keyingset < 0) {
+
+ if (scene->active_keyingset < 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot remove built in keying set");
return OPERATOR_CANCELLED;
}
- else {
- ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
- }
+
+ ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
/* free KeyingSet's data, then remove it from the scene */
BKE_keyingset_free(ks);
@@ -207,9 +206,8 @@ static int add_empty_ks_path_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No active Keying Set to add empty path to");
return OPERATOR_CANCELLED;
}
- else {
- ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
- }
+
+ ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
/* don't use the API method for this, since that checks on values... */
ksp = MEM_callocN(sizeof(KS_Path), "KeyingSetPath Empty");
@@ -414,13 +412,13 @@ static int remove_keyingset_button_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No active Keying Set to remove property from");
return OPERATOR_CANCELLED;
}
- else if (scene->active_keyingset < 0) {
+
+ if (scene->active_keyingset < 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot remove property from built in keying set");
return OPERATOR_CANCELLED;
}
- else {
- ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
- }
+
+ ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
if (ptr.owner_id && ptr.data && prop) {
path = RNA_path_from_ID_to_property(&ptr, prop);
@@ -688,9 +686,7 @@ KeyingSet *ANIM_scene_get_active_keyingset(const Scene *scene)
if (scene->active_keyingset > 0) {
return BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
}
- else {
- return BLI_findlink(&builtin_keyingsets, (-scene->active_keyingset) - 1);
- }
+ return BLI_findlink(&builtin_keyingsets, (-scene->active_keyingset) - 1);
}
/* Get the index of the Keying Set provided, for the given Scene */
@@ -722,9 +718,7 @@ int ANIM_scene_get_keyingset_index(Scene *scene, KeyingSet *ks)
if (index != -1) {
return -(index + 1);
}
- else {
- return 0;
- }
+ return 0;
}
/* Get Keying Set to use for Auto-Keyframing some transforms */
@@ -737,12 +731,10 @@ KeyingSet *ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *tra
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (scene->active_keyingset)) {
return ANIM_scene_get_active_keyingset(scene);
}
- else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
+ if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
return ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_AVAILABLE_ID);
}
- else {
- return ANIM_builtin_keyingset_get_named(NULL, transformKSName);
- }
+ return ANIM_builtin_keyingset_get_named(NULL, transformKSName);
}
/* Menu of All Keying Sets ----------------------------- */
@@ -1128,6 +1120,9 @@ int ANIM_apply_keyingset(
/* for each possible index, perform operation
* - assume that arraylen is greater than index
*/
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
for (; i < arraylen; i++) {
/* action to take depends on mode */
if (mode == MODIFYKEY_MODE_INSERT) {
@@ -1138,7 +1133,7 @@ int ANIM_apply_keyingset(
groupname,
ksp->rna_path,
i,
- cfra,
+ &anim_eval_context,
keytype,
&nla_cache,
kflag2);
diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c
index 7679995d9a4..edc36326c57 100644
--- a/source/blender/editors/animation/time_scrub_ui.c
+++ b/source/blender/editors/animation/time_scrub_ui.c
@@ -92,10 +92,11 @@ 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;
- const uchar color[] = {255, 255, 255, 255};
int frame_x = UI_view2d_view_to_region_x(v2d, current_frame);
char frame_str[64];
@@ -107,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,
@@ -127,11 +144,35 @@ static void draw_current_frame(const Scene *scene,
4 * UI_DPI_FAC,
bg_color);
+ uchar text_color[4];
+ UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, text_color);
UI_fontstyle_draw_simple(fstyle,
frame_x - text_width / 2 + U.pixelsize / 2,
get_centered_text_y(scrub_region_rect),
frame_str,
- color);
+ 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,
@@ -160,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_add.c b/source/blender/editors/armature/armature_add.c
index 895b4953992..016a00bda56 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -182,9 +182,7 @@ static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op))
if (flipbone == NULL) {
break;
}
- else {
- SWAP(EditBone *, flipbone, ebone);
- }
+ SWAP(EditBone *, flipbone, ebone);
}
newbone = ED_armature_ebone_add(arm, ebone->name);
@@ -1084,13 +1082,12 @@ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone)
if (bone == NULL) {
return NULL;
}
- else if (bone->temp.ebone != NULL) {
+ if (bone->temp.ebone != NULL) {
return bone->temp.ebone;
}
- else {
- EditBone *mirror = ED_armature_ebone_get_mirrored(arm->edbo, bone);
- return (mirror != NULL) ? mirror : bone;
- }
+
+ EditBone *mirror = ED_armature_ebone_get_mirrored(arm->edbo, bone);
+ return (mirror != NULL) ? mirror : bone;
}
/**
@@ -1417,9 +1414,7 @@ static int armature_extrude_exec(bContext *C, wmOperator *op)
if (flipbone == NULL) {
break;
}
- else {
- SWAP(EditBone *, flipbone, ebone);
- }
+ SWAP(EditBone *, flipbone, ebone);
}
totbone++;
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index a7a705a6202..113fd54713e 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 */
@@ -728,7 +728,8 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No joints selected");
return OPERATOR_CANCELLED;
}
- else if (mixed_object_error) {
+
+ if (mixed_object_error) {
BKE_report(op->reports, RPT_ERROR, "Bones for different objects selected");
BLI_freelistN(&points);
return OPERATOR_CANCELLED;
@@ -1093,7 +1094,8 @@ static int armature_align_bones_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
return OPERATOR_CANCELLED;
}
- else if (arm->flag & ARM_MIRROR_EDIT) {
+
+ if (arm->flag & ARM_MIRROR_EDIT) {
/* For X-Axis Mirror Editing option, we may need a mirror copy of actbone
* - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone
* (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index d3d00fc44f2..0dd35fb9fdc 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -207,7 +207,7 @@ static void joined_armature_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data
id, dtar->rna_path, "pose.bones", old_name, new_name, 0, 0, false);
break; /* no need to try any more names for bone path */
}
- else if (STREQ(dtar->pchan_name, old_name)) {
+ if (STREQ(dtar->pchan_name, old_name)) {
/* Change target bone name */
BLI_strncpy(dtar->pchan_name, new_name, sizeof(dtar->pchan_name));
break; /* no need to try any more names for bone subtarget */
@@ -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);
@@ -636,7 +636,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op)
has_selected_bone = true;
break;
}
- else if (ebone->flag & (BONE_TIPSEL | BONE_ROOTSEL)) {
+ if (ebone->flag & (BONE_TIPSEL | BONE_ROOTSEL)) {
has_selected_any = true;
}
}
@@ -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 */
@@ -816,7 +816,7 @@ static int armature_parent_set_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
return OPERATOR_CANCELLED;
}
- else if (arm->flag & ARM_MIRROR_EDIT) {
+ if (arm->flag & ARM_MIRROR_EDIT) {
/* For X-Axis Mirror Editing option, we may need a mirror copy of actbone:
* - If there's a mirrored copy of selbone, try to find a mirrored copy of actbone
* (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
@@ -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..7fba855ffdb 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);
@@ -263,10 +263,8 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode
*r_base = firstunSel_base;
return firstunSel;
}
- else {
- *r_base = firstSel_base;
- return firstSel;
- }
+ *r_base = firstSel_base;
+ return firstSel;
}
EditBone *ED_armature_pick_ebone_from_selectbuffer(Base **bases,
@@ -1302,7 +1300,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 +1315,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;
}
}
}
@@ -2253,10 +2251,9 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_WARNING, "Unselectable bone in chain");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_WARNING, "Unselectable bone in chain");
+ return OPERATOR_CANCELLED;
}
void ARMATURE_OT_shortest_path_pick(wmOperatorType *ot)
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/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index cf7f6699e5e..b2e329deee7 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -392,7 +392,7 @@ void armature_tag_unselect(bArmature *arm)
void ED_armature_ebone_transform_mirror_update(bArmature *arm, EditBone *ebo, bool check_select)
{
/* TODO When this function is called by property updates,
- * cancelling the value change will not restore mirrored bone correctly. */
+ * canceling the value change will not restore mirrored bone correctly. */
/* Currently check_select==true when this function is called from a transform operator,
* eg. from 3d viewport. */
@@ -915,9 +915,7 @@ int ED_armature_ebone_selectflag_get(const EditBone *ebone)
return ((ebone->flag & (BONE_SELECTED | BONE_TIPSEL)) |
((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0));
}
- else {
- return (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL));
- }
+ return (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL));
}
void ED_armature_ebone_selectflag_set(EditBone *ebone, int flag)
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index d8a6a22a7df..11066595e2e 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 */
@@ -641,13 +640,11 @@ static float heat_limit_weight(float weight)
if (weight < WEIGHT_LIMIT_END) {
return 0.0f;
}
- else if (weight < WEIGHT_LIMIT_START) {
+ if (weight < WEIGHT_LIMIT_START) {
t = (weight - WEIGHT_LIMIT_END) / (WEIGHT_LIMIT_START - WEIGHT_LIMIT_END);
return t * WEIGHT_LIMIT_START;
}
- else {
- return weight;
- }
+ return weight;
}
void heat_bone_weighting(Object *ob,
@@ -658,7 +655,7 @@ void heat_bone_weighting(Object *ob,
bDeformGroup **dgroupflip,
float (*root)[3],
float (*tip)[3],
- int *selected,
+ const int *selected,
const char **err_str)
{
LaplacianSystem *sys;
@@ -1239,7 +1236,7 @@ static float meshdeform_boundary_phi(const MeshDeformBind *mdb,
}
static float meshdeform_interp_w(MeshDeformBind *mdb,
- float *gridvec,
+ const float *gridvec,
float *UNUSED(vec),
int UNUSED(cagevert))
{
diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h
index ef4759eab4a..0a9e6e878e4 100644
--- a/source/blender/editors/armature/meshlaplacian.h
+++ b/source/blender/editors/armature/meshlaplacian.h
@@ -56,7 +56,7 @@ void heat_bone_weighting(struct Object *ob,
struct bDeformGroup **dgroupflip,
float (*root)[3],
float (*tip)[3],
- int *selected,
+ const int *selected,
const char **error);
#ifdef RIGID_DEFORM
diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c
index c10e204e3a4..cb7c68178d9 100644
--- a/source/blender/editors/armature/pose_group.c
+++ b/source/blender/editors/armature/pose_group.c
@@ -177,11 +177,10 @@ static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *U
return OPERATOR_INTERFACE;
}
- else {
- /* just use the active group index, and call the exec callback for the calling operator */
- RNA_int_set(op->ptr, "type", pose->active_group);
- return op->type->exec(C, op);
- }
+
+ /* just use the active group index, and call the exec callback for the calling operator */
+ RNA_int_set(op->ptr, "type", pose->active_group);
+ return op->type->exec(C, op);
}
/* Assign selected pchans to the bone group that the user selects */
@@ -221,9 +220,7 @@ static int pose_group_assign_exec(bContext *C, wmOperator *op)
if (done) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void POSE_OT_group_assign(wmOperatorType *ot)
@@ -272,9 +269,7 @@ static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op))
if (done) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void POSE_OT_group_unassign(wmOperatorType *ot)
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index aa57fb5844d..c9d0478270a 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -87,12 +87,12 @@ static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUS
* It acts as a kind of "glorified clipboard for poses", allowing for naming of poses.
*
* Features:
- * - PoseLibs are simply normal Actions.
- * - Each "pose" is simply a set of keyframes that occur on a particular frame.
- * - A set of TimeMarkers that belong to each Action, help 'label' where a 'pose' can be
+ * - Pose-libs are simply normal Actions.
+ * - Each "pose" is simply a set of key-frames that occur on a particular frame.
+ * - A set of #TimeMarker that belong to each Action, help 'label' where a 'pose' can be
* found in the Action.
- * - The Scrollwheel or PageUp/Down buttons when used in a special mode or after pressing/holding
- * [a modifier] key, cycles through the poses available for the active pose's poselib,
+ * - The Scroll-wheel or PageUp/Down buttons when used in a special mode or after pressing/holding
+ * [a modifier] key, cycles through the poses available for the active pose's pose-lib,
* allowing the animator to preview what action best suits that pose.
*/
/* ************************************************************* */
@@ -141,9 +141,7 @@ static int poselib_get_free_index(bAction *act)
if (low < high) {
return (low + 1);
}
- else {
- return (high + 1);
- }
+ return (high + 1);
}
/* returns the active pose for a poselib */
@@ -152,9 +150,7 @@ static TimeMarker *poselib_get_active_pose(bAction *act)
if ((act) && (act->active_marker)) {
return BLI_findlink(&act->markers, act->active_marker - 1);
}
- else {
- return NULL;
- }
+ return NULL;
}
/* Get object that Pose Lib should be found on */
@@ -173,9 +169,7 @@ static Object *get_poselib_object(bContext *C)
if (area && (area->spacetype == SPACE_PROPERTIES)) {
return ED_object_context(C);
}
- else {
- return BKE_object_pose_armature_get(CTX_data_active_object(C));
- }
+ return BKE_object_pose_armature_get(CTX_data_active_object(C));
}
/* Poll callback for operators that require existing PoseLib data (with poses) to work */
@@ -221,12 +215,10 @@ static bAction *poselib_validate(Main *bmain, Object *ob)
if (ELEM(NULL, ob, ob->pose)) {
return NULL;
}
- else if (ob->poselib == NULL) {
+ if (ob->poselib == NULL) {
return poselib_init_new(bmain, ob);
}
- else {
- return ob->poselib;
- }
+ return ob->poselib;
}
/* ************************************************************* */
@@ -697,12 +689,11 @@ static int poselib_rename_invoke(bContext *C, wmOperator *op, const wmEvent *eve
BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
return OPERATOR_CANCELLED;
}
- else {
- /* Use the existing name of the marker as the name,
- * and use the active marker as the one to rename. */
- RNA_enum_set(op->ptr, "pose", act->active_marker - 1);
- RNA_string_set(op->ptr, "name", marker->name);
- }
+
+ /* Use the existing name of the marker as the name,
+ * and use the active marker as the one to rename. */
+ RNA_enum_set(op->ptr, "pose", act->active_marker - 1);
+ RNA_string_set(op->ptr, "name", marker->name);
/* part to sync with other similar operators... */
return WM_operator_props_popup_confirm(C, op, event);
@@ -1032,7 +1023,8 @@ static void poselib_backup_free_data(tPoseLib_PreviewData *pld)
* - gets the string to print in the header
* - this code is based on the code for extract_pose_from_action in blenkernel/action.c
*/
-static void poselib_apply_pose(tPoseLib_PreviewData *pld)
+static void poselib_apply_pose(tPoseLib_PreviewData *pld,
+ const AnimationEvalContext *anim_eval_context)
{
PointerRNA *ptr = &pld->rna_ptr;
bArmature *arm = pld->arm;
@@ -1058,6 +1050,8 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld)
group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
ked.f1 = ((float)frame) - 0.5f;
ked.f2 = ((float)frame) + 0.5f;
+ AnimationEvalContext anim_context_at_frame = BKE_animsys_eval_context_construct_at(
+ anim_eval_context, frame);
/* start applying - only those channels which have a key at this point in time! */
for (agrp = act->groups.first; agrp; agrp = agrp->next) {
@@ -1084,7 +1078,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld)
}
if (ok) {
- animsys_evaluate_action_group(ptr, act, agrp, (float)frame);
+ animsys_evaluate_action_group(ptr, act, agrp, &anim_context_at_frame);
}
}
}
@@ -1159,7 +1153,11 @@ static void poselib_preview_apply(bContext *C, wmOperator *op)
/* pose should be the right one to draw (unless we're temporarily not showing it) */
if ((pld->flag & PL_PREVIEW_SHOWORIGINAL) == 0) {
RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker));
- poselib_apply_pose(pld);
+ struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, 0.0f /* poselib_apply_pose() determines its own evaluation time. */);
+ poselib_apply_pose(pld, &anim_eval_context);
}
else {
RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */
@@ -1755,9 +1753,7 @@ static int poselib_preview_exit(bContext *C, wmOperator *op)
if (ELEM(exit_state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR)) {
return OPERATOR_CANCELLED;
}
- else {
- return OPERATOR_FINISHED;
- }
+ return OPERATOR_FINISHED;
}
/* Cancel previewing operation (called when exiting Blender) */
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 9525fcf2154..0e766e6a75f 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -1016,7 +1016,7 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool ex
BKE_report(reports, RPT_ERROR, "No active Keying Set to use");
return false;
}
- else if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
+ if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
if (ks->paths.first == NULL) {
if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) {
BKE_report(reports,
@@ -1129,9 +1129,7 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void POSE_OT_select_grouped(wmOperatorType *ot)
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index d9621dba730..8df9c99896e 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -1122,7 +1122,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
do_pose_update = true;
break;
}
- else if (event->val == KM_PRESS) {
+ if (event->val == KM_PRESS) {
switch (event->type) {
/* Transform Channel Limits */
/* XXX: Replace these hardcoded hotkeys with a modalmap that can be customised */
@@ -1307,9 +1307,8 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1328,9 +1327,8 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op)
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* do common exec work */
return pose_slide_exec_common(C, op, pso);
@@ -1369,9 +1367,8 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1390,9 +1387,8 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op)
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* do common exec work */
return pose_slide_exec_common(C, op, pso);
@@ -1430,9 +1426,8 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1451,9 +1446,8 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op)
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* do common exec work */
return pose_slide_exec_common(C, op, pso);
@@ -1492,9 +1486,8 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1513,9 +1506,8 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op)
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* do common exec work */
return pose_slide_exec_common(C, op, pso);
@@ -1554,9 +1546,8 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* initialise percentage so that it won't pop on first mouse move */
pose_slide_mouse_update_percentage(pso, op, event);
@@ -1575,9 +1566,8 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
pose_slide_exit(op);
return OPERATOR_CANCELLED;
}
- else {
- pso = op->customdata;
- }
+
+ pso = op->customdata;
/* do common exec work */
return pose_slide_exec_common(C, op, pso);
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index 1d2bf152777..a6cf8552ca4 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -1219,7 +1219,9 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
Scene *scene = CTX_data_scene(C);
- float cframe = (float)CFRA;
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ (float)CFRA);
const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
@@ -1240,7 +1242,8 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
workob.adt = ob->adt;
workob.pose = dummyPose;
- BKE_animsys_evaluate_animdata(&workob.id, workob.adt, cframe, ADT_RECALC_ANIM, false);
+ BKE_animsys_evaluate_animdata(
+ &workob.id, workob.adt, &anim_eval_context, ADT_RECALC_ANIM, false);
/* copy back values, but on selected bones only */
for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) {
@@ -1259,10 +1262,8 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
MEM_freeN(dummyPose);
}
else {
- /* no animation, so just reset whole pose to rest pose
- * (cannot just restore for selected though)
- */
- BKE_pose_rest(ob->pose);
+ /* No animation, so just reset to the rest pose. */
+ BKE_pose_rest(ob->pose, only_select);
}
/* notifiers and updates */
@@ -1279,7 +1280,7 @@ void POSE_OT_user_transforms_clear(wmOperatorType *ot)
/* identifiers */
ot->name = "Clear User Transforms";
ot->idname = "POSE_OT_user_transforms_clear";
- ot->description = "Reset pose on selected bones to keyframed state";
+ ot->description = "Reset pose bone transforms to keyframed state";
/* callbacks */
ot->exec = pose_clear_user_transforms_exec;
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 4e1c07af001..91a8ea0fa3a 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);
@@ -2436,12 +2436,12 @@ static void adduplicateflagNurb(
/* actvert in cyclicu selection */
break;
}
- else if (calc_duplicate_actvert(editnurb,
- newnurb,
- cu,
- starta,
- starta + newu,
- cu->actvert - starta + b * newnu->pntsu)) {
+ if (calc_duplicate_actvert(editnurb,
+ newnurb,
+ cu,
+ starta,
+ starta + newu,
+ cu->actvert - starta + b * newnu->pntsu)) {
/* actvert in 'current' iteration selection */
break;
}
@@ -4085,7 +4085,7 @@ static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op)
void CURVE_OT_normals_make_consistent(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Recalc Normals";
+ ot->name = "Recalculate Handles";
ot->description = "Recalculate the direction of selected handles";
ot->idname = "CURVE_OT_normals_make_consistent";
@@ -4544,15 +4544,13 @@ static int make_segment_exec(bContext *C, wmOperator *op)
if (nu_select_num > 1) {
break;
}
- else {
- /* only 1 selected, not first or last, a little complex, but intuitive */
- if (nu->pntsv == 1) {
- if ((nu->bp->f1 & SELECT) || (nu->bp[nu->pntsu - 1].f1 & SELECT)) {
- /* pass */
- }
- else {
- break;
- }
+ /* only 1 selected, not first or last, a little complex, but intuitive */
+ if (nu->pntsv == 1) {
+ if ((nu->bp->f1 & SELECT) || (nu->bp[nu->pntsu - 1].f1 & SELECT)) {
+ /* pass */
+ }
+ else {
+ break;
}
}
}
@@ -5500,6 +5498,7 @@ static int ed_editcurve_addvert(Curve *cu,
if (nu) {
nurb_new = BKE_nurb_copy(nu, 1, 1);
+ memcpy(nurb_new->bezt, nu->bezt, sizeof(BezTriple));
}
else {
nurb_new = MEM_callocN(sizeof(Nurb), "BLI_editcurve_addvert new_bezt_nurb 2");
@@ -5593,9 +5592,7 @@ static int add_vertex_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -6517,9 +6514,7 @@ static int curve_delete_exec(bContext *C, wmOperator *op)
if (changed_multi) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static const EnumPropertyItem curve_delete_type_items[] = {
@@ -6895,7 +6890,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_query.c b/source/blender/editors/curve/editcurve_query.c
index 132f7e58e71..774065b06ff 100644
--- a/source/blender/editors/curve/editcurve_query.c
+++ b/source/blender/editors/curve/editcurve_query.c
@@ -191,7 +191,8 @@ void ED_curve_nurb_vert_selected_find(
*r_bezt = NULL;
return;
}
- else if (*r_bezt || *r_bp) {
+
+ if (*r_bezt || *r_bp) {
*r_bp = NULL;
*r_bezt = NULL;
}
@@ -214,7 +215,8 @@ void ED_curve_nurb_vert_selected_find(
*r_nu = NULL;
return;
}
- else if (*r_bezt || *r_bp) {
+
+ if (*r_bezt || *r_bp) {
*r_bp = NULL;
*r_bezt = NULL;
}
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index 9294bc6e91b..b60eb258916 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -66,12 +66,11 @@ bool select_beztriple(BezTriple *bezt, bool selstatus, short flag, eVisible_Type
bezt->f3 |= flag;
return true;
}
- else { /* deselects */
- bezt->f1 &= ~flag;
- bezt->f2 &= ~flag;
- bezt->f3 &= ~flag;
- return true;
- }
+ /* deselects */
+ bezt->f1 &= ~flag;
+ bezt->f2 &= ~flag;
+ bezt->f3 &= ~flag;
+ return true;
}
return false;
@@ -85,10 +84,8 @@ bool select_bpoint(BPoint *bp, bool selstatus, short flag, bool hidden)
bp->f1 |= flag;
return true;
}
- else {
- bp->f1 &= ~flag;
- return true;
- }
+ bp->f1 &= ~flag;
+ return true;
}
return false;
@@ -99,9 +96,7 @@ static bool swap_selection_beztriple(BezTriple *bezt)
if (bezt->f2 & SELECT) {
return select_beztriple(bezt, DESELECT, SELECT, VISIBLE);
}
- else {
- return select_beztriple(bezt, SELECT, SELECT, VISIBLE);
- }
+ return select_beztriple(bezt, SELECT, SELECT, VISIBLE);
}
static bool swap_selection_bpoint(BPoint *bp)
@@ -109,9 +104,7 @@ static bool swap_selection_bpoint(BPoint *bp)
if (bp->f1 & SELECT) {
return select_bpoint(bp, DESELECT, SELECT, VISIBLE);
}
- else {
- return select_bpoint(bp, SELECT, SELECT, VISIBLE);
- }
+ return select_bpoint(bp, SELECT, SELECT, VISIBLE);
}
bool ED_curve_nurb_select_check(View3D *v3d, Nurb *nu)
@@ -988,7 +981,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 +1196,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..d78c543f94b 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -76,9 +76,9 @@ static int kill_selection(Object *obedit, int ins);
/** \name Internal Utilities
* \{ */
-static wchar_t findaccent(wchar_t char1, uint code)
+static char32_t findaccent(char32_t char1, uint code)
{
- wchar_t new = 0;
+ char32_t new = 0;
if (char1 == 'a') {
if (code == '`') {
@@ -373,9 +373,7 @@ static wchar_t findaccent(wchar_t char1, uint code)
if (new) {
return new;
}
- else {
- return char1;
- }
+ return char1;
}
static int insert_into_textbuf(Object *obedit, uintptr_t c)
@@ -403,9 +401,7 @@ static int insert_into_textbuf(Object *obedit, uintptr_t c)
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static void text_update_edited(bContext *C, Object *obedit, int mode)
@@ -686,7 +682,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const
cu->strinfo = MEM_callocN((nchars + 4) * sizeof(CharInfo), "strinfo");
cu->len = 0;
- cu->len_wchar = nchars - 1;
+ cu->len_char32 = nchars - 1;
cu->pos = 0;
s = cu->str;
@@ -707,7 +703,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const
}
}
- cu->pos = cu->len_wchar;
+ cu->pos = cu->len_char32;
*s = '\0';
WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, obedit);
@@ -887,9 +883,7 @@ static int font_select_all_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void FONT_OT_select_all(wmOperatorType *ot)
@@ -1015,10 +1009,9 @@ static bool paste_selection(Object *obedit, ReportList *reports)
if (font_paste_wchar(obedit, text_buf, len, info_buf)) {
return true;
}
- else {
- BKE_report(reports, RPT_WARNING, "Text too long");
- return false;
- }
+
+ BKE_report(reports, RPT_WARNING, "Text too long");
+ return false;
}
static int paste_text_exec(bContext *C, wmOperator *op)
@@ -1347,9 +1340,7 @@ static int change_spacing_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void FONT_OT_change_spacing(wmOperatorType *ot)
@@ -1635,7 +1626,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 +1636,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]);
@@ -1670,7 +1661,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
uintptr_t ascii = event->ascii;
int alt = event->alt, shift = event->shift, ctrl = event->ctrl;
int event_type = event->type, event_val = event->val;
- wchar_t inserted_text[2] = {0};
+ char32_t inserted_text[2] = {0};
if (RNA_struct_property_is_set(op->ptr, "text")) {
return insert_text_exec(C, op);
@@ -1688,9 +1679,8 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if ((alt || ctrl || shift) == 0) {
return OPERATOR_PASS_THROUGH;
}
- else {
- ascii = 9;
- }
+
+ ascii = 9;
}
if (event_type == EVT_BACKSPACEKEY) {
@@ -1743,7 +1733,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* store as utf8 in RNA string */
char inserted_utf8[8] = {0};
- BLI_strncpy_wchar_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8));
+ BLI_str_utf32_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8));
RNA_string_set(op->ptr, "text", inserted_utf8);
}
@@ -1877,7 +1867,7 @@ void ED_curve_editfont_make(Object *obedit)
{
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
- int len_wchar;
+ int len_char32;
if (ef == NULL) {
ef = cu->editfont = MEM_callocN(sizeof(EditFont), "editfont");
@@ -1886,10 +1876,10 @@ void ED_curve_editfont_make(Object *obedit)
ef->textbufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditbufinfo");
}
- /* Convert the original text to wchar_t */
- len_wchar = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4);
- BLI_assert(len_wchar == cu->len_wchar);
- ef->len = len_wchar;
+ /* Convert the original text to chat32_t. */
+ len_char32 = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4);
+ BLI_assert(len_char32 == cu->len_char32);
+ ef->len = len_char32;
BLI_assert(ef->len >= 0);
memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo));
@@ -1918,7 +1908,7 @@ void ED_curve_editfont_load(Object *obedit)
MEM_freeN(cu->str);
/* Calculate the actual string length in UTF-8 variable characters */
- cu->len_wchar = ef->len;
+ cu->len_char32 = ef->len;
cu->len = BLI_str_utf32_as_utf8_len(ef->textbuf);
/* Alloc memory for UTF-8 variable char length string */
@@ -1930,8 +1920,8 @@ void ED_curve_editfont_load(Object *obedit)
if (cu->strinfo) {
MEM_freeN(cu->strinfo);
}
- cu->strinfo = MEM_callocN((cu->len_wchar + 4) * sizeof(CharInfo), "texteditinfo");
- memcpy(cu->strinfo, ef->textbufinfo, cu->len_wchar * sizeof(CharInfo));
+ cu->strinfo = MEM_callocN((cu->len_char32 + 4) * sizeof(CharInfo), "texteditinfo");
+ memcpy(cu->strinfo, ef->textbufinfo, cu->len_char32 * sizeof(CharInfo));
/* Other vars */
cu->pos = ef->pos;
@@ -2133,7 +2123,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);
@@ -2290,9 +2280,7 @@ bool ED_curve_editfont_select_pick(
}
return true;
}
- else {
- return false;
- }
+ return false;
}
/** \} */
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/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
index 01e5a7eacfd..033673a99a8 100644
--- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c
+++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
@@ -29,7 +29,6 @@
#include "ED_view3d.h"
#include "GPU_batch.h"
-#include "GPU_glew.h"
#include "GPU_immediate.h"
#include "MEM_guardedalloc.h"
@@ -84,7 +83,7 @@ void wm_gizmo_geometryinfo_draw(const GizmoGeomInfo *info,
/* We may want to re-visit this, for now disable
* since it causes issues leaving the GL state modified. */
#if 0
- glEnable(GL_CULL_FACE);
+ GPU_face_culling(GPU_CULL_BACK);
GPU_depth_test(true);
#endif
@@ -92,7 +91,7 @@ void wm_gizmo_geometryinfo_draw(const GizmoGeomInfo *info,
#if 0
GPU_depth_test(false);
- glDisable(GL_CULL_FACE);
+ GPU_face_culling(GPU_CULL_NONE);
#endif
GPU_batch_discard(batch);
diff --git a/source/blender/editors/gizmo_library/gizmo_library_utils.c b/source/blender/editors/gizmo_library/gizmo_library_utils.c
index 3902c112796..847f3e3916c 100644
--- a/source/blender/editors/gizmo_library/gizmo_library_utils.c
+++ b/source/blender/editors/gizmo_library/gizmo_library_utils.c
@@ -211,14 +211,13 @@ bool gizmo_window_project_2d(bContext *C,
}
return false;
}
- else {
- float co[3] = {mval[0], mval[1], 0.0f};
- float imat[4][4];
- invert_m4_m4(imat, mat);
- mul_m4_v3(imat, co);
- copy_v2_v2(r_co, co);
- return true;
- }
+
+ float co[3] = {mval[0], mval[1], 0.0f};
+ float imat[4][4];
+ invert_m4_m4(imat, mat);
+ mul_m4_v3(imat, co);
+ copy_v2_v2(r_co, co);
+ return true;
}
bool gizmo_window_project_3d(
@@ -245,12 +244,11 @@ bool gizmo_window_project_3d(
mul_m4_v3(mat, r_co);
return true;
}
- else {
- float co[3] = {mval[0], mval[1], 0.0f};
- float imat[4][4];
- invert_m4_m4(imat, mat);
- mul_m4_v3(imat, co);
- copy_v2_v2(r_co, co);
- return true;
- }
+
+ float co[3] = {mval[0], mval[1], 0.0f};
+ float imat[4][4];
+ invert_m4_m4(imat, mat);
+ mul_m4_v3(imat, co);
+ copy_v2_v2(r_co, co);
+ return true;
}
diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
index e6333d7d3e0..f31e004264c 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
@@ -88,7 +88,8 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const
const int draw_style = RNA_enum_get(arrow->gizmo.ptr, "draw_style");
const int draw_options = RNA_enum_get(arrow->gizmo.ptr, "draw_options");
- immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+ immBindBuiltinProgram(select ? GPU_SHADER_3D_UNIFORM_COLOR :
+ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
float viewport[4];
GPU_viewport_size_get_f(viewport);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
index fd24149e9ea..85f84af5f14 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
@@ -637,7 +637,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
}
if (select) {
- /* expand for hotspot */
+ /* Expand for hot-spot. */
const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2};
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE) {
@@ -694,7 +694,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
bool show = false;
if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_TRANSLATE) {
/* Only show if we're drawing the center handle
- * otherwise the entire rectangle is the hotspot. */
+ * otherwise the entire rectangle is the hot-spot. */
if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) {
show = true;
}
@@ -805,7 +805,7 @@ static int gizmo_cage2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
return -1;
}
- /* expand for hotspot */
+ /* Expand for hots-pot. */
const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2};
const int transform_flag = RNA_enum_get(gz->ptr, "transform");
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index b0af8641767..8955a666e22 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -314,7 +314,7 @@ static void gizmo_cage3d_draw_intern(
}
if (select) {
- /* expand for hotspot */
+ /* Expand for hot-spot. */
#if 0
const float size[3] = {
size_real[0] + margin[0] / 2,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
index 262f4b78b95..5d7c3a9e717 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
@@ -461,10 +461,6 @@ static void gizmo_dial_draw_select(const bContext *C, wmGizmo *gz, int select_id
GPU_select_load_id(select_id);
dial_draw_intern(C, gz, true, false, clip_plane);
-
- if (clip_plane) {
- glDisable(GL_CLIP_DISTANCE0);
- }
}
static void gizmo_dial_draw(const bContext *C, wmGizmo *gz)
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..df479325027 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -53,9 +53,8 @@
#include "WM_api.h"
-#include "BIF_glutil.h"
-
#include "GPU_immediate.h"
+#include "GPU_matrix.h"
#include "GPU_state.h"
#include "ED_gpencil.h"
@@ -172,9 +171,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 +197,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 +331,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 +361,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 +434,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 +462,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 */
@@ -539,16 +554,13 @@ static void annotation_draw_strokes(const bGPDframe *gpf,
/* check which stroke-drawer to use */
if (dflag & GP_DRAWDATA_ONLY3D) {
const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
- int mask_orig = 0;
if (no_xray) {
- glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
- glDepthMask(0);
GPU_depth_test(true);
/* first arg is normally rv3d->dist, but this isn't
* available here and seems to work quite well without */
- bglPolygonOffset(1.0f, 1.0f);
+ GPU_polygon_offset(1.0f, 1.0f);
}
/* 3D Lines - OpenGL primitives-based */
@@ -562,10 +574,9 @@ static void annotation_draw_strokes(const bGPDframe *gpf,
}
if (no_xray) {
- glDepthMask(mask_orig);
GPU_depth_test(false);
- bglPolygonOffset(0.0, 0.0);
+ GPU_polygon_offset(0.0f, 0.0f);
}
}
else {
@@ -683,9 +694,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
@@ -730,12 +738,18 @@ static void annotation_draw_data(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
GPU_blend(true);
+ /* Do not write to depth (avoid self-occlusion). */
+ bool prev_depth_mask = GPU_depth_mask_get();
+ GPU_depth_mask(false);
+
/* draw! */
annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag);
/* turn off alpha blending, then smooth lines */
GPU_blend(false); // alpha blending
GPU_line_smooth(false); // smooth lines
+
+ GPU_depth_mask(prev_depth_mask);
}
/* if we have strokes for scenes (3d view)/clips (movie clip editor)
@@ -774,13 +788,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 723c7d214e3..3aec1db2587 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -200,16 +200,16 @@ typedef struct tGPsdata {
/* Macros for accessing sensitivity thresholds... */
/* minimum number of pixels mouse should move before new point created */
-#define MIN_MANHATTEN_PX (U.gp_manhattendist)
+#define MIN_MANHATTAN_PX (U.gp_manhattandist)
/* 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 */
@@ -233,9 +233,7 @@ static bool gpencil_draw_poll(bContext *C)
if (ED_gpencil_session_active() == 0) {
return true;
}
- else {
- CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active");
- }
+ CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active");
}
else {
CTX_wm_operator_poll_msg_set(C, "Failed to find Annotation data to draw into");
@@ -249,7 +247,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 +260,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 +271,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]);
@@ -281,44 +279,46 @@ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2
/* if buffer is empty, just let this go through (i.e. so that dots will work) */
if (p->gpd->runtime.sbuffer_used == 0) {
return true;
-
- /* check if mouse moved at least certain distance on both axes (best case)
- * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
- */
}
+
+ /* check if mouse moved at least certain distance on both axes (best case)
+ * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
+ */
+
/* If lazy mouse, check minimum distance. */
- else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
+ if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) {
if ((dx * dx + dy * dy) > (p->stabilizer_radius * p->stabilizer_radius)) {
return true;
}
- else {
- /* If the mouse is moving within the radius of the last move,
- * don't update the mouse position. This allows sharp turns. */
- copy_v2_v2(p->mval, p->mvalo);
- return false;
- }
- }
- else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) {
- return true;
- /* Check if the distance since the last point is significant enough:
- * - Prevents points being added too densely
- * - Distance here doesn't use sqrt to prevent slowness.
- * We should still be safe from overflows though.
- */
+ /* If the mouse is moving within the radius of the last move,
+ * don't update the mouse position. This allows sharp turns. */
+ copy_v2_v2(p->mval, p->mvalo);
+ return false;
}
- else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) {
- return true;
- /* mouse 'didn't move' */
+ if ((dx > MIN_MANHATTAN_PX) && (dy > MIN_MANHATTAN_PX)) {
+ return true;
}
- else {
- return false;
+
+ /* Check if the distance since the last point is significant enough:
+ * - Prevents points being added too densely
+ * - Distance here doesn't use sqrt to prevent slowness.
+ * We should still be safe from overflows though.
+ */
+ if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) {
+ return true;
}
+
+ /* mouse 'didn't move' */
+ return false;
}
/* 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 +326,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 +346,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 +381,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 +439,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 +452,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 +474,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 +487,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 +502,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 +519,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 +575,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);
}
}
@@ -587,7 +591,8 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
/* can keep carrying on this way :) */
return GP_STROKEADD_NORMAL;
}
- else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
+
+ if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
/* check if still room in buffer or add more */
gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure(
gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
@@ -611,13 +616,14 @@ 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);
}
}
return GP_STROKEADD_NORMAL;
}
- else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
+
+ if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
/* get pointer to destination point */
pt = (tGPspoint *)gpd->runtime.sbuffer;
@@ -632,7 +638,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 +655,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 +666,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 +687,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 +805,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 +843,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 +863,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 +877,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 +895,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 +911,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 +932,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 +953,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 +964,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 +1032,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 +1048,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 +1056,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,22 +1074,22 @@ 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) {
return ED_view3d_calc_zfac(rv3d, co, NULL);
}
- else {
- return -dot_v3v3(rv3d->viewinv[2], co);
- }
+ return -dot_v3v3(rv3d->viewinv[2], co);
}
/* 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 +1108,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 +1125,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 +1171,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 +1181,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 +1199,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 +1234,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 +1243,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 +1258,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;
@@ -1402,17 +1409,16 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
}
return 0;
}
- else {
- /* if no existing GPencil block exists, add one */
- if (*gpd_ptr == NULL) {
- bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations");
- *gpd_ptr = gpd;
- /* mark datablock as being used for annotations */
- gpd->flag |= GP_DATA_ANNOTATIONS;
- }
- p->gpd = *gpd_ptr;
+ /* if no existing GPencil block exists, add one */
+ if (*gpd_ptr == NULL) {
+ bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations");
+ *gpd_ptr = gpd;
+
+ /* mark datablock as being used for annotations */
+ gpd->flag |= GP_DATA_ANNOTATIONS;
}
+ p->gpd = *gpd_ptr;
if (ED_gpencil_session_active() == 0) {
/* initialize undo stack,
@@ -1422,13 +1428,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 +1444,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 +1464,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 +1487,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;
@@ -1558,9 +1566,8 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
}
return;
}
- else {
- p->gpf->flag |= GP_FRAME_PAINT;
- }
+
+ p->gpf->flag |= GP_FRAME_PAINT;
}
/* set 'eraser' for this stroke if using eraser */
@@ -1638,13 +1645,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 +1663,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 +1690,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 +1736,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 +1748,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 +1800,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 +1810,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 +1834,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 +1850,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 +1901,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 +1912,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 +1977,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 +1991,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 +2003,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 +2058,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 +2096,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 +2106,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 +2170,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 +2180,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,16 +2188,15 @@ 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);
}
/* printf("\tGP - no valid data\n"); */
return OPERATOR_CANCELLED;
}
- else {
- p = op->customdata;
- }
+
+ p = op->customdata;
/* printf("\tGP - Start redrawing stroke\n"); */
@@ -2217,8 +2221,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 +2237,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 +2256,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 +2270,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);
}
@@ -2275,27 +2279,20 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
}
return OPERATOR_CANCELLED;
}
- else {
- p = op->customdata;
- }
+
+ p = op->customdata;
/* 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 +2309,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 +2345,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 +2369,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 +2381,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 +2436,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 +2539,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 +2558,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 +2634,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 +2643,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 +2730,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 +2808,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;
@@ -2850,7 +2847,7 @@ void GPENCIL_OT_annotate(wmOperatorType *ot)
0,
200,
"Stabilizer Stroke Radius",
- "Minimun distance from last point before stroke continues",
+ "Minimum distance from last point before stroke continues",
1,
100);
RNA_def_property_subtype(prop, PROP_PIXEL);
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 6d41e9bddbe..9d11c1c2a25 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -60,9 +60,8 @@
#include "WM_api.h"
-#include "BIF_glutil.h"
-
#include "GPU_immediate.h"
+#include "GPU_matrix.h"
#include "GPU_state.h"
#include "ED_gpencil.h"
@@ -114,10 +113,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 +129,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 +146,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 +161,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 +211,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 +224,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 +243,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 +258,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 +295,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;
@@ -303,11 +305,15 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
GPU_program_point_size(true);
+ /* Do not write to depth (avoid self-occlusion). */
+ bool prev_depth_mask = GPU_depth_mask_get();
+ GPU_depth_mask(false);
+
bGPDstroke *gps_init = (tgpw->gps) ? tgpw->gps : tgpw->t_gpf->strokes.first;
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 +322,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;
}
@@ -340,16 +346,13 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
/* check which stroke-drawer to use */
if (tgpw->dflag & GP_DRAWDATA_ONLY3D) {
const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY);
- int mask_orig = 0;
if (no_xray) {
- glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
- glDepthMask(0);
GPU_depth_test(true);
/* first arg is normally rv3d->dist, but this isn't
* available here and seems to work quite well without */
- bglPolygonOffset(1.0f, 1.0f);
+ GPU_polygon_offset(1.0f, 1.0f);
}
/* 3D Stroke */
@@ -379,21 +382,20 @@ 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) {
- glDepthMask(mask_orig);
GPU_depth_test(false);
- bglPolygonOffset(0.0, 0.0);
+ GPU_polygon_offset(0.0f, 0.0f);
}
}
/* if only one stroke, exit from loop */
@@ -402,13 +404,14 @@ static void gp_draw_strokes(tGPDdraw *tgpw)
}
}
+ GPU_depth_mask(prev_depth_mask);
GPU_program_point_size(false);
}
/* ----- 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..21d755bea52 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -48,14 +48,18 @@
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
+#include "BKE_animsys.h"
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#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 +155,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 +244,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 +270,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 +309,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 +366,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 +397,17 @@ 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,
+ Depsgraph *depsgraph,
+ 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 +433,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 +
@@ -450,8 +455,16 @@ static void gp_stroke_path_animation_add_keyframes(ReportList *reports,
if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
cfra = last_valid_time + MIN_TIME_DELTA;
}
- insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, cfra);
+ insert_keyframe_direct(reports,
+ ptr,
+ prop,
+ fcu,
+ &anim_eval_context,
+ BEZT_KEYTYPE_KEYFRAME,
+ NULL,
+ INSERTKEY_FAST);
last_valid_time = cfra;
}
else if (G.debug & G_DEBUG) {
@@ -463,8 +476,16 @@ static void gp_stroke_path_animation_add_keyframes(ReportList *reports,
if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
cfra = last_valid_time + MIN_TIME_DELTA;
}
- insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
+ insert_keyframe_direct(reports,
+ ptr,
+ prop,
+ fcu,
+ &anim_eval_context,
+ BEZT_KEYTYPE_KEYFRAME,
+ NULL,
+ INSERTKEY_FAST);
last_valid_time = cfra;
}
else {
@@ -472,8 +493,16 @@ static void gp_stroke_path_animation_add_keyframes(ReportList *reports,
* and also far enough from (not yet added!) end_stroke keyframe!
*/
if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) {
- insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_BREAKDOWN, NULL, INSERTKEY_FAST);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, cfra);
+ insert_keyframe_direct(reports,
+ ptr,
+ prop,
+ fcu,
+ &anim_eval_context,
+ BEZT_KEYTYPE_BREAKDOWN,
+ NULL,
+ INSERTKEY_FAST);
last_valid_time = cfra;
}
else if (G.debug & G_DEBUG) {
@@ -486,13 +515,14 @@ 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);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
bAction *act;
FCurve *fcu;
PointerRNA ptr;
@@ -534,8 +564,16 @@ static void gp_stroke_path_animation(bContext *C,
cu->ctime = 0.0f;
cfra = (float)gtd->start_frame;
- insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
+ AnimationEvalContext anim_eval_context_start = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
+ insert_keyframe_direct(reports,
+ ptr,
+ prop,
+ fcu,
+ &anim_eval_context_start,
+ BEZT_KEYTYPE_KEYFRAME,
+ NULL,
+ INSERTKEY_FAST);
cu->ctime = cu->pathlen;
if (gtd->realtime) {
@@ -544,8 +582,16 @@ static void gp_stroke_path_animation(bContext *C,
else {
cfra = (float)gtd->end_frame;
}
- insert_keyframe_direct(
- reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, NULL, INSERTKEY_FAST);
+ AnimationEvalContext anim_eval_context_end = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
+ insert_keyframe_direct(reports,
+ ptr,
+ prop,
+ fcu,
+ &anim_eval_context_end,
+ BEZT_KEYTYPE_KEYFRAME,
+ NULL,
+ INSERTKEY_FAST);
}
else {
/* Use actual recorded timing! */
@@ -557,7 +603,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,8 +617,8 @@ static void gp_stroke_path_animation(bContext *C,
printf("GP Stroke Path Conversion: Starting keying!\n");
}
- gp_stroke_path_animation_add_keyframes(
- reports, ptr, prop, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time);
+ gpencil_stroke_path_animation_add_keyframes(
+ reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time);
BLI_rng_free(rng);
}
@@ -603,16 +649,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 +677,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 +728,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 +756,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 +771,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 +798,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 +807,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 +824,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 +842,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 +875,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 +893,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 +925,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 +973,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 +981,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 +1064,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 +1106,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 +1135,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 +1185,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 +1214,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 +1249,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 +1280,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 +1302,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 +1339,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 +1382,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 +1419,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 +1446,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 +1494,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 +1506,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 +1528,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 +1549,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 +1586,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 +1608,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 +1689,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;
@@ -1659,7 +1707,7 @@ void GPENCIL_OT_convert(wmOperatorType *ot)
0,
0,
32,
- "Bevel_Resolution",
+ "Bevel Resolution",
"Bevel resolution when depth is non-zero",
0,
32);
@@ -1674,7 +1722,7 @@ void GPENCIL_OT_convert(wmOperatorType *ot)
1.0f,
0.0f,
1000.0f,
- "Radius Fac",
+ "Radius Factor",
"Multiplier for the points' radii (set from stroke width)",
0.0f,
10.0f);
@@ -1725,7 +1773,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 +1819,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 +1862,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..348fb614977 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);
@@ -176,13 +176,11 @@ static int gp_data_unlink_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
return OPERATOR_CANCELLED;
}
- else {
- /* just unlink datablock now, decreasing its user count */
- bGPdata *gpd = (*gpd_ptr);
+ /* just unlink datablock now, decreasing its user count */
+ bGPdata *gpd = (*gpd_ptr);
- id_us_min(&gpd->id);
- *gpd_ptr = NULL;
- }
+ id_us_min(&gpd->id);
+ *gpd_ptr = NULL;
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -199,8 +197,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 +207,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 +266,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 +285,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 +343,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 +365,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 +375,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 +416,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 +439,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 +449,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 +489,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 +502,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 +527,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 +620,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 +639,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 +683,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 +698,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 +768,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 +778,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 +844,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;
@@ -863,9 +861,157 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot)
INT_MAX);
}
+/* ********************* Clean Duplicated Frames ************************** */
+static bool gpencil_frame_is_equal(bGPDframe *gpf_a, bGPDframe *gpf_b)
+{
+ if ((gpf_a == NULL) || (gpf_b == NULL)) {
+ return false;
+ }
+ /* If the number of strokes is different, cannot be equal. */
+ int totstrokes_a = BLI_listbase_count(&gpf_a->strokes);
+ int totstrokes_b = BLI_listbase_count(&gpf_b->strokes);
+ if ((totstrokes_a == 0) || (totstrokes_b == 0) || (totstrokes_a != totstrokes_b)) {
+ return false;
+ }
+ /* Loop all strokes and check. */
+ bGPDstroke *gps_a = gpf_a->strokes.first;
+ bGPDstroke *gps_b = gpf_b->strokes.first;
+ for (int i = 0; i < totstrokes_a; i++) {
+ /* If the number of points is different, cannot be equal. */
+ if (gps_a->totpoints != gps_b->totpoints) {
+ return false;
+ }
+ /* Check other variables. */
+ if (!equals_v4v4(gps_a->vert_color_fill, gps_b->vert_color_fill)) {
+ return false;
+ }
+ if (gps_a->thickness != gps_b->thickness) {
+ return false;
+ }
+ if (gps_a->mat_nr != gps_b->mat_nr) {
+ return false;
+ }
+ if (gps_a->caps[0] != gps_b->caps[0]) {
+ return false;
+ }
+ if (gps_a->caps[1] != gps_b->caps[1]) {
+ return false;
+ }
+ if (gps_a->hardeness != gps_b->hardeness) {
+ return false;
+ }
+ if (!equals_v2v2(gps_a->aspect_ratio, gps_b->aspect_ratio)) {
+ return false;
+ }
+ if (gps_a->uv_rotation != gps_b->uv_rotation) {
+ return false;
+ }
+ if (!equals_v2v2(gps_a->uv_translation, gps_b->uv_translation)) {
+ return false;
+ }
+ if (gps_a->uv_scale != gps_b->uv_scale) {
+ return false;
+ }
+
+ /* Loop points and check if equals or not. */
+ for (int p = 0; p < gps_a->totpoints; p++) {
+ bGPDspoint *pt_a = &gps_a->points[p];
+ bGPDspoint *pt_b = &gps_b->points[p];
+ if (!equals_v3v3(&pt_a->x, &pt_b->x)) {
+ return false;
+ }
+ if (pt_a->pressure != pt_b->pressure) {
+ return false;
+ }
+ if (pt_a->strength != pt_b->strength) {
+ return false;
+ }
+ if (pt_a->uv_fac != pt_b->uv_fac) {
+ return false;
+ }
+ if (pt_a->uv_rot != pt_b->uv_rot) {
+ return false;
+ }
+ if (!equals_v4v4(pt_a->vert_color, pt_b->vert_color)) {
+ return false;
+ }
+ }
+
+ /* Look at next pair of strokes. */
+ gps_a = gps_a->next;
+ gps_b = gps_b->next;
+ }
+
+ return true;
+}
+
+static int gpencil_frame_clean_duplicate_exec(bContext *C, wmOperator *op)
+{
+#define SELECTED 1
+
+ bool changed = false;
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const int type = RNA_enum_get(op->ptr, "type");
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* Only editable and visible layers are considered. */
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->frames.first != NULL)) {
+ bGPDframe *gpf = gpl->frames.first;
+
+ if ((type == SELECTED) && ((gpf->flag & GP_FRAME_SELECT) == 0)) {
+ continue;
+ }
+
+ while (gpf != NULL) {
+ if (gpencil_frame_is_equal(gpf, gpf->next)) {
+ /* Remove frame. */
+ BKE_gpencil_layer_frame_delete(gpl, gpf->next);
+ /* Tag for recalc. */
+ changed = true;
+ }
+ else {
+ gpf = gpf->next;
+ }
+ }
+ }
+ }
+
+ /* notifiers */
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_frame_clean_duplicate(wmOperatorType *ot)
+{
+ static const EnumPropertyItem clean_type[] = {
+ {0, "ALL", 0, "All Frames", ""},
+ {1, "SELECTED", 0, "Selected Frames", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Clean Duplicated Frames";
+ ot->idname = "GPENCIL_OT_frame_clean_duplicate";
+ ot->description = "Remove any duplicated frame";
+
+ /* callbacks */
+ ot->exec = gpencil_frame_clean_duplicate_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", clean_type, 0, "Type", "");
+}
+
/* *********************** 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 +1054,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 +1068,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 +1094,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 +1113,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 +1142,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 +1154,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 +1184,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 +1193,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 +1222,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 +1231,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);
@@ -1125,9 +1271,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
if (gpl == layer) {
continue;
}
- else {
- gpl->flag |= flags;
- }
+ gpl->flag |= flags;
}
}
else {
@@ -1153,8 +1297,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 +1313,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 +1386,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 +1395,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 +1409,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 +1450,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 +1462,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 +1496,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 +1517,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 +1647,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 +1659,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 +1743,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 +1754,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 +1818,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 +1829,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 +1887,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 +1926,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 +1991,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 +2047,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 +2074,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 +2094,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 +2640,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 +2678,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 +2865,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 +3000,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 **************************** */
@@ -2913,9 +3057,7 @@ static int gpencil_material_isolate_exec(bContext *C, wmOperator *op)
if (gp_style == active_color) {
continue;
}
- else {
- gp_style->flag |= flags;
- }
+ gp_style->flag |= flags;
DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE);
}
}
@@ -3415,7 +3557,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 +3618,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 +3667,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..29ae38fdbd8 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -94,8 +94,10 @@
/** \name Stroke Edit Mode Management
* \{ */
+static void gpencil_flip_stroke(bGPDstroke *gps);
+
/* 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 +110,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 +122,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 +791,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 +804,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 +882,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 +935,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 +971,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 +985,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 +1055,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 +1100,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 +1117,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];
@@ -1125,6 +1128,11 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
pt->flag |= GP_SPOINT_SELECT;
}
+ /* Flip stroke if it was only one point to consider extrude point as last point. */
+ if (gps->totpoints == 2) {
+ gpencil_flip_stroke(gps);
+ }
+
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
@@ -1138,7 +1146,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 +1202,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 +1224,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 +1245,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 +1262,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 +1275,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 +1294,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 +1312,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 +1327,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 +1338,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 +1397,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 +1409,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 +1422,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 +1446,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 +1459,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 +1472,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 +1480,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,13 +1497,15 @@ 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)) {
+
+ 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");
return OPERATOR_CANCELLED;
}
- else if (gpl == NULL) {
+
+ if (gpl == NULL) {
/* no active layer - let's just create one */
gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
@@ -1509,7 +1519,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 +1545,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 +1617,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 +1637,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 +1743,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 +1761,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 +1819,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 +1839,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 +1848,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 +1858,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 +1901,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 +1915,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 +1925,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 +1935,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);
@@ -1953,10 +1964,8 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
- return OPERATOR_CANCELLED;
- }
+ BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
+ return OPERATOR_CANCELLED;
}
void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
@@ -1969,8 +1978,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 +2007,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);
@@ -2048,15 +2057,13 @@ static int gp_delete_selected_strokes(bContext *C)
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* ----------------------------------- */
/* 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;
@@ -2266,9 +2273,7 @@ static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* ----------------------------------- */
@@ -2282,7 +2287,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 +2389,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 +2524,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 +2537,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 +2571,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;
}
@@ -2579,15 +2586,13 @@ static int gp_delete_selected_points(bContext *C)
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* 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 +2601,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 +2643,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 +2664,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 +2691,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 +2716,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 +2725,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 +2771,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 +2792,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 +2805,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 +2859,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 +2881,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 +2901,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 +2961,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 +2986,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 +2999,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 +3040,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 +3056,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 +3155,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 +3181,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 +3258,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 +3430,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 +3557,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 +3578,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 +3628,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 +3641,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 +3655,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 +3726,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 +3744,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 +3773,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 +3787,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 +3834,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 +3852,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 +3871,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 +3971,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 +3992,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 +4019,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 +4055,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 +4068,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 +4105,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 +4119,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 +4154,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 +4172,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 +4229,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 +4251,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,13 +4285,16 @@ 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;
ob_dst->mode = OB_MODE_OBJECT;
- /* create new grease pencil datablock */
+ /* Duplication will increment bGPdata usercount, but since we create a new greasepencil datablock
+ * for ob_dst (which gets its own user automatically), we have to decrement the usercount again.
+ */
gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2);
+ id_us_min(ob_dst->data);
ob_dst->data = (bGPdata *)gpd_dst;
/* loop old datablock and separate parts */
@@ -4347,10 +4358,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 +4454,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 +4470,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 +4518,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 +4559,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 +4572,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 +4581,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 +4600,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 +4644,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 +4701,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 +4729,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 +4891,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 +4907,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 +4944,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..d7525c3c6a5 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];
@@ -352,8 +352,7 @@ static bool gp_render_offscreen(tGPDfill *tgpf)
round_v2i_v2fl(tgpf->center, center);
char err_out[256] = "unknown";
- GPUOffScreen *offscreen = GPU_offscreen_create(
- tgpf->sizex, tgpf->sizey, 0, true, false, err_out);
+ GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, true, false, err_out);
if (offscreen == NULL) {
printf("GPencil - Fill - Unable to create fill buffer\n");
return false;
@@ -399,8 +398,8 @@ static bool gp_render_offscreen(tGPDfill *tgpf)
GPU_matrix_push();
GPU_matrix_identity_set();
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
+ GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT);
ED_view3d_update_viewmat(
tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->region, NULL, winmat, NULL, true);
@@ -410,17 +409,17 @@ 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();
/* create a image to see result of template */
if (ibuf->rect_float) {
- GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float);
+ GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, ibuf->rect_float);
}
else if (ibuf->rect) {
- GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect);
+ GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, ibuf->rect);
}
if (ibuf->rect_float && ibuf->rect) {
IMB_rect_from_float(ibuf);
@@ -472,15 +471,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 +576,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 +666,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 +1082,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 +1128,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 +1168,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 */
@@ -1254,19 +1199,16 @@ static bool gpencil_fill_poll(bContext *C)
return true;
}
- else {
- CTX_wm_operator_poll_msg_set(C, "Active region not valid for filling operator");
- return false;
- }
- }
- else {
- CTX_wm_operator_poll_msg_set(C, "Active region not set");
+ CTX_wm_operator_poll_msg_set(C, "Active region not valid for filling operator");
return false;
}
+
+ CTX_wm_operator_poll_msg_set(C, "Active region not set");
+ return false;
}
/* 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 +1341,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);
@@ -1443,9 +1385,8 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
}
return OPERATOR_CANCELLED;
}
- else {
- tgpf = op->customdata;
- }
+
+ tgpf = op->customdata;
/* Enable custom drawing handlers to show help lines */
if (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) {
@@ -1499,7 +1440,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..68424839ab0 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 -------------------------------------- */
@@ -483,8 +485,11 @@ void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot);
void GPENCIL_OT_frame_duplicate(struct wmOperatorType *ot);
void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot);
void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot);
+void GPENCIL_OT_frame_clean_duplicate(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..3a7029b1288 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,19 +136,23 @@ 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(bGPDlayer *gpl)
{
- BLI_assert(gpf != NULL);
+ if (gpl == NULL) {
+ return;
+ }
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_TAG) {
- gps->flag &= ~GP_STROKE_TAG;
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_TAG) {
+ gps->flag &= ~GP_STROKE_TAG;
+ }
}
}
}
/* 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 +162,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 +180,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 +197,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 +247,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);
@@ -261,7 +265,6 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
/* set layers */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
tGPDinterpolate_layer *tgpil;
-
/* all layers or only active */
if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) {
continue;
@@ -278,10 +281,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 +336,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 +412,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 +432,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 +453,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 +474,23 @@ 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);
+
+ /* Untag strokes to be sure nothing is pending due any canceled process. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpi->gpd->layers) {
+ gpencil_interpolate_untag_strokes(gpl);
+ }
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 +502,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 +544,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;
}
@@ -552,9 +556,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
}
return OPERATOR_CANCELLED;
}
- else {
- tgpi = op->customdata;
- }
+ tgpi = op->customdata;
/* set cursor to indicate modal */
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
@@ -602,6 +604,8 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
/* make copy of source stroke, then adjust pointer to points too */
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
+ gps_dst->flag &= ~GP_STROKE_TAG;
+
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps_dst);
@@ -681,10 +685,8 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
break;
}
- else {
- /* unhandled event - allow to pass through */
- return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
- }
+ /* unhandled event - allow to pass through */
+ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
@@ -731,7 +733,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 +1010,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 +1066,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..53dbc1620d0 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -73,7 +73,8 @@ static int gpencil_sort_points(const void *a1, const void *a2)
if (ps1->factor < ps2->factor) {
return -1;
}
- else if (ps1->factor > ps2->factor) {
+
+ if (ps1->factor > ps2->factor) {
return 1;
}
@@ -132,7 +133,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 +179,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 +432,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 +463,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 +547,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 +564,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 +575,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 +643,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..11f42f7d3ac
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -0,0 +1,409 @@
+/*
+ * 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 GpBakeOb *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 bool gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+ bool simple_material = false;
+
+ /* 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);
+ simple_material |= true;
+ }
+
+ /* 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;
+
+ return simple_material;
+}
+
+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};
+ const bool simple_material = 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,
+ simple_material);
+
+ /* 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..3892f451e18 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);
@@ -184,17 +184,16 @@ static bool gp_stroke_sculptmode_poll(bContext *C)
if (area->spacetype != SPACE_VIEW3D) {
return ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
}
- else {
- if ((ob) && (ob->type == OB_GPENCIL)) {
- return GPENCIL_SCULPT_MODE(gpd);
- }
+
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ return GPENCIL_SCULPT_MODE(gpd);
}
return 0;
}
/* 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 +206,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 +219,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 +312,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 +348,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 +356,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 +364,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;
}
/* ==================== */
@@ -601,8 +600,10 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_frame_duplicate);
WM_operatortype_append(GPENCIL_OT_frame_clean_fill);
WM_operatortype_append(GPENCIL_OT_frame_clean_loose);
+ WM_operatortype_append(GPENCIL_OT_frame_clean_duplicate);
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..b61baf3d0b1 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -268,11 +268,11 @@ typedef struct tGPsdata {
/* Macros for accessing sensitivity thresholds... */
/* minimum number of pixels mouse should move before new point created */
-#define MIN_MANHATTEN_PX (U.gp_manhattendist)
+#define MIN_MANHATTEN_PX (U.gp_manhattandist)
/* 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... */
@@ -332,10 +332,9 @@ static bool gpencil_draw_poll(bContext *C)
return true;
}
- else {
- CTX_wm_operator_poll_msg_set(C, "Active region not set");
- return false;
- }
+
+ CTX_wm_operator_poll_msg_set(C, "Active region not set");
+ return false;
}
/* check if projecting strokes into 3d-geometry in the 3D-View */
@@ -352,7 +351,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 +362,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]);
@@ -375,42 +374,37 @@ static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float mvalo[2
return true;
}
/* if lazy mouse, check minimum distance */
- else if (GPENCIL_LAZY_MODE(brush, p->shift)) {
+ if (GPENCIL_LAZY_MODE(brush, p->shift)) {
brush->gpencil_settings->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP;
if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) {
return true;
}
- else {
- /* If the mouse is moving within the radius of the last move,
- * don't update the mouse position. This allows sharp turns. */
- copy_v2_v2(p->mval, p->mvalo);
- return false;
- }
+
+ /* If the mouse is moving within the radius of the last move,
+ * don't update the mouse position. This allows sharp turns. */
+ copy_v2_v2(p->mval, p->mvalo);
+ return false;
}
/* check if mouse moved at least certain distance on both axes (best case)
* - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
*/
- else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) {
+ if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) {
return true;
-
- /* Check if the distance since the last point is significant enough:
- * - Prevents points being added too densely
- * - Distance here doesn't use sqrt to prevent slowness.
- * We should still be safe from overflows though.
- */
}
- else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) {
+ /* Check if the distance since the last point is significant enough:
+ * - Prevents points being added too densely
+ * - Distance here doesn't use sqrt to prevent slowness.
+ * We should still be safe from overflows though.
+ */
+ if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) {
return true;
-
- /* mouse 'didn't move' */
- }
- else {
- return false;
}
+ /* mouse 'didn't move' */
+ return false;
}
/* 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 +424,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 +472,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 +488,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 +509,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 +547,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 +621,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 +688,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 +761,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;
@@ -810,7 +810,8 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
/* can keep carrying on this way :) */
return GP_STROKEADD_NORMAL;
}
- else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
+
+ if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
/* check if still room in buffer or add more */
gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure(
gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
@@ -860,16 +861,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 +883,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 +905,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 +921,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;
@@ -949,10 +950,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
if (ptc->pressure > 0.001f) {
break;
}
- else {
- gpd->runtime.sbuffer_used = last_i - 1;
- CLAMP_MIN(gpd->runtime.sbuffer_used, 1);
- }
+ gpd->runtime.sbuffer_used = last_i - 1;
+ CLAMP_MIN(gpd->runtime.sbuffer_used, 1);
last_i--;
}
@@ -1000,7 +999,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 +1017,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 +1051,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 +1076,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 +1171,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 +1193,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 +1228,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,27 +1283,27 @@ 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) {
return ED_view3d_calc_zfac(rv3d, co, NULL);
}
- else {
- return -dot_v3v3(rv3d->viewinv[2], co);
- }
+ return -dot_v3v3(rv3d->viewinv[2], co);
}
/* 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 +1345,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 +1372,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 +1387,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 +1395,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 +1430,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 +1448,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 +1478,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 +1530,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 +1552,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 +1569,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 +1603,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 +1630,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;
@@ -1688,7 +1688,7 @@ static void gp_stroke_doeraser(tGPsdata *p)
if (BKE_gpencil_layer_is_editable(gpl) == false) {
continue;
}
- else if (gpf == NULL) {
+ if (gpf == NULL) {
continue;
}
/* calculate difference matrix */
@@ -1710,7 +1710,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 +1720,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 +1742,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;
@@ -1768,23 +1768,22 @@ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts)
return brush_dft;
}
/* create a new soft eraser brush */
- else {
- brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL);
- brush_dft->size = 30.0f;
- brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
- brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
- brush_dft->gpencil_tool = GPAINT_TOOL_ERASE;
- brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT;
- /* reset current brush */
- BKE_paint_brush_set(paint, brush_prev);
+ brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL);
+ brush_dft->size = 30.0f;
+ brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
+ brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
+ brush_dft->gpencil_tool = GPAINT_TOOL_ERASE;
+ brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT;
- return brush_dft;
- }
+ /* reset current brush */
+ BKE_paint_brush_set(paint, brush_prev);
+
+ return brush_dft;
}
/* 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 +1802,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 +1830,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 +1848,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 +1861,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;
@@ -1950,24 +1949,23 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
}
return 0;
}
- else {
- /* if no existing GPencil block exists, add one */
- if (*gpd_ptr == NULL) {
- *gpd_ptr = BKE_gpencil_data_addnew(bmain, "GPencil");
- }
- p->gpd = *gpd_ptr;
+
+ /* if no existing GPencil block exists, add one */
+ if (*gpd_ptr == NULL) {
+ *gpd_ptr = BKE_gpencil_data_addnew(bmain, "GPencil");
}
+ p->gpd = *gpd_ptr;
/* 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 +1985,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 +1995,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 +2014,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 +2036,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 +2046,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;
@@ -2141,9 +2141,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
}
return;
}
- else {
- p->gpf->flag |= GP_FRAME_PAINT;
- }
+ p->gpf->flag |= GP_FRAME_PAINT;
}
/* set 'eraser' for this stroke if using eraser */
@@ -2209,12 +2207,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 +2230,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 +2355,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 +2397,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 +2405,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 +2491,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,40 +2504,37 @@ 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;
v -= offset;
v = roundf((v + spacing * 0.5f) / spacing) * spacing;
v += offset;
- return v;
- }
- else {
- return v;
}
+ return v;
}
/* 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 +2544,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 +2558,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 +2569,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 +2578,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 +2590,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 +2600,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 +2628,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 +2644,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 +2670,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 +2678,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 +2698,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 +2885,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);
}
}
@@ -2945,9 +2941,8 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(op->customdata);
return OPERATOR_CANCELLED;
}
- else {
- p = op->customdata;
- }
+
+ p = op->customdata;
/* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
* setting the relevant values in context at each step, then applying
@@ -2968,8 +2963,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);
}
}
@@ -3149,9 +3144,9 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
}
return OPERATOR_CANCELLED;
}
- else {
- p = op->customdata;
- }
+
+ p = op->customdata;
+
/* Init random settings. */
ED_gpencil_init_random_settings(p->brush, event->mval, &p->random_settings);
@@ -3227,8 +3222,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 +3235,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 +3279,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 +3354,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 +3406,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 +3542,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 +3745,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..fb0d94f2307 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;
@@ -1019,7 +1019,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
pt->time = 0.0f;
pt->flag = 0;
pt->uv_fac = tpt->uv_fac;
- copy_v4_v4(pt->vert_color, tpt->vert_color);
+ ED_gpencil_point_vertex_color_set(ts, brush, pt, tpt);
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[i];
@@ -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;
}
@@ -1592,7 +1592,8 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
copy_v2_v2(tgpi->mvalo, tgpi->mval);
return OPERATOR_RUNNING_MODAL;
}
- else if (tgpi->flag == IN_POLYLINE) {
+
+ if (tgpi->flag == IN_POLYLINE) {
switch (event->type) {
@@ -1625,7 +1626,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;
}
@@ -1682,7 +1683,8 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
copy_v2_v2(tgpi->mvalo, tgpi->mval);
return OPERATOR_RUNNING_MODAL;
}
- else if (tgpi->flag == IN_BRUSH_SIZE) {
+
+ if (tgpi->flag == IN_BRUSH_SIZE) {
switch (event->type) {
case MOUSEMOVE:
gpencil_primitive_size(tgpi, false);
@@ -1705,7 +1707,8 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
copy_v2_v2(tgpi->mvalo, tgpi->mval);
return OPERATOR_RUNNING_MODAL;
}
- else if (tgpi->flag == IN_BRUSH_STRENGTH) {
+
+ if (tgpi->flag == IN_BRUSH_STRENGTH) {
switch (event->type) {
case MOUSEMOVE:
gpencil_primitive_strength(tgpi, false);
@@ -1728,7 +1731,8 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
copy_v2_v2(tgpi->mvalo, tgpi->mval);
return OPERATOR_RUNNING_MODAL;
}
- else if (!ELEM(tgpi->flag, IDLE) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) {
+
+ if (!ELEM(tgpi->flag, IDLE) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) {
gpencil_primitive_edit_event_handling(C, op, win, event, tgpi);
}
@@ -1749,7 +1753,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 +1796,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 +1875,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 +1884,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 +1912,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);
}
@@ -1932,10 +1936,9 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
break;
}
- else {
- /* unhandled event - allow to pass through */
- return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
- }
+
+ /* unhandled event - allow to pass through */
+ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index f7f3b128351..20eeab65623 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;
@@ -1227,10 +1229,8 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
op->customdata = NULL;
return false;
}
- else {
- /* initialise customdata */
- gp_brush_clone_init(C, gso);
- }
+ /* initialise customdata */
+ gpencil_brush_clone_init(C, gso);
break;
}
@@ -1246,15 +1246,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 +1267,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 +1305,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 +1318,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 +1360,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 +1402,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 +1447,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 +1459,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 +1483,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 +1496,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;
@@ -1506,9 +1506,14 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso,
continue;
}
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ /* If masked and the point is not selected, skip it. */
+ if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) &&
+ ((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
+ continue;
+ }
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);
}
@@ -1525,7 +1530,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;
}
@@ -1546,7 +1551,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;
}
@@ -1559,11 +1564,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;
@@ -1583,20 +1588,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;
}
@@ -1609,13 +1616,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;
}
}
@@ -1625,28 +1632,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;
}
@@ -1680,7 +1688,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;
@@ -1698,7 +1706,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;
}
@@ -1706,7 +1714,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;
}
@@ -1714,7 +1722,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;
}
@@ -1759,7 +1767,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);
}
}
}
@@ -1767,7 +1775,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);
}
}
}
@@ -1776,7 +1784,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;
@@ -1816,10 +1824,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 */
@@ -1836,7 +1844,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);
@@ -1845,7 +1853,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;
@@ -1873,7 +1881,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;
}
@@ -1885,28 +1893,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");
@@ -1922,7 +1930,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;
}
@@ -1969,11 +1977,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);
@@ -1983,7 +1991,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");
@@ -1998,7 +2006,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;
@@ -2008,7 +2016,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;
@@ -2024,7 +2032,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;
@@ -2033,7 +2041,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;
}
}
@@ -2048,14 +2056,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 */
@@ -2119,11 +2127,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..dcba8f16ee9 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -74,15 +74,13 @@ static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode)
if (mode & GP_SCULPT_MASK_SELECTMODE_POINT) {
return GP_SELECTMODE_POINT;
}
- else if (mode & GP_SCULPT_MASK_SELECTMODE_STROKE) {
+ if (mode & GP_SCULPT_MASK_SELECTMODE_STROKE) {
return GP_SELECTMODE_STROKE;
}
- else if (mode & GP_SCULPT_MASK_SELECTMODE_SEGMENT) {
+ if (mode & GP_SCULPT_MASK_SELECTMODE_SEGMENT) {
return GP_SELECTMODE_SEGMENT;
}
- else {
- return GP_SELECTMODE_POINT;
- }
+ return GP_SELECTMODE_POINT;
}
/* Convert vertex mask mode to Select mode */
@@ -91,15 +89,13 @@ static int gpencil_select_mode_from_vertex(eGP_Sculpt_SelectMaskFlag mode)
if (mode & GP_VERTEX_MASK_SELECTMODE_POINT) {
return GP_SELECTMODE_POINT;
}
- else if (mode & GP_VERTEX_MASK_SELECTMODE_STROKE) {
+ if (mode & GP_VERTEX_MASK_SELECTMODE_STROKE) {
return GP_SELECTMODE_STROKE;
}
- else if (mode & GP_VERTEX_MASK_SELECTMODE_SEGMENT) {
+ if (mode & GP_VERTEX_MASK_SELECTMODE_SEGMENT) {
return GP_SELECTMODE_SEGMENT;
}
- else {
- return GP_SELECTMODE_POINT;
- }
+ return GP_SELECTMODE_POINT;
}
static bool gpencil_select_poll(bContext *C)
@@ -369,7 +365,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 +408,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 +458,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 +849,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 +984,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 +994,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 +1098,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 +1196,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 +1233,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 +1290,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 +1372,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 +1413,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 +1424,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 +1442,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..d412c13aea2 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)) {
@@ -117,11 +117,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ScrArea *area, Object *ob, Pointer
}
return (bGPdata **)&ob->data;
}
- else {
- return NULL;
- }
-
- break;
+ return NULL;
}
default: /* Unsupported space. */
return NULL;
@@ -132,7 +128,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 +150,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) {
@@ -220,12 +216,10 @@ bGPdata **ED_annotation_data_get_pointers_direct(ID *screen_id,
}
return &track->gpd;
}
- else {
- if (r_ptr) {
- RNA_id_pointer_create(&clip->id, r_ptr);
- }
- return &clip->gpd;
+ if (r_ptr) {
+ RNA_id_pointer_create(&clip->id, r_ptr);
}
+ return &clip->gpd;
}
break;
}
@@ -237,8 +231,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 +243,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 +257,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 +272,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 +283,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 +294,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 +321,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));
}
@@ -340,10 +338,8 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra)
// XXX: assumes that frame has been fetched already
return (gpl->actframe->framenum == cfra);
}
- else {
- /* XXX: disabled as could be too much of a penalty */
- /* return BKE_gpencil_layer_frame_find(gpl, cfra); */
- }
+ /* XXX: disabled as could be too much of a penalty */
+ /* return BKE_gpencil_layer_frame_find(gpl, cfra); */
}
}
@@ -354,7 +350,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 +362,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,16 +375,14 @@ 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;
if (paint) {
return (paint->brush != NULL);
}
- else {
- return false;
- }
+ return false;
}
/* ******************************************************** */
@@ -527,13 +521,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};
@@ -564,18 +557,16 @@ bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps
/* 3D strokes - only in 3D view */
return ((area->spacetype == SPACE_VIEW3D) || (area->spacetype == SPACE_PROPERTIES));
}
- else if (gps->flag & GP_STROKE_2DIMAGE) {
+ if (gps->flag & GP_STROKE_2DIMAGE) {
/* Special "image" strokes - only in Image Editor */
return (area->spacetype == SPACE_IMAGE);
}
- else if (gps->flag & GP_STROKE_2DSPACE) {
+ if (gps->flag & GP_STROKE_2DSPACE) {
/* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */
return (area->spacetype != SPACE_VIEW3D);
}
- else {
- /* view aligned - anything goes */
- return true;
- }
+ /* view aligned - anything goes */
+ return true;
}
/* Check whether given stroke can be edited in the current context */
@@ -611,7 +602,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 +649,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 +662,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 +685,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 +711,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 +756,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 +824,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 +895,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];
@@ -924,27 +920,24 @@ bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc,
return true;
}
- else {
- zero_v3(r_out);
-
- return false;
- }
+ zero_v3(r_out);
+ return false;
}
/**
* 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 +975,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 +1017,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 +1027,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 +1041,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 +1158,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 +1192,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 +1231,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 +1244,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 +1323,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 +1723,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);
@@ -1751,12 +1744,10 @@ static bool gp_check_cursor_region(bContext *C, int mval_i[2])
if ((region) && (region->regiontype != RGN_TYPE_WINDOW)) {
return false;
}
- else if (region) {
+ if (region) {
return BLI_rcti_isect_pt_v(&region->winrct, mval_i);
}
- else {
- return false;
- }
+ return false;
}
/* draw eraser cursor */
@@ -1770,7 +1761,8 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
GPU_line_smooth(true);
GPU_blend(true);
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ GPU_blend_set_func_separate(
+ GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
immUniformColor4ub(255, 100, 100, 20);
imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40);
@@ -1780,7 +1772,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
- glGetFloatv(GL_VIEWPORT, viewport_size);
+ GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f);
@@ -1802,7 +1794,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 +1802,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 +1824,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 +2010,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 +2075,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 +2103,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 +2123,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 +2212,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,8 +2245,8 @@ static void gp_copy_points(bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final
}
}
-static void gp_insert_point(
- bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], float co_b[3])
+static void gpencil_insert_point(
+ bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], const float co_b[3])
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@@ -2287,16 +2288,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 +2305,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 +2436,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 +2469,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 +2483,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 */
@@ -2512,15 +2513,13 @@ int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
if ((hit_a) && (hit_b)) {
return 3;
}
- else if (hit_a) {
+ if (hit_a) {
return 1;
}
- else if (hit_b) {
+ if (hit_b) {
return 2;
}
- else {
- return 0;
- }
+ return 0;
}
void ED_gpencil_select_toggle_all(bContext *C, int action)
@@ -2610,7 +2609,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 +2663,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) {
@@ -2684,7 +2688,11 @@ void ED_gpencil_tag_scene_gpencil(Scene *scene)
void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke *gps)
{
- if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) {
+ const bool is_vertex = (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) ||
+ (!GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR));
+ if (is_vertex) {
copy_v3_v3(gps->vert_color_fill, brush->rgb);
gps->vert_color_fill[3] = brush->gpencil_settings->vertex_factor;
srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill);
@@ -2699,7 +2707,12 @@ void ED_gpencil_point_vertex_color_set(ToolSettings *ts,
bGPDspoint *pt,
tGPspoint *tpt)
{
- if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) {
+ const bool is_vertex = (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) ||
+ (!GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR));
+
+ if (is_vertex) {
if (tpt == NULL) {
copy_v3_v3(pt->vert_color, brush->rgb);
pt->vert_color[3] = brush->gpencil_settings->vertex_factor;
@@ -2748,7 +2761,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;
@@ -2761,7 +2774,7 @@ void ED_gpencil_init_random_settings(Brush *brush,
}
static void gpencil_sbuffer_vertex_color_random(
- bGPdata *gpd, Brush *brush, tGPspoint *tpt, float random_color[3], float pen_pressure)
+ bGPdata *gpd, Brush *brush, tGPspoint *tpt, const float random_color[3], float pen_pressure)
{
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) {
@@ -2855,6 +2868,18 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
bGPdata *gpd_eval = (bGPdata *)ob_eval->data;
MaterialGPencilStyle *gp_style = material->gp_style;
+ const bool is_vertex_fill =
+ (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) ||
+ (!GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR));
+
+ const bool is_vertex_stroke =
+ (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) ||
+ (!GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) &&
+ (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR));
+
int idx = gpd->runtime.sbuffer_used;
tGPspoint *tpt = (tGPspoint *)gpd->runtime.sbuffer + idx;
@@ -2864,14 +2889,14 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
srgb_to_linearrgb_v4(vertex_color, vertex_color);
/* Copy fill vertex color. */
- if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) {
+ if (is_vertex_fill) {
copy_v4_v4(gpd->runtime.vert_color_fill, vertex_color);
}
else {
copy_v4_v4(gpd->runtime.vert_color_fill, gp_style->fill_rgba);
}
/* Copy stroke vertex color. */
- if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) {
+ if (is_vertex_stroke) {
copy_v4_v4(tpt->vert_color, vertex_color);
}
else {
@@ -2881,8 +2906,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;
@@ -2892,7 +2917,7 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
/* Check if the stroke collides with brush. */
bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
bGPDstroke *gps,
- float mouse[2],
+ const float mouse[2],
const int radius,
const float diff_mat[4][4])
{
@@ -2908,12 +2933,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 +2950,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 +2958,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 +2985,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 581a5d977c2..c36bc4388d7 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, const 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];
@@ -408,8 +408,6 @@ static void gp_grid_colors_calc(tGP_BrushVertexpaintData *gso)
round_v2i_v2fl(gso->grid_sample, gso->mval);
gso->grid_ready = true;
-
- return;
}
/* ************************************************ */
@@ -639,7 +637,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 +655,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 +698,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 +710,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 +763,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 +789,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 +820,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 +849,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 +861,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);
}
}
}
@@ -880,7 +878,7 @@ static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* Skip if neither one is selected
* (and we are only allowed to edit/consider selected points) */
- if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) && (GPENCIL_VERTEX_MODE(gso->gpd))) {
+ if (GPENCIL_ANY_VERTEX_MASK(gso->mask)) {
if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) {
include_last = false;
continue;
@@ -888,11 +886,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,15 +899,20 @@ 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];
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) {
+ /* If masked and the point is not selected, skip it. */
+ if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) &&
+ ((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
+ 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,
@@ -926,7 +929,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;
}
}
@@ -945,7 +948,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;
}
@@ -965,7 +968,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);
}
}
}
@@ -974,11 +977,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 :
@@ -1005,7 +1008,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
@@ -1087,7 +1090,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);
@@ -1133,7 +1136,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);
}
}
}
@@ -1141,7 +1144,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);
}
}
}
@@ -1150,7 +1153,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;
@@ -1191,9 +1194,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) {
@@ -1211,7 +1214,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;
@@ -1233,28 +1238,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");
@@ -1268,7 +1273,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;
}
@@ -1283,7 +1288,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);
@@ -1293,7 +1298,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");
@@ -1308,7 +1313,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;
@@ -1324,7 +1329,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;
@@ -1333,7 +1338,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;
}
}
@@ -1348,13 +1353,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 */
@@ -1416,11 +1421,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/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h
index 04f9edeaf3a..cfd92bf3113 100644
--- a/source/blender/editors/include/BIF_glutil.h
+++ b/source/blender/editors/include/BIF_glutil.h
@@ -24,6 +24,8 @@
#ifndef __BIF_GLUTIL_H__
#define __BIF_GLUTIL_H__
+#include "GPU_texture.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -66,9 +68,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float xzoom,
float yzoom,
@@ -78,9 +79,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float clip_min_x,
float clip_min_y,
@@ -94,9 +94,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float scaleX,
float scaleY,
@@ -108,9 +107,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float scaleX,
float scaleY,
@@ -133,7 +131,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
void ED_draw_imbuf(struct ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
struct ColorManagedViewSettings *view_settings,
struct ColorManagedDisplaySettings *display_settings,
float zoom_x,
@@ -141,7 +139,7 @@ void ED_draw_imbuf(struct ImBuf *ibuf,
void ED_draw_imbuf_clipping(struct ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
struct ColorManagedViewSettings *view_settings,
struct ColorManagedDisplaySettings *display_settings,
float clip_min_x,
@@ -155,14 +153,14 @@ void ED_draw_imbuf_ctx(const struct bContext *C,
struct ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
float zoom_x,
float zoom_y);
void ED_draw_imbuf_ctx_clipping(const struct bContext *C,
struct ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
float clip_min_x,
float clip_min_y,
float clip_max_x,
@@ -172,13 +170,6 @@ void ED_draw_imbuf_ctx_clipping(const struct bContext *C,
int ED_draw_imbuf_method(struct ImBuf *ibuf);
-/* OpenGL drawing utility functions. Do not use these in new code, these
- * are intended to be moved or removed in the future. */
-
-/* own working polygon offset */
-float bglPolygonOffsetCalc(const float winmat[16], float viewdist, float dist);
-void bglPolygonOffset(float viewdist, float dist);
-
void immDrawBorderCorners(unsigned int pos, const struct rcti *border, float zoomx, float zoomy);
#ifdef __cplusplus
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 426a470b128..9d9f2925c23 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -95,6 +95,8 @@ typedef struct bAnimContext {
struct Scene *scene;
/** active scene layer */
struct ViewLayer *view_layer;
+ /** active dependency graph */
+ struct Depsgraph *depsgraph;
/** active object */
struct Object *obact;
/** active set of markers */
@@ -281,10 +283,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 +302,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 +315,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 +328,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 +647,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);
@@ -703,7 +710,7 @@ void getcolor_fcurve_rainbow(int cur, int tot, float out[3]);
/* ----------------- NLA Drawing ----------------------- */
/* NOTE: Technically, this is not in the animation module (it's in space_nla)
- * but these are sometimes needed by various animation apis.
+ * but these are sometimes needed by various animation API's.
*/
/* Get color to use for NLA Action channel's background */
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 541e2633512..d82c7126cf2 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -34,7 +34,6 @@ struct Depsgraph;
struct IDProperty;
struct ListBase;
struct Main;
-struct Main;
struct Mesh;
struct MeshDeformModifierData;
struct Object;
@@ -174,7 +173,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_buttons.h b/source/blender/editors/include/ED_buttons.h
index 2eaef5e82e0..455eee8580d 100644
--- a/source/blender/editors/include/ED_buttons.h
+++ b/source/blender/editors/include/ED_buttons.h
@@ -27,6 +27,10 @@
extern "C" {
#endif
+struct SpaceProperties;
+
+int ED_buttons_tabs_list(struct SpaceProperties *sbuts, int *context_tabs_array);
+
#ifdef __cplusplus
}
#endif
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_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index a523e924e54..8a239559627 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -106,7 +106,7 @@ struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile);
short ED_fileselect_set_params(struct SpaceFile *sfile);
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,
- int temp_win_size[],
+ const int temp_win_size[],
const bool is_maximized);
void ED_fileselect_reset_params(struct SpaceFile *sfile);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 2a1d9c2dd6c..64276706759 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,
@@ -339,7 +351,7 @@ void ED_gpencil_init_random_settings(struct Brush *brush,
bool ED_gpencil_stroke_check_collision(struct GP_SpaceConversion *gsc,
struct bGPDstroke *gps,
- float mouse[2],
+ const float mouse[2],
const int radius,
const float diff_mat[4][4]);
bool ED_gpencil_stroke_point_is_inside(struct bGPDstroke *gps,
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index a8476e3d1ca..e8343fbbc19 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -125,8 +125,8 @@ void ED_image_draw_info(struct Scene *scene,
const unsigned char cp[4],
const float fp[4],
const float linearcol[4],
- int *zp,
- float *zpf);
+ const int *zp,
+ const float *zpf);
bool ED_space_image_show_cache(struct SpaceImage *sima);
diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h
index 1146c49bef2..e97fd424742 100644
--- a/source/blender/editors/include/ED_info.h
+++ b/source/blender/editors/include/ED_info.h
@@ -31,9 +31,11 @@ struct Main;
/* info_stats.c */
void ED_info_stats_clear(struct ViewLayer *view_layer);
-const char *ED_info_footer_string(struct ViewLayer *view_layer);
+const char *ED_info_statusbar_string(struct Main *bmain,
+ struct bScreen *screen,
+ struct bContext *C);
void ED_info_draw_stats(
- Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height);
+ struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 5635ef2800a..3fe6407aae7 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -38,6 +38,7 @@ struct Scene;
struct KeyingSet;
+struct AnimationEvalContext;
struct BezTriple;
struct FCurve;
struct bAction;
@@ -118,7 +119,7 @@ bool insert_keyframe_direct(struct ReportList *reports,
struct PointerRNA ptr,
struct PropertyRNA *prop,
struct FCurve *fcu,
- float cfra,
+ const struct AnimationEvalContext *anim_eval_context,
eBezTriple_KeyframeType keytype,
struct NlaKeyframingContext *nla,
eInsertKeyFlags flag);
@@ -136,7 +137,7 @@ int insert_keyframe(struct Main *bmain,
const char group[],
const char rna_path[],
int array_index,
- float cfra,
+ const struct AnimationEvalContext *anim_eval_context,
eBezTriple_KeyframeType keytype,
struct ListBase *nla_cache,
eInsertKeyFlags flag);
@@ -458,7 +459,7 @@ bool fcurve_frame_has_keyframe(struct FCurve *fcu, float frame, short filter);
bool fcurve_is_changed(struct PointerRNA ptr,
struct PropertyRNA *prop,
struct FCurve *fcu,
- float frame);
+ const struct AnimationEvalContext *anim_eval_context);
/**
* Main Keyframe Checking API call:
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..392a5075750 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -47,8 +47,6 @@ struct ReportList;
struct Scene;
struct UndoType;
struct UvMapVert;
-struct UvMapVert;
-struct UvVertMap;
struct UvVertMap;
struct View3D;
struct ViewContext;
@@ -125,7 +123,6 @@ struct BMFace *EDBM_uv_active_face_get(struct BMEditMesh *em,
void BM_uv_vert_map_free(struct UvVertMap *vmap);
struct UvMapVert *BM_uv_vert_map_at_index(struct UvVertMap *vmap, unsigned int v);
struct UvVertMap *BM_uv_vert_map_create(struct BMesh *bm,
- const float limit[2],
const bool use_select,
const bool use_winding);
@@ -426,6 +423,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 +451,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_node.h b/source/blender/editors/include/ED_node.h
index 3471f9dcce9..382902cd2de 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -31,7 +31,6 @@ extern "C" {
struct ID;
struct Main;
struct Scene;
-struct Scene;
struct ScrArea;
struct Tex;
struct View2D;
@@ -40,7 +39,6 @@ struct bNode;
struct bNodeSocket;
struct bNodeSocketType;
struct bNodeTree;
-struct bNodeTree;
struct bNodeTreeType;
struct bNodeType;
@@ -123,7 +121,7 @@ void ED_operatormacros_node(void);
bool ED_space_node_color_sample(struct Main *bmain,
struct SpaceNode *snode,
struct ARegion *region,
- int mval[2],
+ const int mval[2],
float r_col[3]);
#ifdef __cplusplus
diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h
index 8c8f3e6f4a3..16d05a7793a 100644
--- a/source/blender/editors/include/ED_numinput.h
+++ b/source/blender/editors/include/ED_numinput.h
@@ -103,8 +103,12 @@ bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event
#define NUM_MODAL_INCREMENT_UP 18
#define NUM_MODAL_INCREMENT_DOWN 19
-bool user_string_to_number(
- bContext *C, const char *str, const struct UnitSettings *unit, int type, double *r_value);
+bool user_string_to_number(bContext *C,
+ const char *str,
+ const struct UnitSettings *unit,
+ int type,
+ const char *error_prefix,
+ double *r_value);
/** \} */
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 5c33513f0a4..e08be5937fe 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -34,7 +34,6 @@ extern "C" {
struct Base;
struct Depsgraph;
struct EnumPropertyItem;
-struct EnumPropertyItem;
struct ID;
struct Main;
struct ModifierData;
@@ -59,9 +58,9 @@ struct wmWindowManager;
/* object_edit.c */
/* context.object */
-struct Object *ED_object_context(struct bContext *C);
+struct Object *ED_object_context(const struct bContext *C);
/* context.object or context.active_object */
-struct Object *ED_object_active_context(struct bContext *C);
+struct Object *ED_object_active_context(const struct bContext *C);
void ED_collection_hide_menu_draw(const struct bContext *C, struct uiLayout *layout);
/* object_utils.c */
@@ -185,7 +184,7 @@ struct Base *ED_object_add_duplicate(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct Base *base,
- int dupflag);
+ const eDupli_ID_Flags dupflag);
void ED_object_parent(struct Object *ob,
struct Object *parent,
@@ -363,9 +362,10 @@ struct ModifierData *ED_object_modifier_add(struct ReportList *reports,
int type);
bool ED_object_modifier_remove(struct ReportList *reports,
struct Main *bmain,
+ struct Scene *scene,
struct Object *ob,
struct ModifierData *md);
-void ED_object_modifier_clear(struct Main *bmain, struct Object *ob);
+void ED_object_modifier_clear(struct Main *bmain, struct Scene *scene, struct Object *ob);
bool ED_object_modifier_move_down(struct ReportList *reports,
struct Object *ob,
struct ModifierData *md);
@@ -390,8 +390,11 @@ bool ED_object_modifier_apply(struct Main *bmain,
struct Scene *scene,
struct Object *ob,
struct ModifierData *md,
- int mode);
+ int mode,
+ bool keep_modifier);
int ED_object_modifier_copy(struct ReportList *reports,
+ struct Main *bmain,
+ struct Scene *scene,
struct Object *ob,
struct ModifierData *md);
@@ -421,6 +424,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 +456,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);
@@ -481,7 +492,7 @@ struct XFormObjectData *ED_object_data_xform_create_ex(struct ID *id, bool is_ed
struct XFormObjectData *ED_object_data_xform_create(struct ID *id);
struct XFormObjectData *ED_object_data_xform_create_from_edit_mode(ID *id);
-void ED_object_data_xform_destroy(struct XFormObjectData *xod);
+void ED_object_data_xform_destroy(struct XFormObjectData *xod_base);
void ED_object_data_xform_by_mat4(struct XFormObjectData *xod, const float mat[4][4]);
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.h b/source/blender/editors/include/ED_screen.h
index bc6a4b23609..71b7d35908b 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -349,6 +349,7 @@ bool ED_operator_console_active(struct bContext *C);
bool ED_operator_object_active(struct bContext *C);
bool ED_operator_object_active_editable_ex(struct bContext *C, const Object *ob);
bool ED_operator_object_active_editable(struct bContext *C);
+bool ED_operator_object_active_local_editable(struct bContext *C);
bool ED_operator_object_active_editable_mesh(struct bContext *C);
bool ED_operator_object_active_editable_font(struct bContext *C);
bool ED_operator_editable_mesh(struct bContext *C);
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/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index f656aaf9c07..935b99ba8e8 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -47,6 +47,7 @@ struct wmKeyConfig;
/* uvedit_ops.c */
void ED_operatortypes_uvedit(void);
+void ED_operatormacros_uvedit(void);
void ED_keymap_uvedit(struct wmKeyConfig *keyconf);
bool ED_uvedit_minmax(const struct Scene *scene,
@@ -114,51 +115,72 @@ bool uvedit_uv_select_test(const struct Scene *scene,
struct BMLoop *l,
const int cd_loop_uv_offset);
/* uv face */
-bool uvedit_face_select_set(const struct Scene *scene,
+void uvedit_face_select_set_with_sticky(const struct SpaceImage *sima,
+ const struct Scene *scene,
+ struct BMEditMesh *em,
+ struct BMFace *efa,
+ const bool select,
+ const bool do_history,
+ const int cd_loop_uv_offset);
+void uvedit_face_select_set(const struct Scene *scene,
struct BMEditMesh *em,
struct BMFace *efa,
const bool select,
const bool do_history,
const int cd_loop_uv_offset);
-bool uvedit_face_select_enable(const struct Scene *scene,
+void uvedit_face_select_enable(const struct Scene *scene,
struct BMEditMesh *em,
struct BMFace *efa,
const bool do_history,
const int cd_loop_uv_offset);
-bool uvedit_face_select_disable(const struct Scene *scene,
+void uvedit_face_select_disable(const struct Scene *scene,
struct BMEditMesh *em,
struct BMFace *efa,
const int cd_loop_uv_offset);
/* uv edge */
-void uvedit_edge_select_set(struct BMEditMesh *em,
- const struct Scene *scene,
+void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima,
+ const struct Scene *scene,
+ struct BMEditMesh *em,
+ struct BMLoop *l,
+ const bool select,
+ const bool do_history,
+ const uint cd_loop_uv_offset);
+void uvedit_edge_select_set(const struct Scene *scene,
+ struct BMEditMesh *em,
struct BMLoop *l,
const bool select,
const bool do_history,
const int cd_loop_uv_offset);
-void uvedit_edge_select_enable(struct BMEditMesh *em,
- const struct Scene *scene,
+void uvedit_edge_select_enable(const struct Scene *scene,
+ struct BMEditMesh *em,
struct BMLoop *l,
const bool do_history,
const int cd_loop_uv_offset);
-void uvedit_edge_select_disable(struct BMEditMesh *em,
- const struct Scene *scene,
+void uvedit_edge_select_disable(const struct Scene *scene,
+ struct BMEditMesh *em,
struct BMLoop *l,
const int cd_loop_uv_offset);
/* uv vert */
-void uvedit_uv_select_set(struct BMEditMesh *em,
- const struct Scene *scene,
+void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima,
+ const struct Scene *scene,
+ struct BMEditMesh *em,
+ struct BMLoop *l,
+ const bool select,
+ const bool do_history,
+ const uint cd_loop_uv_offset);
+void uvedit_uv_select_set(const struct Scene *scene,
+ struct BMEditMesh *em,
struct BMLoop *l,
const bool select,
const bool do_history,
const int cd_loop_uv_offset);
-void uvedit_uv_select_enable(struct BMEditMesh *em,
- const struct Scene *scene,
+void uvedit_uv_select_enable(const struct Scene *scene,
+ struct BMEditMesh *em,
struct BMLoop *l,
const bool do_history,
const int cd_loop_uv_offset);
-void uvedit_uv_select_disable(struct BMEditMesh *em,
- const struct Scene *scene,
+void uvedit_uv_select_disable(const struct Scene *scene,
+ struct BMEditMesh *em,
struct BMLoop *l,
const int cd_loop_uv_offset);
@@ -174,8 +196,29 @@ bool ED_uvedit_nearest_uv_multi(const struct Scene *scene,
float *dist_sq,
float r_uv[2]);
-void ED_uvedit_get_aspect(
- const struct Scene *scene, struct Object *ob, struct BMesh *em, float *r_aspx, float *r_aspy);
+struct BMFace **ED_uvedit_selected_faces(struct Scene *scene,
+ struct BMesh *bm,
+ int len_max,
+ int *r_faces_len);
+struct BMLoop **ED_uvedit_selected_edges(struct Scene *scene,
+ struct BMesh *bm,
+ int len_max,
+ int *r_edges_len);
+struct BMLoop **ED_uvedit_selected_verts(struct Scene *scene,
+ struct BMesh *bm,
+ int len_max,
+ int *r_verts_len);
+
+void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy);
+
+void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l);
+struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
+
+void ED_uvedit_active_edge_loop_set(struct BMesh *bm, struct BMLoop *l);
+struct BMLoop *ED_uvedit_active_edge_loop_get(struct BMesh *bm);
+
+char ED_uvedit_select_mode_get(const Scene *scene);
+void ED_uvedit_select_sync_flush(const ToolSettings *ts, struct BMEditMesh *em, const bool select);
/* uvedit_unwrap_ops.c */
void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 5e706856738..f44b099f42e 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -150,7 +150,7 @@ void ED_view3d_cursor3d_update(struct bContext *C,
struct Camera *ED_view3d_camera_data_get(struct View3D *v3d, struct RegionView3D *rv3d);
void ED_view3d_to_m4(float mat[4][4], const float ofs[3], const float quat[4], const float dist);
-void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], float *dist);
+void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], const float *dist);
void ED_view3d_from_object(
const struct Object *ob, float ofs[3], float quat[4], float *dist, float *lens);
@@ -464,9 +464,6 @@ void ED_view3d_clipping_local(struct RegionView3D *rv3d, float mat[4][4]);
bool ED_view3d_clipping_test(const struct RegionView3D *rv3d,
const float co[3],
const bool is_local);
-void ED_view3d_clipping_set(struct RegionView3D *rv3d);
-void ED_view3d_clipping_enable(void);
-void ED_view3d_clipping_disable(void);
float ED_view3d_radius_to_dist_persp(const float angle, const float radius);
float ED_view3d_radius_to_dist_ortho(const float lens, const float radius);
@@ -502,7 +499,7 @@ bool ED_view3d_autodist_simple(struct ARegion *region,
const int mval[2],
float mouse_worldloc[3],
int margin,
- float *force_depth);
+ const float *force_depth);
bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth);
bool ED_view3d_autodist_depth_seg(struct ARegion *region,
const int mval_sta[2],
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 11dbb105072..2c42f3a5071 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -65,7 +65,6 @@ struct uiWidgetColors;
struct wmDrag;
struct wmDropBox;
struct wmEvent;
-struct wmEvent;
struct wmGizmo;
struct wmKeyConfig;
struct wmKeyMap;
@@ -102,7 +101,7 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle;
/* use for clamping popups within the screen */
#define UI_SCREEN_MARGIN 10
-/* uiBlock->dt and uiBut->dt */
+/** #uiBlock.emboss and #uiBut.emboss */
enum {
UI_EMBOSS = 0, /* use widget style for drawing */
UI_EMBOSS_NONE = 1, /* Nothing, only icon and/or text */
@@ -176,13 +175,6 @@ enum {
UI_RETURN_POPUP_OK = 1 << 5,
};
-/* panel controls */
-enum {
- UI_PNL_SOLID = 1 << 1,
- UI_PNL_CLOSE = 1 << 5,
- UI_PNL_SCALE = 1 << 9,
-};
-
/* but->flag - general state flags. */
enum {
/** Warning, the first 6 flags are internal. */
@@ -301,12 +293,13 @@ enum {
/* 16 to copy ICON_DEFAULT_HEIGHT */
#define UI_DPI_ICON_SIZE ((float)16 * UI_DPI_FAC)
-/* Button types, bits stored in 1 value... and a short even!
- * - bits 0-4: bitnr (0-31)
+/**
+ * Button types, bits stored in 1 value... and a short even!
+ * - bits 0-4: #uiBut.bitnr (0-31)
* - bits 5-7: pointer type
* - bit 8: for 'bit'
* - bit 9-15: button type (now 6 bits, 64 types)
- * */
+ */
typedef enum {
UI_BUT_POIN_CHAR = 32,
UI_BUT_POIN_SHORT = 64,
@@ -664,7 +657,7 @@ bool UI_popup_block_name_exists(const struct bScreen *screen, const char *name);
uiBlock *UI_block_begin(const struct bContext *C,
struct ARegion *region,
const char *name,
- short dt);
+ char emboss);
void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], int r_xy[2]);
void UI_block_end(const struct bContext *C, uiBlock *block);
void UI_block_draw(const struct bContext *C, struct uiBlock *block);
@@ -678,7 +671,7 @@ enum {
};
void UI_block_theme_style_set(uiBlock *block, char theme_style);
char UI_block_emboss_get(uiBlock *block);
-void UI_block_emboss_set(uiBlock *block, char dt);
+void UI_block_emboss_set(uiBlock *block, char emboss);
void UI_block_free(const struct bContext *C, uiBlock *block);
void UI_blocklist_free(const struct bContext *C, struct ListBase *lb);
@@ -764,6 +757,7 @@ bool UI_but_online_manual_id(const uiBut *but,
bool UI_but_online_manual_id_from_active(const struct bContext *C,
char *r_str,
size_t maxlength) ATTR_WARN_UNUSED_RESULT;
+bool UI_but_is_userdef(const uiBut *but);
/* Buttons
*
@@ -1512,7 +1506,7 @@ uiBut *uiDefHotKeyevtButS(uiBlock *block,
short width,
short height,
short *keypoin,
- short *modkeypoin,
+ const short *modkeypoin,
const char *tip);
uiBut *uiDefSearchBut(uiBlock *block,
@@ -1581,7 +1575,13 @@ eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout,
const bool compact);
/* use inside searchfunc to add items */
-bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int state);
+bool UI_search_item_add(uiSearchItems *items,
+ const char *name,
+ void *poin,
+ int iconid,
+ int state,
+ uint8_t name_prefix_offset);
+
void UI_but_func_search_set(uiBut *but,
uiButSearchCreateFn search_create_fn,
uiButSearchUpdateFn search_update_fn,
@@ -1705,12 +1705,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
@@ -2003,6 +2008,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,
@@ -2013,11 +2022,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,
@@ -2445,7 +2452,8 @@ void UI_context_update_anim_flag(const struct bContext *C);
void UI_context_active_but_prop_get_filebrowser(const struct bContext *C,
struct PointerRNA *r_ptr,
struct PropertyRNA **r_prop,
- bool *r_is_undo);
+ bool *r_is_undo,
+ bool *r_is_userdef);
void UI_context_active_but_prop_get_templateID(struct bContext *C,
struct PointerRNA *r_ptr,
struct PropertyRNA **r_prop);
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 04c259ab092..b28a6f530f6 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -44,6 +44,7 @@
#include "BLI_utildefines.h"
+#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_main.h"
@@ -51,7 +52,6 @@
#include "BKE_screen.h"
#include "BKE_unit.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
@@ -737,8 +737,8 @@ static bool ui_but_update_from_old_block(const bContext *C,
#else
BLI_assert(*but_old_p == NULL || BLI_findindex(&oldblock->buttons, *but_old_p) != -1);
- /* fastpath - avoid loop-in-loop, calling 'ui_but_find_old'
- * as long as old/new buttons are aligned */
+ /* Fast-path - avoid loop-in-loop, calling #ui_but_find_old
+ * as long as old/new buttons are aligned. */
if (LIKELY(*but_old_p && ui_but_equals_old(but, *but_old_p))) {
oldbut = *but_old_p;
}
@@ -962,11 +962,9 @@ static bool ui_but_is_rna_undo(const uiBut *but)
if (ID_CHECK_UNDO(id) == false) {
return false;
}
- else {
- return true;
- }
+ return true;
}
- else if (but->rnapoin.type && !RNA_struct_undo_check(but->rnapoin.type)) {
+ if (but->rnapoin.type && !RNA_struct_undo_check(but->rnapoin.type)) {
return false;
}
@@ -1482,13 +1480,13 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
if (but->drawstr[0] == '\0') {
continue;
}
- else if (((block->flag & UI_BLOCK_POPOVER) == 0) && UI_but_is_tool(but)) {
+ if (((block->flag & UI_BLOCK_POPOVER) == 0) && UI_but_is_tool(but)) {
/* For non-popovers, shown in shortcut only
* (has special shortcut handling code). */
continue;
}
}
- else if (but->dt != UI_EMBOSS_PULLDOWN) {
+ else if (but->emboss != UI_EMBOSS_PULLDOWN) {
continue;
}
@@ -1502,10 +1500,10 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
}
}
-void ui_but_override_flag(uiBut *but)
+void ui_but_override_flag(Main *bmain, uiBut *but)
{
- const int override_status = RNA_property_override_library_status(
- &but->rnapoin, but->rnaprop, but->rnaindex);
+ const uint override_status = RNA_property_override_library_status(
+ bmain, &but->rnapoin, but->rnaprop, but->rnaindex);
if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
but->flag |= UI_BUT_OVERRIDEN;
@@ -1735,6 +1733,7 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x
wmWindow *window = CTX_wm_window(C);
Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
uiBut *but;
BLI_assert(block->active);
@@ -1763,8 +1762,10 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x
}
}
- ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
- ui_but_override_flag(but);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (scene) ? scene->r.cfra : 0.0f);
+ ui_but_anim_flag(but, &anim_eval_context);
+ ui_but_override_flag(CTX_data_main(C), but);
if (UI_but_is_decorator(but)) {
ui_but_anim_decorate_update_from_flag(but);
}
@@ -2305,14 +2306,12 @@ bool ui_but_is_rna_valid(uiBut *but)
if (but->rnaprop == NULL || RNA_struct_contains_property(&but->rnapoin, but->rnaprop)) {
return true;
}
- else {
- printf("property removed %s: %p\n", but->drawstr, but->rnaprop);
- return false;
- }
+ printf("property removed %s: %p\n", but->drawstr, but->rnaprop);
+ return false;
}
/**
- * Checks if the button supports ctrl+mousewheel cycling
+ * Checks if the button supports cycling next/previous menu items (ctrl+mouse-wheel).
*/
bool ui_but_supports_cycling(const uiBut *but)
{
@@ -2490,9 +2489,7 @@ int ui_but_string_get_max_length(uiBut *but)
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
return but->hardmax;
}
- else {
- return UI_MAX_DRAW_STR;
- }
+ return UI_MAX_DRAW_STR;
}
uiBut *ui_but_drag_multi_edit_get(uiBut *but)
@@ -2520,9 +2517,7 @@ static double ui_get_but_scale_unit(uiBut *but, double value)
Scene *scene = CTX_data_scene(but->block->evil_C);
return FRA2TIME(value);
}
- else {
- return BKE_scene_unit_scale(unit, RNA_SUBTYPE_UNIT_VALUE(unit_type), value);
- }
+ return BKE_scene_unit_scale(unit, RNA_SUBTYPE_UNIT_VALUE(unit_type), value);
}
/* str will be overwritten */
@@ -2610,9 +2605,7 @@ static float ui_get_but_step_unit(uiBut *but, float step_default)
return (float)step_final;
}
- else {
- return step_default;
- }
+ return step_default;
}
/**
@@ -2807,25 +2800,39 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
return str;
}
-static bool ui_set_but_string_eval_num_unit(bContext *C,
- uiBut *but,
- const char *str,
- double *r_value)
+/**
+ * Report a generic error prefix when evaluating a string with #BPY_execute_string_as_number
+ * as the Python error on it's own doesn't provide enough context.
+ */
+#define UI_NUMBER_EVAL_ERROR_PREFIX IFACE_("Error evaluating number, see Info editor for details")
+
+static bool ui_number_from_string_units(
+ bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value)
+{
+ return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
+}
+
+static bool ui_number_from_string_units_with_but(bContext *C,
+ const char *str,
+ const uiBut *but,
+ double *r_value)
{
+ const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
const UnitSettings *unit = but->block->unit;
- int type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but));
- return user_string_to_number(C, str, unit, type, r_value);
+ return ui_number_from_string_units(C, str, unit_type, unit, r_value);
}
static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
{
+ bool ok;
#ifdef WITH_PYTHON
- return BPY_execute_string_as_number(C, NULL, str, true, r_value);
+ ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
#else
UNUSED_VARS(C);
*r_value = atof(str);
- return true;
+ ok = true;
#endif
+ return ok;
}
static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value)
@@ -2838,15 +2845,13 @@ static bool ui_number_from_string_factor(bContext *C, const char *str, double *r
*r_value /= 100.0;
return success;
}
- else {
- if (!ui_number_from_string(C, str, r_value)) {
- return false;
- }
- if (U.factor_display_type == USER_FACTOR_AS_PERCENTAGE) {
- *r_value /= 100.0;
- }
- return true;
+ if (!ui_number_from_string(C, str, r_value)) {
+ return false;
}
+ if (U.factor_display_type == USER_FACTOR_AS_PERCENTAGE) {
+ *r_value /= 100.0;
+ }
+ return true;
}
static bool ui_number_from_string_percentage(bContext *C, const char *str, double *r_value)
@@ -2858,12 +2863,10 @@ static bool ui_number_from_string_percentage(bContext *C, const char *str, doubl
MEM_freeN(str_new);
return success;
}
- else {
- return ui_number_from_string(C, str, r_value);
- }
+ return ui_number_from_string(C, str, r_value);
}
-bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double *r_value)
+bool ui_but_string_eval_number(bContext *C, const uiBut *but, const char *str, double *r_value)
{
if (str[0] == '\0') {
*r_value = 0.0;
@@ -2877,21 +2880,17 @@ bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double
if (ui_but_is_float(but)) {
if (ui_but_is_unit(but)) {
- return ui_set_but_string_eval_num_unit(C, but, str, r_value);
+ return ui_number_from_string_units_with_but(C, str, but, r_value);
}
- else if (subtype == PROP_FACTOR) {
+ if (subtype == PROP_FACTOR) {
return ui_number_from_string_factor(C, str, r_value);
}
- else if (subtype == PROP_PERCENTAGE) {
+ if (subtype == PROP_PERCENTAGE) {
return ui_number_from_string_percentage(C, str, r_value);
}
- else {
- return ui_number_from_string(C, str, r_value);
- }
- }
- else {
return ui_number_from_string(C, str, r_value);
}
+ return ui_number_from_string(C, str, r_value);
}
/* just the assignment/free part */
@@ -2934,40 +2933,36 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
RNA_property_string_set(&but->rnapoin, but->rnaprop, str);
return true;
}
- else if (type == PROP_POINTER) {
+
+ if (type == PROP_POINTER) {
if (str[0] == '\0') {
RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL, NULL);
return true;
}
- else {
- /* RNA pointer */
- PointerRNA rptr;
- PointerRNA ptr = but->rnasearchpoin;
- PropertyRNA *prop = but->rnasearchprop;
-
- /* This is kind of hackish, in theory think we could only ever use the second member of
- * this if/else, since ui_searchbox_apply() is supposed to always set that pointer when
- * we are storing pointers... But keeping str search first for now,
- * to try to break as little as possible existing code. All this is band-aids anyway.
- * Fact remains, using editstr as main 'reference' over whole search button thingy
- * is utterly weak and should be redesigned imho, but that's not a simple task. */
- if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr)) {
- RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, NULL);
- }
- else if (but->func_arg2 != NULL) {
- RNA_pointer_create(NULL,
- RNA_property_pointer_type(&but->rnapoin, but->rnaprop),
- but->func_arg2,
- &rptr);
- RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, NULL);
- }
-
- return true;
+ /* RNA pointer */
+ PointerRNA rptr;
+ PointerRNA ptr = but->rnasearchpoin;
+ PropertyRNA *prop = but->rnasearchprop;
+
+ /* This is kind of hackish, in theory think we could only ever use the second member of
+ * this if/else, since ui_searchbox_apply() is supposed to always set that pointer when
+ * we are storing pointers... But keeping str search first for now,
+ * to try to break as little as possible existing code. All this is band-aids anyway.
+ * Fact remains, using editstr as main 'reference' over whole search button thingy
+ * is utterly weak and should be redesigned imho, but that's not a simple task. */
+ if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr)) {
+ RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, NULL);
+ }
+ else if (but->func_arg2 != NULL) {
+ RNA_pointer_create(
+ NULL, RNA_property_pointer_type(&but->rnapoin, but->rnaprop), but->func_arg2, &rptr);
+ RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, NULL);
}
- return false;
+ return true;
}
- else if (type == PROP_ENUM) {
+
+ if (type == PROP_ENUM) {
int value;
if (RNA_property_enum_value(
but->block->evil_C, &but->rnapoin, but->rnaprop, str, &value)) {
@@ -2976,9 +2971,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
}
return false;
}
- else {
- BLI_assert(0);
- }
+ BLI_assert(0);
}
}
else if (but->type == UI_BTYPE_TAB) {
@@ -3027,7 +3020,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
/* number editing */
double value;
- if (ui_but_string_set_eval_num(C, but, str, &value) == false) {
+ if (ui_but_string_eval_number(C, but, str, &value) == false) {
WM_report_banner_show();
return false;
}
@@ -3078,12 +3071,10 @@ static double soft_range_round_up(double value, double max)
if (newmax * 0.2 >= max && newmax * 0.2 >= value) {
return newmax * 0.2;
}
- else if (newmax * 0.5 >= max && newmax * 0.5 >= value) {
+ if (newmax * 0.5 >= max && newmax * 0.5 >= value) {
return newmax * 0.5;
}
- else {
- return newmax;
- }
+ return newmax;
}
static double soft_range_round_down(double value, double max)
@@ -3095,12 +3086,10 @@ static double soft_range_round_down(double value, double max)
if (newmax * 5.0 <= max && newmax * 5.0 <= value) {
return newmax * 5.0;
}
- else if (newmax * 2.0 <= max && newmax * 2.0 <= value) {
+ if (newmax * 2.0 <= max && newmax * 2.0 <= value) {
return newmax * 2.0;
}
- else {
- return newmax;
- }
+ return newmax;
}
/* note: this could be split up into functions which handle arrays and not */
@@ -3365,7 +3354,7 @@ void UI_block_region_set(uiBlock *block, ARegion *region)
block->oldblock = oldblock;
}
-uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, short dt)
+uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, char emboss)
{
uiBlock *block;
wmWindow *window;
@@ -3376,7 +3365,7 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh
block = MEM_callocN(sizeof(uiBlock), "uiBlock");
block->active = 1;
- block->dt = dt;
+ block->emboss = emboss;
block->evil_C = (void *)C; /* XXX */
if (scn) {
@@ -3415,12 +3404,12 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh
char UI_block_emboss_get(uiBlock *block)
{
- return block->dt;
+ return block->emboss;
}
-void UI_block_emboss_set(uiBlock *block, char dt)
+void UI_block_emboss_set(uiBlock *block, char emboss)
{
- block->dt = dt;
+ block->emboss = emboss;
}
void UI_block_theme_style_set(uiBlock *block, char theme_style)
@@ -3800,7 +3789,7 @@ static uiBut *ui_def_but(uiBlock *block,
but->tip = tip;
but->disabled_info = block->lockstr;
- but->dt = block->dt;
+ but->emboss = block->emboss;
but->pie_dir = UI_RADIAL_NONE;
but->block = block; /* pointer back, used for frontbuffer status, and picker */
@@ -4333,7 +4322,7 @@ static uiBut *ui_def_but_rna(uiBlock *block,
}
if (type == UI_BTYPE_MENU) {
- if (but->dt == UI_EMBOSS_PULLDOWN) {
+ if (but->emboss == UI_EMBOSS_PULLDOWN) {
ui_but_submenu_enable(block, but);
}
}
@@ -4487,21 +4476,20 @@ uiBut *uiDefButAlert(uiBlock *block, int icon, int x, int y, short width, short
if (icon == ALERT_ICON_BLENDER) {
return uiDefButImage(block, ibuf, x, y, width, height, NULL);
}
- else {
- uchar icon_color[4];
- ThemeColorID color_id = TH_INFO_WARNING;
- if (icon == ALERT_ICON_ERROR) {
- color_id = TH_INFO_ERROR;
- }
- else if (icon == ALERT_ICON_INFO) {
- color_id = TH_INFO_INFO;
- }
- else if (icon == ALERT_ICON_QUESTION) {
- color_id = TH_INFO_PROPERTY;
- }
- UI_GetThemeColorType4ubv(color_id, SPACE_INFO, icon_color);
- return uiDefButImage(block, ibuf, x, y, width, height, icon_color);
+
+ uchar icon_color[4];
+ ThemeColorID color_id = TH_INFO_WARNING;
+ if (icon == ALERT_ICON_ERROR) {
+ color_id = TH_INFO_ERROR;
+ }
+ else if (icon == ALERT_ICON_INFO) {
+ color_id = TH_INFO_INFO;
}
+ else if (icon == ALERT_ICON_QUESTION) {
+ color_id = TH_INFO_PROPERTY;
+ }
+ UI_GetThemeColorType4ubv(color_id, SPACE_INFO, icon_color);
+ return uiDefButImage(block, ibuf, x, y, width, height, icon_color);
}
/**
@@ -4518,31 +4506,29 @@ static int findBitIndex(uint x)
if (!x || !is_power_of_2_i(x)) { /* is_power_of_2_i(x) strips lowest bit */
return -1;
}
- else {
- int idx = 0;
-
- if (x & 0xFFFF0000) {
- idx += 16;
- x >>= 16;
- }
- if (x & 0xFF00) {
- idx += 8;
- x >>= 8;
- }
- if (x & 0xF0) {
- idx += 4;
- x >>= 4;
- }
- if (x & 0xC) {
- idx += 2;
- x >>= 2;
- }
- if (x & 0x2) {
- idx += 1;
- }
+ int idx = 0;
- return idx;
+ if (x & 0xFFFF0000) {
+ idx += 16;
+ x >>= 16;
}
+ if (x & 0xFF00) {
+ idx += 8;
+ x >>= 8;
+ }
+ if (x & 0xF0) {
+ idx += 4;
+ x >>= 4;
+ }
+ if (x & 0xC) {
+ idx += 2;
+ x >>= 2;
+ }
+ if (x & 0x2) {
+ idx += 1;
+ }
+
+ return idx;
}
/* autocomplete helper functions */
@@ -4591,7 +4577,7 @@ void UI_autocomplete_update_name(AutoComplete *autocpl, const char *name)
truncate[a] = 0;
break;
}
- else if (truncate[a] != name[a]) {
+ if (truncate[a] != name[a]) {
truncate[a] = 0;
}
}
@@ -4651,22 +4637,20 @@ static uiBut *uiDefButBit(uiBlock *block,
if (bitIdx == -1) {
return NULL;
}
- else {
- return uiDefBut(block,
- type | UI_BUT_POIN_BIT | bitIdx,
- retval,
- str,
- x,
- y,
- width,
- height,
- poin,
- min,
- max,
- a1,
- a2,
- tip);
- }
+ return uiDefBut(block,
+ type | UI_BUT_POIN_BIT | bitIdx,
+ retval,
+ str,
+ x,
+ y,
+ width,
+ height,
+ poin,
+ min,
+ max,
+ a1,
+ a2,
+ tip);
}
uiBut *uiDefButF(uiBlock *block,
int type,
@@ -5038,22 +5022,20 @@ static uiBut *uiDefIconButBit(uiBlock *block,
if (bitIdx == -1) {
return NULL;
}
- else {
- return uiDefIconBut(block,
- type | UI_BUT_POIN_BIT | bitIdx,
- retval,
- icon,
- x,
- y,
- width,
- height,
- poin,
- min,
- max,
- a1,
- a2,
- tip);
- }
+ return uiDefIconBut(block,
+ type | UI_BUT_POIN_BIT | bitIdx,
+ retval,
+ icon,
+ x,
+ y,
+ width,
+ height,
+ poin,
+ min,
+ max,
+ a1,
+ a2,
+ tip);
}
uiBut *uiDefIconButF(uiBlock *block,
@@ -5426,23 +5408,21 @@ static uiBut *uiDefIconTextButBit(uiBlock *block,
if (bitIdx == -1) {
return NULL;
}
- else {
- return uiDefIconTextBut(block,
- type | UI_BUT_POIN_BIT | bitIdx,
- retval,
- icon,
- str,
- x,
- y,
- width,
- height,
- poin,
- min,
- max,
- a1,
- a2,
- tip);
- }
+ return uiDefIconTextBut(block,
+ type | UI_BUT_POIN_BIT | bitIdx,
+ retval,
+ icon,
+ str,
+ x,
+ y,
+ width,
+ height,
+ poin,
+ min,
+ max,
+ a1,
+ a2,
+ tip);
}
uiBut *uiDefIconTextButF(uiBlock *block,
@@ -5828,7 +5808,7 @@ void UI_block_order_flip(uiBlock *block)
if (U.uiflag & USER_MENUFIXEDORDER) {
return;
}
- else if (block->flag & UI_BLOCK_NO_FLIP) {
+ if (block->flag & UI_BLOCK_NO_FLIP) {
return;
}
@@ -5994,9 +5974,7 @@ int UI_but_unit_type_get(const uiBut *but)
if ((ownUnit != 0) || (but->rnaprop == NULL)) {
return ownUnit << 16;
}
- else {
- return RNA_SUBTYPE_UNIT(RNA_property_subtype(but->rnaprop));
- }
+ return RNA_SUBTYPE_UNIT(RNA_property_subtype(but->rnaprop));
}
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg)
@@ -6301,7 +6279,7 @@ uiBut *uiDefHotKeyevtButS(uiBlock *block,
short width,
short height,
short *keypoin,
- short *modkeypoin,
+ const short *modkeypoin,
const char *tip)
{
uiBut *but = ui_def_but(block,
@@ -6422,7 +6400,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)
@@ -6466,7 +6444,8 @@ static void operator_enum_search_update_fn(const struct bContext *C,
/* note: need to give the index rather than the
* identifier because the enum can be freed */
if (BLI_strcasestr(item->name, str)) {
- if (!UI_search_item_add(items, item->name, POINTER_FROM_INT(item->value), item->icon, 0)) {
+ if (!UI_search_item_add(
+ items, item->name, POINTER_FROM_INT(item->value), item->icon, 0, 0)) {
break;
}
}
diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c
index 32cae609395..8edae5d8740 100644
--- a/source/blender/editors/interface/interface_align.c
+++ b/source/blender/editors/interface/interface_align.c
@@ -31,6 +31,8 @@
#include "interface_intern.h"
+#include "MEM_guardedalloc.h"
+
#ifdef USE_UIBUT_SPATIAL_ALIGN
/**
@@ -122,26 +124,6 @@ bool ui_but_can_align(const uiBut *but)
(BLI_rctf_size_y(&but->rect) > 0.0f));
}
-int ui_but_align_opposite_to_area_align_get(const ARegion *region)
-{
- const ARegion *align_region = (region->alignment & RGN_SPLIT_PREV && region->prev) ?
- region->prev :
- region;
-
- switch (RGN_ALIGN_ENUM_FROM_MASK(align_region->alignment)) {
- case RGN_ALIGN_TOP:
- return UI_BUT_ALIGN_DOWN;
- case RGN_ALIGN_BOTTOM:
- return UI_BUT_ALIGN_TOP;
- case RGN_ALIGN_LEFT:
- return UI_BUT_ALIGN_RIGHT;
- case RGN_ALIGN_RIGHT:
- return UI_BUT_ALIGN_LEFT;
- }
-
- return 0;
-}
-
/**
* This function checks a pair of buttons (assumed in a same align group),
* and if they are neighbors, set needed data accordingly.
@@ -436,7 +418,16 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region)
return;
}
- butal_array = alloca(sizeof(*butal_array) * (size_t)num_buttons);
+ /* Note that this is typically less than ~20, and almost always under ~100.
+ * Even so, we can't ensure this value won't exceed available stack memory.
+ * Fallback to allocation instead of using #alloca, see: T78636. */
+ ButAlign butal_array_buf[256];
+ if (num_buttons <= ARRAY_SIZE(butal_array_buf)) {
+ butal_array = butal_array_buf;
+ }
+ else {
+ butal_array = MEM_mallocN(sizeof(*butal_array) * num_buttons, __func__);
+ }
memset(butal_array, 0, sizeof(*butal_array) * (size_t)num_buttons);
/* Second loop: we initialize our ButAlign data for each button. */
@@ -535,6 +526,9 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region)
}
}
}
+ if (butal_array_buf != butal_array) {
+ MEM_freeN(butal_array);
+ }
}
# undef SIDE_TO_UI_BUT_ALIGN
@@ -545,9 +539,9 @@ void ui_block_align_calc(uiBlock *block, const ARegion *region)
# undef STITCH
# undef MAX_DELTA
-#else
+#else /* !USE_UIBUT_SPATIAL_ALIGN */
-bool ui_but_can_align(uiBut *but)
+bool ui_but_can_align(const uiBut *but)
{
return !ELEM(but->type,
UI_BTYPE_LABEL,
@@ -730,7 +724,7 @@ static void ui_block_align_calc_but(uiBut *first, short nr)
}
}
-void ui_block_align_calc(uiBlock *block)
+void ui_block_align_calc(uiBlock *block, const struct ARegion *UNUSED(region))
{
uiBut *but;
short nr;
@@ -755,4 +749,25 @@ void ui_block_align_calc(uiBlock *block)
}
}
}
-#endif
+
+#endif /* !USE_UIBUT_SPATIAL_ALIGN */
+
+int ui_but_align_opposite_to_area_align_get(const ARegion *region)
+{
+ const ARegion *align_region = (region->alignment & RGN_SPLIT_PREV && region->prev) ?
+ region->prev :
+ region;
+
+ switch (RGN_ALIGN_ENUM_FROM_MASK(align_region->alignment)) {
+ case RGN_ALIGN_TOP:
+ return UI_BUT_ALIGN_DOWN;
+ case RGN_ALIGN_BOTTOM:
+ return UI_BUT_ALIGN_TOP;
+ case RGN_ALIGN_LEFT:
+ return UI_BUT_ALIGN_RIGHT;
+ case RGN_ALIGN_RIGHT:
+ return UI_BUT_ALIGN_LEFT;
+ }
+
+ return 0;
+}
diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c
index 5bf2147aff5..8d12a1dd1ad 100644
--- a/source/blender/editors/interface/interface_anim.c
+++ b/source/blender/editors/interface/interface_anim.c
@@ -33,6 +33,7 @@
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
+#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
@@ -65,7 +66,7 @@ static FCurve *ui_but_get_fcurve(
but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special);
}
-void ui_but_anim_flag(uiBut *but, float cfra)
+void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
{
AnimData *adt;
bAction *act;
@@ -95,6 +96,7 @@ void ui_but_anim_flag(uiBut *but, float cfra)
* we need to correct the frame number to "look inside" the
* remapped action
*/
+ float cfra = anim_eval_context->eval_time;
if (adt) {
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
@@ -105,7 +107,9 @@ void ui_but_anim_flag(uiBut *but, float cfra)
/* XXX: this feature is totally broken and useless with NLA */
if (adt == NULL || adt->nla_tracks.first == NULL) {
- if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, cfra)) {
+ const AnimationEvalContext remapped_context = BKE_animsys_eval_context_construct_at(
+ anim_eval_context, cfra);
+ if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
but->drawflag |= UI_BUT_ANIMATED_CHANGED;
}
}
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index cc370113422..aaa5e1c0cf1 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -104,7 +104,8 @@ static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDPro
*r_prop = (but->opptr && but->opptr->data) ? IDP_CopyProperty(but->opptr->data) : NULL;
return but->optype->idname;
}
- else if (but->rnaprop) {
+
+ if (but->rnaprop) {
const PropertyType rnaprop_type = RNA_property_type(but->rnaprop);
if (rnaprop_type == PROP_BOOLEAN) {
@@ -115,7 +116,7 @@ static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDPro
}
return "WM_OT_context_toggle";
}
- else if (rnaprop_type == PROP_ENUM) {
+ if (rnaprop_type == PROP_ENUM) {
/* Enum */
*r_prop = shortcut_property_from_rna(C, but);
if (*r_prop == NULL) {
@@ -351,7 +352,7 @@ static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *
return (bUserMenuItem *)ED_screen_user_menu_item_find_operator(
&um->items, but->optype, prop, but->opcontext);
}
- else if (but->rnaprop) {
+ if (but->rnaprop) {
const char *member_id = WM_context_member_from_ptr(C, &but->rnapoin);
const char *data_path = RNA_path_from_ID_to_struct(&but->rnapoin);
const char *member_id_data_path = member_id;
@@ -369,12 +370,10 @@ static bUserMenuItem *ui_but_user_menu_find(bContext *C, uiBut *but, bUserMenu *
}
return umi;
}
- else if ((mt = UI_but_menutype_get(but))) {
+ if ((mt = UI_but_menutype_get(but))) {
return (bUserMenuItem *)ED_screen_user_menu_item_find_menu(&um->items, mt);
}
- else {
- return NULL;
- }
+ return NULL;
}
static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
@@ -408,7 +407,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
"'%s').label",
idname);
char *expr_result = NULL;
- if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) {
+ if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
STRNCPY(drawstr, expr_result);
MEM_freeN(expr_result);
}
@@ -561,7 +560,8 @@ 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(
+ CTX_data_main(C), 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..df11a78e657 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 */
@@ -739,9 +773,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region),
(float)rect->ymin,
ibuf->x,
ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
+ GPU_RGBA8,
+ false,
ibuf->rect,
1.0f,
1.0f,
@@ -765,10 +798,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 +832,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(
@@ -835,7 +866,7 @@ static void histogram_draw_one(float r,
}
GPU_line_smooth(true);
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE);
+ GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE, GPU_ONE, GPU_ONE);
immUniformColor4fv(color);
@@ -1136,7 +1167,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
}
if (scopes->ok && scopes->waveform_1 != NULL) {
- glBlendFunc(GL_ONE, GL_ONE);
+ GPU_blend_set_func(GPU_ONE, GPU_ONE);
GPU_point_size(1.0);
/* LUMA (1 channel) */
@@ -1436,7 +1467,7 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
/* pixel point cloud */
float col[3] = {alpha, alpha, alpha};
- glBlendFunc(GL_ONE, GL_ONE);
+ GPU_blend_set_func(GPU_ONE, GPU_ONE);
GPU_point_size(1.0);
GPU_matrix_push();
@@ -1751,8 +1782,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec
UI_draw_roundbox_3ub_alpha(
true, rect->xmin, rect->ymin, rect->xmax, rect->ymax, 5.0f, wcol->inner, 255);
- glCullFace(GL_BACK);
- glEnable(GL_CULL_FACE);
+ GPU_face_culling(GPU_CULL_BACK);
/* setup lights */
ui_but_v3_get(but, light);
@@ -1777,7 +1807,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec
GPU_batch_draw(sphere);
/* restore */
- glDisable(GL_CULL_FACE);
+ GPU_face_culling(GPU_CULL_NONE);
/* AA circle */
GPUVertFormat *format = immVertexFormat();
@@ -2131,7 +2161,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 +2189,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 +2222,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 +2241,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_points = (uint)PROF_TABLE_LEN(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 */
+ for (i = 0; i < (uint)PROF_TABLE_LEN(profile->path_len);
+ 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 +2276,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 +2315,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 +2327,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 +2388,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 +2400,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 +2436,6 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
}
immEnd();
}
-
immUnbindProgram();
/* restore scissortest */
@@ -2448,9 +2544,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
rect.ymin + 1,
drawibuf->x,
drawibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_LINEAR,
+ GPU_RGBA8,
+ true,
drawibuf->rect,
1.0f,
1.0f,
@@ -2678,7 +2773,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 +2781,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_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 817cb44db29..a740a152f1c 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -147,9 +147,7 @@ uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *ev
if (ELEM(NULL, but, but->rnapoin.data, but->rnaprop)) {
return NULL;
}
- else {
- return but;
- }
+ return but;
}
/** \} */
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c
index c917ffb3f3e..5da82b5be9c 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_color.c
@@ -39,8 +39,6 @@
#include "RNA_access.h"
-#include "GPU_glew.h"
-
#include "UI_interface.h"
#include "IMB_colormanagement.h"
@@ -320,9 +318,7 @@ static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_PASS_THROUGH;
- }
+ return OPERATOR_PASS_THROUGH;
}
/* Repeat operator */
@@ -338,9 +334,7 @@ static int eyedropper_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_PASS_THROUGH;
- }
+ return OPERATOR_PASS_THROUGH;
}
static bool eyedropper_poll(bContext *C)
diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c
index 24d06361c54..7b8357f5ef1 100644
--- a/source/blender/editors/interface/interface_eyedropper_colorband.c
+++ b/source/blender/editors/interface/interface_eyedropper_colorband.c
@@ -314,9 +314,7 @@ static int eyedropper_colorband_invoke(bContext *C, wmOperator *op, const wmEven
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* Repeat operator */
@@ -332,9 +330,7 @@ static int eyedropper_colorband_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static bool eyedropper_colorband_poll(bContext *C)
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c
index f2217db9b7d..a162cac1b02 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/interface_eyedropper_datablock.c
@@ -292,10 +292,8 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Could support finished & undo-skip. */
return is_undo ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
- else {
- BKE_report(op->reports, RPT_WARNING, "Failed to set value");
- return OPERATOR_CANCELLED;
- }
+ BKE_report(op->reports, RPT_WARNING, "Failed to set value");
+ return OPERATOR_CANCELLED;
}
}
}
@@ -326,9 +324,7 @@ static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* Repeat operator */
@@ -341,9 +337,7 @@ static int datadropper_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static bool datadropper_poll(bContext *C)
diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c
index 5c85edc94a1..ac142fe7217 100644
--- a/source/blender/editors/interface/interface_eyedropper_depth.c
+++ b/source/blender/editors/interface/interface_eyedropper_depth.c
@@ -321,9 +321,7 @@ static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* Repeat operator */
@@ -336,9 +334,7 @@ static int depthdropper_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static bool depthdropper_poll(bContext *C)
diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c
index 276cc70f2b5..0162e205c29 100644
--- a/source/blender/editors/interface/interface_eyedropper_driver.c
+++ b/source/blender/editors/interface/interface_eyedropper_driver.c
@@ -102,46 +102,44 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
if (but == NULL) {
return;
}
- else {
- /* Get paths for src... */
- PointerRNA *target_ptr = &but->rnapoin;
- PropertyRNA *target_prop = but->rnaprop;
- int target_index = but->rnaindex;
-
- char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
-
- /* ... and destination */
- char *dst_path = BKE_animdata_driver_path_hack(C, &ddr->ptr, ddr->prop, NULL);
-
- /* Now create driver(s) */
- if (target_path && dst_path) {
- int success = ANIM_add_driver_with_target(op->reports,
- ddr->ptr.owner_id,
- dst_path,
- ddr->index,
- target_ptr->owner_id,
- target_path,
- target_index,
- flag,
- DRIVER_TYPE_PYTHON,
- mapping_type);
-
- if (success) {
- /* send updates */
- UI_context_update_anim_flag(C);
- DEG_relations_tag_update(CTX_data_main(C));
- DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
- }
+ /* Get paths for src... */
+ PointerRNA *target_ptr = &but->rnapoin;
+ PropertyRNA *target_prop = but->rnaprop;
+ int target_index = but->rnaindex;
+
+ char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
+
+ /* ... and destination */
+ char *dst_path = BKE_animdata_driver_path_hack(C, &ddr->ptr, ddr->prop, NULL);
+
+ /* Now create driver(s) */
+ if (target_path && dst_path) {
+ int success = ANIM_add_driver_with_target(op->reports,
+ ddr->ptr.owner_id,
+ dst_path,
+ ddr->index,
+ target_ptr->owner_id,
+ target_path,
+ target_index,
+ flag,
+ DRIVER_TYPE_PYTHON,
+ mapping_type);
+
+ if (success) {
+ /* send updates */
+ UI_context_update_anim_flag(C);
+ DEG_relations_tag_update(CTX_data_main(C));
+ DEG_id_tag_update(ddr->ptr.owner_id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
}
+ }
- /* cleanup */
- if (target_path) {
- MEM_freeN(target_path);
- }
- if (dst_path) {
- MEM_freeN(dst_path);
- }
+ /* cleanup */
+ if (target_path) {
+ MEM_freeN(target_path);
+ }
+ if (dst_path) {
+ MEM_freeN(dst_path);
}
}
@@ -190,9 +188,7 @@ static int driverdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* Repeat operator */
@@ -205,9 +201,7 @@ static int driverdropper_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static bool driverdropper_poll(bContext *C)
@@ -215,9 +209,7 @@ static bool driverdropper_poll(bContext *C)
if (!CTX_wm_window(C)) {
return 0;
}
- else {
- return 1;
- }
+ return 1;
}
void UI_OT_eyedropper_driver(wmOperatorType *ot)
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index 3d32ede60c2..978d8ac09de 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -324,9 +324,7 @@ static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_PASS_THROUGH;
- }
+ return OPERATOR_PASS_THROUGH;
}
/* Repeat operator */
@@ -340,9 +338,7 @@ static int eyedropper_gpencil_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_PASS_THROUGH;
- }
+ return OPERATOR_PASS_THROUGH;
}
static bool eyedropper_gpencil_poll(bContext *C)
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index b37b2f2cd4b..6f76da0317c 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -47,6 +47,7 @@
#include "PIL_time.h"
+#include "BKE_animsys.h"
#include "BKE_blender_undo.h"
#include "BKE_brush.h"
#include "BKE_colorband.h"
@@ -585,7 +586,7 @@ static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx)
return true;
}
-static void ui_rna_update_preferences_dirty(PointerRNA *ptr, PropertyRNA *prop)
+static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop)
{
/* Not very elegant, but ensures preference changes force re-save. */
bool tag = false;
@@ -598,8 +599,18 @@ static void ui_rna_update_preferences_dirty(PointerRNA *ptr, PropertyRNA *prop)
tag = true;
}
}
+ return tag;
+}
+
+bool UI_but_is_userdef(const uiBut *but)
+{
+ /* This is read-only, RNA API isn't using const when it could. */
+ return ui_rna_is_userdef((PointerRNA *)&but->rnapoin, but->rnaprop);
+}
- if (tag) {
+static void ui_rna_update_preferences_dirty(PointerRNA *ptr, PropertyRNA *prop)
+{
+ if (ui_rna_is_userdef(ptr, prop)) {
U.runtime.is_dirty = true;
WM_main_add_notifier(NC_WINDOW, NULL);
}
@@ -1374,16 +1385,14 @@ static bool ui_drag_toggle_but_is_supported(const uiBut *but)
if (ui_but_is_bool(but)) {
return true;
}
- else if (UI_but_is_decorator(but)) {
+ if (UI_but_is_decorator(but)) {
return ELEM(but->icon,
ICON_DECORATE,
ICON_DECORATE_KEYFRAME,
ICON_DECORATE_ANIMATE,
ICON_DECORATE_OVERRIDE);
}
- else {
- return false;
- }
+ return false;
}
/* Button pushed state to compare if other buttons match. Can be more
@@ -1394,19 +1403,15 @@ static int ui_drag_toggle_but_pushed_state(bContext *C, uiBut *but)
if (but->pushed_state_func) {
return but->pushed_state_func(C, but->pushed_state_arg);
}
- else {
- /* Assume icon identifies a unique state, for buttons that
- * work though functions callbacks and don't have an boolean
- * value that indicates the state. */
- return but->icon + but->iconadd;
- }
+ /* Assume icon identifies a unique state, for buttons that
+ * work though functions callbacks and don't have an boolean
+ * value that indicates the state. */
+ return but->icon + but->iconadd;
}
- else if (ui_but_is_bool(but)) {
+ if (ui_but_is_bool(but)) {
return ui_but_is_pushed(but);
}
- else {
- return 0;
- }
+ return 0;
}
typedef struct uiDragToggleHandle {
@@ -1572,9 +1577,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void
WM_event_add_mousemove(win);
return WM_UI_HANDLER_BREAK;
}
- else {
- return WM_UI_HANDLER_CONTINUE;
- }
+ return WM_UI_HANDLER_CONTINUE;
}
static bool ui_but_is_drag_toggle(const uiBut *but)
@@ -1750,21 +1753,21 @@ static void ui_selectcontext_apply(bContext *C,
RNA_property_int_range(&but->rnapoin, prop, &min.i, &max.i);
}
else if (rna_type == PROP_ENUM) {
- /* not a delta infact */
+ /* Not a delta in fact. */
delta.i = RNA_property_enum_get(&but->rnapoin, prop);
}
else if (rna_type == PROP_BOOLEAN) {
if (is_array) {
- /* not a delta infact */
+ /* Not a delta in fact. */
delta.b = RNA_property_boolean_get_index(&but->rnapoin, prop, index);
}
else {
- /* not a delta infact */
+ /* Not a delta in fact. */
delta.b = RNA_property_boolean_get(&but->rnapoin, prop);
}
}
else if (rna_type == PROP_POINTER) {
- /* not a delta infact */
+ /* Not a delta in fact. */
delta.p = RNA_property_pointer_get(&but->rnapoin, prop);
}
@@ -2322,9 +2325,7 @@ static bool parse_float_array(char *text, float *values, int expected_length)
memcpy(values, v, sizeof(float) * expected_length);
return true;
}
- else {
- return false;
- }
+ return false;
}
static void ui_but_paste_numeric_array(bContext *C,
@@ -2363,7 +2364,7 @@ static void ui_but_paste_numeric_value(bContext *C,
{
double value;
- if (ui_but_string_set_eval_num(C, but, buf_paste, &value)) {
+ if (ui_but_string_eval_number(C, but, buf_paste, &value)) {
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
data->value = value;
ui_but_string_set(C, but, buf_paste);
@@ -4194,7 +4195,7 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons
button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
return WM_UI_HANDLER_BREAK;
}
- else if (event->type == LEFTMOUSE && event->val == KM_RELEASE && but->block->handle) {
+ if (event->type == LEFTMOUSE && event->val == KM_RELEASE && but->block->handle) {
/* regular buttons will be 'UI_SELECT', menu items 'UI_ACTIVE' */
if (!(but->flag & (UI_SELECT | UI_ACTIVE))) {
data->cancel = true;
@@ -4202,7 +4203,7 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
}
- else if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
+ if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) {
button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
return WM_UI_HANDLER_BREAK;
}
@@ -4237,11 +4238,11 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
return WM_UI_HANDLER_CONTINUE;
}
- else if (event->type == EVT_UNKNOWNKEY) {
+ if (event->type == EVT_UNKNOWNKEY) {
WM_report(RPT_WARNING, "Unsupported key: Unknown");
return WM_UI_HANDLER_CONTINUE;
}
- else if (event->type == EVT_CAPSLOCKKEY) {
+ if (event->type == EVT_CAPSLOCKKEY) {
WM_report(RPT_WARNING, "Unsupported key: CapsLock");
return WM_UI_HANDLER_CONTINUE;
}
@@ -4291,7 +4292,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
}
- else if (event->type == EVT_ESCKEY) {
+ if (event->type == EVT_ESCKEY) {
if (event->val == KM_PRESS) {
data->cancel = true;
data->escapecancel = true;
@@ -4357,7 +4358,7 @@ static int ui_do_but_TAB(
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
return WM_UI_HANDLER_BREAK;
}
- else if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY)) {
+ if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY)) {
int event_val = (is_property) ? KM_PRESS : KM_CLICK;
if (event->val == event_val) {
button_activate_state(C, but, BUTTON_STATE_EXIT);
@@ -4386,7 +4387,7 @@ static int ui_do_but_TEX(
if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) {
/* pass - allow filesel, enter to execute */
}
- else if (but->dt == UI_EMBOSS_NONE && !event->ctrl) {
+ else if (but->emboss == UI_EMBOSS_NONE && !event->ctrl) {
/* pass */
}
else {
@@ -4459,7 +4460,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
button_activate_state(C, but, BUTTON_STATE_EXIT);
return WM_UI_HANDLER_BREAK;
}
- else if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
+ if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
/* Support alt+wheel on expanded enum rows */
if (but->type == UI_BTYPE_ROW) {
const int direction = (event->type == WHEELDOWNMOUSE) ? -1 : 1;
@@ -4862,7 +4863,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;
@@ -5557,7 +5558,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
return WM_UI_HANDLER_BREAK;
}
- else if (but->type == UI_BTYPE_MENU) {
+ if (but->type == UI_BTYPE_MENU) {
if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
const int direction = (event->type == WHEELDOWNMOUSE) ? 1 : -1;
@@ -5731,7 +5732,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
return WM_UI_HANDLER_BREAK;
}
- else if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
+ if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) {
ColorPicker *cpicker = but->custom_data;
float hsv_static[3] = {0.0f};
float *hsv = cpicker ? cpicker->color_data : hsv_static;
@@ -5758,8 +5759,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
ui_apply_but(C, but->block, but, data, true);
return WM_UI_HANDLER_BREAK;
}
- else if ((int)(but->a1) == UI_PALETTE_COLOR && event->type == EVT_DELKEY &&
- event->val == KM_PRESS) {
+ if ((int)(but->a1) == UI_PALETTE_COLOR && event->type == EVT_DELKEY &&
+ event->val == KM_PRESS) {
Palette *palette = (Palette *)but->rnapoin.owner_id;
PaletteColor *color = but->rnapoin.data;
@@ -6164,7 +6165,7 @@ static int ui_do_but_HSVCUBE(
return WM_UI_HANDLER_BREAK;
}
#ifdef WITH_INPUT_NDOF
- else if (event->type == NDOF_MOTION) {
+ if (event->type == NDOF_MOTION) {
const wmNDOFMotionData *ndof = event->customdata;
const enum eSnapType snap = ui_event_to_snap(event);
@@ -6177,7 +6178,7 @@ static int ui_do_but_HSVCUBE(
}
#endif /* WITH_INPUT_NDOF */
/* XXX hardcoded keymap check.... */
- else if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
+ if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
if (ELEM(but->a1, UI_GRAD_V_ALT, UI_GRAD_L_ALT)) {
int len;
@@ -6440,7 +6441,7 @@ static int ui_do_but_HSVCIRCLE(
return WM_UI_HANDLER_BREAK;
}
#ifdef WITH_INPUT_NDOF
- else if (event->type == NDOF_MOTION) {
+ if (event->type == NDOF_MOTION) {
const enum eSnapType snap = ui_event_to_snap(event);
const wmNDOFMotionData *ndof = event->customdata;
@@ -6453,7 +6454,7 @@ static int ui_do_but_HSVCIRCLE(
}
#endif /* WITH_INPUT_NDOF */
/* XXX hardcoded keymap check.... */
- else if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
+ if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
int len;
/* reset only saturation */
@@ -6934,7 +6935,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;
}
}
@@ -6943,32 +6944,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;
@@ -6979,10 +6986,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
@@ -6990,7 +6995,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;
@@ -7021,16 +7026,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. */
@@ -7043,12 +7058,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;
}
@@ -7056,76 +7069,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_TABLE_LEN(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 {
@@ -7156,19 +7187,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);
@@ -7223,7 +7248,7 @@ static int ui_do_but_HISTOGRAM(
return WM_UI_HANDLER_BREAK;
}
/* XXX hardcoded keymap check.... */
- else if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
+ if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
Histogram *hist = (Histogram *)but->poin;
hist->ymax = 1.f;
@@ -7299,7 +7324,7 @@ static int ui_do_but_WAVEFORM(
return WM_UI_HANDLER_BREAK;
}
/* XXX hardcoded keymap check.... */
- else if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
+ if (event->type == EVT_BACKSPACEKEY && event->val == KM_PRESS) {
Scopes *scopes = (Scopes *)but->poin;
scopes->wavefrm_yfac = 1.f;
@@ -8384,6 +8409,9 @@ void UI_context_update_anim_flag(const bContext *C)
{
Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
+ struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ depsgraph, (scene) ? scene->r.cfra : 0.0f);
uiBlock *block;
uiBut *but, *activebut;
@@ -8393,8 +8421,8 @@ void UI_context_update_anim_flag(const bContext *C)
for (block = region->uiblocks.first; block; block = block->next) {
for (but = block->buttons.first; but; but = but->next) {
- ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
- ui_but_override_flag(but);
+ ui_but_anim_flag(but, &anim_eval_context);
+ ui_but_override_flag(CTX_data_main(C), but);
if (UI_but_is_decorator(but)) {
ui_but_anim_decorate_update_from_flag(but);
}
@@ -8911,11 +8939,11 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
my = event->y;
ui_window_to_block(region, listbox->block, &mx, &my);
- /* convert pan to scrollwheel */
+ /* Convert pan to scroll-wheel. */
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
- /* if type still is mousepan, we call it handled, since delta-y accumulate */
+ /* If type still is mouse-pan, we call it handled, since delta-y accumulate. */
/* also see wm_event_system.c do_wheel_ui hack */
if (type == MOUSEPAN) {
retval = WM_UI_HANDLER_BREAK;
@@ -9389,9 +9417,7 @@ static bool ui_menu_pass_event_to_parent_if_nonactive(uiPopupBlockHandle *menu,
BLI_assert(retval == WM_UI_HANDLER_CONTINUE);
return true;
}
- else {
- return false;
- }
+ return false;
}
static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu)
@@ -9644,7 +9670,7 @@ static int ui_handle_menu_event(bContext *C,
int type = event->type;
int val = event->val;
- /* convert pan to scrollwheel */
+ /* Convert pan to scroll-wheel. */
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
}
@@ -9669,7 +9695,7 @@ static int ui_handle_menu_event(bContext *C,
case EVT_PAGEDOWNKEY:
case EVT_HOMEKEY:
case EVT_ENDKEY:
- /* arrowkeys: only handle for block_loop blocks */
+ /* Arrow-keys: only handle for block_loop blocks. */
if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
/* pass */
}
@@ -9677,7 +9703,7 @@ static int ui_handle_menu_event(bContext *C,
int type = event->type;
int val = event->val;
- /* convert pan to scrollwheel */
+ /* Convert pan to scroll-wheel. */
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
}
@@ -9937,7 +9963,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 {
@@ -9945,7 +9971,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 {
@@ -10039,7 +10065,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 {
@@ -10086,12 +10112,10 @@ static int ui_handle_menu_event(bContext *C,
if (menu->menuretval) {
return WM_UI_HANDLER_CONTINUE;
}
- else if (inside) {
+ if (inside) {
return WM_UI_HANDLER_BREAK;
}
- else {
- return retval;
- }
+ return retval;
}
static int ui_handle_menu_return_submenu(bContext *C,
@@ -10146,9 +10170,7 @@ static int ui_handle_menu_return_submenu(bContext *C,
if (menu->menuretval) {
return WM_UI_HANDLER_CONTINUE;
}
- else {
- return WM_UI_HANDLER_BREAK;
- }
+ return WM_UI_HANDLER_BREAK;
}
static bool ui_but_pie_menu_supported_apply(uiBut *but)
@@ -10176,9 +10198,7 @@ static int ui_but_pie_menu_apply(bContext *C,
button_activate_init(C, menu->region, but, BUTTON_ACTIVATE_OPEN);
return retval;
}
- else {
- menu->menuretval = UI_RETURN_CANCEL;
- }
+ menu->menuretval = UI_RETURN_CANCEL;
}
else {
ui_apply_but(C, but->block, but, but->active, false);
@@ -10882,7 +10902,7 @@ static void ui_popup_handler_remove(bContext *C, void *userdata)
/* More correct would be to expect UI_RETURN_CANCEL here, but not wanting to
* cancel when removing handlers because of file exit is a rare exception.
* So instead of setting cancel flag for all menus before removing handlers,
- * just explicitly flag menu with UI_RETURN_OK to avoid cancelling it. */
+ * just explicitly flag menu with UI_RETURN_OK to avoid canceling it. */
if ((menu->menuretval & UI_RETURN_OK) == 0 && menu->cancel_func) {
menu->cancel_func(C, menu->popup_arg);
}
@@ -10964,9 +10984,7 @@ bool UI_textbutton_activate_rna(const bContext *C,
UI_but_active_only(C, region, block, but);
return true;
}
- else {
- return false;
- }
+ return false;
}
bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut)
@@ -10991,9 +11009,7 @@ bool UI_textbutton_activate_but(const bContext *C, uiBut *actbut)
UI_but_active_only(C, region, block, but);
return true;
}
- else {
- return false;
- }
+ return false;
}
/** \} */
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index deea3028354..a7b7bad2fe6 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -31,6 +31,7 @@
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
+#include "GPU_texture.h"
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
@@ -134,7 +135,7 @@ typedef struct DrawInfo {
} DrawInfo;
typedef struct IconTexture {
- GLuint id[2];
+ struct GPUTexture *tex[2];
int num_textures;
int w;
int h;
@@ -151,7 +152,7 @@ typedef struct IconType {
/* Static here to cache results of icon directory scan, so it's not
* scanning the file-system each time the menu is drawn. */
static struct ListBase iconfilelist = {NULL, NULL};
-static IconTexture icongltex = {{0, 0}, 0, 0, 0, 0.0f, 0.0f};
+static IconTexture icongltex = {{NULL, NULL}, 0, 0, 0, 0.0f, 0.0f};
#ifndef WITH_HEADLESS
@@ -593,10 +594,10 @@ int UI_icon_from_event_type(short event_type, short event_value)
if (event_type == LEFTMOUSE) {
return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_LMB : ICON_MOUSE_LMB_DRAG;
}
- else if (event_type == MIDDLEMOUSE) {
+ if (event_type == MIDDLEMOUSE) {
return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_MMB : ICON_MOUSE_MMB_DRAG;
}
- else if (event_type == RIGHTMOUSE) {
+ if (event_type == RIGHTMOUSE) {
return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_RMB : ICON_MOUSE_RMB_DRAG;
}
@@ -802,9 +803,12 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf,
static void free_icons_textures(void)
{
if (icongltex.num_textures > 0) {
- glDeleteTextures(icongltex.num_textures, icongltex.id);
- icongltex.id[0] = 0;
- icongltex.id[1] = 0;
+ for (int i = 0; i < 2; i++) {
+ if (icongltex.tex[i]) {
+ GPU_texture_free(icongltex.tex[i]);
+ icongltex.tex[i] = NULL;
+ }
+ }
icongltex.num_textures = 0;
}
}
@@ -854,69 +858,40 @@ void UI_icons_reload_internal_textures(void)
/* Allocate OpenGL texture. */
icongltex.num_textures = need_icons_with_border ? 2 : 1;
- glGenTextures(icongltex.num_textures, icongltex.id);
/* Note the filter and LOD bias were tweaked to better preserve icon
* sharpness at different UI scales. */
- if (icongltex.id[0]) {
+ if (icongltex.tex[0] == NULL) {
icongltex.w = b32buf->x;
icongltex.h = b32buf->y;
icongltex.invw = 1.0f / b32buf->x;
icongltex.invh = 1.0f / b32buf->y;
- glBindTexture(GL_TEXTURE_2D, icongltex.id[0]);
- glTexImage2D(GL_TEXTURE_2D,
- 0,
- GL_RGBA8,
- b32buf->x,
- b32buf->y,
- 0,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- b32buf->rect);
- glTexImage2D(GL_TEXTURE_2D,
- 1,
- GL_RGBA8,
- b16buf->x,
- b16buf->y,
- 0,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- b16buf->rect);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.5f);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
- glBindTexture(GL_TEXTURE_2D, 0);
+ icongltex.tex[0] = GPU_texture_create_nD(b32buf->x,
+ b32buf->y,
+ 0,
+ 2,
+ b32buf->rect,
+ GPU_RGBA8,
+ GPU_DATA_UNSIGNED_BYTE,
+ 0,
+ false,
+ NULL);
+ GPU_texture_add_mipmap(icongltex.tex[0], GPU_DATA_UNSIGNED_BYTE, 1, b16buf->rect);
}
- if (need_icons_with_border && icongltex.id[1]) {
- glBindTexture(GL_TEXTURE_2D, icongltex.id[1]);
- glTexImage2D(GL_TEXTURE_2D,
- 0,
- GL_RGBA8,
- b32buf_border->x,
- b32buf_border->y,
- 0,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- b32buf_border->rect);
- glTexImage2D(GL_TEXTURE_2D,
- 1,
- GL_RGBA8,
- b16buf_border->x,
- b16buf_border->y,
- 0,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- b16buf_border->rect);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.5f);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
- glBindTexture(GL_TEXTURE_2D, 0);
+ if (need_icons_with_border && icongltex.tex[1] == NULL) {
+ icongltex.tex[1] = GPU_texture_create_nD(b32buf_border->x,
+ b32buf_border->y,
+ 0,
+ 2,
+ b32buf_border->rect,
+ GPU_RGBA8,
+ GPU_DATA_UNSIGNED_BYTE,
+ 0,
+ false,
+ NULL);
+ GPU_texture_add_mipmap(icongltex.tex[1], GPU_DATA_UNSIGNED_BYTE, 1, b16buf_border->rect);
}
}
@@ -1542,18 +1517,8 @@ static void icon_draw_rect(float x,
immUniform1f("factor", desaturate);
}
- immDrawPixelsTex(&state,
- draw_x,
- draw_y,
- draw_w,
- draw_h,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
- rect,
- 1.0f,
- 1.0f,
- col);
+ immDrawPixelsTex(
+ &state, draw_x, draw_y, draw_w, draw_h, GPU_RGBA8, false, rect, 1.0f, 1.0f, col);
if (ima) {
IMB_freeImBuf(ima);
@@ -1590,28 +1555,27 @@ void UI_icon_draw_cache_begin(void)
g_icon_draw_cache.enabled = true;
}
-static void icon_draw_cache_texture_flush_ex(GLuint texture,
+static void icon_draw_cache_texture_flush_ex(GPUTexture *texture,
IconTextureDrawCall *texture_draw_calls)
{
if (texture_draw_calls->calls == 0) {
return;
}
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, texture);
-
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR);
GPU_shader_bind(shader);
- int img_loc = GPU_shader_get_uniform(shader, "image");
+ int img_binding = GPU_shader_get_texture_binding(shader, "image");
int data_loc = GPU_shader_get_uniform(shader, "calls_data");
- glUniform1i(img_loc, 0);
- glUniform4fv(data_loc, ICON_DRAW_CACHE_SIZE * 3, (float *)texture_draw_calls->drawcall_cache);
+ GPU_texture_bind(texture, img_binding);
+ GPU_sampler_icon_bind(img_binding);
+ GPU_shader_uniform_vector(
+ shader, data_loc, 4, ICON_DRAW_CACHE_SIZE * 3, (float *)texture_draw_calls->drawcall_cache);
GPU_draw_primitive(GPU_PRIM_TRIS, 6 * texture_draw_calls->calls);
- glBindTexture(GL_TEXTURE_2D, 0);
+ GPU_texture_unbind(texture);
texture_draw_calls->calls = 0;
}
@@ -1634,11 +1598,11 @@ static void icon_draw_cache_flush_ex(bool only_full_caches)
GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
if (!only_full_caches || g_icon_draw_cache.normal.calls == ICON_DRAW_CACHE_SIZE) {
- icon_draw_cache_texture_flush_ex(icongltex.id[0], &g_icon_draw_cache.normal);
+ icon_draw_cache_texture_flush_ex(icongltex.tex[0], &g_icon_draw_cache.normal);
}
if (!only_full_caches || g_icon_draw_cache.border.calls == ICON_DRAW_CACHE_SIZE) {
- icon_draw_cache_texture_flush_ex(icongltex.id[1], &g_icon_draw_cache.border);
+ icon_draw_cache_texture_flush_ex(icongltex.tex[1], &g_icon_draw_cache.border);
}
GPU_blend_set_func_separate(
@@ -1735,28 +1699,32 @@ static void icon_draw_texture(float x,
y1 = iy * icongltex.invh;
y2 = (iy + ih) * icongltex.invh;
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, with_border ? icongltex.id[1] : icongltex.id[0]);
+ GPUTexture *texture = with_border ? icongltex.tex[1] : icongltex.tex[0];
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
GPU_shader_bind(shader);
+ int img_binding = GPU_shader_get_texture_binding(shader, "image");
+ int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR);
+ int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon");
+ int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom");
+
if (rgb) {
- glUniform4f(
- GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), rgb[0], rgb[1], rgb[2], alpha);
+ GPU_shader_uniform_vector(shader, color_loc, 4, 1, (float[4]){UNPACK3(rgb), alpha});
}
else {
- glUniform4f(
- GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), alpha, alpha, alpha, alpha);
+ GPU_shader_uniform_vector(shader, color_loc, 4, 1, (float[4]){alpha, alpha, alpha, alpha});
}
- glUniform1i(GPU_shader_get_uniform(shader, "image"), 0);
- glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), x1, y1, x2, y2);
- glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), x, y, x + w, y + h);
+ GPU_shader_uniform_vector(shader, rect_tex_loc, 4, 1, (float[4]){x1, y1, x2, y2});
+ GPU_shader_uniform_vector(shader, rect_geom_loc, 4, 1, (float[4]){x, y, x + w, y + h});
+
+ GPU_texture_bind(texture, img_binding);
+ GPU_sampler_icon_bind(img_binding);
GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4);
- glBindTexture(GL_TEXTURE_2D, 0);
+ GPU_texture_unbind(texture);
GPU_blend_set_func_separate(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
@@ -2139,7 +2107,8 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
}
return id->icon_id;
}
- else if (paint_mode != PAINT_MODE_INVALID) {
+
+ if (paint_mode != PAINT_MODE_INVALID) {
items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
const uint tool_offset = BKE_paint_get_brush_tool_offset_from_paintmode(paint_mode);
const int tool_type = *(char *)POINTER_OFFSET(br, tool_offset);
@@ -2192,6 +2161,24 @@ 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;
+ }
+ if (id->tag & LIB_TAG_INDIRECT) {
+ return ICON_LIBRARY_DATA_INDIRECT;
+ }
+ return ICON_LIBRARY_DATA_DIRECT;
+ }
+ 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;
@@ -2219,10 +2206,10 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big
if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) {
return ICON_SHADING_TEXTURE;
}
- else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
+ if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
return ICON_OUTLINER_DATA_MESH;
}
- else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
+ if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
return ICON_FILE_IMAGE;
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 6cd990ec2b0..5ff2d76f7c8 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -32,6 +32,7 @@
#include "UI_interface.h"
#include "UI_resources.h"
+struct AnimationEvalContext;
struct ARegion;
struct ID;
struct ImBuf;
@@ -232,8 +233,8 @@ struct uiBut {
const char *disabled_info;
BIFIconID icon;
- /** drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the block */
- char dt;
+ /** emboss: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the #uiBlock.emboss */
+ char emboss;
/** direction in a pie menu, used for collision detection (RadialDirection) */
signed char pie_dir;
/** could be made into a single flag */
@@ -404,8 +405,8 @@ struct uiBlock {
char direction;
/** UI_BLOCK_THEME_STYLE_* */
char theme_style;
- /** drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to buttons */
- char dt;
+ /** UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to #uiBut.emboss */
+ char emboss;
bool auto_open;
char _pad[5];
double auto_open_last;
@@ -516,10 +517,10 @@ extern void ui_but_string_get(uiBut *but, char *str, const size_t maxlen) ATTR_N
extern char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size);
extern void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) ATTR_NONNULL();
extern bool ui_but_string_set(struct bContext *C, uiBut *but, const char *str) ATTR_NONNULL();
-extern bool ui_but_string_set_eval_num(struct bContext *C,
- uiBut *but,
- const char *str,
- double *value) ATTR_NONNULL();
+extern bool ui_but_string_eval_number(struct bContext *C,
+ const uiBut *but,
+ const char *str,
+ double *value) ATTR_NONNULL();
extern int ui_but_string_get_max_length(uiBut *but);
/* Clear & exit the active button's string. */
extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL();
@@ -548,7 +549,7 @@ extern bool ui_but_supports_cycling(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern int ui_but_is_pushed_ex(uiBut *but, double *value) ATTR_WARN_UNUSED_RESULT;
extern int ui_but_is_pushed(uiBut *but) ATTR_WARN_UNUSED_RESULT;
-void ui_but_override_flag(uiBut *but);
+void ui_but_override_flag(struct Main *bmain, uiBut *but);
extern void ui_block_bounds_calc(uiBlock *block);
@@ -848,6 +849,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 +864,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]);
@@ -937,7 +939,7 @@ int ui_but_align_opposite_to_area_align_get(const struct ARegion *region) ATTR_W
void ui_block_align_calc(uiBlock *block, const struct ARegion *region);
/* interface_anim.c */
-void ui_but_anim_flag(uiBut *but, float cfra);
+void ui_but_anim_flag(uiBut *but, const struct AnimationEvalContext *anim_eval_context);
void ui_but_anim_copy_driver(struct bContext *C);
void ui_but_anim_paste_driver(struct bContext *C);
bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 196fdf47bd3..e82a42d9ad7 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -250,28 +250,23 @@ static int ui_item_fit(
if (is_last) {
return available - pos;
}
- else {
- float width = *extra_pixel + (item * available) / (float)all;
- *extra_pixel = width - (int)width;
- return (int)width;
- }
+
+ float width = *extra_pixel + (item * available) / (float)all;
+ *extra_pixel = width - (int)width;
+ return (int)width;
}
- else {
- /* contents is smaller or equal to available space */
- if (alignment == UI_LAYOUT_ALIGN_EXPAND) {
- if (is_last) {
- return available - pos;
- }
- else {
- float width = *extra_pixel + (item * available) / (float)all;
- *extra_pixel = width - (int)width;
- return (int)width;
- }
- }
- else {
- return item;
+
+ /* contents is smaller or equal to available space */
+ if (alignment == UI_LAYOUT_ALIGN_EXPAND) {
+ if (is_last) {
+ return available - pos;
}
+
+ float width = *extra_pixel + (item * available) / (float)all;
+ *extra_pixel = width - (int)width;
+ return (int)width;
}
+ return item;
}
/* variable button size in which direction? */
@@ -323,9 +318,7 @@ static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool
}
return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin);
}
- else {
- return unit_x * 10;
- }
+ return unit_x * 10;
}
static void ui_item_size(uiItem *item, int *r_w, int *r_h)
@@ -681,7 +674,7 @@ static void ui_item_array(uiLayout *layout,
/* show checkboxes for rna on a non-emboss block (menu for eg) */
if (type == PROP_BOOLEAN &&
- ELEM(layout->root->block->dt, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) {
+ ELEM(layout->root->block->emboss, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) {
boolarr = MEM_callocN(sizeof(bool) * len, __func__);
RNA_property_boolean_get_array(ptr, prop, boolarr);
}
@@ -954,8 +947,11 @@ static uiBut *ui_item_with_label(uiLayout *layout,
const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
#endif
- /* Always align item with label since text is already given enough space not to overlap. */
- sub = uiLayoutRow(layout, true);
+ /* Previously 'align' was enabled to make sure the label is spaced closely to the button.
+ * Set the space to zero instead as aligning a large number of labels can end up aligning
+ * thousands of buttons when displaying key-map search (a heavy operation), see: T78636. */
+ sub = uiLayoutRow(layout, false);
+ sub->space = 0;
UI_block_layout_set_current(block, sub);
#ifdef UI_PROP_DECORATE
@@ -1069,7 +1065,8 @@ static uiBut *ui_item_with_label(uiLayout *layout,
void UI_context_active_but_prop_get_filebrowser(const bContext *C,
PointerRNA *r_ptr,
PropertyRNA **r_prop,
- bool *r_is_undo)
+ bool *r_is_undo,
+ bool *r_is_userdef)
{
ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C);
uiBlock *block;
@@ -1078,6 +1075,7 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C,
memset(r_ptr, 0, sizeof(*r_ptr));
*r_prop = NULL;
*r_is_undo = false;
+ *r_is_userdef = false;
if (!region) {
return;
@@ -1096,6 +1094,7 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C,
*r_ptr = prevbut->rnapoin;
*r_prop = prevbut->rnaprop;
*r_is_undo = (prevbut->flag & UI_BUT_UNDO) != 0;
+ *r_is_userdef = UI_but_is_userdef(prevbut);
return;
}
}
@@ -1455,9 +1454,7 @@ void uiItemsFullEnumO_items(uiLayout *layout,
/* break since rest of items is handled in new pie level */
break;
}
- else {
- last_iter = true;
- }
+ last_iter = true;
}
else {
continue;
@@ -2333,7 +2330,7 @@ void uiItemFullR(uiLayout *layout,
/* Mark non-embossed textfields inside a listbox. */
if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) &&
- (but->dt & UI_EMBOSS_NONE)) {
+ (but->emboss & UI_EMBOSS_NONE)) {
UI_but_flag_enable(but, UI_BUT_LIST_ITEM);
}
@@ -2576,43 +2573,42 @@ void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname
RNA_warning("not an enum property: %s.%s", RNA_struct_identifier(ptr->type), propname);
return;
}
- else {
- const EnumPropertyItem *item;
- int totitem, i;
- bool free;
- uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
- uiLayout *column = uiLayoutColumn(split, false);
- RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item, &totitem, &free);
-
- for (i = 0; i < totitem; i++) {
- if (item[i].identifier[0]) {
- uiItemEnumR_prop(column, item[i].name, item[i].icon, ptr, prop, item[i].value);
- ui_but_tip_from_enum_item(block->buttons.last, &item[i]);
- }
- else {
- if (item[i].name) {
- if (i != 0) {
- column = uiLayoutColumn(split, false);
- /* inconsistent, but menus with labels do not look good flipped */
- block->flag |= UI_BLOCK_NO_FLIP;
- }
+ const EnumPropertyItem *item;
+ int totitem, i;
+ bool free;
+ uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
+ uiLayout *column = uiLayoutColumn(split, false);
- uiItemL(column, item[i].name, ICON_NONE);
- bt = block->buttons.last;
- bt->drawflag = UI_BUT_TEXT_LEFT;
+ RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item, &totitem, &free);
- ui_but_tip_from_enum_item(bt, &item[i]);
- }
- else {
- uiItemS(column);
+ for (i = 0; i < totitem; i++) {
+ if (item[i].identifier[0]) {
+ uiItemEnumR_prop(column, item[i].name, item[i].icon, ptr, prop, item[i].value);
+ ui_but_tip_from_enum_item(block->buttons.last, &item[i]);
+ }
+ else {
+ if (item[i].name) {
+ if (i != 0) {
+ column = uiLayoutColumn(split, false);
+ /* inconsistent, but menus with labels do not look good flipped */
+ block->flag |= UI_BLOCK_NO_FLIP;
}
+
+ uiItemL(column, item[i].name, ICON_NONE);
+ bt = block->buttons.last;
+ bt->drawflag = UI_BUT_TEXT_LEFT;
+
+ ui_but_tip_from_enum_item(bt, &item[i]);
+ }
+ else {
+ uiItemS(column);
}
}
+ }
- if (free) {
- MEM_freeN((void *)item);
- }
+ if (free) {
+ MEM_freeN((void *)item);
}
/* intentionally don't touch UI_BLOCK_IS_FLIP here,
@@ -3196,15 +3192,14 @@ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int
return split_wrapper.decorate_column;
}
- else {
- char namestr[UI_MAX_NAME_STR];
- if (text) {
- text = ui_item_name_add_colon(text, namestr);
- }
- uiItemL_(layout, text, icon);
- return layout;
+ char namestr[UI_MAX_NAME_STR];
+ if (text) {
+ text = ui_item_name_add_colon(text, namestr);
}
+ uiItemL_(layout, text, icon);
+
+ return layout;
}
void uiItemLDrag(uiLayout *layout, PointerRNA *ptr, const char *name, int icon)
@@ -3833,7 +3828,7 @@ static void ui_litem_layout_radial(uiLayout *litem)
bitem->but->rect.xmax += 1.5f * UI_UNIT_X;
/* enable drawing as pie item if supported by widget */
if (ui_item_is_radial_drawable(bitem)) {
- bitem->but->dt = UI_EMBOSS_RADIAL;
+ bitem->but->emboss = UI_EMBOSS_RADIAL;
bitem->but->drawflag |= UI_BUT_ICON_LEFT;
}
}
@@ -5041,11 +5036,9 @@ float uiLayoutGetUnitsY(uiLayout *layout)
int uiLayoutGetEmboss(uiLayout *layout)
{
if (layout->emboss == UI_EMBOSS_UNDEFINED) {
- return layout->root->block->dt;
- }
- else {
- return layout->emboss;
+ return layout->root->block->emboss;
}
+ return layout->emboss;
}
/** \} */
@@ -5418,7 +5411,7 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but)
}
if (layout->emboss != UI_EMBOSS_UNDEFINED) {
- but->dt = layout->emboss;
+ but->emboss = layout->emboss;
}
}
@@ -5513,9 +5506,7 @@ MenuType *UI_but_menutype_get(uiBut *but)
if (but->menu_create_func == ui_item_menutype_func) {
return (MenuType *)but->poin;
}
- else {
- return NULL;
- }
+ return NULL;
}
/* this is a bit of a hack but best keep it in one place at least */
@@ -5524,9 +5515,7 @@ PanelType *UI_but_paneltype_get(uiBut *but)
if (but->menu_create_func == ui_item_paneltype_func) {
return (PanelType *)but->poin;
}
- else {
- return NULL;
- }
+ return NULL;
}
void UI_menutype_draw(bContext *C, MenuType *mt, struct uiLayout *layout)
@@ -5551,6 +5540,26 @@ void UI_menutype_draw(bContext *C, MenuType *mt, struct uiLayout *layout)
}
}
+static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *pt)
+{
+ LISTBASE_FOREACH (uiItem *, subitem, &layout->items) {
+ if (subitem->type == ITEM_BUTTON) {
+ uiButtonItem *bitem = (uiButtonItem *)subitem;
+ if (!(bitem->but->flag & UI_HIDDEN) && STREQ(bitem->but->str, pt->label)) {
+ return true;
+ }
+ }
+ else {
+ uiLayout *litem = (uiLayout *)subitem;
+ if (ui_layout_has_panel_label(litem, pt)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout, bool show_header)
{
Panel *panel = MEM_callocN(sizeof(Panel), "popover panel");
@@ -5567,12 +5576,19 @@ static void ui_paneltype_draw_impl(bContext *C, PanelType *pt, uiLayout *layout,
pt->draw_header(C, panel);
panel->layout = NULL;
}
- uiItemL(row, CTX_IFACE_(pt->translation_context, pt->label), ICON_NONE);
+
+ /* draw_header() is often used to add a checkbox to the header. If we add the label like below
+ * the label is disconnected from the checkbox, adding a weird looking gap. As workaround, let
+ * the checkbox add the label instead. */
+ if (!ui_layout_has_panel_label(row, pt)) {
+ uiItemL(row, CTX_IFACE_(pt->translation_context, pt->label), ICON_NONE);
+ }
}
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..39c1b8bb909 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -209,10 +209,9 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op)
MEM_freeN(path);
return OPERATOR_FINISHED;
}
- else {
- BKE_reportf(op->reports, RPT_ERROR, "Could not compute a valid data path");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_reportf(op->reports, RPT_ERROR, "Could not compute a valid data path");
+ return OPERATOR_CANCELLED;
}
return OPERATOR_CANCELLED;
@@ -312,9 +311,7 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert
/* do nothing, go ahead with undo */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static bool reset_default_button_poll(bContext *C)
@@ -513,7 +510,8 @@ 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(
+ CTX_data_main(C), &ptr, prop, index);
return (ptr.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE));
}
@@ -559,7 +557,7 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op)
}
IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_get(
- &ptr, prop, operation, index, true, NULL, &created);
+ CTX_data_main(C), &ptr, prop, operation, index, true, NULL, &created);
if (!created) {
opop->operation = operation;
}
@@ -613,7 +611,8 @@ 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(
+ CTX_data_main(C), &ptr, prop, index);
return (ptr.data && ptr.owner_id && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN));
}
@@ -630,11 +629,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(bmain, &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. */
@@ -1005,11 +1004,9 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
success = true;
break;
}
- else {
- if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) {
- RNA_property_update(C, &lptr, prop);
- success = true;
- }
+ if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) {
+ RNA_property_update(C, &lptr, prop);
+ success = true;
}
}
}
@@ -1149,7 +1146,7 @@ static bool jump_to_target_button(bContext *C, bool poll)
return jump_to_target_ptr(C, target_ptr, poll);
}
/* For string properties with prop_search, look up the search collection item. */
- else if (type == PROP_STRING) {
+ if (type == PROP_STRING) {
const uiBut *but = UI_context_active_but_get(C);
if (but->type == UI_BTYPE_SEARCH_MENU && but->search &&
@@ -1265,9 +1262,7 @@ static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b)
STREQLEN(but_a->drawstr, but_b->drawstr, UI_MAX_DRAW_STR)) {
return true;
}
- else {
- return false;
- }
+ return false;
}
void UI_editsource_active_but_test(uiBut *but)
@@ -1309,7 +1304,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;
}
}
@@ -1323,22 +1318,21 @@ static int editsource_text_edit(bContext *C,
BKE_reportf(op->reports, RPT_WARNING, "File '%s' cannot be opened", filepath);
return OPERATOR_CANCELLED;
}
- else {
- /* naughty!, find text area to set, not good behavior
- * but since this is a dev tool lets allow it - campbell */
- ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
- if (area) {
- SpaceText *st = area->spacedata.first;
- st->text = text;
- }
- else {
- BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
- }
- txt_move_toline(text, line - 1, false);
- WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
+ /* naughty!, find text area to set, not good behavior
+ * but since this is a dev tool lets allow it - campbell */
+ ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
+ if (area) {
+ SpaceText *st = area->spacedata.first;
+ st->text = text;
+ }
+ else {
+ BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
}
+ txt_move_toline(text, line - 1, false);
+ WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
+
return OPERATOR_FINISHED;
}
@@ -1397,10 +1391,9 @@ static int editsource_exec(bContext *C, wmOperator *op)
return ret;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Active button not found");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "Active button not found");
+ return OPERATOR_CANCELLED;
}
static void UI_OT_editsource(wmOperatorType *ot)
@@ -1585,10 +1578,9 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
return ret;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Active button not found");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "Active button not found");
+ return OPERATOR_CANCELLED;
}
static void UI_OT_edittranslation_init(wmOperatorType *ot)
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 5f56a93c5eb..dd3074d6258 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -147,22 +147,22 @@ static int panel_aligned(const ScrArea *area, const ARegion *region)
if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) {
return BUT_VERTICAL;
}
- else if (area->spacetype == SPACE_USERPREF && region->regiontype == RGN_TYPE_WINDOW) {
+ if (area->spacetype == SPACE_USERPREF && region->regiontype == RGN_TYPE_WINDOW) {
return BUT_VERTICAL;
}
- else if (area->spacetype == SPACE_FILE && region->regiontype == RGN_TYPE_CHANNELS) {
+ if (area->spacetype == SPACE_FILE && region->regiontype == RGN_TYPE_CHANNELS) {
return BUT_VERTICAL;
}
- else if (area->spacetype == SPACE_IMAGE && region->regiontype == RGN_TYPE_PREVIEW) {
+ if (area->spacetype == SPACE_IMAGE && region->regiontype == RGN_TYPE_PREVIEW) {
return BUT_VERTICAL;
}
- else if (ELEM(region->regiontype,
- RGN_TYPE_UI,
- RGN_TYPE_TOOLS,
- RGN_TYPE_TOOL_PROPS,
- RGN_TYPE_HUD,
- RGN_TYPE_NAV_BAR,
- RGN_TYPE_EXECUTE)) {
+ if (ELEM(region->regiontype,
+ RGN_TYPE_UI,
+ RGN_TYPE_TOOLS,
+ RGN_TYPE_TOOL_PROPS,
+ RGN_TYPE_HUD,
+ RGN_TYPE_NAV_BAR,
+ RGN_TYPE_EXECUTE)) {
return BUT_VERTICAL;
}
@@ -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.
@@ -354,15 +364,28 @@ static void panel_delete(ARegion *region, ListBase *panels, Panel *panel)
MEM_freeN(panel);
}
+/**
+ * Remove instanced panels from the region's panel list.
+ *
+ * \note Can be called with NULL \a C, but it should be avoided because
+ * handlers might not be removed.
+ */
void UI_panels_free_instanced(bContext *C, ARegion *region)
{
/* Delete panels with the instanced flag. */
LISTBASE_FOREACH_MUTABLE (Panel *, panel, &region->panels) {
if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED)) {
/* Make sure the panel's handler is removed before deleting it. */
- if (panel->activedata != NULL) {
+ 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);
}
}
@@ -381,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. */
@@ -487,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)
{
@@ -507,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.
*/
@@ -577,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,
@@ -586,9 +639,8 @@ static void panels_collapse_all(const bContext *C,
const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL;
const int flag = ((panel_aligned(area, region) == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY);
const PanelType *from_pt = from_panel->type;
- Panel *panel;
- for (panel = region->panels.first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
PanelType *pt = panel->type;
/* close panels with headers in the same context */
@@ -615,10 +667,9 @@ static bool panel_type_context_poll(PanelType *panel_type, const char *context)
Panel *UI_panel_find_by_type(ListBase *lb, PanelType *pt)
{
- Panel *panel;
const char *idname = pt->idname;
- for (panel = lb->first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, lb) {
if (STREQLEN(panel->panelname, idname, sizeof(panel->panelname))) {
return panel;
}
@@ -637,7 +688,7 @@ Panel *UI_panel_begin(ScrArea *area,
Panel *panel,
bool *r_open)
{
- Panel *panel_last, *panel_next;
+ Panel *panel_last;
const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
const char *idname = pt->idname;
const bool newpanel = (panel == NULL);
@@ -698,7 +749,7 @@ Panel *UI_panel_begin(ScrArea *area,
if (newpanel) {
panel->sortorder = (panel_last) ? panel_last->sortorder + 1 : 0;
- for (panel_next = lb->first; panel_next; panel_next = panel_next->next) {
+ LISTBASE_FOREACH (Panel *, panel_next, lb) {
if (panel_next != panel && panel_next->sortorder >= panel->sortorder) {
panel_next->sortorder++;
}
@@ -827,79 +878,8 @@ void UI_draw_icon_tri(float x, float y, char dir, const float color[4])
}
}
-static void ui_draw_anti_x(uint pos, float x1, float y1, float x2, float y2)
-{
-
- /* set antialias line */
- GPU_line_smooth(true);
- GPU_blend(true);
-
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 4);
-
- immVertex2f(pos, x1, y1);
- immVertex2f(pos, x2, y2);
-
- immVertex2f(pos, x1, y2);
- immVertex2f(pos, x2, y1);
-
- immEnd();
-
- GPU_line_smooth(false);
- GPU_blend(false);
-}
-
-/* x 'icon' for panel header */
-static void ui_draw_x_icon(uint pos, float x, float y)
-{
-
- ui_draw_anti_x(pos, x, y, x + 9.375f, y + 9.375f);
-}
-
#define PNL_ICON UI_UNIT_X /* could be UI_UNIT_Y too */
-static void ui_draw_panel_scalewidget(uint pos, const rcti *rect)
-{
- float xmin, xmax, dx;
- float ymin, ymax, dy;
-
- xmin = rect->xmax - PNL_HEADER + 2;
- xmax = rect->xmax - 3;
- ymin = rect->ymin + 3;
- ymax = rect->ymin + PNL_HEADER - 2;
-
- dx = 0.5f * (xmax - xmin);
- dy = 0.5f * (ymax - ymin);
-
- GPU_blend(true);
- immUniformColor4ub(255, 255, 255, 50);
-
- immBegin(GPU_PRIM_LINES, 4);
-
- immVertex2f(pos, xmin, ymin);
- immVertex2f(pos, xmax, ymax);
-
- immVertex2f(pos, xmin + dx, ymin);
- immVertex2f(pos, xmax, ymax - dy);
-
- immEnd();
-
- immUniformColor4ub(0, 0, 0, 50);
-
- immBegin(GPU_PRIM_LINES, 4);
-
- immVertex2f(pos, xmin, ymin + 1);
- immVertex2f(pos, xmax, ymax + 1);
-
- immVertex2f(pos, xmin + dx, ymin + 1);
- immVertex2f(pos, xmax, ymax - dy + 1);
-
- immEnd();
-
- GPU_blend(false);
-}
-
/* For button layout next to label. */
void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y)
{
@@ -926,12 +906,7 @@ static void ui_draw_aligned_panel_header(
uchar col_title[4];
/* + 0.001f to avoid flirting with float inaccuracy */
- if (panel->control & UI_PNL_CLOSE) {
- pnl_icons = (panel->labelofs + (2.0f * PNL_ICON)) / block->aspect + 0.001f;
- }
- else {
- pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f;
- }
+ pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f;
/* draw text label */
panel_title_color_get(show_background, col_title);
@@ -976,7 +951,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;
@@ -1127,11 +1102,7 @@ void ui_draw_aligned_panel(uiStyle *style,
/* in some occasions, draw a border */
if (panel->flag & PNL_SELECT && !is_subpanel) {
float radius;
- if (panel->control & UI_PNL_SOLID) {
- UI_draw_roundbox_corner_set(UI_CNR_ALL);
- radius = 8.0f;
- }
- else if (draw_box_style) {
+ if (draw_box_style) {
UI_draw_roundbox_corner_set(UI_CNR_ALL);
radius = box_wcol->roundness * U.widget_unit;
}
@@ -1156,7 +1127,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);
@@ -1182,26 +1153,12 @@ void ui_draw_aligned_panel(uiStyle *style,
}
}
- if (panel->control & UI_PNL_SCALE) {
- ui_draw_panel_scalewidget(pos, rect);
- }
-
immUnbindProgram();
}
uchar col_title[4];
panel_title_color_get(show_background, col_title);
- /* draw optional close icon */
-
- if (panel->control & UI_PNL_CLOSE) {
- const int ofsx = 6;
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformColor3ubv(col_title);
- ui_draw_x_icon(pos, rect->xmin + 2 + ofsx, rect->ymax + 2);
- immUnbindProgram();
- }
-
/* draw collapse icon */
/* itemrect smaller */
@@ -1272,9 +1229,7 @@ static int get_panel_real_ofsy(Panel *panel)
if (panel->flag & PNL_CLOSEDY) {
return panel->ofsy + panel->sizey;
}
- else {
- return panel->ofsy;
- }
+ return panel->ofsy;
}
static int get_panel_real_ofsx(Panel *panel)
@@ -1282,9 +1237,7 @@ static int get_panel_real_ofsx(Panel *panel)
if (panel->flag & PNL_CLOSEDX) {
return panel->ofsx + get_panel_header(panel);
}
- else {
- return panel->ofsx + panel->sizex;
- }
+ return panel->ofsx + panel->sizex;
}
bool UI_panel_is_dragging(const struct Panel *panel)
@@ -1312,13 +1265,13 @@ static int find_leftmost_panel(const void *a1, const void *a2)
if (ps1->panel->ofsx > ps2->panel->ofsx) {
return 1;
}
- else if (ps1->panel->ofsx < ps2->panel->ofsx) {
+ if (ps1->panel->ofsx < ps2->panel->ofsx) {
return -1;
}
- else if (ps1->panel->sortorder > ps2->panel->sortorder) {
+ if (ps1->panel->sortorder > ps2->panel->sortorder) {
return 1;
}
- else if (ps1->panel->sortorder < ps2->panel->sortorder) {
+ if (ps1->panel->sortorder < ps2->panel->sortorder) {
return -1;
}
@@ -1334,23 +1287,23 @@ static int find_highest_panel(const void *a1, const void *a2)
if (ps1->panel->type->flag & PNL_NO_HEADER && ps2->panel->type->flag & PNL_NO_HEADER) {
/* skip and check for ofs and sortorder below */
}
- else if (ps1->panel->type->flag & PNL_NO_HEADER) {
+ if (ps1->panel->type->flag & PNL_NO_HEADER) {
return -1;
}
- else if (ps2->panel->type->flag & PNL_NO_HEADER) {
+ if (ps2->panel->type->flag & PNL_NO_HEADER) {
return 1;
}
if (ps1->panel->ofsy + ps1->panel->sizey < ps2->panel->ofsy + ps2->panel->sizey) {
return 1;
}
- else if (ps1->panel->ofsy + ps1->panel->sizey > ps2->panel->ofsy + ps2->panel->sizey) {
+ if (ps1->panel->ofsy + ps1->panel->sizey > ps2->panel->ofsy + ps2->panel->sizey) {
return -1;
}
- else if (ps1->panel->sortorder > ps2->panel->sortorder) {
+ if (ps1->panel->sortorder > ps2->panel->sortorder) {
return 1;
}
- else if (ps1->panel->sortorder < ps2->panel->sortorder) {
+ if (ps1->panel->sortorder < ps2->panel->sortorder) {
return -1;
}
@@ -1364,7 +1317,7 @@ static int compare_panel(const void *a1, const void *a2)
if (ps1->panel->sortorder > ps2->panel->sortorder) {
return 1;
}
- else if (ps1->panel->sortorder < ps2->panel->sortorder) {
+ if (ps1->panel->sortorder < ps2->panel->sortorder) {
return -1;
}
@@ -1393,14 +1346,13 @@ static void align_sub_panels(Panel *panel)
/* returns 1 when it did something */
static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, const bool drag)
{
- Panel *panel;
PanelSort *ps, *panelsort, *psnext;
int a, tot = 0;
bool done;
int align = panel_aligned(area, region);
/* count active, not tabbed panels */
- for (panel = region->panels.first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
tot++;
}
@@ -1411,7 +1363,7 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
}
/* extra; change close direction? */
- for (panel = region->panels.first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
if ((panel->flag & PNL_CLOSEDX) && (align == BUT_VERTICAL)) {
panel->flag ^= PNL_CLOSED;
@@ -1426,7 +1378,7 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort");
ps = panelsort;
- for (panel = region->panels.first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
ps->panel = MEM_dupallocN(panel);
ps->orig = panel;
@@ -1458,11 +1410,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;
@@ -1472,16 +1419,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);
@@ -1489,6 +1432,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;
@@ -1506,7 +1453,7 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
}
/* set locations for tabbed and sub panels */
- for (panel = region->panels.first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
if (panel->children.first) {
align_sub_panels(panel);
@@ -1525,13 +1472,12 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co
static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y)
{
- Panel *panel;
int align = panel_aligned(area, region);
int sizex = 0;
int sizey = 0;
/* compute size taken up by panels, for setting in view2d */
- for (panel = region->panels.first; panel; panel = panel->next) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PNL_ACTIVE) {
int pa_sizex, pa_sizey;
@@ -1617,11 +1563,10 @@ void UI_panels_begin(const bContext *UNUSED(C), ARegion *region)
void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
{
ScrArea *area = CTX_wm_area(C);
- uiBlock *block;
Panel *panel, *panel_first;
/* offset contents */
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
if (block->active && block->panel) {
ui_offset_panel_block(block);
}
@@ -1639,7 +1584,7 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
/* tag first panel */
panel_first = NULL;
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
if (block->active && block->panel) {
if (!panel_first || block->panel->sortorder < panel_first->sortorder) {
panel_first = block->panel;
@@ -1657,8 +1602,6 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
void UI_panels_draw(const bContext *C, ARegion *region)
{
- uiBlock *block;
-
if (region->alignment != RGN_ALIGN_FLOAT) {
UI_ThemeClearColor(TH_BACK);
}
@@ -1666,13 +1609,13 @@ void UI_panels_draw(const bContext *C, ARegion *region)
/* Draw panels, selected on top. Also in reverse order, because
* UI blocks are added in reverse order and we need child panels
* to draw on top. */
- for (block = region->uiblocks.last; block; block = block->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
if (block->active && block->panel && !(block->panel->flag & PNL_SELECT)) {
UI_block_draw(C, block);
}
}
- for (block = region->uiblocks.last; block; block = block->prev) {
+ LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->uiblocks) {
if (block->active && block->panel && (block->panel->flag & PNL_SELECT)) {
UI_block_draw(C, block);
}
@@ -1681,15 +1624,12 @@ void UI_panels_draw(const bContext *C, ARegion *region)
void UI_panels_scale(ARegion *region, float new_width)
{
- uiBlock *block;
- uiBut *but;
-
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
if (block->panel) {
float fac = new_width / (float)block->panel->sizex;
block->panel->sizex = new_width;
- for (but = block->buttons.first; but; but = but->next) {
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
but->rect.xmin *= fac;
but->rect.xmax *= fac;
}
@@ -1697,48 +1637,6 @@ void UI_panels_scale(ARegion *region, float new_width)
}
}
-/* ------------ panel merging ---------------- */
-
-static void check_panel_overlap(ARegion *region, Panel *panel)
-{
- Panel *panel_list;
-
- /* also called with (panel == NULL) for clear */
-
- for (panel_list = region->panels.first; panel_list; panel_list = panel_list->next) {
- panel_list->flag &= ~PNL_OVERLAP;
- if (panel && (panel_list != panel)) {
- if (panel_list->runtime_flag & PNL_ACTIVE) {
- float safex = 0.2, safey = 0.2;
-
- if (panel_list->flag & PNL_CLOSEDX) {
- safex = 0.05;
- }
- else if (panel_list->flag & PNL_CLOSEDY) {
- safey = 0.05;
- }
- else if (panel->flag & PNL_CLOSEDX) {
- safex = 0.05;
- }
- else if (panel->flag & PNL_CLOSEDY) {
- safey = 0.05;
- }
-
- if (panel_list->ofsx > panel->ofsx - safex * panel->sizex) {
- if (panel_list->ofsx + panel_list->sizex < panel->ofsx + (1.0f + safex) * panel->sizex) {
- if (panel_list->ofsy > panel->ofsy - safey * panel->sizey) {
- if (panel_list->ofsy + panel_list->sizey <
- panel->ofsy + (1.0f + safey) * panel->sizey) {
- panel_list->flag |= PNL_OVERLAP;
- }
- }
- }
- }
- }
- }
- }
-}
-
/************************ panel dragging ****************************/
#define DRAG_REGION_PAD (PNL_HEADER * 0.5)
@@ -1779,7 +1677,6 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
dy += ((float)region->v2d.cur.ymin - data->start_cur_ymin);
panel->ofsx = data->startofsx + round_fl_to_int(dx);
panel->ofsy = data->startofsy + round_fl_to_int(dy);
- check_panel_overlap(region, panel);
if (align) {
uiAlignPanelStep(area, region, 0.2f, true);
@@ -1812,13 +1709,6 @@ static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block,
}
/* open panel */
else if (!(panel->flag & PNL_CLOSEDY)) {
- if (panel->control & UI_PNL_SCALE) {
- if (block->rect.xmax - PNL_HEADER <= mx) {
- if (block->rect.ymin + PNL_HEADER >= my) {
- return PANEL_MOUSE_INSIDE_SCALE;
- }
- }
- }
if ((block->rect.xmin <= mx) && (block->rect.xmax >= mx)) {
if ((block->rect.ymin <= my) && (block->rect.ymax + PNL_HEADER >= my)) {
return PANEL_MOUSE_INSIDE_CONTENT;
@@ -1845,10 +1735,9 @@ static void ui_panel_drag_collapse(bContext *C,
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- uiBlock *block;
Panel *panel;
- for (block = region->uiblocks.first; block; block = block->next) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)};
float xy_b_block[2] = {UNPACK2(xy_dst)};
rctf rect = block->rect;
@@ -2005,15 +1894,6 @@ static void ui_handle_panel_header(
button = 1;
}
}
- else if (block->panel->control & UI_PNL_CLOSE) {
- /* whole of header can be used to collapse panel (except top-right corner) */
- if (mx <= block->rect.xmax - 8 - PNL_ICON) {
- button = 2;
- }
- // else if (mx <= block->rect.xmin + 10 + 2 * PNL_ICON + 2) {
- // button = 1;
- //}
- }
else if (mx < rect_leftmost) {
button = 1;
}
@@ -2022,14 +1902,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;
+ }
}
}
@@ -2160,9 +2054,7 @@ void UI_panel_category_active_set_default(ARegion *region, const char *idname)
const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
{
- PanelCategoryStack *pc_act;
-
- for (pc_act = region->panels_category_active.first; pc_act; pc_act = pc_act->next) {
+ LISTBASE_FOREACH (PanelCategoryStack *, pc_act, &region->panels_category_active) {
if (UI_panel_category_find(region, pc_act->idname)) {
return pc_act->idname;
}
@@ -2181,9 +2073,7 @@ const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
PanelCategoryDyn *UI_panel_category_find_mouse_over_ex(ARegion *region, const int x, const int y)
{
- PanelCategoryDyn *ptd;
-
- for (ptd = region->panels_category.first; ptd; ptd = ptd->next) {
+ LISTBASE_FOREACH (PanelCategoryDyn *, ptd, &region->panels_category) {
if (BLI_rcti_isect_pt(&ptd->rect, x, y)) {
return ptd;
}
@@ -2351,7 +2241,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
const uiFontStyle *fstyle = &style->widget;
const int fontid = fstyle->uifont_id;
short fstyle_points = fstyle->points;
- PanelCategoryDyn *pc_dyn;
const float aspect = ((uiBlock *)region->uiblocks.first)->aspect;
const float zoom = 1.0f / aspect;
const int px = max_ii(1, round_fl_to_int(U.pixelsize));
@@ -2428,7 +2317,8 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
}
/* calculate tab rect's and check if we need to scale down */
- for (pc_dyn = region->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
+ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
+
rcti *rct = &pc_dyn->rect;
const char *category_id = pc_dyn->idname;
const char *category_id_draw = IFACE_(category_id);
@@ -2446,7 +2336,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
- for (pc_dyn = region->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
+ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
rcti *rct = &pc_dyn->rect;
rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
@@ -2491,7 +2381,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) :
(v2d->mask.xmax - (category_tabs_width + px)) + px;
- for (pc_dyn = region->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
+ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
const rcti *rct = &pc_dyn->rect;
const char *category_id = pc_dyn->idname;
const char *category_id_draw = IFACE_(category_id);
@@ -2687,7 +2577,6 @@ int ui_handler_panel_region(bContext *C,
ARegion *region,
const uiBut *active_but)
{
- uiBlock *block;
Panel *panel;
int retval, mx, my;
bool has_category_tabs = UI_panel_category_is_visible(region);
@@ -2726,7 +2615,7 @@ int ui_handler_panel_region(bContext *C,
return retval;
}
- for (block = region->uiblocks.last; block; block = block->prev) {
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
uiPanelMouseState mouse_state;
mx = event->x;
@@ -2792,7 +2681,7 @@ int ui_handler_panel_region(bContext *C,
retval = WM_UI_HANDLER_BREAK;
break;
}
- else if ((mouse_state == PANEL_MOUSE_INSIDE_SCALE) && !(panel->flag & PNL_CLOSED)) {
+ if ((mouse_state == PANEL_MOUSE_INSIDE_SCALE) && !(panel->flag & PNL_CLOSED)) {
panel_activate_state(C, panel, PANEL_STATE_DRAG_SCALE);
retval = WM_UI_HANDLER_BREAK;
break;
@@ -2805,51 +2694,6 @@ int ui_handler_panel_region(bContext *C,
break;
}
}
- else if (event->type == EVT_ESCKEY) {
- /*XXX 2.50*/
-#if 0
- if (block->handler) {
- rem_blockhandler(area, block->handler);
- ED_region_tag_redraw(region);
- retval = WM_UI_HANDLER_BREAK;
- }
-#endif
- }
- else if (event->type == EVT_PADPLUSKEY || event->type == EVT_PADMINUS) {
-#if 0 /* XXX make float panel exception? */
- int zoom = 0;
-
- /* if panel is closed, only zoom if mouse is over the header */
- if (panel->flag & (PNL_CLOSEDX | PNL_CLOSEDY)) {
- if (inside_header) {
- zoom = 1;
- }
- }
- else {
- zoom = 1;
- }
-
- if (zoom) {
- ScrArea *area = CTX_wm_area(C);
- SpaceLink *sl = area->spacedata.first;
-
- if (area->spacetype != SPACE_PROPERTIES) {
- if (!(panel->control & UI_PNL_SCALE)) {
- if (event->type == PADPLUSKEY) {
- sl->blockscale += 0.1;
- }
- else {
- sl->blockscale -= 0.1;
- }
- CLAMP(sl->blockscale, 0.6, 1.0);
-
- ED_region_tag_redraw(region);
- retval = WM_UI_HANDLER_BREAK;
- }
- }
- }
-#endif
- }
}
}
}
@@ -2857,6 +2701,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 */
@@ -2897,9 +2791,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
if (data && data->state == PANEL_STATE_ANIMATION) {
return WM_UI_HANDLER_CONTINUE;
}
- else {
- return WM_UI_HANDLER_BREAK;
- }
+ return WM_UI_HANDLER_BREAK;
}
static void ui_handler_remove_panel(bContext *C, void *userdata)
@@ -2909,24 +2801,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;
@@ -2939,22 +2813,13 @@ 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:
- * - the panel tabbing function call below (test_add_new_tabs()) has been commented out
- * "It is too easy to do by accident when reordering panels,
- * is very hard to control and use, and has no real benefit." - BillRey
- * Aligorith, 2009Sep
- */
- // test_add_new_tabs(region); // also copies locations of tabs in dragged panel
- 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_query.c b/source/blender/editors/interface/interface_query.c
index 6d05fe15f97..10b219202e5 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -90,7 +90,7 @@ bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
if (but->flag & UI_SCROLLED) {
return false;
}
- if ((but->type == UI_BTYPE_TEXT) && (but->dt == UI_EMBOSS_NONE) && !labeledit) {
+ if ((but->type == UI_BTYPE_TEXT) && (but->emboss == UI_EMBOSS_NONE) && !labeledit) {
return false;
}
if ((but->type == UI_BTYPE_LISTROW) && labeledit) {
@@ -107,9 +107,7 @@ bool UI_but_is_utf8(const uiBut *but)
const int subtype = RNA_property_subtype(but->rnaprop);
return !(ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING));
}
- else {
- return !(but->flag & UI_BUT_NO_UTF8);
- }
+ return !(but->flag & UI_BUT_NO_UTF8);
}
#ifdef USE_UI_POPOVER_ONCE
@@ -171,9 +169,7 @@ int ui_but_icon(const uiBut *but)
if (but->drawflag & UI_BUT_ICON_REVERSE) {
return but->icon - but->iconadd;
}
- else {
- return but->icon + but->iconadd;
- }
+ return but->icon + but->iconadd;
}
/** \} */
diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c
index 3e34b7f3f8a..077e8888d53 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.c
+++ b/source/blender/editors/interface/interface_region_menu_popup.c
@@ -77,11 +77,10 @@ int ui_but_menu_step(uiBut *but, int direction)
if (but->menu_step_func) {
return but->menu_step_func(but->block->evil_C, direction, but->poin);
}
- else {
- const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
- return RNA_property_enum_step(
- but->block->evil_C, &but->rnapoin, but->rnaprop, curval, direction);
- }
+
+ const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop);
+ return RNA_property_enum_step(
+ but->block->evil_C, &but->rnapoin, but->rnaprop, curval, direction);
}
printf("%s: cannot cycle button '%s'\n", __func__, but->str);
@@ -129,16 +128,15 @@ static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but)
mem[hash_mod] = ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR);
return NULL;
}
- else {
- /* get */
- for (but = block->buttons.first; but; but = but->next) {
- if (mem[hash_mod] == ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR)) {
- return but;
- }
- }
- return NULL;
+ /* get */
+ for (but = block->buttons.first; but; but = but->next) {
+ if (mem[hash_mod] == ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR)) {
+ return but;
+ }
}
+
+ return NULL;
}
uiBut *ui_popup_menu_memory_get(uiBlock *block)
@@ -184,7 +182,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 +196,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 +212,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
}
}
else {
- minwidth = UI_MENU_WIDTH_MIN;
direction = UI_DIR_DOWN;
}
@@ -478,13 +486,11 @@ bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup)
UI_popup_menu_end(C, pup);
return true;
}
- else {
- UI_block_layout_resolve(pup->block, NULL, NULL);
- MEM_freeN(pup->block->handle);
- UI_block_free(C, pup->block);
- MEM_freeN(pup);
- return false;
- }
+ UI_block_layout_resolve(pup->block, NULL, NULL);
+ MEM_freeN(pup->block->handle);
+ UI_block_free(C, pup->block);
+ MEM_freeN(pup);
+ return false;
}
uiLayout *UI_popup_menu_layout(uiPopupMenu *pup)
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index 68be08ef4f8..a9e87f4cc07 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -76,6 +76,10 @@ struct uiSearchItems {
void **pointers;
int *icons;
int *states;
+ uint8_t *name_prefix_offsets;
+
+ /** Is there any item with an icon? */
+ bool has_icon;
AutoComplete *autocpl;
void *active;
@@ -114,7 +118,12 @@ typedef struct uiSearchboxData {
* typically #UI_BUT_DISABLED / #UI_BUT_INACTIVE.
* \return false if there is nothing to add.
*/
-bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int state)
+bool UI_search_item_add(uiSearchItems *items,
+ const char *name,
+ void *poin,
+ int iconid,
+ int state,
+ const uint8_t name_prefix_offset)
{
/* hijack for autocomplete */
if (items->autocpl) {
@@ -122,6 +131,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) {
@@ -152,6 +165,15 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int
items->icons[items->totitem] = iconid;
}
+ if (name_prefix_offset != 0) {
+ /* Lazy initialize, as this isn't used often. */
+ if (items->name_prefix_offsets == NULL) {
+ items->name_prefix_offsets = MEM_callocN(
+ items->maxitem * sizeof(*items->name_prefix_offsets), "search name prefix offsets");
+ }
+ items->name_prefix_offsets[items->totitem] = name_prefix_offset;
+ }
+
/* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set
* which will cause problems, add others as needed. */
BLI_assert(
@@ -177,10 +199,18 @@ int UI_searchbox_size_x(void)
int UI_search_items_find_index(uiSearchItems *items, const char *name)
{
- int i;
- for (i = 0; i < items->totitem; i++) {
- if (STREQ(name, items->names[i])) {
- return i;
+ if (items->name_prefix_offsets != NULL) {
+ for (int i = 0; i < items->totitem; i++) {
+ if (STREQ(name, items->names[i] + items->name_prefix_offsets[i])) {
+ return i;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < items->totitem; i++) {
+ if (STREQ(name, items->names[i])) {
+ return i;
+ }
}
}
return -1;
@@ -276,7 +306,12 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region)
but->func_arg2 = NULL;
if (data->active != -1) {
- const char *name = data->items.names[data->active];
+ const char *name = data->items.names[data->active] +
+ /* Never include the prefix in the button. */
+ (data->items.name_prefix_offsets ?
+ data->items.name_prefix_offsets[data->active] :
+ 0);
+
const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL;
BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen);
@@ -285,16 +320,14 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region)
return true;
}
- else if (but->flag & UI_BUT_VALUE_CLEAR) {
+ if (but->flag & UI_BUT_VALUE_CLEAR) {
/* It is valid for _VALUE_CLEAR flavor to have no active element
* (it's a valid way to unlink). */
but->editstr[0] = '\0';
return true;
}
- else {
- return false;
- }
+ return false;
}
static struct ARegion *wm_searchbox_tooltip_init(struct bContext *C,
@@ -467,7 +500,10 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re
int a;
for (a = 0; a < data->items.totitem; a++) {
- const char *name = data->items.names[a];
+ const char *name = data->items.names[a] +
+ /* Never include the prefix in the button. */
+ (data->items.name_prefix_offsets ? data->items.name_prefix_offsets[a] :
+ 0);
const char *name_sep = data->use_sep ? strrchr(name, UI_SEP_CHAR) : NULL;
if (STREQLEN(but->editstr, name, name_sep ? (name_sep - name) : data->items.maxstrlen)) {
data->active = a;
@@ -562,6 +598,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);
@@ -625,6 +665,10 @@ static void ui_searchbox_region_free_cb(ARegion *region)
MEM_freeN(data->items.icons);
MEM_freeN(data->items.states);
+ if (data->items.name_prefix_offsets != NULL) {
+ MEM_freeN(data->items.name_prefix_offsets);
+ }
+
MEM_freeN(data);
region->regiondata = NULL;
}
@@ -793,6 +837,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but
data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers");
data->items.icons = MEM_callocN(data->items.maxitem * sizeof(int), "search icons");
data->items.states = MEM_callocN(data->items.maxitem * sizeof(int), "search flags");
+ data->items.name_prefix_offsets = NULL; /* Lazy initialized as needed. */
for (i = 0; i < data->items.maxitem; i++) {
data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers");
}
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index b64f080d9cc..7c64e4c2709 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -433,7 +433,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) {
+ else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (STREQ(expr_result, "")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -490,7 +490,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) {
+ else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
if (STREQ(expr_result, ".")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -594,7 +594,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
shortcut = BLI_strdup(has_valid_context_error);
}
- else if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) {
+ else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
if (expr_result != 0) {
wmKeyMap *keymap = (wmKeyMap *)expr_result;
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
@@ -659,7 +659,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* pass */
}
else if (BPY_execute_string_as_string_and_size(
- C, expr_imports, expr, true, &expr_result, &expr_result_len)) {
+ C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) {
/* pass. */
}
}
@@ -736,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
- else if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) {
+ else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
if (expr_result != 0) {
{
uiTooltipField *field = text_field_add(data,
@@ -763,9 +763,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
MEM_freeN(data);
return NULL;
}
- else {
- return data;
- }
+ return data;
}
static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
@@ -923,7 +921,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
.style = UI_TIP_STYLE_NORMAL,
.color_id = UI_TIP_LC_NORMAL,
});
- field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->name);
+ field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->filepath);
}
}
}
@@ -1045,9 +1043,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
MEM_freeN(data);
return NULL;
}
- else {
- return data;
- }
+ return data;
}
static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
@@ -1144,9 +1140,7 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
MEM_freeN(data);
return NULL;
}
- else {
- return data;
- }
+ return data;
}
static ARegion *ui_tooltip_create_with_data(bContext *C,
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index 2c6b09168f4..0708714c659 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -346,7 +346,7 @@ static void menu_types_add_from_keymap_items(bContext *C,
continue;
}
- else if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) {
+ if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) {
wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
if (keymap && WM_keymap_poll(C, keymap)) {
@@ -469,6 +469,34 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
}
}
+ {
+ /* Exclude context menus because:
+ * - The menu items are available elsewhere (and will show up multiple times).
+ * - Menu items depend on exact context, making search results unpredictable
+ * (exact number of items selected for example). See design doc T74158.
+ * There is one exception,
+ * as the outliner only exposes functionality via the context menu. */
+ GHashIterator iter;
+
+ for (WM_menutype_iter(&iter); (!BLI_ghashIterator_done(&iter));
+ (BLI_ghashIterator_step(&iter))) {
+ MenuType *mt = BLI_ghashIterator_getValue(&iter);
+ if (BLI_str_endswith(mt->idname, "_context_menu")) {
+ BLI_gset_add(menu_tagged, mt);
+ }
+ }
+ const char *idname_array[] = {
+ /* Add back some context menus. */
+ "OUTLINER_MT_context_menu",
+ };
+ for (int i = 0; i < ARRAY_SIZE(idname_array); i++) {
+ MenuType *mt = WM_menutype_find(idname_array[i], false);
+ if (mt != NULL) {
+ BLI_gset_remove(menu_tagged, mt, NULL);
+ }
+ }
+ }
+
/* Collect contexts, one for each 'ui_type'. */
struct MenuSearch_Context *wm_contexts = NULL;
@@ -981,7 +1009,7 @@ static void menu_search_update_fn(const bContext *UNUSED(C),
}
if (index == words_len) {
- if (!UI_search_item_add(items, item->drawwstr_full, item, item->icon, item->state)) {
+ if (!UI_search_item_add(items, item->drawwstr_full, item, item->icon, item->state, 0)) {
break;
}
}
diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c
index cdf87103587..b8070ccbb25 100644
--- a/source/blender/editors/interface/interface_template_search_operator.c
+++ b/source/blender/editors/interface/interface_template_search_operator.c
@@ -109,7 +109,7 @@ static void operator_search_update_fn(const bContext *C,
}
}
- if (!UI_search_item_add(items, name, ot, ICON_NONE, 0)) {
+ if (!UI_search_item_add(items, name, ot, ICON_NONE, 0, 0)) {
break;
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 7d856a51720..d3487b635ce 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -361,11 +361,25 @@ 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);
+ const bool use_lib_prefix = template_ui->preview || iconid;
+ const 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. */
+ int name_prefix_offset;
+ BKE_id_full_name_ui_prefix_get(
+ name_ui, id, use_lib_prefix, UI_SEP_CHAR, &name_prefix_offset);
+ if (!use_lib_prefix) {
+ iconid = UI_library_icon_get(id);
+ }
- if (!UI_search_item_add(items, name_ui, id, iconid, UI_BUT_HAS_SEP_CHAR)) {
+ if (!UI_search_item_add(items,
+ name_ui,
+ id,
+ iconid,
+ has_sep_char ? UI_BUT_HAS_SEP_CHAR : 0,
+ name_prefix_offset)) {
return false;
}
}
@@ -509,11 +523,12 @@ 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:
case UI_ID_PIN:
- RNA_warning("warning, id event %d shouldnt come here", event);
+ RNA_warning("warning, id event %d shouldn't come here", event);
break;
case UI_ID_OPEN:
case UI_ID_ADD_NEW:
@@ -529,6 +544,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;
@@ -540,6 +556,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;
@@ -548,7 +565,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
case UI_ID_LOCAL:
if (id) {
Main *bmain = CTX_data_main(C);
- if (BKE_lib_override_library_is_enabled() && CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->shift) {
if (ID_IS_OVERRIDABLE_LIBRARY(id)) {
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
@@ -558,6 +575,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* Assign new pointer, takes care of updates/notifiers */
RNA_id_pointer_create(override_id, &idptr);
}
+ undo_push_label = "Make Library Override";
}
}
else {
@@ -566,19 +584,23 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
+ undo_push_label = "Make Local";
}
}
- RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
- RNA_property_update(C, &template_ui->ptr, template_ui->prop);
+ if (undo_push_label != NULL) {
+ RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
+ RNA_property_update(C, &template_ui->ptr, template_ui->prop);
+ }
}
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:
@@ -599,6 +621,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
@@ -606,6 +629,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)
@@ -913,10 +940,8 @@ static void template_ID(const bContext *C,
0,
0,
0,
- BKE_lib_override_library_is_enabled() ?
- TIP_("Direct linked library data-block, click to make local, "
- "Shift + Click to create a library override") :
- TIP_("Direct linked library data-block, click to make local"));
+ TIP_("Direct linked library data-block, click to make local, "
+ "Shift + Click to create a library override"));
if (disabled) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
@@ -1135,9 +1160,7 @@ ID *UI_context_active_but_get_tab_ID(bContext *C)
if (but && but->type == UI_BTYPE_TAB) {
return but->custom_data;
}
- else {
- return NULL;
- }
+ return NULL;
}
static void template_ID_tabs(const bContext *C,
@@ -1832,14 +1855,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 = ED_object_active_context(C);
ListBase *modifiers = &ob->modifiers;
bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id);
@@ -1849,22 +1865,52 @@ 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);
}
}
}
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))
+ 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;
}
}
}
@@ -1872,268 +1918,344 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
/** \} */
/* -------------------------------------------------------------------- */
-/** \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);
+ return (panel->panelname[0] == 'B') && (panel->panelname[1] == 'O') &&
+ (panel->panelname[2] == 'N') && (panel->panelname[3] == 'E');
+}
- row = uiLayoutRow(box, false);
- block = uiLayoutGetBlock(row);
+/**
+ * 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;
+ }
+ }
+ else {
+ Object *ob = ED_object_active_context(C);
+ if (ob != NULL) {
+ constraints = &ob->constraints;
+ }
+ }
+ return constraints;
+}
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- /* Open/Close ................................. */
- uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
+/**
+ * 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-type icon */
- uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
- UI_block_emboss_set(block, UI_EMBOSS);
+ 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);
+}
- /* modifier name */
- if (mti->isDisabled && mti->isDisabled(md, 0)) {
- uiLayoutSetRedAlert(row, true);
- }
- uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
- uiLayoutSetRedAlert(row, false);
+/**
+ * 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);
- /* mode enabling buttons */
- UI_block_align_begin(block);
- uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
- uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ return con->ui_expand_flag;
+}
- if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
- sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, false);
- uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
- }
+/**
+ * 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_align_end(block);
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ con->ui_expand_flag = expand_flag;
+}
- /* 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);
+/**
+ * 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);
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemO(row, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
- UI_block_emboss_set(block, UI_EMBOSS);
+ strcpy(r_name, CONSTRAINT_TYPE_PANEL_PREFIX);
+ strcat(r_name, cti->structName);
+}
- /* modifier settings (under the header) --------------------------------------------------- */
- if (md->mode & eGpencilModifierMode_Expanded) {
- /* apply/convert/copy */
- box = uiLayoutBox(column);
- row = uiLayoutRow(box, false);
+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);
- /* only here obdata, the rest of modifiers is ob level */
- UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
+ strcpy(r_name, CONSTRAINT_BONE_TYPE_PANEL_PREFIX);
+ strcat(r_name, cti->structName);
+}
- uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
+/**
+ * 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)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
- 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);
+ Object *ob = ED_object_active_context(C);
+ ListBase *constraints = get_constraints(C, use_bone_constraints);
- UI_block_lock_clear(block);
- UI_block_lock_set(block, ob && ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
+ /* 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;
- uiItemO(row,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"),
- ICON_NONE,
- "OBJECT_OT_gpencil_modifier_copy");
+ bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
- /* 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);
- }
+ 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);
- /* error messages */
- if (md->error) {
- box = uiLayoutBox(column);
- row = uiLayoutRow(box, false);
- uiItemL(row, md->error, ICON_ERROR);
- }
+ /* Create custom data RNA pointer. */
+ PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr);
- return result;
-}
+ Panel *new_panel = UI_panel_add_instanced(
+ sa, region, &region->panels, panel_idname, i, con_ptr);
-uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
-{
- Object *ob;
- GpencilModifierData *md, *vmd;
- int i;
+ 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;
- /* verify we have valid data */
- if (!RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) {
- RNA_warning("Expected modifier on object");
- return NULL;
+ 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);
+ }
+ }
- ob = (Object *)ptr->owner_id;
- md = ptr->data;
-
- if (!ob || !(GS(ob->id.name) == ID_OB)) {
- RNA_warning("Expected modifier on object");
- return NULL;
- }
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel = region->panels.first;
+ LISTBASE_FOREACH (bConstraint *, con, constraints) {
+ /* Move to the next instanced panel corresponding to the next constraint. */
+ while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
+ panel = panel->next;
+ BLI_assert(panel != NULL); /* There shouldn't be fewer panels than constraint panels. */
+ }
- UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
+ PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "constraint panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr);
+ UI_panel_custom_data_set(panel, con_ptr);
- /* 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);
+ panel = panel->next;
}
}
-
- 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 = ED_object_active_context(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;
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+ Object *ob = ED_object_active_context(C);
+ ListBase *shaderfx = &ob->shader_fx;
- /* verify we have valid data */
- if (!RNA_struct_is_a(ptr->type, &RNA_ShaderFx)) {
- RNA_warning("Expected shader fx on object");
- return NULL;
- }
+ bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id);
- ob = (Object *)ptr->owner_id;
- fx = ptr->data;
+ 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);
- if (!ob || !(GS(ob->id.name) == ID_OB)) {
- RNA_warning("Expected shader fx on object");
- return NULL;
- }
+ /* Create custom data RNA pointer. */
+ PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
- UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
+ Panel *new_panel = UI_panel_add_instanced(
+ sa, region, &region->panels, panel_idname, i, fx_ptr);
- /* 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);
+ if (new_panel != NULL) {
+ 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;
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel = region->panels.first;
+ LISTBASE_FOREACH (ShaderFxData *, fx, shaderfx) {
+ const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type);
+ if (fxi->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 *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
+ UI_panel_custom_data_set(panel, fx_ptr);
+
+ panel = panel->next;
+ }
+ }
}
/** \} */
@@ -2191,10 +2313,9 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single(
UI_block_lock_set(block, true, "Operator can't' redo");
return return_info;
}
- else {
- /* useful for macros where only one of the steps can't be re-done */
- UI_block_lock_clear(block);
- }
+
+ /* useful for macros where only one of the steps can't be re-done */
+ UI_block_lock_clear(block);
if (layout_flags & UI_TEMPLATE_OP_PROPS_SHOW_TITLE) {
uiItemL(layout, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
@@ -2439,38 +2560,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;
@@ -2485,36 +2593,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 */
-
- /* open/close */
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
+ /* Constraint type icon. */
+ sub = uiLayoutRow(layout, false);
+ uiLayoutSetEmboss(sub, false);
+ uiLayoutSetRedAlert(sub, (con->flag & CONSTRAINT_DISABLE));
+ uiItemL(sub, "", RNA_struct_ui_icon(ptr.type));
- /* 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);
@@ -2553,54 +2648,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!) */
@@ -2608,23 +2669,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;
@@ -2632,7 +2681,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;
@@ -2640,7 +2689,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);
@@ -2649,11 +2698,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);
}
/** \} */
@@ -4523,7 +4572,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));
@@ -4639,7 +4688,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;
@@ -4705,7 +4754,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;
@@ -4769,7 +4818,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);
}
@@ -4778,7 +4827,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);
}
@@ -4787,35 +4836,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);
}
@@ -4834,15 +4871,39 @@ 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
* after a preset is selected would be more complicated. */
+ row = uiLayoutRow(layout, true);
bt = uiDefBlockBut(
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) */
@@ -4950,11 +5011,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;
}
+ 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;
@@ -4970,46 +5044,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,
@@ -5020,16 +5067,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,
@@ -5038,12 +5085,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);
}
@@ -6625,48 +6673,48 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
owner = scene;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_BUILD_PREVIEW)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_BUILD_PREVIEW)) {
handle_event = B_STOPSEQ;
icon = ICON_SEQUENCE;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_BUILD_PROXY)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_BUILD_PROXY)) {
handle_event = B_STOPCLIP;
icon = ICON_TRACKER;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_PREFETCH)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_PREFETCH)) {
handle_event = B_STOPCLIP;
icon = ICON_TRACKER;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_TRACK_MARKERS)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_TRACK_MARKERS)) {
handle_event = B_STOPCLIP;
icon = ICON_TRACKER;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
handle_event = B_STOPCLIP;
icon = ICON_TRACKER;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_FILESEL_READDIR)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_FILESEL_READDIR)) {
handle_event = B_STOPFILE;
icon = ICON_FILEBROWSER;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) {
handle_event = B_STOPRENDER;
icon = ICON_SCENE;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_COMPOSITE)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_COMPOSITE)) {
handle_event = B_STOPCOMPO;
icon = ICON_RENDERLAYERS;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE) ||
- WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_BAKE)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE) ||
+ WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_BAKE)) {
/* Skip bake jobs in compositor to avoid compo header displaying
* progress bar which is not being updated (bake jobs only need
* to update NC_IMAGE context.
@@ -6676,23 +6724,24 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
icon = ICON_IMAGE;
break;
}
+ continue;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_DPAINT_BAKE)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_DPAINT_BAKE)) {
handle_event = B_STOPOTHER;
icon = ICON_MOD_DYNAMICPAINT;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_POINTCACHE)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_POINTCACHE)) {
handle_event = B_STOPOTHER;
icon = ICON_PHYSICS;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_SIM_FLUID)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_SIM_FLUID)) {
handle_event = B_STOPOTHER;
icon = ICON_MOD_FLUIDSIM;
break;
}
- else if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_SIM_OCEAN)) {
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_OBJECT_SIM_OCEAN)) {
handle_event = B_STOPOTHER;
icon = ICON_MOD_OCEAN;
break;
@@ -7269,37 +7318,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_undo.c b/source/blender/editors/interface/interface_undo.c
index 016bc4159db..304d2254a81 100644
--- a/source/blender/editors/interface/interface_undo.c
+++ b/source/blender/editors/interface/interface_undo.c
@@ -88,9 +88,7 @@ const char *ui_textedit_undo(uiUndoStack_Text *stack, int direction, int *r_curs
if (direction < 0) {
return ui_textedit_undo_impl(stack, r_cursor_index);
}
- else {
- return ui_textedit_redo_impl(stack, r_cursor_index);
- }
+ return ui_textedit_redo_impl(stack, r_cursor_index);
}
/**
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 15db947bff6..208fd7136da 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"
@@ -59,13 +60,9 @@ bool ui_str_has_word_prefix(const char *haystack, const char *needle, size_t nee
if ((match == haystack) || (*(match - 1) == ' ') || ispunct(*(match - 1))) {
return true;
}
- else {
- return ui_str_has_word_prefix(match + 1, needle, needle_len);
- }
- }
- else {
- return false;
+ return ui_str_has_word_prefix(match + 1, needle, needle_len);
}
+ return false;
}
/*************************** RNA Utilities ******************************/
@@ -384,6 +381,9 @@ typedef struct CollItemSearch {
char *name;
int index;
int iconid;
+ bool is_id;
+ int name_prefix_offset;
+ uint has_sep_char : 1;
} CollItemSearch;
static int sort_search_items_list(const void *a, const void *b)
@@ -394,9 +394,7 @@ static int sort_search_items_list(const void *a, const void *b)
if (BLI_strcasecmp(cis1->name, cis2->name) > 0) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
void ui_rna_collection_search_update_fn(const struct bContext *C,
@@ -405,7 +403,8 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
uiSearchItems *items)
{
uiRNACollectionSearch *data = arg;
- int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop);
+ const int flag = RNA_property_flag(data->target_prop);
+ int i = 0;
ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
CollItemSearch *cis;
const bool is_ptr_target = (RNA_property_type(data->target_prop) == PROP_POINTER);
@@ -416,6 +415,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) {
@@ -433,18 +433,28 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
}
}
- iconid = 0;
- if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
+ int name_prefix_offset = 0;
+ int iconid = ICON_NONE;
+ bool has_sep_char = false;
+ bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
+
+ 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 {
- BKE_id_full_name_ui_prefix_get(name_buf, itemptr.data, UI_SEP_CHAR);
+ const ID *id = itemptr.data;
+ BKE_id_full_name_ui_prefix_get(
+ name_buf, itemptr.data, true, UI_SEP_CHAR, &name_prefix_offset);
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;
+ has_sep_char = (id->lib != NULL);
}
}
else {
@@ -452,12 +462,15 @@ void ui_rna_collection_search_update_fn(const struct bContext *C,
}
if (name) {
- if (skip_filter || BLI_strcasestr(name, str)) {
+ if (skip_filter || BLI_strcasestr(name + name_prefix_offset, str)) {
cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
cis->data = itemptr.data;
cis->name = BLI_strdup(name);
cis->index = i;
cis->iconid = iconid;
+ cis->is_id = is_id;
+ cis->name_prefix_offset = name_prefix_offset;
+ cis->has_sep_char = has_sep_char;
BLI_addtail(items_list, cis);
}
if (name != name_buf) {
@@ -473,7 +486,24 @@ 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 (!UI_search_item_add(items, cis->name, cis->data, cis->iconid, UI_BUT_HAS_SEP_CHAR)) {
+ /* 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. */
+ int name_prefix_offset = cis->name_prefix_offset;
+ 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, &name_prefix_offset);
+ 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,
+ cis->iconid,
+ cis->has_sep_char ? UI_BUT_HAS_SEP_CHAR : 0,
+ name_prefix_offset)) {
break;
}
}
@@ -505,9 +535,7 @@ int UI_icon_from_id(ID *id)
if (ob->type == OB_EMPTY) {
return ICON_EMPTY_DATA;
}
- else {
- return UI_icon_from_id(ob->data);
- }
+ return UI_icon_from_id(ob->data);
}
/* otherwise get it through RNA, creating the pointer
@@ -523,15 +551,13 @@ int UI_icon_from_report_type(int type)
if (type & RPT_ERROR_ALL) {
return ICON_ERROR;
}
- else if (type & RPT_WARNING_ALL) {
+ if (type & RPT_WARNING_ALL) {
return ICON_ERROR;
}
- else if (type & RPT_INFO_ALL) {
+ if (type & RPT_INFO_ALL) {
return ICON_INFO;
}
- else {
- return ICON_NONE;
- }
+ return ICON_NONE;
}
/********************************** Misc **************************************/
@@ -602,7 +628,7 @@ bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength)
RNA_property_identifier(but->rnaprop));
return true;
}
- else if (but->optype) {
+ if (but->optype) {
WM_operator_py_idname(r_str, but->optype->idname);
return true;
}
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 0498b312618..62e53513927 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -53,6 +53,7 @@
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
+#include "GPU_platform.h"
#include "GPU_state.h"
#ifdef WITH_INPUT_IME
@@ -393,35 +394,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 +416,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 +442,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;
- 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;
- }
- }
+ GPU_vertbuf_data_alloc(vbo, 12);
- 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);
+ 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 (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_indexbuf_add_tri_verts(&ibuf, 8, 9, 10);
+ GPU_indexbuf_add_tri_verts(&ibuf, 10, 9, 11);
- 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 +1148,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 +1165,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 +1209,35 @@ void UI_widgetbase_draw_cache_end(void)
GPU_blend(false);
}
-static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb)
+/* Disable cached/instanced drawing and enforce single widget drawing pipeline.
+ * Works around interface artifacts happening on certain driver and hardware
+ * configurations. */
+static bool draw_widgetbase_batch_skip_draw_cache(void)
{
+ /* MacOS is known to have issues on Mac Mini and MacBook Pro with Intel Iris GPU.
+ * For example, T78307. */
+ if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_ANY)) {
+ return true;
+ }
+
+ /* There are also reports that some AMD and Mesa driver configuration suffer from the
+ * same issue, T78803. */
+ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
+ return true;
+ }
+
+ return false;
+}
+
+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;
- }
-
+ if (g_widget_base_batch.enabled && !draw_widgetbase_batch_skip_draw_cache()) {
g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
g_widget_base_batch.count++;
@@ -1412,6 +1249,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 +1272,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 +1291,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 +1303,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 +1312,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)
@@ -1521,7 +1356,7 @@ static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect)
static int ui_but_draw_menu_icon(const uiBut *but)
{
- return (but->flag & UI_BUT_ICON_SUBMENU) && (but->dt == UI_EMBOSS_PULLDOWN);
+ return (but->flag & UI_BUT_ICON_SUBMENU) && (but->emboss == UI_EMBOSS_PULLDOWN);
}
/* icons have been standardized... and this call draws in untransformed coordinates */
@@ -1582,7 +1417,7 @@ static void widget_draw_icon(
but->str && but->str[0] == '\0') {
xs = rect->xmin + 2.0f * ofs;
}
- else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) {
+ else if (but->emboss == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) {
xs = rect->xmin + 2.0f * ofs;
}
else {
@@ -1701,7 +1536,7 @@ static void ui_text_clip_right_ex(const uiFontStyle *fstyle,
l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp);
memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */
if (r_final_len) {
- *r_final_len = (size_t)(l_end + sep_len);
+ *r_final_len = (size_t)(l_end) + sep_len;
}
}
}
@@ -2540,7 +2375,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
/* pass (even if its a menu toolbar) */
}
else if (ui_block_is_pie_menu(but->block)) {
- if (but->dt == UI_EMBOSS_RADIAL) {
+ if (but->emboss == UI_EMBOSS_RADIAL) {
rect->xmin += 0.3f * U.widget_unit;
}
}
@@ -4666,7 +4501,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
uiWidgetType *wt = NULL;
/* handle menus separately */
- if (but->dt == UI_EMBOSS_PULLDOWN) {
+ if (but->emboss == UI_EMBOSS_PULLDOWN) {
switch (but->type) {
case UI_BTYPE_LABEL:
widget_draw_text_icon(&style->widgetlabel, &tui->wcol_menu_back, but, rect);
@@ -4679,7 +4514,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
break;
}
}
- else if (but->dt == UI_EMBOSS_NONE) {
+ else if (but->emboss == UI_EMBOSS_NONE) {
/* "nothing" */
switch (but->type) {
case UI_BTYPE_LABEL:
@@ -4690,11 +4525,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
break;
}
}
- else if (but->dt == UI_EMBOSS_RADIAL) {
+ else if (but->emboss == UI_EMBOSS_RADIAL) {
wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL);
}
else {
- BLI_assert(but->dt == UI_EMBOSS);
+ BLI_assert(but->emboss == UI_EMBOSS);
switch (but->type) {
case UI_BTYPE_LABEL:
@@ -4770,7 +4605,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
if ((but->drawflag & (UI_BUT_TEXT_LEFT | UI_BUT_TEXT_RIGHT)) == 0) {
but->drawflag |= UI_BUT_TEXT_LEFT;
}
- /* widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the
+ /* #widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the
* text drawing were to add its own padding, DPI and zoom factor would be applied twice
* in the final padding, so it's difficult to control it. */
but->drawflag |= UI_BUT_NO_TEXT_PADDING;
@@ -4943,7 +4778,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
}
if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
- if (but->dt != UI_EMBOSS_PULLDOWN) {
+ if (but->emboss != UI_EMBOSS_PULLDOWN) {
disabled = true;
}
}
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index 01c9716ec86..84fe3e13426 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -1408,7 +1408,7 @@ bool UI_GetIconThemeColor4ubv(int colorid, uchar col[4])
if (colorid == 0) {
return false;
}
- else if (colorid == TH_ICON_FUND) {
+ if (colorid == TH_ICON_FUND) {
/* Always color development fund icon. */
}
else if (!((theme_spacetype == SPACE_OUTLINER && theme_regionid == RGN_TYPE_WINDOW) ||
@@ -1471,7 +1471,7 @@ void UI_ThemeClearColor(int colorid)
float col[3];
UI_GetThemeColor3fv(colorid, col);
- GPU_clear_color(col[0], col[1], col[2], 0.0f);
+ GPU_clear_color(col[0], col[1], col[2], 1.0f);
}
void UI_ThemeClearColorAlpha(int colorid, float alpha)
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index f8419ba3eba..0cbf73280a3 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -74,12 +74,10 @@ BLI_INLINE int clamp_float_to_int(const float f)
if (UNLIKELY(f < min)) {
return min;
}
- else if (UNLIKELY(f > max)) {
+ if (UNLIKELY(f > max)) {
return (int)max;
}
- else {
- return (int)f;
- }
+ return (int)f;
}
/**
@@ -113,10 +111,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 +196,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 +209,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 +1406,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 +1450,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 +1473,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 +1511,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 +1554,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 +1564,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 +1600,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 +1629,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);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1762,12 +1754,11 @@ bool UI_view2d_view_to_region_clip(
return true;
}
- else {
- /* set initial value in case coordinate lies outside of bounds */
- *r_region_x = *r_region_y = V2D_IS_CLIPPED;
- return false;
- }
+ /* set initial value in case coordinate lies outside of bounds */
+ *r_region_x = *r_region_y = V2D_IS_CLIPPED;
+
+ return false;
}
/**
@@ -1861,11 +1852,9 @@ bool UI_view2d_view_to_region_rcti_clip(const View2D *v2d, const rctf *rect_src,
return true;
}
- else {
- rect_dst->xmin = rect_dst->xmax = rect_dst->ymin = rect_dst->ymax = V2D_IS_CLIPPED;
- return false;
- }
+ rect_dst->xmin = rect_dst->xmax = rect_dst->ymin = rect_dst->ymax = V2D_IS_CLIPPED;
+ return false;
}
/** \} */
diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c
index 677043c1ccf..54b25939baf 100644
--- a/source/blender/editors/interface/view2d_draw.c
+++ b/source/blender/editors/interface/view2d_draw.c
@@ -174,26 +174,38 @@ static void get_parallel_lines_draw_steps(const ParallelLinesSet *lines,
}
}
+/**
+ * \param rect_mask: Region size in pixels.
+ */
static void draw_parallel_lines(const ParallelLinesSet *lines,
const rctf *rect,
- const uchar *color,
+ const rcti *rect_mask,
+ const uchar color[3],
char direction)
{
float first;
- uint steps;
+ uint steps, steps_max;
if (direction == 'v') {
get_parallel_lines_draw_steps(lines, rect->xmin, rect->xmax, &first, &steps);
+ steps_max = BLI_rcti_size_x(rect_mask);
}
else {
BLI_assert(direction == 'h');
get_parallel_lines_draw_steps(lines, rect->ymin, rect->ymax, &first, &steps);
+ steps_max = BLI_rcti_size_y(rect_mask);
}
if (steps == 0) {
return;
}
+ if (UNLIKELY(steps >= steps_max)) {
+ /* Note that we could draw a solid color,
+ * however this flickers because of numeric instability when zoomed out. */
+ return;
+ }
+
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -234,12 +246,12 @@ static void draw_parallel_lines(const ParallelLinesSet *lines,
static void view2d_draw_lines_internal(const View2D *v2d,
const ParallelLinesSet *lines,
- const uchar *color,
+ const uchar color[3],
char direction)
{
GPU_matrix_push_projection();
UI_view2d_view_ortho(v2d);
- draw_parallel_lines(lines, &v2d->cur, color, direction);
+ draw_parallel_lines(lines, &v2d->cur, &v2d->mask, color, direction);
GPU_matrix_pop_projection();
}
@@ -248,17 +260,18 @@ static void view2d_draw_lines(const View2D *v2d,
bool display_minor_lines,
char direction)
{
- uchar major_color[3];
- uchar minor_color[3];
- UI_GetThemeColor3ubv(TH_GRID, major_color);
- UI_GetThemeColorShade3ubv(TH_GRID, 16, minor_color);
-
- ParallelLinesSet major_lines;
- major_lines.distance = major_distance;
- major_lines.offset = 0;
- view2d_draw_lines_internal(v2d, &major_lines, major_color, direction);
+ {
+ uchar major_color[3];
+ UI_GetThemeColor3ubv(TH_GRID, major_color);
+ ParallelLinesSet major_lines;
+ major_lines.distance = major_distance;
+ major_lines.offset = 0;
+ view2d_draw_lines_internal(v2d, &major_lines, major_color, direction);
+ }
if (display_minor_lines) {
+ uchar minor_color[3];
+ UI_GetThemeColorShade3ubv(TH_GRID, 16, minor_color);
ParallelLinesSet minor_lines;
minor_lines.distance = major_distance;
minor_lines.offset = major_distance / 2.0f;
@@ -284,9 +297,6 @@ static void draw_horizontal_scale_indicators(const ARegion *region,
return;
}
- GPU_matrix_push_projection();
- wmOrtho2_region_pixelspace(region);
-
float start;
uint steps;
{
@@ -298,8 +308,15 @@ static void draw_horizontal_scale_indicators(const ARegion *region,
UI_view2d_region_to_view_x(v2d, rect->xmax),
&start,
&steps);
+ const uint steps_max = BLI_rcti_size_x(&v2d->mask);
+ if (UNLIKELY(steps >= steps_max)) {
+ return;
+ }
}
+ GPU_matrix_push_projection();
+ wmOrtho2_region_pixelspace(region);
+
const int font_id = BLF_default();
UI_FontThemeColor(font_id, colorid);
@@ -339,9 +356,6 @@ static void draw_vertical_scale_indicators(const ARegion *region,
return;
}
- GPU_matrix_push_projection();
- wmOrtho2_region_pixelspace(region);
-
float start;
uint steps;
{
@@ -353,8 +367,15 @@ static void draw_vertical_scale_indicators(const ARegion *region,
UI_view2d_region_to_view_y(v2d, rect->ymax),
&start,
&steps);
+ const uint steps_max = BLI_rcti_size_y(&v2d->mask);
+ if (UNLIKELY(steps >= steps_max)) {
+ return;
+ }
}
+ GPU_matrix_push_projection();
+ wmOrtho2_region_pixelspace(region);
+
const int font_id = BLF_default();
UI_FontThemeColor(font_id, colorid);
@@ -428,9 +449,7 @@ float UI_view2d_grid_resolution_x__frames_or_seconds(const struct View2D *v2d,
if (display_seconds) {
return view2d_major_step_x__time(v2d, scene);
}
- else {
- return view2d_major_step_x__continuous(v2d);
- }
+ return view2d_major_step_x__continuous(v2d);
}
float UI_view2d_grid_resolution_y__values(const struct View2D *v2d)
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 98bbd7af943..64cacd44e3d 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -68,7 +68,7 @@ static bool view2d_poll(bContext *C)
/**
* This group of operators come in several forms:
* -# Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
- * -# Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
+ * -# Scroll-wheel 'steps' - rolling mouse-wheel by one step moves view by predefined amount
*
* In order to make sure this works, each operator must define the following RNA-Operator Props:
* - `deltax, deltay` - define how much to move view by (relative to zoom-correction factor)
@@ -475,20 +475,29 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event
* On successful handling, always pass events on to other handlers. */
const int success_retval = OPERATOR_PASS_THROUGH;
- /* Find whether the mouse is beyond X and Y edges. */
+ int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X;
+ rcti padding_rect;
+ if (outside_padding != 0) {
+ padding_rect = region->winrct;
+ BLI_rcti_pad(&padding_rect, outside_padding, outside_padding);
+ }
+
int pan_dir_x = 0;
int pan_dir_y = 0;
- if (event->x > region->winrct.xmax - EDGE_PAN_REGION_PAD) {
- pan_dir_x = 1;
- }
- else if (event->x < region->winrct.xmin + EDGE_PAN_REGION_PAD) {
- pan_dir_x = -1;
- }
- if (event->y > region->winrct.ymax - EDGE_PAN_REGION_PAD) {
- pan_dir_y = 1;
- }
- else if (event->y < region->winrct.ymin + EDGE_PAN_REGION_PAD) {
- pan_dir_y = -1;
+ if ((outside_padding == 0) || BLI_rcti_isect_pt(&padding_rect, event->x, event->y)) {
+ /* Find whether the mouse is beyond X and Y edges. */
+ if (event->x > region->winrct.xmax - EDGE_PAN_REGION_PAD) {
+ pan_dir_x = 1;
+ }
+ else if (event->x < region->winrct.xmin + EDGE_PAN_REGION_PAD) {
+ pan_dir_x = -1;
+ }
+ if (event->y > region->winrct.ymax - EDGE_PAN_REGION_PAD) {
+ pan_dir_y = 1;
+ }
+ else if (event->y < region->winrct.ymin + EDGE_PAN_REGION_PAD) {
+ pan_dir_y = -1;
+ }
}
const double current_time = PIL_check_seconds_timer();
@@ -532,6 +541,16 @@ static void VIEW2D_OT_edge_pan(wmOperatorType *ot)
/* operator is modal */
ot->flag = OPTYPE_INTERNAL;
+ RNA_def_int(ot->srna,
+ "outside_padding",
+ 0,
+ 0,
+ 100,
+ "Outside Padding",
+ "Padding around the region in UI units within which panning is activated (0 to "
+ "disable boundary)",
+ 0,
+ 100);
}
#undef EDGE_PAN_REGION_PAD
@@ -738,8 +757,8 @@ static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
/**
* This group of operators come in several forms:
- * -# Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount.
- * -# Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y).
+ * -# Scroll-wheel 'steps' - rolling mouse-wheel by one step zooms view by predefined amount.
+ * -# Scroll-wheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y).
* XXX this could be implemented...
* -# Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount.
*
@@ -1608,57 +1627,56 @@ static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (event->type != NDOF_MOTION) {
return OPERATOR_CANCELLED;
}
- else {
- const wmNDOFMotionData *ndof = event->customdata;
- /* tune these until it feels right */
- const float zoom_sensitivity = 0.5f;
- const float speed = 10.0f; /* match view3d ortho */
- const bool has_translate = (ndof->tvec[0] && ndof->tvec[1]) && view_pan_poll(C);
- const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C);
+ const wmNDOFMotionData *ndof = event->customdata;
- if (has_translate) {
- if (view_pan_init(C, op)) {
- v2dViewPanData *vpd;
- float pan_vec[3];
+ /* tune these until it feels right */
+ const float zoom_sensitivity = 0.5f;
+ const float speed = 10.0f; /* match view3d ortho */
+ const bool has_translate = (ndof->tvec[0] && ndof->tvec[1]) && view_pan_poll(C);
+ const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C);
- WM_event_ndof_pan_get(ndof, pan_vec, false);
+ if (has_translate) {
+ if (view_pan_init(C, op)) {
+ v2dViewPanData *vpd;
+ float pan_vec[3];
- pan_vec[0] *= speed;
- pan_vec[1] *= speed;
+ WM_event_ndof_pan_get(ndof, pan_vec, false);
- vpd = op->customdata;
+ pan_vec[0] *= speed;
+ pan_vec[1] *= speed;
- view_pan_apply_ex(C, vpd, pan_vec[0], pan_vec[1]);
+ vpd = op->customdata;
- view_pan_exit(op);
- }
+ view_pan_apply_ex(C, vpd, pan_vec[0], pan_vec[1]);
+
+ view_pan_exit(op);
}
+ }
- if (has_zoom) {
- if (view_zoomdrag_init(C, op)) {
- v2dViewZoomData *vzd;
- float zoom_factor = zoom_sensitivity * ndof->dt * -ndof->tvec[2];
+ if (has_zoom) {
+ if (view_zoomdrag_init(C, op)) {
+ v2dViewZoomData *vzd;
+ float zoom_factor = zoom_sensitivity * ndof->dt * -ndof->tvec[2];
- bool do_zoom_xy[2];
+ bool do_zoom_xy[2];
- if (U.ndof_flag & NDOF_ZOOM_INVERT) {
- zoom_factor = -zoom_factor;
- }
+ if (U.ndof_flag & NDOF_ZOOM_INVERT) {
+ zoom_factor = -zoom_factor;
+ }
- view_zoom_axis_lock_defaults(C, do_zoom_xy);
+ view_zoom_axis_lock_defaults(C, do_zoom_xy);
- vzd = op->customdata;
+ vzd = op->customdata;
- view_zoomstep_apply_ex(
- C, vzd, false, do_zoom_xy[0] ? zoom_factor : 0.0f, do_zoom_xy[1] ? zoom_factor : 0.0f);
+ view_zoomstep_apply_ex(
+ C, vzd, false, do_zoom_xy[0] ? zoom_factor : 0.0f, do_zoom_xy[1] ? zoom_factor : 0.0f);
- view_zoomstep_exit(op);
- }
+ view_zoomstep_exit(op);
}
-
- return OPERATOR_FINISHED;
}
+
+ return OPERATOR_FINISHED;
}
static void VIEW2D_OT_ndof(wmOperatorType *ot)
@@ -1922,6 +1940,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) */
@@ -1972,16 +1993,16 @@ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_
if (in_bar) {
return SCROLLHANDLE_BAR;
}
- else if (in_max) {
+ if (in_max) {
return SCROLLHANDLE_MAX;
}
- else if (in_min) {
+ if (in_min) {
return SCROLLHANDLE_MIN;
}
- else if (out_min) {
+ if (out_min) {
return SCROLLHANDLE_MIN_OUTSIDE;
}
- else if (out_max) {
+ if (out_max) {
return SCROLLHANDLE_MAX_OUTSIDE;
}
@@ -2011,7 +2032,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 +2053,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 +2070,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 +2090,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 +2319,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);
@@ -2321,11 +2341,10 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
- else {
- /* not in scroller, so nothing happened...
- * (pass through let's something else catch event) */
- return OPERATOR_PASS_THROUGH;
- }
+
+ /* not in scroller, so nothing happened...
+ * (pass through let's something else catch event) */
+ return OPERATOR_PASS_THROUGH;
}
/* LMB-Drag in Scrollers - not repeatable operator! */
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index 39548449a86..e7effd05d34 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -66,10 +66,6 @@ if(WITH_ALEMBIC)
bf_alembic
)
add_definitions(-DWITH_ALEMBIC)
-
- if(WITH_ALEMBIC_HDF5)
- add_definitions(-DWITH_ALEMBIC_HDF5)
- endif()
endif()
if(WITH_USD)
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index dc8ad858a9f..238ebffe153 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -133,7 +133,6 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"),
.export_hair = RNA_boolean_get(op->ptr, "export_hair"),
.export_particles = RNA_boolean_get(op->ptr, "export_particles"),
- .compression_type = RNA_enum_get(op->ptr, "compression_type"),
.packuv = RNA_boolean_get(op->ptr, "packuv"),
.triangulate = RNA_boolean_get(op->ptr, "triangulate"),
.quad_method = RNA_enum_get(op->ptr, "quad_method"),
@@ -159,108 +158,78 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
{
- uiLayout *box;
- uiLayout *row;
- uiLayout *col;
+ uiLayout *box, *row, *col, *sub;
-# ifdef WITH_ALEMBIC_HDF5
- box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Archive Options:"), ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "compression_type", 0, NULL, ICON_NONE);
-# endif
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
+ uiItemL(box, IFACE_("Manual Transform"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "global_scale", 0, NULL, ICON_NONE);
+ uiItemR(box, imfptr, "global_scale", 0, NULL, ICON_NONE);
/* Scene Options */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Scene Options:"), ICON_SCENE_DATA);
+ uiItemL(row, IFACE_("Scene Options"), ICON_SCENE_DATA);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "start", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "end", 0, NULL, ICON_NONE);
+ sub = uiLayoutColumn(col, true);
+ uiItemR(sub, imfptr, "start", 0, IFACE_("Frame Start"), ICON_NONE);
+ uiItemR(sub, imfptr, "end", 0, IFACE_("End"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "xsamples", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "xsamples", 0, IFACE_("Samples Transform"), ICON_NONE);
+ uiItemR(col, imfptr, "gsamples", 0, IFACE_("Geometry"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "gsamples", 0, NULL, ICON_NONE);
+ sub = uiLayoutColumn(col, true);
+ uiItemR(sub, imfptr, "sh_open", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(sub, imfptr, "sh_close", UI_ITEM_R_SLIDER, IFACE_("Close"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "sh_open", 0, NULL, ICON_NONE);
+ uiItemS(col);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "sh_close", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "renderable_only", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "visible_objects_only", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "flatten", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "flatten", 0, NULL, ICON_NONE);
+ sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only"));
+ uiItemR(sub, imfptr, "selected", 0, IFACE_("Selected Objects"), ICON_NONE);
+ uiItemR(sub, imfptr, "renderable_only", 0, IFACE_("Renderable Objects"), ICON_NONE);
+ uiItemR(sub, imfptr, "visible_objects_only", 0, IFACE_("Visible Objects"), ICON_NONE);
/* Object Data */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Object Options:"), ICON_OBJECT_DATA);
+ uiItemL(row, IFACE_("Object Options"), ICON_OBJECT_DATA);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "uvs", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
- row = uiLayoutRow(box, false);
+ uiItemR(col, imfptr, "uvs", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(col, false);
+ uiLayoutSetActive(row, RNA_boolean_get(imfptr, "uvs"));
uiItemR(row, imfptr, "packuv", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "uvs"));
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "normals", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "normals", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "vcolors", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "face_sets", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "curves_as_mesh", 0, NULL, ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "vcolors", 0, NULL, ICON_NONE);
+ uiItemS(col);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "face_sets", 0, NULL, ICON_NONE);
+ sub = uiLayoutColumnWithHeading(col, true, IFACE_("Subdivisions"));
+ uiItemR(sub, imfptr, "apply_subdiv", 0, IFACE_("Apply"), ICON_NONE);
+ uiItemR(sub, imfptr, "subdiv_schema", 0, IFACE_("Use Schema"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "subdiv_schema", 0, NULL, ICON_NONE);
+ uiItemS(col);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "apply_subdiv", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "curves_as_mesh", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "triangulate", 0, NULL, ICON_NONE);
+ sub = uiLayoutColumn(col, false);
+ uiLayoutSetActive(sub, RNA_boolean_get(imfptr, "triangulate"));
+ uiItemR(sub, imfptr, "quad_method", 0, IFACE_("Method Quads"), ICON_NONE);
+ uiItemR(sub, imfptr, "ngon_method", 0, IFACE_("Polygons"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "triangulate", 0, NULL, ICON_NONE);
-
- const bool triangulate = RNA_boolean_get(imfptr, "triangulate");
-
- row = uiLayoutRow(box, false);
- uiLayoutSetEnabled(row, triangulate);
- uiItemR(row, imfptr, "quad_method", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiLayoutSetEnabled(row, triangulate);
- uiItemR(row, imfptr, "ngon_method", 0, NULL, ICON_NONE);
-
- /* Object Data */
+ /* Particle Data */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Particle Systems:"), ICON_PARTICLE_DATA);
+ uiItemL(row, IFACE_("Particle Systems"), ICON_PARTICLE_DATA);
col = uiLayoutColumn(box, true);
uiItemR(col, imfptr, "export_hair", 0, NULL, ICON_NONE);
@@ -420,8 +389,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 Subsurf", "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",
@@ -429,13 +401,6 @@ void WM_OT_alembic_export(wmOperatorType *ot)
"Curves as Mesh",
"Export curves and NURBS surfaces as meshes");
- RNA_def_enum(ot->srna,
- "compression_type",
- rna_enum_abc_compression_items,
- ABC_ARCHIVE_OGAWA,
- "Compression",
- "");
-
RNA_def_float(
ot->srna,
"global_scale",
@@ -592,28 +557,25 @@ static int get_sequence_len(char *filename, int *ofs)
static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
{
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
uiLayout *box = uiLayoutBox(layout);
uiLayout *row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
+ uiItemL(row, IFACE_("Manual Transform"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "scale", 0, NULL, ICON_NONE);
+ uiItemR(box, imfptr, "scale", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Options:"), ICON_NONE);
+ uiItemL(row, IFACE_("Options"), ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "relative_path", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "set_frame_range", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "is_sequence", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
+ uiLayout *col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "relative_path", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "set_frame_range", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "is_sequence", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
}
static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
@@ -684,6 +646,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..b91b3b92947 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -253,21 +253,20 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "No objects selected -- Created empty export file");
return OPERATOR_CANCELLED;
}
- else if (export_count < 0) {
+ if (export_count < 0) {
BKE_report(op->reports, RPT_WARNING, "Error during export (see Console)");
return OPERATOR_CANCELLED;
}
- else {
- char buff[100];
- sprintf(buff, "Exported %d Objects", export_count);
- BKE_report(op->reports, RPT_INFO, buff);
- return OPERATOR_FINISHED;
- }
+
+ char buff[100];
+ sprintf(buff, "Exported %d Objects", export_count);
+ BKE_report(op->reports, RPT_INFO, buff);
+ return OPERATOR_FINISHED;
}
static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
{
- uiLayout *bbox, *box, *row, *col, *split;
+ uiLayout *box, *row, *col, *sub;
bool include_animations = RNA_boolean_get(imfptr, "include_animations");
int ui_section = RNA_enum_get(imfptr, "prop_bc_export_ui_section");
@@ -280,161 +279,126 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
bool sampling = animation_type == BC_ANIMATION_EXPORT_SAMPLES;
/* Export Options: */
- box = uiLayoutBox(layout);
-
- row = uiLayoutRow(box, false);
+ row = uiLayoutRow(layout, false);
uiItemR(row, imfptr, "prop_bc_export_ui_section", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- if (ui_section == BC_UI_SECTION_MAIN) {
- /* =================== */
- /* Export Data options */
- /* =================== */
-
- bbox = uiLayoutBox(layout);
- row = uiLayoutRow(bbox, false);
- uiItemL(row, IFACE_("Global Orientation:"), ICON_ORIENTATION_GLOBAL);
- row = uiLayoutRow(bbox, false);
- uiItemR(row, imfptr, "export_global_forward_selection", 0, "", ICON_NONE);
-
- row = uiLayoutRow(bbox, false);
- uiItemR(row, imfptr, "export_global_up_selection", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
- row = uiLayoutRow(bbox, false);
- uiItemR(row, imfptr, "apply_global_orientation", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE);
+ if (ui_section == BC_UI_SECTION_MAIN) {
+ /* Export data options. */
+ box = uiLayoutBox(layout);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "selected", 0, NULL, ICON_NONE);
+ sub = uiLayoutColumn(col, false);
+ uiLayoutSetEnabled(sub, RNA_boolean_get(imfptr, "selected"));
+ uiItemR(sub, imfptr, "include_children", 0, NULL, ICON_NONE);
+ uiItemR(sub, imfptr, "include_armatures", 0, NULL, ICON_NONE);
+ uiItemR(sub, imfptr, "include_shapekeys", 0, NULL, ICON_NONE);
+ box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "include_children", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
+ uiItemL(row, IFACE_("Global Orientation"), ICON_ORIENTATION_GLOBAL);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "include_armatures", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
+ uiItemR(box, imfptr, "apply_global_orientation", 0, IFACE_("Apply"), ICON_NONE);
row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "include_shapekeys", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
-
+ uiItemR(row,
+ imfptr,
+ "export_global_forward_selection",
+ UI_ITEM_R_EXPAND,
+ IFACE_("Forward Axis"),
+ ICON_NONE);
row = uiLayoutRow(box, false);
+ uiItemR(
+ row, imfptr, "export_global_up_selection", UI_ITEM_R_EXPAND, IFACE_("Up Axis"), ICON_NONE);
/* Texture options */
box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Texture Options:"), ICON_TEXTURE_DATA);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "active_uv_only", 0, NULL, ICON_NONE);
+ uiItemL(box, IFACE_("Texture Options"), ICON_TEXTURE_DATA);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "use_texture_copies", 1, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "use_texture_copies", 0, NULL, ICON_NONE);
+ row = uiLayoutRowWithHeading(col, true, IFACE_("UV"));
+ uiItemR(row, imfptr, "active_uv_only", 0, IFACE_("Only Selected Map"), ICON_NONE);
}
else if (ui_section == BC_UI_SECTION_GEOMETRY) {
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Export Data Options:"), ICON_MESH_DATA);
-
- row = uiLayoutRow(box, false);
- split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
+ box = uiLayoutBox(layout);
+ uiItemL(box, IFACE_("Export Data Options"), ICON_MESH_DATA);
- col = uiLayoutColumn(split, false);
- uiItemR(col, imfptr, "apply_modifiers", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
- col = uiLayoutColumn(split, false);
- uiItemR(col, imfptr, "export_mesh_type_selection", 0, "", ICON_NONE);
- uiLayoutSetEnabled(col, RNA_boolean_get(imfptr, "apply_modifiers"));
+ uiItemR(col, imfptr, "triangulate", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(box, false);
- uiItemR(col, imfptr, "triangulate", 1, NULL, ICON_NONE);
+ row = uiLayoutRowWithHeading(col, true, IFACE_("Apply Modifiers"));
+ uiItemR(row, imfptr, "apply_modifiers", 0, "", ICON_NONE);
+ sub = uiLayoutColumn(row, false);
+ uiLayoutSetActive(sub, RNA_boolean_get(imfptr, "apply_modifiers"));
+ uiItemR(sub, imfptr, "export_mesh_type_selection", 0, "", ICON_NONE);
- row = uiLayoutRow(box, false);
- split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
- uiItemL(split, IFACE_("Transformation Type"), ICON_NONE);
if (RNA_boolean_get(imfptr, "include_animations")) {
- uiItemR(split, imfptr, "export_animation_transformation_type_selection", 0, "", ICON_NONE);
+ uiItemR(col, imfptr, "export_animation_transformation_type_selection", 0, NULL, ICON_NONE);
}
else {
- uiItemR(split, imfptr, "export_object_transformation_type_selection", 0, "", ICON_NONE);
+ uiItemR(col, imfptr, "export_object_transformation_type_selection", 0, NULL, ICON_NONE);
}
}
else if (ui_section == BC_UI_SECTION_ARMATURE) {
/* Armature options */
box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Armature Options:"), ICON_ARMATURE_DATA);
+ uiItemL(box, IFACE_("Armature Options"), ICON_ARMATURE_DATA);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "deform_bones_only", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "open_sim", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "deform_bones_only", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "open_sim", 0, NULL, ICON_NONE);
}
else if (ui_section == BC_UI_SECTION_ANIMATION) {
+ /* Animation options. */
+ box = uiLayoutBox(layout);
+ uiItemR(box, imfptr, "include_animations", 0, NULL, ICON_NONE);
- /* ====================== */
- /* Animation Data options */
- /* ====================== */
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "include_animations", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
+ col = uiLayoutColumn(box, false);
+ row = uiLayoutRow(col, false);
+ uiLayoutSetActive(row, include_animations);
uiItemR(row, imfptr, "export_animation_type_selection", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, include_animations);
- row = uiLayoutRow(box, false);
- split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
- uiItemL(split, IFACE_("Transformation Type"), ICON_NONE);
+ uiLayoutSetActive(row, include_animations && animation_type == BC_ANIMATION_EXPORT_SAMPLES);
if (RNA_boolean_get(imfptr, "include_animations")) {
- uiItemR(split, imfptr, "export_animation_transformation_type_selection", 0, "", ICON_NONE);
+ uiItemR(box, imfptr, "export_animation_transformation_type_selection", 0, NULL, ICON_NONE);
}
else {
- uiItemR(split, imfptr, "export_object_transformation_type_selection", 0, "", ICON_NONE);
+ uiItemR(box, imfptr, "export_object_transformation_type_selection", 0, NULL, ICON_NONE);
}
- uiLayoutSetEnabled(row, include_animations && animation_type == BC_ANIMATION_EXPORT_SAMPLES);
- row = uiLayoutColumn(box, false);
+ row = uiLayoutColumn(col, false);
+ uiLayoutSetActive(row,
+ include_animations &&
+ (animation_transformation_type == BC_TRANSFORMATION_TYPE_DECOMPOSED ||
+ animation_type == BC_ANIMATION_EXPORT_KEYS));
uiItemR(row, imfptr, "keep_smooth_curves", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row,
- include_animations &&
- (animation_transformation_type == BC_TRANSFORMATION_TYPE_DECOMPOSED ||
- animation_type == BC_ANIMATION_EXPORT_KEYS));
-
- row = uiLayoutColumn(box, false);
- uiItemR(row, imfptr, "sampling_rate", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, sampling && include_animations);
-
- row = uiLayoutColumn(box, false);
- uiItemR(row, imfptr, "keep_keyframes", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, sampling && include_animations);
- row = uiLayoutColumn(box, false);
- uiItemR(row, imfptr, "keep_flat_curves", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, include_animations);
+ sub = uiLayoutColumn(col, false);
+ uiLayoutSetActive(sub, sampling && include_animations);
+ uiItemR(sub, imfptr, "sampling_rate", 0, NULL, ICON_NONE);
+ uiItemR(sub, imfptr, "keep_keyframes", 0, NULL, ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "include_all_actions", 0, NULL, ICON_NONE);
- uiLayoutSetEnabled(row, include_animations);
+ sub = uiLayoutColumn(col, false);
+ uiLayoutSetActive(sub, include_animations);
+ uiItemR(sub, imfptr, "keep_flat_curves", 0, NULL, ICON_NONE);
+ uiItemR(sub, imfptr, "include_all_actions", 0, NULL, ICON_NONE);
}
else if (ui_section == BC_UI_SECTION_COLLADA) {
/* Collada options: */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Collada Options:"), ICON_MODIFIER);
+ uiItemL(row, IFACE_("Collada Options"), ICON_MODIFIER);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "use_object_instantiation", 1, NULL, ICON_NONE);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "use_blender_profile", 1, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "sort_by_name", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "limit_precision", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "use_object_instantiation", 1, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "use_blender_profile", 1, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "sort_by_name", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "limit_precision", 0, NULL, ICON_NONE);
}
}
@@ -465,28 +429,28 @@ void WM_OT_collada_export(wmOperatorType *ot)
struct StructRNA *func = ot->srna;
static const EnumPropertyItem prop_bc_export_mesh_type[] = {
- {BC_MESH_TYPE_VIEW, "view", 0, "View", "Apply modifier's view settings"},
+ {BC_MESH_TYPE_VIEW, "view", 0, "Viewport", "Apply modifier's viewport settings"},
{BC_MESH_TYPE_RENDER, "render", 0, "Render", "Apply modifier's render settings"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem prop_bc_export_global_forward[] = {
- {BC_GLOBAL_FORWARD_X, "X", 0, "X Forward", "Global Forward is positive X Axis"},
- {BC_GLOBAL_FORWARD_Y, "Y", 0, "Y Forward", "Global Forward is positive Y Axis"},
- {BC_GLOBAL_FORWARD_Z, "Z", 0, "Z Forward", "Global Forward is positive Z Axis"},
- {BC_GLOBAL_FORWARD_MINUS_X, "-X", 0, "-X Forward", "Global Forward is negative X Axis"},
- {BC_GLOBAL_FORWARD_MINUS_Y, "-Y", 0, "-Y Forward", "Global Forward is negative Y Axis"},
- {BC_GLOBAL_FORWARD_MINUS_Z, "-Z", 0, "-Z Forward", "Global Forward is negative Z Axis"},
+ {BC_GLOBAL_FORWARD_X, "X", 0, "X", "Global Forward is positive X Axis"},
+ {BC_GLOBAL_FORWARD_Y, "Y", 0, "Y", "Global Forward is positive Y Axis"},
+ {BC_GLOBAL_FORWARD_Z, "Z", 0, "Z", "Global Forward is positive Z Axis"},
+ {BC_GLOBAL_FORWARD_MINUS_X, "-X", 0, "-X", "Global Forward is negative X Axis"},
+ {BC_GLOBAL_FORWARD_MINUS_Y, "-Y", 0, "-Y", "Global Forward is negative Y Axis"},
+ {BC_GLOBAL_FORWARD_MINUS_Z, "-Z", 0, "-Z", "Global Forward is negative Z Axis"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem prop_bc_export_global_up[] = {
- {BC_GLOBAL_UP_X, "X", 0, "X Up", "Global UP is positive X Axis"},
- {BC_GLOBAL_UP_Y, "Y", 0, "Y Up", "Global UP is positive Y Axis"},
- {BC_GLOBAL_UP_Z, "Z", 0, "Z Up", "Global UP is positive Z Axis"},
- {BC_GLOBAL_UP_MINUS_X, "-X", 0, "-X Up", "Global UP is negative X Axis"},
- {BC_GLOBAL_UP_MINUS_Y, "-Y", 0, "-Y Up", "Global UP is negative Y Axis"},
- {BC_GLOBAL_UP_MINUS_Z, "-Z", 0, "-Z Up", "Global UP is negative Z Axis"},
+ {BC_GLOBAL_UP_X, "X", 0, "X", "Global UP is positive X Axis"},
+ {BC_GLOBAL_UP_Y, "Y", 0, "Y", "Global UP is positive Y Axis"},
+ {BC_GLOBAL_UP_Z, "Z", 0, "Z", "Global UP is positive Z Axis"},
+ {BC_GLOBAL_UP_MINUS_X, "-X", 0, "-X", "Global UP is negative X Axis"},
+ {BC_GLOBAL_UP_MINUS_Y, "-Y", 0, "-Y", "Global UP is negative Y Axis"},
+ {BC_GLOBAL_UP_MINUS_Z, "-Z", 0, "-Z", "Global UP is negative Z Axis"},
{0, NULL, 0, NULL, NULL},
};
@@ -619,7 +583,7 @@ void WM_OT_collada_export(wmOperatorType *ot)
RNA_def_boolean(func,
"deform_bones_only",
false,
- "Deform Bones only",
+ "Deform Bones Only",
"Only export deforming bones with armatures");
RNA_def_boolean(
@@ -672,7 +636,7 @@ void WM_OT_collada_export(wmOperatorType *ot)
RNA_def_boolean(func,
"keep_flat_curves",
0,
- "All keyed curves",
+ "All Keyed Curves",
"Export also curves which have only one key or are totally flat");
RNA_def_boolean(
@@ -803,45 +767,36 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS);
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Parsing errors in Document (see Blender Console)");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "Parsing errors in Document (see Blender Console)");
+ return OPERATOR_CANCELLED;
}
static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
{
- uiLayout *box, *row;
+ uiLayout *box, *col;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
/* Import Options: */
box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Import Data Options:"), ICON_MESH_DATA);
+ uiItemL(box, IFACE_("Import Data Options"), ICON_MESH_DATA);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "import_units", 0, NULL, ICON_NONE);
+ uiItemR(box, imfptr, "import_units", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- uiItemL(row, IFACE_("Armature Options:"), ICON_MESH_DATA);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "fix_orientation", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "find_chains", 0, NULL, ICON_NONE);
-
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "auto_connect", 0, NULL, ICON_NONE);
+ uiItemL(box, IFACE_("Armature Options"), ICON_ARMATURE_DATA);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "min_chain_length", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "fix_orientation", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "find_chains", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "auto_connect", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "min_chain_length", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
- row = uiLayoutRow(box, false);
- row = uiLayoutRow(box, false);
- uiItemR(row, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
+ uiItemR(box, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
}
static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op)
@@ -857,6 +812,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/io/io_usd.c b/source/blender/editors/io/io_usd.c
index 262b15c63e5..096dc44c758 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -145,21 +145,23 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op)
uiLayoutSetPropSep(layout, true);
- col = uiLayoutColumn(layout, true);
+ uiLayout *box = uiLayoutBox(layout);
+
+ col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "selected_objects_only", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(layout, true);
+ col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "export_animation", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "export_hair", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "export_uvmaps", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "export_normals", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "export_materials", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(layout, true);
+ col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "evaluation_mode", 0, NULL, ICON_NONE);
- uiLayout *box = uiLayoutBox(layout);
- uiItemL(box, IFACE_("Experimental:"), ICON_NONE);
+ box = uiLayoutBox(layout);
+ uiItemL(box, IFACE_("Experimental"), ICON_NONE);
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
}
diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c
index 8751a289b9c..49cf4779496 100644
--- a/source/blender/editors/lattice/editlattice_select.c
+++ b/source/blender/editors/lattice/editlattice_select.c
@@ -268,18 +268,17 @@ void LATTICE_OT_select_mirror(wmOperatorType *ot)
* \{ */
static bool lattice_test_bitmap_uvw(
- Lattice *lt, BLI_bitmap *selpoints, int u, int v, int w, const bool selected)
+ Lattice *lt, const BLI_bitmap *selpoints, int u, int v, int w, const bool selected)
{
if ((u < 0 || u >= lt->pntsu) || (v < 0 || v >= lt->pntsv) || (w < 0 || w >= lt->pntsw)) {
return false;
}
- else {
- int i = BKE_lattice_index_from_uvw(lt, u, v, w);
- if (lt->def[i].hide == 0) {
- return (BLI_BITMAP_TEST(selpoints, i) != 0) == selected;
- }
- return false;
+
+ int i = BKE_lattice_index_from_uvw(lt, u, v, w);
+ if (lt->def[i].hide == 0) {
+ return (BLI_BITMAP_TEST(selpoints, i) != 0) == selected;
}
+ return false;
}
static int lattice_select_more_less(bContext *C, const bool select)
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 767976b5ae6..c19a5b8ef68 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -210,7 +210,7 @@ static void finSelectedSplinePoint(MaskLayer *mask_layer,
*point = NULL;
return;
}
- else if (*point) {
+ if (*point) {
*point = NULL;
}
else {
@@ -319,9 +319,7 @@ static bool add_vertex_extrude(const bContext *C,
if (!mask_layer) {
return false;
}
- else {
- finSelectedSplinePoint(mask_layer, &spline, &point, true);
- }
+ finSelectedSplinePoint(mask_layer, &spline, &point, true);
ED_mask_select_toggle_all(mask, SEL_DESELECT);
@@ -441,7 +439,7 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer,
/* Convert coordinate from normalized space to pixel one.
* TODO(sergey): Make the function more generally available. */
static void mask_point_make_pixel_space(bContext *C,
- float point_normalized[2],
+ const float point_normalized[2],
float point_pixel[2])
{
ScrArea *area = CTX_wm_area(C);
@@ -502,7 +500,7 @@ static int add_vertex_handle_cyclic(
if (is_last_point_active) {
return add_vertex_handle_cyclic_at_point(C, mask, spline, active_point, first_point, co);
}
- else if (is_first_point_active) {
+ if (is_first_point_active) {
return add_vertex_handle_cyclic_at_point(C, mask, spline, active_point, last_point, co);
}
return OPERATOR_PASS_THROUGH;
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index 3786ed2789c..12ce358a501 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -753,8 +753,7 @@ void ED_mask_draw_region(
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_uniform_vector(
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTex(
- &state, 0.0f, 0.0f, width, height, GL_RED, GL_FLOAT, GL_NEAREST, buffer, 1.0f, 1.0f, NULL);
+ immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL);
GPU_matrix_pop();
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 68dfe0d151f..51f3a94efde 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -1589,12 +1589,12 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op)
return OPERATOR_CANCELLED;
}
-/* named to match mesh recalc normals */
+/* Named to match mesh recalculate normals. */
void MASK_OT_normals_make_consistent(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Recalc Normals";
- ot->description = "Re-calculate the direction of selected handles";
+ ot->name = "Recalculate Handles";
+ ot->description = "Recalculate the direction of selected handles";
ot->idname = "MASK_OT_normals_make_consistent";
/* api callbacks */
@@ -1710,9 +1710,7 @@ static int mask_hide_view_clear_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_hide_view_clear(wmOperatorType *ot)
@@ -1773,9 +1771,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_hide_view_set(wmOperatorType *ot)
@@ -1827,9 +1823,7 @@ static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_feather_weight_clear(wmOperatorType *ot)
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index c8cddced99c..82d8a1dc85f 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -331,53 +331,52 @@ static int select_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- MaskSplinePointUW *uw;
- if (ED_mask_feather_find_nearest(
- C, mask, co, threshold, &mask_layer, &spline, &point, &uw, NULL)) {
+ MaskSplinePointUW *uw;
- if (extend) {
- mask_layer->act_spline = spline;
- mask_layer->act_point = point;
+ if (ED_mask_feather_find_nearest(
+ C, mask, co, threshold, &mask_layer, &spline, &point, &uw, NULL)) {
- if (uw) {
- uw->flag |= SELECT;
- }
+ if (extend) {
+ mask_layer->act_spline = spline;
+ mask_layer->act_point = point;
+
+ if (uw) {
+ uw->flag |= SELECT;
}
- else if (deselect) {
- if (uw) {
- uw->flag &= ~SELECT;
- }
+ }
+ else if (deselect) {
+ if (uw) {
+ uw->flag &= ~SELECT;
}
- else {
- mask_layer->act_spline = spline;
- mask_layer->act_point = point;
+ }
+ else {
+ mask_layer->act_spline = spline;
+ mask_layer->act_point = point;
- if (uw) {
- if (!(uw->flag & SELECT)) {
- uw->flag |= SELECT;
- }
- else if (toggle) {
- uw->flag &= ~SELECT;
- }
+ if (uw) {
+ if (!(uw->flag & SELECT)) {
+ uw->flag |= SELECT;
+ }
+ else if (toggle) {
+ uw->flag &= ~SELECT;
}
}
+ }
- ED_mask_select_flush_all(mask);
+ ED_mask_select_flush_all(mask);
- DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ return OPERATOR_FINISHED;
+ }
+ if (deselect_all) {
+ /* For clip editor tracks, leave deselect all to clip editor. */
+ if (!ED_clip_can_select(C)) {
+ ED_mask_deselect_all(C);
return OPERATOR_FINISHED;
}
- else if (deselect_all) {
- /* For clip editor tracks, leave deselect all to clip editor. */
- if (!ED_clip_can_select(C)) {
- ED_mask_deselect_all(C);
- return OPERATOR_FINISHED;
- }
- }
}
return OPERATOR_PASS_THROUGH;
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index f264e67d35c..74348f2c8cf 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -71,9 +71,7 @@ static int mask_shape_key_insert_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_insert(wmOperatorType *ot)
@@ -119,9 +117,7 @@ static int mask_shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_clear(wmOperatorType *ot)
@@ -205,9 +201,7 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
@@ -365,9 +359,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_rekey(wmOperatorType *ot)
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index e94412233ff..61b40dd3e60 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -115,13 +115,13 @@ enum {
BEV_MODAL_SEGMENTS_DOWN,
BEV_MODAL_OFFSET_MODE_CHANGE,
BEV_MODAL_CLAMP_OVERLAP_TOGGLE,
- BEV_MODAL_VERTEX_ONLY_TOGGLE,
+ BEV_MODAL_AFFECT_CHANGE,
BEV_MODAL_HARDEN_NORMALS_TOGGLE,
BEV_MODAL_MARK_SEAM_TOGGLE,
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, *affect_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);
@@ -179,6 +182,9 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
prop = RNA_struct_find_property(op->ptr, "vmesh_method");
RNA_property_enum_name_gettexted(
C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &vmesh_str);
+ prop = RNA_struct_find_property(op->ptr, "affect");
+ RNA_property_enum_name_gettexted(
+ C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &affect_str);
BLI_snprintf(status_text,
sizeof(status_text),
@@ -189,13 +195,13 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
"%s: Segments (%d), "
"%s: Profile (%.3f), "
"%s: Clamp Overlap (%s), "
- "%s: Vertex Only (%s), "
+ "%s: Affect (%s), "
"%s: Outer Miter (%s), "
"%s: Inner Miter (%s), "
"%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),
@@ -209,8 +215,8 @@ static void edbm_bevel_update_status_text(bContext *C, wmOperator *op)
RNA_float_get(op->ptr, "profile"),
WM_MODALKEY(BEV_MODAL_CLAMP_OVERLAP_TOGGLE),
WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")),
- WM_MODALKEY(BEV_MODAL_VERTEX_ONLY_TOGGLE),
- WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")),
+ WM_MODALKEY(BEV_MODAL_AFFECT_CHANGE),
+ affect_str,
WM_MODALKEY(BEV_MODAL_OUTER_MITER_CHANGE),
omiter_str,
WM_MODALKEY(BEV_MODAL_INNER_MITER_CHANGE),
@@ -221,8 +227,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,9 +333,10 @@ 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");
+ const bool affect = RNA_enum_get(op->ptr, "affect");
const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
const int material_init = RNA_int_get(op->ptr, "material");
const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide");
@@ -340,7 +347,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 +370,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 affect=%i 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,
+ affect,
offset_type,
+ profile_type,
profile,
clamp_overlap,
material,
@@ -386,7 +393,6 @@ static bool edbm_bevel_calc(wmOperator *op)
miter_inner,
spread,
me->smoothresh,
- use_custom_profile,
opdata->custom_profile,
vmesh_method);
@@ -623,7 +629,7 @@ static bool edbm_bevel_poll_property(const bContext *UNUSED(C),
if (STREQ(prop_id, "offset") && offset_type == BEVEL_AMT_PERCENT) {
return false;
}
- else if (STREQ(prop_id, "offset_pct") && offset_type != BEVEL_AMT_PERCENT) {
+ if (STREQ(prop_id, "offset_pct") && offset_type != BEVEL_AMT_PERCENT) {
return false;
}
}
@@ -651,11 +657,11 @@ wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
0,
"Toggle clamp overlap",
"Toggle clamp overlap flag"},
- {BEV_MODAL_VERTEX_ONLY_TOGGLE,
- "VERTEX_ONLY_TOGGLE",
+ {BEV_MODAL_AFFECT_CHANGE,
+ "AFFECT_CHANGE",
0,
- "Toggle vertex only",
- "Toggle vertex only flag"},
+ "Change affect type",
+ "Change which geometry type the operation affects, edges or vertices"},
{BEV_MODAL_HARDEN_NORMALS_TOGGLE,
"HARDEN_NORMALS_TOGGLE",
0,
@@ -681,7 +687,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,
@@ -726,7 +732,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
edbm_bevel_update_status_text(C, op);
return OPERATOR_RUNNING_MODAL;
}
- else if (etype == MOUSEMOVE) {
+ if (etype == MOUSEMOVE) {
if (!has_numinput) {
edbm_bevel_mouse_set_value(op, event);
edbm_bevel_calc(op);
@@ -827,9 +833,13 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
edbm_bevel_calc_initial_length(op, event, true);
break;
- case BEV_MODAL_VERTEX_ONLY_TOGGLE: {
- bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
- RNA_boolean_set(op->ptr, "vertex_only", !vertex_only);
+ case BEV_MODAL_AFFECT_CHANGE: {
+ int affect_type = RNA_enum_get(op->ptr, "affect");
+ affect_type++;
+ if (affect_type > BEVEL_AFFECT_EDGES) {
+ affect_type = BEVEL_AFFECT_VERTICES;
+ }
+ RNA_enum_set(op->ptr, "affect", affect_type);
edbm_bevel_calc(op);
edbm_bevel_update_status_text(C, op);
handled = true;
@@ -892,9 +902,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;
@@ -931,68 +945,77 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void edbm_bevel_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
- uiLayout *row, *col, *split;
+ uiLayout *col, *row;
PointerRNA ptr, toolsettings_ptr;
- PropertyRNA *prop;
- const char *offset_name;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- if (RNA_enum_get(&ptr, "offset_type") == BEVEL_AMT_PERCENT) {
+ int profile_type = RNA_enum_get(&ptr, "profile_type");
+ int offset_type = RNA_enum_get(&ptr, "offset_type");
+ bool affect_type = RNA_enum_get(&ptr, "affect");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ uiItemS(layout);
+
+ uiItemR(layout, &ptr, "offset_type", 0, NULL, ICON_NONE);
+
+ if (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);
- uiItemR(layout, &ptr, "offset", 0, offset_name, ICON_NONE);
+ uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
+ }
+
+ uiItemR(layout, &ptr, "segments", 0, 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);
}
- row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "offset_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
- split = uiLayoutSplit(layout, 0.5f, true);
- col = uiLayoutColumn(split, true);
- uiItemR(col, &ptr, "vertex_only", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE);
uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE);
uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(split, true);
- uiItemR(col, &ptr, "mark_seam", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "mark_sharp", 0, NULL, ICON_NONE);
- 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);
- uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
+ col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark"));
+ uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
+ uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
+ uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
+
+ uiItemS(layout);
- uiItemL(layout, "Miter Type:", ICON_NONE);
- uiItemR(layout, &ptr, "miter_outer", 0, "Outer", ICON_NONE);
- uiItemR(layout, &ptr, "miter_inner", 0, "Inner", ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
+ uiItemR(col, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
+ uiItemR(col, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) {
- uiItemR(layout, &ptr, "spread", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "spread", 0, NULL, ICON_NONE);
}
- uiItemL(layout, "Face Strength Mode:", ICON_NONE);
- row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "face_strength_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
+
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
+ uiItemR(col, &ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
+
+ uiItemR(layout, &ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
- uiItemL(layout, "Intersection Type:", ICON_NONE);
- row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "vmesh_method", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemS(layout);
- 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 */
+ row = uiLayoutRow(layout, false);
+ 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 +1035,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},
};
@@ -1050,6 +1092,12 @@ void MESH_OT_bevel(wmOperatorType *ot)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem prop_affect_items[] = {
+ {BEVEL_AFFECT_VERTICES, "VERTICES", 0, "Vertices", "Affect only vertices"},
+ {BEVEL_AFFECT_EDGES, "EDGES", 0, "Edges", "Affect only edges"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
ot->name = "Bevel";
ot->description = "Cut into selected items at an angle to create bevel or chamfer";
@@ -1068,13 +1116,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");
@@ -1099,7 +1158,12 @@ void MESH_OT_bevel(wmOperatorType *ot)
PROFILE_HARD_MIN,
1.0f);
- RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
+ prop = RNA_def_enum(ot->srna,
+ "affect",
+ prop_affect_items,
+ BEVEL_AFFECT_EDGES,
+ "Affect",
+ "Affect Edges or Vertices");
RNA_def_boolean(ot->srna,
"clamp_overlap",
@@ -1119,7 +1183,7 @@ void MESH_OT_bevel(wmOperatorType *ot)
-1,
-1,
INT_MAX,
- "Material",
+ "Material Index",
"Material for bevel faces (-1 means use adjacent faces)",
-1,
100);
@@ -1161,12 +1225,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_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index bf6c5a2f829..5ad1b5d67da 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -424,10 +424,9 @@ static bool edbm_extrude_mesh(Object *obedit, BMEditMesh *em, wmOperator *op)
if (changed) {
return true;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
- return false;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
+ return false;
}
/* extrude without transform */
diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c
index 2eeada95eda..8d9837f123a 100644
--- a/source/blender/editors/mesh/editmesh_inset.c
+++ b/source/blender/editors/mesh/editmesh_inset.c
@@ -313,10 +313,9 @@ static bool edbm_inset_calc(wmOperator *op)
if (!EDBM_op_finish(em, &bmop, op, true)) {
continue;
}
- else {
- EDBM_update_generic(obedit->data, true, true);
- changed = true;
- }
+
+ EDBM_update_generic(obedit->data, true, true);
+ changed = true;
}
return changed;
}
@@ -387,186 +386,181 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event)
edbm_inset_update_header(op, C);
return OPERATOR_RUNNING_MODAL;
}
- else {
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
}
- else if ((event->type == opdata->launch_event) && (event->val == KM_RELEASE) &&
- RNA_boolean_get(op->ptr, "release_confirm")) {
+ if ((event->type == opdata->launch_event) && (event->val == KM_RELEASE) &&
+ RNA_boolean_get(op->ptr, "release_confirm")) {
edbm_inset_calc(op);
edbm_inset_exit(C, op);
return OPERATOR_FINISHED;
}
- else {
- bool handled = false;
- switch (event->type) {
- case EVT_ESCKEY:
- case RIGHTMOUSE:
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
-
- case MOUSEMOVE:
- if (!has_numinput) {
- float mdiff[2];
- float amount;
-
- mdiff[0] = opdata->mcenter[0] - event->mval[0];
- mdiff[1] = opdata->mcenter[1] - event->mval[1];
-
- if (opdata->modify_depth) {
- amount = opdata->old_depth +
- ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size) /
- opdata->max_obj_scale;
- }
- else {
- amount = opdata->old_thickness -
- ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size) /
- opdata->max_obj_scale;
- }
-
- /* Fake shift-transform... */
- if (opdata->shift) {
- amount = (amount - opdata->shift_amount) * 0.1f + opdata->shift_amount;
- }
-
- if (opdata->modify_depth) {
- RNA_float_set(op->ptr, "depth", amount);
- }
- else {
- amount = max_ff(amount, 0.0f);
- RNA_float_set(op->ptr, "thickness", amount);
- }
-
- if (edbm_inset_calc(op)) {
- edbm_inset_update_header(op, C);
- }
- else {
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
- handled = true;
- }
- break;
-
- case LEFTMOUSE:
- case EVT_PADENTER:
- case EVT_RETKEY:
- if ((event->val == KM_PRESS) ||
- ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm"))) {
- edbm_inset_calc(op);
- edbm_inset_exit(C, op);
- return OPERATOR_FINISHED;
- }
- break;
- case EVT_LEFTSHIFTKEY:
- case EVT_RIGHTSHIFTKEY:
- if (event->val == KM_PRESS) {
- if (opdata->modify_depth) {
- opdata->shift_amount = RNA_float_get(op->ptr, "depth");
- }
- else {
- opdata->shift_amount = RNA_float_get(op->ptr, "thickness");
- }
- opdata->shift = true;
- handled = true;
+
+ bool handled = false;
+ switch (event->type) {
+ case EVT_ESCKEY:
+ case RIGHTMOUSE:
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
+
+ case MOUSEMOVE:
+ if (!has_numinput) {
+ float mdiff[2];
+ float amount;
+
+ mdiff[0] = opdata->mcenter[0] - event->mval[0];
+ mdiff[1] = opdata->mcenter[1] - event->mval[1];
+
+ if (opdata->modify_depth) {
+ amount = opdata->old_depth +
+ ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size) /
+ opdata->max_obj_scale;
}
else {
- opdata->shift_amount = 0.0f;
- opdata->shift = false;
- handled = true;
+ amount = opdata->old_thickness -
+ ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size) /
+ opdata->max_obj_scale;
}
- break;
- case EVT_LEFTCTRLKEY:
- case EVT_RIGHTCTRLKEY: {
- float mlen[2];
-
- mlen[0] = opdata->mcenter[0] - event->mval[0];
- mlen[1] = opdata->mcenter[1] - event->mval[1];
+ /* Fake shift-transform... */
+ if (opdata->shift) {
+ amount = (amount - opdata->shift_amount) * 0.1f + opdata->shift_amount;
+ }
- if (event->val == KM_PRESS) {
- opdata->old_thickness = RNA_float_get(op->ptr, "thickness");
- if (opdata->shift) {
- opdata->shift_amount = opdata->old_thickness;
- }
- opdata->modify_depth = true;
+ if (opdata->modify_depth) {
+ RNA_float_set(op->ptr, "depth", amount);
}
else {
- opdata->old_depth = RNA_float_get(op->ptr, "depth");
- if (opdata->shift) {
- opdata->shift_amount = opdata->old_depth;
- }
- opdata->modify_depth = false;
+ amount = max_ff(amount, 0.0f);
+ RNA_float_set(op->ptr, "thickness", amount);
}
- opdata->initial_length = len_v2(mlen);
- edbm_inset_update_header(op, C);
+ if (edbm_inset_calc(op)) {
+ edbm_inset_update_header(op, C);
+ }
+ else {
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
handled = true;
- break;
}
-
- case EVT_OKEY:
- if (event->val == KM_PRESS) {
- const bool use_outset = RNA_boolean_get(op->ptr, "use_outset");
- RNA_boolean_set(op->ptr, "use_outset", !use_outset);
- if (edbm_inset_calc(op)) {
- edbm_inset_update_header(op, C);
- }
- else {
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
- handled = true;
+ break;
+
+ case LEFTMOUSE:
+ case EVT_PADENTER:
+ case EVT_RETKEY:
+ if ((event->val == KM_PRESS) ||
+ ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm"))) {
+ edbm_inset_calc(op);
+ edbm_inset_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY:
+ if (event->val == KM_PRESS) {
+ if (opdata->modify_depth) {
+ opdata->shift_amount = RNA_float_get(op->ptr, "depth");
+ }
+ else {
+ opdata->shift_amount = RNA_float_get(op->ptr, "thickness");
}
- break;
- case EVT_BKEY:
- if (event->val == KM_PRESS) {
- const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
- RNA_boolean_set(op->ptr, "use_boundary", !use_boundary);
- if (edbm_inset_calc(op)) {
- edbm_inset_update_header(op, C);
- }
- else {
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
- handled = true;
+ opdata->shift = true;
+ handled = true;
+ }
+ else {
+ opdata->shift_amount = 0.0f;
+ opdata->shift = false;
+ handled = true;
+ }
+ break;
+
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ float mlen[2];
+
+ mlen[0] = opdata->mcenter[0] - event->mval[0];
+ mlen[1] = opdata->mcenter[1] - event->mval[1];
+
+ if (event->val == KM_PRESS) {
+ opdata->old_thickness = RNA_float_get(op->ptr, "thickness");
+ if (opdata->shift) {
+ opdata->shift_amount = opdata->old_thickness;
}
- break;
- case EVT_IKEY:
- if (event->val == KM_PRESS) {
- const bool use_individual = RNA_boolean_get(op->ptr, "use_individual");
- RNA_boolean_set(op->ptr, "use_individual", !use_individual);
- if (edbm_inset_calc(op)) {
- edbm_inset_update_header(op, C);
- }
- else {
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
- handled = true;
+ opdata->modify_depth = true;
+ }
+ else {
+ opdata->old_depth = RNA_float_get(op->ptr, "depth");
+ if (opdata->shift) {
+ opdata->shift_amount = opdata->old_depth;
}
- break;
+ opdata->modify_depth = false;
+ }
+ opdata->initial_length = len_v2(mlen);
+
+ edbm_inset_update_header(op, C);
+ handled = true;
+ break;
}
- /* Modal numinput inactive, try to handle numeric inputs last... */
- if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input, event)) {
- float amounts[2] = {RNA_float_get(op->ptr, "thickness"), RNA_float_get(op->ptr, "depth")};
- applyNumInput(&opdata->num_input, amounts);
- amounts[0] = max_ff(amounts[0], 0.0f);
- RNA_float_set(op->ptr, "thickness", amounts[0]);
- RNA_float_set(op->ptr, "depth", amounts[1]);
-
- if (edbm_inset_calc(op)) {
- edbm_inset_update_header(op, C);
- return OPERATOR_RUNNING_MODAL;
+ case EVT_OKEY:
+ if (event->val == KM_PRESS) {
+ const bool use_outset = RNA_boolean_get(op->ptr, "use_outset");
+ RNA_boolean_set(op->ptr, "use_outset", !use_outset);
+ if (edbm_inset_calc(op)) {
+ edbm_inset_update_header(op, C);
+ }
+ else {
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ handled = true;
}
- else {
- edbm_inset_cancel(C, op);
- return OPERATOR_CANCELLED;
+ break;
+ case EVT_BKEY:
+ if (event->val == KM_PRESS) {
+ const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
+ RNA_boolean_set(op->ptr, "use_boundary", !use_boundary);
+ if (edbm_inset_calc(op)) {
+ edbm_inset_update_header(op, C);
+ }
+ else {
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ handled = true;
+ }
+ break;
+ case EVT_IKEY:
+ if (event->val == KM_PRESS) {
+ const bool use_individual = RNA_boolean_get(op->ptr, "use_individual");
+ RNA_boolean_set(op->ptr, "use_individual", !use_individual);
+ if (edbm_inset_calc(op)) {
+ edbm_inset_update_header(op, C);
+ }
+ else {
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ handled = true;
}
+ break;
+ }
+
+ /* Modal numinput inactive, try to handle numeric inputs last... */
+ if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input, event)) {
+ float amounts[2] = {RNA_float_get(op->ptr, "thickness"), RNA_float_get(op->ptr, "depth")};
+ applyNumInput(&opdata->num_input, amounts);
+ amounts[0] = max_ff(amounts[0], 0.0f);
+ RNA_float_set(op->ptr, "thickness", amounts[0]);
+ RNA_float_set(op->ptr, "depth", amounts[1]);
+
+ if (edbm_inset_calc(op)) {
+ edbm_inset_update_header(op, C);
+ return OPERATOR_RUNNING_MODAL;
}
+ edbm_inset_cancel(C, op);
+ return OPERATOR_CANCELLED;
}
return OPERATOR_RUNNING_MODAL;
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index fd7cc2733ec..97bd6ee0039 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -60,9 +60,7 @@ static int bm_face_isect_self(BMFace *f, void *UNUSED(user_data))
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
return 0;
}
- else {
- return -1;
- }
+ return -1;
}
/**
@@ -73,12 +71,10 @@ static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
return -1;
}
- else if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
/**
@@ -90,12 +86,10 @@ static int bm_face_isect_pair_swap(BMFace *f, void *UNUSED(user_data))
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
return -1;
}
- else if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
return 0;
}
- else {
- return 1;
- }
+ return 1;
}
/**
@@ -515,12 +509,10 @@ static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
if (val_a > val_b) {
return 1;
}
- else if (val_a < val_b) {
+ if (val_a < val_b) {
return -1;
}
- else {
- return 0;
- }
+ return 0;
}
static void bm_face_split_by_edges_island_connect(
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 5f5599b53df..1f411617c68 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -102,7 +102,7 @@ typedef struct KnifeVert {
ListBase edges;
ListBase faces;
- float co[3], cageco[3], sco[2]; /* sco is screen coordinates for cageco */
+ float co[3], cageco[3];
bool is_face, in_space;
bool is_cut; /* along a cut created by user input (will draw too) */
} KnifeVert;
@@ -429,8 +429,6 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const
copy_v3_v3(kfv->co, co);
copy_v3_v3(kfv->cageco, cageco);
- knife_project_v2(kcd, kfv->cageco, kfv->sco);
-
return kfv;
}
@@ -649,28 +647,22 @@ static int linehit_compare(const void *vlh1, const void *vlh2)
if (lh1->l < lh2->l) {
return -1;
}
- else if (lh1->l > lh2->l) {
+ if (lh1->l > lh2->l) {
return 1;
}
- else {
- if (lh1->m < lh2->m) {
- return -1;
- }
- else if (lh1->m > lh2->m) {
- return 1;
- }
- else {
- if (lh1->v < lh2->v) {
- return -1;
- }
- else if (lh1->v > lh2->v) {
- return 1;
- }
- else {
- return 0;
- }
- }
+ if (lh1->m < lh2->m) {
+ return -1;
}
+ if (lh1->m > lh2->m) {
+ return 1;
+ }
+ if (lh1->v < lh2->v) {
+ return -1;
+ }
+ if (lh1->v > lh2->v) {
+ return 1;
+ }
+ return 0;
}
/*
@@ -816,11 +808,9 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd,
kfe->e = e_base;
return;
}
- else {
- if (knife_add_single_cut__is_linehit_outside_face(f, lh1, lh2->hit) ||
- knife_add_single_cut__is_linehit_outside_face(f, lh2, lh1->hit)) {
- return;
- }
+ if (knife_add_single_cut__is_linehit_outside_face(f, lh1, lh2->hit) ||
+ knife_add_single_cut__is_linehit_outside_face(f, lh2, lh1->hit)) {
+ return;
}
/* Check if edge actually lies within face (might not, if this face is concave) */
@@ -1063,7 +1053,8 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v
const KnifeTool_OpData *kcd = arg;
GPU_depth_test(false);
- glPolygonOffset(1.0f, 1.0f);
+ GPU_matrix_push_projection();
+ GPU_polygon_offset(1.0f, 1.0f);
GPU_matrix_push();
GPU_matrix_mul(kcd->ob->obmat);
@@ -1234,6 +1225,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v
immUnbindProgram();
GPU_matrix_pop();
+ GPU_matrix_pop_projection();
/* Reset default */
GPU_depth_test(true);
@@ -1405,9 +1397,7 @@ static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
(equals_v3v3(co11, co22) && equals_v3v3(co12, co21))) {
return true;
}
- else {
- return false;
- }
+ return false;
}
/* Callback used in point_is_visible to exclude hits on the faces that are the same
@@ -1558,8 +1548,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
{
SmallHash faces, kfes, kfvs;
float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2];
- BVHTree *planetree, *tree;
- BVHTreeOverlap *results, *result;
+ BVHTree *tree;
+ int *results, *result;
BMLoop **ls;
BMFace *f;
KnifeEdge *kfe;
@@ -1572,18 +1562,14 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
KnifeLineHit hit;
void *val;
void **val_p;
- float plane_cos[12];
float s[2], se1[2], se2[2], sint[2];
float r1[3], r2[3];
- float d, d1, d2, lambda;
+ float d1, d2, lambda;
float vert_tol, vert_tol_sq;
float line_tol, line_tol_sq;
float face_tol, face_tol_sq;
- int isect_kind;
uint tot;
int i;
- const bool use_hit_prev = true;
- const bool use_hit_curr = (kcd->is_drag_hold == false);
if (kcd->linehits) {
MEM_freeN(kcd->linehits);
@@ -1633,22 +1619,22 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
clip_to_ortho_planes(v2, v4, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f);
}
- /* First use bvh tree to find faces, knife edges, and knife verts that might
+ float plane[4];
+ {
+ float v1_v2[3], v1_v3[3];
+ sub_v3_v3v3(v1_v2, v2, v1);
+ sub_v3_v3v3(v1_v3, v3, v1);
+ cross_v3_v3v3(plane, v1_v2, v1_v3);
+ plane_from_point_normal_v3(plane, v1, plane);
+ }
+
+ /* First use BVH tree to find faces, knife edges, and knife verts that might
* intersect the cut plane with rays v1-v3 and v2-v4.
- * This deduplicates the candidates before doing more expensive intersection tests. */
+ * This de-duplicates the candidates before doing more expensive intersection tests. */
tree = BKE_bmbvh_tree_get(kcd->bmbvh);
- planetree = BLI_bvhtree_new(4, FLT_EPSILON * 4, 8, 8);
- copy_v3_v3(plane_cos + 0, v1);
- copy_v3_v3(plane_cos + 3, v2);
- copy_v3_v3(plane_cos + 6, v3);
- copy_v3_v3(plane_cos + 9, v4);
- BLI_bvhtree_insert(planetree, 0, plane_cos, 4);
- BLI_bvhtree_balance(planetree);
-
- results = BLI_bvhtree_overlap(tree, planetree, &tot, NULL, NULL);
+ results = BLI_bvhtree_intersect_plane(tree, plane, &tot);
if (!results) {
- BLI_bvhtree_free(planetree);
return;
}
@@ -1657,9 +1643,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_init(&kfvs);
for (i = 0, result = results; i < tot; i++, result++) {
- ls = (BMLoop **)kcd->em->looptris[result->indexA];
+ ls = (BMLoop **)kcd->em->looptris[*result];
f = ls[0]->f;
- set_lowest_face_tri(kcd, f, result->indexA);
+ set_lowest_face_tri(kcd, f, *result);
/* occlude but never cut unselected faces (when only_select is used) */
if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
@@ -1714,10 +1700,25 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
val_p = BLI_smallhash_iternext_p(&hiter, (uintptr_t *)&v)) {
KnifeEdge *kfe_hit = NULL;
- knife_project_v2(kcd, v->cageco, s);
- d = dist_squared_to_line_segment_v2(s, s1, s2);
- if ((d <= vert_tol_sq) &&
- (point_is_visible(kcd, v->cageco, s, bm_elem_from_knife_vert(v, &kfe_hit)))) {
+ bool kfv_is_in_cut = false;
+ if (ELEM(v, kcd->prev.vert, kcd->curr.vert)) {
+ /* This KnifeVert was captured by the snap system.
+ * Since the tolerance distance can be different, add this vertex directly.
+ * Otherwise, the cut may fail or a close cut on a connected edge can be performed. */
+ bm_elem_from_knife_vert(v, &kfe_hit);
+ copy_v2_v2(s, (v == kcd->prev.vert) ? kcd->prev.mval : kcd->curr.mval);
+ kfv_is_in_cut = true;
+ }
+ else {
+ knife_project_v2(kcd, v->cageco, s);
+ float d = dist_squared_to_line_segment_v2(s, s1, s2);
+ if ((d <= vert_tol_sq) &&
+ (point_is_visible(kcd, v->cageco, s, bm_elem_from_knife_vert(v, &kfe_hit)))) {
+ kfv_is_in_cut = true;
+ }
+ }
+
+ if (kfv_is_in_cut) {
memset(&hit, 0, sizeof(hit));
hit.v = v;
@@ -1735,7 +1736,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_array_append(linehits, hit);
}
else {
- /* note that these vertes aren't used */
+ /* This vertex isn't used so remove from `kfvs`.
+ * This is useful to detect KnifeEdges that can be skipped.
+ * And it optimizes smallhash_iternext a little bit. */
*val_p = NULL;
}
}
@@ -1743,30 +1746,38 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
/* now edge hits; don't add if a vertex at end of edge should have hit */
for (val = BLI_smallhash_iternew(&kfes, &hiter, (uintptr_t *)&kfe); val;
val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&kfe)) {
- int kfe_verts_in_cut;
- /* if we intersect both verts, don't attempt to intersect the edge */
-
- kfe_verts_in_cut = (BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v1) != NULL) +
- (BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v2) != NULL);
- if (kfe_verts_in_cut == 2) {
+ /* If we intersect any of the vertices, don't attempt to intersect the edge. */
+ if (BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v1) ||
+ BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v2)) {
continue;
}
knife_project_v2(kcd, kfe->v1->cageco, se1);
knife_project_v2(kcd, kfe->v2->cageco, se2);
- isect_kind = (kfe_verts_in_cut) ? -1 : isect_seg_seg_v2_point(s1, s2, se1, se2, sint);
- if (isect_kind == -1) {
- /* isect_seg_seg_v2_simple doesn't do tolerance test around ends of s1-s2 */
- closest_to_line_segment_v2(sint, s1, se1, se2);
- if (len_squared_v2v2(sint, s1) <= line_tol_sq) {
- isect_kind = 1;
- }
- else {
- closest_to_line_segment_v2(sint, s2, se1, se2);
- if (len_squared_v2v2(sint, s2) <= line_tol_sq) {
+ int isect_kind = 1;
+ if (kfe == kcd->prev.edge) {
+ /* This KnifeEdge was captured by the snap system. */
+ copy_v2_v2(sint, kcd->prev.mval);
+ }
+ else if (kfe == kcd->curr.edge) {
+ /* This KnifeEdge was captured by the snap system. */
+ copy_v2_v2(sint, kcd->curr.mval);
+ }
+ else {
+ isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, sint);
+ if (isect_kind == -1) {
+ /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2 */
+ closest_to_line_segment_v2(sint, s1, se1, se2);
+ if (len_squared_v2v2(sint, s1) <= line_tol_sq) {
isect_kind = 1;
}
+ else {
+ closest_to_line_segment_v2(sint, s2, se1, se2);
+ if (len_squared_v2v2(sint, s2) <= line_tol_sq) {
+ isect_kind = 1;
+ }
+ }
}
}
if (isect_kind == 1) {
@@ -1801,32 +1812,38 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
}
}
}
+
/* now face hits; don't add if a vertex or edge in face should have hit */
- for (val = BLI_smallhash_iternew(&faces, &hiter, (uintptr_t *)&f); val;
- val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) {
- float p[3], p_cage[3];
-
- if (use_hit_prev && knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol_sq, p, p_cage)) {
- if (point_is_visible(kcd, p_cage, s1, (BMElem *)f)) {
- memset(&hit, 0, sizeof(hit));
- hit.f = f;
- copy_v3_v3(hit.hit, p);
- copy_v3_v3(hit.cagehit, p_cage);
- copy_v2_v2(hit.schit, s1);
- set_linehit_depth(kcd, &hit);
- BLI_array_append(linehits, hit);
+ const bool use_hit_prev = (kcd->prev.vert == NULL) && (kcd->prev.edge == NULL);
+ const bool use_hit_curr = (kcd->curr.vert == NULL) && (kcd->curr.edge == NULL) &&
+ !kcd->is_drag_hold;
+ if (use_hit_prev || use_hit_curr) {
+ for (val = BLI_smallhash_iternew(&faces, &hiter, (uintptr_t *)&f); val;
+ val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) {
+ float p[3], p_cage[3];
+
+ if (use_hit_prev && knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol_sq, p, p_cage)) {
+ if (point_is_visible(kcd, p_cage, s1, (BMElem *)f)) {
+ memset(&hit, 0, sizeof(hit));
+ hit.f = f;
+ copy_v3_v3(hit.hit, p);
+ copy_v3_v3(hit.cagehit, p_cage);
+ copy_v2_v2(hit.schit, s1);
+ set_linehit_depth(kcd, &hit);
+ BLI_array_append(linehits, hit);
+ }
}
- }
- if (use_hit_curr && knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol_sq, p, p_cage)) {
- if (point_is_visible(kcd, p_cage, s2, (BMElem *)f)) {
- memset(&hit, 0, sizeof(hit));
- hit.f = f;
- copy_v3_v3(hit.hit, p);
- copy_v3_v3(hit.cagehit, p_cage);
- copy_v2_v2(hit.schit, s2);
- set_linehit_depth(kcd, &hit);
- BLI_array_append(linehits, hit);
+ if (use_hit_curr && knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol_sq, p, p_cage)) {
+ if (point_is_visible(kcd, p_cage, s2, (BMElem *)f)) {
+ memset(&hit, 0, sizeof(hit));
+ hit.f = f;
+ copy_v3_v3(hit.hit, p);
+ copy_v3_v3(hit.cagehit, p_cage);
+ copy_v2_v2(hit.schit, s2);
+ set_linehit_depth(kcd, &hit);
+ BLI_array_append(linehits, hit);
+ }
}
}
}
@@ -1844,7 +1861,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_release(&faces);
BLI_smallhash_release(&kfes);
BLI_smallhash_release(&kfvs);
- BLI_bvhtree_free(planetree);
if (results) {
MEM_freeN(results);
}
@@ -1895,12 +1911,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 +1924,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;
@@ -1936,10 +1954,11 @@ static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius
for (i = 0; i < 2; i++) {
KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv->sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- dis_sq = len_squared_v2v2(kfv->sco, sco);
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
if (dis_sq < radius_sq) {
if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
@@ -1969,11 +1988,12 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
}
/* p is closest point on edge to the mouse cursor */
-static KnifeEdge *knife_find_closest_edge(
- KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space)
+static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
+ BMFace *f,
+ float p[3],
+ float cagep[3])
{
- BMFace *f;
- float co[3], cageco[3], sco[2];
+ float sco[2];
float maxdist;
if (kcd->is_interactive) {
@@ -1987,127 +2007,105 @@ static KnifeEdge *knife_find_closest_edge(
maxdist = KNIFE_FLT_EPS;
}
- f = knife_find_closest_face(kcd, co, cageco, NULL);
- *is_space = !f;
-
- kcd->curr.bmface = f;
-
- if (f) {
- const float maxdist_sq = maxdist * maxdist;
- KnifeEdge *cure = NULL;
- float cur_cagep[3];
- ListBase *lst;
- Ref *ref;
- float dis_sq, curdis_sq = FLT_MAX;
-
- /* set p to co, in case we don't find anything, means a face cut */
- copy_v3_v3(p, co);
- copy_v3_v3(cagep, cageco);
-
- knife_project_v2(kcd, cageco, sco);
-
- /* look through all edges associated with this face */
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- float test_cagep[3];
- float lambda;
-
- /* project edge vertices into screen space */
- knife_project_v2(kcd, kfe->v1->cageco, kfe->v1->sco);
- knife_project_v2(kcd, kfe->v2->cageco, kfe->v2->sco);
-
- /* check if we're close enough and calculate 'lambda' */
- if (kcd->is_angle_snapping) {
- /* if snapping, check we're in bounds */
- float sco_snap[2];
- isect_line_line_v2_point(
- kfe->v1->sco, kfe->v2->sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
- lambda = line_point_factor_v2(sco_snap, kfe->v1->sco, kfe->v2->sco);
-
- /* be strict about angle-snapping within edge */
- if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
- continue;
- }
+ const float maxdist_sq = maxdist * maxdist;
+ KnifeEdge *cure = NULL;
+ float cur_cagep[3];
+ ListBase *lst;
+ Ref *ref;
+ float dis_sq, curdis_sq = FLT_MAX;
+
+ knife_project_v2(kcd, cagep, sco);
+
+ /* look through all edges associated with this face */
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ float kfv1_sco[2], kfv2_sco[2], test_cagep[3];
+ float lambda;
+
+ /* project edge vertices into screen space */
+ knife_project_v2(kcd, kfe->v1->cageco, kfv1_sco);
+ knife_project_v2(kcd, kfe->v2->cageco, kfv2_sco);
+
+ /* check if we're close enough and calculate 'lambda' */
+ if (kcd->is_angle_snapping) {
+ /* if snapping, check we're in bounds */
+ float sco_snap[2];
+ isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
+ lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
+
+ /* be strict about angle-snapping within edge */
+ if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
+ continue;
+ }
- dis_sq = len_squared_v2v2(sco, sco_snap);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- /* we already have 'lambda' */
- }
- else {
- continue;
- }
+ dis_sq = len_squared_v2v2(sco, sco_snap);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ /* we already have 'lambda' */
}
else {
- dis_sq = dist_squared_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- lambda = line_point_factor_v2(sco, kfe->v1->sco, kfe->v2->sco);
- }
- else {
- continue;
- }
+ continue;
+ }
+ }
+ else {
+ dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco);
+ }
+ else {
+ continue;
}
+ }
- /* now we have 'lambda' calculated (in screen-space) */
- knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda);
+ /* now we have 'lambda' calculated (in screen-space) */
+ knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda);
- if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
- /* check we're in the view */
- if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) {
- continue;
- }
+ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
+ /* check we're in the view */
+ if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) {
+ continue;
}
-
- cure = kfe;
- curdis_sq = dis_sq;
- copy_v3_v3(cur_cagep, test_cagep);
}
- if (fptr) {
- *fptr = f;
- }
+ cure = kfe;
+ curdis_sq = dis_sq;
+ copy_v3_v3(cur_cagep, test_cagep);
+ }
- if (cure) {
- if (!kcd->ignore_edge_snapping || !(cure->e)) {
- KnifeVert *edgesnap = NULL;
+ if (cure) {
+ if (!kcd->ignore_edge_snapping || !(cure->e)) {
+ KnifeVert *edgesnap = NULL;
- if (kcd->snap_midpoints) {
- mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
- mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
- }
- else {
- float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
- copy_v3_v3(cagep, cur_cagep);
- interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
- }
-
- /* update mouse coordinates to the snapped-to edge's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- edgesnap = new_knife_vert(kcd, p, cagep);
- kcd->curr.mval[0] = edgesnap->sco[0];
- kcd->curr.mval[1] = edgesnap->sco[1];
+ if (kcd->snap_midpoints) {
+ mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
+ mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
}
else {
- return NULL;
+ float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
+ copy_v3_v3(cagep, cur_cagep);
+ interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
}
- }
-
- return cure;
- }
- if (fptr) {
- *fptr = NULL;
+ /* update mouse coordinates to the snapped-to edge's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ edgesnap = new_knife_vert(kcd, p, cagep);
+ knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
+ }
+ else {
+ return NULL;
+ }
}
- return NULL;
+ return cure;
}
/* find a vertex near the mouse cursor, if it exists */
-static KnifeVert *knife_find_closest_vert(
- KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space)
+static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd,
+ BMFace *f,
+ float p[3],
+ float cagep[3])
{
- BMFace *f;
- float co[3], cageco[3], sco[2];
+ float sco[2];
float maxdist;
if (kcd->is_interactive) {
@@ -2120,86 +2118,58 @@ static KnifeVert *knife_find_closest_vert(
maxdist = KNIFE_FLT_EPS;
}
- f = knife_find_closest_face(kcd, co, cageco, is_space);
-
- kcd->curr.bmface = f;
-
- if (f) {
- const float maxdist_sq = maxdist * maxdist;
- ListBase *lst;
- Ref *ref;
- KnifeVert *curv = NULL;
- float dis_sq, curdis_sq = FLT_MAX;
-
- /* set p to co, in case we don't find anything, means a face cut */
- copy_v3_v3(p, co);
- copy_v3_v3(cagep, cageco);
+ const float maxdist_sq = maxdist * maxdist;
+ ListBase *lst;
+ Ref *ref;
+ KnifeVert *curv = NULL;
+ float cur_kfv_sco[2];
+ float dis_sq, curdis_sq = FLT_MAX;
- knife_project_v2(kcd, cageco, sco);
+ knife_project_v2(kcd, cagep, sco);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- int i;
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ int i;
- for (i = 0; i < 2; i++) {
- KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ for (i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv->sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- /* be strict about angle snapping, the vertex needs to be very close to the angle,
- * or we ignore */
- if (kcd->is_angle_snapping) {
- if (dist_squared_to_line_segment_v2(kfv->sco, kcd->prev.mval, kcd->curr.mval) >
- KNIFE_FLT_EPSBIG) {
- continue;
- }
+ /* be strict about angle snapping, the vertex needs to be very close to the angle,
+ * or we ignore */
+ if (kcd->is_angle_snapping) {
+ if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
+ KNIFE_FLT_EPSBIG) {
+ continue;
}
+ }
- dis_sq = len_squared_v2v2(kfv->sco, sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
- if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
- curv = kfv;
- curdis_sq = dis_sq;
- }
- }
- else {
- curv = kfv;
- curdis_sq = dis_sq;
- }
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
+ !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) {
+ curv = kfv;
+ curdis_sq = dis_sq;
+ copy_v2_v2(cur_kfv_sco, kfv_sco);
}
}
}
+ }
- if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
- if (fptr) {
- *fptr = f;
- }
-
- if (curv) {
- copy_v3_v3(p, curv->co);
- copy_v3_v3(cagep, curv->cageco);
-
- /* update mouse coordinates to the snapped-to vertex's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- kcd->curr.mval[0] = curv->sco[0];
- kcd->curr.mval[1] = curv->sco[1];
- }
-
- return curv;
- }
- else {
- if (fptr) {
- *fptr = f;
- }
+ if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
+ if (curv) {
+ copy_v3_v3(p, curv->co);
+ copy_v3_v3(cagep, curv->cageco);
- return NULL;
+ /* update mouse coordinates to the snapped-to vertex's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
}
- }
- if (fptr) {
- *fptr = NULL;
+ return curv;
}
return NULL;
@@ -2246,11 +2216,10 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd)
return true;
}
-/* update active knife edge/vert pointers */
-static int knife_update_active(KnifeTool_OpData *kcd)
+static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
- copy_v2_v2(kcd->curr.mval, kcd->mval);
+ copy_v2_v2(kcd->curr.mval, mval);
/* view matrix may have changed, reproject */
knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval);
@@ -2262,15 +2231,26 @@ static int knife_update_active(KnifeTool_OpData *kcd)
kcd->is_angle_snapping = false;
}
- kcd->curr.vert = knife_find_closest_vert(
- kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space);
+ kcd->curr.bmface = knife_find_closest_face(
+ kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space);
+
+ if (kcd->curr.bmface) {
+ kcd->curr.vert = knife_find_closest_vert_of_face(
+ kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
- if (!kcd->curr.vert &&
- /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */
- !kcd->is_drag_hold) {
- kcd->curr.edge = knife_find_closest_edge(
- kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space);
+ if (!kcd->curr.vert &&
+ /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */
+ !kcd->is_drag_hold) {
+ kcd->curr.edge = knife_find_closest_edge_of_face(
+ kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
+ }
}
+}
+
+/* update active knife edge/vert pointers */
+static int knife_update_active(KnifeTool_OpData *kcd)
+{
+ knife_snap_update_from_mval(kcd, kcd->mval);
/* if no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
@@ -2308,12 +2288,10 @@ static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cu
if (a_sq < b_sq) {
return -1;
}
- else if (a_sq > b_sq) {
+ if (a_sq > b_sq) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f)
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index 10e8bfa529f..ed22d381a02 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -152,12 +152,11 @@ static int knifeproject_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports,
- RPT_ERROR,
- "No other selected objects have wire or boundary edges to use for projection");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "No other selected objects have wire or boundary edges to use for projection");
+ return OPERATOR_CANCELLED;
}
void MESH_OT_knife_project(wmOperatorType *ot)
diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c
index 0f52911c603..2f453369d92 100644
--- a/source/blender/editors/mesh/editmesh_loopcut.c
+++ b/source/blender/editors/mesh/editmesh_loopcut.c
@@ -451,11 +451,10 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event)
"hold Alt for smooth"));
return OPERATOR_RUNNING_MODAL;
}
- else {
- ringsel_finish(C, op);
- ringsel_exit(C, op);
- return OPERATOR_FINISHED;
- }
+
+ ringsel_finish(C, op);
+ ringsel_exit(C, op);
+ return OPERATOR_FINISHED;
}
static int ringcut_invoke(bContext *C, wmOperator *op, const wmEvent *event)
diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index 739bc5bdf7c..8eeba5007e1 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -66,9 +66,7 @@ static bool paint_mask_extract_poll(bContext *C)
CTX_wm_operator_poll_msg_set(C, "The mask can not be extracted with dyntopo activated");
return false;
}
- else {
- return ED_operator_object_active_editable_mesh(C);
- }
+ return ED_operator_object_active_editable_mesh(C);
}
return false;
}
diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c
index 7cde233c6b6..e7e27c2189f 100644
--- a/source/blender/editors/mesh/editmesh_path.c
+++ b/source/blender/editors/mesh/editmesh_path.c
@@ -642,10 +642,10 @@ static BMElem *edbm_elem_find_nearest(ViewContext *vc, const char htype)
if ((em->selectmode & SCE_SELECT_VERTEX) && (htype == BM_VERT)) {
return (BMElem *)EDBM_vert_find_nearest(vc, &dist);
}
- else if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
+ if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
return (BMElem *)EDBM_edge_find_nearest(vc, &dist);
}
- else if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
+ if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
return (BMElem *)EDBM_face_find_nearest(vc, &dist);
}
diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c
index c7bb45ccfe3..da3d16ba04f 100644
--- a/source/blender/editors/mesh/editmesh_polybuild.c
+++ b/source/blender/editors/mesh/editmesh_polybuild.c
@@ -247,9 +247,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C,
WM_event_add_mousemove(vc.win);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_delete_at_cursor(wmOperatorType *ot)
@@ -415,9 +413,7 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot)
@@ -470,7 +466,7 @@ static int edbm_polybuild_split_at_cursor_invoke(bContext *C,
if (ele_act == NULL || ele_act->head.hflag == BM_FACE) {
return OPERATOR_PASS_THROUGH;
}
- else if (ele_act->head.htype == BM_EDGE) {
+ if (ele_act->head.htype == BM_EDGE) {
BMEdge *e_act = (BMEdge *)ele_act;
mid_v3_v3v3(center, e_act->v1->co, e_act->v2->co);
mul_m4_v3(vc.obedit->obmat, center);
@@ -503,9 +499,7 @@ static int edbm_polybuild_split_at_cursor_invoke(bContext *C,
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_split_at_cursor(wmOperatorType *ot)
@@ -596,9 +590,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C,
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_dissolve_at_cursor(wmOperatorType *ot)
diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c
index e805507351c..89a90dcf12d 100644
--- a/source/blender/editors/mesh/editmesh_rip.c
+++ b/source/blender/editors/mesh/editmesh_rip.c
@@ -155,9 +155,7 @@ static float edbm_rip_edge_side_measure(
dist_squared_to_line_segment_v2(fmval, e_v1_co, e_v2_co)) {
return score;
}
- else {
- return -score;
- }
+ return -score;
}
/* - Advanced selection handling 'ripsel' functions ----- */
@@ -433,9 +431,7 @@ static UnorderedLoopPair *edbm_tagged_loop_pairs_to_fill(BMesh *bm)
return uloop_pairs;
}
- else {
- return NULL;
- }
+ return NULL;
}
static void edbm_tagged_loop_pairs_do_fill_faces(BMesh *bm, UnorderedLoopPair *uloop_pairs)
@@ -658,89 +654,88 @@ static int edbm_rip_invoke__vert(bContext *C, const wmEvent *event, Object *obed
/* should never happen */
return OPERATOR_CANCELLED;
}
- else {
- int vi_best = 0;
- if (ese.ele) {
- BM_select_history_remove(bm, ese.ele);
- }
+ int vi_best = 0;
- dist_sq = FLT_MAX;
+ if (ese.ele) {
+ BM_select_history_remove(bm, ese.ele);
+ }
+
+ dist_sq = FLT_MAX;
- /* in the loop below we find the best vertex to drag based on its connected geometry,
- * either by its face corner, or connected edge (when no faces are attached) */
- for (i = 0; i < vout_len; i++) {
+ /* in the loop below we find the best vertex to drag based on its connected geometry,
+ * either by its face corner, or connected edge (when no faces are attached) */
+ for (i = 0; i < vout_len; i++) {
- if (BM_vert_is_wire(vout[i]) == false) {
- /* find the best face corner */
- BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) {
- if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
- float l_mid_co[3];
+ if (BM_vert_is_wire(vout[i]) == false) {
+ /* find the best face corner */
+ BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) {
+ if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
+ float l_mid_co[3];
- edbm_calc_loop_co(l, l_mid_co);
- d = edbm_rip_edgedist_squared(
- region, projectMat, v->co, l_mid_co, fmval, INSET_DEFAULT);
+ edbm_calc_loop_co(l, l_mid_co);
+ d = edbm_rip_edgedist_squared(
+ region, projectMat, v->co, l_mid_co, fmval, INSET_DEFAULT);
- if (d < dist_sq) {
- dist_sq = d;
- vi_best = i;
- }
+ if (d < dist_sq) {
+ dist_sq = d;
+ vi_best = i;
}
}
}
- else {
- BMEdge *e;
- /* a wire vert, find the best edge */
- BM_ITER_ELEM (e, &iter, vout[i], BM_EDGES_OF_VERT) {
- if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
- float e_mid_co[3];
-
- mid_v3_v3v3(e_mid_co, e->v1->co, e->v2->co);
- d = edbm_rip_edgedist_squared(
- region, projectMat, v->co, e_mid_co, fmval, INSET_DEFAULT);
-
- if (d < dist_sq) {
- dist_sq = d;
- vi_best = i;
- }
+ }
+ else {
+ BMEdge *e;
+ /* a wire vert, find the best edge */
+ BM_ITER_ELEM (e, &iter, vout[i], BM_EDGES_OF_VERT) {
+ if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ float e_mid_co[3];
+
+ mid_v3_v3v3(e_mid_co, e->v1->co, e->v2->co);
+ d = edbm_rip_edgedist_squared(
+ region, projectMat, v->co, e_mid_co, fmval, INSET_DEFAULT);
+
+ if (d < dist_sq) {
+ dist_sq = d;
+ vi_best = i;
}
}
}
}
+ }
- /* vout[0] == best
- * vout[1] == glue
- * vout[2+] == splice with glue (when vout_len > 2)
- */
- if (vi_best != 0) {
- SWAP(BMVert *, vout[0], vout[vi_best]);
- vi_best = 0;
- }
+ /* vout[0] == best
+ * vout[1] == glue
+ * vout[2+] == splice with glue (when vout_len > 2)
+ */
+ if (vi_best != 0) {
+ SWAP(BMVert *, vout[0], vout[vi_best]);
+ vi_best = 0;
+ }
- /* select the vert from the best region */
- v = vout[vi_best];
- BM_vert_select_set(bm, v, true);
+ /* select the vert from the best region */
+ v = vout[vi_best];
+ BM_vert_select_set(bm, v, true);
- if (ese.ele) {
- BM_select_history_store(bm, v);
- }
+ if (ese.ele) {
+ BM_select_history_store(bm, v);
+ }
- /* splice all others back together */
- if (vout_len > 2) {
- for (i = 2; i < vout_len; i++) {
- BM_vert_splice(bm, vout[1], vout[i]);
- }
+ /* splice all others back together */
+ if (vout_len > 2) {
+ for (i = 2; i < vout_len; i++) {
+ BM_vert_splice(bm, vout[1], vout[i]);
}
+ }
- if (do_fill) {
- /* match extrude vert-order */
- BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP);
- }
+ if (do_fill) {
+ /* match extrude vert-order */
+ BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP);
+ }
- MEM_freeN(vout);
+ MEM_freeN(vout);
- return OPERATOR_FINISHED;
- }
+ return OPERATOR_FINISHED;
}
if (!e_best) {
@@ -1090,15 +1085,15 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* Ignore it. */
return OPERATOR_CANCELLED;
}
- else if (error_face_selected) {
+ if (error_face_selected) {
BKE_report(op->reports, RPT_ERROR, "Cannot rip selected faces");
return OPERATOR_CANCELLED;
}
- else if (error_disconnected_vertices) {
+ if (error_disconnected_vertices) {
BKE_report(op->reports, RPT_ERROR, "Cannot rip multiple disconnected vertices");
return OPERATOR_CANCELLED;
}
- else if (error_rip_failed) {
+ if (error_rip_failed) {
BKE_report(op->reports, RPT_ERROR, "Rip failed");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index fe83d9f00ea..d2e9b57e950 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -205,7 +205,7 @@ static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint
/* -------------------------------------------------------------------- */
/** \name Find Nearest Vert/Edge/Face
*
- * \note Screen-space manhatten distances are used here,
+ * \note Screen-space manhattan distances are used here,
* since its faster and good enough for the purpose of selection.
*
* \note \a dist_bias is used so we can bias against selected items.
@@ -316,65 +316,63 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
}
return NULL;
}
- else {
- struct NearestVertUserData data = {{0}};
- const struct NearestVertUserData_Hit *hit = NULL;
- const eV3DProjTest clip_flag = RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d) ?
- V3D_PROJ_TEST_CLIP_DEFAULT :
- V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
- BMesh *prev_select_bm = NULL;
-
- static struct {
- int index;
- const BMVert *elem;
- const BMesh *bm;
- } prev_select = {0};
-
- data.mval_fl[0] = vc->mval[0];
- data.mval_fl[1] = vc->mval[1];
- data.use_select_bias = use_select_bias;
- data.use_cycle = use_cycle;
-
- for (; base_index < bases_len; base_index++) {
- Base *base_iter = bases[base_index];
- ED_view3d_viewcontext_init_object(vc, base_iter->object);
- if (use_cycle && prev_select.bm == vc->em->bm &&
- prev_select.elem == BM_vert_at_index_find_or_table(vc->em->bm, prev_select.index)) {
- data.cycle_index_prev = prev_select.index;
- /* No need to compare in the rest of the loop. */
- use_cycle = false;
- }
- else {
- data.cycle_index_prev = 0;
- }
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
- *r_dist;
+ struct NearestVertUserData data = {{0}};
+ const struct NearestVertUserData_Hit *hit = NULL;
+ const eV3DProjTest clip_flag = RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d) ?
+ V3D_PROJ_TEST_CLIP_DEFAULT :
+ V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
+ BMesh *prev_select_bm = NULL;
- ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
- mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag);
+ static struct {
+ int index;
+ const BMVert *elem;
+ const BMesh *bm;
+ } prev_select = {0};
- hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
+ data.mval_fl[0] = vc->mval[0];
+ data.mval_fl[1] = vc->mval[1];
+ data.use_select_bias = use_select_bias;
+ data.use_cycle = use_cycle;
- if (hit->dist < *r_dist) {
- if (r_base_index) {
- *r_base_index = base_index;
- }
- *r_dist = hit->dist;
- prev_select_bm = vc->em->bm;
- }
+ for (; base_index < bases_len; base_index++) {
+ Base *base_iter = bases[base_index];
+ ED_view3d_viewcontext_init_object(vc, base_iter->object);
+ if (use_cycle && prev_select.bm == vc->em->bm &&
+ prev_select.elem == BM_vert_at_index_find_or_table(vc->em->bm, prev_select.index)) {
+ data.cycle_index_prev = prev_select.index;
+ /* No need to compare in the rest of the loop. */
+ use_cycle = false;
}
-
- if (hit == NULL) {
- return NULL;
+ else {
+ data.cycle_index_prev = 0;
}
- prev_select.index = hit->index;
- prev_select.elem = hit->vert;
- prev_select.bm = prev_select_bm;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+
+ ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+ mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag);
+
+ hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
+
+ if (hit->dist < *r_dist) {
+ if (r_base_index) {
+ *r_base_index = base_index;
+ }
+ *r_dist = hit->dist;
+ prev_select_bm = vc->em->bm;
+ }
+ }
- return hit->vert;
+ if (hit == NULL) {
+ return NULL;
}
+
+ prev_select.index = hit->index;
+ prev_select.elem = hit->vert;
+ prev_select.bm = prev_select_bm;
+
+ return hit->vert;
}
BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist)
@@ -417,7 +415,7 @@ struct NearestEdgeUserData_Hit {
int index;
BMEdge *edge;
- /* edges only, un-biased manhatten distance to which ever edge we pick
+ /* edges only, un-biased manhattan distance to which ever edge we pick
* (not used for choosing) */
float dist_center;
};
@@ -563,69 +561,67 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
}
return NULL;
}
- else {
- struct NearestEdgeUserData data = {{0}};
- const struct NearestEdgeUserData_Hit *hit = NULL;
- /* interpolate along the edge before doing a clipping plane test */
- const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
- BMesh *prev_select_bm = NULL;
-
- static struct {
- int index;
- const BMEdge *elem;
- const BMesh *bm;
- } prev_select = {0};
-
- data.vc = *vc;
- data.mval_fl[0] = vc->mval[0];
- data.mval_fl[1] = vc->mval[1];
- data.use_select_bias = use_select_bias;
- data.use_cycle = use_cycle;
-
- for (; base_index < bases_len; base_index++) {
- Base *base_iter = bases[base_index];
- ED_view3d_viewcontext_init_object(vc, base_iter->object);
- if (use_cycle && prev_select.bm == vc->em->bm &&
- prev_select.elem == BM_edge_at_index_find_or_table(vc->em->bm, prev_select.index)) {
- data.cycle_index_prev = prev_select.index;
- /* No need to compare in the rest of the loop. */
- use_cycle = false;
- }
- else {
- data.cycle_index_prev = 0;
- }
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
- *r_dist;
+ struct NearestEdgeUserData data = {{0}};
+ const struct NearestEdgeUserData_Hit *hit = NULL;
+ /* interpolate along the edge before doing a clipping plane test */
+ const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
+ BMesh *prev_select_bm = NULL;
- ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
- mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag);
+ static struct {
+ int index;
+ const BMEdge *elem;
+ const BMesh *bm;
+ } prev_select = {0};
- hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
+ data.vc = *vc;
+ data.mval_fl[0] = vc->mval[0];
+ data.mval_fl[1] = vc->mval[1];
+ data.use_select_bias = use_select_bias;
+ data.use_cycle = use_cycle;
- if (hit->dist < *r_dist) {
- if (r_base_index) {
- *r_base_index = base_index;
- }
- *r_dist = hit->dist;
- prev_select_bm = vc->em->bm;
- }
+ for (; base_index < bases_len; base_index++) {
+ Base *base_iter = bases[base_index];
+ ED_view3d_viewcontext_init_object(vc, base_iter->object);
+ if (use_cycle && prev_select.bm == vc->em->bm &&
+ prev_select.elem == BM_edge_at_index_find_or_table(vc->em->bm, prev_select.index)) {
+ data.cycle_index_prev = prev_select.index;
+ /* No need to compare in the rest of the loop. */
+ use_cycle = false;
}
-
- if (hit == NULL) {
- return NULL;
+ else {
+ data.cycle_index_prev = 0;
}
- if (r_dist_center) {
- *r_dist_center = hit->dist_center;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+
+ ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+ mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag);
+
+ hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
+
+ if (hit->dist < *r_dist) {
+ if (r_base_index) {
+ *r_base_index = base_index;
+ }
+ *r_dist = hit->dist;
+ prev_select_bm = vc->em->bm;
}
+ }
- prev_select.index = hit->index;
- prev_select.elem = hit->edge;
- prev_select.bm = prev_select_bm;
+ if (hit == NULL) {
+ return NULL;
+ }
- return hit->edge;
+ if (r_dist_center) {
+ *r_dist_center = hit->dist_center;
}
+
+ prev_select.index = hit->index;
+ prev_select.elem = hit->edge;
+ prev_select.bm = prev_select_bm;
+
+ return hit->edge;
}
BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *r_dist)
@@ -769,67 +765,65 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
}
return NULL;
}
- else {
- struct NearestFaceUserData data = {{0}};
- const struct NearestFaceUserData_Hit *hit = NULL;
- const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT;
- BMesh *prev_select_bm = NULL;
-
- static struct {
- int index;
- const BMFace *elem;
- const BMesh *bm;
- } prev_select = {0};
-
- data.mval_fl[0] = vc->mval[0];
- data.mval_fl[1] = vc->mval[1];
- data.use_select_bias = use_select_bias;
- data.use_cycle = use_cycle;
-
- for (; base_index < bases_len; base_index++) {
- Base *base_iter = bases[base_index];
- ED_view3d_viewcontext_init_object(vc, base_iter->object);
- if (use_cycle && prev_select.bm == vc->em->bm &&
- prev_select.elem == BM_face_at_index_find_or_table(vc->em->bm, prev_select.index)) {
- data.cycle_index_prev = prev_select.index;
- /* No need to compare in the rest of the loop. */
- use_cycle = false;
- }
- else {
- data.cycle_index_prev = 0;
- }
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
- *r_dist;
+ struct NearestFaceUserData data = {{0}};
+ const struct NearestFaceUserData_Hit *hit = NULL;
+ const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT;
+ BMesh *prev_select_bm = NULL;
- ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
- mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag);
+ static struct {
+ int index;
+ const BMFace *elem;
+ const BMesh *bm;
+ } prev_select = {0};
- hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
+ data.mval_fl[0] = vc->mval[0];
+ data.mval_fl[1] = vc->mval[1];
+ data.use_select_bias = use_select_bias;
+ data.use_cycle = use_cycle;
- if (hit->dist < *r_dist) {
- if (r_base_index) {
- *r_base_index = base_index;
- }
- *r_dist = hit->dist;
- prev_select_bm = vc->em->bm;
- }
+ for (; base_index < bases_len; base_index++) {
+ Base *base_iter = bases[base_index];
+ ED_view3d_viewcontext_init_object(vc, base_iter->object);
+ if (use_cycle && prev_select.bm == vc->em->bm &&
+ prev_select.elem == BM_face_at_index_find_or_table(vc->em->bm, prev_select.index)) {
+ data.cycle_index_prev = prev_select.index;
+ /* No need to compare in the rest of the loop. */
+ use_cycle = false;
}
-
- if (hit == NULL) {
- return NULL;
+ else {
+ data.cycle_index_prev = 0;
}
- if (r_dist_center) {
- *r_dist_center = hit->dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+
+ ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+ mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag);
+
+ hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
+
+ if (hit->dist < *r_dist) {
+ if (r_base_index) {
+ *r_base_index = base_index;
+ }
+ *r_dist = hit->dist;
+ prev_select_bm = vc->em->bm;
}
+ }
- prev_select.index = hit->index;
- prev_select.elem = hit->face;
- prev_select.bm = prev_select_bm;
+ if (hit == NULL) {
+ return NULL;
+ }
- return hit->face;
+ if (r_dist_center) {
+ *r_dist_center = hit->dist;
}
+
+ prev_select.index = hit->index;
+ prev_select.elem = hit->face;
+ prev_select.bm = prev_select_bm;
+
+ return hit->face;
}
BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist)
@@ -1353,9 +1347,7 @@ static int edbm_select_mode_exec(bContext *C, wmOperator *op)
if (EDBM_selectmode_toggle_multi(C, type, action, use_extend, use_expand)) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -1809,9 +1801,7 @@ static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *e
RNA_boolean_get(op->ptr, "ring"))) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MESH_OT_loop_select(wmOperatorType *ot)
@@ -2556,9 +2546,7 @@ bool EDBM_selectmode_disable(Scene *scene,
return true;
}
- else {
- return false;
- }
+ return false;
}
/** \} */
@@ -4202,11 +4190,11 @@ static bool edbm_deselect_nth(BMEditMesh *em, const struct CheckerIntervalParams
walker_deselect_nth(em, op_params, &v->head);
return true;
}
- else if (e) {
+ if (e) {
walker_deselect_nth(em, op_params, &e->head);
return true;
}
- else if (f) {
+ if (f) {
walker_deselect_nth(em, op_params, &f->head);
return true;
}
@@ -4241,10 +4229,7 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op)
MEM_freeN(objects);
if (!found_active_elt) {
- BKE_report(op->reports,
- RPT_ERROR,
- (objects_len == 1 ? "Mesh has no active vert/edge/face" :
- "Meshes have no active vert/edge/face"));
+ BKE_report(op->reports, RPT_ERROR, "Mesh object(s) have no active vertex/edge/face");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index 365c5b5d264..b3fb9747070 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -23,6 +23,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_bitmap.h"
#include "BLI_kdtree.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -244,9 +245,7 @@ static int similar_face_select_exec(bContext *C, wmOperator *op)
if (custom_data_offset == -1) {
continue;
}
- else {
- gset_array[ob_index] = BLI_gset_ptr_new("Select similar face: facemap gset");
- }
+ gset_array[ob_index] = BLI_gset_ptr_new("Select similar face: facemap gset");
}
}
@@ -1007,7 +1006,9 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
BMEditMesh *em = BKE_editmesh_from_object(ob);
BMesh *bm = em->bm;
int cd_dvert_offset = -1;
- int dvert_selected = 0;
+ BLI_bitmap *defbase_selected = NULL;
+ int defbase_len = 0;
+
invert_m4_m4(ob->imat, ob->obmat);
if (bm->totvertsel == 0) {
@@ -1019,6 +1020,11 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
if (cd_dvert_offset == -1) {
continue;
}
+ defbase_len = BLI_listbase_count(&ob->defbase);
+ if (defbase_len == 0) {
+ continue;
+ }
+ defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
}
BMVert *vert; /* Mesh vertex. */
@@ -1048,7 +1054,9 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
for (int i = 0; i < dvert->totweight; i++, dw++) {
if (dw->weight > 0.0f) {
- dvert_selected |= (1 << dw->def_nr);
+ if (LIKELY(dw->def_nr < defbase_len)) {
+ BLI_BITMAP_ENABLE(defbase_selected, dw->def_nr);
+ }
}
}
break;
@@ -1060,13 +1068,15 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
if (type == SIMVERT_VGROUP) {
/* We store the names of the vertex groups, so we can select
* vertex groups with the same name in different objects. */
- const int dvert_tot = BLI_listbase_count(&ob->defbase);
- for (int i = 0; i < dvert_tot; i++) {
- if (dvert_selected & (1 << i)) {
- bDeformGroup *dg = BLI_findlink(&ob->defbase, i);
+
+ int i = 0;
+ LISTBASE_FOREACH (bDeformGroup *, dg, &ob->defbase) {
+ if (BLI_BITMAP_TEST(defbase_selected, i)) {
BLI_gset_add(gset, dg->name);
}
+ i += 1;
}
+ MEM_freeN(defbase_selected);
}
}
@@ -1082,32 +1092,42 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
BLI_kdtree_3d_balance(tree_3d);
}
- /* Run .the BM operators. */
+ /* Run the matching operations. */
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(ob);
BMesh *bm = em->bm;
bool changed = false;
int cd_dvert_offset = -1;
- int dvert_selected = 0;
+ BLI_bitmap *defbase_selected = NULL;
+ int defbase_len = 0;
if (type == SIMVERT_VGROUP) {
cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
if (cd_dvert_offset == -1) {
continue;
}
+ defbase_len = BLI_listbase_count(&ob->defbase);
+ if (defbase_len == 0) {
+ continue;
+ }
/* We map back the names of the vertex groups to their corresponding indices
* for this object. This is fast, and keep the logic for each vertex very simple. */
+
+ defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
+ bool found_any = false;
GSetIterator gs_iter;
GSET_ITER (gs_iter, gset) {
const char *name = BLI_gsetIterator_getKey(&gs_iter);
int vgroup_id = BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name));
if (vgroup_id != -1) {
- dvert_selected |= (1 << vgroup_id);
+ BLI_BITMAP_ENABLE(defbase_selected, vgroup_id);
+ found_any = true;
}
}
- if (dvert_selected == 0) {
+ if (found_any == false) {
+ MEM_freeN(defbase_selected);
continue;
}
}
@@ -1167,9 +1187,11 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
for (int i = 0; i < dvert->totweight; i++, dw++) {
if (dw->weight > 0.0f) {
- if (dvert_selected & (1 << dw->def_nr)) {
- select = true;
- break;
+ if (LIKELY(dw->def_nr < defbase_len)) {
+ if (BLI_BITMAP_TEST(defbase_selected, dw->def_nr)) {
+ select = true;
+ break;
+ }
}
}
}
@@ -1184,6 +1206,10 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
}
}
+ if (type == SIMVERT_VGROUP) {
+ MEM_freeN(defbase_selected);
+ }
+
if (changed) {
EDBM_selectmode_flush(em);
EDBM_update_generic(ob->data, false, false);
@@ -1221,12 +1247,10 @@ static int edbm_select_similar_exec(bContext *C, wmOperator *op)
if (type < 100) {
return similar_vert_select_exec(C, op);
}
- else if (type < 200) {
+ if (type < 200) {
return similar_edge_select_exec(C, op);
}
- else {
- return similar_face_select_exec(C, op);
- }
+ return similar_face_select_exec(C, op);
}
static const EnumPropertyItem *select_similar_type_itemf(bContext *C,
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 69497d019b8..d699d761709 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -1557,7 +1557,7 @@ static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Invalid selection order");
return OPERATOR_CANCELLED;
}
- else if (failed_connect_len == objects_len) {
+ if (failed_connect_len == objects_len) {
BKE_report(op->reports, RPT_ERROR, "Could not connect vertices");
return OPERATOR_CANCELLED;
}
@@ -2686,7 +2686,7 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "No selected vertex");
return OPERATOR_CANCELLED;
}
- else if (tot_invalid == objects_len) {
+ if (tot_invalid == objects_len) {
BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads");
return OPERATOR_CANCELLED;
}
@@ -3505,7 +3505,7 @@ static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No selected vertex");
return OPERATOR_CANCELLED;
}
- else if (tot_shapekeys == 0) {
+ if (tot_shapekeys == 0) {
BKE_report(op->reports,
RPT_ERROR,
objects_len > 1 ? "Meshes do not have shape keys" :
@@ -3562,7 +3562,7 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Active mesh does not have shape keys");
return OPERATOR_CANCELLED;
}
- else if (shape_ref >= totshape_ref) {
+ if (shape_ref >= totshape_ref) {
/* This case occurs if operator was used before on object with more keys than current one. */
shape_ref = 0; /* default to basis */
}
@@ -3592,10 +3592,8 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
if (!key) {
continue;
}
- else {
- kb = BKE_keyblock_find_name(key, kb_ref->name);
- shape = BLI_findindex(&key->block, kb);
- }
+ kb = BKE_keyblock_find_name(key, kb_ref->name);
+ shape = BLI_findindex(&key->block, kb);
if (kb) {
/* Perform blending on selected vertices. */
@@ -3679,7 +3677,10 @@ static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op)
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
RNA_id_pointer_create((ID *)me->key, &ptr_key);
- uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", "", ICON_SHAPEKEY_DATA);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
uiItemR(layout, &ptr, "blend", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "add", 0, NULL, ICON_NONE);
}
@@ -3861,7 +3862,7 @@ static float bm_edge_seg_isect(const float sco_a[2],
return perc;
}
/* test e->v2 */
- else if ((x11 == x22 && y11 == y22) || (x12 == x22 && y12 == y22)) {
+ if ((x11 == x22 && y11 == y22) || (x12 == x22 && y12 == y22)) {
perc = 0;
*isected = 2;
return perc;
@@ -4161,7 +4162,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 */
@@ -4171,7 +4172,8 @@ static Base *mesh_separate_tagged(
BKE_object_material_array_assign(bmain,
base_new->object,
BKE_object_material_array_p(obedit),
- *BKE_object_material_len_p(obedit));
+ *BKE_object_material_len_p(obedit),
+ false);
ED_object_base_select(base_new, BA_SELECT);
@@ -4233,7 +4235,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 */
@@ -4243,7 +4245,8 @@ static Base *mesh_separate_arrays(Main *bmain,
BKE_object_material_array_assign(bmain,
base_new->object,
BKE_object_material_array_p(obedit),
- *BKE_object_material_len_p(obedit));
+ *BKE_object_material_len_p(obedit),
+ false);
ED_object_base_select(base_new, BA_SELECT);
@@ -5589,25 +5592,26 @@ static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op)
{
- uiLayout *layout = op->layout, *box, *row, *col;
+ uiLayout *layout = op->layout, *row, *col, *sub;
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ uiLayoutSetPropSep(layout, true);
+
uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE);
- box = uiLayoutBox(layout);
- uiItemR(box, &ptr, "use_vertex_group", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(box, false);
+ uiItemR(layout, &ptr, "use_vertex_group", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group"));
uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
- box = uiLayoutBox(layout);
- uiItemR(box, &ptr, "use_symmetry", 0, NULL, ICON_NONE);
- row = uiLayoutRow(box, true);
- uiLayoutSetActive(row, RNA_boolean_get(&ptr, "use_symmetry"));
- uiItemR(row, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry"));
+ uiItemR(row, &ptr, "use_symmetry", 0, "", ICON_NONE);
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_symmetry"));
+ uiItemR(sub, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
}
void MESH_OT_decimate(wmOperatorType *ot)
@@ -5662,7 +5666,7 @@ static void edbm_dissolve_prop__use_verts(wmOperatorType *ot, bool value, int fl
PropertyRNA *prop;
prop = RNA_def_boolean(
- ot->srna, "use_verts", value, "Dissolve Verts", "Dissolve remaining vertices");
+ ot->srna, "use_verts", value, "Dissolve Vertices", "Dissolve remaining vertices");
if (flag) {
RNA_def_property_flag(prop, flag);
@@ -5727,7 +5731,7 @@ void MESH_OT_dissolve_verts(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Dissolve Vertices";
- ot->description = "Dissolve verts, merge edges and faces";
+ ot->description = "Dissolve vertices, merge edges and faces";
ot->idname = "MESH_OT_dissolve_verts";
/* api callbacks */
@@ -5885,12 +5889,10 @@ static int edbm_dissolve_mode_exec(bContext *C, wmOperator *op)
if (em->selectmode & SCE_SELECT_VERTEX) {
return edbm_dissolve_verts_exec(C, op);
}
- else if (em->selectmode & SCE_SELECT_EDGE) {
+ if (em->selectmode & SCE_SELECT_EDGE) {
return edbm_dissolve_edges_exec(C, op);
}
- else {
- return edbm_dissolve_faces_exec(C, op);
- }
+ return edbm_dissolve_faces_exec(C, op);
}
void MESH_OT_dissolve_mode(wmOperatorType *ot)
@@ -5996,7 +5998,7 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot)
ot->name = "Limited Dissolve";
ot->idname = "MESH_OT_dissolve_limited";
ot->description =
- "Dissolve selected edges and verts, limited by the angle of surrounding geometry";
+ "Dissolve selected edges and vertices, limited by the angle of surrounding geometry";
/* api callbacks */
ot->exec = edbm_dissolve_limited_exec;
@@ -6814,9 +6816,7 @@ static bool edbm_sort_elements_poll_property(const bContext *UNUSED(C),
if (action == SRT_RANDOMIZE) {
return true;
}
- else {
- return false;
- }
+ return false;
}
/* Hide seed for reverse and randomize actions! */
@@ -6824,9 +6824,7 @@ static bool edbm_sort_elements_poll_property(const bContext *UNUSED(C),
if (ELEM(action, SRT_RANDOMIZE, SRT_REVERSE)) {
return false;
}
- else {
- return true;
- }
+ return true;
}
return true;
@@ -7240,8 +7238,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 improved subsurf");
+ 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);
@@ -7504,10 +7505,8 @@ static int mesh_symmetrize_exec(bContext *C, wmOperator *op)
if (!EDBM_op_finish(em, &bmop, op, true)) {
continue;
}
- else {
- EDBM_update_generic(obedit->data, true, true);
- EDBM_selectmode_flush(em);
- }
+ EDBM_update_generic(obedit->data, true, true);
+ EDBM_selectmode_flush(em);
}
MEM_freeN(objects);
@@ -7886,7 +7885,7 @@ void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
/* NOTE: We could add more here, like e.g. a switch between local or global coordinates of target,
- * use numinput to type in explicit vector values... */
+ * use number-input to type in explicit vector values. */
enum {
/* Generic commands. */
EDBM_CLNOR_MODAL_CANCEL = 1,
@@ -8410,6 +8409,8 @@ static void edbm_point_normals_ui(bContext *C, wmOperator *op)
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
+ uiLayoutSetPropSep(layout, true);
+
/* Main auto-draw call */
uiDefAutoButsRNA(layout, &ptr, point_normals_draw_check_prop, NULL, NULL, '\0', false);
}
@@ -8698,17 +8699,21 @@ enum {
};
static EnumPropertyItem average_method_items[] = {
- {EDBM_CLNOR_AVERAGE_LOOP, "CUSTOM_NORMAL", 0, "Custom Normal", "Take Average of vert Normals"},
+ {EDBM_CLNOR_AVERAGE_LOOP,
+ "CUSTOM_NORMAL",
+ 0,
+ "Custom Normal",
+ "Take average of vertex normals"},
{EDBM_CLNOR_AVERAGE_FACE_AREA,
"FACE_AREA",
0,
"Face Area",
- "Set all vert normals by Face Area"},
+ "Set all vertex normals by face area"},
{EDBM_CLNOR_AVERAGE_ANGLE,
"CORNER_ANGLE",
0,
"Corner Angle",
- "Set all vert normals by Corner Angle"},
+ "Set all vertex normals by corner angle"},
{0, NULL, 0, NULL, NULL},
};
@@ -8866,7 +8871,7 @@ static bool average_normals_draw_check_prop(PointerRNA *ptr,
if (STREQ(prop_id, "weight")) {
return (average_type == EDBM_CLNOR_AVERAGE_LOOP);
}
- else if (STREQ(prop_id, "threshold")) {
+ if (STREQ(prop_id, "threshold")) {
return (average_type == EDBM_CLNOR_AVERAGE_LOOP);
}
@@ -8882,6 +8887,8 @@ static void edbm_average_normals_ui(bContext *C, wmOperator *op)
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
+ uiLayoutSetPropSep(layout, true);
+
/* Main auto-draw call */
uiDefAutoButsRNA(layout, &ptr, average_normals_draw_check_prop, NULL, NULL, '\0', false);
}
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index e4ecfa9c680..9f625fd0515 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -619,7 +619,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
if (kb_act->totelem != um->me.totvert) {
/* The current mesh has some extra/missing verts compared to the undo, adjust. */
MEM_SAFE_FREE(kb_act->data);
- kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__);
+ kb_act->data = MEM_mallocN((size_t)(key->elemsize) * bm->totvert, __func__);
kb_act->totelem = um->me.totvert;
}
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 6c9973dc209..46c63d2e057 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -187,20 +187,19 @@ bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool
return false;
}
- else {
- em->emcopyusers--;
- if (em->emcopyusers < 0) {
- printf("warning: em->emcopyusers was less than zero.\n");
- }
- if (em->emcopyusers <= 0) {
- BKE_editmesh_free(em->emcopy);
- MEM_freeN(em->emcopy);
- em->emcopy = NULL;
- }
+ em->emcopyusers--;
+ if (em->emcopyusers < 0) {
+ printf("warning: em->emcopyusers was less than zero.\n");
+ }
- return true;
+ if (em->emcopyusers <= 0) {
+ BKE_editmesh_free(em->emcopy);
+ MEM_freeN(em->emcopy);
+ em->emcopy = NULL;
}
+
+ return true;
}
bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt, ...)
@@ -526,10 +525,7 @@ void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
/**
* Return a new UVVertMap from the editmesh
*/
-UvVertMap *BM_uv_vert_map_create(BMesh *bm,
- const float limit[2],
- const bool use_select,
- const bool use_winding)
+UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding)
{
BMVert *ev;
BMFace *efa;
@@ -538,7 +534,7 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm,
/* vars from original func */
UvVertMap *vmap;
UvMapVert *buf;
- MLoopUV *luv;
+ const MLoopUV *luv;
uint a;
int totverts, i, totuv, totfaces;
const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
@@ -610,7 +606,7 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm,
BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, a) {
UvMapVert *newvlist = NULL, *vlist = vmap->vert[a];
UvMapVert *iterv, *v, *lastv, *next;
- float *uv, *uv2, uvdiff[2];
+ const float *uv, *uv2;
while (vlist) {
v = vlist;
@@ -634,9 +630,7 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm,
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv2 = luv->uv;
- sub_v2_v2v2(uvdiff, uv2, uv);
-
- if (fabsf(uvdiff[0]) < limit[0] && fabsf(uvdiff[1]) < limit[1] &&
+ if (compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT) &&
(!use_winding || winding[iterv->poly_index] == winding[v->poly_index])) {
if (lastv) {
lastv->next = next;
@@ -778,7 +772,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) {
UvElement *newvlist = NULL, *vlist = element_map->vert[i];
UvElement *iterv, *v, *lastv, *next;
- float *uv, *uv2, uvdiff[2];
+ const float *uv, *uv2;
+ bool uv_vert_sel, uv2_vert_sel;
while (vlist) {
v = vlist;
@@ -789,6 +784,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = v->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv = luv->uv;
+ uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
lastv = NULL;
iterv = vlist;
@@ -799,12 +795,15 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = iterv->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv2 = luv->uv;
+ uv2_vert_sel = luv->flag & MLOOPUV_VERTSEL;
- sub_v2_v2v2(uvdiff, uv2, uv);
+ /* Check if the uv loops share the same selection state (if not, they are not connected as
+ * they have been ripped or other edit commands have separated them). */
+ const bool connected = (uv_vert_sel == uv2_vert_sel) &&
+ compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
- if (fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT &&
- (!use_winding ||
- winding[BM_elem_index_get(iterv->l->f)] == winding[BM_elem_index_get(v->l->f)])) {
+ if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] ==
+ winding[BM_elem_index_get(v->l->f)])) {
if (lastv) {
lastv->next = next;
}
@@ -1030,7 +1029,7 @@ bool EDBM_vert_color_check(BMEditMesh *em)
/** \name Mirror Cache API
* \{ */
-static BMVert *cache_mirr_intptr_as_bmvert(intptr_t *index_lookup, int index)
+static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
{
intptr_t eve_i = index_lookup[index];
return (eve_i == -1) ? NULL : (BMVert *)eve_i;
@@ -1628,10 +1627,10 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree,
if (f && !edge_ray_cast(tree, co2, dir2, NULL, e)) {
return true;
}
- else if (f && !edge_ray_cast(tree, co3, dir3, NULL, e)) {
+ if (f && !edge_ray_cast(tree, co3, dir3, NULL, e)) {
return true;
}
- else if (!f) {
+ if (!f) {
return true;
}
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index 51b699acd63..f608e5ce6a5 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);
@@ -363,9 +370,7 @@ bool ED_mesh_uv_texture_remove_active(Mesh *me)
if (n != -1) {
return ED_mesh_uv_texture_remove_index(me, n);
}
- else {
- return false;
- }
+ return false;
}
bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name)
{
@@ -375,9 +380,7 @@ bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name)
if (n != -1) {
return ED_mesh_uv_texture_remove_index(me, n);
}
- else {
- return false;
- }
+ return false;
}
/* note: keep in sync with ED_mesh_uv_texture_add */
@@ -472,9 +475,7 @@ bool ED_mesh_color_remove_active(Mesh *me)
if (n != -1) {
return ED_mesh_color_remove_index(me, n);
}
- else {
- return false;
- }
+ return false;
}
bool ED_mesh_color_remove_named(Mesh *me, const char *name)
{
@@ -483,9 +484,114 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name)
if (n != -1) {
return ED_mesh_color_remove_index(me, n);
}
+ return false;
+}
+
+/*********************** 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);
+ }
+ return false;
+}
+bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name)
+{
+ CustomData *vdata = GET_CD_DATA(me, vdata);
+ const int n = CustomData_get_named_layer(vdata, CD_PROP_COLOR, name);
+ if (n != -1) {
+ return ED_mesh_sculpt_color_remove_index(me, n);
+ }
+ return false;
}
/*********************** UV texture operators ************************/
@@ -619,6 +725,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)
@@ -643,9 +805,7 @@ static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int typ
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
/* Clear Mask */
@@ -681,9 +841,7 @@ static int mesh_customdata_mask_clear_exec(bContext *C, wmOperator *UNUSED(op))
if (ret_a == OPERATOR_FINISHED || ret_b == OPERATOR_FINISHED) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void MESH_OT_customdata_mask_clear(wmOperatorType *ot)
@@ -1115,7 +1273,7 @@ void ED_mesh_verts_remove(Mesh *mesh, ReportList *reports, int count)
BKE_report(reports, RPT_ERROR, "Cannot remove vertices in edit mode");
return;
}
- else if (count > mesh->totvert) {
+ if (count > mesh->totvert) {
BKE_report(reports, RPT_ERROR, "Cannot remove more vertices than the mesh contains");
return;
}
@@ -1129,7 +1287,7 @@ void ED_mesh_edges_remove(Mesh *mesh, ReportList *reports, int count)
BKE_report(reports, RPT_ERROR, "Cannot remove edges in edit mode");
return;
}
- else if (count > mesh->totedge) {
+ if (count > mesh->totedge) {
BKE_report(reports, RPT_ERROR, "Cannot remove more edges than the mesh contains");
return;
}
@@ -1143,7 +1301,7 @@ void ED_mesh_loops_remove(Mesh *mesh, ReportList *reports, int count)
BKE_report(reports, RPT_ERROR, "Cannot remove loops in edit mode");
return;
}
- else if (count > mesh->totloop) {
+ if (count > mesh->totloop) {
BKE_report(reports, RPT_ERROR, "Cannot remove more loops than the mesh contains");
return;
}
@@ -1157,7 +1315,7 @@ void ED_mesh_polys_remove(Mesh *mesh, ReportList *reports, int count)
BKE_report(reports, RPT_ERROR, "Cannot remove polys in edit mode");
return;
}
- else if (count > mesh->totpoly) {
+ if (count > mesh->totpoly) {
BKE_report(reports, RPT_ERROR, "Cannot remove more polys than the mesh contains");
return;
}
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 5e70069456b..cec425d687d 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -76,6 +76,7 @@ struct BMElem *EDBM_elem_from_selectmode(struct BMEditMesh *em,
struct BMVert *eve,
struct BMEdge *eed,
struct BMFace *efa);
+
int EDBM_elem_to_index_any(struct BMEditMesh *em, struct BMElem *ele);
struct BMElem *EDBM_elem_from_index_any(struct BMEditMesh *em, int index);
@@ -267,6 +268,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_mirror.c b/source/blender/editors/mesh/mesh_mirror.c
index 0bbc8b0df76..0f746dfd3a0 100644
--- a/source/blender/editors/mesh/mesh_mirror.c
+++ b/source/blender/editors/mesh/mesh_mirror.c
@@ -129,7 +129,7 @@ static int mirrtopo_hash_sort(const void *l1, const void *l2)
if ((MirrTopoHash_t)(intptr_t)l1 > (MirrTopoHash_t)(intptr_t)l2) {
return 1;
}
- else if ((MirrTopoHash_t)(intptr_t)l1 < (MirrTopoHash_t)(intptr_t)l2) {
+ if ((MirrTopoHash_t)(intptr_t)l1 < (MirrTopoHash_t)(intptr_t)l2) {
return -1;
}
return 0;
@@ -140,7 +140,7 @@ static int mirrtopo_vert_sort(const void *v1, const void *v2)
if (((MirrTopoVert_t *)v1)->hash > ((MirrTopoVert_t *)v2)->hash) {
return 1;
}
- else if (((MirrTopoVert_t *)v1)->hash < ((MirrTopoVert_t *)v2)->hash) {
+ if (((MirrTopoVert_t *)v1)->hash < ((MirrTopoVert_t *)v2)->hash) {
return -1;
}
return 0;
@@ -166,9 +166,7 @@ bool ED_mesh_mirrtopo_recalc_check(BMEditMesh *em, Mesh *me, MirrTopoStore_t *me
(totvert != mesh_topo_store->prev_vert_tot) || (totedge != mesh_topo_store->prev_edge_tot)) {
return true;
}
- else {
- return false;
- }
+ return false;
}
void ED_mesh_mirrtopo_init(BMEditMesh *em,
@@ -278,10 +276,8 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em,
* higher number of unique values compared to the previous loop. */
break;
}
- else {
- tot_unique_prev = tot_unique;
- tot_unique_edges_prev = tot_unique_edges;
- }
+ tot_unique_prev = tot_unique;
+ tot_unique_edges_prev = tot_unique_edges;
/* Copy the hash calculated this iteration, so we can use them next time */
memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
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..4d84db9b35b 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -297,7 +297,38 @@ static void join_mesh_single(Depsgraph *depsgraph,
*mpoly_pp += me->totpoly;
}
-int join_mesh_exec(bContext *C, wmOperator *op)
+/* Face Sets IDs are a sparse sequence, so this function offsets all the IDs by face_set_offset and
+ * updates face_set_offset with the maximum ID value. This way, when used in multiple meshes, all
+ * of them will have different IDs for their Face Sets. */
+static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset)
+{
+ if (!mesh->totpoly) {
+ return;
+ }
+
+ int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ if (!face_sets) {
+ return;
+ }
+
+ int max_face_set = 0;
+ for (int f = 0; f < mesh->totpoly; f++) {
+ /* As face sets encode the visibility in the integer sign, the offset needs to be added or
+ * subtracted depending on the initial sign of the integer to get the new ID. */
+ if (abs(face_sets[f]) <= *face_set_offset) {
+ if (face_sets[f] > 0) {
+ face_sets[f] += *face_set_offset;
+ }
+ else {
+ face_sets[f] -= *face_set_offset;
+ }
+ }
+ max_face_set = max_ii(max_face_set, abs(face_sets[f]));
+ }
+ *face_set_offset = max_face_set;
+}
+
+int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -431,7 +462,13 @@ int join_mesh_exec(bContext *C, wmOperator *op)
key->type = KEY_RELATIVE;
}
- /* First pass over objects: Copying materials, vertex-groups & face-maps across. */
+ /* Update face_set_id_offset with the face set data in the active object first. This way the Face
+ * Sets IDs in the active object are not the ones that are modified. */
+ Mesh *mesh_active = BKE_mesh_from_object(ob);
+ int face_set_id_offset = 0;
+ mesh_join_offset_face_sets_ID(mesh_active, &face_set_id_offset);
+
+ /* Copy materials, vertex-groups, face sets & face-maps across objects. */
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
/* only act if a mesh, and not the one we're joining to */
if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) {
@@ -463,6 +500,8 @@ int join_mesh_exec(bContext *C, wmOperator *op)
ob->actfmap = 1;
}
+ mesh_join_offset_face_sets_ID(me, &face_set_id_offset);
+
if (me->totvert) {
/* Add this object's materials to the base one's if they don't exist already
* (but only if limits not exceeded yet) */
@@ -706,12 +745,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 +837,8 @@ int join_mesh_shapes_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Mesh Topology Mirror API
* \{ */
@@ -888,9 +931,7 @@ int mesh_get_x_mirror_vert(Object *ob, Mesh *me_eval, int index, const bool use_
if (use_topology) {
return mesh_get_x_mirror_vert_topo(ob, me_eval, index);
}
- else {
- return mesh_get_x_mirror_vert_spatial(ob, me_eval, index);
- }
+ return mesh_get_x_mirror_vert_spatial(ob, me_eval, index);
}
static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, const float co[3])
@@ -959,9 +1000,7 @@ BMVert *editbmesh_get_x_mirror_vert(Object *ob,
if (use_topology) {
return editbmesh_get_x_mirror_vert_topo(ob, em, eve, index);
}
- else {
- return editbmesh_get_x_mirror_vert_spatial(ob, em, co);
- }
+ return editbmesh_get_x_mirror_vert_spatial(ob, em, co);
}
/**
@@ -1068,13 +1107,13 @@ static int mirror_facerotation(MFace *a, MFace *b)
if (a->v1 == b->v1 && a->v2 == b->v2 && a->v3 == b->v3 && a->v4 == b->v4) {
return 0;
}
- else if (a->v4 == b->v1 && a->v1 == b->v2 && a->v2 == b->v3 && a->v3 == b->v4) {
+ if (a->v4 == b->v1 && a->v1 == b->v2 && a->v2 == b->v3 && a->v3 == b->v4) {
return 1;
}
- else if (a->v3 == b->v1 && a->v4 == b->v2 && a->v1 == b->v3 && a->v2 == b->v4) {
+ if (a->v3 == b->v1 && a->v4 == b->v2 && a->v1 == b->v3 && a->v2 == b->v4) {
return 2;
}
- else if (a->v2 == b->v1 && a->v3 == b->v2 && a->v4 == b->v3 && a->v1 == b->v4) {
+ if (a->v2 == b->v1 && a->v3 == b->v2 && a->v4 == b->v3 && a->v1 == b->v4) {
return 3;
}
}
@@ -1082,10 +1121,10 @@ static int mirror_facerotation(MFace *a, MFace *b)
if (a->v1 == b->v1 && a->v2 == b->v2 && a->v3 == b->v3) {
return 0;
}
- else if (a->v3 == b->v1 && a->v1 == b->v2 && a->v2 == b->v3) {
+ if (a->v3 == b->v1 && a->v1 == b->v2 && a->v2 == b->v3) {
return 1;
}
- else if (a->v2 == b->v1 && a->v3 == b->v2 && a->v1 == b->v3) {
+ if (a->v2 == b->v1 && a->v3 == b->v2 && a->v1 == b->v3) {
return 2;
}
}
@@ -1464,9 +1503,7 @@ MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index)
if (index == -1 || me->dvert == NULL) {
return NULL;
}
- else {
- return me->dvert + index;
- }
+ return me->dvert + index;
}
MDeformVert *ED_mesh_active_dvert_get_only(Object *ob)
@@ -1475,13 +1512,9 @@ MDeformVert *ED_mesh_active_dvert_get_only(Object *ob)
if (ob->mode & OB_MODE_EDIT) {
return ED_mesh_active_dvert_get_em(ob, NULL);
}
- else {
- return ED_mesh_active_dvert_get_ob(ob, NULL);
- }
- }
- else {
- return NULL;
+ return ED_mesh_active_dvert_get_ob(ob, NULL);
}
+ return NULL;
}
void EDBM_mesh_stats_multi(struct Object **objects,
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..f92dafcc7f9 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) {
@@ -1392,7 +1395,7 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op)
/* Avoid dependency cycles. */
LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer);
- while (BKE_collection_find_cycle(active_lc->collection, collection)) {
+ while (BKE_collection_cycle_find(active_lc->collection, collection)) {
active_lc = BKE_layer_collection_activate_parent(view_layer, active_lc);
}
@@ -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;
@@ -1623,7 +1642,7 @@ static int object_delete_exec(bContext *C, wmOperator *op)
ob->id.name + 2);
continue;
}
- else if (is_indirectly_used && ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) {
+ if (is_indirectly_used && ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) {
BKE_reportf(op->reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at "
@@ -1753,6 +1772,21 @@ static void copy_object_set_idnew(bContext *C)
}
CTX_DATA_END;
+#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 (GS(id_iter->name) == ID_OB) {
+ /* Not all duplicated objects would be used by other newly duplicated data, so their flag
+ * will not always be cleared. */
+ continue;
+ }
+ BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
+ }
+ FOREACH_MAIN_ID_END;
+#endif
+
+ BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
@@ -1832,7 +1866,7 @@ static bool dupliobject_cmp(const void *a_, const void *b_)
if (a->persistent_id[i] != b->persistent_id[i]) {
return true;
}
- else if (a->persistent_id[i] == INT_MAX) {
+ if (a->persistent_id[i] == INT_MAX) {
break;
}
}
@@ -1857,7 +1891,7 @@ static bool dupliobject_instancer_cmp(const void *a_, const void *b_)
if (a->persistent_id[i] != b->persistent_id[i]) {
return true;
}
- else if (a->persistent_id[i] == INT_MAX) {
+ if (a->persistent_id[i] == INT_MAX) {
break;
}
}
@@ -2129,11 +2163,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 +2186,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 +2219,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 +2269,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 +2287,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 +2300,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 +2421,55 @@ 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,
+ false);
+ 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 +2565,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 +2590,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 +2604,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 +2644,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 +2713,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 +2747,30 @@ 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;
+
+ uiLayoutSetPropSep(layout, true);
+
+ 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 +2778,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 +2793,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 +2836,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 +2850,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 +2889,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 +2923,16 @@ 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;
+
+ /* We need to handle that here ourselves, because we may duplicate several objects, in which case
+ * we also want to remap pointers between those... */
+ BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
+ BKE_main_id_clear_newpoins(bmain);
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, LIB_ID_DUPLICATE_IS_SUBPROCESS);
/* note that this is safe to do with this context iterator,
* the list is made in advance */
@@ -2800,6 +2954,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
+ /* Note that this will also clear newid pointers and tags. */
copy_object_set_idnew(C);
ED_outliner_select_sync_from_object_tag(C);
@@ -2849,7 +3004,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 +3014,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 +3027,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 +3067,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 +3088,22 @@ 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);
@@ -2957,11 +3111,11 @@ static int join_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "This data does not support joining in edit mode");
return OPERATOR_CANCELLED;
}
- else if (BKE_object_obdata_is_libdata(ob)) {
+ if (BKE_object_obdata_is_libdata(ob)) {
BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data");
return OPERATOR_CANCELLED;
}
- else if (ob->type == OB_GPENCIL) {
+ if (ob->type == OB_GPENCIL) {
bGPdata *gpd = (bGPdata *)ob->data;
if ((!gpd) || GPENCIL_ANY_MODE(gpd)) {
BKE_report(op->reports, RPT_ERROR, "This data does not support joining in this mode");
@@ -2970,15 +3124,15 @@ 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);
+ if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ return ED_curve_join_objects_exec(C, op);
}
- else if (ob->type == OB_ARMATURE) {
- return join_armature_exec(C, op);
+ if (ob->type == OB_ARMATURE) {
+ return ED_armature_join_objects_exec(C, op);
}
- else if (ob->type == OB_GPENCIL) {
+ if (ob->type == OB_GPENCIL) {
return ED_gpencil_join_objects_exec(C, op);
}
@@ -2993,8 +3147,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,17 +3164,16 @@ 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 */
if (ob->type == OB_MESH) {
return ED_operator_screenactive(C);
}
- else {
- return 0;
- }
+ return false;
}
static int join_shapes_exec(bContext *C, wmOperator *op)
@@ -3031,13 +3184,13 @@ static int join_shapes_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "This data does not support joining in edit mode");
return OPERATOR_CANCELLED;
}
- else if (BKE_object_obdata_is_libdata(ob)) {
+ if (BKE_object_obdata_is_libdata(ob)) {
BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data");
return OPERATOR_CANCELLED;
}
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_bake.c b/source/blender/editors/object/object_bake.c
index baa24ab2f4e..ae1aae27b7f 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -60,8 +60,6 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
-#include "GPU_draw.h" /* GPU_free_image */
-
#include "WM_api.h"
#include "WM_types.h"
@@ -530,7 +528,7 @@ static void multiresbake_freejob(void *bkv)
/* delete here, since this delete will be called from main thread */
for (link = data->images.first; link; link = link->next) {
Image *ima = (Image *)link->data;
- GPU_free_image(ima);
+ BKE_image_free_gputextures(ima);
}
MEM_freeN(data->ob_image.array);
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index e84dbca2469..926a47b1ec8 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -68,8 +68,6 @@
#include "ED_screen.h"
#include "ED_uvedit.h"
-#include "GPU_draw.h"
-
#include "object_intern.h"
/* prototypes */
@@ -308,7 +306,7 @@ static void refresh_images(BakeImages *bake_images)
Image *ima = bake_images->data[i].image;
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
if (tile->ok == IMA_OK_LOADED) {
- GPU_free_image(ima);
+ BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
break;
}
@@ -434,14 +432,12 @@ static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *rep
BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh", ob->id.name + 2);
return false;
}
- else {
- Mesh *me = (Mesh *)ob->data;
- if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) {
- BKE_reportf(
- reports, RPT_ERROR, "No active UV layer found in the object \"%s\"", ob->id.name + 2);
- return false;
- }
+ Mesh *me = (Mesh *)ob->data;
+ if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) {
+ BKE_reportf(
+ reports, RPT_ERROR, "No active UV layer found in the object \"%s\"", ob->id.name + 2);
+ return false;
}
for (i = 0; i < ob->totcol; i++) {
@@ -542,14 +538,11 @@ static bool bake_pass_filter_check(eScenePassType pass_type,
return false;
}
- else {
- BKE_report(reports,
- RPT_ERROR,
- "Combined bake pass requires Emit, or a light pass with "
- "Direct or Indirect contributions enabled");
- return false;
- }
- break;
+ BKE_report(reports,
+ RPT_ERROR,
+ "Combined bake pass requires Emit, or a light pass with "
+ "Direct or Indirect contributions enabled");
+ return false;
case SCE_PASS_DIFFUSE_COLOR:
case SCE_PASS_GLOSSY_COLOR:
case SCE_PASS_TRANSM_COLOR:
@@ -1071,10 +1064,7 @@ static int bake(Render *re,
(normal_swizzle[2] == R_BAKE_POSZ)) {
break;
}
- else {
- RE_bake_normal_world_to_world(
- pixel_array_low, num_pixels, depth, result, normal_swizzle);
- }
+ RE_bake_normal_world_to_world(pixel_array_low, num_pixels, depth, result, normal_swizzle);
break;
}
case R_BAKE_SPACE_OBJECT: {
diff --git a/source/blender/editors/object/object_collection.c b/source/blender/editors/object/object_collection.c
index 7a83d582299..24f6d8c79f9 100644
--- a/source/blender/editors/object/object_collection.c
+++ b/source/blender/editors/object/object_collection.c
@@ -483,13 +483,13 @@ static int collection_link_exec(bContext *C, wmOperator *op)
/* Currently this should not be allowed (might be supported in the future though...). */
if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
- BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is overridden.");
+ BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is overridden");
return OPERATOR_CANCELLED;
}
/* Linked collections are already checked for by using RNA_collection_local_itemf
* but operator can be called without invoke */
if (ID_IS_LINKED(&collection->id)) {
- BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is linked.");
+ BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is linked");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 1e04355c9a7..bcb1b8afbdd 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -670,20 +670,20 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
if (!ob) {
CTX_wm_operator_poll_msg_set(C, "Context missing active object");
- return 0;
+ return false;
}
if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
- return 0;
+ return false;
}
- if (ID_IS_OVERRIDE_LIBRARY(ob)) {
+ if (ID_IS_OVERRIDE_LIBRARY(ob) && ptr.data != NULL) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit constraints coming from library override");
return (((bConstraint *)ptr.data)->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0;
}
- return 1;
+ return true;
}
static bool edit_constraint_poll(bContext *C)
@@ -702,7 +702,17 @@ static void edit_constraint_properties(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
+static void edit_constraint_report_property(wmOperatorType *ot)
+{
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "report", false, "Report", "Create a notification after the operation");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+static bool edit_constraint_invoke_properties(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
@@ -711,7 +721,7 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
if (RNA_struct_property_is_set(op->ptr, "constraint") &&
RNA_struct_property_is_set(op->ptr, "owner")) {
- return 1;
+ return true;
}
if (ptr.data) {
@@ -727,10 +737,35 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_BONE);
}
- return 1;
+ 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_Constraint)) {
+ con = panel_ptr->data;
+ RNA_string_set(op->ptr, "constraint", con->name);
+ list = ED_object_constraint_list_from_constraint(ob, con, NULL);
+ RNA_enum_set(op->ptr,
+ "owner",
+ (&ob->constraints == list) ? EDIT_CONSTRAINT_OWNER_OBJECT :
+ EDIT_CONSTRAINT_OWNER_BONE);
+
+ return true;
+ }
+
+ 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;
+ }
}
- return 0;
+ return false;
}
static bConstraint *edit_constraint_property_get(wmOperator *op, Object *ob, int type)
@@ -813,12 +848,10 @@ static int stretchto_reset_exec(bContext *C, wmOperator *op)
static int stretchto_reset_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return stretchto_reset_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot)
@@ -870,12 +903,10 @@ static int limitdistance_reset_exec(bContext *C, wmOperator *op)
static int limitdistance_reset_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return limitdistance_reset_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot)
@@ -950,12 +981,10 @@ static int childof_set_inverse_exec(bContext *C, wmOperator *op)
static int childof_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return childof_set_inverse_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot)
@@ -1001,12 +1030,10 @@ static int childof_clear_inverse_exec(bContext *C, wmOperator *op)
static int childof_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return childof_clear_inverse_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot)
@@ -1128,12 +1155,10 @@ static int followpath_path_animate_invoke(bContext *C,
const wmEvent *UNUSED(event))
{
/* hook up invoke properties for figuring out which constraint we're dealing with */
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return followpath_path_animate_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_followpath_path_animate(wmOperatorType *ot)
@@ -1211,12 +1236,10 @@ static int objectsolver_set_inverse_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return objectsolver_set_inverse_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_objectsolver_set_inverse(wmOperatorType *ot)
@@ -1269,12 +1292,10 @@ static int objectsolver_clear_inverse_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
- if (edit_constraint_invoke_properties(C, op)) {
+ if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return objectsolver_clear_inverse_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_objectsolver_clear_inverse(wmOperatorType *ot)
@@ -1388,26 +1409,23 @@ void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstr
DEG_relations_tag_update(bmain);
}
-static bool constraint_poll(bContext *C)
-{
- PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
- return (ptr.owner_id && ptr.data);
-}
-
/** \} */
/* ------------------------------------------------------------------- */
/** \name Delete Constraint Operator
* \{ */
-static int constraint_delete_exec(bContext *C, wmOperator *UNUSED(op))
+static int constraint_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
- Object *ob = (Object *)ptr.owner_id;
- bConstraint *con = ptr.data;
+ Object *ob = ED_object_active_context(C);
+ bConstraint *con = edit_constraint_property_get(op, ob, 0);
ListBase *lb = ED_object_constraint_list_from_constraint(ob, con, NULL);
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, con->name);
+
/* free the constraint */
if (BKE_constraint_remove_ex(lb, ob, con, true)) {
/* there's no active constraint now, so make sure this is the case */
@@ -1421,12 +1439,23 @@ static int constraint_delete_exec(bContext *C, wmOperator *UNUSED(op))
/* notifiers */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Removed constraint: %s", name);
+ }
+
return OPERATOR_FINISHED;
}
- else {
- /* couldn't remove due to some invalid data */
- return OPERATOR_CANCELLED;
+ /* couldn't remove due to some invalid data */
+ return OPERATOR_CANCELLED;
+}
+
+static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (edit_constraint_invoke_properties(C, op, event, &retval)) {
+ return constraint_delete_exec(C, op);
}
+ return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_delete(wmOperatorType *ot)
@@ -1437,11 +1466,14 @@ void CONSTRAINT_OT_delete(wmOperatorType *ot)
ot->description = "Remove constraint from constraint stack";
/* callbacks */
+ ot->invoke = constraint_delete_invoke;
ot->exec = constraint_delete_exec;
- ot->poll = constraint_poll;
+ ot->poll = edit_constraint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ edit_constraint_properties(ot);
+ edit_constraint_report_property(ot);
}
/** \} */
@@ -1471,14 +1503,13 @@ static int constraint_move_down_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int constraint_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int constraint_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_constraint_invoke_properties(C, op)) {
+ int retval;
+ if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_move_down_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return retval;
}
void CONSTRAINT_OT_move_down(wmOperatorType *ot)
@@ -1527,14 +1558,13 @@ static int constraint_move_up_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int constraint_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int constraint_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_constraint_invoke_properties(C, op)) {
+ int retval;
+ if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_move_up_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return retval;
}
void CONSTRAINT_OT_move_up(wmOperatorType *ot)
@@ -1557,6 +1587,74 @@ 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 *event)
+{
+ int retval;
+ if (edit_constraint_invoke_properties(C, op, event, &retval)) {
+ return constraint_move_to_index_exec(C, op);
+ }
+ return retval;
+}
+
+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
* \{ */
@@ -1634,7 +1732,7 @@ void OBJECT_OT_constraints_clear(wmOperatorType *ot)
/* callbacks */
ot->exec = object_constraints_clear_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
}
/** \} */
@@ -1837,8 +1935,7 @@ static bool get_new_constraint_target(
break;
}
- else if (((!only_curve) || (ob->type == OB_CURVE)) &&
- ((!only_mesh) || (ob->type == OB_MESH))) {
+ if (((!only_curve) || (ob->type == OB_CURVE)) && ((!only_mesh) || (ob->type == OB_MESH))) {
/* set target */
*tar_ob = ob;
found = true;
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 1e2b7702dc5..5a0656ee916 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -76,7 +76,7 @@ static const EnumPropertyItem DT_layer_items[] = {
{0, "", 0, "Edge Data", ""},
{DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"},
{DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"},
- {DT_TYPE_CREASE, "CREASE", 0, "Subsurf Crease", "Transfer crease values"},
+ {DT_TYPE_CREASE, "CREASE", 0, "Subdivision Crease", "Transfer crease values"},
{DT_TYPE_BWEIGHT_EDGE, "BEVEL_WEIGHT_EDGE", 0, "Bevel Weight", "Transfer bevel weights"},
{DT_TYPE_FREESTYLE_EDGE,
"FREESTYLE_EDGE",
@@ -254,16 +254,12 @@ static const EnumPropertyItem *dt_layers_select_itemf(bContext *C,
if (reverse_transfer) {
return dt_layers_select_src_itemf(C, ptr, prop, r_free);
}
- else {
- return dt_layers_select_dst_itemf(C, ptr, prop, r_free);
- }
- }
- else if (reverse_transfer) {
return dt_layers_select_dst_itemf(C, ptr, prop, r_free);
}
- else {
- return dt_layers_select_src_itemf(C, ptr, prop, r_free);
+ if (reverse_transfer) {
+ return dt_layers_select_dst_itemf(C, ptr, prop, r_free);
}
+ return dt_layers_select_src_itemf(C, ptr, prop, r_free);
}
/* Note: rna_enum_dt_mix_mode_items enum is from rna_modifier.c */
@@ -381,7 +377,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)) {
+ 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(
@@ -424,8 +420,8 @@ static int data_transfer_exec(bContext *C, wmOperator *op)
const float ray_radius = RNA_float_get(op->ptr, "ray_radius");
const float islands_precision = RNA_float_get(op->ptr, "islands_precision");
- const int layers_src = RNA_enum_get(op->ptr, "layers_select_src");
- const int layers_dst = RNA_enum_get(op->ptr, "layers_select_dst");
+ int layers_src = RNA_enum_get(op->ptr, "layers_select_src");
+ int layers_dst = RNA_enum_get(op->ptr, "layers_select_dst");
int layers_select_src[DT_MULTILAYER_INDEX_MAX] = {0};
int layers_select_dst[DT_MULTILAYER_INDEX_MAX] = {0};
const int fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(data_type);
@@ -446,11 +442,15 @@ 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;
}
+ if (reverse_transfer) {
+ SWAP(int, layers_src, layers_dst);
+ }
+
if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) {
layers_select_src[fromto_idx] = layers_src;
layers_select_dst[fromto_idx] = layers_dst;
@@ -512,13 +512,15 @@ static int data_transfer_exec(bContext *C, wmOperator *op)
BLI_freelistN(&ctx_objects);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+ if (changed) {
+ DEG_relations_tag_update(CTX_data_main(C));
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+ }
#if 0 /* TODO */
/* Note: issue with that is that if canceled, operator cannot be redone... Nasty in our case. */
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
#else
- (void)changed;
return OPERATOR_FINISHED;
#endif
}
@@ -530,7 +532,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 +788,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,12 +856,10 @@ 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 {
- return WM_menu_invoke(C, op, event);
- }
+ return WM_menu_invoke(C, op, event);
}
void OBJECT_OT_datalayout_transfer(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c
index 54fd1fe6671..8ea35c7a92c 100644
--- a/source/blender/editors/object/object_data_transform.c
+++ b/source/blender/editors/object/object_data_transform.c
@@ -33,6 +33,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_collection_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
@@ -46,6 +47,8 @@
#include "BKE_armature.h"
#include "BKE_curve.h"
#include "BKE_editmesh.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_mball.h"
#include "BKE_mesh.h"
@@ -280,16 +283,22 @@ struct XFormObjectData {
struct XFormObjectData_Mesh {
struct XFormObjectData base;
+ /* Optional data for shape keys. */
+ void *key_data;
float elem_array[0][3];
};
struct XFormObjectData_Lattice {
struct XFormObjectData base;
+ /* Optional data for shape keys. */
+ void *key_data;
float elem_array[0][3];
};
struct XFormObjectData_Curve {
struct XFormObjectData base;
+ /* Optional data for shape keys. */
+ void *key_data;
float elem_array[0][3];
};
@@ -303,54 +312,109 @@ struct XFormObjectData_MetaBall {
struct ElemData_MetaBall elem_array[0];
};
+struct XFormObjectData_GPencil {
+ struct XFormObjectData base;
+ struct GPencilPointCoordinates elem_array[0];
+};
+
struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode)
{
struct XFormObjectData *xod_base = NULL;
if (id == NULL) {
return xod_base;
}
+
switch (GS(id->name)) {
case ID_ME: {
Mesh *me = (Mesh *)id;
+ struct Key *key = me->key;
+ const int key_index = -1;
+
if (is_edit_mode) {
BMesh *bm = me->edit_mesh->bm;
+ /* Always operate on all keys for the moment. */
+ // key_index = bm->shapenr - 1;
const int elem_array_len = bm->totvert;
struct XFormObjectData_Mesh *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
BM_mesh_vert_coords_get(bm, xod->elem_array);
xod_base = &xod->base;
+
+ if (key != NULL) {
+ const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
+ if (key_size) {
+ xod->key_data = MEM_mallocN(key_size, __func__);
+ BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
+ }
+ }
}
else {
const int elem_array_len = me->totvert;
struct XFormObjectData_Mesh *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
BKE_mesh_vert_coords_get(me, xod->elem_array);
xod_base = &xod->base;
+
+ if (key != NULL) {
+ const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
+ if (key_size) {
+ xod->key_data = MEM_mallocN(key_size, __func__);
+ BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
+ }
+ }
}
break;
}
case ID_LT: {
Lattice *lt_orig = (Lattice *)id;
Lattice *lt = is_edit_mode ? lt_orig->editlatt->latt : lt_orig;
+ struct Key *key = lt->key;
+ const int key_index = -1;
+
+ if (is_edit_mode) {
+ /* Always operate on all keys for the moment. */
+ // key_index = lt_orig->editlatt->shapenr - 1;
+ }
+
const int elem_array_len = lt->pntsu * lt->pntsv * lt->pntsw;
struct XFormObjectData_Lattice *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
BKE_lattice_vert_coords_get(lt, xod->elem_array);
xod_base = &xod->base;
+
+ if (key != NULL) {
+ const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
+ if (key_size) {
+ xod->key_data = MEM_mallocN(key_size, __func__);
+ BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
+ }
+ }
+
break;
}
case ID_CU: {
Curve *cu = (Curve *)id;
+ struct Key *key = cu->key;
+
const short ob_type = BKE_curve_type_get(cu);
if (ob_type == OB_FONT) {
/* We could support translation. */
break;
}
+ const int key_index = -1;
ListBase *nurbs;
if (is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
nurbs = &editnurb->nurbs;
+ /* Always operate on all keys for the moment. */
+ // key_index = editnurb->shapenr - 1;
}
else {
nurbs = &cu->nurb;
@@ -359,8 +423,19 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode
const int elem_array_len = BKE_nurbList_verts_count(nurbs);
struct XFormObjectData_Curve *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
BKE_curve_nurbs_vert_coords_get(nurbs, xod->elem_array, elem_array_len);
xod_base = &xod->base;
+
+ if (key != NULL) {
+ const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
+ if (key_size) {
+ xod->key_data = MEM_mallocN(key_size, __func__);
+ BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
+ }
+ }
+
break;
}
case ID_AR: {
@@ -369,6 +444,8 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode
const int elem_array_len = BLI_listbase_count(arm->edbo);
struct XFormObjectData_Armature *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
edit_armature_coords_and_quats_get(arm, xod->elem_array);
xod_base = &xod->base;
}
@@ -376,6 +453,8 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode
const int elem_array_len = BKE_armature_bonelist_count(&arm->bonebase);
struct XFormObjectData_Armature *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
armature_coords_and_quats_get(arm, xod->elem_array);
xod_base = &xod->base;
}
@@ -387,10 +466,23 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode
const int elem_array_len = BLI_listbase_count(&mb->elems);
struct XFormObjectData_MetaBall *xod = MEM_mallocN(
sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
metaball_coords_and_quats_get(mb, xod->elem_array);
xod_base = &xod->base;
break;
}
+ case ID_GD: {
+ bGPdata *gpd = (bGPdata *)id;
+ const int elem_array_len = BKE_gpencil_stroke_point_count(gpd);
+ struct XFormObjectData_GPencil *xod = MEM_mallocN(
+ sizeof(*xod) + (sizeof(*xod->elem_array) * elem_array_len), __func__);
+ memset(xod, 0x0, sizeof(*xod));
+
+ BKE_gpencil_point_coords_get(gpd, xod->elem_array);
+ xod_base = &xod->base;
+ break;
+ }
default: {
break;
}
@@ -412,9 +504,35 @@ struct XFormObjectData *ED_object_data_xform_create_from_edit_mode(ID *id)
return ED_object_data_xform_create_ex(id, true);
}
-void ED_object_data_xform_destroy(struct XFormObjectData *xod)
+void ED_object_data_xform_destroy(struct XFormObjectData *xod_base)
{
- MEM_freeN(xod);
+ switch (GS(xod_base->id->name)) {
+ case ID_ME: {
+ struct XFormObjectData_Mesh *xod = (struct XFormObjectData_Mesh *)xod_base;
+ if (xod->key_data != NULL) {
+ MEM_freeN(xod->key_data);
+ }
+ break;
+ }
+ case ID_LT: {
+ struct XFormObjectData_Lattice *xod = (struct XFormObjectData_Lattice *)xod_base;
+ if (xod->key_data != NULL) {
+ MEM_freeN(xod->key_data);
+ }
+ break;
+ }
+ case ID_CU: {
+ struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
+ if (xod->key_data != NULL) {
+ MEM_freeN(xod->key_data);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ MEM_freeN(xod_base);
}
void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float mat[4][4])
@@ -422,34 +540,72 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float
switch (GS(xod_base->id->name)) {
case ID_ME: {
Mesh *me = (Mesh *)xod_base->id;
+
+ struct Key *key = me->key;
+ const int key_index = -1;
+
struct XFormObjectData_Mesh *xod = (struct XFormObjectData_Mesh *)xod_base;
if (xod_base->is_edit_mode) {
BMesh *bm = me->edit_mesh->bm;
BM_mesh_vert_coords_apply_with_mat4(bm, xod->elem_array, mat);
+ /* Always operate on all keys for the moment. */
+ // key_index = bm->shapenr - 1;
}
else {
BKE_mesh_vert_coords_apply_with_mat4(me, xod->elem_array, mat);
}
+
+ if (key != NULL) {
+ BKE_keyblock_data_set_with_mat4(key, key_index, xod->key_data, mat);
+ }
+
break;
}
case ID_LT: {
Lattice *lt_orig = (Lattice *)xod_base->id;
Lattice *lt = xod_base->is_edit_mode ? lt_orig->editlatt->latt : lt_orig;
+
+ struct Key *key = lt->key;
+ const int key_index = -1;
+
struct XFormObjectData_Lattice *xod = (struct XFormObjectData_Lattice *)xod_base;
BKE_lattice_vert_coords_apply_with_mat4(lt, xod->elem_array, mat);
+ if (xod_base->is_edit_mode) {
+ /* Always operate on all keys for the moment. */
+ // key_index = lt_orig->editlatt->shapenr - 1;
+ }
+
+ if ((key != NULL) && (xod->key_data != NULL)) {
+ BKE_keyblock_data_set_with_mat4(key, key_index, xod->key_data, mat);
+ }
+
break;
}
case ID_CU: {
BLI_assert(xod_base->is_edit_mode == false); /* Not used currently. */
Curve *cu = (Curve *)xod_base->id;
+
+ struct Key *key = cu->key;
+ const int key_index = -1;
+ ListBase *nurb = NULL;
+
struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
if (xod_base->is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
+ nurb = &editnurb->nurbs;
BKE_curve_nurbs_vert_coords_apply_with_mat4(&editnurb->nurbs, xod->elem_array, mat, true);
+ /* Always operate on all keys for the moment. */
+ // key_index = editnurb->shapenr - 1;
}
else {
+ nurb = &cu->nurb;
BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, true);
}
+
+ if ((key != NULL) && (xod->key_data != NULL)) {
+ BKE_keyblock_curve_data_set_with_mat4(key, nurb, key_index, xod->key_data, mat);
+ }
+
break;
}
case ID_AR: {
@@ -471,6 +627,12 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float
metaball_coords_and_quats_apply_with_mat4(mb, xod->elem_array, mat);
break;
}
+ case ID_GD: {
+ bGPdata *gpd = (bGPdata *)xod_base->id;
+ struct XFormObjectData_GPencil *xod = (struct XFormObjectData_GPencil *)xod_base;
+ BKE_gpencil_point_coords_apply_with_mat4(gpd, xod->elem_array, mat);
+ break;
+ }
default: {
break;
}
@@ -482,33 +644,68 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base)
switch (GS(xod_base->id->name)) {
case ID_ME: {
Mesh *me = (Mesh *)xod_base->id;
+
+ struct Key *key = me->key;
+ const int key_index = -1;
+
struct XFormObjectData_Mesh *xod = (struct XFormObjectData_Mesh *)xod_base;
if (xod_base->is_edit_mode) {
BMesh *bm = me->edit_mesh->bm;
BM_mesh_vert_coords_apply(bm, xod->elem_array);
+ /* Always operate on all keys for the moment. */
+ // key_index = bm->shapenr - 1;
}
else {
BKE_mesh_vert_coords_apply(me, xod->elem_array);
}
+
+ if ((key != NULL) && (xod->key_data != NULL)) {
+ BKE_keyblock_data_set(key, key_index, xod->key_data);
+ }
+
break;
}
case ID_LT: {
Lattice *lt_orig = (Lattice *)xod_base->id;
Lattice *lt = xod_base->is_edit_mode ? lt_orig->editlatt->latt : lt_orig;
+
+ struct Key *key = lt->key;
+ const int key_index = -1;
+
struct XFormObjectData_Lattice *xod = (struct XFormObjectData_Lattice *)xod_base;
BKE_lattice_vert_coords_apply(lt, xod->elem_array);
+ if (xod_base->is_edit_mode) {
+ /* Always operate on all keys for the moment. */
+ // key_index = lt_orig->editlatt->shapenr - 1;
+ }
+
+ if ((key != NULL) && (xod->key_data != NULL)) {
+ BKE_keyblock_data_set(key, key_index, xod->key_data);
+ }
+
break;
}
case ID_CU: {
Curve *cu = (Curve *)xod_base->id;
+
+ struct Key *key = cu->key;
+ const int key_index = -1;
+
struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
if (xod_base->is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
BKE_curve_nurbs_vert_coords_apply(&editnurb->nurbs, xod->elem_array, true);
+ /* Always operate on all keys for the moment. */
+ // key_index = editnurb->shapenr - 1;
}
else {
BKE_curve_nurbs_vert_coords_apply(&cu->nurb, xod->elem_array, true);
}
+
+ if ((key != NULL) && (xod->key_data != NULL)) {
+ BKE_keyblock_data_set(key, key_index, xod->key_data);
+ }
+
break;
}
case ID_AR: {
@@ -529,6 +726,12 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base)
metaball_coords_and_quats_apply(mb, xod->elem_array);
break;
}
+ case ID_GD: {
+ bGPdata *gpd = (bGPdata *)xod_base->id;
+ struct XFormObjectData_GPencil *xod = (struct XFormObjectData_GPencil *)xod_base;
+ BKE_gpencil_point_coords_apply(gpd, xod->elem_array);
+ break;
+ }
default: {
break;
}
@@ -572,6 +775,12 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base)
DEG_id_tag_update(&mb->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
break;
}
+ case ID_GD: {
+ /* Generic update. */
+ bGPdata *gpd = (bGPdata *)xod_base->id;
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ break;
+ }
default: {
break;
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index d522dcabae3..04113f70e52 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -126,14 +126,14 @@ static ListBase selected_objects_get(bContext *C);
/** \name Internal Utilities
* \{ */
-Object *ED_object_context(bContext *C)
+Object *ED_object_context(const bContext *C)
{
return CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
}
/* find the correct active object per context
* note: context can be NULL when called from a enum with PROP_ENUM_NO_CONTEXT */
-Object *ED_object_active_context(bContext *C)
+Object *ED_object_active_context(const bContext *C)
{
Object *ob = NULL;
if (C) {
@@ -156,9 +156,7 @@ static bool object_hide_poll(bContext *C)
if (CTX_wm_space_outliner(C) != NULL) {
return ED_outliner_collections_editor_poll(C);
}
- else {
- return ED_operator_view3d_active(C);
- }
+ return ED_operator_view3d_active(C);
}
static int object_hide_view_clear_exec(bContext *C, wmOperator *op)
@@ -600,7 +598,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 +694,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 +755,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;
}
@@ -899,7 +895,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object)
else {
if (!pd || (pd->shape != PFIELD_SHAPE_SURFACE) ||
ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) {
- ED_object_modifier_remove(NULL, bmain, object, md);
+ ED_object_modifier_remove(NULL, bmain, scene, object, md);
}
}
}
@@ -1368,7 +1364,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;
}
}
@@ -1635,15 +1632,14 @@ static bool move_to_collection_poll(bContext *C)
if (CTX_wm_space_outliner(C) != NULL) {
return ED_outliner_collections_editor_poll(C);
}
- else {
- View3D *v3d = CTX_wm_view3d(C);
- if (v3d && v3d->localvd) {
- return false;
- }
+ View3D *v3d = CTX_wm_view3d(C);
- return ED_operator_objectmode(C);
+ if (v3d && v3d->localvd) {
+ return false;
}
+
+ return ED_operator_objectmode(C);
}
static int move_to_collection_exec(bContext *C, wmOperator *op)
@@ -1771,15 +1767,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 +1774,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..019ab2c345b 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)
{
@@ -225,7 +245,7 @@ static int gpencil_modifier_apply_obdata(
if (ELEM(NULL, ob, ob->data)) {
return 0;
}
- else if (mti->bakeModifier == NULL) {
+ if (mti->bakeModifier == NULL) {
BKE_report(reports, RPT_ERROR, "Not implemented");
return 0;
}
@@ -428,22 +448,57 @@ 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;
+ }
+
+ 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,25 +527,34 @@ 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;
}
void OBJECT_OT_gpencil_modifier_remove(wmOperatorType *ot)
@@ -506,6 +570,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,16 +590,13 @@ 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;
}
void OBJECT_OT_gpencil_modifier_move_up(wmOperatorType *ot)
@@ -569,16 +631,13 @@ 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;
}
void OBJECT_OT_gpencil_modifier_move_down(wmOperatorType *ot)
@@ -596,6 +655,57 @@ 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);
+ }
+ 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,24 +716,35 @@ 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;
}
static const EnumPropertyItem gpencil_modifier_apply_as_items[] = {
@@ -656,6 +777,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,14 +797,13 @@ 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;
}
void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c
index 9d2e5e74352..6bb03e62191 100644
--- a/source/blender/editors/object/object_hook.c
+++ b/source/blender/editors/object/object_hook.c
@@ -649,9 +649,7 @@ static int object_add_hook_selob_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obedit);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_hook_add_selob(wmOperatorType *ot)
@@ -690,9 +688,7 @@ static int object_add_hook_newob_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obedit);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_hook_add_newob(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index c5a6e38fbcb..9dc204b9083 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);
@@ -164,6 +167,7 @@ void OBJECT_OT_modifier_move_up(struct wmOperatorType *ot);
void OBJECT_OT_modifier_move_down(struct wmOperatorType *ot);
void OBJECT_OT_modifier_move_to_index(struct wmOperatorType *ot);
void OBJECT_OT_modifier_apply(struct wmOperatorType *ot);
+void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot);
void OBJECT_OT_modifier_convert(struct wmOperatorType *ot);
void OBJECT_OT_modifier_copy(struct wmOperatorType *ot);
void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot);
@@ -190,6 +194,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 +203,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 +223,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..8a736c380bf 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"
@@ -328,10 +330,8 @@ static bool object_modifier_safe_to_delete(Main *bmain,
!ED_object_iter_other(bmain, ob, false, object_has_modifier_cb, &type));
}
-static bool object_modifier_remove(Main *bmain,
- Object *ob,
- ModifierData *md,
- bool *r_sort_depsgraph)
+static bool object_modifier_remove(
+ Main *bmain, Scene *scene, Object *ob, ModifierData *md, bool *r_sort_depsgraph)
{
/* It seems on rapid delete it is possible to
* get called twice on same modifier, so make
@@ -342,11 +342,8 @@ static bool object_modifier_remove(Main *bmain,
/* special cases */
if (md->type == eModifierType_ParticleSystem) {
- ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
-
- BLI_remlink(&ob->particlesystem, psmd->psys);
- psys_free(ob, psmd->psys);
- psmd->psys = NULL;
+ object_remove_particle_system(bmain, scene, ob);
+ return true;
}
else if (md->type == eModifierType_Softbody) {
if (ob->soft) {
@@ -389,12 +386,13 @@ static bool object_modifier_remove(Main *bmain,
return 1;
}
-bool ED_object_modifier_remove(ReportList *reports, Main *bmain, Object *ob, ModifierData *md)
+bool ED_object_modifier_remove(
+ ReportList *reports, Main *bmain, Scene *scene, Object *ob, ModifierData *md)
{
bool sort_depsgraph = false;
bool ok;
- ok = object_modifier_remove(bmain, ob, md, &sort_depsgraph);
+ ok = object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph);
if (!ok) {
BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name);
@@ -407,7 +405,7 @@ bool ED_object_modifier_remove(ReportList *reports, Main *bmain, Object *ob, Mod
return 1;
}
-void ED_object_modifier_clear(Main *bmain, Object *ob)
+void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob)
{
ModifierData *md = ob->modifiers.first;
bool sort_depsgraph = false;
@@ -421,7 +419,7 @@ void ED_object_modifier_clear(Main *bmain, Object *ob)
next_md = md->next;
- object_modifier_remove(bmain, ob, md, &sort_depsgraph);
+ object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph);
md = next_md;
}
@@ -820,7 +818,8 @@ bool ED_object_modifier_apply(Main *bmain,
Scene *scene,
Object *ob,
ModifierData *md,
- int mode)
+ int mode,
+ bool keep_modifier)
{
int prev_mode;
@@ -828,12 +827,12 @@ bool ED_object_modifier_apply(Main *bmain,
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode");
return false;
}
- else if (ID_REAL_USERS(ob->data) > 1) {
+ if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) {
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
return false;
}
- else if ((ob->mode & OB_MODE_SCULPT) && (find_multires_modifier_before(scene, md)) &&
- (BKE_modifier_is_same_topology(md) == false)) {
+ if ((ob->mode & OB_MODE_SCULPT) && (find_multires_modifier_before(scene, md)) &&
+ (BKE_modifier_is_same_topology(md) == false)) {
BKE_report(reports,
RPT_ERROR,
"Constructive modifier cannot be applied to multi-res data in sculpt mode");
@@ -867,22 +866,34 @@ bool ED_object_modifier_apply(Main *bmain,
}
md_eval->mode = prev_mode;
- BLI_remlink(&ob->modifiers, md);
- BKE_modifier_free(md);
+
+ if (!keep_modifier) {
+ BLI_remlink(&ob->modifiers, md);
+ BKE_modifier_free(md);
+ }
BKE_object_free_derived_caches(ob);
return true;
}
-int ED_object_modifier_copy(ReportList *UNUSED(reports), Object *ob, ModifierData *md)
+int ED_object_modifier_copy(
+ ReportList *UNUSED(reports), Main *bmain, Scene *scene, Object *ob, ModifierData *md)
{
ModifierData *nmd;
- nmd = BKE_modifier_new(md->type);
- BKE_modifier_copydata(md, nmd);
- BLI_insertlinkafter(&ob->modifiers, md, nmd);
- BKE_modifier_unique_name(&ob->modifiers, nmd);
+ if (md->type == eModifierType_ParticleSystem) {
+ nmd = object_copy_particle_system(bmain, scene, ob, ((ParticleSystemModifierData *)md)->psys);
+ BLI_remlink(&ob->modifiers, nmd);
+ BLI_insertlinkafter(&ob->modifiers, md, nmd);
+ return true;
+ }
+ else {
+ nmd = BKE_modifier_new(md->type);
+ BKE_modifier_copydata(md, nmd);
+ BLI_insertlinkafter(&ob->modifiers, md, nmd);
+ BKE_modifier_unique_name(&ob->modifiers, nmd);
+ }
return 1;
}
@@ -1037,22 +1048,56 @@ 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;
+ }
+ 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;
}
@@ -1080,12 +1125,21 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
static int modifier_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = ED_object_active_context(C);
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, scene, ob, md)) {
return OPERATOR_CANCELLED;
}
@@ -1099,17 +1153,21 @@ 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;
}
void OBJECT_OT_modifier_remove(wmOperatorType *ot)
@@ -1125,6 +1183,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,14 +1207,13 @@ 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;
}
void OBJECT_OT_modifier_move_up(wmOperatorType *ot)
@@ -1194,14 +1252,13 @@ 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;
}
void OBJECT_OT_modifier_move_down(wmOperatorType *ot)
@@ -1246,14 +1303,13 @@ 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;
}
void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
@@ -1280,7 +1336,7 @@ void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
/** \name Apply Modifier Operator
* \{ */
-static bool modifier_apply_poll(bContext *C)
+static bool modifier_apply_poll_ex(bContext *C, bool allow_shared)
{
if (!edit_modifier_poll_generic(C, &RNA_Modifier, 0, false)) {
return false;
@@ -1291,11 +1347,15 @@ 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 ((ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) {
+ 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 (!allow_shared && (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;
}
- else if (md != NULL) {
+ if (md != NULL) {
if ((ob->mode & OB_MODE_SCULPT) && (find_multires_modifier_before(scene, md)) &&
(BKE_modifier_is_same_topology(md) == false)) {
CTX_wm_operator_poll_msg_set(
@@ -1306,16 +1366,29 @@ static bool modifier_apply_poll(bContext *C)
return true;
}
-static int modifier_apply_exec(bContext *C, wmOperator *op)
+static bool modifier_apply_poll(bContext *C)
+{
+ return modifier_apply_poll_ex(C, false);
+}
+
+static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, bool keep_modifier)
{
Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
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, keep_modifier)) {
return OPERATOR_CANCELLED;
}
@@ -1323,29 +1396,27 @@ 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_exec(bContext *C, wmOperator *op)
+{
+ return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_DATA, false);
+}
+
+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;
}
-static const EnumPropertyItem modifier_apply_as_items[] = {
- {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"},
- {MODIFIER_APPLY_SHAPE,
- "SHAPE",
- 0,
- "New Shape",
- "Apply deform-only modifier to a new shape on this object"},
- {0, NULL, 0, NULL, NULL},
-};
-
void OBJECT_OT_modifier_apply(wmOperatorType *ot)
{
ot->name = "Apply Modifier";
@@ -1359,13 +1430,70 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_enum(ot->srna,
- "apply_as",
- modifier_apply_as_items,
- MODIFIER_APPLY_DATA,
- "Apply as",
- "How to apply the modifier to the geometry");
edit_modifier_properties(ot);
+ edit_modifier_report_property(ot);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Apply Modifier As Shapekey Operator
+ * \{ */
+
+static bool modifier_apply_as_shapekey_poll(bContext *C)
+{
+ return modifier_apply_poll_ex(C, true);
+}
+
+static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op)
+{
+ bool keep = RNA_boolean_get(op->ptr, "keep_modifier");
+
+ return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_SHAPE, keep);
+}
+
+static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
+ return modifier_apply_as_shapekey_exec(C, op);
+ }
+ else {
+ return retval;
+ }
+}
+
+static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(C),
+ struct wmOperatorType *UNUSED(op),
+ struct PointerRNA *values)
+{
+ bool keep = RNA_boolean_get(values, "keep_modifier");
+
+ if (keep) {
+ return BLI_strdup("Apply modifier as a new shapekey and keep it in the stack");
+ }
+
+ return NULL;
+}
+
+void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot)
+{
+ ot->name = "Apply Modifier As Shapekey";
+ ot->description = "Apply modifier as a new shapekey and remove from the stack";
+ ot->idname = "OBJECT_OT_modifier_apply_as_shapekey";
+
+ ot->invoke = modifier_apply_as_shapekey_invoke;
+ ot->exec = modifier_apply_as_shapekey_exec;
+ ot->poll = modifier_apply_as_shapekey_poll;
+ ot->get_description = modifier_apply_as_shapekey_get_description;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+
+ RNA_def_boolean(
+ ot->srna, "keep_modifier", false, "Keep Modifier", "Do not remove the modifier from stack");
+ edit_modifier_properties(ot);
+ edit_modifier_report_property(ot);
}
/** \} */
@@ -1396,12 +1524,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_modifier_convert(wmOperatorType *ot)
@@ -1427,10 +1553,12 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot)
static int modifier_copy_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
- if (!md || !ED_object_modifier_copy(op->reports, ob, md)) {
+ if (!md || !ED_object_modifier_copy(op->reports, bmain, scene, ob, md)) {
return OPERATOR_CANCELLED;
}
@@ -1440,14 +1568,13 @@ 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;
}
void OBJECT_OT_modifier_copy(wmOperatorType *ot)
@@ -1501,12 +1628,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot)
@@ -1579,12 +1704,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_subdivide(wmOperatorType *ot)
@@ -1656,12 +1779,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_reshape(wmOperatorType *ot)
@@ -1720,7 +1841,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,12 +1958,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_base_apply(wmOperatorType *ot)
@@ -1891,12 +2010,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot)
@@ -1949,12 +2066,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot)
@@ -1999,8 +2114,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)
@@ -2246,7 +2362,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph,
BKE_object_transform_copy(arm_ob, skin_ob);
arm = arm_ob->data;
arm->layer = 1;
- arm_ob->dtx |= OB_DRAWXRAY;
+ arm_ob->dtx |= OB_DRAW_IN_FRONT;
arm->drawtype = ARM_LINE;
arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature");
@@ -2327,12 +2443,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_skin_armature_create(wmOperatorType *ot)
@@ -2406,12 +2520,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot)
@@ -2483,12 +2595,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_meshdeform_bind(wmOperatorType *ot)
@@ -2539,12 +2649,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_explode_refresh(wmOperatorType *ot)
@@ -2688,12 +2796,16 @@ static int ocean_bake_exec(bContext *C, wmOperator *op)
cfra = scene->r.cfra;
/* precalculate time variable before baking */
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
for (f = omd->bakestart; f <= omd->bakeend; f++) {
/* For now only simple animation of time value is supported, nothing else.
* No drivers or other modifier parameters. */
/* TODO(sergey): This operates on an original data, so no flush is needed. However, baking
* usually should happen on an evaluated objects, so this seems to be deeper issue here. */
- BKE_animsys_evaluate_animdata((ID *)ob, ob->adt, f, ADT_RECALC_ANIM, false);
+
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ f);
+ BKE_animsys_evaluate_animdata((ID *)ob, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, false);
och->time[i] = omd->time;
i++;
@@ -2743,12 +2855,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_ocean_bake(wmOperatorType *ot)
@@ -2822,12 +2932,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_laplaciandeform_bind(wmOperatorType *ot)
@@ -2891,12 +2999,10 @@ 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 {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 05f1ced8615..92880e5a114 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);
@@ -132,6 +130,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_modifier_move_down);
WM_operatortype_append(OBJECT_OT_modifier_move_to_index);
WM_operatortype_append(OBJECT_OT_modifier_apply);
+ WM_operatortype_append(OBJECT_OT_modifier_apply_as_shapekey);
WM_operatortype_append(OBJECT_OT_modifier_convert);
WM_operatortype_append(OBJECT_OT_modifier_copy);
WM_operatortype_append(OBJECT_OT_multires_subdivide);
@@ -152,6 +151,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 +160,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 +180,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 eed3f2ea90c..9520b03f3a6 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"
@@ -357,7 +359,7 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_enum_search_invoke(C, op, event);
return OPERATOR_CANCELLED;
}
- else if (ID_IS_LINKED(ob)) {
+ if (ID_IS_LINKED(ob)) {
uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
uiLayout *layout = UI_popup_menu_layout(pup);
@@ -372,12 +374,10 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* this invoke just calls another instance of this operator... */
return OPERATOR_INTERFACE;
}
- else {
- /* error.. cannot continue */
- BKE_report(
- op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection");
- return OPERATOR_CANCELLED;
- }
+
+ /* error.. cannot continue */
+ BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection");
+ return OPERATOR_CANCELLED;
}
static int make_proxy_exec(bContext *C, wmOperator *op)
@@ -706,36 +706,34 @@ bool ED_object_parent_set(ReportList *reports,
if (par->type != OB_CURVE) {
return 0;
}
+ Curve *cu = par->data;
+ Curve *cu_eval = parent_eval->data;
+ if ((cu->flag & CU_PATH) == 0) {
+ cu->flag |= CU_PATH | CU_FOLLOW;
+ cu_eval->flag |= CU_PATH | CU_FOLLOW;
+ /* force creation of path data */
+ BKE_displist_make_curveTypes(depsgraph, scene, par, false, false);
+ }
else {
- Curve *cu = par->data;
- Curve *cu_eval = parent_eval->data;
- if ((cu->flag & CU_PATH) == 0) {
- cu->flag |= CU_PATH | CU_FOLLOW;
- cu_eval->flag |= CU_PATH | CU_FOLLOW;
- /* force creation of path data */
- BKE_displist_make_curveTypes(depsgraph, scene, par, false, false);
- }
- else {
- cu->flag |= CU_FOLLOW;
- cu_eval->flag |= CU_FOLLOW;
- }
+ cu->flag |= CU_FOLLOW;
+ cu_eval->flag |= CU_FOLLOW;
+ }
- /* if follow, add F-Curve for ctime (i.e. "eval_time") so that path-follow works */
- if (partype == PAR_FOLLOW) {
- /* get or create F-Curve */
- bAction *act = ED_id_action_ensure(bmain, &cu->id);
- FCurve *fcu = ED_action_fcurve_ensure(bmain, act, NULL, NULL, "eval_time", 0);
+ /* if follow, add F-Curve for ctime (i.e. "eval_time") so that path-follow works */
+ if (partype == PAR_FOLLOW) {
+ /* get or create F-Curve */
+ bAction *act = ED_id_action_ensure(bmain, &cu->id);
+ FCurve *fcu = ED_action_fcurve_ensure(bmain, act, NULL, NULL, "eval_time", 0);
- /* setup dummy 'generator' modifier here to get 1-1 correspondence still working */
- if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
- add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR, fcu);
- }
+ /* setup dummy 'generator' modifier here to get 1-1 correspondence still working */
+ if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
+ add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR, fcu);
}
+ }
- /* fall back on regular parenting now (for follow only) */
- if (partype == PAR_FOLLOW) {
- partype = PAR_OBJECT;
- }
+ /* fall back on regular parenting now (for follow only) */
+ if (partype == PAR_FOLLOW) {
+ partype = PAR_OBJECT;
}
}
else if (ELEM(partype, PAR_BONE, PAR_BONE_RELATIVE)) {
@@ -753,188 +751,185 @@ bool ED_object_parent_set(ReportList *reports,
BKE_report(reports, RPT_ERROR, "Loop in parents");
return false;
}
- else {
- Object workob;
- /* apply transformation of previous parenting */
- if (keep_transform) {
- /* was removed because of bug [#23577],
- * but this can be handy in some cases too [#32616], so make optional */
- BKE_object_apply_mat4(ob, ob->obmat, false, false);
- }
+ Object workob;
- /* set the parent (except for follow-path constraint option) */
- if (partype != PAR_PATH_CONST) {
- ob->parent = par;
- /* Always clear parentinv matrix for sake of consistency, see T41950. */
- unit_m4(ob->parentinv);
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
- }
+ /* apply transformation of previous parenting */
+ if (keep_transform) {
+ /* was removed because of bug [#23577],
+ * but this can be handy in some cases too [#32616], so make optional */
+ BKE_object_apply_mat4(ob, ob->obmat, false, false);
+ }
- /* handle types */
- if (pchan) {
- BLI_strncpy(ob->parsubstr, pchan->name, sizeof(ob->parsubstr));
- }
- else {
- ob->parsubstr[0] = 0;
- }
+ /* set the parent (except for follow-path constraint option) */
+ if (partype != PAR_PATH_CONST) {
+ ob->parent = par;
+ /* Always clear parentinv matrix for sake of consistency, see T41950. */
+ unit_m4(ob->parentinv);
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+ }
- if (partype == PAR_PATH_CONST) {
- /* don't do anything here, since this is not technically "parenting" */
- }
- else if (ELEM(partype, PAR_CURVE, PAR_LATTICE) || (pararm)) {
- /* partype is now set to PAROBJECT so that invisible 'virtual'
- * modifiers don't need to be created.
- * NOTE: the old (2.4x) method was to set ob->partype = PARSKEL,
- * creating the virtual modifiers.
- */
- ob->partype = PAROBJECT; /* note, dna define, not operator property */
- /* ob->partype = PARSKEL; */ /* note, dna define, not operator property */
+ /* handle types */
+ if (pchan) {
+ BLI_strncpy(ob->parsubstr, pchan->name, sizeof(ob->parsubstr));
+ }
+ else {
+ ob->parsubstr[0] = 0;
+ }
- /* BUT, to keep the deforms, we need a modifier,
- * and then we need to set the object that it uses
- * - We need to ensure that the modifier we're adding doesn't already exist,
- * so we check this by assuming that the parent is selected too.
- */
- /* XXX currently this should only happen for meshes, curves, surfaces,
- * and lattices - this stuff isn't available for metas yet */
- if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
- ModifierData *md;
-
- switch (partype) {
- case PAR_CURVE: /* curve deform */
- if (BKE_modifiers_is_deformed_by_curve(ob) != par) {
- md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Curve);
- if (md) {
- ((CurveModifierData *)md)->object = par;
- }
- if (par->runtime.curve_cache && par->runtime.curve_cache->path == NULL) {
- DEG_id_tag_update(&par->id, ID_RECALC_GEOMETRY);
- }
+ if (partype == PAR_PATH_CONST) {
+ /* don't do anything here, since this is not technically "parenting" */
+ }
+ else if (ELEM(partype, PAR_CURVE, PAR_LATTICE) || (pararm)) {
+ /* partype is now set to PAROBJECT so that invisible 'virtual'
+ * modifiers don't need to be created.
+ * NOTE: the old (2.4x) method was to set ob->partype = PARSKEL,
+ * creating the virtual modifiers.
+ */
+ ob->partype = PAROBJECT; /* note, dna define, not operator property */
+ /* ob->partype = PARSKEL; */ /* note, dna define, not operator property */
+
+ /* BUT, to keep the deforms, we need a modifier,
+ * and then we need to set the object that it uses
+ * - We need to ensure that the modifier we're adding doesn't already exist,
+ * so we check this by assuming that the parent is selected too.
+ */
+ /* XXX currently this should only happen for meshes, curves, surfaces,
+ * and lattices - this stuff isn't available for metas yet */
+ if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
+ ModifierData *md;
+
+ switch (partype) {
+ case PAR_CURVE: /* curve deform */
+ if (BKE_modifiers_is_deformed_by_curve(ob) != par) {
+ md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Curve);
+ if (md) {
+ ((CurveModifierData *)md)->object = par;
}
- break;
- case PAR_LATTICE: /* lattice deform */
- if (BKE_modifiers_is_deformed_by_lattice(ob) != par) {
- md = ED_object_modifier_add(
- reports, bmain, scene, ob, NULL, eModifierType_Lattice);
- if (md) {
- ((LatticeModifierData *)md)->object = par;
- }
+ if (par->runtime.curve_cache && par->runtime.curve_cache->path == NULL) {
+ DEG_id_tag_update(&par->id, ID_RECALC_GEOMETRY);
}
- break;
- default: /* armature deform */
- if (BKE_modifiers_is_deformed_by_armature(ob) != par) {
- md = ED_object_modifier_add(
- reports, bmain, scene, ob, NULL, eModifierType_Armature);
- if (md) {
- ((ArmatureModifierData *)md)->object = par;
- }
+ }
+ break;
+ case PAR_LATTICE: /* lattice deform */
+ if (BKE_modifiers_is_deformed_by_lattice(ob) != par) {
+ md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Lattice);
+ if (md) {
+ ((LatticeModifierData *)md)->object = par;
}
- break;
- }
- }
- }
- else if (partype == PAR_BONE) {
- ob->partype = PARBONE; /* note, dna define, not operator property */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_RELATIVE_PARENTING;
- pchan_eval->bone->flag &= ~BONE_RELATIVE_PARENTING;
- }
- }
- else if (partype == PAR_BONE_RELATIVE) {
- ob->partype = PARBONE; /* note, dna define, not operator property */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_RELATIVE_PARENTING;
- pchan_eval->bone->flag |= BONE_RELATIVE_PARENTING;
+ }
+ break;
+ default: /* armature deform */
+ if (BKE_modifiers_is_deformed_by_armature(ob) != par) {
+ md = ED_object_modifier_add(reports, bmain, scene, ob, NULL, eModifierType_Armature);
+ if (md) {
+ ((ArmatureModifierData *)md)->object = par;
+ }
+ }
+ break;
}
}
- else if (partype == PAR_VERTEX) {
- ob->partype = PARVERT1;
- ob->par1 = vert_par[0];
- }
- else if (partype == PAR_VERTEX_TRI) {
- ob->partype = PARVERT3;
- copy_v3_v3_int(&ob->par1, vert_par);
+ }
+ else if (partype == PAR_BONE) {
+ ob->partype = PARBONE; /* note, dna define, not operator property */
+ if (pchan->bone) {
+ pchan->bone->flag &= ~BONE_RELATIVE_PARENTING;
+ pchan_eval->bone->flag &= ~BONE_RELATIVE_PARENTING;
}
- else {
- ob->partype = PAROBJECT; /* note, dna define, not operator property */
+ }
+ else if (partype == PAR_BONE_RELATIVE) {
+ ob->partype = PARBONE; /* note, dna define, not operator property */
+ if (pchan->bone) {
+ pchan->bone->flag |= BONE_RELATIVE_PARENTING;
+ pchan_eval->bone->flag |= BONE_RELATIVE_PARENTING;
}
+ }
+ else if (partype == PAR_VERTEX) {
+ ob->partype = PARVERT1;
+ ob->par1 = vert_par[0];
+ }
+ else if (partype == PAR_VERTEX_TRI) {
+ ob->partype = PARVERT3;
+ copy_v3_v3_int(&ob->par1, vert_par);
+ }
+ else {
+ ob->partype = PAROBJECT; /* note, dna define, not operator property */
+ }
- /* constraint */
- if (partype == PAR_PATH_CONST) {
- bConstraint *con;
- bFollowPathConstraint *data;
- float cmat[4][4], vec[3];
+ /* constraint */
+ if (partype == PAR_PATH_CONST) {
+ bConstraint *con;
+ bFollowPathConstraint *data;
+ float cmat[4][4], vec[3];
- con = BKE_constraint_add_for_object(ob, "AutoPath", CONSTRAINT_TYPE_FOLLOWPATH);
+ con = BKE_constraint_add_for_object(ob, "AutoPath", CONSTRAINT_TYPE_FOLLOWPATH);
- data = con->data;
- data->tar = par;
+ data = con->data;
+ data->tar = par;
- BKE_constraint_target_matrix_get(
- depsgraph, scene, con, 0, CONSTRAINT_OBTYPE_OBJECT, NULL, cmat, scene->r.cfra);
- sub_v3_v3v3(vec, ob->obmat[3], cmat[3]);
+ BKE_constraint_target_matrix_get(
+ depsgraph, scene, con, 0, CONSTRAINT_OBTYPE_OBJECT, NULL, cmat, scene->r.cfra);
+ sub_v3_v3v3(vec, ob->obmat[3], cmat[3]);
- copy_v3_v3(ob->loc, vec);
+ copy_v3_v3(ob->loc, vec);
+ }
+ else if (pararm && (ob->type == OB_MESH) && (par->type == OB_ARMATURE)) {
+ if (partype == PAR_ARMATURE_NAME) {
+ ED_object_vgroup_calc_from_armature(
+ reports, depsgraph, scene, ob, par, ARM_GROUPS_NAME, false);
}
- else if (pararm && (ob->type == OB_MESH) && (par->type == OB_ARMATURE)) {
- if (partype == PAR_ARMATURE_NAME) {
- ED_object_vgroup_calc_from_armature(
- reports, depsgraph, scene, ob, par, ARM_GROUPS_NAME, false);
- }
- else if (partype == PAR_ARMATURE_ENVELOPE) {
- ED_object_vgroup_calc_from_armature(
- reports, depsgraph, scene, ob, par, ARM_GROUPS_ENVELOPE, xmirror);
- }
- else if (partype == PAR_ARMATURE_AUTO) {
- WM_cursor_wait(1);
- ED_object_vgroup_calc_from_armature(
- reports, depsgraph, scene, ob, par, ARM_GROUPS_AUTO, xmirror);
- WM_cursor_wait(0);
- }
- /* get corrected inverse */
- ob->partype = PAROBJECT;
- BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
-
- invert_m4_m4(ob->parentinv, workob.obmat);
+ else if (partype == PAR_ARMATURE_ENVELOPE) {
+ ED_object_vgroup_calc_from_armature(
+ reports, depsgraph, scene, ob, par, ARM_GROUPS_ENVELOPE, xmirror);
}
- else if (pararm && (ob->type == OB_GPENCIL) && (par->type == OB_ARMATURE)) {
- if (partype == PAR_ARMATURE) {
- ED_gpencil_add_armature(C, reports, ob, par);
- }
- else if (partype == PAR_ARMATURE_NAME) {
- ED_gpencil_add_armature_weights(C, reports, ob, par, GP_PAR_ARMATURE_NAME);
- }
- else if ((partype == PAR_ARMATURE_AUTO) || (partype == PAR_ARMATURE_ENVELOPE)) {
- WM_cursor_wait(1);
- ED_gpencil_add_armature_weights(C, reports, ob, par, GP_PAR_ARMATURE_AUTO);
- WM_cursor_wait(0);
- }
- /* get corrected inverse */
- ob->partype = PAROBJECT;
- BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
-
- invert_m4_m4(ob->parentinv, workob.obmat);
+ else if (partype == PAR_ARMATURE_AUTO) {
+ WM_cursor_wait(1);
+ ED_object_vgroup_calc_from_armature(
+ reports, depsgraph, scene, ob, par, ARM_GROUPS_AUTO, xmirror);
+ WM_cursor_wait(0);
}
- else if ((ob->type == OB_GPENCIL) && (par->type == OB_LATTICE)) {
- /* Add Lattice modifier */
- if (partype == PAR_LATTICE) {
- ED_gpencil_add_lattice_modifier(C, reports, ob, par);
- }
- /* get corrected inverse */
- ob->partype = PAROBJECT;
- BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
+ /* get corrected inverse */
+ ob->partype = PAROBJECT;
+ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
- invert_m4_m4(ob->parentinv, workob.obmat);
+ invert_m4_m4(ob->parentinv, workob.obmat);
+ }
+ else if (pararm && (ob->type == OB_GPENCIL) && (par->type == OB_ARMATURE)) {
+ if (partype == PAR_ARMATURE) {
+ ED_gpencil_add_armature(C, reports, ob, par);
}
- else {
- /* calculate inverse parent matrix */
- BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
- invert_m4_m4(ob->parentinv, workob.obmat);
+ else if (partype == PAR_ARMATURE_NAME) {
+ ED_gpencil_add_armature_weights(C, reports, ob, par, GP_PAR_ARMATURE_NAME);
+ }
+ else if ((partype == PAR_ARMATURE_AUTO) || (partype == PAR_ARMATURE_ENVELOPE)) {
+ WM_cursor_wait(1);
+ ED_gpencil_add_armature_weights(C, reports, ob, par, GP_PAR_ARMATURE_AUTO);
+ WM_cursor_wait(0);
+ }
+ /* get corrected inverse */
+ ob->partype = PAROBJECT;
+ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
+
+ invert_m4_m4(ob->parentinv, workob.obmat);
+ }
+ else if ((ob->type == OB_GPENCIL) && (par->type == OB_LATTICE)) {
+ /* Add Lattice modifier */
+ if (partype == PAR_LATTICE) {
+ ED_gpencil_add_lattice_modifier(C, reports, ob, par);
}
+ /* get corrected inverse */
+ ob->partype = PAROBJECT;
+ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ invert_m4_m4(ob->parentinv, workob.obmat);
}
+ else {
+ /* calculate inverse parent matrix */
+ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
+ invert_m4_m4(ob->parentinv, workob.obmat);
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
return true;
@@ -1122,9 +1117,7 @@ static bool parent_set_poll_property(const bContext *UNUSED(C),
if (ELEM(type, PAR_ARMATURE_ENVELOPE, PAR_ARMATURE_AUTO)) {
return true;
}
- else {
- return false;
- }
+ return false;
}
return true;
@@ -1771,7 +1764,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. */
@@ -1882,7 +1878,7 @@ static void single_obdata_users(
/* Needed to remap texcomesh below. */
me = ob->data = ID_NEW_SET(ob->data, BKE_mesh_copy(bmain, ob->data));
if (me->key) { /* We do not need to set me->key->id.newid here... */
- BKE_animdata_copy_id_action(bmain, (ID *)me->key, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)me->key);
}
break;
case OB_MBALL:
@@ -1895,13 +1891,13 @@ static void single_obdata_users(
ID_NEW_REMAP(cu->bevobj);
ID_NEW_REMAP(cu->taperobj);
if (cu->key) { /* We do not need to set cu->key->id.newid here... */
- BKE_animdata_copy_id_action(bmain, (ID *)cu->key, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)cu->key);
}
break;
case OB_LATTICE:
ob->data = lat = ID_NEW_SET(ob->data, BKE_lattice_copy(bmain, ob->data));
if (lat->key) { /* We do not need to set lat->key->id.newid here... */
- BKE_animdata_copy_id_action(bmain, (ID *)lat->key, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)lat->key);
}
break;
case OB_ARMATURE:
@@ -1941,7 +1937,7 @@ static void single_obdata_users(
* AnimData structure, which is not what we want.
* (sergey)
*/
- BKE_animdata_copy_id_action(bmain, (ID *)ob->data, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)ob->data);
id_us_min(id);
}
@@ -1962,7 +1958,7 @@ static void single_object_action_users(
FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) {
if (!ID_IS_LINKED(ob)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- BKE_animdata_copy_id_action(bmain, &ob->id, false);
+ BKE_animdata_copy_id_action(bmain, &ob->id);
}
}
FOREACH_OBJECT_FLAG_END;
@@ -1984,7 +1980,7 @@ static void single_mat_users(
if (ma->id.us > 1) {
man = BKE_material_copy(bmain, ma);
- BKE_animdata_copy_id_action(bmain, &man->id, false);
+ BKE_animdata_copy_id_action(bmain, &man->id);
man->id.us = 0;
BKE_object_material_assign(bmain, ob, man, a, BKE_MAT_ASSIGN_USERPREF);
@@ -2246,52 +2242,22 @@ 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_library_ovject_overridable_check(Main *bmain, Object *object)
{
- if (ob == obact) {
- return;
- }
-
- if (!ID_IS_LINKED(ob)) {
- return;
- }
-
- /* 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... */
-
- 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;
- }
- }
+ /* An object is actually overrideable only if it is in at least one local collections.
+ * Unfortunately 'direct link' flag is not enough here. */
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (!ID_IS_LINKED(collection) && BKE_collection_has_object(collection, object)) {
+ return true;
}
}
- else if (ob->parent == obact) {
- ob->id.tag |= LIB_TAG_DOIT;
- }
-
- if (ob->id.tag & LIB_TAG_DOIT) {
- printf("Indirectly overriding %s for %s\n", ob->id.name, obact->id.name);
- }
-}
-
-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);
- }
+ return false;
}
/* Set the object to override. */
static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *obact = ED_object_active_context(C);
@@ -2300,14 +2266,9 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve
return OPERATOR_CANCELLED;
}
- /* Get object to work on - use a menu if we need to... */
- if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
- ID_IS_LINKED(obact->instance_collection)) {
- /* Gives menu with list of objects in group. */
- WM_enum_search_invoke(C, op, event);
- return OPERATOR_CANCELLED;
- }
- else if (ID_IS_LINKED(obact)) {
+ if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) ||
+ make_override_library_ovject_overridable_check(bmain, obact)) {
uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
uiLayout *layout = UI_popup_menu_layout(pup);
@@ -2322,21 +2283,27 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve
/* This invoke just calls another instance of this operator... */
return OPERATOR_INTERFACE;
}
- else {
- /* Error.. cannot continue. */
- BKE_report(op->reports,
- RPT_ERROR,
- "Can only make library override for a referenced object or collection");
+ else if (ID_IS_LINKED(obact)) {
+ /* Show menu with list of directly linked collections containing the active object. */
+ WM_enum_search_invoke(C, op, event);
return OPERATOR_CANCELLED;
}
+
+ /* Error.. cannot continue. */
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Can only make library override for a referenced object or collection");
+ return OPERATOR_CANCELLED;
}
static int make_override_library_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obact = CTX_data_active_object(C);
-
- bool success = false;
+ ID *id_root = NULL;
+ bool is_override_instancing_object = false;
if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
ID_IS_LINKED(obact->instance_collection)) {
@@ -2348,115 +2315,50 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-
- Object *obcollection = obact;
- Collection *collection = obcollection->instance_collection;
-
- 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;
-
- /* First, we make a library override of the linked collection itself, and all its children. */
- make_override_library_tag_collections(collection);
-
- /* 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;
- }
- }
- }
+ id_root = &obact->instance_collection->id;
+ is_override_instancing_object = true;
+ }
+ else if (!make_override_library_ovject_overridable_check(bmain, obact)) {
+ const int i = RNA_property_enum_get(op->ptr, op->type->prop);
+ const uint collection_session_uuid = *((uint *)&i);
+ if (collection_session_uuid == MAIN_ID_SESSION_UUID_UNSET) {
+ BKE_reportf(op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Active object '%s' is not overridable",
+ obact->id.name + 2);
+ return OPERATOR_CANCELLED;
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
-
- success = BKE_lib_override_library_create_from_tag(bmain);
- /* Instantiate our newly overridden objects in scene, if not yet done. */
- 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);
-
- 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 (new_ob == (Object *)obact->id.newid) {
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
- }
- else {
- /* Disable auto-override tags for non-active objects, will help with performaces... */
- new_ob->id.override_library->flag &= ~OVERRIDE_LIBRARY_AUTO;
- }
- /* 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);
- }
+ Collection *collection = BLI_listbase_bytes_find(&bmain->collections,
+ &collection_session_uuid,
+ sizeof(collection_session_uuid),
+ offsetof(ID, session_uuid));
+ if (!ID_IS_OVERRIDABLE_LIBRARY(collection)) {
+ BKE_reportf(op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Could not find an overridable collection containing object '%s'",
+ obact->id.name + 2);
+ return OPERATOR_CANCELLED;
}
- 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;
+ id_root = &collection->id;
}
/* 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);
- }
+ else {
+ id_root = &obact->id;
+ }
- success = BKE_lib_override_library_create_from_tag(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- /* Also, we'd likely want to lock by default things like
- * transformations of implicitly overridden objects? */
+ const bool success = BKE_lib_override_library_create(
+ bmain, scene, view_layer, id_root, &obact->id);
- /* 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);
+ /* Remove the instance empty from this scene, the items now have an overridden collection
+ * instead. */
+ if (success && is_override_instancing_object) {
+ ED_object_base_free_and_unlink(bmain, scene, obact);
}
+ 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;
@@ -2467,10 +2369,40 @@ static bool make_override_library_poll(bContext *C)
Object *obact = CTX_data_active_object(C);
/* Object must be directly linked to be overridable. */
- return (BKE_lib_override_library_is_enabled() && ED_operator_objectmode(C) && obact != NULL &&
- ((ID_IS_LINKED(obact) && obact->id.tag & LIB_TAG_EXTERN) ||
- (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
- ID_IS_LINKED(obact->instance_collection))));
+ return (ED_operator_objectmode(C) && obact != NULL &&
+ (ID_IS_LINKED(obact) || (obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection))));
+}
+
+static const EnumPropertyItem *make_override_collections_of_linked_object_itemf(
+ bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ EnumPropertyItem item_tmp = {0}, *item = NULL;
+ int totitem = 0;
+
+ Object *object = ED_object_active_context(C);
+ Main *bmain = CTX_data_main(C);
+
+ if (!object || !ID_IS_LINKED(object)) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ /* Only check for directly linked collections. */
+ if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0) {
+ continue;
+ }
+ if (BKE_collection_has_object_recursive(collection, object)) {
+ item_tmp.identifier = item_tmp.name = collection->id.name + 2;
+ item_tmp.value = *((int *)&collection->id.session_uuid);
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
}
void OBJECT_OT_make_override_library(wmOperatorType *ot)
@@ -2491,12 +2423,13 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_enum(ot->srna,
- "object",
+ "collection",
DummyRNA_DEFAULT_items,
- 0,
- "Override Object",
- "Name of lib-linked/collection object to make an override from");
- RNA_def_enum_funcs(prop, proxy_collection_object_itemf);
+ MAIN_ID_SESSION_UUID_UNSET,
+ "Override Collection",
+ "Name of directly linked collection containing the selected object, to make "
+ "an override from");
+ RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
}
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 1d7920b9991..28f58a34814 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -72,7 +72,6 @@
#include "RNA_define.h"
#include "RNA_enum_types.h"
-#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
@@ -100,7 +99,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 +178,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) {
@@ -393,6 +402,7 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve
mesh->remesh_voxel_size = cd->voxel_size;
MEM_freeN(op->customdata);
ED_region_tag_redraw(ar);
+ ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
}
@@ -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);
@@ -1027,7 +1041,7 @@ static bool quadriflow_poll_property(const bContext *C, wmOperator *op, const Pr
if (STREQ(prop_id, "target_edge_length") && mode != QUADRIFLOW_REMESH_EDGE_LENGTH) {
return false;
}
- else if (STREQ(prop_id, "target_faces")) {
+ if (STREQ(prop_id, "target_faces")) {
if (mode != QUADRIFLOW_REMESH_FACES) {
/* Make sure we can edit the target_faces value even if it doesn't start as EDITABLE */
float area = RNA_float_get(op->ptr, "mesh_area");
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index 5f9799710dc..e20de5a92ff 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -218,13 +218,9 @@ static int get_base_select_priority(Base *base)
if (base->flag & BASE_SELECTABLE) {
return 3;
}
- else {
- return 2;
- }
- }
- else {
- return 1;
+ return 2;
}
+ return 1;
}
/**
@@ -251,13 +247,12 @@ Base *ED_object_find_first_by_data_id(ViewLayer *view_layer, ID *id)
if (base->flag & BASE_SELECTED) {
return base;
}
- else {
- int priority_test = get_base_select_priority(base);
- if (priority_test > priority_best) {
- priority_best = priority_test;
- base_best = base;
- }
+ int priority_test = get_base_select_priority(base);
+
+ if (priority_test > priority_best) {
+ priority_best = priority_test;
+ base_best = base;
}
}
}
@@ -671,7 +666,7 @@ static int object_select_linked_exec(bContext *C, wmOperator *op)
// object_select_all_by_ipo(C, ob->ipo)
return OPERATOR_CANCELLED;
}
- else if (nr == OBJECT_SELECT_LINKED_OBDATA) {
+ if (nr == OBJECT_SELECT_LINKED_OBDATA) {
if (ob->data == NULL) {
return OPERATOR_CANCELLED;
}
@@ -855,7 +850,7 @@ static bool select_grouped_collection(bContext *C, Object *ob)
if (!collection_count) {
return 0;
}
- else if (collection_count == 1) {
+ if (collection_count == 1) {
collection = ob_collections[0];
CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
@@ -998,7 +993,7 @@ static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob), ReportList
BKE_report(reports, RPT_ERROR, "No active Keying Set to use");
return false;
}
- else if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
+ if (ANIM_validate_keyingset(C, NULL, ks) != 0) {
if (ks->paths.first == NULL) {
if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) {
BKE_report(reports,
@@ -1160,14 +1155,12 @@ static int object_select_all_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else if (any_visible == false) {
+ if (any_visible == false) {
/* TODO(campbell): Looks like we could remove this,
* if not comment should say why its needed. */
return OPERATOR_PASS_THROUGH;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_select_all(wmOperatorType *ot)
@@ -1386,9 +1379,7 @@ static int object_select_more_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_select_more(wmOperatorType *ot)
@@ -1419,9 +1410,7 @@ static int object_select_less_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_select_less(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index 5db4a5a4f57..977d4abd4d4 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -24,6 +24,7 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "MEM_guardedalloc.h"
@@ -55,6 +56,8 @@
#include "ED_object.h"
#include "ED_screen.h"
+#include "UI_interface.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -200,6 +203,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)
@@ -213,7 +250,7 @@ static int shaderfx_add_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
@@ -326,22 +363,57 @@ static void edit_shaderfx_properties(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-static int edit_shaderfx_invoke_properties(bContext *C, wmOperator *op)
+static void edit_shaderfx_report_property(wmOperatorType *ot)
{
- ShaderFxData *fx;
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "report", false, "Report", "Create a notification after the operation");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+/**
+ * \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 edit_shaderfx_invoke_properties(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
+{
if (RNA_struct_property_is_set(op->ptr, "shaderfx")) {
return true;
}
- else {
- PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx);
- if (ptr.data) {
- fx = ptr.data;
- RNA_string_set(op->ptr, "shaderfx", fx->name);
- return true;
+
+ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx);
+ if (ctx_ptr.data != NULL) {
+ ShaderFxData *fx = ctx_ptr.data;
+ RNA_string_set(op->ptr, "shaderfx", fx->name);
+ return true;
+ }
+
+ /* Check the custom data of panels under the mouse for an effect. */
+ 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_ShaderFx)) {
+ ShaderFxData *fx = panel_ptr->data;
+ RNA_string_set(op->ptr, "shaderfx", fx->name);
+ return true;
+ }
+
+ 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;
}
@@ -370,29 +442,36 @@ static int shaderfx_remove_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, fx->name);
+
if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) {
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 effect: %s", name);
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_remove_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return retval;
}
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;
@@ -402,6 +481,7 @@ void OBJECT_OT_shaderfx_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
+ edit_shaderfx_report_property(ot);
}
/************************ move up shaderfx operator *********************/
@@ -416,25 +496,24 @@ static int shaderfx_move_up_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_up_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return retval;
}
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;
@@ -458,25 +537,24 @@ static int shaderfx_move_down_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_down_exec(C, op);
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return retval;
}
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 +565,54 @@ 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_SHADERFX, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
+ return shaderfx_move_to_index_exec(C, op);
+ }
+ return retval;
+}
+
+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..52e06f46149 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)
@@ -307,9 +305,7 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_shape_key_remove(wmOperatorType *ot)
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 132b530455e..f2d7ad3ac11 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -1002,10 +1002,8 @@ static int object_transform_apply_exec(bContext *C, wmOperator *op)
if (loc || rot || sca) {
return apply_objects_internal(C, op->reports, loc, rot, sca, do_props);
}
- else {
- /* allow for redo */
- return OPERATOR_FINISHED;
- }
+ /* allow for redo */
+ return OPERATOR_FINISHED;
}
void OBJECT_OT_transform_apply(wmOperatorType *ot)
@@ -1098,15 +1096,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 +1104,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 +1209,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 +1229,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 +1322,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 +1349,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);
@@ -2036,7 +2034,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
object_transform_axis_target_free_data(op);
return OPERATOR_FINISHED;
}
- else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
+ if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
object_transform_axis_target_cancel(C, op);
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c
index 00aafc2120f..fbbfde1ebef 100644
--- a/source/blender/editors/object/object_utils.c
+++ b/source/blender/editors/object/object_utils.c
@@ -131,20 +131,18 @@ bool ED_object_calc_active_center(Object *ob, const bool select_only, float r_ce
}
return false;
}
- else if (ob->mode & OB_MODE_POSE) {
+ if (ob->mode & OB_MODE_POSE) {
if (ED_object_calc_active_center_for_posemode(ob, select_only, r_center)) {
mul_m4_v3(ob->obmat, r_center);
return true;
}
return false;
}
- else {
- if (!select_only || (ob->base_flag & BASE_SELECTED)) {
- copy_v3_v3(r_center, ob->obmat[3]);
- return true;
- }
- return false;
+ if (!select_only || (ob->base_flag & BASE_SELECTED)) {
+ copy_v3_v3(r_center, ob->obmat[3]);
+ return true;
}
+ return false;
}
/** \} */
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index fb79cfb910e..253287c382e 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -77,19 +77,20 @@
#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) {
return true;
}
- else if ((ob->type == OB_MESH) &&
- ((Mesh *)ob->data)->editflag & (ME_EDIT_PAINT_VERT_SEL | ME_EDIT_PAINT_FACE_SEL)) {
+ if ((ob->type == OB_MESH) &&
+ ((Mesh *)ob->data)->editflag & (ME_EDIT_PAINT_VERT_SEL | ME_EDIT_PAINT_FACE_SEL)) {
return true;
}
- else {
- return false;
- }
+ return false;
}
static Lattice *vgroup_edit_lattice(Object *ob)
@@ -186,7 +187,7 @@ bool ED_vgroup_parray_alloc(ID *id,
return true;
}
- else if (me->dvert) {
+ if (me->dvert) {
MVert *mvert = me->mvert;
MDeformVert *dvert = me->dvert;
int i;
@@ -682,7 +683,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 +784,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 +989,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)
@@ -1752,12 +1769,14 @@ static void vgroup_lock_all(Object *ob, int action, int mask)
switch (mask) {
case VGROUP_MASK_INVERT_UNSELECTED:
case VGROUP_MASK_SELECTED:
- if (!selected[i])
+ if (!selected[i]) {
continue;
+ }
break;
case VGROUP_MASK_UNSELECTED:
- if (selected[i])
+ if (selected[i]) {
continue;
+ }
break;
default:;
}
@@ -1772,12 +1791,14 @@ static void vgroup_lock_all(Object *ob, int action, int mask)
for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) {
switch (mask) {
case VGROUP_MASK_SELECTED:
- if (!selected[i])
+ if (!selected[i]) {
continue;
+ }
break;
case VGROUP_MASK_UNSELECTED:
- if (selected[i])
+ if (selected[i]) {
continue;
+ }
break;
default:;
}
@@ -2096,15 +2117,13 @@ static int inv_cmp_mdef_vert_weights(const void *a1, const void *a2)
if (dw1->weight < dw2->weight) {
return 1;
}
- else if (dw1->weight > dw2->weight) {
+ if (dw1->weight > dw2->weight) {
return -1;
}
- else if (&dw1 < &dw2) {
+ if (&dw1 < &dw2) {
return 1; /* compare address for stable sort algorithm */
}
- else {
- return -1;
- }
+ return -1;
}
/* Used for limiting the number of influencing bones per vertex when exporting
@@ -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;
}
@@ -2700,23 +2719,17 @@ static bool vertex_group_vert_poll_ex(bContext *C,
if (BKE_object_is_in_editmode_vgroup(ob)) {
return true;
}
- else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
+ if (ob->mode & OB_MODE_WEIGHT_PAINT) {
if (needs_select) {
if (BKE_object_is_in_wpaint_select_vert(ob)) {
return true;
}
- else {
- CTX_wm_operator_poll_msg_set(C, "Vertex select needs to be enabled in weight paint mode");
- return false;
- }
- }
- else {
- return true;
+ CTX_wm_operator_poll_msg_set(C, "Vertex select needs to be enabled in weight paint mode");
+ return false;
}
+ return true;
}
- else {
- return false;
- }
+ return false;
}
#if 0
@@ -2747,14 +2760,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 +2775,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 +2828,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 +2881,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 +2917,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 +2952,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");
@@ -2964,10 +3005,16 @@ void OBJECT_OT_vertex_group_remove_from(wmOperatorType *ot)
/* properties */
prop = RNA_def_boolean(ot->srna, "use_all_groups", 0, "All Groups", "Remove from all groups");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "use_all_verts", 0, "All Verts", "Clear the active group");
+ prop = RNA_def_boolean(ot->srna, "use_all_verts", 0, "All Vertices", "Clear the active group");
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 +3045,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 +3090,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 +3133,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 */
@@ -3110,11 +3175,15 @@ static int vertex_group_normalize_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize Operator
+ * \{ */
+
void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot)
{
/* identifiers */
@@ -3152,12 +3221,17 @@ static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- /* allow to adjust settings */
- return OPERATOR_FINISHED;
- }
+
+ /* allow to adjust settings */
+ return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Vertex Group Normalize All Operator
+ * \{ */
+
void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot)
{
/* identifiers */
@@ -3182,6 +3256,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 +3339,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 +3447,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);
@@ -3402,14 +3494,20 @@ void OBJECT_OT_vertex_group_invert(wmOperatorType *ot)
"auto_assign",
true,
"Add Weights",
- "Add verts from groups that have zero weight before inverting");
+ "Add vertices from groups that have zero weight before inverting");
RNA_def_boolean(ot->srna,
"auto_remove",
true,
"Remove Weights",
- "Remove verts from groups that have zero weight after inverting");
+ "Remove vertices 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 +3580,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 +3639,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 +3684,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);
@@ -3598,11 +3714,10 @@ static int vertex_group_limit_total_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- /* note, would normally return canceled, except we want the redo
- * UI to show up for users to change */
- return OPERATOR_FINISHED;
- }
+
+ /* note, would normally return canceled, except we want the redo
+ * UI to show up for users to change */
+ return OPERATOR_FINISHED;
}
void OBJECT_OT_vertex_group_limit_total(wmOperatorType *ot)
@@ -3625,6 +3740,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 +3798,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 +3847,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 +3901,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 +3975,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)
@@ -3964,8 +4109,6 @@ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase)
}
}
}
-
- return;
}
enum {
@@ -4030,6 +4173,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 +4238,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;
@@ -4197,7 +4352,7 @@ void OBJECT_OT_vertex_weight_paste(wmOperatorType *ot)
ot->name = "Paste Weight to Selected";
ot->idname = "OBJECT_OT_vertex_weight_paste";
ot->description =
- "Copy this group's weight to other selected verts (disabled if vertex group is locked)";
+ "Copy this group's weight to other selected vertices (disabled if vertex group is locked)";
/* api callbacks */
ot->poll = vertex_group_vert_select_mesh_poll;
@@ -4218,6 +4373,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 +4423,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 +4470,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);
@@ -4318,9 +4491,7 @@ static int vertex_weight_normalize_active_vertex_exec(bContext *C, wmOperator *U
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void OBJECT_OT_vertex_weight_normalize_active_vertex(wmOperatorType *ot)
@@ -4338,6 +4509,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 +4543,5 @@ void OBJECT_OT_vertex_weight_copy(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+/** \} */
diff --git a/source/blender/editors/object/object_volume.c b/source/blender/editors/object/object_volume.c
index 4cdbbea492b..c5dc7f9f24d 100644
--- a/source/blender/editors/object/object_volume.c
+++ b/source/blender/editors/object/object_volume.c
@@ -119,7 +119,7 @@ static int volume_import_exec(bContext *C, wmOperator *op)
BKE_id_delete(bmain, &volume->id);
continue;
}
- else if (BKE_volume_is_points_only(volume)) {
+ if (BKE_volume_is_points_only(volume)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Volume \"%s\" contains points, only voxel grids are supported",
diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c
index 6922a03b12f..381bf317bee 100644
--- a/source/blender/editors/physics/dynamicpaint_ops.c
+++ b/source/blender/editors/physics/dynamicpaint_ops.c
@@ -102,7 +102,7 @@ void DPAINT_OT_surface_slot_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = surface_slot_add_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -151,7 +151,7 @@ void DPAINT_OT_surface_slot_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = surface_slot_remove_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -203,7 +203,7 @@ void DPAINT_OT_type_toggle(wmOperatorType *ot)
/* api callbacks */
ot->exec = type_toggle_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -286,7 +286,7 @@ void DPAINT_OT_output_toggle(wmOperatorType *ot)
/* api callbacks */
ot->exec = output_toggle_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -538,5 +538,5 @@ void DPAINT_OT_bake(wmOperatorType *ot)
/* api callbacks */
ot->exec = dynamicpaint_bake_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
}
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index ef5ed806c1e..749c6cd640e 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -216,11 +216,9 @@ PTCacheEdit *PE_get_current_from_psys(ParticleSystem *psys)
if ((psys->flag & PSYS_HAIR_DYNAMICS) != 0 && (psys->pointcache->flag & PTCACHE_BAKED) != 0) {
return psys->pointcache->edit;
}
- else {
- return psys->edit;
- }
+ return psys->edit;
}
- else if (psys->pointcache->flag & PTCACHE_BAKED) {
+ if (psys->pointcache->flag & PTCACHE_BAKED) {
return psys->pointcache->edit;
}
return NULL;
@@ -561,9 +559,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre
if (win[2] - 0.00001f > depth) {
return 0;
}
- else {
- return 1;
- }
+ return 1;
}
static bool key_inside_circle(const PEData *data, float rad, const float co[3], float *distance)
@@ -618,9 +614,7 @@ static bool key_inside_test(PEData *data, const float co[3])
if (data->mval) {
return key_inside_circle(data, data->rad, co, NULL);
}
- else {
- return key_inside_rect(data, co);
- }
+ return key_inside_rect(data, co);
}
static bool point_is_selected(PTCacheEditPoint *point)
@@ -3372,9 +3366,7 @@ static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagg
PE_mirror_particle(ob, psmd_eval->mesh_final, psys, pa, NULL);
continue;
}
- else {
- point->flag |= PEP_TAG;
- }
+ point->flag |= PEP_TAG;
}
}
@@ -4103,7 +4095,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/particle_object.c b/source/blender/editors/physics/particle_object.c
index e75169a476b..41e30adf724 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -104,7 +104,7 @@ void OBJECT_OT_particle_system_add(wmOperatorType *ot)
ot->description = "Add a particle system";
/* api callbacks */
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = particle_system_add_exec;
/* flags */
@@ -151,7 +151,7 @@ void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
ot->description = "Remove the selected particle system";
/* api callbacks */
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = particle_system_remove_exec;
/* flags */
@@ -1210,7 +1210,7 @@ static bool copy_particle_systems_to_object(const bContext *C,
static bool copy_particle_systems_poll(bContext *C)
{
Object *ob;
- if (!ED_operator_object_active_editable(C)) {
+ if (!ED_operator_object_active_local_editable(C)) {
return false;
}
@@ -1311,7 +1311,7 @@ void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot)
static bool duplicate_particle_systems_poll(bContext *C)
{
- if (!ED_operator_object_active_editable(C)) {
+ if (!ED_operator_object_active_local_editable(C)) {
return false;
}
Object *ob = ED_object_active_context(C);
diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c
index 8524870c15e..26b5f7fb2af 100644
--- a/source/blender/editors/physics/physics_fluid.c
+++ b/source/blender/editors/physics/physics_fluid.c
@@ -93,7 +93,7 @@ typedef struct FluidJob {
Depsgraph *depsgraph;
Object *ob;
- FluidModifierData *mmd;
+ FluidModifierData *fmd;
int success;
double start;
@@ -153,17 +153,17 @@ static inline bool fluid_is_free_guiding(FluidJob *job)
static bool fluid_initjob(
bContext *C, FluidJob *job, wmOperator *op, char *error_msg, int error_size)
{
- FluidModifierData *mmd = NULL;
- FluidDomainSettings *mds;
+ FluidModifierData *fmd = NULL;
+ FluidDomainSettings *fds;
Object *ob = ED_object_active_context(C);
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
- if (!mmd) {
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ if (!fmd) {
BLI_strncpy(error_msg, N_("Bake failed: no Fluid modifier found"), error_size);
return false;
}
- mds = mmd->domain;
- if (!mds) {
+ fds = fmd->domain;
+ if (!fds) {
BLI_strncpy(error_msg, N_("Bake failed: invalid domain"), error_size);
return false;
}
@@ -172,7 +172,7 @@ static bool fluid_initjob(
job->scene = CTX_data_scene(C);
job->depsgraph = CTX_data_depsgraph_pointer(C);
job->ob = ob;
- job->mmd = mmd;
+ job->fmd = fmd;
job->type = op->type->idname;
job->name = op->type->name;
@@ -181,7 +181,7 @@ static bool fluid_initjob(
static bool fluid_validatepaths(FluidJob *job, ReportList *reports)
{
- FluidDomainSettings *mds = job->mmd->domain;
+ FluidDomainSettings *fds = job->fmd->domain;
char temp_dir[FILE_MAX];
temp_dir[0] = '\0';
bool is_relative = false;
@@ -189,17 +189,17 @@ static bool fluid_validatepaths(FluidJob *job, ReportList *reports)
const char *relbase = BKE_modifier_path_relbase(job->bmain, job->ob);
/* We do not accept empty paths, they can end in random places silently, see T51176. */
- if (mds->cache_directory[0] == '\0') {
+ if (fds->cache_directory[0] == '\0') {
char cache_name[64];
BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
- BKE_modifier_path_init(mds->cache_directory, sizeof(mds->cache_directory), cache_name);
+ BKE_modifier_path_init(fds->cache_directory, sizeof(fds->cache_directory), cache_name);
BKE_reportf(reports,
RPT_WARNING,
"Fluid: Empty cache path, reset to default '%s'",
- mds->cache_directory);
+ fds->cache_directory);
}
- BLI_strncpy(temp_dir, mds->cache_directory, FILE_MAXDIR);
+ BLI_strncpy(temp_dir, fds->cache_directory, FILE_MAXDIR);
is_relative = BLI_path_abs(temp_dir, relbase);
/* Ensure whole path exists */
@@ -210,13 +210,13 @@ static bool fluid_validatepaths(FluidJob *job, ReportList *reports)
if (!dir_exists) {
char cache_name[64];
BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
- BKE_modifier_path_init(mds->cache_directory, sizeof(mds->cache_directory), cache_name);
+ BKE_modifier_path_init(fds->cache_directory, sizeof(fds->cache_directory), cache_name);
BKE_reportf(reports,
RPT_ERROR,
"Fluid: Could not create cache directory '%s', reset to default '%s'",
temp_dir,
- mds->cache_directory);
+ fds->cache_directory);
/* Ensure whole path exists and is writable. */
if (!BLI_dir_create_recursive(temp_dir)) {
@@ -228,7 +228,7 @@ static bool fluid_validatepaths(FluidJob *job, ReportList *reports)
return false;
}
/* Copy final dir back into domain settings */
- BLI_strncpy(mds->cache_directory, temp_dir, FILE_MAXDIR);
+ BLI_strncpy(fds->cache_directory, temp_dir, FILE_MAXDIR);
return false;
}
@@ -239,7 +239,7 @@ static bool fluid_validatepaths(FluidJob *job, ReportList *reports)
}
/* Copy final dir back into domain settings */
- BLI_strncpy(mds->cache_directory, temp_dir, FILE_MAXDIR);
+ BLI_strncpy(fds->cache_directory, temp_dir, FILE_MAXDIR);
return true;
}
@@ -251,17 +251,17 @@ static void fluid_bake_free(void *customdata)
static void fluid_bake_sequence(FluidJob *job)
{
- FluidDomainSettings *mds = job->mmd->domain;
+ FluidDomainSettings *fds = job->fmd->domain;
Scene *scene = job->scene;
int frame = 1, orig_frame;
int frames;
int *pause_frame = NULL;
bool is_first_frame;
- frames = mds->cache_frame_end - mds->cache_frame_start + 1;
+ frames = fds->cache_frame_end - fds->cache_frame_start + 1;
if (frames <= 0) {
- BLI_strncpy(mds->error, N_("No frames to bake"), sizeof(mds->error));
+ BLI_strncpy(fds->error, N_("No frames to bake"), sizeof(fds->error));
return;
}
@@ -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);
+ frame = is_first_frame ? fds->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 */
- for (; frame <= mds->cache_frame_end; frame++) {
- const float progress = (frame - mds->cache_frame_start) / (float)frames;
+ /* Loop through selected frames. */
+ for (; frame <= fds->cache_frame_end; frame++) {
+ const float progress = (frame - fds->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,49 +304,49 @@ 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;
}
static void fluid_bake_endjob(void *customdata)
{
FluidJob *job = customdata;
- FluidDomainSettings *mds = job->mmd->domain;
+ FluidDomainSettings *fds = job->fmd->domain;
if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) {
- mds->cache_flag &= ~FLUID_DOMAIN_BAKING_NOISE;
- mds->cache_flag |= FLUID_DOMAIN_BAKED_NOISE;
- mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE;
+ fds->cache_flag &= ~FLUID_DOMAIN_BAKING_NOISE;
+ fds->cache_flag |= FLUID_DOMAIN_BAKED_NOISE;
+ fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE;
}
if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) {
- mds->cache_flag &= ~FLUID_DOMAIN_BAKING_MESH;
- mds->cache_flag |= FLUID_DOMAIN_BAKED_MESH;
- mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_MESH;
+ fds->cache_flag &= ~FLUID_DOMAIN_BAKING_MESH;
+ fds->cache_flag |= FLUID_DOMAIN_BAKED_MESH;
+ fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_MESH;
}
if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) {
- mds->cache_flag &= ~FLUID_DOMAIN_BAKING_PARTICLES;
- mds->cache_flag |= FLUID_DOMAIN_BAKED_PARTICLES;
- mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_PARTICLES;
+ fds->cache_flag &= ~FLUID_DOMAIN_BAKING_PARTICLES;
+ fds->cache_flag |= FLUID_DOMAIN_BAKED_PARTICLES;
+ fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_PARTICLES;
}
if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) {
- mds->cache_flag &= ~FLUID_DOMAIN_BAKING_GUIDE;
- mds->cache_flag |= FLUID_DOMAIN_BAKED_GUIDE;
- mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_GUIDE;
+ fds->cache_flag &= ~FLUID_DOMAIN_BAKING_GUIDE;
+ fds->cache_flag |= FLUID_DOMAIN_BAKED_GUIDE;
+ fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_GUIDE;
}
if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) {
- mds->cache_flag &= ~FLUID_DOMAIN_BAKING_DATA;
- mds->cache_flag |= FLUID_DOMAIN_BAKED_DATA;
- mds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA;
+ fds->cache_flag &= ~FLUID_DOMAIN_BAKING_DATA;
+ fds->cache_flag |= FLUID_DOMAIN_BAKED_DATA;
+ fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA;
}
DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY);
@@ -355,17 +355,17 @@ 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);
}
else {
- if (mds->error[0] != '\0') {
- WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, mds->error);
+ if (fds->error[0] != '\0') {
+ WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, fds->error);
}
- else { /* User canceled the bake */
+ else { /* User canceled the bake. */
WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name);
}
}
@@ -374,7 +374,7 @@ static void fluid_bake_endjob(void *customdata)
static void fluid_bake_startjob(void *customdata, short *stop, short *do_update, float *progress)
{
FluidJob *job = customdata;
- FluidDomainSettings *mds = job->mmd->domain;
+ FluidDomainSettings *fds = job->fmd->domain;
char temp_dir[FILE_MAX];
const char *relbase = BKE_modifier_path_relbase_from_global(job->ob);
@@ -390,54 +390,54 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update,
BKE_spacedata_draw_locks(true);
if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) {
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(temp_dir); /* Create 'noise' subdir if it does not exist already */
- mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE);
- mds->cache_flag |= FLUID_DOMAIN_BAKING_NOISE;
- job->pause_frame = &mds->cache_frame_pause_noise;
+ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE);
+ fds->cache_flag |= FLUID_DOMAIN_BAKING_NOISE;
+ job->pause_frame = &fds->cache_frame_pause_noise;
}
if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) {
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(temp_dir); /* Create 'mesh' subdir if it does not exist already */
- mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH);
- mds->cache_flag |= FLUID_DOMAIN_BAKING_MESH;
- job->pause_frame = &mds->cache_frame_pause_mesh;
+ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH);
+ fds->cache_flag |= FLUID_DOMAIN_BAKING_MESH;
+ job->pause_frame = &fds->cache_frame_pause_mesh;
}
if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) {
BLI_path_join(
- temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL);
+ temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(
temp_dir); /* Create 'particles' subdir if it does not exist already */
- mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES);
- mds->cache_flag |= FLUID_DOMAIN_BAKING_PARTICLES;
- job->pause_frame = &mds->cache_frame_pause_particles;
+ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES);
+ fds->cache_flag |= FLUID_DOMAIN_BAKING_PARTICLES;
+ job->pause_frame = &fds->cache_frame_pause_particles;
}
if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) {
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(temp_dir); /* Create 'guiding' subdir if it does not exist already */
- mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE);
- mds->cache_flag |= FLUID_DOMAIN_BAKING_GUIDE;
- job->pause_frame = &mds->cache_frame_pause_guide;
+ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE);
+ fds->cache_flag |= FLUID_DOMAIN_BAKING_GUIDE;
+ job->pause_frame = &fds->cache_frame_pause_guide;
}
if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) {
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(temp_dir); /* Create 'config' subdir if it does not exist already */
- BLI_path_join(temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL);
+ BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(temp_dir); /* Create 'data' subdir if it does not exist already */
- mds->cache_flag &= ~(FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA);
- mds->cache_flag |= FLUID_DOMAIN_BAKING_DATA;
- job->pause_frame = &mds->cache_frame_pause_data;
+ fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA);
+ fds->cache_flag |= FLUID_DOMAIN_BAKING_DATA;
+ job->pause_frame = &fds->cache_frame_pause_data;
- if (mds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT) {
+ if (fds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT) {
BLI_path_join(
- temp_dir, sizeof(temp_dir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL);
+ temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, NULL);
BLI_path_abs(temp_dir, relbase);
BLI_dir_create_recursive(temp_dir); /* Create 'script' subdir if it does not exist already */
}
@@ -457,7 +457,7 @@ static void fluid_bake_startjob(void *customdata, short *stop, short *do_update,
static void fluid_free_endjob(void *customdata)
{
FluidJob *job = customdata;
- FluidDomainSettings *mds = job->mmd->domain;
+ FluidDomainSettings *fds = job->fmd->domain;
G.is_rendering = false;
BKE_spacedata_draw_locks(false);
@@ -474,8 +474,8 @@ static void fluid_free_endjob(void *customdata)
RPT_INFO, "Fluid: %s complete! (%.2f)", job->name, PIL_check_seconds_timer() - job->start);
}
else {
- if (mds->error[0] != '\0') {
- WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, mds->error);
+ if (fds->error[0] != '\0') {
+ WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, fds->error);
}
else { /* User canceled the free job */
WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name);
@@ -486,7 +486,7 @@ static void fluid_free_endjob(void *customdata)
static void fluid_free_startjob(void *customdata, short *stop, short *do_update, float *progress)
{
FluidJob *job = customdata;
- FluidDomainSettings *mds = job->mmd->domain;
+ FluidDomainSettings *fds = job->fmd->domain;
job->stop = stop;
job->do_update = do_update;
@@ -519,9 +519,9 @@ static void fluid_free_startjob(void *customdata, short *stop, short *do_update,
FLUID_DOMAIN_OUTDATED_GUIDE);
}
#ifdef WITH_FLUID
- BKE_fluid_cache_free(mds, job->ob, cache_map);
+ BKE_fluid_cache_free(fds, job->ob, cache_map);
#else
- UNUSED_VARS(mds);
+ UNUSED_VARS(fds);
#endif
*do_update = true;
@@ -546,6 +546,7 @@ static int fluid_bake_exec(struct bContext *C, struct wmOperator *op)
return OPERATOR_CANCELLED;
}
if (!fluid_validatepaths(job, op->reports)) {
+ fluid_bake_free(job);
return OPERATOR_CANCELLED;
}
WM_report_banners_cancel(job->bmain);
@@ -574,6 +575,7 @@ static int fluid_bake_invoke(struct bContext *C,
}
if (!fluid_validatepaths(job, op->reports)) {
+ fluid_bake_free(job);
return OPERATOR_CANCELLED;
}
@@ -615,27 +617,27 @@ static int fluid_bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *
static int fluid_free_exec(struct bContext *C, struct wmOperator *op)
{
- FluidModifierData *mmd = NULL;
- FluidDomainSettings *mds;
+ FluidModifierData *fmd = NULL;
+ FluidDomainSettings *fds;
Object *ob = ED_object_active_context(C);
Scene *scene = CTX_data_scene(C);
/*
* Get modifier data
*/
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
- if (!mmd) {
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ if (!fmd) {
BKE_report(op->reports, RPT_ERROR, "Bake free failed: no Fluid modifier found");
return OPERATOR_CANCELLED;
}
- mds = mmd->domain;
- if (!mds) {
+ fds = fmd->domain;
+ if (!fds) {
BKE_report(op->reports, RPT_ERROR, "Bake free failed: invalid domain");
return OPERATOR_CANCELLED;
}
/* Cannot free data if other bakes currently working */
- if (mmd->domain->cache_flag & (FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKING_NOISE |
+ if (fmd->domain->cache_flag & (FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKING_NOISE |
FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKING_PARTICLES)) {
BKE_report(op->reports, RPT_ERROR, "Bake free failed: pending bake jobs found");
return OPERATOR_CANCELLED;
@@ -646,11 +648,12 @@ static int fluid_free_exec(struct bContext *C, struct wmOperator *op)
job->scene = scene;
job->depsgraph = CTX_data_depsgraph_pointer(C);
job->ob = ob;
- job->mmd = mmd;
+ job->fmd = fmd;
job->type = op->type->idname;
job->name = op->type->name;
if (!fluid_validatepaths(job, op->reports)) {
+ fluid_bake_free(job);
return OPERATOR_CANCELLED;
}
@@ -678,20 +681,20 @@ static int fluid_free_exec(struct bContext *C, struct wmOperator *op)
static int fluid_pause_exec(struct bContext *C, struct wmOperator *op)
{
- FluidModifierData *mmd = NULL;
- FluidDomainSettings *mds;
+ FluidModifierData *fmd = NULL;
+ FluidDomainSettings *fds;
Object *ob = ED_object_active_context(C);
/*
* Get modifier data
*/
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
- if (!mmd) {
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ if (!fmd) {
BKE_report(op->reports, RPT_ERROR, "Bake free failed: no Fluid modifier found");
return OPERATOR_CANCELLED;
}
- mds = mmd->domain;
- if (!mds) {
+ fds = fmd->domain;
+ if (!fds) {
BKE_report(op->reports, RPT_ERROR, "Bake free failed: invalid domain");
return OPERATOR_CANCELLED;
}
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/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c
index a59b031298c..4939bf0086b 100644
--- a/source/blender/editors/physics/rigidbody_constraint.c
+++ b/source/blender/editors/physics/rigidbody_constraint.c
@@ -68,9 +68,7 @@ static bool ED_operator_rigidbody_con_active_poll(bContext *C)
Object *ob = ED_object_active_context(C);
return (ob && ob->rigidbody_constraint);
}
- else {
- return false;
- }
+ return false;
}
static bool ED_operator_rigidbody_con_add_poll(bContext *C)
@@ -151,9 +149,7 @@ static int rigidbody_con_add_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_constraint_add(wmOperatorType *ot)
@@ -193,9 +189,7 @@ static int rigidbody_con_remove_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body Constraint to remove");
return OPERATOR_CANCELLED;
}
- else {
- ED_rigidbody_constraint_remove(bmain, scene, ob);
- }
+ ED_rigidbody_constraint_remove(bmain, scene, ob);
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
index 78369345b9a..b91385c502c 100644
--- a/source/blender/editors/physics/rigidbody_object.c
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -73,9 +73,7 @@ static bool ED_operator_rigidbody_active_poll(bContext *C)
Object *ob = ED_object_active_context(C);
return (ob && ob->rigidbody_object);
}
- else {
- return 0;
- }
+ return 0;
}
static bool ED_operator_rigidbody_add_poll(bContext *C)
@@ -91,9 +89,7 @@ static bool ED_operator_rigidbody_add_poll(bContext *C)
Object *ob = ED_object_active_context(C);
return (ob && ob->type == OB_MESH);
}
- else {
- return false;
- }
+ return false;
}
/* ----------------- */
@@ -135,9 +131,7 @@ static int rigidbody_object_add_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_object_add(wmOperatorType *ot)
@@ -186,10 +180,9 @@ static int rigidbody_object_remove_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_object_remove(wmOperatorType *ot)
@@ -233,9 +226,7 @@ static int rigidbody_objects_add_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_objects_add(wmOperatorType *ot)
@@ -286,9 +277,7 @@ static int rigidbody_objects_remove_exec(bContext *C, wmOperator *UNUSED(op))
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_objects_remove(wmOperatorType *ot)
@@ -340,9 +329,7 @@ static int rigidbody_objects_shape_change_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
@@ -531,9 +518,7 @@ static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 1db7bf5a766..25b4ddc15fd 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -220,12 +220,10 @@ static void image_buffer_rect_update(RenderJob *rj,
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
return;
}
- else {
- if (rr->renlay == NULL) {
- return;
- }
- rectf = RE_RenderLayerGetPass(rr->renlay, RE_PASSNAME_COMBINED, viewname);
+ if (rr->renlay == NULL) {
+ return;
}
+ rectf = RE_RenderLayerGetPass(rr->renlay, RE_PASSNAME_COMBINED, viewname);
}
if (rectf == NULL) {
return;
@@ -579,7 +577,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
rj->image_outdated = true;
return;
}
- else if (rj->image_outdated) {
+ if (rj->image_outdated) {
/* update entire render */
rj->image_outdated = false;
BKE_image_signal(rj->main, ima, NULL, IMA_SIGNAL_COLORMANAGE);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 2861e851282..3d3936523f9 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -79,7 +79,6 @@
#include "RNA_define.h"
#include "GPU_framebuffer.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "render_intern.h"
@@ -351,7 +350,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
G.f &= ~G_FLAG_RENDER_VIEWPORT;
gp_rect = MEM_mallocN(sizex * sizey * sizeof(uchar) * 4, "offscreen rect");
- GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect);
+ GPU_offscreen_read_pixels(oglrender->ofs, GPU_DATA_UNSIGNED_BYTE, gp_rect);
for (i = 0; i < sizex * sizey * 4; i += 4) {
blend_color_mix_byte(&render_rect[i], &render_rect[i], &gp_rect[i]);
@@ -766,7 +765,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
/* corrects render size with actual size, not every card supports non-power-of-two dimensions */
DRW_opengl_context_enable(); /* Offscreen creation needs to be done in DRW context. */
- ofs = GPU_offscreen_create(sizex, sizey, 0, true, true, err_out);
+ ofs = GPU_offscreen_create(sizex, sizey, true, true, err_out);
DRW_opengl_context_disable();
if (!ofs) {
@@ -1236,9 +1235,8 @@ static int screen_opengl_render_modal(bContext *C, wmOperator *op, const wmEvent
screen_opengl_render_end(C, op->customdata);
return OPERATOR_FINISHED;
}
- else {
- ret = screen_opengl_render_anim_step(C, op);
- }
+
+ ret = screen_opengl_render_anim_step(C, op);
/* stop at the end or on error */
if (ret == false) {
@@ -1291,16 +1289,15 @@ static int screen_opengl_render_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- bool ret = true;
- if (!screen_opengl_render_anim_initialize(C, op)) {
- return OPERATOR_CANCELLED;
- }
+ bool ret = true;
- while (ret) {
- ret = screen_opengl_render_anim_step(C, op);
- }
+ if (!screen_opengl_render_anim_initialize(C, op)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ while (ret) {
+ ret = screen_opengl_render_anim_step(C, op);
}
/* no redraw needed, we leave state as we entered it */
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 19e4f652963..85fc6927063 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -82,7 +82,6 @@
#include "BIF_glutil.h"
-#include "GPU_glew.h"
#include "GPU_shader.h"
#include "RE_engine.h"
@@ -632,18 +631,8 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect
}
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
- immDrawPixelsTex(&state,
- fx,
- fy,
- rres.rectx,
- rres.recty,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
- rect_byte,
- 1.0f,
- 1.0f,
- NULL);
+ immDrawPixelsTex(
+ &state, fx, fy, rres.rectx, rres.recty, GPU_RGBA8, false, rect_byte, 1.0f, 1.0f, NULL);
MEM_freeN(rect_byte);
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 49ab2c485b1..536252f5003 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -162,7 +162,7 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_add_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -207,7 +207,7 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_remove_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -238,7 +238,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op))
else {
/* Find the first matching material.
* Note: there may be multiple but that's not a common use case. */
- for (short i = 0; i < ob->totcol; i++) {
+ for (int i = 0; i < ob->totcol; i++) {
const Material *mat = BKE_object_material_get(ob, i + 1);
if (mat_active == mat) {
mat_nr_active = i;
@@ -310,7 +310,7 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_assign_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -339,7 +339,7 @@ static int material_slot_de_select(bContext *C, bool select)
else {
/* Find the first matching material.
* Note: there may be multiple but that's not a common use case. */
- for (short i = 0; i < ob->totcol; i++) {
+ for (int i = 0; i < ob->totcol; i++) {
const Material *mat = BKE_object_material_get(ob, i + 1);
if (mat_active == mat) {
mat_nr_active = i;
@@ -465,17 +465,27 @@ static int material_slot_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_context(C);
- Material ***matar;
+ Material ***matar_obdata;
- if (!ob || !(matar = BKE_object_material_array_p(ob))) {
+ if (!ob || !(matar_obdata = BKE_object_material_array_p(ob))) {
return OPERATOR_CANCELLED;
}
+ BLI_assert(ob->totcol == *BKE_object_material_len_p(ob));
+
+ Material ***matar_object = &ob->mat;
+
+ Material **matar = MEM_callocN(sizeof(*matar) * (size_t)ob->totcol, __func__);
+ for (int i = ob->totcol; i--;) {
+ matar[i] = ob->matbits[i] ? (*matar_object)[i] : (*matar_obdata)[i];
+ }
+
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
if (ob != ob_iter && BKE_object_material_array_p(ob_iter)) {
- if (ob->data != ob_iter->data) {
- BKE_object_material_array_assign(bmain, ob_iter, matar, ob->totcol);
- }
+ /* If we are using the same obdata, we only assign slots in ob_iter that are using object
+ * materials, and not obdata ones. */
+ const bool is_same_obdata = ob->data == ob_iter->data;
+ BKE_object_material_array_assign(bmain, ob_iter, &matar, ob->totcol, is_same_obdata);
if (ob_iter->totcol == ob->totcol) {
ob_iter->actcol = ob->actcol;
@@ -486,6 +496,8 @@ static int material_slot_copy_exec(bContext *C, wmOperator *UNUSED(op))
}
CTX_DATA_END;
+ MEM_freeN(matar);
+
return OPERATOR_FINISHED;
}
@@ -564,6 +576,7 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot)
ot->description = "Move the active material up/down in the list";
/* api callbacks */
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = material_slot_move_exec;
/* flags */
@@ -638,7 +651,7 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_remove_unused_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -707,6 +720,7 @@ void MATERIAL_OT_new(wmOperatorType *ot)
ot->description = "Add a new material";
/* api callbacks */
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = new_material_exec;
/* flags */
@@ -1666,13 +1680,13 @@ static int freestyle_get_modifier_type(PointerRNA *ptr)
if (RNA_struct_is_a(ptr->type, &RNA_LineStyleColorModifier)) {
return LS_MODIFIER_TYPE_COLOR;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_LineStyleAlphaModifier)) {
+ if (RNA_struct_is_a(ptr->type, &RNA_LineStyleAlphaModifier)) {
return LS_MODIFIER_TYPE_ALPHA;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_LineStyleThicknessModifier)) {
+ if (RNA_struct_is_a(ptr->type, &RNA_LineStyleThicknessModifier)) {
return LS_MODIFIER_TYPE_THICKNESS;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_LineStyleGeometryModifier)) {
+ if (RNA_struct_is_a(ptr->type, &RNA_LineStyleGeometryModifier)) {
return LS_MODIFIER_TYPE_GEOMETRY;
}
return -1;
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/render/render_view.c b/source/blender/editors/render/render_view.c
index a9c855b14b0..fd5963b217b 100644
--- a/source/blender/editors/render/render_view.c
+++ b/source/blender/editors/render/render_view.c
@@ -283,12 +283,12 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else if (sima->flag & SI_FULLWINDOW) {
+ if (sima->flag & SI_FULLWINDOW) {
sima->flag &= ~SI_FULLWINDOW;
ED_screen_state_toggle(C, win, area, SCREENMAXIMIZED);
return OPERATOR_FINISHED;
}
- else if (WM_window_is_temp_screen(win)) {
+ if (WM_window_is_temp_screen(win)) {
wm_window_close(C, CTX_wm_manager(C), win);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c
index c32bb69db4e..fa63a890de7 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);
}
@@ -133,8 +133,8 @@ static bool view_layer_remove_poll(const Scene *scene, const ViewLayer *layer)
if (act == -1) {
return false;
}
- else if ((scene->view_layers.first == scene->view_layers.last) &&
- (scene->view_layers.first == layer)) {
+ if ((scene->view_layers.first == scene->view_layers.last) &&
+ (scene->view_layers.first == layer)) {
/* ensure 1 layer is kept */
return false;
}
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index c04122edd36..f87f631c643 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -135,7 +135,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si
immUnbindProgram();
GPU_blend(false);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
}
void ED_region_pixelspace(ARegion *region)
@@ -357,12 +357,12 @@ static void region_draw_status_text(ScrArea *area, ARegion *region)
bool overlap = ED_region_is_overlap(area->spacetype, region->regiontype);
if (overlap) {
- GPU_clear_color(0.0, 0.0, 0.0, 0.0);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
+ GPU_clear(GPU_COLOR_BIT);
}
else {
UI_ThemeClearColor(TH_HEADER);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear(GPU_COLOR_BIT);
}
int fontid = BLF_set_default();
@@ -527,11 +527,11 @@ void ED_region_do_draw(bContext *C, ARegion *region)
if (area && area_is_pseudo_minimized(area)) {
UI_ThemeClearColor(TH_EDITOR_OUTLINE);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear(GPU_COLOR_BIT);
return;
}
/* optional header info instead? */
- else if (region->headerstr) {
+ if (region->headerstr) {
region_draw_status_text(area, region);
}
else if (at->draw) {
@@ -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;
}
}
@@ -1128,9 +1128,8 @@ static int rct_fits(const rcti *rect, char dir, int size)
if (dir == 'h') {
return BLI_rcti_size_x(rect) + 1 - size;
}
- else { /* 'v' */
- return BLI_rcti_size_y(rect) + 1 - size;
- }
+ /* 'v' */
+ return BLI_rcti_size_y(rect) + 1 - size;
}
/* *************************************************************** */
@@ -1145,7 +1144,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;
}
@@ -1176,25 +1175,21 @@ static void region_overlap_fix(ScrArea *area, ARegion *region)
region->flag |= RGN_FLAG_TOO_SMALL;
return;
}
- else {
- BLI_rcti_translate(&region->winrct, ar1->winx, 0);
- }
+ BLI_rcti_translate(&region->winrct, ar1->winx, 0);
}
else if (align1 == RGN_ALIGN_RIGHT) {
if (region->winrct.xmin - ar1->winx < U.widget_unit) {
region->flag |= RGN_FLAG_TOO_SMALL;
return;
}
- else {
- BLI_rcti_translate(&region->winrct, -ar1->winx, 0);
- }
+ BLI_rcti_translate(&region->winrct, -ar1->winx, 0);
}
}
/* 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 +1551,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 +2350,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 +2568,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 +2790,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(
@@ -3009,7 +3009,7 @@ ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen)
if (!global_area) {
return screen->areabase.first;
}
- else if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
+ if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) {
return global_area;
}
/* Find next visible area. */
diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c
index 8df1172dda1..07a122c7094 100644
--- a/source/blender/editors/screen/glutil.c
+++ b/source/blender/editors/screen/glutil.c
@@ -39,6 +39,7 @@
#include "GPU_immediate.h"
#include "GPU_matrix.h"
+#include "GPU_texture.h"
#ifdef __APPLE__
# include "GPU_state.h"
@@ -48,30 +49,6 @@
/* ******************************************** */
-static int get_cached_work_texture(int *r_w, int *r_h)
-{
- static GLint texid = -1;
- static int tex_w = 256;
- static int tex_h = 256;
-
- if (texid == -1) {
- glGenTextures(1, (GLuint *)&texid);
-
- glBindTexture(GL_TEXTURE_2D, texid);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- *r_w = tex_w;
- *r_h = tex_h;
- return texid;
-}
-
static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state)
{
GPUVertFormat *vert_format = immVertexFormat();
@@ -118,9 +95,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float scaleX,
float scaleY,
@@ -132,61 +108,45 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
float yzoom,
float color[4])
{
- uchar *uc_rect = (uchar *)rect;
- const float *f_rect = (float *)rect;
- int subpart_x, subpart_y, tex_w, tex_h;
+ int subpart_x, subpart_y, tex_w = 256, tex_h = 256;
int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y;
- int texid = get_cached_work_texture(&tex_w, &tex_h);
int components;
const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y));
float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- GLint unpack_row_length;
- glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_row_length);
-
- glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, texid);
- glBindSampler(0, 0);
-
- /* don't want nasty border artifacts */
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, zoomfilter);
-
- /* setup seamless 2=on, 0=off */
- seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0;
-
- offset_x = tex_w - seamless;
- offset_y = tex_h - seamless;
-
- nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
- nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
-
- if (format == GL_RGBA) {
+ if (ELEM(gpu_format, GPU_RGBA8, GPU_RGBA16F)) {
components = 4;
}
- else if (format == GL_RGB) {
+ else if (ELEM(gpu_format, GPU_RGB16F)) {
components = 3;
}
- else if (format == GL_RED) {
+ else if (ELEM(gpu_format, GPU_R8, GPU_R16F)) {
components = 1;
}
else {
- BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled");
+ BLI_assert(!"Incompatible format passed to immDrawPixels");
return;
}
- if (type == GL_FLOAT) {
- /* need to set internal format to higher range float */
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, tex_w, tex_h, 0, format, GL_FLOAT, NULL);
- }
- else {
- /* switch to 8bit RGBA for byte buffer */
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, format, GL_UNSIGNED_BYTE, NULL);
- }
+ const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F);
+ eGPUDataFormat gpu_data = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE;
+ size_t stride = components * ((use_float_data) ? sizeof(float) : sizeof(uchar));
- uint pos = state->pos, texco = state->texco;
+ GPUTexture *tex = GPU_texture_create_2d(tex_w, tex_h, gpu_format, NULL, NULL);
+
+ GPU_texture_filter_mode(tex, use_filter);
+ GPU_texture_wrap_mode(tex, false, true);
+
+ GPU_texture_bind(tex, 0);
+
+ /* setup seamless 2=on, 0=off */
+ seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0;
+
+ offset_x = tex_w - seamless;
+ offset_y = tex_h - seamless;
+
+ nsubparts_x = (img_w + (offset_x - 1)) / (offset_x);
+ nsubparts_y = (img_h + (offset_y - 1)) / (offset_y);
/* optional */
/* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
@@ -196,6 +156,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
immUniformColor4fv((color) ? color : white);
}
+ GPU_unpack_row_length_set(img_w);
+
for (subpart_y = 0; subpart_y < nsubparts_y; subpart_y++) {
for (subpart_x = 0; subpart_x < nsubparts_x; subpart_x++) {
int remainder_x = img_w - subpart_x * offset_x;
@@ -213,141 +175,68 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
continue;
}
+ int right = subpart_w - offset_right;
+ int top = subpart_h - offset_top;
+ int bottom = 0 + offset_bot;
+ int left = 0 + offset_left;
+
if (use_clipping) {
- if (rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX < clip_min_x ||
- rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY < clip_min_y) {
+ if (rast_x + right * xzoom * scaleX < clip_min_x ||
+ rast_y + top * yzoom * scaleY < clip_min_y) {
continue;
}
- if (rast_x + (float)offset_left * xzoom > clip_max_x ||
- rast_y + (float)offset_bot * yzoom > clip_max_y) {
+ if (rast_x + left * xzoom > clip_max_x || rast_y + bottom * yzoom > clip_max_y) {
continue;
}
}
- if (type == GL_FLOAT) {
- glTexSubImage2D(GL_TEXTURE_2D,
- 0,
- 0,
- 0,
- subpart_w,
- subpart_h,
- format,
- GL_FLOAT,
- &f_rect[((size_t)subpart_y) * offset_y * img_w * components +
- subpart_x * offset_x * components]);
-
- /* add an extra border of pixels so linear looks ok at edges of full image */
- if (subpart_w < tex_w) {
- glTexSubImage2D(GL_TEXTURE_2D,
- 0,
- subpart_w,
- 0,
- 1,
- subpart_h,
- format,
- GL_FLOAT,
- &f_rect[((size_t)subpart_y) * offset_y * img_w * components +
- (subpart_x * offset_x + subpart_w - 1) * components]);
- }
- if (subpart_h < tex_h) {
- glTexSubImage2D(
- GL_TEXTURE_2D,
- 0,
- 0,
- subpart_h,
- subpart_w,
- 1,
- format,
- GL_FLOAT,
- &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components +
- subpart_x * offset_x * components]);
- }
- if (subpart_w < tex_w && subpart_h < tex_h) {
- glTexSubImage2D(
- GL_TEXTURE_2D,
- 0,
- subpart_w,
- subpart_h,
- 1,
- 1,
- format,
- GL_FLOAT,
- &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components +
- (subpart_x * offset_x + subpart_w - 1) * components]);
- }
- }
- else {
- glTexSubImage2D(GL_TEXTURE_2D,
- 0,
- 0,
- 0,
- subpart_w,
- subpart_h,
- format,
- GL_UNSIGNED_BYTE,
- &uc_rect[((size_t)subpart_y) * offset_y * img_w * components +
- subpart_x * offset_x * components]);
+ {
+ int src_y = subpart_y * offset_y;
+ int src_x = subpart_x * offset_x;
+#define DATA(_y, _x) ((char *)rect + stride * ((size_t)(_y)*img_w + (_x)))
+ {
+ void *data = DATA(src_y, src_x);
+ GPU_texture_update_sub(tex, gpu_data, data, 0, 0, 0, subpart_w, subpart_h, 0);
+ }
+ /* Add an extra border of pixels so linear interpolation looks ok
+ * at edges of full image. */
if (subpart_w < tex_w) {
- glTexSubImage2D(GL_TEXTURE_2D,
- 0,
- subpart_w,
- 0,
- 1,
- subpart_h,
- format,
- GL_UNSIGNED_BYTE,
- &uc_rect[((size_t)subpart_y) * offset_y * img_w * components +
- (subpart_x * offset_x + subpart_w - 1) * components]);
+ void *data = DATA(src_y, src_x + subpart_w - 1);
+ int offset[2] = {subpart_w, 0};
+ int extent[2] = {1, subpart_h};
+ GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0);
}
if (subpart_h < tex_h) {
- glTexSubImage2D(
- GL_TEXTURE_2D,
- 0,
- 0,
- subpart_h,
- subpart_w,
- 1,
- format,
- GL_UNSIGNED_BYTE,
- &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components +
- subpart_x * offset_x * components]);
+ void *data = DATA(src_y + subpart_h - 1, src_x);
+ int offset[2] = {0, subpart_h};
+ int extent[2] = {subpart_w, 1};
+ GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0);
}
+
if (subpart_w < tex_w && subpart_h < tex_h) {
- glTexSubImage2D(
- GL_TEXTURE_2D,
- 0,
- subpart_w,
- subpart_h,
- 1,
- 1,
- format,
- GL_UNSIGNED_BYTE,
- &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components +
- (subpart_x * offset_x + subpart_w - 1) * components]);
+ void *data = DATA(src_y + subpart_h - 1, src_x + subpart_w - 1);
+ int offset[2] = {subpart_w, subpart_h};
+ int extent[2] = {1, 1};
+ GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0);
}
+#undef DATA
}
+ uint pos = state->pos, texco = state->texco;
+
immBegin(GPU_PRIM_TRI_FAN, 4);
- immAttr2f(texco, (float)(0 + offset_left) / tex_w, (float)(0 + offset_bot) / tex_h);
- immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)offset_bot * yzoom);
-
- immAttr2f(texco, (float)(subpart_w - offset_right) / tex_w, (float)(0 + offset_bot) / tex_h);
- immVertex2f(pos,
- rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX,
- rast_y + (float)offset_bot * yzoom);
-
- immAttr2f(texco,
- (float)(subpart_w - offset_right) / tex_w,
- (float)(subpart_h - offset_top) / tex_h);
- immVertex2f(pos,
- rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX,
- rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
-
- immAttr2f(texco, (float)(0 + offset_left) / tex_w, (float)(subpart_h - offset_top) / tex_h);
- immVertex2f(pos,
- rast_x + (float)offset_left * xzoom,
- rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
+ immAttr2f(texco, left / (float)tex_w, bottom / (float)tex_h);
+ immVertex2f(pos, rast_x + offset_left * xzoom, rast_y + offset_bot * yzoom);
+
+ immAttr2f(texco, right / (float)tex_w, bottom / (float)tex_h);
+ immVertex2f(pos, rast_x + right * xzoom * scaleX, rast_y + offset_bot * yzoom);
+
+ immAttr2f(texco, right / (float)tex_w, top / (float)tex_h);
+ immVertex2f(pos, rast_x + right * xzoom * scaleX, rast_y + top * yzoom * scaleY);
+
+ immAttr2f(texco, left / (float)tex_w, top / (float)tex_h);
+ immVertex2f(pos, rast_x + offset_left * xzoom, rast_y + top * yzoom * scaleY);
immEnd();
/* NOTE: Weirdly enough this is only required on macOS. Without this there is some sort of
@@ -364,8 +253,11 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
immUnbindProgram();
}
- glBindTexture(GL_TEXTURE_2D, 0);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, unpack_row_length);
+ GPU_texture_unbind(tex);
+ GPU_texture_free(tex);
+
+ /* Restore default. */
+ GPU_unpack_row_length_set(0);
}
void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
@@ -373,9 +265,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float scaleX,
float scaleY,
@@ -388,9 +279,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state,
y,
img_w,
img_h,
- format,
- type,
- zoomfilter,
+ gpu_format,
+ use_filter,
rect,
scaleX,
scaleY,
@@ -408,9 +298,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float xzoom,
float yzoom,
@@ -421,9 +310,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state,
y,
img_w,
img_h,
- format,
- type,
- zoomfilter,
+ gpu_format,
+ use_filter,
rect,
1.0f,
1.0f,
@@ -441,9 +329,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
float y,
int img_w,
int img_h,
- int format,
- int type,
- int zoomfilter,
+ eGPUTextureFormat gpu_format,
+ bool use_filter,
void *rect,
float clip_min_x,
float clip_min_y,
@@ -458,9 +345,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
y,
img_w,
img_h,
- format,
- type,
- zoomfilter,
+ gpu_format,
+ use_filter,
rect,
1.0f,
1.0f,
@@ -473,76 +359,13 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state,
color);
}
-/* *************** glPolygonOffset hack ************* */
-
-float bglPolygonOffsetCalc(const float winmat[16], float viewdist, float dist)
-{
- /* Seems like we have a factor of 2 more offset than 2.79 for some reason. Correct for this. */
- dist *= 0.5f;
-
- if (winmat[15] > 0.5f) {
-#if 1
- return 0.00001f * dist * viewdist; // ortho tweaking
-#else
- static float depth_fac = 0.0f;
- if (depth_fac == 0.0f) {
- int depthbits;
- glGetIntegerv(GL_DEPTH_BITS, &depthbits);
- depth_fac = 1.0f / (float)((1 << depthbits) - 1);
- }
- offs = (-1.0 / winmat[10]) * dist * depth_fac;
-
- UNUSED_VARS(viewdist);
-#endif
- }
- else {
- /* This adjustment effectively results in reducing the Z value by 0.25%.
- *
- * winmat[14] actually evaluates to `-2 * far * near / (far - near)`,
- * is very close to -0.2 with default clip range,
- * and is used as the coefficient multiplied by `w / z`,
- * thus controlling the z dependent part of the depth value.
- */
- return winmat[14] * -0.0025f * dist;
- }
-}
-
-/**
- * \note \a viewdist is only for ortho at the moment.
- */
-void bglPolygonOffset(float viewdist, float dist)
-{
- static float winmat[16], offset = 0.0f;
-
- if (dist != 0.0f) {
- // glEnable(GL_POLYGON_OFFSET_FILL);
- // glPolygonOffset(-1.0, -1.0);
-
- /* hack below is to mimic polygon offset */
- GPU_matrix_projection_get(winmat);
-
- /* dist is from camera to center point */
-
- float offs = bglPolygonOffsetCalc(winmat, viewdist, dist);
-
- winmat[14] -= offs;
- offset += offs;
- }
- else {
- winmat[14] += offset;
- offset = 0.0;
- }
-
- GPU_matrix_projection_set(winmat);
-}
-
/* **** Color management helper functions for GLSL display/transform ***** */
/* Draw given image buffer on a screen using GLSL for display transform */
void ED_draw_imbuf_clipping(ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
ColorManagedViewSettings *view_settings,
ColorManagedDisplaySettings *display_settings,
float clip_min_x,
@@ -592,13 +415,13 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf,
if (ok) {
if (ibuf->rect_float) {
- int format = 0;
+ eGPUTextureFormat format = 0;
if (ibuf->channels == 3) {
- format = GL_RGB;
+ format = GPU_RGB16F;
}
else if (ibuf->channels == 4) {
- format = GL_RGBA;
+ format = GPU_RGBA16F;
}
else {
BLI_assert(!"Incompatible number of channels for GLSL display");
@@ -611,8 +434,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf,
ibuf->x,
ibuf->y,
format,
- GL_FLOAT,
- zoomfilter,
+ use_filter,
ibuf->rect_float,
clip_min_x,
clip_min_y,
@@ -630,9 +452,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf,
y,
ibuf->x,
ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- zoomfilter,
+ GPU_RGBA8,
+ use_filter,
ibuf->rect,
clip_min_x,
clip_min_y,
@@ -664,9 +485,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf,
y,
ibuf->x,
ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- zoomfilter,
+ GPU_RGBA8,
+ use_filter,
display_buffer,
clip_min_x,
clip_min_y,
@@ -684,7 +504,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf,
void ED_draw_imbuf(ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
ColorManagedViewSettings *view_settings,
ColorManagedDisplaySettings *display_settings,
float zoom_x,
@@ -693,7 +513,7 @@ void ED_draw_imbuf(ImBuf *ibuf,
ED_draw_imbuf_clipping(ibuf,
x,
y,
- zoomfilter,
+ use_filter,
view_settings,
display_settings,
0.0f,
@@ -708,7 +528,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C,
ImBuf *ibuf,
float x,
float y,
- int zoomfilter,
+ bool use_filter,
float clip_min_x,
float clip_min_y,
float clip_max_x,
@@ -724,7 +544,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C,
ED_draw_imbuf_clipping(ibuf,
x,
y,
- zoomfilter,
+ use_filter,
view_settings,
display_settings,
clip_min_x,
@@ -736,9 +556,9 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C,
}
void ED_draw_imbuf_ctx(
- const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, float zoom_x, float zoom_y)
+ const bContext *C, ImBuf *ibuf, float x, float y, bool use_filter, float zoom_x, float zoom_y)
{
- ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
+ ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, use_filter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y);
}
int ED_draw_imbuf_method(ImBuf *ibuf)
@@ -752,9 +572,7 @@ int ED_draw_imbuf_method(ImBuf *ibuf)
return (size > threshold) ? IMAGE_DRAW_METHOD_2DTEXTURE : IMAGE_DRAW_METHOD_GLSL;
}
- else {
- return U.image_draw_method;
- }
+ return U.image_draw_method;
}
/* don't move to GPU_immediate_util.h because this uses user-prefs
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 3202dc68f37..c17a34f97b9 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -125,11 +125,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_dir_set(result, screen_context_dir);
return 1;
}
- else if (CTX_data_equals(member, "scene")) {
+ if (CTX_data_equals(member, "scene")) {
CTX_data_id_pointer_set(result, &scene->id);
return 1;
}
- else if (CTX_data_equals(member, "visible_objects")) {
+ if (CTX_data_equals(member, "visible_objects")) {
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (BASE_VISIBLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
@@ -138,7 +138,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "selectable_objects")) {
+ if (CTX_data_equals(member, "selectable_objects")) {
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (BASE_SELECTABLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
@@ -147,7 +147,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "selected_objects")) {
+ if (CTX_data_equals(member, "selected_objects")) {
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (BASE_SELECTED(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
@@ -156,7 +156,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "selected_editable_objects")) {
+ if (CTX_data_equals(member, "selected_editable_objects")) {
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (BASE_SELECTED_EDITABLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
@@ -165,7 +165,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "editable_objects")) {
+ if (CTX_data_equals(member, "editable_objects")) {
/* Visible + Editable, but not necessarily selected */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (BASE_EDITABLE(v3d, base)) {
@@ -175,7 +175,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "objects_in_mode")) {
+ if (CTX_data_equals(member, "objects_in_mode")) {
if (obact && (obact->mode != OB_MODE_OBJECT)) {
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) {
CTX_data_id_list_add(result, &ob_iter->id);
@@ -185,7 +185,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "objects_in_mode_unique_data")) {
+ if (CTX_data_equals(member, "objects_in_mode_unique_data")) {
if (obact && (obact->mode != OB_MODE_OBJECT)) {
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) {
ob_iter->id.tag |= LIB_TAG_DOIT;
@@ -202,7 +202,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) {
+ if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) {
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
EditBone *ebone, *flipbone = NULL;
const bool editable_bones = CTX_data_equals(member, "editable_bones");
@@ -257,9 +257,10 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "selected_bones") ||
- CTX_data_equals(member, "selected_editable_bones")) {
+ if (CTX_data_equals(member, "selected_bones") ||
+ CTX_data_equals(member, "selected_editable_bones")) {
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
EditBone *ebone, *flipbone = NULL;
const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones");
@@ -314,8 +315,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "visible_pose_bones")) {
+ if (CTX_data_equals(member, "visible_pose_bones")) {
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose && obpose->pose && obpose->data) {
if (obpose != obact) {
@@ -336,8 +338,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "selected_pose_bones")) {
+ if (CTX_data_equals(member, "selected_pose_bones")) {
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose && obpose->pose && obpose->data) {
if (obpose != obact) {
@@ -358,8 +361,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "selected_pose_bones_from_active_object")) {
+ if (CTX_data_equals(member, "selected_pose_bones_from_active_object")) {
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose && obpose->pose && obpose->data) {
if (obpose != obact) {
@@ -377,8 +381,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_bone")) {
+ if (CTX_data_equals(member, "active_bone")) {
if (obact && obact->type == OB_ARMATURE) {
bArmature *arm = obact->data;
if (arm->edbo) {
@@ -394,8 +399,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
}
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_pose_bone")) {
+ if (CTX_data_equals(member, "active_pose_bone")) {
bPoseChannel *pchan;
Object *obpose = BKE_object_pose_armature_get(obact);
@@ -404,22 +410,23 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_pointer_set(result, &obpose->id, &RNA_PoseBone, pchan);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_object")) {
+ if (CTX_data_equals(member, "active_object")) {
if (obact) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "object")) {
+ if (CTX_data_equals(member, "object")) {
if (obact) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "edit_object")) {
+ if (CTX_data_equals(member, "edit_object")) {
/* convenience for now, 1 object per scene in editmode */
if (obedit) {
CTX_data_id_pointer_set(result, &obedit->id);
@@ -427,49 +434,49 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
- else if (CTX_data_equals(member, "sculpt_object")) {
+ if (CTX_data_equals(member, "sculpt_object")) {
if (obact && (obact->mode & OB_MODE_SCULPT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "vertex_paint_object")) {
+ if (CTX_data_equals(member, "vertex_paint_object")) {
if (obact && (obact->mode & OB_MODE_VERTEX_PAINT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "weight_paint_object")) {
+ if (CTX_data_equals(member, "weight_paint_object")) {
if (obact && (obact->mode & OB_MODE_WEIGHT_PAINT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "image_paint_object")) {
+ if (CTX_data_equals(member, "image_paint_object")) {
if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "particle_edit_object")) {
+ if (CTX_data_equals(member, "particle_edit_object")) {
if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
return 1;
}
- else if (CTX_data_equals(member, "pose_object")) {
+ if (CTX_data_equals(member, "pose_object")) {
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose) {
CTX_data_id_pointer_set(result, &obpose->id);
}
return 1;
}
- else if (CTX_data_equals(member, "sequences")) {
+ if (CTX_data_equals(member, "sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
Sequence *seq;
@@ -479,8 +486,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "selected_sequences")) {
+ if (CTX_data_equals(member, "selected_sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
Sequence *seq;
@@ -492,8 +500,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "selected_editable_sequences")) {
+ if (CTX_data_equals(member, "selected_editable_sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
Sequence *seq;
@@ -505,8 +514,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "selected_nla_strips")) {
+ if (CTX_data_equals(member, "selected_nla_strips")) {
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) != 0) {
ListBase anim_data = {NULL, NULL};
@@ -530,8 +540,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "gpencil_data")) {
+ if (CTX_data_equals(member, "gpencil_data")) {
/* FIXME: for some reason, CTX_data_active_object(C) returns NULL when called from these
* situations (as outlined above - see Campbell's #ifdefs).
* That causes the get_active function to fail when called from context.
@@ -543,8 +554,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_id_pointer_set(result, &gpd->id);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "gpencil_data_owner")) {
+ if (CTX_data_equals(member, "gpencil_data_owner")) {
/* Pointer to which data/datablock owns the reference to the Grease Pencil data being used
* (as gpencil_data). */
bGPdata **gpd_ptr = NULL;
@@ -557,16 +569,18 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "annotation_data")) {
+ if (CTX_data_equals(member, "annotation_data")) {
bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)screen, area, scene);
if (gpd) {
CTX_data_id_pointer_set(result, &gpd->id);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "annotation_data_owner")) {
+ if (CTX_data_equals(member, "annotation_data_owner")) {
/* Pointer to which data/datablock owns the reference to the Grease Pencil data being used. */
bGPdata **gpd_ptr = NULL;
PointerRNA ptr;
@@ -578,8 +592,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_pointer_set(result, ptr.owner_id, ptr.type, ptr.data);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_gpencil_layer")) {
+ if (CTX_data_equals(member, "active_gpencil_layer")) {
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -590,8 +605,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_annotation_layer")) {
+ if (CTX_data_equals(member, "active_annotation_layer")) {
bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)screen, area, scene);
if (gpd) {
@@ -602,8 +618,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_gpencil_frame")) {
+ if (CTX_data_equals(member, "active_gpencil_frame")) {
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -614,8 +631,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "visible_gpencil_layers")) {
+ if (CTX_data_equals(member, "visible_gpencil_layers")) {
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -629,8 +647,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "editable_gpencil_layers")) {
+ if (CTX_data_equals(member, "editable_gpencil_layers")) {
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -644,8 +663,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "editable_gpencil_strokes")) {
+ if (CTX_data_equals(member, "editable_gpencil_strokes")) {
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
@@ -684,8 +704,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_operator")) {
+ if (CTX_data_equals(member, "active_operator")) {
wmOperator *op = NULL;
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -706,11 +727,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_pointer_set(result, NULL, &RNA_Operator, op);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "editable_fcurves") ||
- CTX_data_equals(member, "visible_fcurves") ||
- CTX_data_equals(member, "selected_editable_fcurves") ||
- CTX_data_equals(member, "selected_visible_fcurves")) {
+ if (CTX_data_equals(member, "editable_fcurves") || CTX_data_equals(member, "visible_fcurves") ||
+ CTX_data_equals(member, "selected_editable_fcurves") ||
+ CTX_data_equals(member, "selected_visible_fcurves")) {
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_ACTION, SPACE_GRAPH)) {
@@ -740,8 +761,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "active_editable_fcurve")) {
+ if (CTX_data_equals(member, "active_editable_fcurve")) {
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_GRAPH)) {
@@ -762,10 +784,8 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
ANIM_animdata_freelist(&anim_data);
return 1;
}
- }
- else {
- return 0; /* not found */
+ return -1; /* found but not available */
}
- return -1; /* found but not available */
+ return 0; /* not found */
}
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index 2452302561b..40a452a5363 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -404,7 +404,7 @@ void ED_screen_draw_edges(wmWindow *win)
/* It seems that all areas gets smaller when pixelsize is > 1.
* So in order to avoid missing pixels we just disable de scissors. */
if (U.pixelsize <= 1.0f) {
- glEnable(GL_SCISSOR_TEST);
+ GPU_scissor_test(true);
}
UI_GetThemeColor4fv(TH_EDITOR_OUTLINE, col);
@@ -429,7 +429,7 @@ void ED_screen_draw_edges(wmWindow *win)
GPU_blend(false);
if (U.pixelsize <= 1.0f) {
- glDisable(GL_SCISSOR_TEST);
+ GPU_scissor_test(false);
}
}
@@ -611,7 +611,7 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y)
void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect)
{
char err_out[256] = "unknown";
- GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, 0, true, false, err_out);
+ GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out);
GPU_offscreen_bind(offscreen, true);
GPU_clear_color(0.0, 0.0, 0.0, 0.0);
@@ -619,7 +619,7 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin
screen_preview_draw(screen, size_x, size_y);
- GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, r_rect);
+ GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, r_rect);
GPU_offscreen_unbind(offscreen, true);
GPU_offscreen_free(offscreen);
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_geometry.c b/source/blender/editors/screen/screen_geometry.c
index 47580c2f4b3..0b83a657265 100644
--- a/source/blender/editors/screen/screen_geometry.c
+++ b/source/blender/editors/screen/screen_geometry.c
@@ -327,27 +327,26 @@ short screen_geom_find_area_split_point(const ScrArea *area,
return y;
}
- else {
- x = area->v1->vec.x + round_fl_to_short(fac * cur_area_width);
- area_min = area_min_x;
+ x = area->v1->vec.x + round_fl_to_short(fac * cur_area_width);
- if (area->v1->vec.x > window_rect->xmin) {
- area_min += U.pixelsize;
- }
- if (area->v4->vec.x < (window_rect->xmax - 1)) {
- area_min += U.pixelsize;
- }
+ area_min = area_min_x;
- if (x - area->v1->vec.x < area_min) {
- x = area->v1->vec.x + area_min;
- }
- else if (area->v4->vec.x - x < area_min) {
- x = area->v4->vec.x - area_min;
- }
+ if (area->v1->vec.x > window_rect->xmin) {
+ area_min += U.pixelsize;
+ }
+ if (area->v4->vec.x < (window_rect->xmax - 1)) {
+ area_min += U.pixelsize;
+ }
- return x;
+ if (x - area->v1->vec.x < area_min) {
+ x = area->v1->vec.x + area_min;
}
+ else if (area->v4->vec.x - x < area_min) {
+ x = area->v4->vec.x - area_min;
+ }
+
+ return x;
}
/**
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index b2243f2ccb9..fde1498bc5e 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -89,6 +89,8 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "GPU_extensions.h"
+
#include "screen_intern.h" /* own module include */
#define KM_MODAL_CANCEL 1
@@ -260,9 +262,7 @@ bool ED_operator_outliner_active_no_editobject(bContext *C)
if (ob && ob == obedit) {
return 0;
}
- else {
- return 1;
- }
+ return 1;
}
return 0;
}
@@ -362,23 +362,31 @@ bool ED_operator_object_active_editable(bContext *C)
return ED_operator_object_active_editable_ex(C, ob);
}
+/** Object must be editable and fully local (i.e. not an override). */
+bool ED_operator_object_active_local_editable(bContext *C)
+{
+ Object *ob = ED_object_active_context(C);
+ return ED_operator_object_active_editable_ex(C, ob) && !ID_IS_OVERRIDE_LIBRARY(ob);
+}
+
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)
@@ -761,10 +769,10 @@ static AZone *area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const b
if (az->type == AZONE_AREA) {
break;
}
- else if (az->type == AZONE_REGION) {
+ if (az->type == AZONE_REGION) {
break;
}
- else if (az->type == AZONE_FULLSCREEN) {
+ if (az->type == AZONE_FULLSCREEN) {
rcti click_rect;
fullscreen_click_rcti_init(&click_rect, az->x1, az->y1, az->x2, az->y2);
const bool click_isect = BLI_rcti_isect_pt_v(&click_rect, xy);
@@ -996,14 +1004,13 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
actionzone_exit(op);
return OPERATOR_FINISHED;
}
- else {
- BLI_assert(ELEM(sad->az->type, AZONE_AREA, AZONE_REGION_SCROLL));
- /* add modal handler */
- G.moving |= G_TRANSFORM_WM;
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
+ BLI_assert(ELEM(sad->az->type, AZONE_AREA, AZONE_REGION_SCROLL));
+
+ /* add modal handler */
+ G.moving |= G_TRANSFORM_WM;
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
}
static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
@@ -1391,9 +1398,7 @@ finally:
if (newwin) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static void SCREEN_OT_area_dupli(wmOperatorType *ot)
@@ -1491,21 +1496,21 @@ static void area_move_set_limits(wmWindow *win,
return;
}
/* top edge */
- else if ((area->v2->editflag && area->v3->editflag)) {
+ if ((area->v2->editflag && area->v3->editflag)) {
*smaller = area->v1->vec.y + size_min;
*bigger = area->v1->vec.y + size_max;
*use_bigger_smaller_snap = true;
return;
}
/* right edge */
- else if ((area->v3->editflag && area->v4->editflag)) {
+ if ((area->v3->editflag && area->v4->editflag)) {
*smaller = area->v1->vec.x + size_min;
*bigger = area->v1->vec.x + size_max;
*use_bigger_smaller_snap = true;
return;
}
/* lower edge */
- else if ((area->v4->editflag && area->v1->editflag)) {
+ if ((area->v4->editflag && area->v1->editflag)) {
*smaller = area->v2->vec.y - size_max;
*bigger = area->v2->vec.y - size_min;
*use_bigger_smaller_snap = true;
@@ -1761,7 +1766,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) {
@@ -2045,13 +2050,13 @@ static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *area, ScrArea *sb)
if (sav1 == sbv4 && sav2 == sbv3) { /* area to right of sb = W */
return BKE_screen_find_edge(screen, sav1, sav2);
}
- else if (sav2 == sbv1 && sav3 == sbv4) { /* area to bottom of sb = N */
+ if (sav2 == sbv1 && sav3 == sbv4) { /* area to bottom of sb = N */
return BKE_screen_find_edge(screen, sav2, sav3);
}
- else if (sav3 == sbv2 && sav4 == sbv1) { /* area to left of sb = E */
+ if (sav3 == sbv2 && sav4 == sbv1) { /* area to left of sb = E */
return BKE_screen_find_edge(screen, sav3, sav4);
}
- else if (sav1 == sbv2 && sav4 == sbv3) { /* area on top of sb = S*/
+ if (sav1 == sbv2 && sav4 == sbv3) { /* area on top of sb = S*/
return BKE_screen_find_edge(screen, sav1, sav4);
}
@@ -3027,15 +3032,14 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- else {
- areas_do_frame_follow(C, true);
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ areas_do_frame_follow(C, true);
- WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
- return OPERATOR_FINISHED;
- }
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+
+ return OPERATOR_FINISHED;
}
static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
@@ -3091,17 +3095,16 @@ static int marker_jump_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- else {
- CFRA = closest;
- areas_do_frame_follow(C, true);
+ CFRA = closest;
- DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
+ areas_do_frame_follow(C, true);
- WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
- return OPERATOR_FINISHED;
- }
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+
+ return OPERATOR_FINISHED;
}
static void SCREEN_OT_marker_jump(wmOperatorType *ot)
@@ -3365,10 +3368,8 @@ static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (sad->sa1 == sad->sa2) {
return OPERATOR_PASS_THROUGH;
}
- else {
- if (!area_join_init(C, op, sad->sa1, sad->sa2)) {
- return OPERATOR_CANCELLED;
- }
+ if (!area_join_init(C, op, sad->sa1, sad->sa2)) {
+ return OPERATOR_CANCELLED;
}
}
@@ -3676,9 +3677,7 @@ static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
if (lastop->type->flag & OPTYPE_REGISTER) {
break;
}
- else {
- lastop = lastop->prev;
- }
+ lastop = lastop->prev;
}
if (lastop) {
@@ -4130,12 +4129,6 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
/** \name Region Context Menu Operator (Header/Footer/Navbar)
* \{ */
-static bool screen_region_context_menu_poll(bContext *C)
-{
- ScrArea *area = CTX_wm_area(C);
- return (area && area->spacetype != SPACE_STATUSBAR);
-}
-
void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
ScrArea *area = CTX_wm_area(C);
@@ -4224,15 +4217,35 @@ void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout,
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
}
+static void ed_screens_statusbar_menu_create(uiLayout *layout, void *UNUSED(arg))
+{
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, &RNA_PreferencesView, &U, &ptr);
+ uiItemR(layout, &ptr, "show_statusbar_stats", 0, IFACE_("Scene Statistics"), ICON_NONE);
+ uiItemR(layout, &ptr, "show_statusbar_memory", 0, IFACE_("System Memory"), ICON_NONE);
+ if (GPU_mem_stats_supported()) {
+ uiItemR(layout, &ptr, "show_statusbar_vram", 0, IFACE_("Video Memory"), ICON_NONE);
+ }
+ uiItemR(layout, &ptr, "show_statusbar_version", 0, IFACE_("Blender Version"), ICON_NONE);
+}
+
static int screen_context_menu_invoke(bContext *C,
wmOperator *UNUSED(op),
const wmEvent *UNUSED(event))
{
uiPopupMenu *pup;
uiLayout *layout;
+ const ScrArea *area = CTX_wm_area(C);
const ARegion *region = CTX_wm_region(C);
- if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
+ if (area && area->spacetype == SPACE_STATUSBAR) {
+ pup = UI_popup_menu_begin(C, IFACE_("Status Bar"), ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ ed_screens_statusbar_menu_create(layout, NULL);
+ UI_popup_menu_end(C, pup);
+ }
+ else if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
pup = UI_popup_menu_begin(C, IFACE_("Header"), ICON_NONE);
layout = UI_popup_menu_layout(pup);
ED_screens_header_tools_menu_create(C, layout, NULL);
@@ -4262,7 +4275,6 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot)
ot->idname = "SCREEN_OT_region_context_menu";
/* api callbacks */
- ot->poll = screen_region_context_menu_poll;
ot->invoke = screen_context_menu_invoke;
}
@@ -4273,68 +4285,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 +4356,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;
+ }
+ 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 +4458,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 +4599,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);
}
}
}
@@ -4893,10 +4927,8 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
- return OPERATOR_CANCELLED;
- }
+ BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
+ return OPERATOR_CANCELLED;
}
static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
@@ -4971,10 +5003,8 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
- return OPERATOR_CANCELLED;
- }
+ BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
+ return OPERATOR_CANCELLED;
}
static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot)
@@ -5015,10 +5045,8 @@ static int info_log_show_exec(bContext *C, wmOperator *op)
false) != NULL) {
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
- return OPERATOR_CANCELLED;
- }
+ BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
+ return OPERATOR_CANCELLED;
}
static void SCREEN_OT_info_log_show(struct wmOperatorType *ot)
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index 83ded5b3503..f80c13a7fb7 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -91,10 +91,8 @@ static int screenshot_data_create(bContext *C, wmOperator *op)
return true;
}
- else {
- op->customdata = NULL;
- return false;
- }
+ op->customdata = NULL;
+ return false;
}
static void screenshot_data_free(wmOperator *op)
@@ -207,6 +205,8 @@ static void screenshot_draw(bContext *UNUSED(C), wmOperator *op)
ScreenshotData *scd = op->customdata;
PointerRNA ptr;
+ uiLayoutSetPropSep(layout, true);
+
/* image template */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &scd->im_format, &ptr);
uiTemplateImageSettings(layout, &ptr, false);
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..da39fbcf0ce 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -56,11 +56,11 @@
#include "DEG_depsgraph.h"
-#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
+#include "GPU_texture.h"
#include "UI_resources.h"
@@ -79,7 +79,7 @@
*/
typedef struct TexSnapshot {
- GLuint overlay_texture;
+ GPUTexture *overlay_texture;
int winx;
int winy;
int old_size;
@@ -88,7 +88,7 @@ typedef struct TexSnapshot {
} TexSnapshot;
typedef struct CursorSnapshot {
- GLuint overlay_texture;
+ GPUTexture *overlay_texture;
int size;
int zoom;
int curve_preset;
@@ -102,13 +102,13 @@ static CursorSnapshot cursor_snap = {0};
void paint_cursor_delete_textures(void)
{
if (primary_snap.overlay_texture) {
- glDeleteTextures(1, &primary_snap.overlay_texture);
+ GPU_texture_free(primary_snap.overlay_texture);
}
if (secondary_snap.overlay_texture) {
- glDeleteTextures(1, &secondary_snap.overlay_texture);
+ GPU_texture_free(secondary_snap.overlay_texture);
}
if (cursor_snap.overlay_texture) {
- glDeleteTextures(1, &cursor_snap.overlay_texture);
+ GPU_texture_free(cursor_snap.overlay_texture);
}
memset(&primary_snap, 0, sizeof(TexSnapshot));
@@ -298,15 +298,15 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool prima
size = 512;
}
- if (target->old_size != size) {
+ if (target->old_size != size || target->old_col != col) {
if (target->overlay_texture) {
- glDeleteTextures(1, &target->overlay_texture);
- target->overlay_texture = 0;
+ GPU_texture_free(target->overlay_texture);
+ target->overlay_texture = NULL;
}
-
init = false;
target->old_size = size;
+ target->old_col = col;
}
if (col) {
buffer = MEM_mallocN(sizeof(GLubyte) * size * size * 4, "load_tex");
@@ -347,41 +347,27 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool prima
}
if (!target->overlay_texture) {
- glGenTextures(1, &target->overlay_texture);
+ eGPUTextureFormat format = col ? GPU_RGBA8 : GPU_R8;
+ target->overlay_texture = GPU_texture_create_nD(
+ size, size, 0, 2, buffer, format, GPU_DATA_UNSIGNED_BYTE, 0, false, NULL);
+
+ if (!col) {
+ GPU_texture_bind(target->overlay_texture, 0);
+ GPU_texture_swizzle_set(target->overlay_texture, "rrrr");
+ GPU_texture_unbind(target->overlay_texture);
+ }
}
- }
- else {
- size = target->old_size;
- }
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, target->overlay_texture);
- if (refresh) {
- GLenum format = col ? GL_RGBA : GL_RED;
- GLenum internalformat = col ? GL_RGBA8 : GL_R8;
-
- if (!init || (target->old_col != col)) {
- glTexImage2D(
- GL_TEXTURE_2D, 0, internalformat, size, size, 0, format, GL_UNSIGNED_BYTE, buffer);
- }
- else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, format, GL_UNSIGNED_BYTE, buffer);
+ if (init) {
+ GPU_texture_update(target->overlay_texture, GPU_DATA_UNSIGNED_BYTE, buffer);
}
if (buffer) {
MEM_freeN(buffer);
}
-
- target->old_col = col;
}
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ else {
+ size = target->old_size;
}
BKE_paint_reset_overlay_invalid(invalid);
@@ -459,8 +445,8 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom)
if (cursor_snap.size != size) {
if (cursor_snap.overlay_texture) {
- glDeleteTextures(1, &cursor_snap.overlay_texture);
- cursor_snap.overlay_texture = 0;
+ GPU_texture_free(cursor_snap.overlay_texture);
+ cursor_snap.overlay_texture = NULL;
}
init = false;
@@ -482,34 +468,25 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom)
BLI_task_parallel_range(0, size, &data, load_tex_cursor_task_cb, &settings);
if (!cursor_snap.overlay_texture) {
- glGenTextures(1, &cursor_snap.overlay_texture);
- }
- }
- else {
- size = cursor_snap.size;
- }
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, cursor_snap.overlay_texture);
+ cursor_snap.overlay_texture = GPU_texture_create_nD(
+ size, size, 0, 2, buffer, GPU_R8, GPU_DATA_UNSIGNED_BYTE, 0, false, NULL);
- if (refresh) {
- if (!init) {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size, size, 0, GL_RED, GL_UNSIGNED_BYTE, buffer);
+ GPU_texture_bind(cursor_snap.overlay_texture, 0);
+ GPU_texture_swizzle_set(cursor_snap.overlay_texture, "rrrr");
+ GPU_texture_unbind(cursor_snap.overlay_texture);
}
- else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_RED, GL_UNSIGNED_BYTE, buffer);
+
+ if (init) {
+ GPU_texture_update(cursor_snap.overlay_texture, GPU_DATA_UNSIGNED_BYTE, buffer);
}
if (buffer) {
MEM_freeN(buffer);
}
}
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ else {
+ size = cursor_snap.size;
+ }
cursor_snap.curve_preset = br->curve_preset;
BKE_paint_reset_overlay_invalid(PAINT_OVERLAY_INVALID_CURVE);
@@ -557,11 +534,9 @@ static int project_brush_radius(ViewContext *vc, float radius, const float locat
/* The distance between these points is the size of the projected brush in pixels. */
return len_v2v2(p1, p2);
}
- else {
- /* Assert because the code that sets up the vectors should disallow this. */
- BLI_assert(0);
- return 0;
- }
+ /* Assert because the code that sets up the vectors should disallow this. */
+ BLI_assert(0);
+ return 0;
}
static bool sculpt_get_brush_geometry(bContext *C,
@@ -638,11 +613,8 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
}
if (load_tex(brush, vc, zoom, col, primary)) {
- GPU_blend(true);
-
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glDepthMask(GL_FALSE);
- glDepthFunc(GL_ALWAYS);
+ GPU_color_mask(true, true, true, true);
+ GPU_depth_test(false);
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
GPU_matrix_push();
@@ -709,19 +681,28 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- if (col) {
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
- immUniformColor4f(1.0f, 1.0f, 1.0f, overlay_alpha * 0.01f);
- }
- else {
- GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR);
- immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, overlay_alpha * 0.01f);
+ /* Premultiplied alpha blending. */
+ GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(true);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+
+ float final_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ if (!col) {
+ copy_v3_v3(final_color, U.sculpt_paint_overlay_col);
}
+ mul_v4_fl(final_color, overlay_alpha * 0.01f);
+ immUniformColor4fv(final_color);
- /* Draw textured quad. */
- immUniform1i("image", 0);
+ GPUTexture *texture = (primary) ? primary_snap.overlay_texture :
+ secondary_snap.overlay_texture;
+ eGPUSamplerState state = GPU_SAMPLER_FILTER;
+ state |= (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) ? GPU_SAMPLER_CLAMP_BORDER :
+ GPU_SAMPLER_REPEAT;
+ immBindTextureSampler("image", texture, state);
+
+ /* Draw textured quad. */
immBegin(GPU_PRIM_TRI_FAN, 4);
immAttr2f(texCoord, 0.0f, 0.0f);
immVertex2f(pos, quad.xmin, quad.ymin);
@@ -734,6 +715,9 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
immEnd();
immUnbindProgram();
+
+ GPU_texture_unbind(texture);
+
GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) {
@@ -758,11 +742,9 @@ static bool paint_draw_cursor_overlay(
if (load_tex_cursor(brush, vc, zoom)) {
bool do_pop = false;
float center[2];
- GPU_blend(true);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glDepthMask(GL_FALSE);
- glDepthFunc(GL_ALWAYS);
+ GPU_color_mask(true, true, true, true);
+ GPU_depth_test(false);
if (ups->draw_anchored) {
copy_v2_v2(center, ups->anchored_initial_mouse);
@@ -796,14 +778,17 @@ static bool paint_draw_cursor_overlay(
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR);
+ GPU_blend(true);
- immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, brush->cursor_overlay_alpha * 0.01f);
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
- /* Draw textured quad. */
+ float final_color[4] = {UNPACK3(U.sculpt_paint_overlay_col), 1.0f};
+ mul_v4_fl(final_color, brush->cursor_overlay_alpha * 0.01f);
+ immUniformColor4fv(final_color);
/* Draw textured quad. */
- immUniform1i("image", 0);
+ immBindTextureSampler(
+ "image", cursor_snap.overlay_texture, GPU_SAMPLER_FILTER | GPU_SAMPLER_CLAMP_BORDER);
immBegin(GPU_PRIM_TRI_FAN, 4);
immAttr2f(texCoord, 0.0f, 0.0f);
@@ -816,6 +801,8 @@ static bool paint_draw_cursor_overlay(
immVertex2f(pos, quad.xmin, quad.ymax);
immEnd();
+ GPU_texture_unbind(cursor_snap.overlay_texture);
+
immUnbindProgram();
GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
@@ -877,8 +864,12 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
return alpha_overlay_active;
}
-BLI_INLINE void draw_tri_point(
- uint pos, const float sel_col[4], float pivot_col[4], float *co, float width, bool selected)
+BLI_INLINE void draw_tri_point(uint pos,
+ const float sel_col[4],
+ const float pivot_col[4],
+ float *co,
+ float width,
+ bool selected)
{
immUniformColor4fv(selected ? sel_col : pivot_col);
@@ -907,8 +898,12 @@ BLI_INLINE void draw_tri_point(
immEnd();
}
-BLI_INLINE void draw_rect_point(
- uint pos, const float sel_col[4], float handle_col[4], float *co, float width, bool selected)
+BLI_INLINE void draw_rect_point(uint pos,
+ const float sel_col[4],
+ const float handle_col[4],
+ const float *co,
+ float width,
+ bool selected)
{
immUniformColor4fv(selected ? sel_col : handle_col);
@@ -1441,7 +1436,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) {
@@ -1567,7 +1562,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
}
}
else {
- if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) {
+ if (vc.obact->sculpt->cache &&
+ !SCULPT_stroke_is_first_brush_step_of_symmetry_pass(vc.obact->sculpt->cache)) {
wmViewport(&region->winrct);
/* Draw cached dynamic mesh preview lines. */
@@ -1593,7 +1589,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
}
if (brush->sculpt_tool == SCULPT_TOOL_MULTIPLANE_SCRAPE &&
- brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW && !ss->cache->first_time) {
+ brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW &&
+ !SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
GPU_matrix_push_projection();
ED_view3d_draw_setup_view(wm,
CTX_wm_window(C),
@@ -1611,7 +1608,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
GPU_matrix_pop_projection();
}
- if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && !ss->cache->first_time) {
+ if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
+ !SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
GPU_matrix_push_projection();
ED_view3d_draw_setup_view(CTX_wm_manager(C),
CTX_wm_window(C),
diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c
index 68fd2f15877..74e022bf84f 100644
--- a/source/blender/editors/sculpt_paint/paint_curve.c
+++ b/source/blender/editors/sculpt_paint/paint_curve.c
@@ -142,19 +142,15 @@ static char paintcurve_point_side_index(const BezTriple *bezt,
if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) {
return is_first ? SEL_F1 : SEL_F3;
}
- else if (bezt->f1 & SELECT) {
+ if (bezt->f1 & SELECT) {
return SEL_F1;
}
- else if (bezt->f3 & SELECT) {
+ if (bezt->f3 & SELECT) {
return SEL_F3;
}
- else {
- return fallback;
- }
- }
- else {
- return 0;
+ return fallback;
}
+ return 0;
}
/******************* Operators *********************************/
@@ -491,9 +487,7 @@ static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmE
RNA_int_set_array(op->ptr, "location", loc);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static int paintcurve_select_point_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index e9dcc4a356a..339921fe601 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -70,13 +70,12 @@ static bool is_effected(PartialVisArea area,
if (area == PARTIALVIS_ALL) {
return true;
}
- else if (area == PARTIALVIS_MASKED) {
+ if (area == PARTIALVIS_MASKED) {
return mask > 0.5f;
}
- else {
- bool inside = isect_point_planes_v3(planes, 4, co);
- return ((inside && area == PARTIALVIS_INSIDE) || (!inside && area == PARTIALVIS_OUTSIDE));
- }
+
+ bool inside = isect_point_planes_v3(planes, 4, co);
+ return ((inside && area == PARTIALVIS_INSIDE) || (!inside && area == PARTIALVIS_OUTSIDE));
}
static void partialvis_update_mesh(Object *ob,
@@ -418,9 +417,7 @@ static int hide_show_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (!ELEM(area, PARTIALVIS_ALL, PARTIALVIS_MASKED)) {
return WM_gesture_box_invoke(C, op, event);
}
- else {
- return op->type->exec(C, op);
- }
+ return op->type->exec(C, op);
}
void PAINT_OT_hide_show(struct wmOperatorType *ot)
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 08af3bdd16c..34c9cac67c8 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -74,7 +74,6 @@
#include "RNA_access.h"
#include "RNA_define.h"
-#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -182,7 +181,7 @@ void imapaint_image_update(
int h = imapaintpartial.y2 - imapaintpartial.y1;
if (w && h) {
/* Testing with partial update in uv editor too */
- GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h);
+ BKE_image_update_gputexture(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h);
}
}
}
@@ -1118,7 +1117,6 @@ void PAINT_OT_sample_color(wmOperatorType *ot)
void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob)
{
- bScreen *screen;
Image *ima = NULL;
ImagePaintSettings *imapaint = &scene->toolsettings->imapaint;
@@ -1142,17 +1140,16 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
}
if (ima) {
- for (screen = bmain->screens.first; screen; screen = screen->id.next) {
- ScrArea *area;
- for (area = screen->areabase.first; area; area = area->next) {
- SpaceLink *sl;
- for (sl = area->spacedata.first; sl; sl = sl->next) {
- if (sl->spacetype == SPACE_IMAGE) {
- SpaceImage *sima = (SpaceImage *)sl;
-
- if (!sima->pin) {
- ED_space_image_set(bmain, sima, NULL, ima, true);
- }
+ wmWindowManager *wm = bmain->wm.first;
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ const bScreen *screen = WM_window_get_active_screen(win);
+ for (ScrArea *area = screen->areabase.first; area; area = area->next) {
+ SpaceLink *sl = area->spacedata.first;
+ if (sl->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = (SpaceImage *)sl;
+
+ if (!sima->pin) {
+ ED_space_image_set(bmain, sima, NULL, ima, true);
}
}
}
@@ -1166,9 +1163,9 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint);
if (U.glreslimit != 0) {
- GPU_free_images(bmain);
+ BKE_image_free_all_gputextures(bmain);
}
- GPU_paint_set_mipmap(bmain, 0);
+ BKE_image_paint_set_mipmap(bmain, 0);
toggle_paint_cursor(scene, true);
@@ -1191,9 +1188,9 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob)
ob->mode &= ~OB_MODE_TEXTURE_PAINT;
if (U.glreslimit != 0) {
- GPU_free_images(bmain);
+ BKE_image_free_all_gputextures(bmain);
}
- GPU_paint_set_mipmap(bmain, 1);
+ BKE_image_paint_set_mipmap(bmain, 1);
toggle_paint_cursor(scene, false);
Mesh *me = BKE_mesh_from_object(ob);
@@ -1301,7 +1298,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_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 16ccfaf8286..6d588d1450b 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -57,8 +57,6 @@
#include "UI_view2d.h"
-#include "GPU_draw.h"
-
#include "paint_intern.h"
/* Brush Painting for 2D image editor */
@@ -257,7 +255,7 @@ static ushort *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size
/* update rectangular section of the brush image */
static void brush_painter_mask_imbuf_update(BrushPainter *painter,
ImagePaintTile *tile,
- ushort *tex_mask_old,
+ const ushort *tex_mask_old,
int origx,
int origy,
int w,
@@ -1052,7 +1050,7 @@ static void paint_2d_lift_soften(ImagePaintState *s,
ImagePaintTile *tile,
ImBuf *ibuf,
ImBuf *ibufb,
- int *pos,
+ const int *pos,
const short paint_tile)
{
bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
@@ -1255,7 +1253,7 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint
}
}
-static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos)
+static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, const int *pos)
{
/* note: allocImbuf returns zero'd memory, so regions outside image will
* have zero alpha, and hence not be blended onto the image */
@@ -1784,7 +1782,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final)
if (final) {
if (s->image && !(s->sima && s->sima->lock)) {
- GPU_free_image(s->image);
+ BKE_image_free_gputextures(s->image);
}
/* compositor listener deals with updating */
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 5e3204b6d5a..5991032536b 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -100,8 +100,6 @@
#include "RNA_define.h"
#include "RNA_enum_types.h"
-#include "GPU_draw.h"
-
#include "IMB_colormanagement.h"
//#include "bmesh_tools.h"
@@ -557,12 +555,11 @@ static Image *project_paint_face_paint_image(const ProjPaintState *ps, int tri_i
if (ps->do_stencil_brush) {
return ps->stencil_ima;
}
- else {
- const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
- Material *ma = ps->mat_array[mp->mat_nr];
- TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL;
- return slot ? slot->ima : ps->canvas_ima;
- }
+
+ const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
+ Material *ma = ps->mat_array[mp->mat_nr];
+ TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL;
+ return slot ? slot->ima : ps->canvas_ima;
}
static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int tri_index)
@@ -605,9 +602,7 @@ static int project_bucket_offset_safe(const ProjPaintState *ps, const float proj
if (bucket_index < 0 || bucket_index >= ps->buckets_x * ps->buckets_y) {
return -1;
}
- else {
- return bucket_index;
- }
+ return bucket_index;
}
static float VecZDepthOrtho(
@@ -811,7 +806,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,22 +831,21 @@ 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;
}
- else {
- /* we intersect? - find the exact depth at the point of intersection */
- /* Is this point is occluded by another face? */
- if (is_ortho) {
- if (VecZDepthOrtho(pt, v1, v2, v3, w) < pt[2]) {
- return 2;
- }
+
+ /* we intersect? - find the exact depth at the point of intersection */
+ /* Is this point is occluded by another face? */
+ if (is_ortho) {
+ if (VecZDepthOrtho(pt, v1, v2, v3, w) < pt[2]) {
+ return 2;
}
- else {
- if (VecZDepthPersp(pt, v1, v2, v3, w) < pt[2]) {
- return 2;
- }
+ }
+ else {
+ if (VecZDepthPersp(pt, v1, v2, v3, w) < pt[2]) {
+ return 2;
}
}
return -1;
@@ -977,14 +971,12 @@ static int line_isect_y(const float p1[2], const float p2[2], const float y_leve
*x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff;
return ISECT_TRUE;
}
- else if (p1[1] < y_level && p2[1] > y_level) {
+ if (p1[1] < y_level && p2[1] > y_level) {
/* (p2[1] - p1[1]); */
*x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff;
return ISECT_TRUE;
}
- else {
- return 0;
- }
+ return 0;
}
static int line_isect_x(const float p1[2], const float p2[2], const float x_level, float *y_isect)
@@ -1014,14 +1006,12 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve
*y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff;
return ISECT_TRUE;
}
- else if (p1[0] < x_level && p2[0] > x_level) {
+ if (p1[0] < x_level && p2[0] > x_level) {
/* (p2[0] - p1[0]); */
*y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff;
return ISECT_TRUE;
}
- else {
- return 0;
- }
+ return 0;
}
/* simple func use for comparing UV locations to check if there are seams.
@@ -1204,10 +1194,8 @@ static bool check_seam(const ProjPaintState *ps,
// printf("SEAM (NONE)\n");
return false;
}
- else {
- // printf("SEAM (UV GAP)\n");
- return true;
- }
+ // printf("SEAM (UV GAP)\n");
+ return true;
}
}
}
@@ -1436,7 +1424,7 @@ static void insert_seam_vert_array(const ProjPaintState *ps,
* Be tricky with flags, first 4 bits are #PROJ_FACE_SEAM0 to 4,
* last 4 bits are #PROJ_FACE_NOSEAM0 to 4. `1 << i` - where i is `(0..3)`.
*
- * If we're multithreadng, make sure threads are locked when this is called.
+ * If we're multi-threading, make sure threads are locked when this is called.
*/
static void project_face_seams_init(const ProjPaintState *ps,
MemArena *arena,
@@ -1788,7 +1776,7 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps,
/* outsize the normal limit*/
return 0.0f;
}
- else if (angle_cos < ps->normal_angle_inner__cos) {
+ if (angle_cos < ps->normal_angle_inner__cos) {
mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range;
} /* otherwise no mask normal is needed, we're within the limit */
}
@@ -1805,9 +1793,7 @@ static int project_paint_pixel_sizeof(const short tool)
if ((tool == PAINT_TOOL_CLONE) || (tool == PAINT_TOOL_SMEAR)) {
return sizeof(ProjPixelClone);
}
- else {
- return sizeof(ProjPixel);
- }
+ return sizeof(ProjPixel);
}
static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
@@ -2083,9 +2069,7 @@ static bool line_clip_rect2f(const rctf *cliprect,
copy_v2_v2(l2_clip, l2);
return true;
}
- else {
- return false;
- }
+ return false;
}
copy_v2_v2(l1_clip, l1);
@@ -2094,7 +2078,7 @@ static bool line_clip_rect2f(const rctf *cliprect,
CLAMP(l2_clip[0], rect->xmin, rect->xmax);
return true;
}
- else if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) {
+ if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) {
/* is the line out of range on its X axis? */
if (l1[0] < rect->xmin || l1[0] > rect->xmax) {
return 0;
@@ -2112,9 +2096,7 @@ static bool line_clip_rect2f(const rctf *cliprect,
copy_v2_v2(l2_clip, l2);
return true;
}
- else {
- return false;
- }
+ return false;
}
copy_v2_v2(l1_clip, l1);
@@ -2123,106 +2105,103 @@ static bool line_clip_rect2f(const rctf *cliprect,
CLAMP(l2_clip[1], rect->ymin, rect->ymax);
return true;
}
- else {
- float isect;
- short ok1 = 0;
- short ok2 = 0;
- /* Done with vertical lines */
+ float isect;
+ short ok1 = 0;
+ short ok2 = 0;
- /* are either of the points inside the rectangle ? */
- if (BLI_rctf_isect_pt_v(rect, l1)) {
- copy_v2_v2(l1_clip, l1);
- ok1 = 1;
- }
+ /* Done with vertical lines */
- if (BLI_rctf_isect_pt_v(rect, l2)) {
- copy_v2_v2(l2_clip, l2);
- ok2 = 1;
- }
+ /* are either of the points inside the rectangle ? */
+ if (BLI_rctf_isect_pt_v(rect, l1)) {
+ copy_v2_v2(l1_clip, l1);
+ ok1 = 1;
+ }
- /* line inside rect */
- if (ok1 && ok2) {
- return 1;
- }
+ if (BLI_rctf_isect_pt_v(rect, l2)) {
+ copy_v2_v2(l2_clip, l2);
+ ok2 = 1;
+ }
- /* top/bottom */
- if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= cliprect->xmin) &&
- (isect <= cliprect->xmax)) {
- if (l1[1] < l2[1]) { /* line 1 is outside */
- l1_clip[0] = isect;
- l1_clip[1] = rect->ymin;
- ok1 = 1;
- }
- else {
- l2_clip[0] = isect;
- l2_clip[1] = rect->ymin;
- ok2 = 2;
- }
- }
+ /* line inside rect */
+ if (ok1 && ok2) {
+ return 1;
+ }
- if (ok1 && ok2) {
- return true;
+ /* top/bottom */
+ if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= cliprect->xmin) &&
+ (isect <= cliprect->xmax)) {
+ if (l1[1] < l2[1]) { /* line 1 is outside */
+ l1_clip[0] = isect;
+ l1_clip[1] = rect->ymin;
+ ok1 = 1;
}
-
- if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= cliprect->xmin) &&
- (isect <= cliprect->xmax)) {
- if (l1[1] > l2[1]) { /* line 1 is outside */
- l1_clip[0] = isect;
- l1_clip[1] = rect->ymax;
- ok1 = 1;
- }
- else {
- l2_clip[0] = isect;
- l2_clip[1] = rect->ymax;
- ok2 = 2;
- }
+ else {
+ l2_clip[0] = isect;
+ l2_clip[1] = rect->ymin;
+ ok2 = 2;
}
+ }
- if (ok1 && ok2) {
- return true;
- }
+ if (ok1 && ok2) {
+ return true;
+ }
- /* left/right */
- if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= cliprect->ymin) &&
- (isect <= cliprect->ymax)) {
- if (l1[0] < l2[0]) { /* line 1 is outside */
- l1_clip[0] = rect->xmin;
- l1_clip[1] = isect;
- ok1 = 1;
- }
- else {
- l2_clip[0] = rect->xmin;
- l2_clip[1] = isect;
- ok2 = 2;
- }
+ if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= cliprect->xmin) &&
+ (isect <= cliprect->xmax)) {
+ if (l1[1] > l2[1]) { /* line 1 is outside */
+ l1_clip[0] = isect;
+ l1_clip[1] = rect->ymax;
+ ok1 = 1;
}
-
- if (ok1 && ok2) {
- return true;
+ else {
+ l2_clip[0] = isect;
+ l2_clip[1] = rect->ymax;
+ ok2 = 2;
}
+ }
- if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= cliprect->ymin) &&
- (isect <= cliprect->ymax)) {
- if (l1[0] > l2[0]) { /* line 1 is outside */
- l1_clip[0] = rect->xmax;
- l1_clip[1] = isect;
- ok1 = 1;
- }
- else {
- l2_clip[0] = rect->xmax;
- l2_clip[1] = isect;
- ok2 = 2;
- }
+ if (ok1 && ok2) {
+ return true;
+ }
+
+ /* left/right */
+ if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= cliprect->ymin) &&
+ (isect <= cliprect->ymax)) {
+ if (l1[0] < l2[0]) { /* line 1 is outside */
+ l1_clip[0] = rect->xmin;
+ l1_clip[1] = isect;
+ ok1 = 1;
}
+ else {
+ l2_clip[0] = rect->xmin;
+ l2_clip[1] = isect;
+ ok2 = 2;
+ }
+ }
- if (ok1 && ok2) {
- return true;
+ if (ok1 && ok2) {
+ return true;
+ }
+
+ if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= cliprect->ymin) &&
+ (isect <= cliprect->ymax)) {
+ if (l1[0] > l2[0]) { /* line 1 is outside */
+ l1_clip[0] = rect->xmax;
+ l1_clip[1] = isect;
+ ok1 = 1;
}
else {
- return false;
+ l2_clip[0] = rect->xmax;
+ l2_clip[1] = isect;
+ ok2 = 2;
}
}
+
+ if (ok1 && ok2) {
+ return true;
+ }
+ return false;
}
/**
@@ -2299,7 +2278,7 @@ static bool project_bucket_isect_circle(const float cent[2],
false;
}
/* top left test */
- else if (cent[1] > bucket_bounds->ymax) {
+ if (cent[1] > bucket_bounds->ymax) {
return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymax) <
radius_squared) ?
true :
@@ -2315,7 +2294,7 @@ static bool project_bucket_isect_circle(const float cent[2],
false;
}
/* top right test */
- else if (cent[1] > bucket_bounds->ymax) {
+ if (cent[1] > bucket_bounds->ymax) {
return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymax) <
radius_squared) ?
true :
@@ -2670,7 +2649,8 @@ static void project_bucket_clip_face(const bool is_ortho,
*tot = 4;
return;
}
- else {
+
+ {
/* The Complicated Case!
*
* The 2 cases above are where the face is inside the bucket
@@ -3584,8 +3564,8 @@ static bool project_bucket_face_isect(ProjPaintState *ps,
int bucket_y,
const MLoopTri *lt)
{
- /* TODO - replace this with a tricker method that uses sideofline for all
- * screenCoords's edges against the closest bucket corner */
+ /* TODO - replace this with a trickier method that uses side-of-line for all
+ * #ProjPaintState.screenCoords edges against the closest bucket corner. */
const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)};
rctf bucket_bounds;
float p1[2], p2[2], p3[2], p4[2];
@@ -4209,9 +4189,7 @@ static bool project_paint_check_face_sel(const ProjPaintState *ps,
return ((mp->flag & ME_FACE_SEL) != 0);
}
- else {
- return true;
- }
+ return true;
}
typedef struct {
@@ -5906,8 +5884,6 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int
ps->dither = settings->imapaint.dither;
ps->use_colormanagement = BKE_scene_check_color_management_enabled(CTX_data_scene(C));
-
- return;
}
void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode)
@@ -6146,19 +6122,18 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh");
return OPERATOR_CANCELLED;
}
- else {
- float pos[2] = {0.0, 0.0};
- float lastpos[2] = {0.0, 0.0};
- int a;
- project_paint_op(&ps, lastpos, pos);
+ float pos[2] = {0.0, 0.0};
+ float lastpos[2] = {0.0, 0.0};
+ int a;
- project_image_refresh_tagged(&ps);
+ project_paint_op(&ps, lastpos, pos);
- for (a = 0; a < ps.image_tot; a++) {
- GPU_free_image(ps.projImages[a].ima);
- WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima);
- }
+ project_image_refresh_tagged(&ps);
+
+ for (a = 0; a < ps.image_tot; a++) {
+ BKE_image_free_gputextures(ps.projImages[a].ima);
+ WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima);
}
project_paint_end(&ps);
@@ -6699,9 +6674,7 @@ static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op
if (proj_paint_add_slot(C, op)) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static void get_default_texture_layer_name_for_object(Object *ob,
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..d65f158174f 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -246,7 +246,11 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op))
color = BKE_palette_color_add(palette);
palette->active_color = BLI_listbase_count(&palette->colors) - 1;
- if (ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX)) {
+ if (ELEM(mode,
+ PAINT_MODE_TEXTURE_3D,
+ PAINT_MODE_TEXTURE_2D,
+ PAINT_MODE_VERTEX,
+ PAINT_MODE_SCULPT)) {
copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush));
color->value = 0.0;
}
@@ -304,7 +308,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 +333,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 +367,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 +382,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. */
@@ -689,14 +699,12 @@ static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, co
return br;
}
- else if (brush_orig->toggle_brush) {
+ if (brush_orig->toggle_brush) {
/* if current brush is using the desired tool, try to toggle
* back to the previously selected brush. */
return brush_orig->toggle_brush;
}
- else {
- return NULL;
- }
+ return NULL;
}
static bool brush_generic_tool_set(bContext *C,
@@ -745,9 +753,7 @@ static bool brush_generic_tool_set(bContext *C,
return true;
}
- else {
- return false;
- }
+ return false;
}
static const ePaintMode brush_select_paint_modes[] = {
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 2c6f708d82a..caecc7b708a 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -696,9 +696,7 @@ static float paint_space_stroke_spacing(bContext *C,
if (paint_stroke_use_scene_spacing(brush, mode)) {
return max_ff(0.001f, size_clamp * spacing / 50.f);
}
- else {
- return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
- }
+ return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
}
static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing)
@@ -751,9 +749,7 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor)
if (max == 0.0f) {
return 1.0f;
}
- else {
- return 1.0f / max;
- }
+ return 1.0f / max;
}
static float paint_space_stroke_spacing_variable(bContext *C,
@@ -781,10 +777,9 @@ static float paint_space_stroke_spacing_variable(bContext *C,
return 0.5f * (last_spacing + new_spacing);
}
- else {
- /* no size pressure */
- return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
- }
+
+ /* no size pressure */
+ return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
}
/* For brushes with stroke spacing enabled, moves mouse in steps
@@ -909,7 +904,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 +1462,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_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index c84a3b9cbfc..6c5d6f4ee4e 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -55,9 +55,10 @@
#include "RNA_access.h"
#include "RNA_define.h"
-#include "GPU_glew.h"
+#include "GPU_framebuffer.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
+#include "GPU_texture.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
@@ -246,7 +247,7 @@ static void imapaint_project(float matrix[4][4], const float co[3], float pco[4]
}
static void imapaint_tri_weights(float matrix[4][4],
- const GLint view[4],
+ const int view[4],
const float v1[3],
const float v2[3],
const float v3[3],
@@ -300,7 +301,7 @@ static void imapaint_pick_uv(
int i, findex;
float p[2], w[3], absw, minabsw;
float matrix[4][4], proj[4][4];
- GLint view[4];
+ int view[4];
const eImagePaintMode mode = scene->toolsettings->imapaint.mode;
const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval);
@@ -576,20 +577,16 @@ void paint_sample_color(
}
if (!sample_success) {
- glReadBuffer(GL_FRONT);
- glReadPixels(
- x + region->winrct.xmin, y + region->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col);
- glReadBuffer(GL_BACK);
+ GPU_frontbuffer_read_pixels(
+ x + region->winrct.xmin, y + region->winrct.ymin, 1, 1, 4, GPU_DATA_UNSIGNED_BYTE, &col);
}
else {
return;
}
}
else {
- glReadBuffer(GL_FRONT);
- glReadPixels(
- x + region->winrct.xmin, y + region->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col);
- glReadBuffer(GL_BACK);
+ GPU_frontbuffer_read_pixels(
+ x + region->winrct.xmin, y + region->winrct.ymin, 1, 1, 4, GPU_DATA_UNSIGNED_BYTE, &col);
}
cp = (uchar *)&col;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 6de54b3ae6a..e6b0c2fbd1b 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -137,13 +137,11 @@ static float view_angle_limits_apply_falloff(const struct NormalAnglePrecalc *a,
/* outsize the normal limit */
return false;
}
- else if (angle_cos < a->angle_inner__cos) {
+ if (angle_cos < a->angle_inner__cos) {
*mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range;
return true;
}
- else {
- return true;
- }
+ return true;
}
static bool vwpaint_use_normal(const VPaint *vp)
@@ -405,12 +403,10 @@ static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
if (newval < oldval) {
return MIN2(newval, curval);
}
- else if (newval > oldval) {
+ if (newval > oldval) {
return MAX2(newval, curval);
}
- else {
- return newval;
- }
+ return newval;
}
static float wpaint_undo_lock_relative(
@@ -535,7 +531,7 @@ static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert,
return (lock_weight == 1.0f);
}
- else if (sum_unlock != 0.0f) {
+ if (sum_unlock != 0.0f) {
fac = (1.0f - lock_weight) / sum_unlock;
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
@@ -1101,12 +1097,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 +1421,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 +2167,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/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
index addf7e9f868..d05b1673c9a 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
@@ -115,9 +115,7 @@ static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_set(wmOperatorType *ot)
@@ -182,9 +180,7 @@ static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op))
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
@@ -210,7 +206,7 @@ void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
/** \name Smooth Vertex Colors Operator
* \{ */
-static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag)
+static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag)
{
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const MPoly *mp;
@@ -323,9 +319,7 @@ static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op))
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
@@ -402,9 +396,7 @@ static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
@@ -469,9 +461,7 @@ static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
@@ -509,9 +499,7 @@ static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
@@ -555,9 +543,7 @@ static int vertex_color_levels_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
index c26db3e265b..637ce8193c1 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
@@ -220,7 +220,7 @@ BLI_INLINE uint mcol_lighten(uint col_src, uint col_dst, int fac)
if (fac == 0) {
return col_src;
}
- else if (fac >= 255) {
+ if (fac >= 255) {
return col_dst;
}
@@ -254,7 +254,7 @@ BLI_INLINE uint mcol_darken(uint col_src, uint col_dst, int fac)
if (fac == 0) {
return col_src;
}
- else if (fac >= 255) {
+ if (fac >= 255) {
return col_dst;
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 08ffd8e620e..6965946d2ce 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -272,9 +272,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_weight_sample(wmOperatorType *ot)
@@ -541,9 +539,7 @@ static int weight_paint_set_exec(bContext *C, wmOperator *op)
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void PAINT_OT_weight_set(wmOperatorType *ot)
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 75c88047914..2c110e23ac0 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"
@@ -103,7 +106,7 @@
* This is read-only, for writing use PBVH vertex iterators. There vd.index matches
* the indices used here.
*
- * For multires, the same vertex in multiple grids is counted multiple times, with
+ * For multi-resolution, the same vertex in multiple grids is counted multiple times, with
* different index for each grid. */
void SCULPT_vertex_random_access_init(SculptSession *ss)
@@ -136,9 +139,7 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
return mverts[index].co;
}
- else {
- return ss->mvert[index].co;
- }
+ return ss->mvert[index].co;
}
case PBVH_BMESH:
return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
@@ -153,6 +154,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 +196,23 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
+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);
+}
+
+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;
@@ -356,7 +389,7 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
return true;
}
-bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -514,28 +547,81 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
}
}
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
+static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
{
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- int face_set = -1;
- for (int i = 0; i < ss->pmap[index].count; i++) {
- if (face_set == -1) {
- face_set = abs(ss->face_sets[vert_map->indices[i]]);
+ MeshElemMap *vert_map = &ss->pmap[index];
+ int face_set = -1;
+ for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (face_set == -1) {
+ face_set = abs(ss->face_sets[vert_map->indices[i]]);
+ }
+ else {
+ if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2
+ * in the base mesh are equal.
+ */
+static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2)
+{
+ MeshElemMap *vert_map = &ss->pmap[v1];
+ int p1 = -1, p2 = -1;
+ for (int i = 0; i < ss->pmap[v1].count; i++) {
+ MPoly *p = &ss->mpoly[vert_map->indices[i]];
+ for (int l = 0; l < p->totloop; l++) {
+ MLoop *loop = &ss->mloop[p->loopstart + l];
+ if (loop->v == v2) {
+ if (p1 == -1) {
+ p1 = vert_map->indices[i];
+ break;
}
- else {
- if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
- return false;
- }
+ else if (p2 == -1) {
+ p2 = vert_map->indices[i];
+ break;
}
}
- return true;
+ }
+ }
+
+ if (p1 != -1 && p2 != -1) {
+ return abs(ss->face_sets[p1]) == (ss->face_sets[p2]);
+ }
+ return true;
+}
+
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES: {
+ return sculpt_check_unique_face_set_in_base_mesh(ss, index);
}
case PBVH_BMESH:
return false;
- case PBVH_GRIDS:
- return true;
+ case PBVH_GRIDS: {
+ const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
+ const int grid_index = index / key->grid_area;
+ const int vertex_index = index - grid_index * key->grid_area;
+ const SubdivCCGCoord coord = {.grid_index = grid_index,
+ .x = vertex_index % key->grid_size,
+ .y = vertex_index / key->grid_size};
+ int v1, v2;
+ const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
+ ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
+ switch (adjacency) {
+ case SUBDIV_CCG_ADJACENT_VERTEX:
+ return sculpt_check_unique_face_set_in_base_mesh(ss, v1);
+ case SUBDIV_CCG_ADJACENT_EDGE:
+ return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2);
+ case SUBDIV_CCG_ADJACENT_NONE:
+ return true;
+ }
+ }
}
return false;
}
@@ -633,6 +719,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,
@@ -640,9 +733,9 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
- /* TODO: optimize this. We could fill SculptVertexNeighborIter directly,
+ /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly,
* maybe provide coordinate and mask pointers directly rather than converting
- * back and forth between CCGElem and global index. */
+ * back and forth between #CCGElem and global index. */
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = index / key->grid_area;
const int vertex_index = index - grid_index * key->grid_area;
@@ -665,6 +758,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);
}
@@ -688,47 +788,84 @@ void SCULPT_vertex_neighbors_get(SculptSession *ss,
}
}
-bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index)
+static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, const int index)
+{
+ BLI_assert(ss->vertex_info.boundary);
+ return BLI_BITMAP_TEST(ss->vertex_info.boundary, index);
+}
+
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- const MeshElemMap *vert_map = &ss->pmap[index];
-
- if (vert_map->count <= 1) {
- return false;
- }
-
if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) {
- return false;
- }
-
- for (int i = 0; i < vert_map->count; i++) {
- const MPoly *p = &ss->mpoly[vert_map->indices[i]];
- unsigned f_adj_v[2];
- if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
- int j;
- for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
- if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) {
- return false;
- }
- }
- }
+ return true;
}
- return true;
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, index);
}
case PBVH_BMESH: {
BMVert *v = BM_vert_at_index(ss->bm, index);
return BM_vert_is_boundary(v);
}
- case PBVH_GRIDS:
- return true;
+ case PBVH_GRIDS: {
+ const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
+ const int grid_index = index / key->grid_area;
+ const int vertex_index = index - grid_index * key->grid_area;
+ const SubdivCCGCoord coord = {.grid_index = grid_index,
+ .x = vertex_index % key->grid_size,
+ .y = vertex_index / key->grid_size};
+ int v1, v2;
+ const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
+ ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
+ switch (adjacency) {
+ case SUBDIV_CCG_ADJACENT_VERTEX:
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, v1);
+ case SUBDIV_CCG_ADJACENT_EDGE:
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) &&
+ sculpt_check_boundary_vertex_in_base_mesh(ss, v2);
+ case SUBDIV_CCG_ADJACENT_NONE:
+ return false;
+ }
+ }
}
- return true;
+ return false;
+}
+
+/* Utilities */
+
+/**
+ * Returns true when the step belongs to the stroke that is directly performed by the brush and
+ * not by one of the symmetry passes.
+ */
+bool SCULPT_stroke_is_main_symmetry_pass(StrokeCache *cache)
+{
+ return cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0 &&
+ cache->tile_pass == 0;
+}
+
+/**
+ * Return true only once per stroke on the first symmetry pass, regardless of the symmetry passes
+ * enabled.
+ *
+ * This should be used for functionality that needs to be computed once per stroke of a particular
+ * tool (allocating memory, updating random seeds...).
+ */
+bool SCULPT_stroke_is_first_brush_step(StrokeCache *cache)
+{
+ return cache->first_time && cache->mirror_symmetry_pass == 0 &&
+ cache->radial_symmetry_pass == 0 && cache->tile_pass == 0;
+}
+
+/**
+ * Returns true on the first brush step of each symmetry pass.
+ */
+bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(StrokeCache *cache)
+{
+ return cache->first_time;
}
-/* Utils */
bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3], const char symm)
{
bool is_in_symmetry_area = true;
@@ -821,7 +958,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 +1135,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);
}
@@ -1043,11 +1182,11 @@ typedef enum StrokeFlags {
CLIP_Z = 4,
} 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)
+/**
+ * Initialize a #SculptOrigVertData for accessing original vertex data;
+ * handles #BMesh, #Mesh, and multi-resolution.
+ */
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
BMesh *bm = ss->bm;
@@ -1062,20 +1201,24 @@ 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;
}
}
-/* Initialize a SculptOrigVertData for accessing original vertex data;
- * handles BMesh, mesh, and multires. */
+/**
+ * Initialize a #SculptOrigVertData for accessing original vertex data;
+ * handles #BMesh, #Mesh, and multi-resolution.
+ */
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node)
{
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
- * iterator. */
+/**
+ * Update a #SculptOrigVertData for a particular vertex from the PBVH iterator.
+ */
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter)
{
if (orig_data->unode->type == SCULPT_UNDO_COORDS) {
@@ -1087,6 +1230,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);
@@ -1208,8 +1354,8 @@ static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3
* otherwise.
*
* Factors: some brushes like grab cannot do dynamic topology.
- * Others, like smooth, are better without. Same goes for alt-
- * key smoothing. */
+ * Others, like smooth, are better without.
+ * Same goes for alt-key smoothing. */
bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush)
{
return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) &&
@@ -1247,7 +1393,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 +1411,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;
@@ -1287,9 +1436,10 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
/**
- * 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()
- * by a different thread. See T33787. */
+ * Disable multi-threading 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() by a different thread. See T33787.
+ */
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
@@ -1298,9 +1448,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);
}
@@ -1308,11 +1460,11 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
static void sculpt_extend_redraw_rect_previous(Object *ob, rcti *rect)
{
- /* Expand redraw rect with redraw rect from previous step to
+ /* Expand redraw \a rect with redraw \a rect from previous step to
* prevent partial-redraw issues caused by fast strokes. This is
* needed here (not in sculpt_flush_update) as it was before
* because redraw rectangle should be the same in both of
- * optimized PBVH draw function and 3d view redraw (if not -- some
+ * optimized PBVH draw function and 3d view redraw, if not -- some
* mesh parts could disappear from screen (sergey). */
SculptSession *ss = ob->sculpt;
@@ -1353,8 +1505,8 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *region, Object *ob
paint_calc_redraw_planes(planes, region, ob, &rect);
- /* We will draw this rect, so now we can set it as the previous partial rect.
- * Note that we don't update with the union of previous/current (rect), only with
+ /* We will draw this \a rect, so now we can set it as the previous partial \a rect.
+ * Note that we don't update with the union of previous/current (\a rect), only with
* the current. Thus we avoid the rectangle needlessly growing to include
* all the stroke area. */
ob->sculpt->cache->previous_r = ob->sculpt->cache->current_r;
@@ -1424,9 +1576,7 @@ bool SCULPT_brush_test_sphere(SculptBrushTest *test, const float co[3])
test->dist = sqrtf(distsq);
return true;
}
- else {
- return false;
- }
+ return false;
}
bool SCULPT_brush_test_sphere_sq(SculptBrushTest *test, const float co[3])
@@ -1440,9 +1590,7 @@ bool SCULPT_brush_test_sphere_sq(SculptBrushTest *test, const float co[3])
test->dist = distsq;
return true;
}
- else {
- return false;
- }
+ return false;
}
bool SCULPT_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3])
@@ -1466,9 +1614,7 @@ bool SCULPT_brush_test_circle_sq(SculptBrushTest *test, const float co[3])
test->dist = distsq;
return true;
}
- else {
- return false;
- }
+ return false;
}
bool SCULPT_brush_test_cube(SculptBrushTest *test,
@@ -1478,6 +1624,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;
@@ -1513,10 +1662,8 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test,
test->dist = 0.0f;
return true;
}
- else {
- /* Outside the square. */
- return false;
- }
+ /* Outside the square. */
+ return false;
}
SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss,
@@ -1542,10 +1689,8 @@ const float *SCULPT_brush_frontface_normal_from_falloff_shape(SculptSession *ss,
if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
return ss->cache->sculpt_normal_symm;
}
- else {
- /* PAINT_FALLOFF_SHAPE_TUBE */
- return ss->cache->view_normal;
- }
+ /* PAINT_FALLOFF_SHAPE_TUBE */
+ return ss->cache->view_normal;
}
static float frontface(const Brush *br,
@@ -1567,9 +1712,7 @@ static float frontface(const Brush *br,
}
return dot > 0.0f ? dot : 0.0f;
}
- else {
- return 1.0f;
- }
+ return 1.0f;
}
#if 0
@@ -1629,9 +1772,7 @@ static float calc_overlap(StrokeCache *cache, const char symm, const char axis,
if (distsq <= 4.0f * (cache->radius_squared)) {
return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius));
}
- else {
- return 0.0f;
- }
+ return 0.0f;
}
static float calc_radial_symmetry_feather(Sculpt *sd,
@@ -1669,9 +1810,7 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache)
return 1.0f / overlap;
}
- else {
- return 1.0f;
- }
+ return 1.0f;
}
/** \name Calculate Normal and Center
@@ -1924,7 +2063,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 +2092,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 +2162,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);
@@ -2058,9 +2196,11 @@ static void calc_area_normal_and_center(
/** \} */
-/* Return modified brush strength. Includes the direction of the brush, positive
+/**
+ * Return modified brush strength. Includes the direction of the brush, positive
* values pull vertices, negative values push. Uses tablet pressure and a
- * special multiplier found experimentally to scale the strength factor. */
+ * special multiplier found experimentally to scale the strength factor.
+ */
static float brush_strength(const Sculpt *sd,
const StrokeCache *cache,
const float feather,
@@ -2116,6 +2256,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);
@@ -2273,7 +2418,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
/* Hardness. */
float final_len = len;
- const float hardness = br->hardness;
+ const float hardness = cache->paint_brush.hardness;
float p = len / cache->radius;
if (p < hardness) {
final_len = 0.0f;
@@ -2293,7 +2438,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
/* Paint mask. */
avg *= 1.0f - mask;
- /* Automasking. */
+ /* Auto-masking. */
avg *= SCULPT_automasking_factor_get(ss, vertex_index);
return avg;
@@ -2313,7 +2458,10 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v)
}
float t[3], bb_min[3], bb_max[3];
- if (data->ignore_fully_masked) {
+ if (data->ignore_fully_ineffective) {
+ if (BKE_pbvh_node_fully_hidden_get(node)) {
+ return false;
+ }
if (BKE_pbvh_node_fully_masked_get(node)) {
return false;
}
@@ -2349,7 +2497,7 @@ bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v)
SculptSearchCircleData *data = data_v;
float bb_min[3], bb_max[3];
- if (data->ignore_fully_masked) {
+ if (data->ignore_fully_ineffective) {
if (BKE_pbvh_node_fully_masked_get(node)) {
return false;
}
@@ -2371,7 +2519,9 @@ bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v)
return dist_sq < data->radius_squared || true;
}
-/* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags. */
+/**
+ * Handles clipping against a mirror modifier and #SCULPT_LOCK_X/Y/Z axis flags.
+ */
void SCULPT_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3])
{
for (int i = 0; i < 3; i++) {
@@ -2400,7 +2550,7 @@ static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob,
.sd = sd,
.radius_squared = ss->cursor_radius,
.original = use_original,
- .ignore_fully_masked = false,
+ .ignore_fully_ineffective = false,
.center = NULL,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
@@ -2425,7 +2575,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
.sd = sd,
.radius_squared = square_f(ss->cache->radius * radius_scale),
.original = use_original,
- .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK,
+ .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK,
.center = NULL,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
@@ -2441,7 +2591,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
ss->cursor_radius,
.original = use_original,
.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
- .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK,
+ .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode);
}
@@ -2493,7 +2643,7 @@ static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
cache->normal_weight > 0.0f);
if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0 &&
- (cache->first_time || update_normal)) {
+ (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(cache) || update_normal)) {
calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal);
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
project_plane_v3_v3v3(cache->sculpt_normal, cache->sculpt_normal, cache->view_normal);
@@ -2533,7 +2683,7 @@ static void calc_brush_local_mat(const Brush *brush, Object *ob, float local_mat
float angle, v[3];
float up[3];
- /* Ensure ob->imat is up to date. */
+ /* Ensure `ob->imat` is up to date. */
invert_m4_m4(ob->imat, ob->obmat);
/* Initialize last column of matrix. */
@@ -2563,8 +2713,7 @@ static void calc_brush_local_mat(const Brush *brush, Object *ob, float local_mat
scale_m4_fl(scale, cache->radius);
mul_m4_m4m4(tmat, mat, scale);
- /* Return inverse (for converting from modelspace coords to local
- * area coords). */
+ /* Return inverse (for converting from model-space coords to local area coords). */
invert_m4_m4(local_mat, tmat);
}
@@ -2577,11 +2726,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 +2833,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 +2890,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 +2978,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 +3055,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);
}
@@ -2956,11 +3100,23 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
thread_id);
float current_disp[3];
float current_disp_norm[3];
- float final_disp[3];
- zero_v3(final_disp);
- sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ float final_disp[3] = {0.0f, 0.0f, 0.0f};
+
+ switch (brush->slide_deform_type) {
+ case BRUSH_SLIDE_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SLIDE_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SLIDE_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
+
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];
@@ -2991,21 +3147,49 @@ void SCULPT_relax_vertex(SculptSession *ss,
{
float smooth_pos[3];
float final_disp[3];
- int count = 0;
+ float boundary_normal[3];
+ int avg_count = 0;
+ int neighbor_count = 0;
zero_v3(smooth_pos);
+ zero_v3(boundary_normal);
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ neighbor_count++;
if (!filter_boundary_face_sets ||
(filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) {
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
- count++;
+
+ /* When the vertex to relax is boundary, use only connected boundary vertices for the average
+ * position. */
+ if (is_boundary) {
+ if (SCULPT_vertex_is_boundary(ss, ni.index)) {
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ avg_count++;
+
+ /* Calculate a normal for the constraint plane using the edges of the boundary. */
+ float to_neighbor[3];
+ sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ normalize_v3(to_neighbor);
+ add_v3_v3(boundary_normal, to_neighbor);
+ }
+ }
+ else {
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ avg_count++;
+ }
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (count > 0) {
- mul_v3_fl(smooth_pos, 1.0f / (float)count);
+ /* Don't modify corner vertices. */
+ if (neighbor_count <= 2) {
+ copy_v3_v3(r_final_pos, vd->co);
+ return;
+ }
+
+ if (avg_count > 0) {
+ mul_v3_fl(smooth_pos, 1.0f / (float)avg_count);
}
else {
copy_v3_v3(r_final_pos, vd->co);
@@ -3015,11 +3199,12 @@ void SCULPT_relax_vertex(SculptSession *ss,
float plane[4];
float smooth_closest_plane[3];
float vno[3];
- if (vd->no) {
- normal_short_to_float_v3(vno, vd->no);
+
+ if (is_boundary && avg_count == 2) {
+ normalize_v3_v3(vno, boundary_normal);
}
else {
- copy_v3_v3(vno, vd->fno);
+ SCULPT_vertex_normal_get(ss, vd->index, vno);
}
if (is_zero_v3(vno)) {
@@ -3084,7 +3269,7 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- if (ss->cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
return;
}
@@ -3098,8 +3283,9 @@ 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) {
+ SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
}
@@ -3115,10 +3301,9 @@ static void calc_sculpt_plane(
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
- if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 &&
- ss->cache->tile_pass == 0 &&
- (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_PLANE) ||
- !(brush->flag & BRUSH_ORIGINAL_NORMAL))) {
+ if (SCULPT_stroke_is_main_symmetry_pass(ss->cache) &&
+ (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) ||
+ !(brush->flag & BRUSH_ORIGINAL_PLANE) || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) {
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
copy_v3_v3(r_area_no, ss->cache->true_view_normal);
@@ -3155,7 +3340,8 @@ static void calc_sculpt_plane(
}
/* For area normal. */
- if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
+ if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
+ (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
}
else {
@@ -3163,7 +3349,8 @@ static void calc_sculpt_plane(
}
/* For flatten center. */
- if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_PLANE)) {
+ if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
+ (brush->flag & BRUSH_ORIGINAL_PLANE)) {
copy_v3_v3(r_area_co, ss->cache->last_center);
}
else {
@@ -3308,7 +3495,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);
}
@@ -3352,7 +3539,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
float disp_center[3];
float x_disp[3];
float z_disp[3];
- /* Calcualte displacement from the vertex to the brush center. */
+ /* Calculate displacement from the vertex to the brush center. */
sub_v3_v3v3(disp_center, test.location, vd.co);
/* Project the displacement into the X vector (aligned to the stroke). */
@@ -3361,8 +3548,8 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
/* Project the displacement into the Z vector (aligned to the surface normal). */
mul_v3_v3fl(z_disp, z_object_space, dot_v3v3(disp_center, z_object_space));
- /* Add the two projected vectors to calculate the final displacement. The Y component is
- * removed */
+ /* Add the two projected vectors to calculate the final displacement.
+ * The Y component is removed. */
add_v3_v3v3(disp_center, x_disp, z_disp);
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
@@ -3390,7 +3577,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co);
/* delay the first daub because grab delta is not setup */
- if (ss->cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
return;
}
@@ -3398,7 +3585,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
return;
}
- /* Init mat */
+ /* Initialize `mat`. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
cross_v3_v3v3(mat[1], area_no, mat[0]);
@@ -3422,7 +3609,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 +3682,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 +3791,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);
}
@@ -3668,10 +3855,9 @@ void SCULPT_calc_brush_plane(
zero_v3(r_area_co);
zero_v3(r_area_no);
- if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 &&
- ss->cache->tile_pass == 0 &&
- (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_PLANE) ||
- !(brush->flag & BRUSH_ORIGINAL_NORMAL))) {
+ if (SCULPT_stroke_is_main_symmetry_pass(ss->cache) &&
+ (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) ||
+ !(brush->flag & BRUSH_ORIGINAL_PLANE) || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) {
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
copy_v3_v3(r_area_no, ss->cache->true_view_normal);
@@ -3702,13 +3888,14 @@ void SCULPT_calc_brush_plane(
}
/* For flatten center. */
- /* fFlatten center has not been calculated yet if we are not using the area normal. */
+ /* Flatten center has not been calculated yet if we are not using the area normal. */
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) {
calc_area_center(sd, ob, nodes, totnode, r_area_co);
}
/* For area normal. */
- if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
+ if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
+ (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
}
else {
@@ -3716,7 +3903,8 @@ void SCULPT_calc_brush_plane(
}
/* For flatten center. */
- if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_PLANE)) {
+ if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
+ (brush->flag & BRUSH_ORIGINAL_PLANE)) {
copy_v3_v3(r_area_co, ss->cache->last_center);
}
else {
@@ -3811,7 +3999,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 +4121,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 +4194,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 +4268,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 +4281,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,15 +4311,15 @@ 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];
}
- /* When using persistent base, the layer brush Ctrl invert mode resets the height of the
- * layer to 0. This makes possible to clean edges of previously added layers on top of the
- * base. */
+ /* When using persistent base, the layer brush (holding Control) invert mode resets the
+ * height of the layer to 0. This makes possible to clean edges of previously added layers
+ * on top of the base. */
/* The main direction of the layers is inverted using the regular brush strength with the
* brush direction property. */
if (use_persistent_base && ss->cache->invert) {
@@ -4153,9 +4341,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 +4384,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 +4451,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 +4576,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 +4727,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 +4758,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);
}
@@ -4665,7 +4853,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
}
/* Delay the first daub because grab delta is not setup. */
- if (ss->cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
return;
}
@@ -4677,20 +4865,37 @@ 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);
- /* Init brush local space matrix. */
+ /* 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);
+
+ /* Initialize brush local-space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
cross_v3_v3v3(mat[1], area_no, mat[0]);
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 +4909,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 +5003,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 +5096,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);
}
@@ -5011,14 +5216,14 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
}
/* Delay the first daub because grab delta is not setup. */
- if (ss->cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
ss->cache->clay_thumb_front_angle = 0.0f;
return;
}
/* Simulate the clay accumulation by increasing the plane angle as more samples are added to the
* stroke. */
- if (ss->cache->mirror_symmetry_pass == 0) {
+ if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
ss->cache->clay_thumb_front_angle += 0.8f;
CLAMP(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f);
}
@@ -5033,7 +5238,7 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp);
- /* Init brush local space matrix. */
+ /* Initialize brush local-space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
cross_v3_v3v3(mat[1], area_no, mat[0]);
@@ -5064,7 +5269,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 +5341,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 +5460,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 +5487,21 @@ 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) {
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return;
+ }
+ }
+
+ if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ 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
@@ -5297,9 +5516,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
SculptSearchSphereData data = {
.ss = ss,
.sd = sd,
- .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)),
+ .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)),
.original = false,
- .ignore_fully_masked = false,
+ .ignore_fully_ineffective = false,
.center = ss->cache->initial_location,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
@@ -5320,9 +5539,16 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
* vertices and uses regular coords undo. */
/* It also assigns the paint_face_set here as it needs to be done regardless of the stroke type
* 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);
+ if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS &&
+ SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) {
+
+ /* Dynamic-topology 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 +5571,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)) {
@@ -5356,14 +5582,13 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
update_brush_local_mat(sd, ob);
}
- if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
SCULPT_automasking_init(sd, ob);
}
}
- if (brush->sculpt_tool == SCULPT_TOOL_POSE && ss->cache->first_time &&
- ss->cache->mirror_symmetry_pass == 0) {
+ if (brush->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) {
SCULPT_pose_brush_init(sd, ob, ss, brush);
}
@@ -5464,6 +5689,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,20 +5833,22 @@ 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);
}
MEM_SAFE_FREE(nodes);
}
-/*Copy the modified vertices from bvh to the active key. */
+/**
+ * Copy the modified vertices from the #PBVH to the active key.
+ */
static void sculpt_update_keyblock(Object *ob)
{
SculptSession *ss = ob->sculpt;
float(*vertCos)[3];
- /* Keyblock update happens after handling deformation caused by modifiers,
+ /* Key-block update happens after handling deformation caused by modifiers,
* so ss->orig_cos would be updated with new stroke. */
if (ss->orig_cos) {
vertCos = ss->orig_cos;
@@ -5690,7 +5923,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) {
@@ -5702,7 +5935,7 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
/* Modifiers could depend on mesh normals, so we should update them.
* Note, then if sculpting happens on locked key, normals should be re-calculate after applying
- * coords from keyblock on base mesh. */
+ * coords from key-block on base mesh. */
BKE_mesh_calc_normals(me);
}
else if (ss->shapekey_active) {
@@ -5710,8 +5943,10 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
}
}
-/* Flip all the editdata across the axis/axes specified by symm. Used to
- * calculate multiple modifications to the mesh when symmetry is enabled. */
+/**
+ * Flip all the edit-data across the axis/axes specified by \a symm.
+ * Used to calculate multiple modifications to the mesh when symmetry is enabled.
+ */
void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,
const char symm,
const char axis,
@@ -5841,9 +6076,10 @@ static void do_radial_symmetry(Sculpt *sd,
}
}
-/* Noise texture gives different values for the same input coord; this
- * can tear a multires mesh during sculpting so do a stitch in this
- * case. */
+/**
+ * Noise texture gives different values for the same input coord; this
+ * can tear a multi-resolution mesh during sculpting so do a stitch in this case.
+ */
static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
@@ -5870,7 +6106,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd,
cache->bstrength = brush_strength(sd, cache, feather, ups);
cache->symmetry = symm;
- /* symm is a bit combination of XYZ -
+ /* `symm` is a bit combination of XYZ -
* 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (int i = 0; i <= symm; i++) {
if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
@@ -5917,6 +6153,14 @@ bool SCULPT_mode_poll(bContext *C)
return ob && ob->mode & OB_MODE_SCULPT;
}
+bool SCULPT_vertex_colors_poll(bContext *C)
+{
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return false;
+ }
+ return SCULPT_mode_poll(C);
+}
+
bool SCULPT_mode_poll_view3d(bContext *C)
{
return (SCULPT_mode_poll(C) && CTX_wm_region_view3d(C));
@@ -5991,6 +6235,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 +6253,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 +6368,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 +6519,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,
@@ -6280,7 +6534,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
sculpt_brush_use_topology_rake(ss, brush)) {
float grab_location[3], imat[4][4], delta[3], loc[3];
- if (cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
if (tool == SCULPT_TOOL_GRAB && brush->flag & BRUSH_GRAB_ACTIVE_VERTEX) {
copy_v3_v3(cache->orig_grab_location, SCULPT_active_vertex_co_get(ss));
}
@@ -6297,7 +6551,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mouse, grab_location);
/* Compute delta to move verts by. */
- if (!cache->first_time) {
+ if (!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
if (sculpt_needs_delta_from_anchored_origin(brush)) {
sub_v3_v3v3(delta, grab_location, cache->old_grab_location);
invert_m4_m4(imat, ob->obmat);
@@ -6361,7 +6615,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
invert_m4_m4(imat, ob->obmat);
mul_mat3_m4_v3(imat, grab_location);
- if (cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
copy_v3_v3(cache->rake_data.follow_co, grab_location);
}
@@ -6406,6 +6660,50 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
}
}
+static void sculpt_update_cache_paint_variants(StrokeCache *cache, const Brush *brush)
+{
+ cache->paint_brush.hardness = brush->hardness;
+ if (brush->paint_flags & BRUSH_PAINT_HARDNESS_PRESSURE) {
+ cache->paint_brush.hardness *= brush->paint_flags & BRUSH_PAINT_HARDNESS_PRESSURE_INVERT ?
+ 1.0f - cache->pressure :
+ cache->pressure;
+ }
+
+ cache->paint_brush.flow = brush->flow;
+ if (brush->paint_flags & BRUSH_PAINT_FLOW_PRESSURE) {
+ cache->paint_brush.flow *= brush->paint_flags & BRUSH_PAINT_FLOW_PRESSURE_INVERT ?
+ 1.0f - cache->pressure :
+ cache->pressure;
+ }
+
+ cache->paint_brush.wet_mix = brush->wet_mix;
+ if (brush->paint_flags & BRUSH_PAINT_WET_MIX_PRESSURE) {
+ cache->paint_brush.wet_mix *= brush->paint_flags & BRUSH_PAINT_WET_MIX_PRESSURE_INVERT ?
+ 1.0f - cache->pressure :
+ cache->pressure;
+
+ /* This makes wet mix more sensible in higher values, which allows to create brushes that have
+ * a wider pressure range were they only blend colors without applying too much of the brush
+ * color. */
+ cache->paint_brush.wet_mix = 1.0f - pow2f(1.0f - cache->paint_brush.wet_mix);
+ }
+
+ cache->paint_brush.wet_persistence = brush->wet_persistence;
+ if (brush->paint_flags & BRUSH_PAINT_WET_PERSISTENCE_PRESSURE) {
+ cache->paint_brush.wet_persistence = brush->paint_flags &
+ BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT ?
+ 1.0f - cache->pressure :
+ cache->pressure;
+ }
+
+ cache->paint_brush.density = brush->density;
+ if (brush->paint_flags & BRUSH_PAINT_DENSITY_PRESSURE) {
+ cache->paint_brush.density = brush->paint_flags & BRUSH_PAINT_DENSITY_PRESSURE_INVERT ?
+ 1.0f - cache->pressure :
+ cache->pressure;
+ }
+}
+
/* Initialize the stroke cache variants from operator properties. */
static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, PointerRNA *ptr)
{
@@ -6415,7 +6713,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
StrokeCache *cache = ss->cache;
Brush *brush = BKE_paint_brush(&sd->paint);
- if (cache->first_time ||
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) ||
!((brush->flag & BRUSH_ANCHORED) || (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) ||
(brush->sculpt_tool == SCULPT_TOOL_ROTATE) || SCULPT_is_cloth_deform_brush(brush))) {
RNA_float_get_array(ptr, "location", cache->true_location);
@@ -6433,7 +6731,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
}
/* Truly temporary data that isn't stored in properties. */
- if (cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
if (!BKE_brush_use_locked_size(scene, brush)) {
cache->initial_radius = paint_calc_object_space_radius(
cache->vc, cache->true_location, BKE_brush_size_get(scene, brush));
@@ -6446,7 +6744,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
/* Clay stabilized pressure. */
if (brush->sculpt_tool == SCULPT_TOOL_CLAY_THUMB) {
- if (ss->cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
for (int i = 0; i < SCULPT_CLAY_STABILIZER_LEN; i++) {
ss->cache->clay_pressure_stabilizer[i] = 0.0f;
}
@@ -6472,6 +6770,8 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
cache->dyntopo_pixel_radius = ups->initial_pixel_radius;
}
+ sculpt_update_cache_paint_variants(cache, brush);
+
cache->radius_squared = cache->radius * cache->radius;
if (brush->flag & BRUSH_ANCHORED) {
@@ -6519,7 +6819,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 +6833,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);
}
}
@@ -6870,14 +7170,13 @@ static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession
static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
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 +7191,16 @@ 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);
+
+ if (needs_colors) {
+ BKE_sculpt_color_layer_create_if_needed(ob);
+ }
+
+ /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
+ * earlier steps modifying the data. */
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors);
}
static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
@@ -6900,11 +7208,16 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ /* For the cloth brush it makes more sense to not restore the mesh state to keep running the
+ * simulation from the previous state. */
+ if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
+ return;
+ }
+
/* Restore the mesh before continuing with anchored stroke. */
if ((brush->flag & BRUSH_ANCHORED) ||
((brush->sculpt_tool == SCULPT_TOOL_GRAB ||
- brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM ||
- brush->sculpt_tool == SCULPT_TOOL_CLOTH) &&
+ brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) &&
BKE_brush_use_size_pressure(brush)) ||
(brush->flag & BRUSH_DRAG_DOT)) {
@@ -7035,12 +7348,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);
}
@@ -7089,9 +7409,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
return true;
}
- else {
- return false;
- }
+ return false;
}
static void sculpt_stroke_update_step(bContext *C,
@@ -7155,6 +7473,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 +7513,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 +7533,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 +7672,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 +7874,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 +8160,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 +8212,754 @@ 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_vertex_colors_poll;
+ ot->exec = vertex_to_loop_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && 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_vertex_colors_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);
+
+ 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_vertex_colors_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 minimum 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
+ * (without 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_boundary_info_ensure(Object *object)
+{
+ SculptSession *ss = object->sculpt;
+ if (ss->vertex_info.boundary) {
+ return;
+ }
+
+ Mesh *base_mesh = BKE_mesh_from_object(object);
+ ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
+ int *adjacent_faces_edge_count = MEM_calloc_arrayN(
+ base_mesh->totedge, sizeof(int), "Adjacent face edge count");
+
+ for (int p = 0; p < base_mesh->totpoly; p++) {
+ MPoly *poly = &base_mesh->mpoly[p];
+ for (int l = 0; l < poly->totloop; l++) {
+ MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
+ adjacent_faces_edge_count[loop->e]++;
+ }
+ }
+
+ for (int e = 0; e < base_mesh->totedge; e++) {
+ if (adjacent_faces_edge_count[e] < 2) {
+ MEdge *edge = &base_mesh->medge[e];
+ BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
+ BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
+ }
+ }
+
+ MEM_freeN(adjacent_faces_edge_count);
+}
+
+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);
+}
+
+/**
+ * #sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the
+ * mask based on the difference between two colors (the active color and the color of any other
+ * vertex). Ideally, a threshold of 0 should mask only the colors that are equal to the active
+ * color and threshold of 1 should mask all colors. In order to avoid artifacts and produce softer
+ * falloffs in the mask, the MASK_BY_COLOR_SLOPE defines the size of the transition values between
+ * masked and unmasked vertices. The smaller this value is, the sharper the generated mask is going
+ * to be.
+ */
+#define MASK_BY_COLOR_SLOPE 0.25f
+
+static float sculpt_mask_by_color_delta_get(const float *color_a,
+ const float *color_b,
+ const float threshold,
+ const bool invert)
+{
+ float len = len_v3v3(color_a, color_b);
+ /* Normalize len to the (0, 1) range. */
+ len = len / M_SQRT3;
+
+ if (len < threshold - MASK_BY_COLOR_SLOPE) {
+ len = 1.0f;
+ }
+ else if (len >= threshold) {
+ len = 0.0f;
+ }
+ else {
+ len = (-len + threshold) / MASK_BY_COLOR_SLOPE;
+ }
+
+ if (invert) {
+ return 1.0f - len;
+ }
+ return len;
+}
+
+static float sculpt_mask_by_color_final_mask_get(const float current_mask,
+ const float new_mask,
+ const bool invert,
+ const bool preserve_mask)
+{
+ if (preserve_mask) {
+ if (invert) {
+ return min_ff(current_mask, new_mask);
+ }
+ return max_ff(current_mask, new_mask);
+ }
+ return new_mask;
+}
+
+typedef struct MaskByColorContiguousFloodFillData {
+ float threshold;
+ bool invert;
+ float *new_mask;
+ float initial_color[3];
+} MaskByColorContiguousFloodFillData;
+
+static void do_mask_by_color_contiguous_update_nodes_cb(
+ void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
+ bool update_node = false;
+
+ const bool invert = data->mask_by_color_invert;
+ const bool preserve_mask = data->mask_by_color_preserve_mask;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ const float current_mask = *vd.mask;
+ const float new_mask = data->mask_by_color_floodfill[vd.index];
+ *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
+ if (current_mask != *vd.mask) {
+ update_node = true;
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ if (update_node) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ }
+}
+
+static bool sculpt_mask_by_color_contiguous_floodfill_cb(
+ SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+{
+ MaskByColorContiguousFloodFillData *data = userdata;
+ const float *current_color = SCULPT_vertex_color_get(ss, to_v);
+ float new_vertex_mask = sculpt_mask_by_color_delta_get(
+ current_color, data->initial_color, data->threshold, data->invert);
+ data->new_mask[to_v] = new_vertex_mask;
+
+ if (is_duplicate) {
+ data->new_mask[to_v] = data->new_mask[from_v];
+ }
+
+ float len = len_v3v3(current_color, data->initial_color);
+ len = len / M_SQRT3;
+ return len <= data->threshold;
+}
+
+static void sculpt_mask_by_color_contiguous(Object *object,
+ const int vertex,
+ const float threshold,
+ const bool invert,
+ const bool preserve_mask)
+{
+ SculptSession *ss = object->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ float *new_mask = MEM_calloc_arrayN(totvert, sizeof(float), "new mask");
+
+ if (invert) {
+ for (int i = 0; i < totvert; i++) {
+ new_mask[i] = 1.0f;
+ }
+ }
+
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ SCULPT_floodfill_add_initial(&flood, vertex);
+
+ MaskByColorContiguousFloodFillData ffd;
+ ffd.threshold = threshold;
+ ffd.invert = invert;
+ ffd.new_mask = new_mask;
+ copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex));
+
+ SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd);
+ SCULPT_floodfill_free(&flood);
+
+ int totnode;
+ PBVHNode **nodes;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .ob = object,
+ .nodes = nodes,
+ .mask_by_color_floodfill = new_mask,
+ .mask_by_color_vertex = vertex,
+ .mask_by_color_threshold = threshold,
+ .mask_by_color_invert = invert,
+ .mask_by_color_preserve_mask = preserve_mask,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_mask_by_color_contiguous_update_nodes_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+
+ MEM_freeN(new_mask);
+}
+
+static void do_mask_by_color_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
+ bool update_node = false;
+
+ const float threshold = data->mask_by_color_threshold;
+ const bool invert = data->mask_by_color_invert;
+ const bool preserve_mask = data->mask_by_color_preserve_mask;
+ const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ const float current_mask = *vd.mask;
+ const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert);
+ *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
+
+ if (current_mask != *vd.mask) {
+ update_node = true;
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ if (update_node) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ }
+}
+
+static void sculpt_mask_by_color_full_mesh(Object *object,
+ const int vertex,
+ const float threshold,
+ const bool invert,
+ const bool preserve_mask)
+{
+ SculptSession *ss = object->sculpt;
+
+ int totnode;
+ PBVHNode **nodes;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .ob = object,
+ .nodes = nodes,
+ .mask_by_color_vertex = vertex,
+ .mask_by_color_threshold = threshold,
+ .mask_by_color_invert = invert,
+ .mask_by_color_preserve_mask = preserve_mask,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_mask_by_color_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+}
+
+static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+
+ /* Color data is not available in Multires. */
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!ss->vcol) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_vertex_random_access_init(ss);
+
+ /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
+ * so it needs to be updated here. */
+ SculptCursorGeometryInfo sgi;
+ float mouse[2];
+ mouse[0] = event->mval[0];
+ mouse[1] = event->mval[1];
+ SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+
+ SCULPT_undo_push_begin("Mask by color");
+
+ const int active_vertex = SCULPT_active_vertex_get(ss);
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const bool invert = RNA_boolean_get(op->ptr, "invert");
+ const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
+
+ if (RNA_boolean_get(op->ptr, "contiguous")) {
+ sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask);
+ }
+ else {
+ sculpt_mask_by_color_full_mesh(ob, active_vertex, threshold, invert, preserve_mask);
+ }
+
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
+ SCULPT_undo_push_end();
+
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Mask By Color";
+ ot->idname = "SCULPT_OT_mask_by_color";
+ ot->description = "Creates a mask based on the sculpt vertex colors";
+
+ /* api callbacks */
+ ot->invoke = sculpt_mask_by_color_invoke;
+ ot->poll = SCULPT_vertex_colors_poll;
+
+ ot->flag = OPTYPE_REGISTER;
+
+ ot->prop = RNA_def_boolean(
+ ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas");
+
+ ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
+ ot->prop = RNA_def_boolean(
+ ot->srna,
+ "preserve_previous_mask",
+ false,
+ "Preserve Previous Mask",
+ "Preserve the previous mask and add or substract the new one generated by the colors");
+
+ RNA_def_float(ot->srna,
+ "threshold",
+ 0.35f,
+ 0.0f,
+ 1.0f,
+ "Threshold",
+ "How much changes in color affect the mask generation",
+ 0.0f,
+ 1.0f);
+}
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -7908,4 +8982,10 @@ 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);
+ WM_operatortype_append(SCULPT_OT_mask_by_color);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c
index bfa657147fd..c475259623a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -89,21 +89,65 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
return false;
}
-float SCULPT_automasking_factor_get(SculptSession *ss, int vert)
+static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
{
- if (ss->cache && ss->cache->automask) {
- return ss->cache->automask[vert];
+ return sculpt->automasking_flags | brush->automasking_flags;
+}
+
+static bool SCULPT_automasking_needs_cache(const Sculpt *sd, const Brush *brush)
+{
+
+ const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
+ if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+ return true;
+ }
+ if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
+ return brush->automasking_boundary_edges_propagation_steps != 1;
}
- else {
+ if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
+ return brush->automasking_boundary_edges_propagation_steps != 1;
+ }
+ return false;
+}
+
+float SCULPT_automasking_factor_get(SculptSession *ss, int vert)
+{
+ if (!ss->cache) {
return 1.0f;
}
+ /* If the cache is initialized with valid info, use the cache. This is used when the
+ * automasking information can't be computed in real time per vertex and needs to be
+ * initialized for the whole mesh when the stroke starts. */
+ if (ss->cache->automask_factor) {
+ return ss->cache->automask_factor[vert];
+ }
+
+ if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
+ if (!SCULPT_vertex_has_face_set(ss, vert, ss->cache->automask_settings.initial_face_set)) {
+ return 0.0f;
+ }
+ }
+
+ if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
+ if (SCULPT_vertex_is_boundary(ss, vert)) {
+ return 0.0f;
+ }
+ }
+
+ if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
+ if (!SCULPT_vertex_has_unique_face_set(ss, vert)) {
+ return 0.0f;
+ }
+ }
+
+ return 1.0f;
}
void SCULPT_automasking_end(Object *ob)
{
SculptSession *ss = ob->sculpt;
- if (ss->cache && ss->cache->automask) {
- MEM_freeN(ss->cache->automask);
+ if (ss->cache && ss->cache->automask_factor) {
+ MEM_freeN(ss->cache->automask_factor);
}
}
@@ -129,11 +173,12 @@ typedef struct AutomaskFloodFillData {
} AutomaskFloodFillData;
static bool automask_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata)
+ SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
{
AutomaskFloodFillData *data = userdata;
data->automask_factor[to_v] = 1.0f;
+ data->automask_factor[from_v] = 1.0f;
return (!data->use_radius ||
SCULPT_is_vertex_inside_brush_radius_symm(
SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
@@ -155,7 +200,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
- ss->cache->automask[i] = 0.0f;
+ ss->cache->automask_factor[i] = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
@@ -211,7 +256,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
{
SculptSession *ss = ob->sculpt;
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
+ if (!ss->pmap) {
BLI_assert(!"Boundary Edges masking: pmap missing");
return NULL;
}
@@ -223,7 +268,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
- if (!SCULPT_vertex_is_boundary(ss, i)) {
+ if (SCULPT_vertex_is_boundary(ss, i)) {
edge_distance[i] = 0;
}
break;
@@ -261,6 +306,14 @@ float *SCULPT_boundary_automasking_init(Object *ob,
return automask_factor;
}
+static void SCULPT_stroke_automasking_settings_update(SculptSession *ss, Sculpt *sd, Brush *brush)
+{
+ BLI_assert(ss->cache);
+
+ ss->cache->automask_settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
+ ss->cache->automask_settings.initial_face_set = SCULPT_active_face_set_get(ss);
+}
+
void SCULPT_automasking_init(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
@@ -271,20 +324,26 @@ void SCULPT_automasking_init(Sculpt *sd, Object *ob)
return;
}
- ss->cache->automask = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss),
- "automask_factor");
+ SCULPT_stroke_automasking_settings_update(ss, sd, brush);
+ SCULPT_boundary_info_ensure(ob);
+
+ if (!SCULPT_automasking_needs_cache(sd, brush)) {
+ return;
+ }
+
+ ss->cache->automask_factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
for (int i = 0; i < totvert; i++) {
- ss->cache->automask[i] = 1.0f;
+ ss->cache->automask_factor[i] = 1.0f;
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_init(ss);
- SCULPT_topology_automasking_init(sd, ob, ss->cache->automask);
+ SCULPT_topology_automasking_init(sd, ob, ss->cache->automask_factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_init(ss);
- sculpt_face_sets_automasking_init(sd, ob, ss->cache->automask);
+ sculpt_face_sets_automasking_init(sd, ob, ss->cache->automask_factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
@@ -292,13 +351,13 @@ void SCULPT_automasking_init(Sculpt *sd, Object *ob)
SCULPT_boundary_automasking_init(ob,
AUTOMASK_INIT_BOUNDARY_EDGES,
brush->automasking_boundary_edges_propagation_steps,
- ss->cache->automask);
+ ss->cache->automask_factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
SCULPT_vertex_random_access_init(ss);
SCULPT_boundary_automasking_init(ob,
AUTOMASK_INIT_BOUNDARY_FACE_SETS,
brush->automasking_boundary_edges_propagation_steps,
- ss->cache->automask);
+ ss->cache->automask_factor);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 3203282c30c..39cab883100 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"
@@ -85,7 +85,6 @@
#include "RNA_access.h"
#include "RNA_define.h"
-#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
@@ -106,19 +105,15 @@
#define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024
#define CLOTH_SIMULATION_TIME_STEP 0.01f
-static void cloth_brush_add_length_constraint(SculptSession *ss,
- SculptClothSimulation *cloth_sim,
- const int v1,
- const int v2)
+static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim,
+ const int v1,
+ const int v2)
{
- cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v1 = v1;
- cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v2 = v2;
- cloth_sim->length_constraints[cloth_sim->tot_length_constraints].length = len_v3v3(
- SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2));
-
- cloth_sim->tot_length_constraints++;
+ return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2);
+}
- /* Reallocation if the array capacity is exceeded. */
+static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim)
+{
if (cloth_sim->tot_length_constraints >= cloth_sim->capacity_length_constraints) {
cloth_sim->capacity_length_constraints += CLOTH_LENGTH_CONSTRAINTS_BLOCK;
cloth_sim->length_constraints = MEM_reallocN_id(cloth_sim->length_constraints,
@@ -128,18 +123,110 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
}
}
+static void cloth_brush_add_length_constraint(SculptSession *ss,
+ SculptClothSimulation *cloth_sim,
+ const int v1,
+ const int v2,
+ const bool use_persistent)
+{
+ SculptClothLengthConstraint *length_constraint =
+ &cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
+
+ length_constraint->elem_index_a = v1;
+ length_constraint->elem_index_b = v2;
+
+ length_constraint->elem_position_a = cloth_sim->pos[v1];
+ length_constraint->elem_position_b = cloth_sim->pos[v2];
+
+ if (use_persistent) {
+ length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1),
+ SCULPT_vertex_persistent_co_get(ss, v2));
+ }
+ else {
+ length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1),
+ SCULPT_vertex_co_get(ss, v2));
+ }
+ length_constraint->strength = 1.0f;
+
+ cloth_sim->tot_length_constraints++;
+
+ /* Reallocation if the array capacity is exceeded. */
+ cloth_brush_reallocate_constraints(cloth_sim);
+
+ /* Add the constraint to the GSet to avoid creating it again. */
+ BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2);
+}
+
+static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim,
+ const int v,
+ const float strength)
+{
+ SculptClothLengthConstraint *length_constraint =
+ &cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
+
+ length_constraint->elem_index_a = v;
+ length_constraint->elem_index_b = v;
+
+ length_constraint->elem_position_a = cloth_sim->pos[v];
+ length_constraint->elem_position_b = cloth_sim->init_pos[v];
+
+ length_constraint->length = 0.0f;
+ length_constraint->strength = strength;
+
+ cloth_sim->tot_length_constraints++;
+
+ /* Reallocation if the array capacity is exceeded. */
+ cloth_brush_reallocate_constraints(cloth_sim);
+}
+
+static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim,
+ const int v,
+ const float strength)
+{
+ SculptClothLengthConstraint *length_constraint =
+ &cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
+
+ length_constraint->elem_index_a = v;
+ length_constraint->elem_index_b = v;
+
+ length_constraint->elem_position_a = cloth_sim->pos[v];
+ length_constraint->elem_position_b = cloth_sim->deformation_pos[v];
+
+ length_constraint->length = 0.0f;
+ length_constraint->strength = strength;
+
+ cloth_sim->tot_length_constraints++;
+
+ /* Reallocation if the array capacity is exceeded. */
+ cloth_brush_reallocate_constraints(cloth_sim);
+}
+
static void do_cloth_brush_build_constraints_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
PBVHVertexIter vd;
+ const bool use_persistent = brush != NULL && brush->flag & BRUSH_PERSISTENT;
+
+ /* Brush can be NULL in tools that use the solver without relying of constraints with deformation
+ * positions. */
+ const bool cloth_is_deform_brush = ss->cache != NULL && brush != NULL &&
+ SCULPT_is_cloth_deform_brush(brush);
+ float radius_squared = 0.0f;
+ if (cloth_is_deform_brush) {
+ radius_squared = ss->cache->initial_radius * ss->cache->initial_radius;
+ }
+
+ const float cloth_sim_radius_squared = data->cloth_sim_radius * data->cloth_sim_radius;
+
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (len_squared_v3v3(vd.co, data->cloth_sim_initial_location) <
- data->cloth_sim_radius * data->cloth_sim_radius) {
+ const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location);
+ if (len_squared < cloth_sim_radius_squared) {
SculptVertexNeighborIter ni;
int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX];
@@ -152,6 +239,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+ if (brush->cloth_constraint_softbody_strength > 0.0f) {
+ cloth_brush_add_softbody_constraint(
+ data->cloth_sim, vd.index, brush->cloth_constraint_softbody_strength);
+ }
+
/* As we don't know the order of the neighbor vertices, we create all possible combinations
* between the neighbor and the original vertex as length constraints. */
/* This results on a pattern that contains structural, shear and bending constraints for all
@@ -159,13 +251,19 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
for (int c_i = 0; c_i < tot_indices; c_i++) {
for (int c_j = 0; c_j < tot_indices; c_j++) {
- if (c_i != c_j) {
+ if (c_i != c_j && !cloth_brush_sim_has_length_constraint(
+ data->cloth_sim, build_indices[c_i], build_indices[c_j])) {
cloth_brush_add_length_constraint(
- ss, data->cloth_sim, build_indices[c_i], build_indices[c_j]);
+ ss, data->cloth_sim, build_indices[c_i], build_indices[c_j], use_persistent);
}
}
}
}
+
+ if (cloth_is_deform_brush && len_squared < radius_squared) {
+ const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius);
+ cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade);
+ }
}
BKE_pbvh_vertex_iter_end;
}
@@ -183,15 +281,13 @@ static float cloth_brush_simulation_falloff_get(const Brush *brush,
/* Outiside the limits. */
return 0.0f;
}
- else if (distance < falloff) {
+ if (distance < falloff) {
/* Before the falloff area. */
return 1.0f;
}
- else {
- /* Do a smoothstep transition inside the falloff area. */
- float p = 1.0f - ((distance - falloff) / (limit - falloff));
- return 3.0f * p * p - 2.0f * p * p * p;
- }
+ /* Do a smoothstep transition inside the falloff area. */
+ float p = 1.0f - ((distance - falloff) / (limit - falloff));
+ return 3.0f * p * p - 2.0f * p * p * p;
}
static void cloth_brush_apply_force_to_vertex(SculptSession *UNUSED(ss),
@@ -245,8 +341,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
/* Gravity */
float gravity[3] = {0.0f};
if (ss->cache->supports_gravity) {
- madd_v3_v3fl(
- gravity, ss->cache->gravity_direction, -ss->cache->radius * data->sd->gravity_factor);
+ madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor);
}
/* Original data for deform brushes. */
@@ -270,6 +365,11 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
copy_v3_v3(current_vertex_location, vd.co);
}
+ /* Apply gravity in the entire simulation area. */
+ float vertex_gravity[3];
+ mul_v3_v3fl(vertex_gravity, gravity, sim_factor);
+ cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, vertex_gravity, vd.index);
+
/* When using the plane falloff mode the falloff is not constrained by the brush radius. */
if (sculpt_brush_test_sq_fn(&test, current_vertex_location) || use_falloff_plane) {
@@ -310,9 +410,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(force, offset, -fade);
break;
case BRUSH_CLOTH_DEFORM_GRAB:
- /* Grab writes the positions in the simulation directly without applying forces. */
- madd_v3_v3v3fl(
- cloth_sim->pos[vd.index], orig_data.co, ss->cache->grab_delta_symmetry, fade);
+ madd_v3_v3v3fl(cloth_sim->deformation_pos[vd.index],
+ orig_data.co,
+ ss->cache->grab_delta_symmetry,
+ fade);
zero_v3(force);
break;
case BRUSH_CLOTH_DEFORM_PINCH_POINT:
@@ -347,8 +448,6 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
break;
}
- madd_v3_v3fl(force, gravity, fade);
-
cloth_brush_apply_force_to_vertex(ss, ss->cache->cloth_sim, force, vd.index);
}
}
@@ -356,6 +455,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
}
static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss,
+ Brush *brush,
const float cloth_mass,
const float cloth_damping)
{
@@ -369,12 +469,20 @@ static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss,
"cloth length constraints");
cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK;
- cloth_sim->acceleration = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim acceleration");
- cloth_sim->pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim pos");
- cloth_sim->prev_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim prev pos");
- cloth_sim->init_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim init pos");
- cloth_sim->length_constraint_tweak = MEM_callocN(sizeof(float) * totverts,
- "cloth sim length tweak");
+ cloth_sim->acceleration = MEM_calloc_arrayN(
+ totverts, 3 * sizeof(float), "cloth sim acceleration");
+ cloth_sim->pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim pos");
+ cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim prev pos");
+ cloth_sim->init_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim init pos");
+ cloth_sim->length_constraint_tweak = MEM_calloc_arrayN(
+ totverts, sizeof(float), "cloth sim length tweak");
+
+ /* Brush can be NULL for tools that neeed the solver but don't rely on constraint to deformation
+ * positions. */
+ if (brush && SCULPT_is_cloth_deform_brush(brush)) {
+ cloth_sim->deformation_pos = MEM_calloc_arrayN(
+ totverts, 3 * sizeof(float), "cloth sim deformation positions");
+ }
cloth_sim->mass = cloth_mass;
cloth_sim->damping = cloth_damping;
@@ -428,13 +536,16 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
BKE_pbvh_vertex_iter_end;
}
-static void cloth_brush_build_nodes_constraints(Sculpt *sd,
- Object *ob,
- PBVHNode **nodes,
- int totnode,
- SculptClothSimulation *cloth_sim,
- float initial_location[3],
- const float radius)
+static void cloth_brush_build_nodes_constraints(
+ Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ int totnode,
+ SculptClothSimulation *cloth_sim,
+ /* Cannot be const, because it is assigned to a non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ float initial_location[3],
+ const float radius)
{
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -445,6 +556,8 @@ 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_edgeset_new("created length constraints");
+
SculptThreadedTaskData build_constraints_data = {
.sd = sd,
.ob = ob,
@@ -456,6 +569,8 @@ 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_edgeset_free(cloth_sim->created_length_constraints);
}
static void cloth_brush_satisfy_constraints(SculptSession *ss,
@@ -466,11 +581,11 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
for (int i = 0; i < cloth_sim->tot_length_constraints; i++) {
const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i];
- const int v1 = constraint->v1;
- const int v2 = constraint->v2;
+ const int v1 = constraint->elem_index_a;
+ const int v2 = constraint->elem_index_b;
float v1_to_v2[3];
- sub_v3_v3v3(v1_to_v2, cloth_sim->pos[v2], cloth_sim->pos[v1]);
+ sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a);
const float current_distance = len_v3(v1_to_v2);
float correction_vector[3];
float correction_vector_half[3];
@@ -506,8 +621,14 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
cloth_sim->init_pos[v2]) :
1.0f;
- madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, 1.0f * mask_v1 * sim_factor_v1);
- madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, -1.0f * mask_v2 * sim_factor_v2);
+ madd_v3_v3fl(cloth_sim->pos[v1],
+ correction_vector_half,
+ 1.0f * mask_v1 * sim_factor_v1 * constraint->strength);
+ if (v1 != v2) {
+ madd_v3_v3fl(cloth_sim->pos[v2],
+ correction_vector_half,
+ -1.0f * mask_v2 * sim_factor_v2 * constraint->strength);
+ }
}
}
}
@@ -532,7 +653,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);
}
@@ -607,7 +728,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);
}
@@ -623,17 +744,24 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* In the first brush step of each symmetry pass, build the constraints for the vertices in all
* nodes inside the simulation's limits. */
- /* Brush stroke types that restore the mesh on each brush step also need the cloth sim data to be
- * created on each step. */
- if (ss->cache->first_time || !ss->cache->cloth_sim) {
+ /* Brushes that use anchored strokes and restore the mesh can't rely on symmetry passes and steps
+ * count as it is always the first step, so the simulation needs to be created when it does not
+ * exist for this stroke. */
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) || !ss->cache->cloth_sim) {
/* The simulation structure only needs to be created on the first symmetry pass. */
- if (ss->cache->mirror_symmetry_pass == 0) {
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
ss->cache->cloth_sim = cloth_brush_simulation_create(
- ss, brush->cloth_mass, brush->cloth_damping);
+ ss, brush, brush->cloth_mass, brush->cloth_damping);
+
+ const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush);
+
for (int i = 0; i < totverts; i++) {
copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
copy_v3_v3(ss->cache->cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
+ if (is_cloth_deform_brush) {
+ copy_v3_v3(ss->cache->cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
+ }
}
}
@@ -656,8 +784,6 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* Update and write the simulation to the nodes. */
cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode);
-
- return;
}
void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
@@ -667,6 +793,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim)
MEM_SAFE_FREE(cloth_sim->acceleration);
MEM_SAFE_FREE(cloth_sim->length_constraints);
MEM_SAFE_FREE(cloth_sim->length_constraint_tweak);
+ MEM_SAFE_FREE(cloth_sim->deformation_pos);
MEM_SAFE_FREE(cloth_sim->init_pos);
MEM_SAFE_FREE(cloth_sim);
}
@@ -841,7 +968,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++) {
@@ -857,8 +984,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);
@@ -890,14 +1016,14 @@ 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");
- ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, cloth_mass, cloth_damping);
+ ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, NULL, cloth_mass, cloth_damping);
copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss));
const int totverts = SCULPT_vertex_count_get(ss);
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..1940b007cb0 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -203,8 +203,9 @@ 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) {
+ SCULPT_boundary_info_ensure(ob);
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 +270,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 +631,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 +788,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 +1084,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..878a08ceeb7
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -0,0 +1,327 @@
+/*
+ * 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;
+
+static 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);
+ 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");
+
+ BKE_sculpt_color_layer_create_if_needed(ob);
+
+ /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
+ * earlier steps modifying the data. */
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ 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_vertex_colors_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..e9a98a17f8a 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;
@@ -87,7 +87,7 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
.original = true,
.center = center,
.radius_squared = FLT_MAX,
- .ignore_fully_masked = true,
+ .ignore_fully_ineffective = true,
};
BKE_pbvh_search_gather(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);
}
@@ -261,17 +261,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
switch (filter_type) {
case MESH_FILTER_SMOOTH:
CLAMP(fade, -1.0f, 1.0f);
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- SCULPT_neighbor_average(ss, avg, vd.index);
- break;
- case PBVH_BMESH:
- SCULPT_bmesh_neighbor_average(avg, vd.bm_vert);
- break;
- case PBVH_GRIDS:
- SCULPT_neighbor_coords_average(ss, avg, vd.index);
- break;
- }
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
sub_v3_v3v3(val, avg, orig_co);
madd_v3_v3v3fl(val, orig_co, val, fade);
sub_v3_v3v3(disp, val, orig_co);
@@ -320,8 +310,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
break;
}
case MESH_FILTER_RELAX: {
- SCULPT_relax_vertex(
- ss, &vd, clamp_f(fade * ss->filter_cache->automask[vd.index], 0.0f, 1.0f), false, val);
+ SCULPT_relax_vertex(ss, &vd, clamp_f(fade, 0.0f, 1.0f), false, val);
sub_v3_v3v3(disp, val, vd.co);
break;
}
@@ -475,7 +464,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 +475,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) {
@@ -541,17 +529,24 @@ 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);
+ const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false);
+ if (needs_topology_info) {
+ SCULPT_boundary_info_ensure(ob);
+ }
const int totvert = SCULPT_vertex_count_get(ss);
- if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
+ if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) {
return OPERATOR_CANCELLED;
}
SCULPT_undo_push_begin("Mesh filter");
- SCULPT_filter_cache_init(ob, sd);
+ if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) {
+ SCULPT_boundary_info_ensure(ob);
+ }
+
+ 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);
@@ -580,16 +575,6 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y;
ss->filter_cache->enabled_axis[2] = deform_axis & MESH_FILTER_DEFORM_Z;
- if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_RELAX) {
- ss->filter_cache->automask = MEM_mallocN(totvert * sizeof(float),
- "Relax filter edge automask");
- for (int i = 0; i < totvert; i++) {
- ss->filter_cache->automask[i] = 1.0f;
- }
- SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_EDGES, 1, ss->filter_cache->automask);
- }
-
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 0e27658e848..47e6fb55d81 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"
@@ -50,12 +51,15 @@ bool SCULPT_mode_poll_view3d(struct bContext *C);
bool SCULPT_poll(struct bContext *C);
bool SCULPT_poll_view3d(struct bContext *C);
+bool SCULPT_vertex_colors_poll(struct bContext *C);
+
/* Updates */
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 +96,10 @@ 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);
+
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index);
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]);
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
@@ -143,7 +151,19 @@ int SCULPT_active_vertex_get(SculptSession *ss);
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]);
-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);
+
+/* Vertex Info. */
+void SCULPT_boundary_info_ensure(Object *object);
+/* Boundary Info needs to be initialized in order to use this function. */
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index);
/* Sculpt Visibility API */
@@ -165,12 +185,16 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
int SCULPT_face_set_next_available_get(SculptSession *ss);
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
-bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index);
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index);
bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index);
void SCULPT_face_sets_visibility_invert(SculptSession *ss);
void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
+bool SCULPT_stroke_is_main_symmetry_pass(struct StrokeCache *cache);
+bool SCULPT_stroke_is_first_brush_step(struct StrokeCache *cache);
+bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cache);
+
/* Sculpt Original Data */
typedef struct {
struct BMLog *bm_log;
@@ -179,15 +203,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 +334,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,15 +403,21 @@ 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);
-/* Smooth Brush. */
+/* Paint Brush. */
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
-void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert);
-void SCULPT_bmesh_neighbor_average(float avg[3], struct BMVert *v);
+/* Smear Brush. */
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+/* Smooth Brush. */
void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v);
void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
+
+/* Mask the mesh boundaries smoothing only the mesh surface without using automasking. */
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index);
void SCULPT_smooth(Sculpt *sd,
Object *ob,
@@ -427,6 +462,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 +493,7 @@ typedef struct SculptUndoNode {
float (*co)[3];
float (*orig_co)[3];
short (*no)[3];
+ float (*col)[4];
float *mask;
int totvert;
@@ -556,6 +593,7 @@ typedef struct SculptThreadedTaskData {
int filter_type;
float filter_strength;
+ float *filter_fill_color;
bool use_area_cos;
bool use_area_nos;
@@ -568,6 +606,8 @@ typedef struct SculptThreadedTaskData {
bool any_vertex_sampled;
+ float *wet_mix_sampled_color;
+
float *prev_mask;
float *pose_factor;
@@ -600,7 +640,18 @@ typedef struct SculptThreadedTaskData {
float dirty_mask_max;
bool dirty_mask_dirty_only;
+ /* Mask By Color Tool */
+
+ float mask_by_color_threshold;
+ bool mask_by_color_invert;
+ bool mask_by_color_preserve_mask;
+
+ /* Index of the vertex that is going to be used as a reference for the colors. */
+ int mask_by_color_vertex;
+ float *mask_by_color_floodfill;
+
int face_set;
+ int filter_undo_type;
ThreadMutex mutex;
@@ -632,7 +683,8 @@ typedef struct {
float radius_squared;
const float *center;
bool original;
- bool ignore_fully_masked;
+ /* This ignores fully masked and fully hidden nodes. */
+ bool ignore_fully_ineffective;
} SculptSearchSphereData;
typedef struct {
@@ -640,7 +692,7 @@ typedef struct {
struct SculptSession *ss;
float radius_squared;
bool original;
- bool ignore_fully_masked;
+ bool ignore_fully_ineffective;
struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc;
} SculptSearchCircleData;
@@ -688,6 +740,12 @@ bool SCULPT_pbvh_calc_area_normal(const struct Brush *brush,
#define SCULPT_CLAY_STABILIZER_LEN 10
+typedef struct AutomaskingSettings {
+ /* Flags from eAutomasking_flag. */
+ int flags;
+ int initial_face_set;
+} AutomaskingSettings;
+
typedef struct StrokeCache {
/* Invariants */
float initial_radius;
@@ -720,6 +778,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 */
@@ -770,6 +830,15 @@ typedef struct StrokeCache {
bool original;
float anchored_location[3];
+ /* Paint Brush. */
+ struct {
+ float hardness;
+ float flow;
+ float wet_mix;
+ float wet_persistence;
+ float density;
+ } paint_brush;
+
/* Pose brush */
struct SculptPoseIKChain *pose_ik_chain;
@@ -808,11 +877,18 @@ typedef struct StrokeCache {
float true_gravity_direction[3];
float gravity_direction[3];
- float *automask;
+ /* Automasking. */
+ AutomaskingSettings automask_settings;
+ /* Precomputed automask factor indexed by vertex, owned by the automasking system and initialized
+ * in SCULPT_automasking_init when needed. */
+ float *automask_factor;
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 +977,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..bc3f8537289 100644
--- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
+++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
@@ -47,7 +47,6 @@
#include "paint_intern.h"
#include "sculpt_intern.h"
-#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
@@ -257,7 +256,7 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes,
}
/* Delay the first daub because grab delta is not setup. */
- if (ss->cache->first_time) {
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
ss->cache->multiplane_scrape_angle = 0.0f;
return;
}
@@ -304,7 +303,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 +394,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..f01a914fdd3
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -0,0 +1,492 @@
+/*
+ * 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 = ss->cache->paint_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 * ss->cache->paint_brush.flow);
+ mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * ss->cache->paint_brush.flow);
+
+ /* Interpolate with the wet_mix color for wet paint mixing. */
+ blend_color_interpolate_float(
+ paint_color, paint_color, wet_mix_color, ss->cache->paint_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 (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ 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 (ss->cache->paint_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,
+ ss->cache->paint_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]);
+
+ switch (brush->smear_deform_type) {
+ case BRUSH_SMEAR_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SMEAR_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SMEAR_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
+ 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 (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ 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..a338b5346af 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);
@@ -430,7 +430,7 @@ static bool pose_topology_floodfill_cb(
co, data->pose_initial_co, data->radius, data->symm)) {
return true;
}
- else if (SCULPT_check_vertex_pivot_symmetry(co, data->pose_initial_co, data->symm)) {
+ if (SCULPT_check_vertex_pivot_symmetry(co, data->pose_initial_co, data->symm)) {
if (!is_duplicate) {
add_v3_v3(data->pose_origin, co);
data->tot_co++;
@@ -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);
+ ik_chain = 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..7fbbcd1c896 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -62,78 +62,29 @@
#include <math.h>
#include <stdlib.h>
-/* For the smooth brush, uses the neighboring vertices around vert to calculate
- * a smoothed location for vert. Skips corner vertices (used by only one
- * polygon). */
-void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert)
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index)
{
- const MeshElemMap *vert_map = &ss->pmap[vert];
- const MVert *mvert = ss->mvert;
- float(*deform_co)[3] = ss->deform_cos;
-
- /* Don't modify corner vertices. */
- if (vert_map->count > 1) {
- int total = 0;
-
- zero_v3(avg);
-
- for (int i = 0; i < vert_map->count; i++) {
- const MPoly *p = &ss->mpoly[vert_map->indices[i]];
- uint f_adj_v[2];
-
- if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) {
- for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
- if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) {
- add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : mvert[f_adj_v[j]].co);
-
- total++;
- }
- }
- }
- }
+ float avg[3] = {0.0f, 0.0f, 0.0f};
+ int total = 0;
- if (total > 0) {
- mul_v3_fl(avg, 1.0f / total);
- return;
- }
+ if (SCULPT_vertex_is_boundary(ss, index)) {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ return;
}
- copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co);
-}
-
-/* Same logic as neighbor_average(), but for bmesh rather than mesh. */
-void SCULPT_bmesh_neighbor_average(float avg[3], BMVert *v)
-{
- /* logic for 3 or more is identical. */
- const int vfcount = BM_vert_face_count_at_most(v, 3);
-
- /* Don't modify corner vertices. */
- if (vfcount > 1) {
- BMIter liter;
- BMLoop *l;
- int total = 0;
-
- zero_v3(avg);
-
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- const BMVert *adj_v[2] = {l->prev->v, l->next->v};
-
- for (int i = 0; i < ARRAY_SIZE(adj_v); i++) {
- const BMVert *v_other = adj_v[i];
- if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) {
- add_v3_v3(avg, v_other->co);
- total++;
- }
- }
- }
-
- if (total > 0) {
- mul_v3_fl(avg, 1.0f / total);
- return;
- }
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ total++;
}
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- copy_v3_v3(avg, v->co);
+ if (total > 0) {
+ mul_v3_v3fl(result, avg, 1.0f / total);
+ }
+ else {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ }
}
/* For bmesh: Average surrounding verts based on an orthogonality measure.
@@ -221,130 +172,32 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
if (total > 0) {
return avg / (float)total;
}
- else {
- return SCULPT_vertex_mask_get(ss, index);
- }
+ return SCULPT_vertex_mask_get(ss, index);
}
-static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- Sculpt *sd = data->sd;
- const Brush *brush = data->brush;
- const bool smooth_mask = data->smooth_mask;
- float bstrength = data->strength;
-
- PBVHVertexIter vd;
-
- CLAMP(bstrength, 0.0f, 1.0f);
-
- 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,
- smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
- vd.index,
- thread_id);
- if (smooth_mask) {
- float val = SCULPT_neighbor_mask_average(ss, vd.vert_indices[vd.i]) - *vd.mask;
- val *= fade * bstrength;
- *vd.mask += val;
- CLAMP(*vd.mask, 0.0f, 1.0f);
- }
- else {
- float avg[3], val[3];
-
- SCULPT_neighbor_average(ss, avg, vd.vert_indices[vd.i]);
- sub_v3_v3v3(val, avg, vd.co);
-
- madd_v3_v3v3fl(val, vd.co, val, fade);
-
- SCULPT_clip(sd, ss, vd.co, val);
- }
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ int total = 0;
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+ total++;
}
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- Sculpt *sd = data->sd;
- const Brush *brush = data->brush;
- const bool smooth_mask = data->smooth_mask;
- float bstrength = data->strength;
-
- PBVHVertexIter vd;
-
- CLAMP(bstrength, 0.0f, 1.0f);
-
- 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,
- smooth_mask ? 0.0f : *vd.mask,
- vd.index,
- thread_id);
- if (smooth_mask) {
- float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask;
- val *= fade * bstrength;
- *vd.mask += val;
- CLAMP(*vd.mask, 0.0f, 1.0f);
- }
- else {
- float avg[3], val[3];
-
- SCULPT_bmesh_neighbor_average(avg, vd.bm_vert);
- sub_v3_v3v3(val, avg, vd.co);
-
- madd_v3_v3v3fl(val, vd.co, val, fade);
-
- SCULPT_clip(sd, ss, vd.co, val);
- }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
+ if (total > 0) {
+ mul_v4_v4fl(result, avg, 1.0f / (float)total);
+ }
+ else {
+ copy_v4_v4(result, SCULPT_vertex_color_get(ss, index));
}
- BKE_pbvh_vertex_iter_end;
}
-static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
+static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@@ -389,6 +242,9 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata,
madd_v3_v3v3fl(val, vd.co, val, fade);
SCULPT_clip(sd, ss, vd.co, val);
}
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
}
}
BKE_pbvh_vertex_iter_end;
@@ -420,6 +276,9 @@ void SCULPT_smooth(Sculpt *sd,
return;
}
+ SCULPT_vertex_random_access_init(ss);
+ SCULPT_boundary_info_ensure(ob);
+
for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last;
@@ -433,19 +292,8 @@ void SCULPT_smooth(Sculpt *sd,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
-
- switch (type) {
- case PBVH_GRIDS:
- BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings);
- break;
- case PBVH_FACES:
- BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings);
- break;
- case PBVH_BMESH:
- BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings);
- break;
- }
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
}
}
@@ -525,10 +373,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 +419,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);
}
@@ -582,8 +440,7 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
- if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0 &&
- ss->cache->radial_symmetry_pass == 0) {
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL);
ss->cache->surface_smooth_laplacian_disp = MEM_callocN(
SCULPT_vertex_count_get(ss) * 3 * sizeof(float), "HC smooth laplacian b");
@@ -598,7 +455,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 2eb1191b950..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);
@@ -199,7 +198,7 @@ void ED_sculpt_end_transform(struct bContext *C)
}
/* Force undo push to happen even inside transform operator, since the sculpt
* undo system works separate from regular undo and this is require to properly
- * finish an undo step also when cancelling. */
+ * finish an undo step also when canceling. */
const bool use_nested_undo = true;
SCULPT_undo_push_end_ex(use_nested_undo);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
@@ -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..fd800ca2c18 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -167,9 +167,7 @@ static bool test_swap_v3_v3(float a[3], float b[3])
swap_v3_v3(a, b);
return true;
}
- else {
- return false;
- }
+ return false;
}
static bool sculpt_undo_restore_deformed(
@@ -179,9 +177,7 @@ static bool sculpt_undo_restore_deformed(
copy_v3_v3(unode->co[uindex], ss->deform_cos[oindex]);
return true;
}
- else {
- return false;
- }
+ return false;
}
static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, SculptUndoNode *unode)
@@ -205,7 +201,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 +322,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 +413,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 +427,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 +603,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 +648,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 +674,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 +727,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 +1018,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 +1109,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 +1241,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;
}
}
@@ -1227,17 +1266,17 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
- else if (type == SCULPT_UNDO_GEOMETRY) {
+ if (type == SCULPT_UNDO_GEOMETRY) {
unode = sculpt_undo_geometry_push(ob, type);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
- else if (type == SCULPT_UNDO_FACE_SETS) {
+ if (type == SCULPT_UNDO_FACE_SETS) {
unode = sculpt_undo_face_sets_push(ob, type);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
- else if ((unode = SCULPT_undo_get_node(node))) {
+ if ((unode = SCULPT_undo_get_node(node))) {
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
@@ -1272,6 +1311,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/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c
index d6b259c9ac0..4b3df2dfea2 100644
--- a/source/blender/editors/sculpt_paint/sculpt_uv.c
+++ b/source/blender/editors/sculpt_paint/sculpt_uv.c
@@ -228,8 +228,6 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
}
MEM_freeN(tmp_uvdata);
-
- return;
}
static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
@@ -302,8 +300,6 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
}
MEM_freeN(tmp_uvdata);
-
- return;
}
static void uv_sculpt_stroke_apply(bContext *C,
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 81ac8a16d8a..050dca07c34 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -564,6 +564,9 @@ static void sound_mixdown_draw(bContext *C, wmOperator *op)
PropertyRNA *prop_codec;
PropertyRNA *prop_bitrate;
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
AUD_Container container = RNA_enum_get(op->ptr, "container");
AUD_Codec codec = RNA_enum_get(op->ptr, "codec");
@@ -771,7 +774,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 +850,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_data.c b/source/blender/editors/space_action/action_data.c
index b5a0c4a9e22..bffd6cc421d 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -337,10 +337,9 @@ static int action_pushdown_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
return OPERATOR_CANCELLED;
}
- else {
- /* action can be safely added */
- BKE_nla_action_pushdown(adt);
- }
+
+ /* action can be safely added */
+ BKE_nla_action_pushdown(adt);
/* Stop displaying this action in this editor
* NOTE: The editor itself doesn't set a user...
@@ -383,24 +382,23 @@ static int action_stash_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
return OPERATOR_CANCELLED;
}
- else {
- /* stash the action */
- if (BKE_nla_action_stash(adt)) {
- /* The stash operation will remove the user already,
- * so the flushing step later shouldn't double up
- * the user-count fixes. Hence, we must unset this ref
- * first before setting the new action.
- */
- saction->action = NULL;
- }
- else {
- /* action has already been added - simply warn about this, and clear */
- BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
- }
- /* clear action refs from editor, and then also the backing data (not necessary) */
- actedit_change_action(C, NULL);
+ /* stash the action */
+ if (BKE_nla_action_stash(adt)) {
+ /* The stash operation will remove the user already,
+ * so the flushing step later shouldn't double up
+ * the user-count fixes. Hence, we must unset this ref
+ * first before setting the new action.
+ */
+ saction->action = NULL;
+ }
+ else {
+ /* action has already been added - simply warn about this, and clear */
+ BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
}
+
+ /* clear action refs from editor, and then also the backing data (not necessary) */
+ actedit_change_action(C, NULL);
}
/* Send notifiers that stuff has changed */
@@ -486,28 +484,27 @@ static int action_stash_create_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier");
return OPERATOR_CANCELLED;
}
- else {
- /* stash the action */
- if (BKE_nla_action_stash(adt)) {
- bAction *new_action = NULL;
- /* Create new action not based on the old one
- * (since the "new" operator already does that). */
- new_action = action_create_new(C, NULL);
+ /* stash the action */
+ if (BKE_nla_action_stash(adt)) {
+ bAction *new_action = NULL;
- /* The stash operation will remove the user already,
- * so the flushing step later shouldn't double up
- * the user-count fixes. Hence, we must unset this ref
- * first before setting the new action.
- */
- saction->action = NULL;
- actedit_change_action(C, new_action);
- }
- else {
- /* action has already been added - simply warn about this, and clear */
- BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
- actedit_change_action(C, NULL);
- }
+ /* Create new action not based on the old one
+ * (since the "new" operator already does that). */
+ new_action = action_create_new(C, NULL);
+
+ /* The stash operation will remove the user already,
+ * so the flushing step later shouldn't double up
+ * the user-count fixes. Hence, we must unset this ref
+ * first before setting the new action.
+ */
+ saction->action = NULL;
+ actedit_change_action(C, new_action);
+ }
+ else {
+ /* action has already been added - simply warn about this, and clear */
+ BKE_report(op->reports, RPT_ERROR, "Action has already been stashed");
+ actedit_change_action(C, NULL);
}
}
@@ -709,11 +706,11 @@ static NlaStrip *action_layer_get_nlastrip(ListBase *strips, float ctime)
/* in range - use this one */
return strip;
}
- else if ((ctime < strip->start) && (strip->prev == NULL)) {
+ if ((ctime < strip->start) && (strip->prev == NULL)) {
/* before first - use this one */
return strip;
}
- else if ((ctime > strip->end) && (strip->next == NULL)) {
+ if ((ctime > strip->end) && (strip->next == NULL)) {
/* after last - use this one */
return strip;
}
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index b390e4b56d6..af8e0e9d9de 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -272,9 +272,8 @@ static int actkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
if (ac.scene == NULL) {
return OPERATOR_CANCELLED;
}
- else {
- scene = ac.scene;
- }
+
+ scene = ac.scene;
/* set the range directly */
get_keyframe_extents(&ac, &min, &max, false);
@@ -725,9 +724,10 @@ static void insert_action_keys(bAnimContext *ac, short mode)
flag = ANIM_get_keyframing_flags(scene, true);
/* insert keyframes */
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(ac->depsgraph,
+ (float)CFRA);
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
- float cfra = (float)CFRA;
/* Read value from property the F-Curve represents, or from the curve only?
* - ale->id != NULL:
@@ -746,7 +746,7 @@ static void insert_action_keys(bAnimContext *ac, short mode)
((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path,
fcu->array_index,
- cfra,
+ &anim_eval_context,
ts->keyframe_type,
&nla_cache,
flag);
@@ -755,8 +755,9 @@ static void insert_action_keys(bAnimContext *ac, short mode)
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
/* adjust current frame for NLA-scaling */
+ float cfra = anim_eval_context.eval_time;
if (adt) {
- cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+ cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
const float curval = evaluate_fcurve(fcu, cfra);
@@ -800,10 +801,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 +847,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 +899,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 +975,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 +1550,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 +1751,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 +1881,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_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index e084d5fcdf2..3ae203b563b 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -44,7 +44,6 @@
#include "ED_gizmo_library.h"
#include "ED_gpencil.h"
#include "ED_lattice.h"
-#include "ED_logic.h"
#include "ED_markers.h"
#include "ED_mask.h"
#include "ED_mball.h"
@@ -159,6 +158,7 @@ void ED_spacemacros_init(void)
* We need to have them go after python operators too */
ED_operatormacros_armature();
ED_operatormacros_mesh();
+ ED_operatormacros_uvedit();
ED_operatormacros_metaball();
ED_operatormacros_node();
ED_operatormacros_object();
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..5885d3dcbb0 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -56,7 +56,6 @@
#include "RNA_access.h"
#include "ED_armature.h"
-#include "ED_buttons.h"
#include "ED_physics.h"
#include "ED_screen.h"
@@ -102,7 +101,7 @@ static PointerRNA *get_pointer_type(ButsContextPath *path, StructRNA *type)
/************************* Creating the Path ************************/
-static int buttons_context_path_scene(ButsContextPath *path)
+static bool buttons_context_path_scene(ButsContextPath *path)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
@@ -146,7 +145,7 @@ static int buttons_context_path_world(ButsContextPath *path)
return 1;
}
/* if we have a scene, use the scene's world */
- else if (buttons_context_path_scene(path)) {
+ if (buttons_context_path_scene(path)) {
scene = path->ptr[path->len - 1].data;
world = scene->world;
@@ -155,49 +154,48 @@ static int buttons_context_path_world(ButsContextPath *path)
path->len++;
return 1;
}
- else {
- return 1;
- }
+
+ return 1;
}
/* no path to a world possible */
return 0;
}
-static int buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window)
+static bool buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window)
{
FreestyleLineStyle *linestyle;
PointerRNA *ptr = &path->ptr[path->len - 1];
/* if we already have a (pinned) linestyle, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_FreestyleLineStyle)) {
- return 1;
+ return true;
}
/* if we have a view layer, use the lineset's linestyle */
- else if (buttons_context_path_view_layer(path, window)) {
+ if (buttons_context_path_view_layer(path, window)) {
ViewLayer *view_layer = path->ptr[path->len - 1].data;
linestyle = BKE_linestyle_active_from_view_layer(view_layer);
if (linestyle) {
RNA_id_pointer_create(&linestyle->id, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
/* no path to a linestyle possible */
- return 0;
+ return false;
}
-static int buttons_context_path_object(ButsContextPath *path)
+static bool buttons_context_path_object(ButsContextPath *path)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
/* if we already have a (pinned) object, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_Object)) {
- return 1;
+ return true;
}
if (!RNA_struct_is_a(ptr->type, &RNA_ViewLayer)) {
- return 0;
+ return false;
}
ViewLayer *view_layer = ptr->data;
@@ -207,78 +205,76 @@ static int buttons_context_path_object(ButsContextPath *path)
RNA_id_pointer_create(&ob->id, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
/* no path to a object possible */
- return 0;
+ return false;
}
-static int buttons_context_path_data(ButsContextPath *path, int type)
+static bool buttons_context_path_data(ButsContextPath *path, int type)
{
Object *ob;
PointerRNA *ptr = &path->ptr[path->len - 1];
/* if we already have a data, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) {
- return 1;
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_Curve) &&
- (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Curve) &&
+ (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) {
+ return true;
}
-#ifdef WITH_NEW_OBJECT_TYPES
- else if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) {
+ return true;
}
- else if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
+ return true;
}
-#endif
- else if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
- return 1;
+ if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
+ return true;
}
/* try to get an object in the path, no pinning supported here */
- else if (buttons_context_path_object(path)) {
+ if (buttons_context_path_object(path)) {
ob = path->ptr[path->len - 1].data;
if (ob && (type == -1 || type == ob->type)) {
RNA_id_pointer_create(ob->data, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
/* no path to data possible */
- return 0;
+ return false;
}
-static int buttons_context_path_modifier(ButsContextPath *path)
+static bool buttons_context_path_modifier(ButsContextPath *path)
{
Object *ob;
@@ -295,14 +291,14 @@ static int buttons_context_path_modifier(ButsContextPath *path)
OB_HAIR,
OB_POINTCLOUD,
OB_VOLUME)) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
-static int buttons_context_path_shaderfx(ButsContextPath *path)
+static bool buttons_context_path_shaderfx(ButsContextPath *path)
{
Object *ob;
@@ -310,14 +306,14 @@ static int buttons_context_path_shaderfx(ButsContextPath *path)
ob = path->ptr[path->len - 1].data;
if (ob && ELEM(ob->type, OB_GPENCIL)) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
-static int buttons_context_path_material(ButsContextPath *path)
+static bool buttons_context_path_material(ButsContextPath *path)
{
Object *ob;
PointerRNA *ptr = &path->ptr[path->len - 1];
@@ -325,25 +321,25 @@ static int buttons_context_path_material(ButsContextPath *path)
/* if we already have a (pinned) material, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_Material)) {
- return 1;
+ return true;
}
/* if we have an object, use the object material slot */
- else if (buttons_context_path_object(path)) {
+ if (buttons_context_path_object(path)) {
ob = path->ptr[path->len - 1].data;
if (ob && OB_TYPE_SUPPORT_MATERIAL(ob->type)) {
ma = BKE_object_material_get(ob, ob->actcol);
RNA_id_pointer_create(&ma->id, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
/* no path to a material possible */
- return 0;
+ return false;
}
-static int buttons_context_path_bone(ButsContextPath *path)
+static bool buttons_context_path_bone(ButsContextPath *path)
{
bArmature *arm;
EditBone *edbo;
@@ -357,29 +353,29 @@ static int buttons_context_path_bone(ButsContextPath *path)
edbo = arm->act_edbone;
RNA_pointer_create(&arm->id, &RNA_EditBone, edbo, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
else {
if (arm->act_bone) {
RNA_pointer_create(&arm->id, &RNA_Bone, arm->act_bone, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
}
/* no path to a bone possible */
- return 0;
+ return false;
}
-static int buttons_context_path_pose_bone(ButsContextPath *path)
+static bool buttons_context_path_pose_bone(ButsContextPath *path)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
/* if we already have a (pinned) PoseBone, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) {
- return 1;
+ return true;
}
/* if we have an armature, get the active bone */
@@ -388,25 +384,24 @@ static int buttons_context_path_pose_bone(ButsContextPath *path)
bArmature *arm = ob->data; /* path->ptr[path->len-1].data - works too */
if (ob->type != OB_ARMATURE || arm->edbo) {
- return 0;
+ return false;
}
- else {
- if (arm->act_bone) {
- bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, arm->act_bone->name);
- if (pchan) {
- RNA_pointer_create(&ob->id, &RNA_PoseBone, pchan, &path->ptr[path->len]);
- path->len++;
- return 1;
- }
+
+ if (arm->act_bone) {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, arm->act_bone->name);
+ if (pchan) {
+ RNA_pointer_create(&ob->id, &RNA_PoseBone, pchan, &path->ptr[path->len]);
+ path->len++;
+ return true;
}
}
}
/* no path to a bone possible */
- return 0;
+ return false;
}
-static int buttons_context_path_particle(ButsContextPath *path)
+static bool buttons_context_path_particle(ButsContextPath *path)
{
Object *ob;
ParticleSystem *psys;
@@ -414,7 +409,7 @@ static int buttons_context_path_particle(ButsContextPath *path)
/* if we already have (pinned) particle settings, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_ParticleSettings)) {
- return 1;
+ return true;
}
/* if we have an object, get the active particle system */
if (buttons_context_path_object(path)) {
@@ -425,15 +420,15 @@ static int buttons_context_path_particle(ButsContextPath *path)
RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
/* no path to a particle system possible */
- return 0;
+ return false;
}
-static int buttons_context_path_brush(const bContext *C, ButsContextPath *path)
+static bool buttons_context_path_brush(const bContext *C, ButsContextPath *path)
{
Scene *scene;
Brush *br = NULL;
@@ -441,10 +436,10 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path)
/* if we already have a (pinned) brush, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_Brush)) {
- return 1;
+ return true;
}
/* if we have a scene, use the toolsettings brushes */
- else if (buttons_context_path_scene(path)) {
+ if (buttons_context_path_scene(path)) {
scene = path->ptr[path->len - 1].data;
if (scene) {
@@ -457,32 +452,32 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path)
RNA_id_pointer_create((ID *)br, &path->ptr[path->len]);
path->len++;
- return 1;
+ return true;
}
}
/* no path to a brush possible */
- return 0;
+ return false;
}
-static int buttons_context_path_texture(const bContext *C,
- ButsContextPath *path,
- ButsContextTexture *ct)
+static bool buttons_context_path_texture(const bContext *C,
+ ButsContextPath *path,
+ ButsContextTexture *ct)
{
PointerRNA *ptr = &path->ptr[path->len - 1];
ID *id;
if (!ct) {
- return 0;
+ return false;
}
/* if we already have a (pinned) texture, we're done */
if (RNA_struct_is_a(ptr->type, &RNA_Texture)) {
- return 1;
+ return true;
}
if (!ct->user) {
- return 0;
+ return false;
}
id = ct->user->id;
@@ -507,7 +502,7 @@ static int buttons_context_path_texture(const bContext *C,
path->len++;
}
- return 1;
+ return true;
}
#ifdef WITH_FREESTYLE
@@ -536,7 +531,7 @@ static bool buttons_context_linestyle_pinnable(const bContext *C, ViewLayer *vie
}
#endif
-static int buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag)
+static bool buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag)
{
/* Note we don't use CTX_data here, instead we get it from the window.
* Otherwise there is a loop reading the context that we are setting. */
@@ -631,27 +626,27 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma
found = buttons_context_path_pose_bone(path);
break;
default:
- found = 0;
+ found = false;
break;
}
return found;
}
-static int buttons_shading_context(const bContext *C, int mainb)
+static bool buttons_shading_context(const bContext *C, int mainb)
{
wmWindow *window = CTX_wm_window(C);
ViewLayer *view_layer = WM_window_get_active_view_layer(window);
Object *ob = OBACT(view_layer);
if (ELEM(mainb, BCONTEXT_MATERIAL, BCONTEXT_WORLD, BCONTEXT_TEXTURE)) {
- return 1;
+ return true;
}
if (mainb == BCONTEXT_DATA && ob && ELEM(ob->type, OB_LAMP, OB_CAMERA)) {
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static int buttons_shading_new_context(const bContext *C, int flag)
@@ -663,10 +658,10 @@ static int buttons_shading_new_context(const bContext *C, int flag)
if (flag & (1 << BCONTEXT_MATERIAL)) {
return BCONTEXT_MATERIAL;
}
- else if (ob && ELEM(ob->type, OB_LAMP, OB_CAMERA) && (flag & (1 << BCONTEXT_DATA))) {
+ if (ob && ELEM(ob->type, OB_LAMP, OB_CAMERA) && (flag & (1 << BCONTEXT_DATA))) {
return BCONTEXT_DATA;
}
- else if (flag & (1 << BCONTEXT_WORLD)) {
+ if (flag & (1 << BCONTEXT_WORLD)) {
return BCONTEXT_WORLD;
}
@@ -796,10 +791,8 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
-#ifdef WITH_NEW_OBJECT_TYPES
"hair",
"pointcloud",
-#endif
"volume",
NULL,
};
@@ -829,74 +822,72 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
}
return 1;
}
- else if (CTX_data_equals(member, "scene")) {
+ if (CTX_data_equals(member, "scene")) {
/* Do not return one here if scene not found in path,
* in this case we want to get default context scene! */
return set_pointer_type(path, result, &RNA_Scene);
}
- else if (CTX_data_equals(member, "world")) {
+ if (CTX_data_equals(member, "world")) {
set_pointer_type(path, result, &RNA_World);
return 1;
}
- else if (CTX_data_equals(member, "object")) {
+ if (CTX_data_equals(member, "object")) {
set_pointer_type(path, result, &RNA_Object);
return 1;
}
- else if (CTX_data_equals(member, "mesh")) {
+ if (CTX_data_equals(member, "mesh")) {
set_pointer_type(path, result, &RNA_Mesh);
return 1;
}
- else if (CTX_data_equals(member, "armature")) {
+ if (CTX_data_equals(member, "armature")) {
set_pointer_type(path, result, &RNA_Armature);
return 1;
}
- else if (CTX_data_equals(member, "lattice")) {
+ if (CTX_data_equals(member, "lattice")) {
set_pointer_type(path, result, &RNA_Lattice);
return 1;
}
- else if (CTX_data_equals(member, "curve")) {
+ if (CTX_data_equals(member, "curve")) {
set_pointer_type(path, result, &RNA_Curve);
return 1;
}
- else if (CTX_data_equals(member, "meta_ball")) {
+ if (CTX_data_equals(member, "meta_ball")) {
set_pointer_type(path, result, &RNA_MetaBall);
return 1;
}
- else if (CTX_data_equals(member, "light")) {
+ if (CTX_data_equals(member, "light")) {
set_pointer_type(path, result, &RNA_Light);
return 1;
}
- else if (CTX_data_equals(member, "camera")) {
+ if (CTX_data_equals(member, "camera")) {
set_pointer_type(path, result, &RNA_Camera);
return 1;
}
- else if (CTX_data_equals(member, "speaker")) {
+ if (CTX_data_equals(member, "speaker")) {
set_pointer_type(path, result, &RNA_Speaker);
return 1;
}
- else if (CTX_data_equals(member, "lightprobe")) {
+ if (CTX_data_equals(member, "lightprobe")) {
set_pointer_type(path, result, &RNA_LightProbe);
return 1;
}
-#ifdef WITH_NEW_OBJECT_TYPES
- else if (CTX_data_equals(member, "hair")) {
+ if (CTX_data_equals(member, "hair")) {
set_pointer_type(path, result, &RNA_Hair);
return 1;
}
- else if (CTX_data_equals(member, "pointcloud")) {
+ if (CTX_data_equals(member, "pointcloud")) {
set_pointer_type(path, result, &RNA_PointCloud);
return 1;
}
-#endif
- else if (CTX_data_equals(member, "volume")) {
+ if (CTX_data_equals(member, "volume")) {
set_pointer_type(path, result, &RNA_Volume);
return 1;
}
- else if (CTX_data_equals(member, "material")) {
+ if (CTX_data_equals(member, "material")) {
set_pointer_type(path, result, &RNA_Material);
return 1;
}
- else if (CTX_data_equals(member, "texture")) {
+ if (CTX_data_equals(member, "texture")) {
ButsContextTexture *ct = sbuts->texuser;
if (ct) {
@@ -905,7 +896,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
return 1;
}
- else if (CTX_data_equals(member, "material_slot")) {
+ if (CTX_data_equals(member, "material_slot")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
if (ptr) {
@@ -923,7 +914,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
return 1;
}
- else if (CTX_data_equals(member, "texture_user")) {
+ if (CTX_data_equals(member, "texture_user")) {
ButsContextTexture *ct = sbuts->texuser;
if (!ct) {
@@ -937,7 +928,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
return 1;
}
- else if (CTX_data_equals(member, "texture_user_property")) {
+ if (CTX_data_equals(member, "texture_user_property")) {
ButsContextTexture *ct = sbuts->texuser;
if (!ct) {
@@ -951,7 +942,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
return 1;
}
- else if (CTX_data_equals(member, "texture_node")) {
+ if (CTX_data_equals(member, "texture_node")) {
ButsContextTexture *ct = sbuts->texuser;
if (ct) {
@@ -962,8 +953,9 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "texture_slot")) {
+ if (CTX_data_equals(member, "texture_slot")) {
ButsContextTexture *ct = sbuts->texuser;
PointerRNA *ptr;
@@ -990,23 +982,23 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
return 1;
}
- else if (CTX_data_equals(member, "bone")) {
+ if (CTX_data_equals(member, "bone")) {
set_pointer_type(path, result, &RNA_Bone);
return 1;
}
- else if (CTX_data_equals(member, "edit_bone")) {
+ if (CTX_data_equals(member, "edit_bone")) {
set_pointer_type(path, result, &RNA_EditBone);
return 1;
}
- else if (CTX_data_equals(member, "pose_bone")) {
+ if (CTX_data_equals(member, "pose_bone")) {
set_pointer_type(path, result, &RNA_PoseBone);
return 1;
}
- else if (CTX_data_equals(member, "particle_system")) {
+ if (CTX_data_equals(member, "particle_system")) {
set_pointer_type(path, result, &RNA_ParticleSystem);
return 1;
}
- else if (CTX_data_equals(member, "particle_system_editable")) {
+ if (CTX_data_equals(member, "particle_system_editable")) {
if (PE_poll((bContext *)C)) {
set_pointer_type(path, result, &RNA_ParticleSystem);
}
@@ -1015,7 +1007,7 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
}
return 1;
}
- else if (CTX_data_equals(member, "particle_settings")) {
+ if (CTX_data_equals(member, "particle_settings")) {
/* only available when pinned */
PointerRNA *ptr = get_pointer_type(path, &RNA_ParticleSettings);
@@ -1023,20 +1015,20 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, ptr->data);
return 1;
}
- else {
- /* get settings from active particle system instead */
- ptr = get_pointer_type(path, &RNA_ParticleSystem);
- if (ptr && ptr->data) {
- ParticleSettings *part = ((ParticleSystem *)ptr->data)->part;
- CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, part);
- return 1;
- }
+ /* get settings from active particle system instead */
+ ptr = get_pointer_type(path, &RNA_ParticleSystem);
+
+ if (ptr && ptr->data) {
+ ParticleSettings *part = ((ParticleSystem *)ptr->data)->part;
+ CTX_data_pointer_set(result, ptr->owner_id, &RNA_ParticleSettings, part);
+ return 1;
}
+
set_pointer_type(path, result, &RNA_ParticleSettings);
return 1;
}
- else if (CTX_data_equals(member, "cloth")) {
+ if (CTX_data_equals(member, "cloth")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
if (ptr && ptr->data) {
@@ -1045,8 +1037,9 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
CTX_data_pointer_set(result, &ob->id, &RNA_ClothModifier, md);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "soft_body")) {
+ if (CTX_data_equals(member, "soft_body")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
if (ptr && ptr->data) {
@@ -1055,9 +1048,10 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
CTX_data_pointer_set(result, &ob->id, &RNA_SoftBodyModifier, md);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "fluid")) {
+ if (CTX_data_equals(member, "fluid")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
if (ptr && ptr->data) {
@@ -1066,8 +1060,9 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
CTX_data_pointer_set(result, &ob->id, &RNA_FluidModifier, md);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "collision")) {
+ if (CTX_data_equals(member, "collision")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
if (ptr && ptr->data) {
@@ -1076,12 +1071,13 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
CTX_data_pointer_set(result, &ob->id, &RNA_CollisionModifier, md);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "brush")) {
+ if (CTX_data_equals(member, "brush")) {
set_pointer_type(path, result, &RNA_Brush);
return 1;
}
- else if (CTX_data_equals(member, "dynamic_paint")) {
+ if (CTX_data_equals(member, "dynamic_paint")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
if (ptr && ptr->data) {
@@ -1090,20 +1086,17 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
CTX_data_pointer_set(result, &ob->id, &RNA_DynamicPaintModifier, md);
return 1;
}
+ return -1; /* found but not available */
}
- else if (CTX_data_equals(member, "line_style")) {
+ if (CTX_data_equals(member, "line_style")) {
set_pointer_type(path, result, &RNA_FreestyleLineStyle);
return 1;
}
- else if (CTX_data_equals(member, "gpencil")) {
+ if (CTX_data_equals(member, "gpencil")) {
set_pointer_type(path, result, &RNA_GreasePencil);
return 1;
}
- else {
- return 0; /* not found */
- }
-
- return -1; /* found but not available */
+ return 0; /* not found */
}
/************************* Drawing the Path ************************/
@@ -1154,13 +1147,13 @@ void buttons_context_draw(const bContext *C, uiLayout *layout)
ptr->type == &RNA_Scene)) {
continue;
}
- else if ((!ELEM(sbuts->mainb,
- BCONTEXT_RENDER,
- BCONTEXT_OUTPUT,
- BCONTEXT_SCENE,
- BCONTEXT_VIEW_LAYER,
- BCONTEXT_WORLD) &&
- ptr->type == &RNA_ViewLayer)) {
+ if ((!ELEM(sbuts->mainb,
+ BCONTEXT_RENDER,
+ BCONTEXT_OUTPUT,
+ BCONTEXT_SCENE,
+ BCONTEXT_VIEW_LAYER,
+ BCONTEXT_WORLD) &&
+ ptr->type == &RNA_ViewLayer)) {
continue;
}
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index 9af623d8065..a062b178fc8 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -52,7 +52,9 @@
#include "buttons_intern.h" /* own include */
-/********************** context_menu operator *********************/
+/* -------------------------------------------------------------------- */
+/** \name Context Menu Operator
+ * \{ */
static int context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
{
@@ -67,22 +69,27 @@ static int context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven
void BUTTONS_OT_context_menu(wmOperatorType *ot)
{
- /* identifiers */
+ /* Identifiers. */
ot->name = "Context Menu";
ot->description = "Display properties editor context_menu";
ot->idname = "BUTTONS_OT_context_menu";
- /* api callbacks */
+ /* Callbacks. */
ot->invoke = context_menu_invoke;
ot->poll = ED_operator_buttons_active;
}
-/********************** filebrowse operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name File Browse Operator
+ * \{ */
typedef struct FileBrowseOp {
PointerRNA ptr;
PropertyRNA *prop;
bool is_undo;
+ bool is_userdef;
} FileBrowseOp;
static int file_browse_exec(bContext *C, wmOperator *op)
@@ -100,7 +107,7 @@ static int file_browse_exec(bContext *C, wmOperator *op)
str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0);
- /* add slash for directories, important for some properties */
+ /* Add slash for directories, important for some properties. */
if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) {
const bool is_relative = RNA_boolean_get(op->ptr, "relative_path");
id = fbo->ptr.owner_id;
@@ -109,7 +116,7 @@ static int file_browse_exec(bContext *C, wmOperator *op)
BLI_path_abs(path, id ? ID_BLEND_PATH(bmain, id) : BKE_main_blendfile_path(bmain));
if (BLI_is_dir(path)) {
- /* do this first so '//' isnt converted to '//\' on windows */
+ /* Do this first so '//' isnt converted to '//\' on windows. */
BLI_path_slash_ensure(path);
if (is_relative) {
BLI_strncpy(path, str, FILE_MAX);
@@ -138,7 +145,7 @@ static int file_browse_exec(bContext *C, wmOperator *op)
ED_undo_push(C, undostr);
}
- /* special, annoying exception, filesel on redo panel [#26618] */
+ /* Special annoying exception, filesel on redo panel [#26618]. */
{
wmOperator *redo_op = WM_operator_last_redo(C);
if (redo_op) {
@@ -148,6 +155,11 @@ static int file_browse_exec(bContext *C, wmOperator *op)
}
}
+ /* Tag user preferences as dirty. */
+ if (fbo->is_userdef) {
+ U.runtime.is_dirty = true;
+ }
+
MEM_freeN(op->customdata);
return OPERATOR_FINISHED;
@@ -164,6 +176,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
PointerRNA ptr;
PropertyRNA *prop;
bool is_undo;
+ bool is_userdef;
FileBrowseOp *fbo;
char *str;
@@ -172,7 +185,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- UI_context_active_but_prop_get_filebrowser(C, &ptr, &prop, &is_undo);
+ UI_context_active_but_prop_get_filebrowser(C, &ptr, &prop, &is_undo, &is_userdef);
if (!prop) {
return OPERATOR_CANCELLED;
@@ -180,8 +193,8 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
str = RNA_property_string_get_alloc(&ptr, prop, NULL, 0, NULL);
- /* useful yet irritating feature, Shift+Click to open the file
- * Alt+Click to browse a folder in the OS's browser */
+ /* Useful yet irritating feature, Shift+Click to open the file
+ * Alt+Click to browse a folder in the OS's browser. */
if (event->shift || event->alt) {
wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true);
PointerRNA props_ptr;
@@ -201,63 +214,63 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
MEM_freeN(str);
return OPERATOR_CANCELLED;
}
- else {
- PropertyRNA *prop_relpath;
- const char *path_prop = RNA_struct_find_property(op->ptr, "directory") ? "directory" :
- "filepath";
- fbo = MEM_callocN(sizeof(FileBrowseOp), "FileBrowseOp");
- fbo->ptr = ptr;
- fbo->prop = prop;
- fbo->is_undo = is_undo;
- op->customdata = fbo;
-
- /* normally ED_fileselect_get_params would handle this but we need to because of stupid
- * user-prefs exception - campbell */
- if ((prop_relpath = RNA_struct_find_property(op->ptr, "relative_path"))) {
- if (!RNA_property_is_set(op->ptr, prop_relpath)) {
- bool is_relative = (U.flag & USER_RELPATHS) != 0;
-
- /* while we want to follow the defaults,
- * we better not switch existing paths relative/absolute state. */
- if (str[0]) {
- is_relative = BLI_path_is_rel(str);
- }
-
- if (UNLIKELY(ptr.data == &U)) {
- is_relative = false;
- }
-
- /* annoying exception!, if we're dealing with the user prefs, default relative to be off */
- RNA_property_boolean_set(op->ptr, prop_relpath, is_relative);
+
+ PropertyRNA *prop_relpath;
+ const char *path_prop = RNA_struct_find_property(op->ptr, "directory") ? "directory" :
+ "filepath";
+ fbo = MEM_callocN(sizeof(FileBrowseOp), "FileBrowseOp");
+ fbo->ptr = ptr;
+ fbo->prop = prop;
+ fbo->is_undo = is_undo;
+ fbo->is_userdef = is_userdef;
+ op->customdata = fbo;
+
+ /* Normally ED_fileselect_get_params would handle this but we need to because of stupid
+ * user-prefs exception. - campbell */
+ if ((prop_relpath = RNA_struct_find_property(op->ptr, "relative_path"))) {
+ if (!RNA_property_is_set(op->ptr, prop_relpath)) {
+ bool is_relative = (U.flag & USER_RELPATHS) != 0;
+
+ /* While we want to follow the defaults,
+ * we better not switch existing paths relative/absolute state. */
+ if (str[0]) {
+ is_relative = BLI_path_is_rel(str);
+ }
+
+ if (UNLIKELY(ptr.data == &U)) {
+ is_relative = false;
}
+
+ /* Annoying exception!, if we're dealing with the user prefs, default relative to be off. */
+ RNA_property_boolean_set(op->ptr, prop_relpath, is_relative);
}
+ }
- RNA_string_set(op->ptr, path_prop, str);
- MEM_freeN(str);
+ RNA_string_set(op->ptr, path_prop, str);
+ MEM_freeN(str);
- WM_event_add_fileselect(C, op);
+ WM_event_add_fileselect(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
+ return OPERATOR_RUNNING_MODAL;
}
void BUTTONS_OT_file_browse(wmOperatorType *ot)
{
- /* identifiers */
+ /* Identifiers. */
ot->name = "Accept";
ot->description =
"Open a file browser, Hold Shift to open the file, Alt to browse containing directory";
ot->idname = "BUTTONS_OT_file_browse";
- /* api callbacks */
+ /* Callbacks. */
ot->invoke = file_browse_invoke;
ot->exec = file_browse_exec;
ot->cancel = file_browse_cancel;
- /* conditional undo based on button flag */
+ /* Conditional undo based on button flag. */
ot->flag = 0;
- /* properties */
+ /* Properties. */
WM_operator_properties_filesel(ot,
0,
FILE_SPECIAL,
@@ -267,7 +280,7 @@ void BUTTONS_OT_file_browse(wmOperatorType *ot)
FILE_SORT_ALPHA);
}
-/* second operator, only difference from BUTTONS_OT_file_browse is WM_FILESEL_DIRECTORY */
+/* Second operator, only difference from BUTTONS_OT_file_browse is WM_FILESEL_DIRECTORY. */
void BUTTONS_OT_directory_browse(wmOperatorType *ot)
{
/* identifiers */
@@ -293,3 +306,5 @@ void BUTTONS_OT_directory_browse(wmOperatorType *ot)
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
}
+
+/** \} */
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 3dc5eca8a8b..8e5aa00115b 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -63,7 +63,6 @@
#include "UI_interface.h"
#include "UI_resources.h"
-#include "ED_buttons.h"
#include "ED_node.h"
#include "ED_screen.h"
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 187334a5c34..67efd8f4b8e 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -30,11 +30,12 @@
#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 "BKE_shader_fx.h"
-#include "DNA_modifier_types.h"
-
+#include "ED_buttons.h"
#include "ED_screen.h"
#include "ED_space_api.h"
#include "ED_view3d.h" /* To draw toolbar UI. */
@@ -49,8 +50,6 @@
#include "UI_resources.h"
-#include "GPU_glew.h"
-
#include "buttons_intern.h" /* own include */
/* ******************** default callbacks for buttons space ***************** */
@@ -139,6 +138,98 @@ static void buttons_main_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->handlers, keymap);
}
+/**
+ * Fills an array with the tab context values for the properties editor. -1 signals a separator.
+ *
+ * \return The total number of items in the array returned.
+ */
+int ED_buttons_tabs_list(SpaceProperties *sbuts, int *context_tabs_array)
+{
+ int length = 0;
+ if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) {
+ context_tabs_array[length] = BCONTEXT_TOOL;
+ length++;
+ }
+ if (length != 0) {
+ context_tabs_array[length] = -1;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) {
+ context_tabs_array[length] = BCONTEXT_RENDER;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) {
+ context_tabs_array[length] = BCONTEXT_OUTPUT;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) {
+ context_tabs_array[length] = BCONTEXT_VIEW_LAYER;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) {
+ context_tabs_array[length] = BCONTEXT_SCENE;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) {
+ context_tabs_array[length] = BCONTEXT_WORLD;
+ length++;
+ }
+ if (length != 0) {
+ context_tabs_array[length] = -1;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) {
+ context_tabs_array[length] = BCONTEXT_OBJECT;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) {
+ context_tabs_array[length] = BCONTEXT_MODIFIER;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) {
+ context_tabs_array[length] = BCONTEXT_SHADERFX;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) {
+ context_tabs_array[length] = BCONTEXT_PARTICLE;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) {
+ context_tabs_array[length] = BCONTEXT_PHYSICS;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) {
+ context_tabs_array[length] = BCONTEXT_CONSTRAINT;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_DATA)) {
+ context_tabs_array[length] = BCONTEXT_DATA;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_BONE)) {
+ context_tabs_array[length] = BCONTEXT_BONE;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) {
+ context_tabs_array[length] = BCONTEXT_BONE_CONSTRAINT;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) {
+ context_tabs_array[length] = BCONTEXT_MATERIAL;
+ length++;
+ }
+ if (length != 0) {
+ context_tabs_array[length] = -1;
+ length++;
+ }
+ if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) {
+ context_tabs_array[length] = BCONTEXT_TEXTURE;
+ length++;
+ }
+
+ return length;
+}
+
static void buttons_main_region_layout_properties(const bContext *C,
SpaceProperties *sbuts,
ARegion *region)
@@ -422,6 +513,9 @@ static void buttons_area_listener(wmWindow *UNUSED(win),
buttons_area_redraw(area, BCONTEXT_CONSTRAINT);
buttons_area_redraw(area, BCONTEXT_BONE_CONSTRAINT);
break;
+ case ND_SHADERFX:
+ buttons_area_redraw(area, BCONTEXT_SHADERFX);
+ break;
case ND_PARTICLE:
if (wmn->action == NA_EDITED) {
buttons_area_redraw(area, BCONTEXT_PARTICLE);
@@ -435,13 +529,6 @@ static void buttons_area_listener(wmWindow *UNUSED(win),
/* Needed to refresh context path when changing active particle system index. */
buttons_area_redraw(area, BCONTEXT_PARTICLE);
break;
- case ND_SHADING:
- case ND_SHADING_DRAW:
- case ND_SHADING_LINKS:
- case ND_SHADING_PREVIEW:
- /* currently works by redraws... if preview is set, it (re)starts job */
- sbuts->preview = 1;
- break;
default:
/* Not all object RNA props have a ND_ notifier (yet) */
ED_area_tag_redraw(area);
@@ -643,6 +730,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_draw.c b/source/blender/editors/space_clip/clip_draw.c
index fe7ae7096a0..1d510d2989c 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -88,7 +88,7 @@ static int generic_track_get_markersnr(MovieTrackingTrack *track,
if (track) {
return track->markersnr;
}
- else if (plane_track) {
+ if (plane_track) {
return plane_track->markersnr;
}
@@ -103,7 +103,7 @@ static int generic_track_get_marker_framenr(MovieTrackingTrack *track,
BLI_assert(marker_index < track->markersnr);
return track->markers[marker_index].framenr;
}
- else if (plane_track) {
+ if (plane_track) {
BLI_assert(marker_index < plane_track->markersnr);
return plane_track->markers[marker_index].framenr;
}
@@ -119,7 +119,7 @@ static bool generic_track_is_marker_enabled(MovieTrackingTrack *track,
BLI_assert(marker_index < track->markersnr);
return (track->markers[marker_index].flag & MARKER_DISABLED) == 0;
}
- else if (plane_track) {
+ if (plane_track) {
return true;
}
@@ -134,7 +134,7 @@ static bool generic_track_is_marker_keyframed(MovieTrackingTrack *track,
BLI_assert(marker_index < track->markersnr);
return (track->markers[marker_index].flag & MARKER_TRACKED) == 0;
}
- else if (plane_track) {
+ if (plane_track) {
BLI_assert(marker_index < plane_track->markersnr);
return (plane_track->markers[marker_index].flag & PLANE_MARKER_TRACKED) == 0;
}
@@ -228,7 +228,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
ok = true;
break;
}
- else if (cameras[a].framenr > i) {
+ if (cameras[a].framenr > i) {
break;
}
@@ -322,7 +322,7 @@ static void draw_movieclip_buffer(const bContext *C,
float zoomy)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
- int filter = GL_LINEAR;
+ bool use_filter = true;
int x, y;
/* find window pixel coordinates of origin */
@@ -340,10 +340,10 @@ static void draw_movieclip_buffer(const bContext *C,
/* non-scaled proxy shouldn't use filtering */
if ((clip->flag & MCLIP_USE_PROXY) == 0 ||
ELEM(sc->user.render_size, MCLIP_PROXY_RENDER_SIZE_FULL, MCLIP_PROXY_RENDER_SIZE_100)) {
- filter = GL_NEAREST;
+ use_filter = false;
}
- ED_draw_imbuf_ctx(C, ibuf, x, y, filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y);
+ ED_draw_imbuf_ctx(C, ibuf, x, y, use_filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y);
if (ibuf->planes == 32) {
GPU_blend(false);
@@ -373,8 +373,7 @@ static void draw_stabilization_border(
/* Exclusive OR allows to get orig value when second operand is 0,
* and negative of orig value when second operand is 1. */
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_XOR);
+ GPU_logic_op_xor_set(true);
GPU_matrix_push();
GPU_matrix_translate_2f(x, y);
@@ -399,7 +398,7 @@ static void draw_stabilization_border(
GPU_matrix_pop();
- glDisable(GL_COLOR_LOGIC_OP);
+ GPU_logic_op_xor_set(false);
}
}
@@ -790,15 +789,14 @@ static void draw_marker_areas(SpaceClip *sc,
immUniform1f("dash_width", 6.0f);
immUniform1f("dash_factor", 0.5f);
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_XOR);
+ GPU_logic_op_xor_set(true);
immBegin(GPU_PRIM_LINES, 2);
immVertex2fv(shdr_pos, pos);
immVertex2fv(shdr_pos, marker_pos);
immEnd();
- glDisable(GL_COLOR_LOGIC_OP);
+ GPU_logic_op_xor_set(false);
}
}
@@ -1111,7 +1109,7 @@ static void draw_marker_texts(SpaceClip *sc,
pos[1] -= fontsize;
if (track->flag & TRACK_HAS_BUNDLE) {
- BLI_snprintf(str, sizeof(str), "Average error: %.3f", track->error);
+ BLI_snprintf(str, sizeof(str), "Average error: %.2f px", track->error);
BLF_position(fontid, pos[0], pos[1], 0.0f);
BLF_draw(fontid, str, sizeof(str));
pos[1] -= fontsize;
@@ -1203,7 +1201,6 @@ static void draw_plane_marker_image(Scene *scene,
}
if (display_buffer) {
- GLuint texid;
float frame_corners[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};
float perspective_matrix[3][3];
float gl_matrix[4][4];
@@ -1220,23 +1217,17 @@ static void draw_plane_marker_image(Scene *scene,
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
}
- glGenTextures(1, (GLuint *)&texid);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, texid);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glTexImage2D(GL_TEXTURE_2D,
- 0,
- GL_RGBA8,
- ibuf->x,
- ibuf->y,
- 0,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- display_buffer);
+ GPUTexture *texture = GPU_texture_create_nD(ibuf->x,
+ ibuf->y,
+ 0,
+ 2,
+ display_buffer,
+ GPU_RGBA8,
+ GPU_DATA_UNSIGNED_BYTE,
+ 0,
+ false,
+ NULL);
+ GPU_texture_filter_mode(texture, false);
GPU_matrix_push();
GPU_matrix_mul(gl_matrix);
@@ -1246,10 +1237,10 @@ static void draw_plane_marker_image(Scene *scene,
uint texCoord = GPU_vertformat_attr_add(
imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA);
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
- immUniform1f("alpha", plane_track->image_opacity);
- immUniform1i("image", 0);
+ immBindTexture("image", texture);
+ immUniformColor4f(1.0f, 1.0f, 1.0f, plane_track->image_opacity);
immBegin(GPU_PRIM_TRI_FAN, 4);
@@ -1271,7 +1262,8 @@ static void draw_plane_marker_image(Scene *scene,
GPU_matrix_pop();
- glBindTexture(GL_TEXTURE_2D, 0);
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
if (transparent) {
GPU_blend(false);
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 5be4b2d5df0..83096b4eded 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -381,21 +381,20 @@ static bool selected_boundbox(const bContext *C, float min[2], float max[2])
if (sc->mode == SC_MODE_TRACKING) {
return selected_tracking_boundbox(sc, min, max);
}
- else {
- if (ED_mask_selected_minmax(C, min, max)) {
- MovieClip *clip = ED_space_clip_get_clip(sc);
- int width, height;
- ED_space_clip_get_size(sc, &width, &height);
- BKE_mask_coord_to_movieclip(clip, &sc->user, min, min);
- BKE_mask_coord_to_movieclip(clip, &sc->user, max, max);
- min[0] *= width;
- min[1] *= height;
- max[0] *= width;
- max[1] *= height;
- return true;
- }
- return false;
+
+ if (ED_mask_selected_minmax(C, min, max)) {
+ MovieClip *clip = ED_space_clip_get_clip(sc);
+ int width, height;
+ ED_space_clip_get_size(sc, &width, &height);
+ BKE_mask_coord_to_movieclip(clip, &sc->user, min, min);
+ BKE_mask_coord_to_movieclip(clip, &sc->user, max, max);
+ min[0] *= width;
+ min[1] *= height;
+ max[0] *= width;
+ max[1] *= height;
+ return true;
}
+ return false;
}
bool ED_clip_view_selection(const bContext *C, ARegion *region, bool fit)
@@ -726,7 +725,7 @@ typedef struct PrefetchQueue {
int initial_frame, current_frame, start_frame, end_frame;
short render_size, render_flag;
- /* If true prefecthing goes forward in time,
+ /* If true pre-fetching goes forward in time,
* otherwise it goes backwards in time (starting from current frame).
*/
bool forward;
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 984aa0a63ad..8532d8420f9 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);
@@ -463,11 +463,10 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_FINISHED;
}
- else {
- view_pan_init(C, op, event);
- return OPERATOR_RUNNING_MODAL;
- }
+ view_pan_init(C, op, event);
+
+ return OPERATOR_RUNNING_MODAL;
}
static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
@@ -633,11 +632,10 @@ static int view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_FINISHED;
}
- else {
- view_zoom_init(C, op, event);
- return OPERATOR_RUNNING_MODAL;
- }
+ view_zoom_init(C, op, event);
+
+ return OPERATOR_RUNNING_MODAL;
}
static void view_zoom_apply(
@@ -1253,10 +1251,9 @@ static void do_movie_proxy(void *pjv,
return;
}
- else {
- sfra = 1;
- efra = clip->len;
- }
+
+ sfra = 1;
+ efra = clip->len;
if (build_undistort_count) {
int threads = BLI_system_thread_count();
@@ -1406,6 +1403,8 @@ static void do_sequence_proxy(void *pjv,
int build_count,
int *build_undistort_sizes,
int build_undistort_count,
+ /* Cannot be const, because it is assigned to a non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
short *stop,
short *do_update,
float *progress)
@@ -1516,7 +1515,7 @@ static void proxy_endjob(void *pjv)
}
if (pj->clip->source == MCLIP_SRC_MOVIE) {
- /* Timecode might have changed, so do a full reload to deal with this. */
+ /* Time-code might have changed, so do a full reload to deal with this. */
DEG_id_tag_update(&pj->clip->id, ID_RECALC_SOURCE);
}
else {
@@ -1646,27 +1645,26 @@ static int clip_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv
if (event->type != NDOF_MOTION) {
return OPERATOR_CANCELLED;
}
- else {
- SpaceClip *sc = CTX_wm_space_clip(C);
- ARegion *region = CTX_wm_region(C);
- float pan_vec[3];
- const wmNDOFMotionData *ndof = event->customdata;
- const float speed = NDOF_PIXELS_PER_SECOND;
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ ARegion *region = CTX_wm_region(C);
+ float pan_vec[3];
+
+ const wmNDOFMotionData *ndof = event->customdata;
+ const float speed = NDOF_PIXELS_PER_SECOND;
- WM_event_ndof_pan_get(ndof, pan_vec, true);
+ WM_event_ndof_pan_get(ndof, pan_vec, true);
- mul_v2_fl(pan_vec, (speed * ndof->dt) / sc->zoom);
- pan_vec[2] *= -ndof->dt;
+ mul_v2_fl(pan_vec, (speed * ndof->dt) / sc->zoom);
+ pan_vec[2] *= -ndof->dt;
- sclip_zoom_set_factor(C, 1.0f + pan_vec[2], NULL, false);
- sc->xof += pan_vec[0];
- sc->yof += pan_vec[1];
+ sclip_zoom_set_factor(C, 1.0f + pan_vec[2], NULL, false);
+ sc->xof += pan_vec[0];
+ sc->yof += pan_vec[1];
- ED_region_tag_redraw(region);
+ ED_region_tag_redraw(region);
- return OPERATOR_FINISHED;
- }
+ return OPERATOR_FINISHED;
}
void CLIP_OT_view_ndof(wmOperatorType *ot)
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index a68e06951f7..37d5f602291 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -56,7 +56,6 @@
#include "IMB_imbuf.h"
#include "GPU_framebuffer.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "WM_api.h"
@@ -96,7 +95,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;
@@ -585,13 +584,13 @@ static int clip_context(const bContext *C, const char *member, bContextDataResul
return true;
}
- else if (CTX_data_equals(member, "edit_movieclip")) {
+ if (CTX_data_equals(member, "edit_movieclip")) {
if (sc->clip) {
CTX_data_id_pointer_set(result, &sc->clip->id);
}
return true;
}
- else if (CTX_data_equals(member, "edit_mask")) {
+ if (CTX_data_equals(member, "edit_mask")) {
if (sc->mask_info.mask) {
CTX_data_id_pointer_set(result, &sc->mask_info.mask->id);
}
@@ -1045,7 +1044,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 +1074,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 +1091,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 +1122,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_clip/tracking_ops_detect.c b/source/blender/editors/space_clip/tracking_ops_detect.c
index dd620e85324..54ec439471d 100644
--- a/source/blender/editors/space_clip/tracking_ops_detect.c
+++ b/source/blender/editors/space_clip/tracking_ops_detect.c
@@ -117,13 +117,13 @@ void CLIP_OT_detect_features(wmOperatorType *ot)
{1,
"INSIDE_GPENCIL",
0,
- "Inside Grease Pencil",
- "Place markers only inside areas outlined with Grease Pencil"},
+ "Inside Annotated Area",
+ "Place markers only inside areas outlined with the Annotation tool"},
{2,
"OUTSIDE_GPENCIL",
0,
- "Outside Grease Pencil",
- "Place markers only outside areas outlined with Grease Pencil"},
+ "Outside Annotated Area",
+ "Place markers only outside areas outlined with the Annotation tool"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/space_clip/tracking_ops_orient.c b/source/blender/editors/space_clip/tracking_ops_orient.c
index 25e30c116c5..04c335cdf5e 100644
--- a/source/blender/editors/space_clip/tracking_ops_orient.c
+++ b/source/blender/editors/space_clip/tracking_ops_orient.c
@@ -110,9 +110,7 @@ static bool set_orientation_poll(bContext *C)
if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
return true;
}
- else {
- return OBACT(view_layer) != NULL;
- }
+ return OBACT(view_layer) != NULL;
}
}
return false;
diff --git a/source/blender/editors/space_clip/tracking_ops_plane.c b/source/blender/editors/space_clip/tracking_ops_plane.c
index 3c5646911a3..964b59babee 100644
--- a/source/blender/editors/space_clip/tracking_ops_plane.c
+++ b/source/blender/editors/space_clip/tracking_ops_plane.c
@@ -61,18 +61,17 @@ static int create_plane_track_tracks_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Need at least 4 selected point tracks to create a plane");
return OPERATOR_CANCELLED;
}
- else {
- BKE_tracking_tracks_deselect_all(tracks_base);
- plane_track->flag |= SELECT;
- clip->tracking.act_track = NULL;
- clip->tracking.act_plane_track = plane_track;
+ BKE_tracking_tracks_deselect_all(tracks_base);
- /* Compute homoraphies and apply them on marker's corner, so we've got
- * quite nice motion from the very beginning.
- */
- BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
- }
+ plane_track->flag |= SELECT;
+ clip->tracking.act_track = NULL;
+ clip->tracking.act_plane_track = plane_track;
+
+ /* Compute homoraphies and apply them on marker's corner, so we've got
+ * quite nice motion from the very beginning.
+ */
+ BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
DEG_id_tag_update(&clip->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c
index c18207d7045..1ed965c30d2 100644
--- a/source/blender/editors/space_clip/tracking_ops_solve.c
+++ b/source/blender/editors/space_clip/tracking_ops_solve.c
@@ -144,7 +144,7 @@ static void solve_camera_freejob(void *scv)
else {
BKE_reportf(scj->reports,
RPT_INFO,
- "Average re-projection error: %.3f",
+ "Average re-projection error: %.2f px",
tracking->reconstruction.error);
}
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index 05623347366..afbca48bd45 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -214,7 +214,13 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa
return true;
}
-static void track_markers_startjob(void *tmv, short *stop, short *do_update, float *progress)
+static void track_markers_startjob(
+ void *tmv,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
int framenr = tmj->sfra;
@@ -358,14 +364,13 @@ static int track_markers(bContext *C, wmOperator *op, bool use_job)
return OPERATOR_RUNNING_MODAL;
}
- else {
- short stop = 0, do_update = 0;
- float progress = 0.0f;
- track_markers_startjob(tmj, &stop, &do_update, &progress);
- track_markers_endjob(tmj);
- track_markers_freejob(tmj);
- return OPERATOR_FINISHED;
- }
+
+ short stop = 0, do_update = 0;
+ float progress = 0.0f;
+ track_markers_startjob(tmj, &stop, &do_update, &progress);
+ track_markers_endjob(tmj);
+ track_markers_freejob(tmj);
+ return OPERATOR_FINISHED;
}
static int track_markers_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c
index 81cc858c69f..b6f9ca9589f 100644
--- a/source/blender/editors/space_clip/tracking_select.c
+++ b/source/blender/editors/space_clip/tracking_select.c
@@ -731,7 +731,9 @@ void CLIP_OT_select_lasso(wmOperatorType *ot)
/********************** circle select operator *********************/
-static int point_inside_ellipse(float point[2], float offset[2], float ellipse[2])
+static int point_inside_ellipse(const float point[2],
+ const float offset[2],
+ const float ellipse[2])
{
/* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
float x, y;
diff --git a/source/blender/editors/space_console/console_draw.c b/source/blender/editors/space_console/console_draw.c
index 805e9608fec..7e3dbcfefa4 100644
--- a/source/blender/editors/space_console/console_draw.c
+++ b/source/blender/editors/space_console/console_draw.c
@@ -139,7 +139,6 @@ static void console_cursor_wrap_offset(
*column += col;
}
- return;
}
static void console_textview_draw_cursor(TextViewContext *tvc, int cwidth, int columns)
diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c
index 981e056fa63..f0d4a45a3dd 100644
--- a/source/blender/editors/space_console/console_ops.c
+++ b/source/blender/editors/space_console/console_ops.c
@@ -287,12 +287,11 @@ static bool console_line_column_from_index(
*r_col = offset - pos;
return true;
}
- else {
- *r_cl = NULL;
- *r_cl_offset = -1;
- *r_col = -1;
- return false;
- }
+
+ *r_cl = NULL;
+ *r_cl_offset = -1;
+ *r_col = -1;
+ return false;
}
/* static funcs for text editing */
@@ -403,9 +402,8 @@ static int console_insert_exec(bContext *C, wmOperator *op)
if (len == 0) {
return OPERATOR_CANCELLED;
}
- else {
- console_select_offset(sc, len);
- }
+
+ console_select_offset(sc, len);
console_textview_update_rect(sc, region);
ED_area_tag_redraw(CTX_wm_area(C));
@@ -426,21 +424,20 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
return OPERATOR_PASS_THROUGH;
}
- else {
- char str[BLI_UTF8_MAX + 1];
- size_t len;
- if (event->utf8_buf[0]) {
- len = BLI_str_utf8_size_safe(event->utf8_buf);
- memcpy(str, event->utf8_buf, len);
- }
- else {
- /* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str);
- }
- str[len] = '\0';
- RNA_string_set(op->ptr, "text", str);
+ char str[BLI_UTF8_MAX + 1];
+ size_t len;
+
+ if (event->utf8_buf[0]) {
+ len = BLI_str_utf8_size_safe(event->utf8_buf);
+ memcpy(str, event->utf8_buf, len);
+ }
+ else {
+ /* in theory, ghost can set value to extended ascii here */
+ len = BLI_str_utf8_from_unicode(event->ascii, str);
}
+ str[len] = '\0';
+ RNA_string_set(op->ptr, "text", str);
}
return console_insert_exec(C, op);
}
@@ -674,9 +671,8 @@ static int console_delete_exec(bContext *C, wmOperator *op)
if (!done) {
return OPERATOR_CANCELLED;
}
- else {
- console_select_offset(sc, -stride);
- }
+
+ console_select_offset(sc, -stride);
console_textview_update_rect(sc, region);
ED_area_tag_redraw(CTX_wm_area(C));
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/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 8d14664c0fa..68e9f76abc5 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -300,9 +300,8 @@ static void file_draw_preview(uiBlock *block,
(float)yco,
imb->x,
imb->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
+ GPU_RGBA8,
+ false,
imb->rect,
scale,
scale,
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 6552dfc18f3..45e45093238 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -778,7 +778,7 @@ static bool file_walk_select_do(bContext *C,
}
/* if we don't extend, selecting '..' (index == 0) is allowed so
* using key selection to go to parent directory is possible */
- else if (active_new != 0) {
+ if (active_new != 0) {
/* select initial file */
active_new = active_old;
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index d8d7ef01a2e..3ce80c11160 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -359,12 +359,24 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(
- struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
-static void filelist_readjob_lib(
- struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
-static void filelist_readjob_dir(
- struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+static void filelist_readjob_main(FileList *filelist,
+ const char *main_name,
+ short *stop,
+ short *do_update,
+ float *progress,
+ ThreadMutex *lock);
+static void filelist_readjob_lib(FileList *filelist,
+ const char *main_name,
+ short *stop,
+ short *do_update,
+ float *progress,
+ ThreadMutex *lock);
+static void filelist_readjob_dir(FileList *filelist,
+ const char *main_name,
+ short *stop,
+ short *do_update,
+ float *progress,
+ ThreadMutex *lock);
/* helper, could probably go in BKE actually? */
static int groupname_to_code(const char *group);
@@ -625,11 +637,10 @@ static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *fi
if (filename[0] == '.' && !ELEM(filename[1], '.', '\0')) {
return true; /* ignore .file */
}
- else {
- int len = strlen(filename);
- if ((len > 0) && (filename[len - 1] == '~')) {
- return true; /* ignore file~ */
- }
+
+ int len = strlen(filename);
+ if ((len > 0) && (filename[len - 1] == '~')) {
+ return true; /* ignore file~ */
}
/* filename might actually be a piece of path, in which case we have to check all its parts. */
@@ -999,43 +1010,41 @@ static int filelist_geticon_ex(FileDirEntry *file,
if (FILENAME_IS_PARENT(file->relpath)) {
return is_main ? ICON_FILE_PARENT : ICON_NONE;
}
- else if (typeflag & FILE_TYPE_APPLICATIONBUNDLE) {
+ if (typeflag & FILE_TYPE_APPLICATIONBUNDLE) {
return ICON_UGLYPACKAGE;
}
- else if (typeflag & FILE_TYPE_BLENDER) {
+ if (typeflag & FILE_TYPE_BLENDER) {
return ICON_FILE_BLEND;
}
- else if (is_main) {
+ if (is_main) {
/* Do not return icon for folders if icons are not 'main' draw type
* (e.g. when used over previews). */
return (file->attributes & FILE_ATTR_ANY_LINK) ? ICON_FOLDER_REDIRECT : ICON_FILE_FOLDER;
}
- else {
- /* If this path is in System list or path cache then use that icon. */
- struct FSMenu *fsmenu = ED_fsmenu_get();
- FSMenuCategory categories[] = {
- FS_CATEGORY_SYSTEM,
- FS_CATEGORY_SYSTEM_BOOKMARKS,
- FS_CATEGORY_OTHER,
- };
-
- for (int i = 0; i < ARRAY_SIZE(categories); i++) {
- FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, categories[i]);
- char fullpath[FILE_MAX_LIBEXTRA];
- char *target = fullpath;
- if (file->redirection_path) {
- target = file->redirection_path;
- }
- else {
- BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath);
- BLI_path_slash_ensure(fullpath);
- }
- for (; tfsm; tfsm = tfsm->next) {
- if (STREQ(tfsm->path, target)) {
- /* Never want a little folder inside a large one. */
- return (tfsm->icon == ICON_FILE_FOLDER) ? ICON_NONE : tfsm->icon;
- }
+ /* If this path is in System list or path cache then use that icon. */
+ struct FSMenu *fsmenu = ED_fsmenu_get();
+ FSMenuCategory categories[] = {
+ FS_CATEGORY_SYSTEM,
+ FS_CATEGORY_SYSTEM_BOOKMARKS,
+ FS_CATEGORY_OTHER,
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(categories); i++) {
+ FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, categories[i]);
+ char fullpath[FILE_MAX_LIBEXTRA];
+ char *target = fullpath;
+ if (file->redirection_path) {
+ target = file->redirection_path;
+ }
+ else {
+ BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath);
+ BLI_path_slash_ensure(fullpath);
+ }
+ for (; tfsm; tfsm = tfsm->next) {
+ if (STREQ(tfsm->path, target)) {
+ /* Never want a little folder inside a large one. */
+ return (tfsm->icon == ICON_FILE_FOLDER) ? ICON_NONE : tfsm->icon;
}
}
}
@@ -1043,10 +1052,10 @@ static int filelist_geticon_ex(FileDirEntry *file,
if (file->attributes & FILE_ATTR_OFFLINE) {
return ICON_ERROR;
}
- else if (file->attributes & FILE_ATTR_TEMPORARY) {
+ if (file->attributes & FILE_ATTR_TEMPORARY) {
return ICON_FILE_CACHE;
}
- else if (file->attributes & FILE_ATTR_SYSTEM) {
+ if (file->attributes & FILE_ATTR_SYSTEM) {
return ICON_SYSTEM;
}
}
@@ -1054,49 +1063,49 @@ static int filelist_geticon_ex(FileDirEntry *file,
if (typeflag & FILE_TYPE_BLENDER) {
return ICON_FILE_BLEND;
}
- else if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
+ if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
return ICON_FILE_BACKUP;
}
- else if (typeflag & FILE_TYPE_IMAGE) {
+ if (typeflag & FILE_TYPE_IMAGE) {
return ICON_FILE_IMAGE;
}
- else if (typeflag & FILE_TYPE_MOVIE) {
+ if (typeflag & FILE_TYPE_MOVIE) {
return ICON_FILE_MOVIE;
}
- else if (typeflag & FILE_TYPE_PYSCRIPT) {
+ if (typeflag & FILE_TYPE_PYSCRIPT) {
return ICON_FILE_SCRIPT;
}
- else if (typeflag & FILE_TYPE_SOUND) {
+ if (typeflag & FILE_TYPE_SOUND) {
return ICON_FILE_SOUND;
}
- else if (typeflag & FILE_TYPE_FTFONT) {
+ if (typeflag & FILE_TYPE_FTFONT) {
return ICON_FILE_FONT;
}
- else if (typeflag & FILE_TYPE_BTX) {
+ if (typeflag & FILE_TYPE_BTX) {
return ICON_FILE_BLANK;
}
- else if (typeflag & FILE_TYPE_COLLADA) {
+ if (typeflag & FILE_TYPE_COLLADA) {
return ICON_FILE_3D;
}
- else if (typeflag & FILE_TYPE_ALEMBIC) {
+ if (typeflag & FILE_TYPE_ALEMBIC) {
return ICON_FILE_3D;
}
- else if (typeflag & FILE_TYPE_USD) {
+ if (typeflag & FILE_TYPE_USD) {
return ICON_FILE_3D;
}
- else if (typeflag & FILE_TYPE_VOLUME) {
+ if (typeflag & FILE_TYPE_VOLUME) {
return ICON_FILE_VOLUME;
}
- else if (typeflag & FILE_TYPE_OBJECT_IO) {
+ if (typeflag & FILE_TYPE_OBJECT_IO) {
return ICON_FILE_3D;
}
- else if (typeflag & FILE_TYPE_TEXT) {
+ if (typeflag & FILE_TYPE_TEXT) {
return ICON_FILE_TEXT;
}
- else if (typeflag & FILE_TYPE_ARCHIVE) {
+ if (typeflag & FILE_TYPE_ARCHIVE) {
return ICON_FILE_ARCHIVE;
}
- else if (typeflag & FILE_TYPE_BLENDERLIB) {
+ if (typeflag & FILE_TYPE_BLENDERLIB) {
const int ret = UI_idcode_icon_get(file->blentype);
if (ret != ICON_NONE) {
return ret;
@@ -1133,9 +1142,7 @@ static bool filelist_checkdir_dir(struct FileList *UNUSED(filelist),
parent_dir_until_exists_or_default_root(r_dir);
return true;
}
- else {
- return BLI_is_dir(r_dir);
- }
+ return BLI_is_dir(r_dir);
}
static bool filelist_checkdir_lib(struct FileList *UNUSED(filelist),
@@ -2106,7 +2113,7 @@ void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
return;
}
/* Do not start preview work while listing, gives nasty flickering! */
- else if (use_previews && (filelist->flags & FL_IS_READY)) {
+ if (use_previews && (filelist->flags & FL_IS_READY)) {
cache->flags |= FLC_PREVIEWS_ACTIVE;
BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL));
@@ -2215,67 +2222,65 @@ int ED_path_extension_type(const char *path)
if (BLO_has_bfile_extension(path)) {
return FILE_TYPE_BLENDER;
}
- else if (file_is_blend_backup(path)) {
+ if (file_is_blend_backup(path)) {
return FILE_TYPE_BLENDER_BACKUP;
}
- else if (BLI_path_extension_check(path, ".app")) {
+ if (BLI_path_extension_check(path, ".app")) {
return FILE_TYPE_APPLICATIONBUNDLE;
}
- else if (BLI_path_extension_check(path, ".py")) {
+ if (BLI_path_extension_check(path, ".py")) {
return FILE_TYPE_PYSCRIPT;
}
- else if (BLI_path_extension_check_n(path,
- ".txt",
- ".glsl",
- ".osl",
- ".data",
- ".pov",
- ".ini",
- ".mcr",
- ".inc",
- ".fountain",
- NULL)) {
+ if (BLI_path_extension_check_n(path,
+ ".txt",
+ ".glsl",
+ ".osl",
+ ".data",
+ ".pov",
+ ".ini",
+ ".mcr",
+ ".inc",
+ ".fountain",
+ NULL)) {
return FILE_TYPE_TEXT;
}
- else if (BLI_path_extension_check_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
+ if (BLI_path_extension_check_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
return FILE_TYPE_FTFONT;
}
- else if (BLI_path_extension_check(path, ".btx")) {
+ if (BLI_path_extension_check(path, ".btx")) {
return FILE_TYPE_BTX;
}
- else if (BLI_path_extension_check(path, ".dae")) {
+ if (BLI_path_extension_check(path, ".dae")) {
return FILE_TYPE_COLLADA;
}
- else if (BLI_path_extension_check(path, ".abc")) {
+ if (BLI_path_extension_check(path, ".abc")) {
return FILE_TYPE_ALEMBIC;
}
- else if (BLI_path_extension_check_n(path, ".usd", ".usda", ".usdc", NULL)) {
+ if (BLI_path_extension_check_n(path, ".usd", ".usda", ".usdc", NULL)) {
return FILE_TYPE_USD;
}
- else if (BLI_path_extension_check(path, ".vdb")) {
+ if (BLI_path_extension_check(path, ".vdb")) {
return FILE_TYPE_VOLUME;
}
- else if (BLI_path_extension_check(path, ".zip")) {
+ if (BLI_path_extension_check(path, ".zip")) {
return FILE_TYPE_ARCHIVE;
}
- else if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) {
+ if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) {
return FILE_TYPE_OBJECT_IO;
}
- else if (BLI_path_extension_check_array(path, imb_ext_image)) {
+ if (BLI_path_extension_check_array(path, imb_ext_image)) {
return FILE_TYPE_IMAGE;
}
- else if (BLI_path_extension_check(path, ".ogg")) {
+ if (BLI_path_extension_check(path, ".ogg")) {
if (IMB_isanim(path)) {
return FILE_TYPE_MOVIE;
}
- else {
- return FILE_TYPE_SOUND;
- }
+ return FILE_TYPE_SOUND;
}
- else if (BLI_path_extension_check_array(path, imb_ext_movie)) {
+ if (BLI_path_extension_check_array(path, imb_ext_movie)) {
return FILE_TYPE_MOVIE;
}
- else if (BLI_path_extension_check_array(path, imb_ext_audio)) {
+ if (BLI_path_extension_check_array(path, imb_ext_audio)) {
return FILE_TYPE_SOUND;
}
return 0;
@@ -2655,9 +2660,9 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
if (filelist->dir[0] == 0) {
/* make directories */
# ifdef WITH_FREESTYLE
- filelist->filelist.nbr_entries = 27;
+ filelist->filelist.nbr_entries = 27;
# else
- filelist->filelist.nbr_entries = 26;
+ filelist->filelist.nbr_entries = 26;
# endif
filelist_resize(filelist, filelist->filelist.nbr_entries);
@@ -2688,11 +2693,11 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
- filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
- filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
- filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
+ filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
+ filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
+ filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
# ifdef WITH_FREESTYLE
- filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
+ filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
# endif
}
else {
@@ -2740,7 +2745,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;
@@ -2804,7 +2809,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
static void filelist_readjob_do(const bool do_lib,
FileList *filelist,
const char *main_name,
- short *stop,
+ const short *stop,
short *do_update,
float *progress,
ThreadMutex *lock)
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 3b62941af83..88c8c6b2939 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -374,7 +374,7 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile)
* pass its size here so we can store that in the preferences. Otherwise NULL.
*/
void ED_fileselect_params_to_userdef(SpaceFile *sfile,
- int temp_win_size[2],
+ const int temp_win_size[2],
const bool is_maximized)
{
UserDef_FileSpaceData *sfile_udata_new = &U.file_space_data;
@@ -438,13 +438,12 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *region)
numfiles = (int)((float)(x_view + x_over) / (float)(x_item));
return numfiles * layout->rows;
}
- else {
- const int y_item = layout->tile_h + (2 * layout->tile_border_y);
- const int y_view = (int)(BLI_rctf_size_y(&region->v2d.cur)) - layout->offset_top;
- const int y_over = y_item - (y_view % y_item);
- numfiles = (int)((float)(y_view + y_over) / (float)(y_item));
- return numfiles * layout->flow_columns;
- }
+
+ const int y_item = layout->tile_h + (2 * layout->tile_border_y);
+ const int y_view = (int)(BLI_rctf_size_y(&region->v2d.cur)) - layout->offset_top;
+ const int y_over = y_item - (y_view % y_item);
+ numfiles = (int)((float)(y_view + y_over) / (float)(y_item));
+ return numfiles * layout->flow_columns;
}
static bool is_inside(int x, int y, int cols, int rows)
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index b03df01a02b..b1c94c2668f 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -307,14 +307,13 @@ char *ED_fsmenu_entry_get_name(struct FSMenuEntry *fsentry)
if (fsentry->name[0]) {
return fsentry->name;
}
- else {
- /* Here we abuse fsm_iter->name, keeping first char NULL. */
- char *name = fsentry->name + 1;
- size_t name_size = sizeof(fsentry->name) - 1;
- fsmenu_entry_generate_name(fsentry, name, name_size);
- return name;
- }
+ /* Here we abuse fsm_iter->name, keeping first char NULL. */
+ char *name = fsentry->name + 1;
+ size_t name_size = sizeof(fsentry->name) - 1;
+
+ fsmenu_entry_generate_name(fsentry, name, name_size);
+ return name;
}
void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name)
@@ -411,7 +410,7 @@ void fsmenu_insert_entry(struct FSMenu *fsmenu,
}
return;
}
- else if ((flag & FS_INSERT_SORTED) && cmp_ret < 0) {
+ if ((flag & FS_INSERT_SORTED) && cmp_ret < 0) {
break;
}
}
@@ -755,8 +754,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
fsmenu, FS_CATEGORY_OTHER, &FOLDERID_SkyDrive, NULL, ICON_URL, FS_INSERT_LAST);
}
}
-#else
-# ifdef __APPLE__
+#elif defined(__APPLE__)
{
/* We store some known macOS system paths and corresponding icons
* and names in the FS_CATEGORY_OTHER (not displayed directly) category. */
@@ -775,9 +773,9 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
const char *home = BLI_getenv("HOME");
-# define FS_MACOS_PATH(path, name, icon) \
- BLI_snprintf(line, sizeof(line), path, home); \
- fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST);
+# define FS_MACOS_PATH(path, name, icon) \
+ BLI_snprintf(line, sizeof(line), path, home); \
+ fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST);
FS_MACOS_PATH("%s/", NULL, ICON_HOME)
FS_MACOS_PATH("%s/Desktop/", IFACE_("Desktop"), ICON_DESKTOP)
@@ -788,7 +786,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
FS_MACOS_PATH("%s/Pictures/", IFACE_("Pictures"), ICON_FILE_IMAGE)
FS_MACOS_PATH("%s/Library/Fonts/", IFACE_("Fonts"), ICON_FILE_FONT)
-# undef FS_MACOS_PATH
+# undef FS_MACOS_PATH
/* Get mounted volumes better method OSX 10.6 and higher, see:
* https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html
@@ -850,8 +848,8 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
/* kLSSharedFileListFavoriteItems is deprecated, but available till macOS 10.15.
* Will have to find a new method to sync the Finder Favorites with File Browser. */
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
/* Finally get user favorite places */
if (read_bookmarks) {
UInt32 seed;
@@ -895,9 +893,9 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
CFRelease(pathesArray);
CFRelease(list);
}
-# pragma GCC diagnostic pop
+# pragma GCC diagnostic pop
}
-# else
+#else
/* unix */
{
const char *home = BLI_getenv("HOME");
@@ -933,14 +931,14 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
{
int found = 0;
-# ifdef __linux__
+# ifdef __linux__
/* loop over mount points */
struct mntent *mnt;
FILE *fp;
fp = setmntent(MOUNTED, "r");
if (fp == NULL) {
- fprintf(stderr, "could not get a list of mounted filesystems\n");
+ fprintf(stderr, "could not get a list of mounted file-systems\n");
}
else {
while ((mnt = getmntent(fp))) {
@@ -948,10 +946,10 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
/* Hide share not usable to the user. */
continue;
}
- else if (!STRPREFIX(mnt->mnt_fsname, "/dev")) {
+ if (!STRPREFIX(mnt->mnt_fsname, "/dev")) {
continue;
}
- else if (STRPREFIX(mnt->mnt_fsname, "/dev/loop")) {
+ if (STRPREFIX(mnt->mnt_fsname, "/dev/loop")) {
/* The dev/loop* entries are SNAPS used by desktop environment
* (Gnome) no need for them to show up in the list. */
continue;
@@ -995,7 +993,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
BLI_filelist_free(dir, dir_len);
}
-# endif
+# endif
/* fallback */
if (!found) {
@@ -1004,7 +1002,6 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
}
}
-# endif
#endif
#if defined(WIN32) || defined(__APPLE__)
@@ -1132,10 +1129,13 @@ int fsmenu_get_active_indices(struct FSMenu *fsmenu, enum FSMenuCategory categor
* before being defined as unreachable by the OS, we need to validate the bookmarks in an async
* job...
*/
-static void fsmenu_bookmark_validate_job_startjob(void *fsmenuv,
- short *stop,
- short *do_update,
- float *UNUSED(progress))
+static void fsmenu_bookmark_validate_job_startjob(
+ void *fsmenuv,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *UNUSED(progress))
{
FSMenu *fsmenu = fsmenuv;
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 21b705cbb44..43939b9ff54 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -449,7 +449,6 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
FileSelectParams *params = ED_fileselect_get_params(sfile);
View2D *v2d = &region->v2d;
- View2DScrollers *scrollers;
float col[3];
/* Needed, because filelist is not initialized on loading */
@@ -459,7 +458,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
/* clear and setup matrix */
UI_GetThemeColor3fv(TH_BACK, col);
- GPU_clear_color(col[0], col[1], col[2], 0.0);
+ GPU_clear_color(col[0], col[1], col[2], 1.0f);
GPU_clear(GPU_COLOR_BIT);
/* Allow dynamically sliders to be set, saves notifiers etc. */
@@ -509,9 +508,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/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index 179d73a38ba..0158e12c79c 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -1420,7 +1420,7 @@ void graph_buttons_register(ARegionType *art)
pt->poll = graph_panel_drivers_poll;
BLI_addtail(&art->paneltypes, pt);
- pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel drivers pover");
+ pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel drivers popover");
strcpy(pt->idname, "GRAPH_PT_drivers_popover");
strcpy(pt->label, N_("Add/Edit Driver"));
strcpy(pt->category, "Drivers");
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index f2071d292ca..5ae175f525f 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -327,9 +327,7 @@ static bool draw_fcurve_handles_check(SpaceGraph *sipo, FCurve *fcu)
(fcu->totvert <= 1)) {
return false;
}
- else {
- return true;
- }
+ return true;
}
/* draw lines for F-Curve handles only (this is only done in EditMode)
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 03151da8d4d..68fdef54a53 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -233,9 +233,8 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
if (ac.scene == NULL) {
return OPERATOR_CANCELLED;
}
- else {
- scene = ac.scene;
- }
+
+ scene = ac.scene;
/* set the range directly */
get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, false, false);
@@ -682,9 +681,10 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
}
}
else {
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ ac->depsgraph, (float)CFRA);
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
- float cfra = (float)CFRA;
/* Read value from property the F-Curve represents, or from the curve only?
*
@@ -706,7 +706,7 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path,
fcu->array_index,
- cfra,
+ &anim_eval_context,
ts->keyframe_type,
&nla_cache,
flag);
@@ -715,6 +715,7 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
/* adjust current frame for NLA-mapping */
+ float cfra = (float)CFRA;
if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) {
cfra = sipo->cursorTime;
}
@@ -1631,11 +1632,10 @@ static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent *
graphkeys_decimate_modal_update(C, op);
break;
}
- else {
- /* unhandled event - maybe it was some view manip? */
- /* allow to pass through */
- return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
- }
+
+ /* unhandled event - maybe it was some view manip? */
+ /* allow to pass through */
+ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
@@ -1694,7 +1694,7 @@ static bool graphkeys_decimate_poll_property(const bContext *UNUSED(C),
if (STREQ(prop_id, "remove_ratio") && mode != DECIM_RATIO) {
return false;
}
- else if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
+ if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
return false;
}
}
@@ -2577,7 +2577,7 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op)
if (strstr(fcu->rna_path, "rotation_euler") == NULL) {
continue;
}
- else if (ELEM(fcu->array_index, 0, 1, 2) == 0) {
+ if (ELEM(fcu->array_index, 0, 1, 2) == 0) {
BKE_reportf(op->reports,
RPT_WARNING,
"Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)",
@@ -2689,23 +2689,22 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op)
"and that F-Curves for these are in consecutive XYZ order and selected");
return OPERATOR_CANCELLED;
}
- else {
- if (failed) {
- BKE_report(
- op->reports,
- RPT_ERROR,
- "Some Euler Rotations could not be corrected due to missing/unselected/out-of-order "
- "F-Curves, "
- "ensure each rotation has keys for all components, and that F-Curves for these are in "
- "consecutive XYZ order and selected");
- }
- /* set notifier that keyframes have changed */
- WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
-
- /* done at last */
- return OPERATOR_FINISHED;
+ if (failed) {
+ BKE_report(
+ op->reports,
+ RPT_ERROR,
+ "Some Euler Rotations could not be corrected due to missing/unselected/out-of-order "
+ "F-Curves, "
+ "ensure each rotation has keys for all components, and that F-Curves for these are in "
+ "consecutive XYZ order and selected");
}
+
+ /* set notifier that keyframes have changed */
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+
+ /* done at last */
+ return OPERATOR_FINISHED;
}
void GRAPH_OT_euler_filter(wmOperatorType *ot)
@@ -3347,9 +3346,7 @@ static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No F-Modifiers available to be copied");
return OPERATOR_CANCELLED;
}
- else {
- return OPERATOR_FINISHED;
- }
+ return OPERATOR_FINISHED;
}
void GRAPH_OT_fmodifier_copy(wmOperatorType *ot)
@@ -3434,10 +3431,9 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
+ return OPERATOR_CANCELLED;
}
void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
@@ -3487,9 +3483,7 @@ static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op)
if (ok) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void GRAPH_OT_driver_variables_copy(wmOperatorType *ot)
@@ -3533,9 +3527,7 @@ static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void GRAPH_OT_driver_variables_paste(wmOperatorType *ot)
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index ae435b5624a..160b731629c 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -298,11 +298,10 @@ static tNearestVertInfo *get_best_nearest_fcurve_vert(ListBase *matches)
BLI_remlink(matches, nvi);
return nvi;
}
- else {
- /* if vert is selected, we've got what we want... */
- if (nvi->sel) {
- found = 1;
- }
+
+ /* if vert is selected, we've got what we want... */
+ if (nvi->sel) {
+ found = 1;
}
}
diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c
index 03ed8870d67..6d08fd00cab 100644
--- a/source/blender/editors/space_graph/graph_utils.c
+++ b/source/blender/editors/space_graph/graph_utils.c
@@ -52,7 +52,7 @@
/* Set Up Drivers Editor */
/* Set up UI configuration for Drivers Editor */
-/* NOTE: Currently called from windowmanager
+/* NOTE: Currently called from window-manager
* (new drivers editor window) and RNA (mode switching) */
void ED_drivers_editor_init(bContext *C, ScrArea *area)
{
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index b9c7c529620..b1d995a7a0b 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -200,13 +200,11 @@ 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);
- GPU_clear_color(col[0], col[1], col[2], 0.0);
+ GPU_clear_color(col[0], col[1], col[2], 1.0f);
GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
@@ -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,12 +358,11 @@ 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 */
UI_GetThemeColor3fv(TH_BACK, col);
- GPU_clear_color(col[0], col[1], col[2], 0.0);
+ GPU_clear_color(col[0], col[1], col[2], 1.0f);
GPU_clear(GPU_COLOR_BIT);
UI_view2d_view_ortho(v2d);
@@ -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_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 6f3ef44fe94..1f8dd7cfe44 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -139,9 +139,7 @@ static bool ui_imageuser_slot_menu_step(bContext *C, int direction, void *image_
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
return true;
}
- else {
- return true;
- }
+ return true;
}
static const char *ui_imageuser_layer_fake_name(RenderResult *rr)
@@ -150,12 +148,10 @@ static const char *ui_imageuser_layer_fake_name(RenderResult *rr)
if (rv->rectf) {
return IFACE_("Composite");
}
- else if (rv->rect32) {
+ if (rv->rect32) {
return IFACE_("Sequence");
}
- else {
- return NULL;
- }
+ return NULL;
}
/* workaround for passing many args */
@@ -576,8 +572,12 @@ static void image_multiview_cb(bContext *C, void *rnd_pt, void *UNUSED(arg_v))
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
}
-static void uiblock_layer_pass_buttons(
- uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot)
+static void uiblock_layer_pass_buttons(uiLayout *layout,
+ Image *image,
+ RenderResult *rr,
+ ImageUser *iuser,
+ int w,
+ const short *render_slot)
{
struct ImageUI_Data rnd_pt_local, *rnd_pt = NULL;
uiBlock *block = uiLayoutGetBlock(layout);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 9040ca5e79c..1038011e480 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -150,8 +150,8 @@ void ED_image_draw_info(Scene *scene,
const uchar cp[4],
const float fp[4],
const float linearcol[4],
- int *zp,
- float *zpf)
+ const int *zp,
+ const float *zpf)
{
rcti color_rect;
char str[256];
@@ -463,25 +463,24 @@ void ED_image_draw_info(Scene *scene,
/* image drawing */
static void sima_draw_zbuf_pixels(
- float x1, float y1, int rectx, int recty, int *rect, float zoomx, float zoomy)
+ float x1, float y1, int rectx, int recty, const int *rect, float zoomx, float zoomy)
{
float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
/* Slowwww */
- int *recti = MEM_mallocN(rectx * recty * sizeof(int), "temp");
+ float *rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp");
for (int a = rectx * recty - 1; a >= 0; a--) {
/* zbuffer values are signed, so we need to shift color range */
- recti[a] = rect[a] * 0.5f + 0.5f;
+ rectf[a] = rect[a] * 0.5f + 0.5f;
}
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_uniform_vector(
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTex(
- &state, x1, y1, rectx, recty, GL_RED, GL_INT, GL_NEAREST, recti, zoomx, zoomy, NULL);
+ immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL);
- MEM_freeN(recti);
+ MEM_freeN(rectf);
}
static void sima_draw_zbuffloat_pixels(Scene *scene,
@@ -489,7 +488,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene,
float y1,
int rectx,
int recty,
- float *rect_float,
+ const float *rect_float,
float zoomx,
float zoomy)
{
@@ -526,8 +525,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene,
GPU_shader_uniform_vector(
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTex(
- &state, x1, y1, rectx, recty, GL_RED, GL_FLOAT, GL_NEAREST, rectf, zoomx, zoomy, NULL);
+ immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL);
MEM_freeN(rectf);
}
@@ -612,8 +610,7 @@ static void draw_image_buffer(const bContext *C,
/* If RGBA display with color management */
if ((sima_flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | SI_SHOW_ALPHA)) == 0) {
- ED_draw_imbuf_ctx_clipping(
- C, ibuf, x, y, GL_NEAREST, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy);
+ ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, false, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy);
}
else {
float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f};
@@ -649,9 +646,8 @@ static void draw_image_buffer(const bContext *C,
y,
ibuf->x,
ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
+ GPU_RGBA8,
+ false,
display_buffer,
0,
0,
@@ -780,18 +776,8 @@ static void draw_image_paint_helpers(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
- immDrawPixelsTex(&state,
- x,
- y,
- ibuf->x,
- ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
- display_buffer,
- zoomx,
- zoomy,
- col);
+ immDrawPixelsTex(
+ &state, x, y, ibuf->x, ibuf->y, GPU_RGBA8, false, display_buffer, zoomx, zoomy, col);
GPU_blend(false);
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..d05fe215840 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -69,7 +69,6 @@
#include "DEG_depsgraph.h"
-#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -198,11 +197,10 @@ static Image *image_from_context(const bContext *C)
if (ima) {
return ima;
}
- else {
- /* Image editor. */
- SpaceImage *sima = CTX_wm_space_image(C);
- return (sima) ? sima->image : NULL;
- }
+
+ /* Image editor. */
+ SpaceImage *sima = CTX_wm_space_image(C);
+ return (sima) ? sima->image : NULL;
}
static ImageUser *image_user_from_context(const bContext *C)
@@ -214,11 +212,10 @@ static ImageUser *image_user_from_context(const bContext *C)
if (iuser) {
return iuser;
}
- else {
- /* Image editor. */
- SpaceImage *sima = CTX_wm_space_image(C);
- return (sima) ? &sima->iuser : NULL;
- }
+
+ /* Image editor. */
+ SpaceImage *sima = CTX_wm_space_image(C);
+ return (sima) ? &sima->iuser : NULL;
}
static bool image_from_context_has_data_poll(bContext *C)
@@ -359,10 +356,9 @@ static int image_view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *eve
image_view_pan_exec(C, op);
return OPERATOR_FINISHED;
}
- else {
- image_view_pan_init(C, op, event);
- return OPERATOR_RUNNING_MODAL;
- }
+
+ image_view_pan_init(C, op, event);
+ return OPERATOR_RUNNING_MODAL;
}
static int image_view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
@@ -549,10 +545,9 @@ static int image_view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev
return OPERATOR_FINISHED;
}
- else {
- image_view_zoom_init(C, op, event);
- return OPERATOR_RUNNING_MODAL;
- }
+
+ image_view_zoom_init(C, op, event);
+ return OPERATOR_RUNNING_MODAL;
}
static void image_zoom_apply(ViewZoomData *vpd,
@@ -697,27 +692,26 @@ static int image_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmE
if (event->type != NDOF_MOTION) {
return OPERATOR_CANCELLED;
}
- else {
- SpaceImage *sima = CTX_wm_space_image(C);
- ARegion *region = CTX_wm_region(C);
- float pan_vec[3];
- const wmNDOFMotionData *ndof = event->customdata;
- const float speed = NDOF_PIXELS_PER_SECOND;
+ SpaceImage *sima = CTX_wm_space_image(C);
+ ARegion *region = CTX_wm_region(C);
+ float pan_vec[3];
- WM_event_ndof_pan_get(ndof, pan_vec, true);
+ const wmNDOFMotionData *ndof = event->customdata;
+ const float speed = NDOF_PIXELS_PER_SECOND;
- mul_v2_fl(pan_vec, (speed * ndof->dt) / sima->zoom);
- pan_vec[2] *= -ndof->dt;
+ WM_event_ndof_pan_get(ndof, pan_vec, true);
- sima_zoom_set_factor(sima, region, 1.0f + pan_vec[2], NULL, false);
- sima->xof += pan_vec[0];
- sima->yof += pan_vec[1];
+ mul_v2_fl(pan_vec, (speed * ndof->dt) / sima->zoom);
+ pan_vec[2] *= -ndof->dt;
- ED_region_tag_redraw(region);
+ sima_zoom_set_factor(sima, region, 1.0f + pan_vec[2], NULL, false);
+ sima->xof += pan_vec[0];
+ sima->yof += pan_vec[1];
- return OPERATOR_FINISHED;
- }
+ ED_region_tag_redraw(region);
+
+ return OPERATOR_FINISHED;
}
void IMAGE_OT_view_ndof(wmOperatorType *ot)
@@ -1237,7 +1231,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 +1411,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 +1571,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 +1608,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;
}
@@ -1678,24 +1672,23 @@ static char imtype_best_depth(ImBuf *ibuf, const char imtype)
}
return R_IMF_CHAN_DEPTH_8;
}
- else {
- if (depth_ok & R_IMF_CHAN_DEPTH_8) {
- return R_IMF_CHAN_DEPTH_8;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_12) {
- return R_IMF_CHAN_DEPTH_12;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_16) {
- return R_IMF_CHAN_DEPTH_16;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_24) {
- return R_IMF_CHAN_DEPTH_24;
- }
- if (depth_ok & R_IMF_CHAN_DEPTH_32) {
- return R_IMF_CHAN_DEPTH_32;
- }
- return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
+
+ if (depth_ok & R_IMF_CHAN_DEPTH_8) {
+ return R_IMF_CHAN_DEPTH_8;
}
+ if (depth_ok & R_IMF_CHAN_DEPTH_12) {
+ return R_IMF_CHAN_DEPTH_12;
+ }
+ if (depth_ok & R_IMF_CHAN_DEPTH_16) {
+ return R_IMF_CHAN_DEPTH_16;
+ }
+ if (depth_ok & R_IMF_CHAN_DEPTH_24) {
+ return R_IMF_CHAN_DEPTH_24;
+ }
+ if (depth_ok & R_IMF_CHAN_DEPTH_32) {
+ return R_IMF_CHAN_DEPTH_32;
+ }
+ return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
}
static int image_save_options_init(Main *bmain,
@@ -1777,7 +1770,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");
}
@@ -2147,9 +2140,8 @@ static int image_save_exec(bContext *C, wmOperator *op)
if (ok) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+
+ return OPERATOR_CANCELLED;
}
static int image_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
@@ -2160,9 +2152,7 @@ static int image_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
WM_operator_name_call(C, "IMAGE_OT_save_as", WM_OP_INVOKE_DEFAULT, NULL);
return OPERATOR_CANCELLED;
}
- else {
- return image_save_exec(C, op);
- }
+ return image_save_exec(C, op);
}
void IMAGE_OT_save(wmOperatorType *ot)
@@ -2294,14 +2284,12 @@ static bool image_should_be_saved(Image *ima, bool *is_format_writable)
ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_GENERATED, IMA_SRC_TILED)) {
return image_should_be_saved_when_modified(ima);
}
- else {
- return false;
- }
+ return false;
}
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 +2324,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 +2336,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);
}
}
}
@@ -2604,7 +2594,7 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
{
- uiLayout *split, *col[2];
+ uiLayout *col;
uiLayout *layout = op->layout;
PointerRNA ptr;
#if 0
@@ -2616,33 +2606,18 @@ static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
/* copy of WM_operator_props_dialog_popup() layout */
- split = uiLayoutSplit(layout, 0.5f, false);
- col[0] = uiLayoutColumn(split, false);
- col[1] = uiLayoutColumn(split, false);
-
- uiItemL(col[0], IFACE_("Name"), ICON_NONE);
- uiItemR(col[1], &ptr, "name", 0, "", ICON_NONE);
-
- uiItemL(col[0], IFACE_("Width"), ICON_NONE);
- uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE);
-
- uiItemL(col[0], IFACE_("Height"), ICON_NONE);
- uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
- uiItemL(col[0], IFACE_("Color"), ICON_NONE);
- uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE);
-
- uiItemL(col[0], "", ICON_NONE);
- uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE);
-
- uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE);
- uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE);
-
- uiItemL(col[0], "", ICON_NONE);
- uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE);
-
- uiItemL(col[0], "", ICON_NONE);
- uiItemR(col[1], &ptr, "tiled", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE);
#if 0
if (is_multiview) {
@@ -2793,7 +2768,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
/* force GPU reupload, all image is invalid */
- GPU_free_image(ima);
+ BKE_image_free_gputextures(ima);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@@ -2884,7 +2859,7 @@ static int image_scale_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
/* force GPU reupload, all image is invalid */
- GPU_free_image(ima);
+ BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@@ -3044,7 +3019,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_image/image_undo.c b/source/blender/editors/space_image/image_undo.c
index e0c44c3a0ba..27b84307f7d 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.c
@@ -60,8 +60,6 @@
#include "ED_undo.h"
#include "ED_util.h"
-#include "GPU_draw.h"
-
#include "WM_api.h"
static CLG_LogRef LOG = {"ed.image.undo"};
@@ -295,7 +293,8 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
SWAP(uint *, ptile->rect.uint, tmpibuf->rect);
}
- GPU_free_image(image); /* force OpenGL reload (maybe partial update will operate better?) */
+ BKE_image_free_gputextures(
+ image); /* force OpenGL reload (maybe partial update will operate better?) */
if (ibuf->rect_float) {
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
}
@@ -570,7 +569,7 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
if (changed) {
BKE_image_mark_dirty(image, ibuf);
- GPU_free_image(image); /* force OpenGL reload */
+ BKE_image_free_gputextures(image); /* force OpenGL reload */
if (ibuf->rect_float) {
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c
index 3685e5de852..6c818257ec7 100644
--- a/source/blender/editors/space_info/info_draw.c
+++ b/source/blender/editors/space_info/info_draw.c
@@ -108,9 +108,8 @@ static enum eTextViewContext_LineFlag report_line_data(TextViewContext *tvc,
UI_GetThemeColor4ubv(icon_bg_id, r_icon_bg);
return TVC_LINE_FG | TVC_LINE_BG | TVC_LINE_ICON | TVC_LINE_ICON_FG | TVC_LINE_ICON_BG;
}
- else {
- return TVC_LINE_FG | TVC_LINE_BG;
- }
+
+ return TVC_LINE_FG | TVC_LINE_BG;
}
/* reports! */
@@ -159,9 +158,8 @@ static int report_textview_begin(TextViewContext *tvc)
return true;
}
- else {
- return false;
- }
+
+ return false;
}
static void report_textview_end(TextViewContext *UNUSED(tvc))
@@ -185,17 +183,14 @@ static int report_textview_step(TextViewContext *tvc)
return true;
}
- else {
- return false;
- }
+ return false;
}
- else {
- /* step to the next newline */
- tvc->iter_char_end = tvc->iter_char_begin - 1;
- report_textview_init__internal(tvc);
- return true;
- }
+ /* step to the next newline */
+ tvc->iter_char_end = tvc->iter_char_begin - 1;
+ report_textview_init__internal(tvc);
+
+ return true;
}
static void report_textview_line_get(TextViewContext *tvc, const char **r_line, int *r_len)
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index e1937dffb37..4e91da01cc9 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -43,6 +43,7 @@
#include "BLT_translation.h"
#include "BKE_blender_version.h"
+#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
@@ -405,46 +406,6 @@ static void stats_update(Depsgraph *depsgraph, ViewLayer *view_layer)
*(view_layer->stats) = stats;
}
-static const char *footer_string(ViewLayer *view_layer)
-{
-#define MAX_INFO_MEM_LEN 64
- char memstr[MAX_INFO_MEM_LEN];
- char gpumemstr[MAX_INFO_MEM_LEN] = "";
- char formatted_mem[15];
- size_t ofs = 0;
-
- uintptr_t mem_in_use = MEM_get_memory_in_use();
-
- /* get memory statistics */
- BLI_str_format_byte_unit(formatted_mem, mem_in_use, false);
- ofs = BLI_snprintf(memstr, MAX_INFO_MEM_LEN, TIP_("Mem: %s"), formatted_mem);
-
- if (GPU_mem_stats_supported()) {
- int gpu_free_mem, gpu_tot_memory;
-
- GPU_mem_stats_get(&gpu_tot_memory, &gpu_free_mem);
-
- BLI_str_format_byte_unit(formatted_mem, gpu_free_mem, false);
- ofs = BLI_snprintf(gpumemstr, MAX_INFO_MEM_LEN, TIP_(" | Free GPU Mem: %s"), formatted_mem);
-
- if (gpu_tot_memory) {
- BLI_str_format_byte_unit(formatted_mem, gpu_tot_memory, false);
- BLI_snprintf(gpumemstr + ofs, MAX_INFO_MEM_LEN - ofs, TIP_("/%s"), formatted_mem);
- }
- }
-
- BLI_snprintf(view_layer->footer_str,
- sizeof(view_layer->footer_str),
- "%s%s | %s",
- memstr,
- gpumemstr,
- BKE_blender_version_string());
-
- return view_layer->footer_str;
-
-#undef MAX_INFO_MEM_LEN
-}
-
void ED_info_stats_clear(ViewLayer *view_layer)
{
if (view_layer->stats) {
@@ -453,45 +414,26 @@ void ED_info_stats_clear(ViewLayer *view_layer)
}
}
-const char *ED_info_footer_string(ViewLayer *view_layer)
-{
- return footer_string(view_layer);
-}
-
-static void stats_row(int col1,
- const char *key,
- int col2,
- const char *value1,
- const char *value2,
- int *y,
- int height)
-{
- *y -= height;
- BLF_draw_default(col1, *y, 0.0f, key, 128);
- char values[128];
- BLI_snprintf(values, sizeof(values), (value2) ? "%s / %s" : "%s", value1, value2);
- BLF_draw_default(col2, *y, 0.0f, values, sizeof(values));
-}
-
-void ED_info_draw_stats(
- Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height)
+static bool format_stats(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ SceneStatsFmt *stats_fmt)
{
/* Create stats if they don't already exist. */
if (!view_layer->stats) {
/* Do not not access dependency graph if interface is marked as locked. */
wmWindowManager *wm = bmain->wm.first;
if (wm->is_interface_locked) {
- return;
+ return false;
}
Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
stats_update(depsgraph, view_layer);
}
SceneStats *stats = view_layer->stats;
- SceneStatsFmt stats_fmt;
/* Generate formatted numbers. */
-#define SCENE_STATS_FMT_INT(_id) BLI_str_format_uint64_grouped(stats_fmt._id, stats->_id)
+#define SCENE_STATS_FMT_INT(_id) BLI_str_format_uint64_grouped(stats_fmt->_id, stats->_id)
SCENE_STATS_FMT_INT(totvert);
SCENE_STATS_FMT_INT(totvertsel);
@@ -519,6 +461,176 @@ void ED_info_draw_stats(
SCENE_STATS_FMT_INT(totgppoint);
#undef SCENE_STATS_FMT_INT
+ return true;
+}
+
+static void get_stats_string(
+ char *info, int len, size_t *ofs, ViewLayer *view_layer, SceneStatsFmt *stats_fmt)
+{
+ Object *ob = OBACT(view_layer);
+ Object *obedit = OBEDIT_FROM_OBACT(ob);
+ eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT;
+ LayerCollection *layer_collection = view_layer->active_collection;
+
+ if (object_mode == OB_MODE_OBJECT) {
+ *ofs += BLI_snprintf(info + *ofs,
+ len - *ofs,
+ "%s | ",
+ BKE_collection_ui_name_get(layer_collection->collection));
+ }
+
+ if (ob) {
+ *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2);
+ }
+
+ if (obedit) {
+ if (BKE_keyblock_from_object(obedit)) {
+ *ofs += BLI_strncpy_rlen(info + *ofs, TIP_("(Key) "), len - *ofs);
+ }
+
+ if (obedit->type == OB_MESH) {
+ *ofs += BLI_snprintf(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"),
+ stats_fmt->totvertsel,
+ stats_fmt->totvert,
+ stats_fmt->totedgesel,
+ stats_fmt->totedge,
+ stats_fmt->totfacesel,
+ stats_fmt->totface,
+ stats_fmt->tottri);
+ }
+ else if (obedit->type == OB_ARMATURE) {
+ *ofs += BLI_snprintf(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s/%s | Bones:%s/%s"),
+ stats_fmt->totvertsel,
+ stats_fmt->totvert,
+ stats_fmt->totbonesel,
+ stats_fmt->totbone);
+ }
+ else {
+ *ofs += BLI_snprintf(
+ info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert);
+ }
+ }
+ else if (ob && (object_mode & OB_MODE_POSE)) {
+ *ofs += BLI_snprintf(
+ info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone);
+ }
+ else if ((ob) && (ob->type == OB_GPENCIL)) {
+ *ofs += BLI_snprintf(info + *ofs,
+ len - *ofs,
+ TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"),
+ stats_fmt->totgplayer,
+ stats_fmt->totgpframe,
+ stats_fmt->totgpstroke,
+ stats_fmt->totgppoint);
+ }
+ else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) {
+ *ofs += BLI_snprintf(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s | Tris:%s"),
+ stats_fmt->totvert,
+ stats_fmt->tottri);
+ }
+ else {
+ *ofs += BLI_snprintf(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s | Faces:%s | Tris:%s"),
+ stats_fmt->totvert,
+ stats_fmt->totface,
+ stats_fmt->tottri);
+ }
+
+ *ofs += BLI_snprintf(
+ info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj);
+}
+
+const char *ED_info_statusbar_string(Main *bmain, bScreen *screen, bContext *C)
+{
+ char formatted_mem[15];
+ size_t ofs = 0;
+ char *info = screen->statusbar_info;
+ int len = sizeof(screen->statusbar_info);
+
+ info[0] = '\0';
+
+ /* Scene statistics. */
+ if (U.statusbar_flag & STATUSBAR_SHOW_STATS) {
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Scene *scene = CTX_data_scene(C);
+ SceneStatsFmt stats_fmt;
+ if (format_stats(bmain, scene, view_layer, &stats_fmt)) {
+ get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt);
+ }
+ }
+
+ /* Memory status. */
+ if (U.statusbar_flag & STATUSBAR_SHOW_MEMORY) {
+ if (info[0]) {
+ ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ }
+ uintptr_t mem_in_use = MEM_get_memory_in_use();
+ BLI_str_format_byte_unit(formatted_mem, mem_in_use, false);
+ ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem);
+ }
+
+ /* GPU VRAM status. */
+ if ((U.statusbar_flag & STATUSBAR_SHOW_VRAM) && (GPU_mem_stats_supported())) {
+ int gpu_free_mem_kb, gpu_tot_mem_kb;
+ GPU_mem_stats_get(&gpu_tot_mem_kb, &gpu_free_mem_kb);
+ float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f;
+ float gpu_free_gb = gpu_free_mem_kb / 1048576.0f;
+ if (info[0]) {
+ ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ }
+ if (gpu_free_mem_kb && gpu_tot_mem_kb) {
+ ofs += BLI_snprintf(info + ofs,
+ len - ofs,
+ TIP_("VRAM: %.1f/%.1f GiB"),
+ gpu_total_gb - gpu_free_gb,
+ gpu_total_gb);
+ }
+ else {
+ /* Can only show amount of GPU VRAM available. */
+ ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb);
+ }
+ }
+
+ /* Blender version. */
+ if (U.statusbar_flag & STATUSBAR_SHOW_VERSION) {
+ if (info[0]) {
+ ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ }
+ ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string());
+ }
+
+ return info;
+}
+
+static void stats_row(int col1,
+ const char *key,
+ int col2,
+ const char *value1,
+ const char *value2,
+ int *y,
+ int height)
+{
+ *y -= height;
+ BLF_draw_default(col1, *y, 0.0f, key, 128);
+ char values[128];
+ BLI_snprintf(values, sizeof(values), (value2) ? "%s / %s" : "%s", value1, value2);
+ BLF_draw_default(col2, *y, 0.0f, values, sizeof(values));
+}
+
+void ED_info_draw_stats(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height)
+{
+ SceneStatsFmt stats_fmt;
+ if (!format_stats(bmain, scene, view_layer, &stats_fmt)) {
+ return;
+ }
Object *ob = OBACT(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
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_info/textview.c b/source/blender/editors/space_info/textview.c
index 8076d3d7eaf..eee8b989cc2 100644
--- a/source/blender/editors/space_info/textview.c
+++ b/source/blender/editors/space_info/textview.c
@@ -186,7 +186,7 @@ static bool textview_draw_string(TextViewDrawState *tds,
MEM_freeN(offsets);
return true;
}
- else if (y_next < tds->scroll_ymin) {
+ if (y_next < tds->scroll_ymin) {
/* Have not reached the drawable area so don't break. */
tds->xy[1] = y_next;
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 307b6d9bc21..130167f1bd0 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -392,7 +392,7 @@ static void nla_panel_properties(const bContext *C, Panel *panel)
uiItemR(column, &strip_ptr, "blend_type", 0, NULL, ICON_NONE);
/* Blend in/out + auto-blending:
- * - blend in/out can only be set when autoblending is off
+ * - blend in/out can only be set when auto-blending is off.
*/
uiItemS(layout);
@@ -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/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index d399ea47d7e..86bb189c638 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -461,10 +461,9 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
"block)");
return OPERATOR_CANCELLED;
}
- else {
- id = adt_ptr.owner_id;
- adt = adt_ptr.data;
- }
+
+ id = adt_ptr.owner_id;
+ adt = adt_ptr.data;
}
else {
/* indexed channel */
@@ -483,7 +482,7 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
ANIM_animdata_freelist(&anim_data);
return OPERATOR_CANCELLED;
}
- else if (ale->type != ANIMTYPE_NLAACTION) {
+ if (ale->type != ANIMTYPE_NLAACTION) {
BKE_reportf(op->reports,
RPT_ERROR,
"Animation channel at index %d is not a NLA 'Active Action' channel",
@@ -505,22 +504,21 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "Internal Error - AnimData block is not valid");
return OPERATOR_CANCELLED;
}
- else if (nlaedit_is_tweakmode_on(&ac)) {
+ if (nlaedit_is_tweakmode_on(&ac)) {
BKE_report(op->reports,
RPT_WARNING,
"Cannot push down actions while tweaking a strip's action, exit tweak mode first");
return OPERATOR_CANCELLED;
}
- else if (adt->action == NULL) {
+ if (adt->action == NULL) {
BKE_report(op->reports, RPT_WARNING, "No active action to push down");
return OPERATOR_CANCELLED;
}
- else {
- /* 'push-down' action - only usable when not in TweakMode */
- BKE_nla_action_pushdown(adt);
- DEG_id_tag_update_ex(CTX_data_main(C), id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE);
- }
+ /* 'push-down' action - only usable when not in TweakMode */
+ BKE_nla_action_pushdown(adt);
+
+ DEG_id_tag_update_ex(CTX_data_main(C), id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE);
/* set notifier that things have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
@@ -735,14 +733,13 @@ static int nlaedit_add_tracks_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- /* failed to add any tracks */
- BKE_report(
- op->reports, RPT_WARNING, "Select an existing NLA Track or an empty action line first");
- /* not done */
- return OPERATOR_CANCELLED;
- }
+ /* failed to add any tracks */
+ BKE_report(
+ op->reports, RPT_WARNING, "Select an existing NLA Track or an empty action line first");
+
+ /* not done */
+ return OPERATOR_CANCELLED;
}
void NLA_OT_tracks_add(wmOperatorType *ot)
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index a56b7f8422e..96599fd92a7 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -645,8 +645,9 @@ static void nla_draw_strip_text(AnimData *adt,
UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
}
-/* add frame extents to cache of text-strings to draw in pixelspace
- * for now, only used when transforming strips
+/**
+ * Add frame extents to cache of text-strings to draw in pixel-space
+ * for now, only used when transforming strips.
*/
static void nla_draw_strip_frames_text(
NlaTrack *UNUSED(nlt), NlaStrip *strip, View2D *v2d, float UNUSED(yminc), float ymaxc)
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index dec7a0fd93c..bc9bd0e18f2 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -269,9 +269,7 @@ static int nlaedit_disable_tweakmode_exec(bContext *C, wmOperator *op)
if (ok) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void NLA_OT_tweakmode_exit(wmOperatorType *ot)
@@ -373,9 +371,8 @@ static int nlaedit_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
if (ac.scene == NULL) {
return OPERATOR_CANCELLED;
}
- else {
- scene = ac.scene;
- }
+
+ scene = ac.scene;
/* set the range directly */
get_nlastrip_extents(&ac, &min, &max, true);
@@ -616,7 +613,7 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
// printf("Add strip - actname = '%s'\n", actname);
return OPERATOR_CANCELLED;
}
- else if (act->idroot == 0) {
+ if (act->idroot == 0) {
/* hopefully in this case (i.e. library of userless actions),
* the user knows what they're doing... */
BKE_reportf(op->reports,
@@ -830,12 +827,11 @@ static int nlaedit_add_transition_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports,
- RPT_ERROR,
- "Needs at least a pair of adjacent selected strips with a gap between them");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Needs at least a pair of adjacent selected strips with a gap between them");
+ return OPERATOR_CANCELLED;
}
void NLA_OT_transition_add(wmOperatorType *ot)
@@ -1146,9 +1142,8 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op)
/* done */
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+
+ return OPERATOR_CANCELLED;
}
static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
@@ -1293,9 +1288,8 @@ static void nlaedit_split_strip_actclip(
if (IS_EQF(len, 0.0f)) {
return;
}
- else {
- splitframe = strip->start + (len / 2.0f);
- }
+
+ splitframe = strip->start + (len / 2.0f);
/* action range */
len = strip->actend - strip->actstart;
@@ -2504,10 +2498,9 @@ static int nla_fmodifier_copy_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No F-Modifiers available to be copied");
return OPERATOR_CANCELLED;
}
- else {
- /* no updates needed - copy is non-destructive operation */
- return OPERATOR_FINISHED;
- }
+
+ /* no updates needed - copy is non-destructive operation */
+ return OPERATOR_FINISHED;
}
void NLA_OT_fmodifier_copy(wmOperatorType *ot)
@@ -2591,10 +2584,9 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
+ return OPERATOR_CANCELLED;
}
void NLA_OT_fmodifier_paste(wmOperatorType *ot)
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..4203eac16c4 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -284,23 +284,21 @@ static int node_resize_area_default(bNode *node, int x, int y)
if (BLI_rctf_isect_pt(&totr, x, y)) {
return NODE_RESIZE_RIGHT;
}
- else {
- return 0;
- }
+
+ return 0;
}
- else {
- const float size = NODE_RESIZE_MARGIN;
- rctf totr = node->totr;
- int dir = 0;
- if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) {
- dir |= NODE_RESIZE_RIGHT;
- }
- if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) {
- dir |= NODE_RESIZE_LEFT;
- }
- return dir;
+ const float size = NODE_RESIZE_MARGIN;
+ rctf totr = node->totr;
+ int dir = 0;
+
+ if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) {
+ dir |= NODE_RESIZE_RIGHT;
+ }
+ if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) {
+ dir |= NODE_RESIZE_LEFT;
}
+ return dir;
}
/* ****************** BUTTON CALLBACKS FOR COMMON NODES ***************** */
@@ -849,12 +847,37 @@ 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);
+
+ uiLayout *col;
+ if (RNA_boolean_get(ptr, "sun_disc")) {
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE);
+ }
+
+ 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,15 @@ 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 (U.experimental.use_sculpt_vertex_colors &&
+ 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);
@@ -3645,9 +3676,8 @@ void draw_nodespace_back_pix(const bContext *C,
y,
ibuf->x,
ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
+ GPU_RGBA8,
+ false,
display_buffer,
snode->zoom,
snode->zoom,
@@ -3660,12 +3690,12 @@ void draw_nodespace_back_pix(const bContext *C,
GPU_blend_set_func_separate(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom);
+ ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom);
GPU_blend(false);
}
else {
- ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom);
+ ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom);
}
if (cache_handle) {
@@ -3803,7 +3833,7 @@ static bool node_link_bezier_handles(View2D *v2d,
if (v2d && min_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > v2d->cur.xmax) {
return 0; /* clipped */
}
- else if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) {
+ if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) {
return 0; /* clipped */
}
diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c
index 95a37f85828..037fe575973 100644
--- a/source/blender/editors/space_node/node_add.c
+++ b/source/blender/editors/space_node/node_add.c
@@ -382,9 +382,7 @@ static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *even
RNA_struct_property_is_set(op->ptr, "name")) {
return node_add_file_exec(C, op);
}
- else {
- return WM_operator_filesel(C, op, event);
- }
+ return WM_operator_filesel(C, op, event);
}
void NODE_OT_add_file(wmOperatorType *ot)
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index bd8950c5085..52bd4d68649 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -97,9 +97,7 @@ static bNodeTree *node_tree_from_ID(ID *id)
if (GS(id->name) == ID_NT) {
return (bNodeTree *)id;
}
- else {
- return ntreeFromID(id);
- }
+ return ntreeFromID(id);
}
return NULL;
@@ -217,7 +215,7 @@ static bool compare_nodes(const bNode *a, const bNode *b)
if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND)) {
return 0;
}
- else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND)) {
+ if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND)) {
return 1;
}
@@ -225,7 +223,7 @@ static bool compare_nodes(const bNode *a, const bNode *b)
if (!b_active && a_active) {
return 1;
}
- else if (!b_select && (a_active || a_select)) {
+ if (!b_select && (a_active || a_select)) {
return 1;
}
@@ -949,9 +947,8 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv)
draw_rect.ymin,
preview->xsize,
preview->ysize,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_LINEAR,
+ GPU_RGBA8,
+ true,
preview->rect,
scale,
scale,
@@ -1323,7 +1320,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,
@@ -1558,15 +1555,13 @@ int node_get_resize_cursor(int directions)
if (directions == 0) {
return WM_CURSOR_DEFAULT;
}
- else if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0) {
+ if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0) {
return WM_CURSOR_Y_MOVE;
}
- else if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0) {
+ if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0) {
return WM_CURSOR_X_MOVE;
}
- else {
- return WM_CURSOR_EDIT;
- }
+ return WM_CURSOR_EDIT;
}
void node_set_cursor(wmWindow *win, SpaceNode *snode, float cursor[2])
@@ -1772,7 +1767,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 +1915,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..3c927dbf25f 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -238,7 +238,12 @@ static void compo_progressjob(void *cjv, float progress)
}
/* only this runs inside thread */
-static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress)
+static void compo_startjob(void *cjv,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
CompoJob *cj = cjv;
bNodeTree *ntree = cj->localtree;
@@ -1428,11 +1433,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);
@@ -1688,6 +1697,8 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
+ do_tag_update |= ED_node_is_simulation(snode);
+
snode_notify(C, snode);
if (do_tag_update) {
snode_dag_update(C, snode);
@@ -1730,6 +1741,8 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
+ do_tag_update |= ED_node_is_simulation(snode);
+
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c
index 2617384d046..ef468c63d77 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.c
@@ -107,13 +107,13 @@ static const char *group_node_idname(bContext *C)
if (ED_node_is_shader(snode)) {
return "ShaderNodeGroup";
}
- else if (ED_node_is_compositor(snode)) {
+ if (ED_node_is_compositor(snode)) {
return "CompositorNodeGroup";
}
- else if (ED_node_is_texture(snode)) {
+ if (ED_node_is_texture(snode)) {
return "TextureNodeGroup";
}
- else if (ED_node_is_simulation(snode)) {
+ if (ED_node_is_simulation(snode)) {
return "SimulationNodeGroup";
}
@@ -128,9 +128,7 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname)
if (node && STREQ(node->idname, node_idname)) {
return node;
}
- else {
- return NULL;
- }
+ return NULL;
}
/* ***************** Edit Group operator ************* */
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index 144e3bd3506..0a4607d2869 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -76,10 +76,9 @@ static bool ntree_check_nodes_connected_dfs(bNodeTree *ntree, bNode *from, bNode
if (link->tonode == to) {
return true;
}
- else {
- if (ntree_check_nodes_connected_dfs(ntree, link->tonode, to)) {
- return true;
- }
+
+ if (ntree_check_nodes_connected_dfs(ntree, link->tonode, to)) {
+ return true;
}
}
}
@@ -191,9 +190,7 @@ static int sort_nodes_locx(const void *a, const void *b)
if (node1->locx > node2->locx) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
@@ -667,6 +664,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
+ do_tag_update |= ED_node_is_simulation(snode);
+
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -921,9 +920,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
- else {
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
- }
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
static void node_link_cancel(bContext *C, wmOperator *op)
@@ -1069,6 +1066,8 @@ static int cut_links_exec(bContext *C, wmOperator *op)
}
}
+ do_tag_update |= ED_node_is_simulation(snode);
+
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1078,9 +1077,8 @@ static int cut_links_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+
+ return OPERATOR_CANCELLED;
}
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
@@ -1473,9 +1471,7 @@ static bool ed_node_link_conditions(ScrArea *area,
if (select) {
break;
}
- else {
- select = node;
- }
+ select = node;
}
}
/* only one selected */
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c
index 06f568c80f3..90b824811d9 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.c
@@ -1097,9 +1097,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
if (node->type == active->type) {
break;
}
- else {
- node = NULL;
- }
+ node = NULL;
}
if (node) {
active = node;
@@ -1184,7 +1182,7 @@ static void node_find_update_fn(const struct bContext *C,
else {
BLI_strncpy(name, node->name, 256);
}
- if (!UI_search_item_add(items, name, node, ICON_NONE, 0)) {
+ if (!UI_search_item_add(items, name, node, ICON_NONE, 0, 0)) {
break;
}
}
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c
index 87b1f662b59..4f15cec8c84 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.c
@@ -71,9 +71,7 @@ static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
return (node->id == (ID *)item->ngroup);
}
- else {
- return true;
- }
+ return true;
}
static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item)
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c
index e879e01ecc4..ce66740dc60 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.c
@@ -133,9 +133,7 @@ static int node_view_all_exec(bContext *C, wmOperator *op)
if (space_node_view_flag(C, snode, region, 0, smooth_viewtx)) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void NODE_OT_view_all(wmOperatorType *ot)
@@ -162,9 +160,7 @@ static int node_view_selected_exec(bContext *C, wmOperator *op)
if (space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx)) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void NODE_OT_view_selected(wmOperatorType *ot)
@@ -423,7 +419,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
* And here we've got recursion in the comments tips...
*/
bool ED_space_node_color_sample(
- Main *bmain, SpaceNode *snode, ARegion *region, int mval[2], float r_col[3])
+ Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float r_col[3])
{
void *lock;
Image *ima;
@@ -460,7 +456,7 @@ bool ED_space_node_color_sample(
if (ibuf->rect_float) {
fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
- /* IB_PROFILE_NONE is default but infact its linear */
+ /* #IB_PROFILE_NONE is default but in fact its linear. */
copy_v3_v3(r_col, fp);
ret = true;
}
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index f339d11b842..d4adad3fc25 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -641,9 +641,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C),
/* rule might not work? */
return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
}
- else {
- return WM_drag_ID(drag, ID_IM) != NULL;
- }
+ return WM_drag_ID(drag, ID_IM) != NULL;
}
static bool node_mask_drop_poll(bContext *UNUSED(C),
@@ -787,7 +785,7 @@ static int node_context(const bContext *C, const char *member, bContextDataResul
CTX_data_dir_set(result, node_context_dir);
return 1;
}
- else if (CTX_data_equals(member, "selected_nodes")) {
+ if (CTX_data_equals(member, "selected_nodes")) {
bNode *node;
if (snode->edittree) {
@@ -800,7 +798,7 @@ static int node_context(const bContext *C, const char *member, bContextDataResul
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
- else if (CTX_data_equals(member, "active_node")) {
+ if (CTX_data_equals(member, "active_node")) {
if (snode->edittree) {
bNode *node = nodeGetActive(snode->edittree);
CTX_data_pointer_set(result, &snode->edittree->id, &RNA_Node, node);
@@ -809,7 +807,7 @@ static int node_context(const bContext *C, const char *member, bContextDataResul
CTX_data_type_set(result, CTX_DATA_TYPE_POINTER);
return 1;
}
- else if (CTX_data_equals(member, "node_previews")) {
+ if (CTX_data_equals(member, "node_previews")) {
if (snode->nodetree) {
CTX_data_pointer_set(
result, &snode->nodetree->id, &RNA_NodeInstanceHash, snode->nodetree->previews);
@@ -818,19 +816,19 @@ static int node_context(const bContext *C, const char *member, bContextDataResul
CTX_data_type_set(result, CTX_DATA_TYPE_POINTER);
return 1;
}
- else if (CTX_data_equals(member, "material")) {
+ if (CTX_data_equals(member, "material")) {
if (snode->id && GS(snode->id->name) == ID_MA) {
CTX_data_id_pointer_set(result, snode->id);
}
return 1;
}
- else if (CTX_data_equals(member, "light")) {
+ if (CTX_data_equals(member, "light")) {
if (snode->id && GS(snode->id->name) == ID_LA) {
CTX_data_id_pointer_set(result, snode->id);
}
return 1;
}
- else if (CTX_data_equals(member, "world")) {
+ if (CTX_data_equals(member, "world")) {
if (snode->id && GS(snode->id->name) == ID_WO) {
CTX_data_id_pointer_set(result, snode->id);
}
@@ -936,11 +934,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..0964e0c753e 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -70,7 +70,7 @@ bool outliner_is_collection_tree_element(const TreeElement *te)
TSE_VIEW_COLLECTION_BASE)) {
return true;
}
- else if (tselem->type == 0 && te->idcode == ID_GR) {
+ if (tselem->type == 0 && te->idcode == ID_GR) {
return true;
}
@@ -89,11 +89,11 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te)
LayerCollection *lc = te->directdata;
return lc->collection;
}
- else if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
+ if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
Scene *scene = (Scene *)tselem->id;
return scene->master_collection;
}
- else if (tselem->type == 0 && te->idcode == ID_GR) {
+ if (tselem->type == 0 && te->idcode == ID_GR) {
return (Collection *)tselem->id;
}
@@ -338,7 +338,7 @@ void outliner_collection_delete(
skip = true;
break;
}
- else if (parent->flag & COLLECTION_IS_MASTER) {
+ if (parent->flag & COLLECTION_IS_MASTER) {
Scene *parent_scene = BKE_collection_master_scene_search(bmain, parent);
if (ID_IS_LINKED(parent_scene)) {
skip = true;
@@ -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));
@@ -716,7 +717,7 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op))
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
- while (BKE_collection_find_cycle(active_lc->collection, collection)) {
+ while (BKE_collection_cycle_find(active_lc->collection, collection)) {
active_lc = BKE_layer_collection_activate_parent(view_layer, active_lc);
}
}
@@ -1193,7 +1194,7 @@ static bool collection_flag_poll(bContext *C, bool clear, int flag)
if (clear && (collection->flag & flag)) {
return true;
}
- else if (!clear && !(collection->flag & flag)) {
+ if (!clear && !(collection->flag & flag)) {
return true;
}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index c2c9f3a5bfb..5baaef958fa 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -123,9 +123,7 @@ static ID *outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode
if (te && te->idcode == idcode && tselem->type == 0) {
return tselem->id;
}
- else {
- return NULL;
- }
+ return NULL;
}
/* Find tree element to drop into, with additional before and after reorder support. */
@@ -154,44 +152,35 @@ static TreeElement *outliner_drop_insert_find(bContext *C,
*r_insert_type = TE_INSERT_INTO;
return te_hovered;
}
- else {
- *r_insert_type = TE_INSERT_BEFORE;
- return te_hovered->subtree.first;
- }
+ *r_insert_type = TE_INSERT_BEFORE;
+ return te_hovered->subtree.first;
}
- else {
- *r_insert_type = TE_INSERT_AFTER;
- return te_hovered;
- }
- }
- else if (view_mval[1] > (te_hovered->ys + (3 * margin))) {
- *r_insert_type = TE_INSERT_BEFORE;
+ *r_insert_type = TE_INSERT_AFTER;
return te_hovered;
}
- else {
- *r_insert_type = TE_INSERT_INTO;
+ if (view_mval[1] > (te_hovered->ys + (3 * margin))) {
+ *r_insert_type = TE_INSERT_BEFORE;
return te_hovered;
}
+ *r_insert_type = TE_INSERT_INTO;
+ return te_hovered;
}
- else {
- /* Mouse doesn't hover any item (ignoring x-axis),
- * so it's either above list bounds or below. */
- TreeElement *first = soops->tree.first;
- TreeElement *last = soops->tree.last;
- if (view_mval[1] < last->ys) {
- *r_insert_type = TE_INSERT_AFTER;
- return last;
- }
- else if (view_mval[1] > (first->ys + UI_UNIT_Y)) {
- *r_insert_type = TE_INSERT_BEFORE;
- return first;
- }
- else {
- BLI_assert(0);
- return NULL;
- }
+ /* Mouse doesn't hover any item (ignoring x-axis),
+ * so it's either above list bounds or below. */
+ TreeElement *first = soops->tree.first;
+ TreeElement *last = soops->tree.last;
+
+ if (view_mval[1] < last->ys) {
+ *r_insert_type = TE_INSERT_AFTER;
+ return last;
+ }
+ if (view_mval[1] > (first->ys + UI_UNIT_Y)) {
+ *r_insert_type = TE_INSERT_BEFORE;
+ return first;
}
+ BLI_assert(0);
+ return NULL;
}
static Collection *outliner_collection_from_tree_element_and_parents(TreeElement *te,
@@ -270,9 +259,7 @@ static bool parent_drop_allowed(TreeElement *te, Object *potential_child)
}
return false;
}
- else {
- return true;
- }
+ return true;
}
static bool allow_parenting_without_modifier_key(SpaceOutliner *soops)
@@ -650,7 +637,7 @@ static Collection *collection_parent_from_ID(ID *id)
if (GS(id->name) == ID_SCE) {
return ((Scene *)id)->master_collection;
}
- else if (GS(id->name) == ID_GR) {
+ if (GS(id->name) == ID_GR) {
return (Collection *)id;
}
@@ -772,12 +759,10 @@ static bool collection_drop_poll(bContext *C,
}
return true;
}
- else {
- if (changed) {
- ED_region_tag_redraw_no_rebuild(region);
- }
- return false;
+ if (changed) {
+ ED_region_tag_redraw_no_rebuild(region);
}
+ return false;
}
static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -873,6 +858,8 @@ void OUTLINER_OT_collection_drop(wmOperatorType *ot)
/* ********************* Outliner Drag Operator ******************** */
+#define OUTLINER_DRAG_SCOLL_OUTSIDE_PAD 7 /* In UI units */
+
static TreeElement *outliner_item_drag_element_find(SpaceOutliner *soops,
ARegion *region,
const wmEvent *event)
@@ -907,6 +894,17 @@ static int outliner_item_drag_drop_invoke(bContext *C,
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
}
+ /* Scroll the view when dragging near edges, but not
+ * when the drag goes too far outside the region. */
+ {
+ wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
+ PointerRNA op_ptr;
+ WM_operator_properties_create_ptr(&op_ptr, ot);
+ RNA_int_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr);
+ WM_operator_properties_free(&op_ptr);
+ }
+
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)) {
@@ -1005,6 +1003,8 @@ void OUTLINER_OT_item_drag_drop(wmOperatorType *ot)
ot->poll = ED_operator_outliner_active;
}
+#undef OUTLINER_DRAG_SCOLL_OUTSIDE_PAD
+
/* *************************** Drop Boxes ************************** */
/* region dropbox definition */
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 7d3b95721c6..a45b415b629 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -80,8 +80,8 @@
#include "outliner_intern.h"
-/* disable - this is far too slow - campbell */
-// #define USE_GROUP_SELECT
+/* Disable - this is far too slow - campbell. */
+/* #define USE_GROUP_SELECT */
/* ****************************************************** */
/* Tree Size Functions */
@@ -173,12 +173,12 @@ static void restrictbutton_recursive_bone(Bone *bone_parent, int flag, bool set_
}
}
-static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2))
+static void restrictbutton_r_lay_fn(bContext *C, void *poin, void *UNUSED(poin2))
{
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin);
}
-static void restrictbutton_bone_visibility_cb(bContext *C, void *poin, void *UNUSED(poin2))
+static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNUSED(poin2))
{
Bone *bone = (Bone *)poin;
@@ -187,7 +187,7 @@ static void restrictbutton_bone_visibility_cb(bContext *C, void *poin, void *UNU
}
}
-static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
+static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void *poin2)
{
Bone *bone = (Bone *)poin2;
if (bone->flag & BONE_UNSELECTABLE) {
@@ -201,7 +201,7 @@ static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
}
-static void restrictbutton_ebone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
+static void restrictbutton_ebone_select_fn(bContext *C, void *UNUSED(poin), void *poin2)
{
EditBone *ebone = (EditBone *)poin2;
@@ -217,7 +217,7 @@ static void restrictbutton_ebone_select_cb(bContext *C, void *UNUSED(poin), void
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
}
-static void restrictbutton_ebone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
+static void restrictbutton_ebone_visibility_fn(bContext *C, void *UNUSED(poin), void *poin2)
{
EditBone *ebone = (EditBone *)poin2;
if (ebone->flag & BONE_HIDDEN_A) {
@@ -231,7 +231,7 @@ static void restrictbutton_ebone_visibility_cb(bContext *C, void *UNUSED(poin),
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
}
-static void restrictbutton_gp_layer_flag_cb(bContext *C, void *poin, void *UNUSED(poin2))
+static void restrictbutton_gp_layer_flag_fn(bContext *C, void *poin, void *UNUSED(poin2))
{
ID *id = (ID *)poin;
@@ -253,7 +253,7 @@ static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void
}
}
-static void outliner_object_set_flag_recursive_cb(bContext *C,
+static void outliner_object_set_flag_recursive_fn(bContext *C,
Base *base,
Object *ob,
const char *propname)
@@ -313,21 +313,21 @@ static void outliner_object_set_flag_recursive_cb(bContext *C,
/**
* Object properties.
* */
-static void outliner__object_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
+static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
Object *ob = poin;
char *propname = poin2;
- outliner_object_set_flag_recursive_cb(C, NULL, ob, propname);
+ outliner_object_set_flag_recursive_fn(C, NULL, ob, propname);
}
/**
* Base properties.
* */
-static void outliner__base_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
+static void outliner__base_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
Base *base = poin;
char *propname = poin2;
- outliner_object_set_flag_recursive_cb(C, base, NULL, propname);
+ outliner_object_set_flag_recursive_fn(C, base, NULL, propname);
}
/** Create either a RNA_LayerCollection or a RNA_Collection pointer. */
@@ -568,7 +568,7 @@ void outliner_collection_isolate_flag(Scene *scene,
}
}
-static void outliner_collection_set_flag_recursive_cb(bContext *C,
+static void outliner_collection_set_flag_recursive_fn(bContext *C,
LayerCollection *layer_collection,
Collection *collection,
const char *propname)
@@ -632,24 +632,24 @@ static void outliner_collection_set_flag_recursive_cb(bContext *C,
* Layer collection properties called from the ViewLayer mode.
* Change the (non-excluded) collection children, and the objects nested to them all.
*/
-static void view_layer__layer_collection_set_flag_recursive_cb(bContext *C,
+static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C,
void *poin,
void *poin2)
{
LayerCollection *layer_collection = poin;
char *propname = poin2;
- outliner_collection_set_flag_recursive_cb(C, layer_collection, NULL, propname);
+ outliner_collection_set_flag_recursive_fn(C, layer_collection, NULL, propname);
}
/**
* Collection properties called from the ViewLayer mode.
* Change the (non-excluded) collection children, and the objects nested to them all.
*/
-static void view_layer__collection_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
+static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
LayerCollection *layer_collection = poin;
char *propname = poin2;
- outliner_collection_set_flag_recursive_cb(
+ outliner_collection_set_flag_recursive_fn(
C, layer_collection, layer_collection->collection, propname);
}
@@ -657,14 +657,14 @@ static void view_layer__collection_set_flag_recursive_cb(bContext *C, void *poin
* Collection properties called from the Scenes mode.
* Change the collection children but no objects.
*/
-static void scenes__collection_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
+static void scenes__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
Collection *collection = poin;
char *propname = poin2;
- outliner_collection_set_flag_recursive_cb(C, NULL, collection, propname);
+ outliner_collection_set_flag_recursive_fn(C, NULL, collection, propname);
}
-static void namebutton_cb(bContext *C, void *tsep, char *oldname)
+static void namebutton_fn(bContext *C, void *tsep, char *oldname)
{
Main *bmain = CTX_data_main(C);
SpaceOutliner *soops = CTX_wm_space_outliner(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),
@@ -731,7 +731,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname)
else {
switch (tselem->type) {
case TSE_DEFGROUP:
- BKE_object_defgroup_unique_name(te->directdata, (Object *)tselem->id); // id = object
+ BKE_object_defgroup_unique_name(te->directdata, (Object *)tselem->id); /* id = object. */
break;
case TSE_NLA_ACTION:
BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
@@ -790,7 +790,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname)
break;
}
case TSE_POSEGRP: {
- Object *ob = (Object *)tselem->id; // id = object
+ Object *ob = (Object *)tselem->id; /* id = object. */
bActionGroup *grp = te->directdata;
BLI_uniquename(&ob->pose->agroups,
@@ -809,7 +809,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname)
/* always make layer active */
BKE_gpencil_layer_active_set(gpd, gpl);
- // XXX: name needs translation stuff
+ /* XXX: name needs translation stuff. */
BLI_uniquename(
&gpd->layers, gpl, "GP Layer", '.', offsetof(bGPDlayer, info), sizeof(gpl->info));
@@ -1073,7 +1073,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
TIP_("Use view layer for rendering"));
- UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL);
+ UI_but_func_set(bt, restrictbutton_r_lay_fn, tselem->id, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
@@ -1111,7 +1111,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
TIP_("Temporarily hide in viewport\n"
"* Shift to set children"));
UI_but_func_set(
- bt, outliner__base_set_flag_recursive_cb, base, (void *)"hide_viewport");
+ bt, outliner__base_set_flag_recursive_fn, base, (void *)"hide_viewport");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.base_hide_viewport) {
UI_but_flag_enable(bt, UI_BUT_INACTIVE);
@@ -1137,7 +1137,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
-1,
TIP_("Disable selection in viewport\n"
"* Shift to set children"));
- UI_but_func_set(bt, outliner__object_set_flag_recursive_cb, ob, (char *)"hide_select");
+ UI_but_func_set(bt, outliner__object_set_flag_recursive_fn, ob, (char *)"hide_select");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.object_hide_select) {
UI_but_flag_enable(bt, UI_BUT_INACTIVE);
@@ -1162,7 +1162,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
-1,
TIP_("Globally disable in viewports\n"
"* Shift to set children"));
- UI_but_func_set(bt, outliner__object_set_flag_recursive_cb, ob, (void *)"hide_viewport");
+ UI_but_func_set(bt, outliner__object_set_flag_recursive_fn, ob, (void *)"hide_viewport");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.object_hide_viewport) {
UI_but_flag_enable(bt, UI_BUT_INACTIVE);
@@ -1187,7 +1187,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
-1,
TIP_("Globally disable in renders\n"
"* Shift to set children"));
- UI_but_func_set(bt, outliner__object_set_flag_recursive_cb, ob, (char *)"hide_render");
+ UI_but_func_set(bt, outliner__object_set_flag_recursive_fn, ob, (char *)"hide_render");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.object_hide_render) {
UI_but_flag_enable(bt, UI_BUT_INACTIVE);
@@ -1301,7 +1301,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
-1,
-1,
TIP_("Restrict visibility in the 3D View"));
- UI_but_func_set(bt, restrictbutton_bone_visibility_cb, bone, NULL);
+ UI_but_func_set(bt, restrictbutton_bone_visibility_fn, bone, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
@@ -1322,7 +1322,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
TIP_("Restrict selection in the 3D View"));
- UI_but_func_set(bt, restrictbutton_bone_select_cb, ob->data, bone);
+ UI_but_func_set(bt, restrictbutton_bone_select_fn, ob->data, bone);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
@@ -1346,7 +1346,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
TIP_("Restrict visibility in the 3D View"));
- UI_but_func_set(bt, restrictbutton_ebone_visibility_cb, NULL, ebone);
+ UI_but_func_set(bt, restrictbutton_ebone_visibility_fn, NULL, ebone);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
@@ -1367,7 +1367,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
TIP_("Restrict selection in the 3D View"));
- UI_but_func_set(bt, restrictbutton_ebone_select_cb, NULL, ebone);
+ UI_but_func_set(bt, restrictbutton_ebone_select_fn, NULL, ebone);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
@@ -1376,13 +1376,13 @@ static void outliner_draw_restrictbuts(uiBlock *block,
ID *id = tselem->id;
bGPDlayer *gpl = (bGPDlayer *)te->directdata;
- if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
+ if (soops->show_restrict_flags & SO_RESTRICT_HIDE) {
bt = uiDefIconButBitS(block,
UI_BTYPE_ICON_TOGGLE,
GP_LAYER_HIDE,
0,
ICON_HIDE_OFF,
- (int)(region->v2d.cur.xmax - restrict_offsets.viewport),
+ (int)(region->v2d.cur.xmax - restrict_offsets.hide),
te->ys,
UI_UNIT_X,
UI_UNIT_Y,
@@ -1392,7 +1392,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
TIP_("Restrict visibility in the 3D View"));
- UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, id, gpl);
+ UI_but_func_set(bt, restrictbutton_gp_layer_flag_fn, id, gpl);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
@@ -1413,7 +1413,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
0,
0,
TIP_("Restrict editing of strokes and keyframes in this layer"));
- UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, id, gpl);
+ UI_but_func_set(bt, restrictbutton_gp_layer_flag_fn, id, gpl);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
}
}
@@ -1450,7 +1450,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
"* Ctrl to isolate collection\n"
"* Shift to set inside collections and objects"));
UI_but_func_set(bt,
- view_layer__layer_collection_set_flag_recursive_cb,
+ view_layer__layer_collection_set_flag_recursive_fn,
layer_collection,
(char *)"hide_viewport");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
@@ -1479,7 +1479,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
"* Ctrl to isolate collection\n"
"* Shift to set inside collections"));
UI_but_func_set(bt,
- view_layer__layer_collection_set_flag_recursive_cb,
+ view_layer__layer_collection_set_flag_recursive_fn,
layer_collection,
(char *)"holdout");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
@@ -1510,7 +1510,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
"* Ctrl to isolate collection\n"
"* Shift to set inside collections"));
UI_but_func_set(bt,
- view_layer__layer_collection_set_flag_recursive_cb,
+ view_layer__layer_collection_set_flag_recursive_fn,
layer_collection,
(char *)"indirect_only");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
@@ -1542,13 +1542,13 @@ static void outliner_draw_restrictbuts(uiBlock *block,
"* Shift to set inside collections and objects"));
if (layer_collection != NULL) {
UI_but_func_set(bt,
- view_layer__collection_set_flag_recursive_cb,
+ view_layer__collection_set_flag_recursive_fn,
layer_collection,
(char *)"hide_viewport");
}
else {
UI_but_func_set(bt,
- scenes__collection_set_flag_recursive_cb,
+ scenes__collection_set_flag_recursive_fn,
collection,
(char *)"hide_viewport");
}
@@ -1579,13 +1579,13 @@ static void outliner_draw_restrictbuts(uiBlock *block,
"* Shift to set inside collections and objects"));
if (layer_collection != NULL) {
UI_but_func_set(bt,
- view_layer__collection_set_flag_recursive_cb,
+ view_layer__collection_set_flag_recursive_fn,
layer_collection,
(char *)"hide_render");
}
else {
UI_but_func_set(
- bt, scenes__collection_set_flag_recursive_cb, collection, (char *)"hide_render");
+ bt, scenes__collection_set_flag_recursive_fn, collection, (char *)"hide_render");
}
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.collection_hide_render) {
@@ -1614,13 +1614,13 @@ static void outliner_draw_restrictbuts(uiBlock *block,
"* Shift to set inside collections and objects"));
if (layer_collection != NULL) {
UI_but_func_set(bt,
- view_layer__collection_set_flag_recursive_cb,
+ view_layer__collection_set_flag_recursive_fn,
layer_collection,
(char *)"hide_select");
}
else {
UI_but_func_set(
- bt, scenes__collection_set_flag_recursive_cb, collection, (char *)"hide_select");
+ bt, scenes__collection_set_flag_recursive_fn, collection, (char *)"hide_select");
}
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.collection_hide_select) {
@@ -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;
@@ -1891,7 +1869,7 @@ static void outliner_buttons(const bContext *C,
0,
0,
"");
- UI_but_func_rename_set(bt, namebutton_cb, tselem);
+ UI_but_func_rename_set(bt, namebutton_fn, tselem);
/* returns false if button got removed */
if (false == UI_but_active_only(C, region, block, bt)) {
@@ -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 : "");
}
}
@@ -2837,13 +2816,11 @@ int tree_element_id_type_to_index(TreeElement *te)
if (id_index < INDEX_ID_OB) {
return id_index;
}
- else if (id_index == INDEX_ID_OB) {
+ if (id_index == INDEX_ID_OB) {
const Object *ob = (Object *)tselem->id;
return INDEX_ID_OB + ob->type;
}
- else {
- return id_index + OB_TYPE_MAX;
- }
+ return id_index + OB_TYPE_MAX;
}
typedef struct MergedIconRow {
@@ -3092,7 +3069,7 @@ static void outliner_draw_tree_element(bContext *C,
icon_border);
GPU_blend(true); /* roundbox disables it */
- te->flag |= TE_ACTIVE; // for lookup in display hierarchies
+ te->flag |= TE_ACTIVE; /* For lookup in display hierarchies. */
}
if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
@@ -3103,7 +3080,7 @@ static void outliner_draw_tree_element(bContext *C,
/* open/close icon, only when sublevels, except for scene */
int icon_x = startx;
- // icons a bit higher
+ /* Icons a bit higher. */
if (TSELEM_OPEN(tselem, soops)) {
UI_icon_draw_alpha((float)icon_x + 2 * ufac,
(float)*starty + 1 * ufac,
@@ -3129,33 +3106,14 @@ 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);
+ 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 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);
- }
- else {
- UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
- (float)*starty + 2 * ufac,
- ICON_LIBRARY_DATA_DIRECT,
- alpha_fac);
- }
- 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);
@@ -3211,9 +3169,9 @@ static void outliner_draw_tree_element(bContext *C,
*starty -= UI_UNIT_Y;
LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) {
- /* check if element needs to be drawn grayed out, but also gray out
- * childs of a grayed out parent (pass on draw_grayed_out to childs) */
- bool draw_childs_grayed_out = draw_grayed_out || (ten->flag & TE_DRAGGING);
+ /* Check if element needs to be drawn grayed out, but also gray out
+ * children of a grayed out parent (pass on draw_grayed_out to children). */
+ bool draw_children_grayed_out = draw_grayed_out || (ten->flag & TE_DRAGGING);
outliner_draw_tree_element(C,
block,
fstyle,
@@ -3221,7 +3179,7 @@ static void outliner_draw_tree_element(bContext *C,
region,
soops,
ten,
- draw_childs_grayed_out,
+ draw_children_grayed_out,
startx + UI_UNIT_X,
starty,
restrict_column_width,
@@ -3268,10 +3226,10 @@ static void outliner_draw_hierarchy_lines_recursive(uint pos,
/* For vertical lines between objects. */
y1 = y2 = y1_dashed = y2_dashed = *starty;
for (te = lb->first; te; te = te->next) {
- bool draw_childs_grayed_out = draw_grayed_out || (te->flag & TE_DRAGGING);
+ bool draw_children_grayed_out = draw_grayed_out || (te->flag & TE_DRAGGING);
TreeStoreElem *tselem = TREESTORE(te);
- if (draw_childs_grayed_out) {
+ if (draw_children_grayed_out) {
immUniformColor3ubvAlpha(col, grayed_alpha);
}
else {
@@ -3308,7 +3266,7 @@ static void outliner_draw_hierarchy_lines_recursive(uint pos,
if (TSELEM_OPEN(tselem, soops)) {
outliner_draw_hierarchy_lines_recursive(
- pos, soops, &te->subtree, startx + UI_UNIT_X, col, draw_childs_grayed_out, starty);
+ pos, soops, &te->subtree, startx + UI_UNIT_X, col, draw_children_grayed_out, starty);
}
}
@@ -3529,7 +3487,7 @@ static void outliner_draw_tree(bContext *C,
int starty, startx;
GPU_blend_set_func_separate(
- GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); // only once
+ GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); /* Only once. */
if (soops->outlinevis == SO_DATA_API) {
/* struct marks */
@@ -3552,13 +3510,12 @@ static void outliner_draw_tree(bContext *C,
GPU_scissor(0, 0, mask_x, region->winy);
}
- // gray hierarchy lines
-
+ /* Gray hierarchy lines. */
starty = (int)region->v2d.tot.ymax - UI_UNIT_Y / 2 - OL_Y_OFFSET;
startx = UI_UNIT_X / 2 - (U.pixelsize + 1) / 2;
outliner_draw_hierarchy_lines(soops, &soops->tree, startx, &starty);
- // items themselves
+ /* Items themselves. */
starty = (int)region->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
startx = 0;
LISTBASE_FOREACH (TreeElement *, te, &soops->tree) {
@@ -3630,9 +3587,7 @@ static int outliner_width(SpaceOutliner *soops, int max_tree_width, float restri
if (soops->outlinevis == SO_DATA_API) {
return outliner_data_api_buttons_start_x(max_tree_width) + OL_RNA_COL_SIZEX + 10 * UI_DPI_FAC;
}
- else {
- return max_tree_width + restrict_column_width;
- }
+ return max_tree_width + restrict_column_width;
}
static void outliner_update_viewable_area(ARegion *region,
@@ -3665,7 +3620,7 @@ void draw_outliner(const bContext *C)
TreeViewContext tvc;
outliner_viewcontext_init(C, &tvc);
- outliner_build_tree(mainvar, tvc.scene, tvc.view_layer, soops, region); // always
+ outliner_build_tree(mainvar, tvc.scene, tvc.view_layer, soops, region); /* Always. */
/* If global sync select is dirty, flag other outliners */
if (ED_outliner_select_sync_is_dirty(C)) {
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 3db75d9288b..4fe3d5b0df7 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) {
@@ -487,14 +500,14 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto
BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked id '%s'", id->name);
return;
}
- else if (BKE_library_ID_is_indirectly_used(bmain, id) && ID_REAL_USERS(id) <= 1) {
+ if (BKE_library_ID_is_indirectly_used(bmain, id) && ID_REAL_USERS(id) <= 1) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete id '%s', indirectly used data-blocks need at least one user",
id->name);
return;
}
- else if (te->idcode == ID_WS) {
+ if (te->idcode == ID_WS) {
BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT);
if (id->tag & LIB_TAG_DOIT) {
BKE_reportf(
@@ -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,15 +944,13 @@ 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 {
- wmOperatorType *ot = WM_operatortype_find(
- reload ? "WM_OT_lib_reload" : "WM_OT_lib_relocate", false);
- return lib_relocate(C, te, tselem, ot, reload);
- }
+ wmOperatorType *ot = WM_operatortype_find(reload ? "WM_OT_lib_reload" : "WM_OT_lib_relocate",
+ false);
+ return lib_relocate(C, te, tselem, ot, reload);
}
}
else {
@@ -1463,7 +1475,7 @@ void OUTLINER_OT_scroll_page(wmOperatorType *ot)
/** \} */
-#if 0 // TODO: probably obsolete now with filtering?
+#if 0 /* TODO: probably obsolete now with filtering? */
/* -------------------------------------------------------------------- */
/** \name Search
@@ -1507,7 +1519,7 @@ static TreeElement *outliner_find_name(
static void outliner_find_panel(
Scene *UNUSED(scene), ARegion *region, SpaceOutliner *soops, int again, int flags)
{
- ReportList *reports = NULL; // CTX_wm_reports(C);
+ ReportList *reports = NULL; /* CTX_wm_reports(C); */
TreeElement *te = NULL;
TreeElement *last_find;
TreeStoreElem *tselem;
@@ -1534,10 +1546,10 @@ static void outliner_find_panel(
else {
/* pop up panel - no previous, or user didn't want search after previous */
name[0] = '\0';
- // XXX if (sbutton(name, 0, sizeof(name) - 1, "Find: ") && name[0]) {
- // te = outliner_find_name(soops, &soops->tree, name, flags, NULL, &prevFound);
- // }
- // else return; /* XXX RETURN! XXX */
+ /* XXX if (sbutton(name, 0, sizeof(name) - 1, "Find: ") && name[0]) { */
+ /* te = outliner_find_name(soops, &soops->tree, name, flags, NULL, &prevFound); */
+ /* } */
+ /* else return; XXX RETURN! XXX */
}
/* do selection and reveal */
@@ -1752,7 +1764,7 @@ void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
/* callbacks */
ot->exec = outliner_show_hierarchy_exec;
- ot->poll = ED_operator_outliner_active; // TODO: shouldn't be allowed in RNA views...
+ ot->poll = ED_operator_outliner_active; /* TODO: shouldn't be allowed in RNA views... */
/* no undo or registry, UI option */
}
@@ -2038,7 +2050,7 @@ static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
/* send notifiers */
- WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
+ WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); /* XXX */
return OPERATOR_FINISHED;
}
@@ -2077,7 +2089,7 @@ static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
/* send notifiers */
- WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
+ WM_event_add_notifier(C, ND_KEYS, NULL); /* XXX */
return OPERATOR_FINISHED;
}
@@ -2114,8 +2126,8 @@ enum {
KEYINGSET_EDITMODE_REMOVE,
} /*eKeyingSet_EditModes*/;
-/* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
-// TODO: should this be an API func?
+/* Find the 'active' KeyingSet, and add if not found (if adding is allowed). */
+/* TODO: should this be an API func? */
static KeyingSet *verify_active_keyingset(Scene *scene, short add)
{
KeyingSet *ks = NULL;
@@ -2130,8 +2142,8 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add)
ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
}
- /* add if none found */
- // XXX the default settings have yet to evolve
+ /* Add if none found */
+ /* XXX the default settings have yet to evolve. */
if ((add) && (ks == NULL)) {
ks = BKE_keyingset_add(&scene->keyingsets, NULL, NULL, KEYINGSET_ABSOLUTE, 0);
scene->active_keyingset = BLI_listbase_count(&scene->keyingsets);
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index f2b64bc2a4b..186a66a95c0 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -50,11 +50,11 @@ typedef enum TreeElementInsertType {
} TreeElementInsertType;
typedef enum TreeTraversalAction {
- /* Continue traversal regularly, don't skip children. */
+ /** Continue traversal regularly, don't skip children. */
TRAVERSE_CONTINUE = 0,
- /* Stop traversal */
+ /** Stop traversal. */
TRAVERSE_BREAK,
- /* Continue traversal, but skip childs of traversed element */
+ /** Continue traversal, but skip children of traversed element. */
TRAVERSE_SKIP_CHILDS,
} TreeTraversalAction;
@@ -63,15 +63,15 @@ typedef TreeTraversalAction (*TreeTraversalFunc)(struct TreeElement *te, void *c
typedef struct TreeElement {
struct TreeElement *next, *prev, *parent;
ListBase subtree;
- int xs, ys; // do selection
- TreeStoreElem *store_elem; // element in tree store
- short flag; // flag for non-saved stuff
- short index; // index for data arrays
- short idcode; // from TreeStore id
- short xend; // width of item display, for select
+ int xs, ys; /* Do selection. */
+ TreeStoreElem *store_elem; /* Element in tree store. */
+ short flag; /* Flag for non-saved stuff. */
+ short index; /* Index for data arrays. */
+ short idcode; /* From TreeStore id. */
+ short xend; /* Width of item display, for select. */
const char *name;
- void *directdata; // Armature Bones, Base, Sequence, Strip...
- PointerRNA rnaptr; // RNA Pointer
+ void *directdata; /* Armature Bones, Base, Sequence, Strip... */
+ PointerRNA rnaptr; /* RNA Pointer. */
} TreeElement;
typedef struct TreeElementIcon {
@@ -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..8bf05c3018a 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 */
@@ -424,7 +455,7 @@ static eOLDrawState tree_element_active_material(bContext *C,
/* we search for the object parent */
ob = (Object *)outliner_search_back(te, ID_OB);
- // note: ob->matbits can be NULL when a local object points to a library mesh.
+ /* Note : ob->matbits can be NULL when a local object points to a library mesh. */
if (ob == NULL || ob != OBACT(view_layer) || ob->matbits == NULL) {
return OL_DRAWSEL_NONE; /* just paranoia */
}
@@ -434,7 +465,7 @@ static eOLDrawState tree_element_active_material(bContext *C,
if (tes->idcode == ID_OB) {
if (set != OL_SETSEL_NONE) {
ob->actcol = te->index + 1;
- ob->matbits[te->index] = 1; // make ob material active too
+ ob->matbits[te->index] = 1; /* Make ob material active too. */
}
else {
if (ob->actcol == te->index + 1) {
@@ -448,7 +479,7 @@ static eOLDrawState tree_element_active_material(bContext *C,
else {
if (set != OL_SETSEL_NONE) {
ob->actcol = te->index + 1;
- ob->matbits[te->index] = 0; // make obdata material active too
+ ob->matbits[te->index] = 0; /* Make obdata material active too. */
}
else {
if (ob->actcol == te->index + 1) {
@@ -489,9 +520,7 @@ static eOLDrawState tree_element_active_camera(bContext *C,
return OL_DRAWSEL_NONE;
}
- else {
- return scene->camera == ob;
- }
+ return scene->camera == ob;
}
static eOLDrawState tree_element_active_world(bContext *C,
@@ -521,10 +550,7 @@ static eOLDrawState tree_element_active_world(bContext *C,
}
if (tep == NULL || tselem->id == (ID *)scene) {
- if (set != OL_SETSEL_NONE) {
- // XXX extern_set_butspace(F8KEY, 0);
- }
- else {
+ if (set == OL_SETSEL_NONE) {
return OL_DRAWSEL_NORMAL;
}
}
@@ -736,14 +762,14 @@ static void tree_element_active_ebone__sel(bContext *C, bArmature *arm, EditBone
if (sel) {
ebone->flag |= BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL;
arm->act_edbone = ebone;
- // flush to parent?
+ /* Flush to parent? */
if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
ebone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
- // flush to parent?
+ /* Flush to parent? */
if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
ebone->parent->flag &= ~BONE_TIPSEL;
}
@@ -811,8 +837,6 @@ static eOLDrawState tree_element_active_modifier(bContext *C,
Object *ob = (Object *)tselem->id;
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
-
- // XXX extern_set_butspace(F9KEY, 0);
}
return OL_DRAWSEL_NONE;
@@ -828,8 +852,6 @@ static eOLDrawState tree_element_active_psys(bContext *C,
Object *ob = (Object *)tselem->id;
WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
-
- // XXX extern_set_butspace(F7KEY, 0);
}
return OL_DRAWSEL_NONE;
@@ -846,25 +868,13 @@ static int tree_element_active_constraint(bContext *C,
Object *ob = (Object *)tselem->id;
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
- // XXX extern_set_butspace(F7KEY, 0);
}
return OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_active_text(bContext *UNUSED(C),
+static eOLDrawState tree_element_active_pose(bContext *UNUSED(C),
Scene *UNUSED(scene),
- ViewLayer *UNUSED(sl),
- SpaceOutliner *UNUSED(soops),
- TreeElement *UNUSED(te),
- int UNUSED(set))
-{
- // XXX removed
- return OL_DRAWSEL_NONE;
-}
-
-static eOLDrawState tree_element_active_pose(bContext *C,
- Scene *scene,
ViewLayer *view_layer,
TreeElement *UNUSED(te),
TreeStoreElem *tselem,
@@ -879,7 +889,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) {
@@ -941,7 +950,7 @@ static eOLDrawState tree_element_active_sequence_dup(Scene *scene,
return OL_DRAWSEL_NONE;
}
- // XXX select_single_seq(seq, 1);
+ /* XXX select_single_seq(seq, 1); */
p = ed->seqbasep->first;
while (p) {
if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
@@ -949,8 +958,8 @@ static eOLDrawState tree_element_active_sequence_dup(Scene *scene,
continue;
}
- // if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
- // XXX select_single_seq(p, 0);
+ /* XXX: if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) select_single_seq(p,
+ * 0); */
p = p->next;
}
return OL_DRAWSEL_NONE;
@@ -971,9 +980,7 @@ static eOLDrawState tree_element_active_keymap_item(bContext *UNUSED(C),
}
return OL_DRAWSEL_NORMAL;
}
- else {
- kmi->flag ^= KMI_INACTIVE;
- }
+ kmi->flag ^= KMI_INACTIVE;
return OL_DRAWSEL_NONE;
}
@@ -1045,8 +1052,6 @@ eOLDrawState tree_element_active(bContext *C,
return tree_element_active_material(C, tvc->scene, tvc->view_layer, te, set);
case ID_WO:
return tree_element_active_world(C, tvc->scene, tvc->view_layer, soops, te, set);
- case ID_TXT:
- return tree_element_active_text(C, tvc->scene, tvc->view_layer, soops, te, set);
case ID_CA:
return tree_element_active_camera(C, tvc->scene, tvc->view_layer, te, set);
}
@@ -1113,13 +1118,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 +1131,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 +1150,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,10 +1160,7 @@ 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 (tselem->type == 0) { /* The lib blocks. */
if (do_activate_data == false) {
/* Only select in outliner. */
}
@@ -1216,20 +1209,7 @@ 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
+ else { /* Rest of types. */
tree_element_active(C, tvc, soops, te, OL_SETSEL_NORMAL, false);
}
}
@@ -1239,23 +1219,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 +1304,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 +1317,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 +1338,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.
@@ -1385,7 +1372,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
else {
- /* The row may also contain children, if one is hovered we want this instead of current te */
+ /* The row may also contain children, if one is hovered we want this instead of current te. */
bool merged_elements = false;
TreeElement *activate_te = outliner_find_item_at_x_in_row(
soops, te, view_mval[0], &merged_elements);
@@ -1402,14 +1389,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 +1458,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 +1492,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 +1573,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 */
@@ -1609,107 +1595,85 @@ static TreeElement *outliner_element_find_successor_in_parents(TreeElement *te)
te = successor->parent->next;
break;
}
- else {
- successor = successor->parent;
- }
+ successor = successor->parent;
}
return 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 +1702,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 80a63af3f42..dae2ba32f09 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"
@@ -53,6 +54,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
+#include "BKE_idtype.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
@@ -541,7 +543,7 @@ static void merged_element_search_cb_recursive(
/* Don't allow duplicate named items */
if (UI_search_items_find_index(items, name) == -1) {
- if (!UI_search_item_add(items, name, te, iconid, 0)) {
+ if (!UI_search_item_add(items, name, te, iconid, 0, 0)) {
break;
}
}
@@ -573,8 +575,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 +665,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 +691,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) {
@@ -702,8 +700,8 @@ static void outliner_object_delete(bContext *C,
reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2);
return;
}
- else if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
- ID_EXTRA_USERS(ob) == 0) {
+ if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
+ ID_EXTRA_USERS(ob) == 0) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at "
@@ -742,26 +740,91 @@ static void id_local_cb(bContext *C,
}
}
-static void id_override_library_cb(bContext *C,
- ReportList *UNUSED(reports),
- Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
+typedef struct OutlinerLibOverrideData {
+ bool do_hierarchy;
+} OutlinerLibOverrideData;
+
+static void id_override_library_create_cb(bContext *C,
+ ReportList *UNUSED(reports),
+ Scene *UNUSED(scene),
+ TreeElement *te,
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *user_data)
{
- if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) {
+ BLI_assert(TSE_IS_REAL_ID(tselem));
+ ID *id_root = tselem->id;
+ OutlinerLibOverrideData *data = user_data;
+ const bool do_hierarchy = data->do_hierarchy;
+
+ if (ID_IS_OVERRIDABLE_LIBRARY(id_root) || (ID_IS_LINKED(id_root) && do_hierarchy)) {
Main *bmain = CTX_data_main(C);
+
+ id_root->tag |= LIB_TAG_DOIT;
+
/* For now, remapp all local usages of linked ID to local override one here. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true);
- ID *override_id = BKE_lib_override_library_create_from_id(bmain, tselem->id, true);
- if (override_id != NULL) {
- BKE_main_id_clear_newpoins(bmain);
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter)) {
+ id_iter->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ if (do_hierarchy) {
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ while ((te = te->parent) != NULL) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
+ }
+ if (!ID_IS_LINKED(te->store_elem->id)) {
+ break;
+ }
+ te->store_elem->id->tag |= LIB_TAG_DOIT;
+ }
+ BKE_lib_override_library_create(
+ bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, NULL);
+ }
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
+ BKE_lib_override_library_create_from_id(bmain, id_root, true);
}
+
+ BKE_main_id_clear_newpoins(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
}
+static void id_override_library_reset_cb(bContext *C,
+ ReportList *UNUSED(reports),
+ Scene *UNUSED(scene),
+ TreeElement *UNUSED(te),
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *user_data)
+{
+ BLI_assert(TSE_IS_REAL_ID(tselem));
+ ID *id_root = tselem->id;
+ OutlinerLibOverrideData *data = user_data;
+ const bool do_hierarchy = data->do_hierarchy;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ Main *bmain = CTX_data_main(C);
+
+ if (do_hierarchy) {
+ BKE_lib_override_library_id_hierarchy_reset(bmain, id_root);
+ }
+ else {
+ BKE_lib_override_library_id_reset(bmain, id_root);
+ }
+
+ WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ }
+}
+
static void id_fake_user_set_cb(bContext *UNUSED(C),
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
@@ -804,11 +867,17 @@ static void id_select_linked_cb(bContext *C,
static void singleuser_action_cb(bContext *C,
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
+ TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *UNUSED(user_data))
{
+ /* This callback runs for all selected elements, some of which may not be actions which results
+ * in a crash. */
+ if (te->idcode != ID_AC) {
+ return;
+ }
+
ID *id = tselem->id;
if (id) {
@@ -860,18 +929,17 @@ 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;
if (tselem->flag & TSE_SELECTED) {
if (tselem->type == 0 && te->idcode == ID_OB) {
- // when objects selected in other scenes... dunno if that should be allowed
+ /* When objects selected in other scenes... dunno if that should be allowed. */
Scene *scene_owner = (Scene *)outliner_search_back(te, ID_SCE);
if (scene_owner && scene_act != scene_owner) {
WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), scene_owner);
}
- /* important to use 'scene_owner' not scene_act else deleting objects can crash.
+ /* Important to use 'scene_owner' not scene_act else deleting objects can crash.
* only use 'scene_act' when 'scene_owner' is NULL, which can happen when the
* outliner isn't showing scenes: Visible Layer draw mode for eg. */
operation_cb(
@@ -1051,10 +1119,10 @@ static void sequence_cb(int event, TreeElement *te, TreeStoreElem *tselem, void
(void)tselem;
}
-static void gp_layer_cb(int event,
- TreeElement *te,
- TreeStoreElem *UNUSED(tselem),
- void *UNUSED(arg))
+static void gpencil_layer_cb(int event,
+ TreeElement *te,
+ TreeStoreElem *UNUSED(tselem),
+ void *UNUSED(arg))
{
bGPDlayer *gpl = (bGPDlayer *)te->directdata;
@@ -1131,6 +1199,7 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem
{
bContext *C = (bContext *)Carg;
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
ModifierData *md = (ModifierData *)te->directdata;
Object *ob = (Object *)outliner_search_back(te, ID_OB);
@@ -1145,7 +1214,7 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
}
else if (event == OL_MODIFIER_OP_DELETE) {
- ED_object_modifier_remove(NULL, bmain, ob, md);
+ ED_object_modifier_remove(NULL, bmain, scene, ob, md);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_REMOVED, ob);
te->store_elem->flag &= ~TSE_SELECTED;
}
@@ -1175,82 +1244,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)
{
@@ -1282,8 +1275,8 @@ static Base *outline_batch_delete_hierarchy(
base->object->id.name + 2);
return base_next;
}
- else if (BKE_library_ID_is_indirectly_used(bmain, object) && ID_REAL_USERS(object) <= 1 &&
- ID_EXTRA_USERS(object) == 0) {
+ if (BKE_library_ID_is_indirectly_used(bmain, object) && ID_REAL_USERS(object) <= 1 &&
+ ID_EXTRA_USERS(object) == 0) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
@@ -1303,21 +1296,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) {
@@ -1341,7 +1329,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,
@@ -1356,7 +1343,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,
@@ -1370,7 +1356,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);
@@ -1387,7 +1372,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
event = RNA_enum_get(op->ptr, "type");
if (event == OL_OP_SELECT) {
- Scene *sce = scene; // to be able to delete, scenes are set...
+ Scene *sce = scene; /* To be able to delete, scenes are set... */
outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_select_cb);
if (scene != sce) {
WM_window_set_active_scene(bmain, C, win, sce);
@@ -1397,7 +1382,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
selection_changed = true;
}
else if (event == OL_OP_SELECT_HIERARCHY) {
- Scene *sce = scene; // to be able to delete, scenes are set...
+ Scene *sce = scene; /* To be able to delete, scenes are set... */
outliner_do_object_operation_ex(
C, op->reports, scene, soops, &soops->tree, object_select_hierarchy_cb, NULL, false);
if (scene != sce) {
@@ -1411,47 +1396,10 @@ 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
- * does not work here). */
+ /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
+ * trick does not work here). */
}
else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */
outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb);
@@ -1511,22 +1459,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)
@@ -1535,12 +1499,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
@@ -1577,6 +1561,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);
}
/** \} */
@@ -1590,7 +1579,10 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_UNLINK,
OUTLINER_IDOP_LOCAL,
- OUTLINER_IDOP_OVERRIDE_LIBRARY,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
OUTLINER_IDOP_SINGLE,
OUTLINER_IDOP_DELETE,
OUTLINER_IDOP_REMAP,
@@ -1605,15 +1597,10 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_SELECT_LINKED,
} eOutlinerIdOpTypes;
-// TODO: implement support for changing the ID-block used
+/* TODO: implement support for changing the ID-block used. */
static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
{OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY,
- "OVERRIDE_LIBRARY",
- 0,
- "Add Library Override",
- "Add a local override of this linked data-block"},
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
{OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
{OUTLINER_IDOP_REMAP,
@@ -1622,6 +1609,27 @@ static const EnumPropertyItem prop_id_op_types[] = {
"Remap Users",
"Make all users of selected data-blocks to use instead current (clicked) one"},
{0, "", 0, NULL, NULL},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
+ "OVERRIDE_LIBRARY_CREATE",
+ 0,
+ "Add Library Override",
+ "Add a local override of this linked data-block"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
+ "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
+ 0,
+ "Add Library Override Hierarchy",
+ "Add a local override of this linked data-block, and its hierarchy of dependencies"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
+ "OVERRIDE_LIBRARY_RESET",
+ 0,
+ "Reset Library Override",
+ "Reset this local override to its linked values"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
+ "OVERRIDE_LIBRARY_RESET_HIERARCHY",
+ 0,
+ "Reset Library Override Hierarchy",
+ "Reset this local override to its linked values, as well as its hierarchy of dependencies"},
+ {0, "", 0, NULL, NULL},
{OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
{OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
{0, "", 0, NULL, NULL},
@@ -1645,8 +1653,12 @@ static bool outliner_id_operation_item_poll(bContext *C,
SpaceOutliner *soops = CTX_wm_space_outliner(C);
switch (enum_value) {
- case OUTLINER_IDOP_OVERRIDE_LIBRARY:
- return BKE_lib_override_library_is_enabled();
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE:
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY:
+ return true;
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET:
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY:
+ return true;
case OUTLINER_IDOP_SINGLE:
if (!soops || ELEM(soops->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
return true;
@@ -1759,13 +1771,52 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Localized Data");
break;
}
- case OUTLINER_IDOP_OVERRIDE_LIBRARY: {
- if (BKE_lib_override_library_is_enabled()) {
- /* make local */
- outliner_do_libdata_operation(
- C, op->reports, scene, soops, &soops->tree, id_override_library_cb, NULL);
- ED_undo_push(C, "Overridden Data");
- }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
+ /* make local */
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ soops,
+ &soops->tree,
+ id_override_library_create_cb,
+ &(OutlinerLibOverrideData){.do_hierarchy = false});
+ ED_undo_push(C, "Overridden Data");
+ break;
+ }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
+ /* make local */
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ soops,
+ &soops->tree,
+ id_override_library_create_cb,
+ &(OutlinerLibOverrideData){.do_hierarchy = true});
+ ED_undo_push(C, "Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
+ /* make local */
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ soops,
+ &soops->tree,
+ id_override_library_reset_cb,
+ &(OutlinerLibOverrideData){.do_hierarchy = false});
+ ED_undo_push(C, "Reset Overridden Data");
+ break;
+ }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: {
+ /* make local */
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ soops,
+ &soops->tree,
+ id_override_library_reset_cb,
+ &(OutlinerLibOverrideData){.do_hierarchy = true});
+ ED_undo_push(C, "Reset Overridden Data Hierarchy");
break;
}
case OUTLINER_IDOP_SINGLE: {
@@ -1860,14 +1911,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
break;
default:
- // invalid - unhandled
+ /* Invalid - unhandled. */
break;
}
/* wrong notifier still... */
WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
- // XXX: this is just so that outliner is always up to date
+ /* XXX: this is just so that outliner is always up to date. */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
return OPERATOR_FINISHED;
@@ -2043,7 +2094,7 @@ static void actionset_id_cb(TreeElement *UNUSED(te),
/* "animation" entries case again */
BKE_animdata_set_action(NULL, tsep->id, act);
}
- // TODO: other cases not supported yet
+ /* TODO: other cases not supported yet. */
}
static int outliner_action_set_exec(bContext *C, wmOperator *op)
@@ -2067,7 +2118,7 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No valid action to add");
return OPERATOR_CANCELLED;
}
- else if (act->idroot == 0) {
+ if (act->idroot == 0) {
/* Hopefully in this case (i.e. library of userless actions),
* the user knows what they're doing. */
BKE_reportf(op->reports,
@@ -2115,7 +2166,7 @@ void OUTLINER_OT_action_set(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
- // TODO: this would be nicer as an ID-pointer...
+ /* TODO: this would be nicer as an ID-pointer... */
prop = RNA_def_enum(ot->srna, "action", DummyRNA_NULL_items, 0, "Action", "");
RNA_def_enum_funcs(prop, RNA_action_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
@@ -2139,8 +2190,8 @@ typedef enum eOutliner_AnimDataOps {
OUTLINER_ANIMOP_REFRESH_DRV,
OUTLINER_ANIMOP_CLEAR_DRV
- // OUTLINER_ANIMOP_COPY_DRIVERS,
- // OUTLINER_ANIMOP_PASTE_DRIVERS
+ /* OUTLINER_ANIMOP_COPY_DRIVERS, */
+ /* OUTLINER_ANIMOP_PASTE_DRIVERS */
} eOutliner_AnimDataOps;
static const EnumPropertyItem prop_animdata_op_types[] = {
@@ -2152,8 +2203,8 @@ static const EnumPropertyItem prop_animdata_op_types[] = {
{OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
{OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
{OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
- //{OUTLINER_ANIMOP_COPY_DRIVERS, "COPY_DRIVERS", 0, "Copy Drivers", ""},
- //{OUTLINER_ANIMOP_PASTE_DRIVERS, "PASTE_DRIVERS", 0, "Paste Drivers", ""},
+ /* {OUTLINER_ANIMOP_COPY_DRIVERS, "COPY_DRIVERS", 0, "Copy Drivers", ""}, */
+ /* {OUTLINER_ANIMOP_PASTE_DRIVERS, "PASTE_DRIVERS", 0, "Paste Drivers", ""}, */
{OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2209,7 +2260,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
soops, datalevel, event, &soops->tree, refreshdrivers_animdata_cb, NULL);
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
- // ED_undo_push(C, "Refresh Drivers"); /* no undo needed - shouldn't have any impact? */
+ /* ED_undo_push(C, "Refresh Drivers"); No undo needed - shouldn't have any impact? */
break;
case OUTLINER_ANIMOP_CLEAR_DRV:
@@ -2220,7 +2271,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Clear Drivers");
break;
- default: // invalid
+ default: /* Invalid. */
break;
}
@@ -2351,7 +2402,7 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
/** \name Data Menu Operator
* \{ */
-// XXX: select linked is for RNA structs only
+/* XXX: select linked is for RNA structs only. */
static const EnumPropertyItem prop_data_op_types[] = {
{OL_DOP_SELECT, "SELECT", 0, "Select", ""},
{OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
@@ -2404,7 +2455,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
break;
}
case TSE_GP_LAYER: {
- outliner_do_data_operation(soops, datalevel, event, &soops->tree, gp_layer_cb, NULL);
+ outliner_do_data_operation(soops, datalevel, event, &soops->tree, gpencil_layer_cb, NULL);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
ED_undo_push(C, "Grease Pencil Layer operation");
@@ -2470,7 +2521,7 @@ static int outliner_operator_menu(bContext *C, const char *opname)
static int do_outliner_operation_event(
bContext *C, ARegion *region, SpaceOutliner *soops, TreeElement *te, const float mval[2])
{
- ReportList *reports = CTX_wm_reports(C); // XXX...
+ ReportList *reports = CTX_wm_reports(C); /* XXX... */
if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
@@ -2498,32 +2549,29 @@ static int do_outliner_operation_event(
BKE_report(reports, RPT_WARNING, "Mixed selection");
return OPERATOR_CANCELLED;
}
- else {
- return outliner_operator_menu(C, "OUTLINER_OT_scene_operation");
- }
+ return outliner_operator_menu(C, "OUTLINER_OT_scene_operation");
}
- else if (objectlevel) {
+ if (objectlevel) {
WM_menu_name_call(C, "OUTLINER_MT_object", WM_OP_INVOKE_REGION_WIN);
return OPERATOR_FINISHED;
}
- else if (idlevel) {
+ if (idlevel) {
if (idlevel == -1 || datalevel) {
BKE_report(reports, RPT_WARNING, "Mixed selection");
return OPERATOR_CANCELLED;
}
- else {
- switch (idlevel) {
- case ID_GR:
- WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
- return OPERATOR_FINISHED;
- break;
- case ID_LI:
- return outliner_operator_menu(C, "OUTLINER_OT_lib_operation");
- break;
- default:
- return outliner_operator_menu(C, "OUTLINER_OT_id_operation");
- break;
- }
+
+ switch (idlevel) {
+ case ID_GR:
+ WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
+ return OPERATOR_FINISHED;
+ break;
+ case ID_LI:
+ return outliner_operator_menu(C, "OUTLINER_OT_lib_operation");
+ break;
+ default:
+ return outliner_operator_menu(C, "OUTLINER_OT_id_operation");
+ break;
}
}
else if (datalevel) {
@@ -2531,35 +2579,32 @@ static int do_outliner_operation_event(
BKE_report(reports, RPT_WARNING, "Mixed selection");
return OPERATOR_CANCELLED;
}
- else {
- if (datalevel == TSE_ANIM_DATA) {
- return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation");
- }
- else if (datalevel == TSE_DRIVER_BASE) {
- /* do nothing... no special ops needed yet */
- return OPERATOR_CANCELLED;
- }
- else if (datalevel == TSE_LAYER_COLLECTION) {
- WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
- return OPERATOR_FINISHED;
- }
- else if (ELEM(datalevel, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
- WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN);
- return OPERATOR_FINISHED;
- }
- else if (datalevel == TSE_ID_BASE) {
- /* do nothing... there are no ops needed here yet */
- }
- else if (datalevel == TSE_CONSTRAINT) {
- return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
- }
- else if (datalevel == TSE_MODIFIER) {
- return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation");
- }
- else {
- return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
- }
+ if (datalevel == TSE_ANIM_DATA) {
+ return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation");
+ }
+ if (datalevel == TSE_DRIVER_BASE) {
+ /* do nothing... no special ops needed yet */
+ return OPERATOR_CANCELLED;
+ }
+ if (datalevel == TSE_LAYER_COLLECTION) {
+ WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
+ return OPERATOR_FINISHED;
+ }
+ if (ELEM(datalevel, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
+ WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN);
+ return OPERATOR_FINISHED;
+ }
+ if (datalevel == TSE_ID_BASE) {
+ /* do nothing... there are no ops needed here yet */
+ return 0;
+ }
+ if (datalevel == TSE_CONSTRAINT) {
+ return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
+ }
+ if (datalevel == TSE_MODIFIER) {
+ return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation");
}
+ return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
}
return 0;
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index d6efe683673..db42fb8f319 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)
@@ -329,7 +340,7 @@ static void outliner_add_scene_contents(SpaceOutliner *soops,
}
}
-// can be inlined if necessary
+/* Can be inlined if necessary. */
static void outliner_add_object_contents(SpaceOutliner *soops,
TreeElement *te,
TreeStoreElem *tselem,
@@ -340,7 +351,7 @@ static void outliner_add_object_contents(SpaceOutliner *soops,
}
outliner_add_element(
- soops, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
+ soops, &te->subtree, ob->poselib, te, 0, 0); /* XXX FIXME.. add a special type for this. */
if (ob->proxy && !ID_IS_LINKED(ob)) {
outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
@@ -367,12 +378,12 @@ static void outliner_add_object_contents(SpaceOutliner *soops,
pchan->temp = (void *)ten;
if (pchan->constraints.first) {
- // Object *target;
+ /* Object *target; */
bConstraint *con;
TreeElement *ten1;
TreeElement *tenla1 = outliner_add_element(
soops, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
- // char *str;
+ /* char *str; */
tenla1->name = IFACE_("Constraints");
for (con = pchan->constraints.first; con; con = con->next, const_index++) {
@@ -436,11 +447,11 @@ static void outliner_add_object_contents(SpaceOutliner *soops,
}
if (ob->constraints.first) {
- // Object *target;
+ /* Object *target; */
bConstraint *con;
TreeElement *ten;
TreeElement *tenla = outliner_add_element(soops, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
- // char *str;
+ /* char *str; */
int a;
tenla->name = IFACE_("Constraints");
@@ -524,7 +535,7 @@ static void outliner_add_object_contents(SpaceOutliner *soops,
}
}
-// can be inlined if necessary
+/* Can be inlined if necessary. */
static void outliner_add_id_contents(SpaceOutliner *soops,
TreeElement *te,
TreeStoreElem *tselem,
@@ -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: {
@@ -666,8 +677,8 @@ static void outliner_add_id_contents(SpaceOutliner *soops,
break;
}
case ID_AC: {
- // XXX do we want to be exposing the F-Curves here?
- // bAction *act = (bAction *)id;
+ /* XXX do we want to be exposing the F-Curves here? */
+ /* bAction *act = (bAction *)id; */
break;
}
case ID_AR: {
@@ -741,7 +752,7 @@ static void outliner_add_id_contents(SpaceOutliner *soops,
outliner_add_element(soops, &te->subtree, gpd, te, TSE_ANIM_DATA, 0);
}
- // TODO: base element for layers?
+ /* TODO: base element for layers? */
for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) {
outliner_add_element(soops, &te->subtree, gpl, te, TSE_GP_LAYER, a);
a++;
@@ -758,20 +769,23 @@ static void outliner_add_id_contents(SpaceOutliner *soops,
}
case ID_HA: {
Hair *hair = (Hair *)id;
- if (outliner_animdata_test(hair->adt))
+ if (outliner_animdata_test(hair->adt)) {
outliner_add_element(soops, &te->subtree, hair, te, TSE_ANIM_DATA, 0);
+ }
break;
}
case ID_PT: {
PointCloud *pointcloud = (PointCloud *)id;
- if (outliner_animdata_test(pointcloud->adt))
+ if (outliner_animdata_test(pointcloud->adt)) {
outliner_add_element(soops, &te->subtree, pointcloud, te, TSE_ANIM_DATA, 0);
+ }
break;
}
case ID_VO: {
Volume *volume = (Volume *)id;
- if (outliner_animdata_test(volume->adt))
+ if (outliner_animdata_test(volume->adt)) {
outliner_add_element(soops, &te->subtree, volume, te, TSE_ANIM_DATA, 0);
+ }
break;
}
case ID_SIM: {
@@ -786,8 +800,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)
{
@@ -832,7 +851,7 @@ static TreeElement *outliner_add_element(
}
te->parent = parent;
- te->index = index; // for data arrays
+ te->index = index; /* For data arrays. */
if (ELEM(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
/* pass */
}
@@ -854,10 +873,10 @@ 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
+ te->name = id->name + 2; /* Default, can be overridden by Library or non-ID data. */
}
te->idcode = GS(id->name);
}
@@ -898,7 +917,7 @@ static TreeElement *outliner_add_element(
/* loop over all targets used here */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
if (lastadded != dtar->id) {
- // XXX this lastadded check is rather lame, and also fails quite badly...
+ /* XXX this lastadded check is rather lame, and also fails quite badly... */
outliner_add_element(soops, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0);
lastadded = dtar->id;
}
@@ -1510,7 +1529,7 @@ static void outliner_make_object_parent_hierarchy(ListBase *lb)
TreeStoreElem *tselem;
/* build hierarchy */
- // XXX also, set extents here...
+ /* XXX also, set extents here... */
te = lb->first;
while (te) {
ten = te->next;
@@ -1650,10 +1669,10 @@ static int treesort_alpha_ob(const void *v1, const void *v2)
if (comp == 1) {
return 1;
}
- else if (comp == 2) {
+ if (comp == 2) {
return -1;
}
- else if (comp == 3) {
+ if (comp == 3) {
/* Among objects first come the ones in the collection, followed by the ones not on it.
* This way we can have the dashed lines in a separate style connecting the former. */
if ((x1->te->flag & TE_CHILD_NOT_IN_COLLECTION) !=
@@ -1666,7 +1685,7 @@ static int treesort_alpha_ob(const void *v1, const void *v2)
if (comp > 0) {
return 1;
}
- else if (comp < 0) {
+ if (comp < 0) {
return -1;
}
return 0;
@@ -1698,7 +1717,7 @@ static int treesort_alpha(const void *v1, const void *v2)
if (comp > 0) {
return 1;
}
- else if (comp < 0) {
+ if (comp < 0) {
return -1;
}
return 0;
@@ -1774,10 +1793,10 @@ static void outliner_sort(ListBase *lb)
tp->idcode = te->idcode;
if (tselem->type && tselem->type != TSE_DEFGROUP) {
- tp->idcode = 0; // don't sort this
+ tp->idcode = 0; /* Don't sort this. */
}
if (tselem->type == TSE_ID_BASE) {
- tp->idcode = 1; // do sort this
+ tp->idcode = 1; /* Do sort this. */
}
tp->id = tselem->id;
@@ -1965,9 +1984,7 @@ static TreeElement *outliner_find_first_desired_element_at_y(const SpaceOutliner
if (te->ys + UI_UNIT_Y > view_co_limit) {
return te_sub;
}
- else {
- return NULL;
- }
+ return NULL;
}
if (te->next) {
@@ -2175,7 +2192,9 @@ static bool outliner_element_is_collection_or_object(TreeElement *te)
if ((tselem->type == 0) && (te->idcode == ID_OB)) {
return true;
}
- else if (outliner_is_collection_tree_element(te)) {
+
+ /* Collection instance datablocks should not be extracted. */
+ if (outliner_is_collection_tree_element(te) && !(te->parent && te->parent->idcode == ID_OB)) {
return true;
}
@@ -2225,7 +2244,7 @@ static int outliner_filter_subtree(SpaceOutliner *soops,
te_next = outliner_extract_children_from_subtree(te, lb);
continue;
}
- else if ((exclude_filter & SO_FILTER_SEARCH) == 0) {
+ if ((exclude_filter & SO_FILTER_SEARCH) == 0) {
/* Filter subtree too. */
outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter);
continue;
@@ -2290,8 +2309,8 @@ static void outliner_filter_tree(SpaceOutliner *soops, ViewLayer *view_layer)
/* ======================================================= */
/* Main Tree Building API */
-/* Main entry point for building the tree data-structure that the outliner represents */
-// TODO: split each mode into its own function?
+/* Main entry point for building the tree data-structure that the outliner represents. */
+/* TODO: split each mode into its own function? */
void outliner_build_tree(
Main *mainvar, Scene *scene, ViewLayer *view_layer, SpaceOutliner *soops, ARegion *region)
{
@@ -2340,8 +2359,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..9accf35784a 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;
}
}
}
@@ -110,7 +122,7 @@ static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement *
if ((child_te->flag & TE_ICONROW) && over_element) {
return child_te;
}
- else if ((child_te->flag & TE_ICONROW_MERGED) && over_element) {
+ if ((child_te->flag & TE_ICONROW_MERGED) && over_element) {
if (r_merged) {
*r_merged = true;
}
@@ -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.
@@ -384,9 +409,7 @@ bool outliner_is_element_visible(const TreeElement *te)
if (tselem->flag & TSE_CLOSED) {
return false;
}
- else {
- te = te->parent;
- }
+ te = te->parent;
}
return true;
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..7863d2b724d 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);
@@ -1449,8 +1450,11 @@ static void seq_prefetch_wm_notify(const bContext *C, Scene *scene)
}
}
-static void *sequencer_OCIO_transform_ibuf(
- const bContext *C, ImBuf *ibuf, bool *r_glsl_used, int *r_format, int *r_type)
+static void *sequencer_OCIO_transform_ibuf(const bContext *C,
+ ImBuf *ibuf,
+ bool *r_glsl_used,
+ eGPUTextureFormat *r_format,
+ eGPUDataFormat *r_data)
{
void *display_buffer;
void *cache_handle = NULL;
@@ -1459,30 +1463,32 @@ static void *sequencer_OCIO_transform_ibuf(
force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
force_fallback |= (ibuf->dither != 0.0f);
+ /* Default */
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UNSIGNED_BYTE;
+
/* Fallback to CPU based color space conversion. */
if (force_fallback) {
*r_glsl_used = false;
- *r_format = GL_RGBA;
- *r_type = GL_UNSIGNED_BYTE;
display_buffer = NULL;
}
else if (ibuf->rect_float) {
display_buffer = ibuf->rect_float;
+ *r_data = GPU_DATA_FLOAT;
if (ibuf->channels == 4) {
- *r_format = GL_RGBA;
+ *r_format = GPU_RGBA16F;
}
else if (ibuf->channels == 3) {
- *r_format = GL_RGB;
+ /* Alpha is implicitly 1. */
+ *r_format = GPU_RGB16F;
}
else {
BLI_assert(!"Incompatible number of channels for float buffer in sequencer");
- *r_format = GL_RGBA;
+ *r_format = GPU_RGBA16F;
display_buffer = NULL;
}
- *r_type = GL_FLOAT;
-
if (ibuf->float_colorspace) {
*r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space_ctx(
C, ibuf->float_colorspace, ibuf->dither, true);
@@ -1493,15 +1499,11 @@ static void *sequencer_OCIO_transform_ibuf(
}
else if (ibuf->rect) {
display_buffer = ibuf->rect;
- *r_format = GL_RGBA;
- *r_type = GL_UNSIGNED_BYTE;
*r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space_ctx(
C, ibuf->rect_colorspace, ibuf->dither, false);
}
else {
- *r_format = GL_RGBA;
- *r_type = GL_UNSIGNED_BYTE;
display_buffer = NULL;
}
@@ -1509,8 +1511,6 @@ static void *sequencer_OCIO_transform_ibuf(
* properly, in this case we fallback to CPU-based display transform. */
if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) {
display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
- *r_format = GL_RGBA;
- *r_type = GL_UNSIGNED_BYTE;
}
if (cache_handle) {
IMB_display_buffer_release(cache_handle);
@@ -1599,11 +1599,11 @@ static void sequencer_draw_display_buffer(const bContext *C,
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
}
- /* Format needs to be created prior to any immBindProgram call.
+ /* Format needs to be created prior to any immBindShader call.
* Do it here because OCIO binds it's own shader. */
- int format, type;
+ eGPUTextureFormat format;
+ eGPUDataFormat data;
bool glsl_used = false;
- GLuint texid;
GPUVertFormat *imm_format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint texCoord = GPU_vertformat_attr_add(
@@ -1617,11 +1617,11 @@ static void sequencer_draw_display_buffer(const bContext *C,
}
display_buffer = (uchar *)ibuf->rect;
- format = GL_RGBA;
- type = GL_UNSIGNED_BYTE;
+ format = GPU_RGBA8;
+ data = GPU_DATA_UNSIGNED_BYTE;
}
else {
- display_buffer = sequencer_OCIO_transform_ibuf(C, ibuf, &glsl_used, &format, &type);
+ display_buffer = sequencer_OCIO_transform_ibuf(C, ibuf, &glsl_used, &format, &data);
}
if (draw_backdrop) {
@@ -1631,18 +1631,11 @@ static void sequencer_draw_display_buffer(const bContext *C,
GPU_matrix_identity_projection_set();
}
- glGenTextures(1, (GLuint *)&texid);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, texid);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ GPUTexture *texture = GPU_texture_create_nD(
+ ibuf->x, ibuf->y, 0, 2, display_buffer, format, data, 0, false, NULL);
+ GPU_texture_filter_mode(texture, false);
- if (type == GL_FLOAT) {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, ibuf->x, ibuf->y, 0, format, type, display_buffer);
- }
- else {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, format, type, display_buffer);
- }
+ GPU_texture_bind(texture, 0);
if (!glsl_used) {
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
@@ -1676,8 +1669,9 @@ static void sequencer_draw_display_buffer(const bContext *C,
immVertex2f(pos, preview.xmax, preview.ymin);
immEnd();
- glBindTexture(GL_TEXTURE_2D, 0);
- glDeleteTextures(1, &texid);
+
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
if (!glsl_used) {
immUnbindProgram();
@@ -1897,19 +1891,19 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
if ((seq->flag & SELECT) != sel) {
continue;
}
- else if (seq == last_seq && (last_seq->flag & SELECT)) {
+ if (seq == last_seq && (last_seq->flag & SELECT)) {
continue;
}
- else if (min_ii(seq->startdisp, seq->start) > v2d->cur.xmax) {
+ if (min_ii(seq->startdisp, seq->start) > v2d->cur.xmax) {
continue;
}
- else if (max_ii(seq->enddisp, seq->start + seq->len) < v2d->cur.xmin) {
+ if (max_ii(seq->enddisp, seq->start + seq->len) < v2d->cur.xmin) {
continue;
}
- else if (seq->machine + 1.0f < v2d->cur.ymin) {
+ if (seq->machine + 1.0f < v2d->cur.ymin) {
continue;
}
- else if (seq->machine > v2d->cur.ymax) {
+ if (seq->machine > v2d->cur.ymax) {
continue;
}
@@ -2257,7 +2251,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 +2292,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 +2335,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 +2344,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 28ded81eeae..8be7c7b4fb6 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -50,17 +50,12 @@
#include "WM_types.h"
#include "RNA_define.h"
-#include "RNA_enum_types.h"
/* For menu, popup, icons, etc. */
-
-#include "ED_anim_api.h"
#include "ED_numinput.h"
#include "ED_outliner.h"
#include "ED_screen.h"
#include "ED_sequencer.h"
-#include "ED_space_api.h"
-#include "ED_transform.h"
#include "UI_interface.h"
#include "UI_view2d.h"
@@ -229,11 +224,11 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
selected = true;
if (!(seq->flag & SEQ_USE_PROXY)) {
- BKE_reportf(reports, RPT_WARNING, "Proxy is not enabled for %s, skipping.", seq->name);
+ BKE_reportf(reports, RPT_WARNING, "Proxy is not enabled for %s, skipping", seq->name);
continue;
}
- else if (seq->strip->proxy->build_size_flags == 0) {
- BKE_reportf(reports, RPT_WARNING, "Resolution is not selected for %s, skipping.", seq->name);
+ if (seq->strip->proxy->build_size_flags == 0) {
+ BKE_reportf(reports, RPT_WARNING, "Resolution is not selected for %s, skipping", seq->name);
continue;
}
@@ -241,13 +236,13 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue);
if (!success && (seq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) != 0) {
- BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping.", seq->name);
+ BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name);
}
}
SEQ_END;
if (!selected) {
- BKE_reportf(reports, RPT_WARNING, "Select movie or image strips.");
+ BKE_reportf(reports, RPT_WARNING, "Select movie or image strips");
return;
}
@@ -397,7 +392,7 @@ static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, i
best_seq = seq;
break;
}
- else if (dist < best_dist) {
+ if (dist < best_dist) {
best_dist = dist;
best_seq = seq;
}
@@ -472,30 +467,6 @@ static bool seq_is_parent(Sequence *par, Sequence *seq)
return ((par->seq1 == seq) || (par->seq2 == seq) || (par->seq3 == seq));
}
-static bool seq_is_predecessor(Sequence *pred, Sequence *seq)
-{
- if (!pred) {
- return 0;
- }
- if (pred == seq) {
- return 0;
- }
- else if (seq_is_parent(pred, seq)) {
- return 1;
- }
- else if (pred->seq1 && seq_is_predecessor(pred->seq1, seq)) {
- return 1;
- }
- else if (pred->seq2 && seq_is_predecessor(pred->seq2, seq)) {
- return 1;
- }
- else if (pred->seq3 && seq_is_predecessor(pred->seq3, seq)) {
- return 1;
- }
-
- return 0;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -678,6 +649,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;
}
@@ -687,78 +666,6 @@ int seq_effect_find_selected(Scene *scene,
/** \name Delete Utilities
* \{ */
-static Sequence *del_seq_find_replace_recurs(Scene *scene, Sequence *seq)
-{
- Sequence *seq1, *seq2, *seq3;
-
- /* Try to find a replacement input sequence, and flag for later deletion if
- * no replacement can be found. */
-
- if (!seq) {
- return NULL;
- }
- else if (!(seq->type & SEQ_TYPE_EFFECT)) {
- return ((seq->flag & SELECT) ? NULL : seq);
- }
- else if (!(seq->flag & SELECT)) {
- /* Try to find replacement for effect inputs. */
- seq1 = del_seq_find_replace_recurs(scene, seq->seq1);
- seq2 = del_seq_find_replace_recurs(scene, seq->seq2);
- seq3 = del_seq_find_replace_recurs(scene, seq->seq3);
-
- if (seq1 == seq->seq1 && seq2 == seq->seq2 && seq3 == seq->seq3) {
- /* Pass. */
- }
- else if (seq1 || seq2 || seq3) {
- seq->seq1 = (seq1) ? seq1 : (seq2) ? seq2 : seq3;
- seq->seq2 = (seq2) ? seq2 : (seq1) ? seq1 : seq3;
- seq->seq3 = (seq3) ? seq3 : (seq1) ? seq1 : seq2;
-
- BKE_sequencer_update_changed_seq_and_deps(scene, seq, 1, 1);
- }
- else {
- seq->flag |= SELECT; /* Mark for delete. */
- }
- }
-
- if (seq->flag & SELECT) {
- if ((seq1 = del_seq_find_replace_recurs(scene, seq->seq1))) {
- return seq1;
- }
- if ((seq2 = del_seq_find_replace_recurs(scene, seq->seq2))) {
- return seq2;
- }
- if ((seq3 = del_seq_find_replace_recurs(scene, seq->seq3))) {
- return seq3;
- }
- else {
- return NULL;
- }
- }
- else {
- return seq;
- }
-}
-
-static void del_seq_clear_modifiers_recurs(Scene *scene, Sequence *deleting_sequence)
-{
- Editing *ed = BKE_sequencer_editing_get(scene, false);
- Sequence *current_sequence;
-
- SEQP_BEGIN (ed, current_sequence) {
- if (!(current_sequence->flag & SELECT) && current_sequence != deleting_sequence) {
- SequenceModifierData *smd;
-
- for (smd = current_sequence->modifiers.first; smd; smd = smd->next) {
- if (smd->mask_sequence == deleting_sequence) {
- smd->mask_sequence = NULL;
- }
- }
- }
- }
- SEQ_END;
-}
-
static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short deleteall)
{
Sequence *seq, *seqn;
@@ -1408,14 +1315,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);
}
}
@@ -1734,9 +1648,7 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static void sequencer_slip_update_header(Scene *scene, ScrArea *area, SlipData *data, int offset)
@@ -2223,15 +2135,22 @@ 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)) {
+ scene, last_seq, last_seq->type, &seq1, &seq2, &seq3, &error_msg) ||
+ BKE_sequence_effect_get_num_inputs(last_seq->type) == 0) {
BKE_report(op->reports, RPT_ERROR, error_msg);
return OPERATOR_CANCELLED;
}
/* Check if reassigning would create recursivity. */
- if (seq_is_predecessor(seq1, last_seq) || seq_is_predecessor(seq2, last_seq) ||
- seq_is_predecessor(seq3, last_seq)) {
- BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: no cycles allowed");
+ if (BKE_sequencer_render_loop_check(seq1, last_seq) ||
+ BKE_sequencer_render_loop_check(seq2, last_seq) ||
+ BKE_sequencer_render_loop_check(seq3, last_seq)) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: recursion detected.");
return OPERATOR_CANCELLED;
}
@@ -2323,6 +2242,11 @@ void SEQUENCER_OT_swap_inputs(struct wmOperatorType *ot)
/** \name Split Strips Operator
* \{ */
+enum {
+ SEQ_SPLIT_SOFT,
+ SEQ_SPLIT_HARD,
+};
+
static const EnumPropertyItem prop_split_types[] = {
{SEQ_SPLIT_SOFT, "SOFT", 0, "Soft", ""},
{SEQ_SPLIT_HARD, "HARD", 0, "Hard", ""},
@@ -2345,6 +2269,8 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
split_side = RNA_enum_get(op->ptr, "side");
ignore_selection = RNA_boolean_get(op->ptr, "ignore_selection");
+ BKE_sequencer_prefetch_stop(scene);
+
if (split_hard == SEQ_SPLIT_HARD) {
changed = split_seq_list(bmain,
scene,
@@ -2413,10 +2339,9 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
- else {
- /* Passthrough to selection if used as tool. */
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
- }
+
+ /* Passthrough to selection if used as tool. */
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -2591,59 +2516,18 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
Scene *scene = CTX_data_scene(C);
Editing *ed = BKE_sequencer_editing_get(scene, false);
Sequence *seq;
- MetaStack *ms;
- bool nothing_selected = true;
-
- seq = BKE_sequencer_active_get(scene);
- if (seq && seq->flag & SELECT) { /* Avoid a loop since this is likely to be selected. */
- nothing_selected = false;
- }
- else {
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if (seq->flag & SELECT) {
- nothing_selected = false;
- break;
- }
- }
- }
- if (nothing_selected) {
- return OPERATOR_FINISHED;
- }
-
- /* For effects and modifiers, try to find a replacement input. */
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if (!(seq->flag & SELECT)) {
- if ((seq->type & SEQ_TYPE_EFFECT)) {
- del_seq_find_replace_recurs(scene, seq);
- }
- }
- else {
- del_seq_clear_modifiers_recurs(scene, seq);
+ SEQP_BEGIN (scene->ed, seq) {
+ if (seq->flag & SELECT) {
+ BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq);
}
}
-
- /* Delete all selected strips. */
- recurs_del_seq_flag(scene, ed->seqbasep, SELECT, 0);
-
- /* Update lengths, etc. */
- seq = ed->seqbasep->first;
- while (seq) {
- BKE_sequence_calc(scene, seq);
- seq = seq->next;
- }
-
- /* Free parent metas. */
- ms = ed->metastack.last;
- while (ms) {
- BKE_sequence_calc(scene, ms->parseq);
- ms = ms->prev;
- }
+ SEQ_END;
+ BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
-
return OPERATOR_FINISHED;
}
@@ -3010,18 +2894,16 @@ static int seq_depends_on_meta(Sequence *seq, Sequence *seqm)
if (seq == seqm) {
return 1;
}
- else if (seq->seq1 && seq_depends_on_meta(seq->seq1, seqm)) {
+ if (seq->seq1 && seq_depends_on_meta(seq->seq1, seqm)) {
return 1;
}
- else if (seq->seq2 && seq_depends_on_meta(seq->seq2, seqm)) {
+ if (seq->seq2 && seq_depends_on_meta(seq->seq2, seqm)) {
return 1;
}
- else if (seq->seq3 && seq_depends_on_meta(seq->seq3, seqm)) {
+ if (seq->seq3 && seq_depends_on_meta(seq->seq3, seqm)) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
@@ -3092,321 +2974,6 @@ void SEQUENCER_OT_meta_separate(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Sequencer Frame All Operator
- * \{ */
-
-static int sequencer_view_all_exec(bContext *C, wmOperator *op)
-{
- ARegion *region = CTX_wm_region(C);
- rctf box;
-
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- boundbox_seq(CTX_data_scene(C), &box);
- UI_view2d_smooth_view(C, region, &box, smooth_viewtx);
- return OPERATOR_FINISHED;
-}
-
-void SEQUENCER_OT_view_all(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Frame All";
- ot->idname = "SEQUENCER_OT_view_all";
- ot->description = "View all the strips in the sequencer";
-
- /* Api callbacks. */
- ot->exec = sequencer_view_all_exec;
- ot->poll = ED_operator_sequencer_active;
-
- /* Flags. */
- ot->flag = OPTYPE_REGISTER;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Go to Current Frame Operator
- * \{ */
-
-static int sequencer_view_frame_exec(bContext *C, wmOperator *op)
-{
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
- ANIM_center_frame(C, smooth_viewtx);
-
- return OPERATOR_FINISHED;
-}
-
-void SEQUENCER_OT_view_frame(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Go to Current Frame";
- ot->idname = "SEQUENCER_OT_view_frame";
- ot->description = "Move the view to the current frame";
-
- /* Api callbacks. */
- ot->exec = sequencer_view_frame_exec;
- ot->poll = ED_operator_sequencer_active;
-
- /* Flags. */
- ot->flag = 0;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Preview Frame All Operator
- * \{ */
-
-static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
-{
- bScreen *screen = CTX_wm_screen(C);
- ScrArea *area = CTX_wm_area(C);
-#if 0
- ARegion *region = CTX_wm_region(C);
- SpaceSeq *sseq = area->spacedata.first;
- Scene *scene = CTX_data_scene(C);
-#endif
- View2D *v2d = UI_view2d_fromcontext(C);
-
- v2d->cur = v2d->tot;
- UI_view2d_curRect_validate(v2d);
- UI_view2d_sync(screen, area, v2d, V2D_LOCK_COPY);
-
-#if 0
- /* Like zooming on an image view. */
- float zoomX, zoomY;
- int width, height, imgwidth, imgheight;
-
- width = region->winx;
- height = region->winy;
-
- seq_reset_imageofs(sseq);
-
- imgwidth = (scene->r.size * scene->r.xsch) / 100;
- imgheight = (scene->r.size * scene->r.ysch) / 100;
-
- /* Apply aspect, doesn't need to be that accurate. */
- imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp));
-
- if (((imgwidth >= width) || (imgheight >= height)) && ((width > 0) && (height > 0))) {
- /* Find the zoom value that will fit the image in the image space. */
- zoomX = ((float)width) / ((float)imgwidth);
- zoomY = ((float)height) / ((float)imgheight);
- sseq->zoom = (zoomX < zoomY) ? zoomX : zoomY;
-
- sseq->zoom = 1.0f / power_of_2(1 / min_ff(zoomX, zoomY));
- }
- else {
- sseq->zoom = 1.0f;
- }
-#endif
-
- ED_area_tag_redraw(CTX_wm_area(C));
- return OPERATOR_FINISHED;
-}
-
-void SEQUENCER_OT_view_all_preview(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Frame All";
- ot->idname = "SEQUENCER_OT_view_all_preview";
- ot->description = "Zoom preview to fit in the area";
-
- /* Api callbacks. */
- ot->exec = sequencer_view_all_preview_exec;
- ot->poll = ED_operator_sequencer_active;
-
- /* Flags. */
- ot->flag = OPTYPE_REGISTER;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sequencer View Zoom Ratio Operator
- * \{ */
-
-static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op)
-{
- RenderData *rd = &CTX_data_scene(C)->r;
- View2D *v2d = UI_view2d_fromcontext(C);
-
- float ratio = RNA_float_get(op->ptr, "ratio");
-
- float winx = (int)(rd->size * rd->xsch) / 100;
- float winy = (int)(rd->size * rd->ysch) / 100;
-
- float facx = BLI_rcti_size_x(&v2d->mask) / winx;
- float facy = BLI_rcti_size_y(&v2d->mask) / winy;
-
- BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f));
-
- ED_region_tag_redraw(CTX_wm_region(C));
-
- return OPERATOR_FINISHED;
-}
-
-void SEQUENCER_OT_view_zoom_ratio(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Sequencer View Zoom Ratio";
- ot->idname = "SEQUENCER_OT_view_zoom_ratio";
- ot->description = "Change zoom ratio of sequencer preview";
-
- /* Api callbacks. */
- ot->exec = sequencer_view_zoom_ratio_exec;
- ot->poll = ED_operator_sequencer_active;
-
- /* Properties. */
- RNA_def_float(ot->srna,
- "ratio",
- 1.0f,
- -FLT_MAX,
- FLT_MAX,
- "Ratio",
- "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out",
- -FLT_MAX,
- FLT_MAX);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name View Toggle Operator
- * \{ */
-
-#if 0
-static const EnumPropertyItem view_type_items[] = {
- {SEQ_VIEW_SEQUENCE, "SEQUENCER", ICON_SEQ_SEQUENCER, "Sequencer", ""},
- {SEQ_VIEW_PREVIEW, "PREVIEW", ICON_SEQ_PREVIEW, "Image Preview", ""},
- {SEQ_VIEW_SEQUENCE_PREVIEW,
- "SEQUENCER_PREVIEW",
- ICON_SEQ_SEQUENCER,
- "Sequencer and Image Preview",
- ""},
- {0, NULL, 0, NULL, NULL},
-};
-#endif
-
-static int sequencer_view_toggle_exec(bContext *C, wmOperator *UNUSED(op))
-{
- SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
-
- sseq->view++;
- if (sseq->view > SEQ_VIEW_SEQUENCE_PREVIEW) {
- sseq->view = SEQ_VIEW_SEQUENCE;
- }
-
- ED_area_tag_refresh(CTX_wm_area(C));
-
- return OPERATOR_FINISHED;
-}
-
-void SEQUENCER_OT_view_toggle(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "View Toggle";
- ot->idname = "SEQUENCER_OT_view_toggle";
- ot->description = "Toggle between sequencer views (sequence, preview, both)";
-
- /* Api callbacks. */
- ot->exec = sequencer_view_toggle_exec;
- ot->poll = ED_operator_sequencer_active;
-
- /* Flags. */
- ot->flag = OPTYPE_REGISTER;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Frame Selected Operator
- * \{ */
-
-static int sequencer_view_selected_exec(bContext *C, wmOperator *op)
-{
- Scene *scene = CTX_data_scene(C);
- View2D *v2d = UI_view2d_fromcontext(C);
- ARegion *region = CTX_wm_region(C);
- Editing *ed = BKE_sequencer_editing_get(scene, false);
- Sequence *last_seq = BKE_sequencer_active_get(scene);
- Sequence *seq;
- rctf cur_new = v2d->cur;
-
- int xmin = MAXFRAME * 2;
- int xmax = -MAXFRAME * 2;
- int ymin = MAXSEQ + 1;
- int ymax = 0;
- int orig_height;
- int ymid;
- int ymargin = 1;
- int xmargin = FPS;
-
- if (ed == NULL) {
- return OPERATOR_CANCELLED;
- }
-
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if ((seq->flag & SELECT) || (seq == last_seq)) {
- xmin = min_ii(xmin, seq->startdisp);
- xmax = max_ii(xmax, seq->enddisp);
-
- ymin = min_ii(ymin, seq->machine);
- ymax = max_ii(ymax, seq->machine);
- }
- }
-
- if (ymax != 0) {
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-
- xmax += xmargin;
- xmin -= xmargin;
- ymax += ymargin;
- ymin -= ymargin;
-
- orig_height = BLI_rctf_size_y(&cur_new);
-
- cur_new.xmin = xmin;
- cur_new.xmax = xmax;
-
- cur_new.ymin = ymin;
- cur_new.ymax = ymax;
-
- /* Only zoom out vertically. */
- if (orig_height > BLI_rctf_size_y(&cur_new)) {
- ymid = BLI_rctf_cent_y(&cur_new);
-
- cur_new.ymin = ymid - (orig_height / 2);
- cur_new.ymax = ymid + (orig_height / 2);
- }
-
- UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx);
-
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-void SEQUENCER_OT_view_selected(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Frame Selected";
- ot->idname = "SEQUENCER_OT_view_selected";
- ot->description = "Zoom the sequencer on the selected strips";
-
- /* Api callbacks. */
- ot->exec = sequencer_view_selected_exec;
- ot->poll = ED_operator_sequencer_active;
-
- /* Flags. */
- ot->flag = OPTYPE_REGISTER;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Jump to Strip Operator
* \{ */
@@ -3748,6 +3315,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;
@@ -3766,6 +3334,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);
@@ -3857,67 +3426,6 @@ void SEQUENCER_OT_swap_data(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Border Offset View Operator
- * \{ */
-
-static int view_ghost_border_exec(bContext *C, wmOperator *op)
-{
- Scene *scene = CTX_data_scene(C);
- View2D *v2d = UI_view2d_fromcontext(C);
-
- rctf rect;
-
- /* Convert coordinates of rect to 'tot' rect coordinates. */
- WM_operator_properties_border_to_rctf(op, &rect);
- UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
-
- rect.xmin /= fabsf(BLI_rctf_size_x(&v2d->tot));
- rect.ymin /= fabsf(BLI_rctf_size_y(&v2d->tot));
-
- rect.xmax /= fabsf(BLI_rctf_size_x(&v2d->tot));
- rect.ymax /= fabsf(BLI_rctf_size_y(&v2d->tot));
-
- rect.xmin += 0.5f;
- rect.xmax += 0.5f;
- rect.ymin += 0.5f;
- rect.ymax += 0.5f;
-
- CLAMP(rect.xmin, 0.0f, 1.0f);
- CLAMP(rect.ymin, 0.0f, 1.0f);
- CLAMP(rect.xmax, 0.0f, 1.0f);
- CLAMP(rect.ymax, 0.0f, 1.0f);
-
- scene->ed->over_border = rect;
-
- WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
-
- return OPERATOR_FINISHED;
-}
-
-void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Border Offset View";
- ot->idname = "SEQUENCER_OT_view_ghost_border";
- ot->description = "Set the boundaries of the border used for offset-view";
-
- /* Api callbacks. */
- ot->invoke = WM_gesture_box_invoke;
- ot->exec = view_ghost_border_exec;
- ot->modal = WM_gesture_box_modal;
- ot->poll = sequencer_view_preview_poll;
- ot->cancel = WM_gesture_box_cancel;
-
- /* Flags. */
- ot->flag = 0;
-
- /* Properties. */
- WM_operator_properties_gesture_box(ot);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Rebuild Proxy and Timecode Indices Operator
* \{ */
@@ -4126,9 +3634,8 @@ static int sequencer_change_effect_input_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "One of the effect inputs is unset, cannot swap");
return OPERATOR_CANCELLED;
}
- else {
- SWAP(Sequence *, *seq_1, *seq_2);
- }
+
+ SWAP(Sequence *, *seq_1, *seq_2);
BKE_sequencer_update_changed_seq_and_deps(scene, seq, 0, 1);
@@ -4183,15 +3690,14 @@ static int sequencer_change_effect_type_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "New effect needs more input strips");
return OPERATOR_CANCELLED;
}
- else {
- sh = BKE_sequence_get_effect(seq);
- sh.free(seq, true);
- seq->type = new_type;
+ sh = BKE_sequence_get_effect(seq);
+ sh.free(seq, true);
- sh = BKE_sequence_get_effect(seq);
- sh.init(seq);
- }
+ seq->type = new_type;
+
+ sh = BKE_sequence_get_effect(seq);
+ sh.init(seq);
BKE_sequencer_update_changed_seq_and_deps(scene, seq, 0, 1);
/* Invalidate cache. */
@@ -4301,7 +3807,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 {
@@ -4545,7 +4051,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "Select one or more strips");
return OPERATOR_CANCELLED;
}
- else if (efra < 0) {
+ if (efra < 0) {
BKE_report(op->reports, RPT_ERROR, "Can't set a negative range");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index eec8d604b64..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,
@@ -131,13 +132,6 @@ void SEQUENCER_OT_swap(struct wmOperatorType *ot);
void SEQUENCER_OT_swap_data(struct wmOperatorType *ot);
void SEQUENCER_OT_rendersize(struct wmOperatorType *ot);
-void SEQUENCER_OT_view_toggle(struct wmOperatorType *ot);
-void SEQUENCER_OT_view_all(struct wmOperatorType *ot);
-void SEQUENCER_OT_view_selected(struct wmOperatorType *ot);
-void SEQUENCER_OT_view_frame(struct wmOperatorType *ot);
-void SEQUENCER_OT_view_zoom_ratio(struct wmOperatorType *ot);
-void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot);
-
void SEQUENCER_OT_change_effect_input(struct wmOperatorType *ot);
void SEQUENCER_OT_change_effect_type(struct wmOperatorType *ot);
void SEQUENCER_OT_change_path(struct wmOperatorType *ot);
@@ -152,9 +146,6 @@ void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot);
void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot);
-/* Preview specific operators. */
-void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot);
-
/* sequencer_select.c */
void SEQUENCER_OT_select_all(struct wmOperatorType *ot);
void SEQUENCER_OT_select(struct wmOperatorType *ot);
@@ -169,7 +160,7 @@ void SEQUENCER_OT_select_box(struct wmOperatorType *ot);
void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot);
void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot);
-/* sequencer_select.c */
+/* sequencer_add.c */
void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot);
@@ -178,11 +169,6 @@ void SEQUENCER_OT_sound_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot);
-enum {
- SEQ_SPLIT_SOFT,
- SEQ_SPLIT_HARD,
-};
-
/* sequencer_ops.c */
void sequencer_operatortypes(void);
void sequencer_keymap(struct wmKeyConfig *keyconf);
@@ -205,6 +191,12 @@ void SEQUENCER_OT_strip_modifier_copy(struct wmOperatorType *ot);
/* sequencer_view.c */
void SEQUENCER_OT_sample(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_all(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_frame(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_zoom_ratio(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_selected(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot);
/* sequencer_preview.c */
void sequencer_preview_add_sound(const struct bContext *C, struct Sequence *seq);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index 4fe1c953c74..0a7aa1a6072 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -74,14 +74,6 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_copy);
WM_operatortype_append(SEQUENCER_OT_paste);
- WM_operatortype_append(SEQUENCER_OT_view_all);
- WM_operatortype_append(SEQUENCER_OT_view_selected);
- WM_operatortype_append(SEQUENCER_OT_view_frame);
- WM_operatortype_append(SEQUENCER_OT_view_all_preview);
- WM_operatortype_append(SEQUENCER_OT_view_toggle);
- WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio);
- WM_operatortype_append(SEQUENCER_OT_view_ghost_border);
-
WM_operatortype_append(SEQUENCER_OT_rebuild_proxy);
WM_operatortype_append(SEQUENCER_OT_enable_proxies);
WM_operatortype_append(SEQUENCER_OT_change_effect_input);
@@ -120,6 +112,12 @@ void sequencer_operatortypes(void)
/* sequencer_view.h */
WM_operatortype_append(SEQUENCER_OT_sample);
+ WM_operatortype_append(SEQUENCER_OT_view_all);
+ WM_operatortype_append(SEQUENCER_OT_view_frame);
+ WM_operatortype_append(SEQUENCER_OT_view_all_preview);
+ WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio);
+ WM_operatortype_append(SEQUENCER_OT_view_selected);
+ WM_operatortype_append(SEQUENCER_OT_view_ghost_border);
}
void sequencer_keymap(wmKeyConfig *keyconf)
diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c
index 243a6e193eb..80b1c7a5194 100644
--- a/source/blender/editors/space_sequencer/sequencer_scopes.c
+++ b/source/blender/editors/space_sequencer/sequencer_scopes.c
@@ -50,14 +50,14 @@ static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3])
yuv[2] += 0.5f;
}
-static void scope_put_pixel(uchar *table, uchar *pos)
+static void scope_put_pixel(const uchar *table, uchar *pos)
{
uchar newval = table[*pos];
pos[0] = pos[1] = pos[2] = newval;
pos[3] = 255;
}
-static void scope_put_pixel_single(uchar *table, uchar *pos, int col)
+static void scope_put_pixel_single(const uchar *table, uchar *pos, int col)
{
char newval = table[pos[col]];
pos[col] = newval;
@@ -232,9 +232,7 @@ ImBuf *make_waveform_view_from_ibuf(ImBuf *ibuf)
if (ibuf->rect_float) {
return make_waveform_view_from_ibuf_float(ibuf);
}
- else {
- return make_waveform_view_from_ibuf_byte(ibuf);
- }
+ return make_waveform_view_from_ibuf_byte(ibuf);
}
static ImBuf *make_sep_waveform_view_from_ibuf_byte(ImBuf *ibuf)
@@ -336,9 +334,7 @@ ImBuf *make_sep_waveform_view_from_ibuf(ImBuf *ibuf)
if (ibuf->rect_float) {
return make_sep_waveform_view_from_ibuf_float(ibuf);
}
- else {
- return make_sep_waveform_view_from_ibuf_byte(ibuf);
- }
+ return make_sep_waveform_view_from_ibuf_byte(ibuf);
}
static void draw_zebra_byte(ImBuf *src, ImBuf *ibuf, float perc)
@@ -541,7 +537,7 @@ BLI_INLINE int get_bin_float(float f)
if (f < -0.25f) {
return 0;
}
- else if (f >= 1.25f) {
+ if (f >= 1.25f) {
return 511;
}
@@ -627,9 +623,7 @@ ImBuf *make_histogram_view_from_ibuf(ImBuf *ibuf)
if (ibuf->rect_float) {
return make_histogram_view_from_ibuf_float(ibuf);
}
- else {
- return make_histogram_view_from_ibuf_byte(ibuf);
- }
+ return make_histogram_view_from_ibuf_byte(ibuf);
}
static void vectorscope_put_cross(uchar r, uchar g, uchar b, char *tgt, int w, int h, int size)
@@ -757,7 +751,5 @@ ImBuf *make_vectorscope_view_from_ibuf(ImBuf *ibuf)
if (ibuf->rect_float) {
return make_vectorscope_view_from_ibuf_float(ibuf);
}
- else {
- return make_vectorscope_view_from_ibuf_byte(ibuf);
- }
+ return make_vectorscope_view_from_ibuf_byte(ibuf);
}
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/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index d397c255b03..c1dac30bcb6 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -21,16 +21,36 @@
* \ingroup spseq
*/
-#include "ED_util_imbuf.h"
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
-#include "RNA_define.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_context.h"
+#include "BKE_sequencer.h"
+#include "WM_api.h"
#include "WM_types.h"
+#include "RNA_define.h"
+
+#include "UI_view2d.h"
+
+#include "RNA_define.h"
+
+/* For menu, popup, icons, etc. */
+#include "ED_anim_api.h"
+#include "ED_screen.h"
+#include "ED_util_imbuf.h"
+
/* Own include. */
#include "sequencer_intern.h"
-/******************** sample backdrop operator ********************/
+/* -------------------------------------------------------------------- */
+/** \name Sequencer Sample Backdrop Operator
+ * \{ */
+
void SEQUENCER_OT_sample(wmOperatorType *ot)
{
/* Identifiers. */
@@ -53,3 +73,331 @@ void SEQUENCER_OT_sample(wmOperatorType *ot)
RNA_def_property_subtype(prop, PROP_PIXEL);
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sequencer Frame All Operator
+ * \{ */
+
+static int sequencer_view_all_exec(bContext *C, wmOperator *op)
+{
+ ARegion *region = CTX_wm_region(C);
+ rctf box;
+
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ boundbox_seq(CTX_data_scene(C), &box);
+ UI_view2d_smooth_view(C, region, &box, smooth_viewtx);
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_view_all(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Frame All";
+ ot->idname = "SEQUENCER_OT_view_all";
+ ot->description = "View all the strips in the sequencer";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_view_all_exec;
+ ot->poll = ED_operator_sequencer_active;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Go to Current Frame Operator
+ * \{ */
+
+static int sequencer_view_frame_exec(bContext *C, wmOperator *op)
+{
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ ANIM_center_frame(C, smooth_viewtx);
+
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_view_frame(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Go to Current Frame";
+ ot->idname = "SEQUENCER_OT_view_frame";
+ ot->description = "Move the view to the current frame";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_view_frame_exec;
+ ot->poll = ED_operator_sequencer_active;
+
+ /* Flags. */
+ ot->flag = 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Preview Frame All Operator
+ * \{ */
+
+static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bScreen *screen = CTX_wm_screen(C);
+ ScrArea *area = CTX_wm_area(C);
+#if 0
+ ARegion *region = CTX_wm_region(C);
+ SpaceSeq *sseq = area->spacedata.first;
+ Scene *scene = CTX_data_scene(C);
+#endif
+ View2D *v2d = UI_view2d_fromcontext(C);
+
+ v2d->cur = v2d->tot;
+ UI_view2d_curRect_validate(v2d);
+ UI_view2d_sync(screen, area, v2d, V2D_LOCK_COPY);
+
+#if 0
+ /* Like zooming on an image view. */
+ float zoomX, zoomY;
+ int width, height, imgwidth, imgheight;
+
+ width = region->winx;
+ height = region->winy;
+
+ seq_reset_imageofs(sseq);
+
+ imgwidth = (scene->r.size * scene->r.xsch) / 100;
+ imgheight = (scene->r.size * scene->r.ysch) / 100;
+
+ /* Apply aspect, doesn't need to be that accurate. */
+ imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp));
+
+ if (((imgwidth >= width) || (imgheight >= height)) && ((width > 0) && (height > 0))) {
+ /* Find the zoom value that will fit the image in the image space. */
+ zoomX = ((float)width) / ((float)imgwidth);
+ zoomY = ((float)height) / ((float)imgheight);
+ sseq->zoom = (zoomX < zoomY) ? zoomX : zoomY;
+
+ sseq->zoom = 1.0f / power_of_2(1 / min_ff(zoomX, zoomY));
+ }
+ else {
+ sseq->zoom = 1.0f;
+ }
+#endif
+
+ ED_area_tag_redraw(CTX_wm_area(C));
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_view_all_preview(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Frame All";
+ ot->idname = "SEQUENCER_OT_view_all_preview";
+ ot->description = "Zoom preview to fit in the area";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_view_all_preview_exec;
+ ot->poll = ED_operator_sequencer_active;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sequencer View Zoom Ratio Operator
+ * \{ */
+
+static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op)
+{
+ RenderData *rd = &CTX_data_scene(C)->r;
+ View2D *v2d = UI_view2d_fromcontext(C);
+
+ float ratio = RNA_float_get(op->ptr, "ratio");
+
+ float winx = (int)(rd->size * rd->xsch) / 100;
+ float winy = (int)(rd->size * rd->ysch) / 100;
+
+ float facx = BLI_rcti_size_x(&v2d->mask) / winx;
+ float facy = BLI_rcti_size_y(&v2d->mask) / winy;
+
+ BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f));
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_view_zoom_ratio(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Sequencer View Zoom Ratio";
+ ot->idname = "SEQUENCER_OT_view_zoom_ratio";
+ ot->description = "Change zoom ratio of sequencer preview";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_view_zoom_ratio_exec;
+ ot->poll = ED_operator_sequencer_active;
+
+ /* Properties. */
+ RNA_def_float(ot->srna,
+ "ratio",
+ 1.0f,
+ -FLT_MAX,
+ FLT_MAX,
+ "Ratio",
+ "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out",
+ -FLT_MAX,
+ FLT_MAX);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Frame Selected Operator
+ * \{ */
+
+static int sequencer_view_selected_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ View2D *v2d = UI_view2d_fromcontext(C);
+ ARegion *region = CTX_wm_region(C);
+ Editing *ed = BKE_sequencer_editing_get(scene, false);
+ Sequence *last_seq = BKE_sequencer_active_get(scene);
+ Sequence *seq;
+ rctf cur_new = v2d->cur;
+
+ int xmin = MAXFRAME * 2;
+ int xmax = -MAXFRAME * 2;
+ int ymin = MAXSEQ + 1;
+ int ymax = 0;
+ int orig_height;
+ int ymid;
+ int ymargin = 1;
+ int xmargin = FPS;
+
+ if (ed == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if ((seq->flag & SELECT) || (seq == last_seq)) {
+ xmin = min_ii(xmin, seq->startdisp);
+ xmax = max_ii(xmax, seq->enddisp);
+
+ ymin = min_ii(ymin, seq->machine);
+ ymax = max_ii(ymax, seq->machine);
+ }
+ }
+
+ if (ymax != 0) {
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+
+ xmax += xmargin;
+ xmin -= xmargin;
+ ymax += ymargin;
+ ymin -= ymargin;
+
+ orig_height = BLI_rctf_size_y(&cur_new);
+
+ cur_new.xmin = xmin;
+ cur_new.xmax = xmax;
+
+ cur_new.ymin = ymin;
+ cur_new.ymax = ymax;
+
+ /* Only zoom out vertically. */
+ if (orig_height > BLI_rctf_size_y(&cur_new)) {
+ ymid = BLI_rctf_cent_y(&cur_new);
+
+ cur_new.ymin = ymid - (orig_height / 2);
+ cur_new.ymax = ymid + (orig_height / 2);
+ }
+
+ UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx);
+
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+void SEQUENCER_OT_view_selected(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Frame Selected";
+ ot->idname = "SEQUENCER_OT_view_selected";
+ ot->description = "Zoom the sequencer on the selected strips";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_view_selected_exec;
+ ot->poll = ED_operator_sequencer_active;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Border Offset View Operator
+ * \{ */
+
+static int view_ghost_border_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ View2D *v2d = UI_view2d_fromcontext(C);
+
+ rctf rect;
+
+ /* Convert coordinates of rect to 'tot' rect coordinates. */
+ WM_operator_properties_border_to_rctf(op, &rect);
+ UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
+
+ rect.xmin /= fabsf(BLI_rctf_size_x(&v2d->tot));
+ rect.ymin /= fabsf(BLI_rctf_size_y(&v2d->tot));
+
+ rect.xmax /= fabsf(BLI_rctf_size_x(&v2d->tot));
+ rect.ymax /= fabsf(BLI_rctf_size_y(&v2d->tot));
+
+ rect.xmin += 0.5f;
+ rect.xmax += 0.5f;
+ rect.ymin += 0.5f;
+ rect.ymax += 0.5f;
+
+ CLAMP(rect.xmin, 0.0f, 1.0f);
+ CLAMP(rect.ymin, 0.0f, 1.0f);
+ CLAMP(rect.xmax, 0.0f, 1.0f);
+ CLAMP(rect.ymax, 0.0f, 1.0f);
+
+ scene->ed->over_border = rect;
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Border Offset View";
+ ot->idname = "SEQUENCER_OT_view_ghost_border";
+ ot->description = "Set the boundaries of the border used for offset-view";
+
+ /* Api callbacks. */
+ ot->invoke = WM_gesture_box_invoke;
+ ot->exec = view_ghost_border_exec;
+ ot->modal = WM_gesture_box_modal;
+ ot->poll = sequencer_view_preview_poll;
+ ot->cancel = WM_gesture_box_cancel;
+
+ /* Flags. */
+ ot->flag = 0;
+
+ /* Properties. */
+ WM_operator_properties_gesture_box(ot);
+}
+
+/** \} */
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index f52cfdd34c0..926752c6488 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -464,7 +464,7 @@ static int sequencer_context(const bContext *C, const char *member, bContextData
return true;
}
- else if (CTX_data_equals(member, "edit_mask")) {
+ if (CTX_data_equals(member, "edit_mask")) {
Mask *mask = BKE_sequencer_mask_get(scene);
if (mask) {
CTX_data_id_pointer_set(result, &mask->id);
@@ -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/space_text.c b/source/blender/editors/space_text/space_text.c
index 76d61193ce0..a2af99ee9f9 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -256,7 +256,7 @@ static int text_context(const bContext *C, const char *member, bContextDataResul
CTX_data_dir_set(result, text_context_dir);
return 1;
}
- else if (CTX_data_equals(member, "edit_text")) {
+ if (CTX_data_equals(member, "edit_text")) {
if (st->text != NULL) {
CTX_data_id_pointer_set(result, &st->text->id);
}
diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c
index 1f034bdbd09..24c55e60513 100644
--- a/source/blender/editors/space_text/text_autocomplete.c
+++ b/source/blender/editors/space_text/text_autocomplete.c
@@ -319,15 +319,12 @@ static int text_autocomplete_invoke(bContext *C, wmOperator *op, const wmEvent *
ED_undo_push(C, op->type->name);
return OPERATOR_FINISHED;
}
- else {
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
- }
- else {
- text_autocomplete_free(C, op);
- return OPERATOR_CANCELLED;
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
}
+ text_autocomplete_free(C, op);
+ return OPERATOR_CANCELLED;
}
static int doc_scroll = 0;
@@ -590,10 +587,8 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e
}
return retval;
}
- else {
- text_autocomplete_free(C, op);
- return OPERATOR_FINISHED;
- }
+ text_autocomplete_free(C, op);
+ return OPERATOR_FINISHED;
}
static void text_autocomplete_free(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index a0339b35c57..e9ac6946032 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -234,11 +234,10 @@ void wrap_offset(
if (i - lines < 0) {
break;
}
- else {
- linep = linep->next;
- (*offl) += lines - 1;
- i -= lines;
- }
+
+ linep = linep->next;
+ (*offl) += lines - 1;
+ i -= lines;
}
max = wrap_width(st, region);
@@ -849,9 +848,7 @@ int text_get_span_wrap(const SpaceText *st, ARegion *region, TextLine *from, Tex
return ret;
}
- else {
- return txt_get_span(from, to);
- }
+ return txt_get_span(from, to);
}
int text_get_total_lines(SpaceText *st, ARegion *region)
@@ -1612,11 +1609,10 @@ void draw_text_main(SpaceText *st, ARegion *region)
wrap_skip = st->top - wraplinecount;
break;
}
- else {
- wraplinecount += lines;
- tmp = tmp->next;
- linecount++;
- }
+
+ wraplinecount += lines;
+ tmp = tmp->next;
+ linecount++;
}
else {
tmp = tmp->next;
diff --git a/source/blender/editors/space_text/text_format.c b/source/blender/editors/space_text/text_format.c
index bdbf55d9198..d099f2a20d8 100644
--- a/source/blender/editors/space_text/text_format.c
+++ b/source/blender/editors/space_text/text_format.c
@@ -222,10 +222,9 @@ TextFormatType *ED_text_format_get(Text *text)
* the "default" text format */
return tft_lb.first;
}
- else {
- /* Return the "default" text format */
- return tft_lb.first;
- }
+
+ /* Return the "default" text format */
+ return tft_lb.first;
}
bool ED_text_is_syntax_highlight_supported(Text *text)
diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c
index 42a52ce0f58..4f6d91451e0 100644
--- a/source/blender/editors/space_text/text_format_lua.c
+++ b/source/blender/editors/space_text/text_format_lua.c
@@ -226,7 +226,7 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_
continue;
}
/* Handle continuations */
- else if (cont) {
+ if (cont) {
/* Multi-line comments */
if (cont & FMT_CONT_COMMENT_C) {
if (*str == ']' && *(str + 1) == ']') {
diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c
index 53e38cfe870..b205996d1d2 100644
--- a/source/blender/editors/space_text/text_format_osl.c
+++ b/source/blender/editors/space_text/text_format_osl.c
@@ -252,7 +252,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_
continue;
}
/* Handle continuations */
- else if (cont) {
+ if (cont) {
/* C-Style comments */
if (cont & FMT_CONT_COMMENT_C) {
if (*str == '*' && *(str + 1) == '/') {
diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c
index 52aaad034bf..96d9c234a40 100644
--- a/source/blender/editors/space_text/text_format_pov.c
+++ b/source/blender/editors/space_text/text_format_pov.c
@@ -825,7 +825,7 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_
continue;
}
/* Handle continuations */
- else if (cont) {
+ if (cont) {
/* C-Style comments */
if (cont & FMT_CONT_COMMENT_C) {
if (*str == '*' && *(str + 1) == '/') {
diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c
index df4f1e6b38c..8d6b877d3f7 100644
--- a/source/blender/editors/space_text/text_format_pov_ini.c
+++ b/source/blender/editors/space_text/text_format_pov_ini.c
@@ -410,7 +410,7 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool
continue;
}
/* Handle continuations */
- else if (cont) {
+ if (cont) {
/* Multi-line comments */
if (cont & FMT_CONT_COMMENT_C) {
if (*str == ']' && *(str + 1) == ']') {
diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c
index 3858f7225ef..39985438462 100644
--- a/source/blender/editors/space_text/text_format_py.c
+++ b/source/blender/editors/space_text/text_format_py.c
@@ -376,7 +376,7 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n
continue;
}
/* Handle continuations */
- else if (cont) {
+ if (cont) {
/* Triple strings ("""...""" or '''...''') */
if (cont & FMT_CONT_TRIPLE) {
find = (cont & FMT_CONT_QUOTEDOUBLE) ? '"' : '\'';
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index fa378954a30..6be436cffb5 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;
@@ -3008,7 +3012,7 @@ static void text_cursor_set_to_pos_wrapped(
break;
/* Exactly at the cursor */
}
- else if (y == 0 && i - start <= x && i + columns - start > x) {
+ if (y == 0 && i - start <= x && i + columns - start > x) {
/* current position could be wrapped to next line */
/* this should be checked when end of current line would be reached */
charp = curs = j;
@@ -3467,21 +3471,20 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
return OPERATOR_PASS_THROUGH;
}
- else {
- char str[BLI_UTF8_MAX + 1];
- size_t len;
- if (event->utf8_buf[0]) {
- len = BLI_str_utf8_size_safe(event->utf8_buf);
- memcpy(str, event->utf8_buf, len);
- }
- else {
- /* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str);
- }
- str[len] = '\0';
- RNA_string_set(op->ptr, "text", str);
+ char str[BLI_UTF8_MAX + 1];
+ size_t len;
+
+ if (event->utf8_buf[0]) {
+ len = BLI_str_utf8_size_safe(event->utf8_buf);
+ memcpy(str, event->utf8_buf, len);
+ }
+ else {
+ /* in theory, ghost can set value to extended ascii here */
+ len = BLI_str_utf8_from_unicode(event->ascii, str);
}
+ str[len] = '\0';
+ RNA_string_set(op->ptr, "text", str);
}
ret = text_insert_exec(C, op);
@@ -3621,8 +3624,54 @@ 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)
{
+ bool replace_all = RNA_boolean_get(op->ptr, "all");
+ if (replace_all) {
+ return text_replace_all(C);
+ }
return text_find_and_replace(C, op, TEXT_REPLACE);
}
@@ -3639,6 +3688,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);
}
/** \} */
@@ -3667,7 +3721,7 @@ static int text_find_set_selected_exec(bContext *C, wmOperator *op)
void TEXT_OT_find_set_selected(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Find Set Selected";
+ ot->name = "Find & Set Selection";
ot->idname = "TEXT_OT_find_set_selected";
ot->description = "Find specified text and set as selected";
@@ -3698,7 +3752,7 @@ static int text_replace_set_selected_exec(bContext *C, wmOperator *UNUSED(op))
void TEXT_OT_replace_set_selected(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Replace Set Selected";
+ ot->name = "Replace & Set Selection";
ot->idname = "TEXT_OT_replace_set_selected";
ot->description = "Replace text with specified text and set as selected";
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 5959052b0ab..f17d7ccd136 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -126,7 +126,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
}
}
- glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
+ GPU_front_facing(ob->transflag & OB_NEG_SCALE);
/* Just to create the data to pass to immediate mode, grr! */
const int *facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP);
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 4fc98789c18..c88303daa16 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -120,11 +120,10 @@ bool ED_view3d_context_user_region(bContext *C, View3D **r_v3d, ARegion **r_regi
*r_region = region;
return true;
}
- else {
- if (ED_view3d_area_user_region(area, v3d, r_region)) {
- *r_v3d = v3d;
- return true;
- }
+
+ if (ED_view3d_area_user_region(area, v3d, r_region)) {
+ *r_v3d = v3d;
+ return true;
}
}
}
@@ -508,9 +507,8 @@ static bool view3d_ima_drop_poll(bContext *C,
/* rule might not work? */
return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
}
- else {
- return WM_drag_ID(drag, ID_IM) != NULL;
- }
+
+ return WM_drag_ID(drag, ID_IM) != NULL;
}
static bool view3d_ima_bg_is_camera_view(bContext *C)
@@ -827,6 +825,7 @@ static void view3d_main_region_listener(
case ND_POSE:
case ND_DRAW:
case ND_MODIFIER:
+ case ND_SHADERFX:
case ND_CONSTRAINT:
case ND_KEYS:
case ND_PARTICLE:
@@ -1383,6 +1382,7 @@ static void view3d_buttons_region_listener(wmWindow *UNUSED(win),
case ND_DRAW:
case ND_KEYS:
case ND_MODIFIER:
+ case ND_SHADERFX:
ED_region_tag_redraw(region);
break;
}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index cb87ddafea1..d78c58c0c64 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -126,27 +126,25 @@ static float compute_scale_factor(const float ve_median, const float median)
if (ve_median <= 0.0f) {
return 0.0f;
}
- else if (ve_median >= 1.0f) {
+ if (ve_median >= 1.0f) {
return 1.0f;
}
- else {
- /* Scale value to target median. */
- float median_new = ve_median;
- float median_orig = ve_median - median; /* Previous median value. */
- /* In case of floating point error. */
- CLAMP(median_orig, 0.0f, 1.0f);
- CLAMP(median_new, 0.0f, 1.0f);
+ /* Scale value to target median. */
+ float median_new = ve_median;
+ float median_orig = ve_median - median; /* Previous median value. */
- if (median_new <= median_orig) {
- /* Scale down. */
- return median_new / median_orig;
- }
- else {
- /* Scale up, negative to indicate it... */
- return -(1.0f - median_new) / (1.0f - median_orig);
- }
+ /* In case of floating point error. */
+ CLAMP(median_orig, 0.0f, 1.0f);
+ CLAMP(median_new, 0.0f, 1.0f);
+
+ if (median_new <= median_orig) {
+ /* Scale down. */
+ return median_new / median_orig;
}
+
+ /* Scale up, negative to indicate it... */
+ return -(1.0f - median_new) / (1.0f - median_orig);
}
/**
@@ -1117,13 +1115,12 @@ static void do_view3d_vgroup_buttons(bContext *C, void *UNUSED(arg), int event)
/* not for me */
return;
}
- else {
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = view_layer->basact->object;
- ED_vgroup_vert_active_mirror(ob, event - B_VGRP_PNL_EDIT_SINGLE);
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
- }
+
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *ob = view_layer->basact->object;
+ ED_vgroup_vert_active_mirror(ob, event - B_VGRP_PNL_EDIT_SINGLE);
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
}
static bool view3d_panel_vgroup_poll(const bContext *C, PanelType *UNUSED(pt))
@@ -1477,6 +1474,7 @@ static void v3d_editmetaball_buts(uiLayout *layout, Object *ob)
uiLayout *col;
if (!mball || !(mball->lastelem)) {
+ uiItemL(layout, IFACE_("Nothing selected"), ICON_NONE);
return;
}
@@ -1641,14 +1639,13 @@ static int view3d_object_mode_menu(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "No active object found");
return OPERATOR_CANCELLED;
}
- else if (((ob->mode & OB_MODE_EDIT) == 0) && (ELEM(ob->type, OB_ARMATURE))) {
+ if (((ob->mode & OB_MODE_EDIT) == 0) && (ELEM(ob->type, OB_ARMATURE))) {
ED_object_mode_set(C, (ob->mode == OB_MODE_OBJECT) ? OB_MODE_POSE : OB_MODE_OBJECT);
return OPERATOR_CANCELLED;
}
- else {
- UI_pie_menu_invoke(C, "VIEW3D_MT_object_mode_pie", CTX_wm_window(C)->eventstate);
- return OPERATOR_CANCELLED;
- }
+
+ UI_pie_menu_invoke(C, "VIEW3D_MT_object_mode_pie", CTX_wm_window(C)->eventstate);
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_object_mode_pie_or_toggle(wmOperatorType *ot)
diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c
index daa3f641404..7e63d650409 100644
--- a/source/blender/editors/space_view3d/view3d_camera_control.c
+++ b/source/blender/editors/space_view3d/view3d_camera_control.c
@@ -116,9 +116,8 @@ Object *ED_view3d_cameracontrol_object_get(View3DCameraControl *vctrl)
if (rv3d->persp == RV3D_CAMOB) {
return view3d_cameracontrol_object(vctrl);
}
- else {
- return NULL;
- }
+
+ return NULL;
}
/**
@@ -182,7 +181,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..411b18717bf 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -36,6 +36,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_main.h"
@@ -74,7 +75,6 @@
#include "GPU_batch.h"
#include "GPU_batch_presets.h"
-#include "GPU_draw.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
@@ -844,16 +844,9 @@ void ED_view3d_draw_depth(Depsgraph *depsgraph, ARegion *region, View3D *v3d, bo
ED_view3d_draw_setup_view(
G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
- GPU_clear(GPU_DEPTH_BIT);
-
- if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- ED_view3d_clipping_set(rv3d);
- }
/* get surface depth without bias */
rv3d->rflag |= RV3D_ZOFFSET_DISABLED;
- GPU_depth_test(true);
-
/* Needed in cases the view-port isn't already setup. */
WM_draw_region_viewport_ensure(region, SPACE_VIEW3D);
WM_draw_region_viewport_bind(region);
@@ -867,14 +860,8 @@ void ED_view3d_draw_depth(Depsgraph *depsgraph, ARegion *region, View3D *v3d, bo
WM_draw_region_viewport_unbind(region);
- if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
- ED_view3d_clipping_disable();
- }
rv3d->rflag &= ~RV3D_ZOFFSET_DISABLED;
- /* Reset default for UI */
- GPU_depth_test(false);
-
U.glalphaclip = glalphaclip;
v3d->flag = flag;
@@ -1085,7 +1072,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d)
GPU_blend(true);
GPU_blend_set_func_separate(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
- glDepthMask(GL_FALSE); /* don't overwrite zbuf */
+ GPU_depth_mask(false); /* don't overwrite zbuf */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
@@ -1175,7 +1162,7 @@ static void draw_rotation_guide(const RegionView3D *rv3d)
immUnbindProgram();
GPU_blend(false);
- glDepthMask(GL_TRUE);
+ GPU_depth_mask(true);
}
#endif /* WITH_INPUT_NDOF */
@@ -1619,9 +1606,7 @@ RenderEngineType *ED_view3d_engine_type(const Scene *scene, int drawtype)
if (drawtype == OB_MATERIAL && (type->flag & RE_USE_EEVEE_VIEWPORT)) {
return RE_engines_find(RE_engine_id_BLENDER_EEVEE);
}
- else {
- return type;
- }
+ return type;
}
void view3d_main_region_draw(const bContext *C, ARegion *region)
@@ -1632,7 +1617,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
view3d_draw_view(C, region);
DRW_cache_free_old_batches(bmain);
- GPU_free_images_old(bmain);
+ BKE_image_free_old_gputextures(bmain);
GPU_pass_cache_garbage_collect();
/* XXX This is in order to draw UI batches with the DRW
@@ -1714,10 +1699,15 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
/* set flags */
G.f |= G_FLAG_RENDER_VIEWPORT;
+ /* There are too many functions inside the draw manager that check the shading type,
+ * so use a temporary override instead. */
+ const eDrawType drawtype_orig = v3d->shading.type;
+ v3d->shading.type = drawtype;
+
{
/* free images which can have changed on frame-change
* warning! can be slow so only free animated images - campbell */
- GPU_free_images_anim(G.main); /* XXX :((( */
+ BKE_image_free_anim_gputextures(G.main); /* XXX :((( */
}
GPU_matrix_push_projection();
@@ -1754,6 +1744,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
UI_Theme_Restore(&theme_state);
+ v3d->shading.type = drawtype_orig;
G.f &= ~G_FLAG_RENDER_VIEWPORT;
}
@@ -1888,7 +1879,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
if (own_ofs) {
/* bind */
- ofs = GPU_offscreen_create(sizex, sizey, 0, true, false, err_out);
+ ofs = GPU_offscreen_create(sizex, sizey, true, false, err_out);
if (ofs == NULL) {
DRW_opengl_context_disable();
return NULL;
@@ -1963,10 +1954,10 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
NULL);
if (ibuf->rect_float) {
- GPU_offscreen_read_pixels(ofs, GL_FLOAT, ibuf->rect_float);
+ GPU_offscreen_read_pixels(ofs, GPU_DATA_FLOAT, ibuf->rect_float);
}
else if (ibuf->rect) {
- GPU_offscreen_read_pixels(ofs, GL_UNSIGNED_BYTE, ibuf->rect);
+ GPU_offscreen_read_pixels(ofs, GPU_DATA_UNSIGNED_BYTE, ibuf->rect);
}
/* unbind */
@@ -2116,27 +2107,6 @@ bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const
return view3d_clipping_test(co, is_local ? rv3d->clip_local : rv3d->clip);
}
-void ED_view3d_clipping_set(RegionView3D *UNUSED(rv3d))
-{
- for (uint a = 0; a < 6; a++) {
- glEnable(GL_CLIP_DISTANCE0 + a);
- }
-}
-
-/* Use these to temp disable/enable clipping when 'rv3d->rflag & RV3D_CLIPPING' is set. */
-void ED_view3d_clipping_disable(void)
-{
- for (uint a = 0; a < 6; a++) {
- glDisable(GL_CLIP_DISTANCE0 + a);
- }
-}
-void ED_view3d_clipping_enable(void)
-{
- for (uint a = 0; a < 6; a++) {
- glEnable(GL_CLIP_DISTANCE0 + a);
- }
-}
-
/* *********************** backdraw for selection *************** */
/**
@@ -2194,13 +2164,8 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void
GPU_framebuffer_texture_attach(tmp_fb, dtxl->depth, 0, 0);
GPU_framebuffer_bind(tmp_fb);
- glReadPixels(rect->xmin,
- rect->ymin,
- BLI_rcti_size_x(rect),
- BLI_rcti_size_y(rect),
- GL_DEPTH_COMPONENT,
- GL_FLOAT,
- data);
+ GPU_framebuffer_read_depth(
+ tmp_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), data);
GPU_framebuffer_restore();
GPU_framebuffer_free(tmp_fb);
@@ -2287,7 +2252,9 @@ void view3d_update_depths_rect(ARegion *region, ViewDepths *d, rcti *rect)
if (d->damaged) {
GPUViewport *viewport = WM_draw_region_get_viewport(region);
view3d_opengl_read_Z_pixels(viewport, rect, d->depths);
- glGetDoublev(GL_DEPTH_RANGE, d->depth_range);
+ /* Range is assumed to be this as they are never changed. */
+ d->depth_range[0] = 0.0;
+ d->depth_range[1] = 1.0;
d->damaged = false;
}
}
@@ -2322,7 +2289,9 @@ void ED_view3d_depth_update(ARegion *region)
.ymax = d->h,
};
view3d_opengl_read_Z_pixels(viewport, &r, d->depths);
- glGetDoublev(GL_DEPTH_RANGE, d->depth_range);
+ /* Assumed to be this as they are never changed. */
+ d->depth_range[0] = 0.0;
+ d->depth_range[1] = 1.0;
d->damaged = false;
}
}
@@ -2376,7 +2345,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 +2353,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..bf02845058e 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -509,8 +509,8 @@ static void viewops_data_create(bContext *C,
negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
- /* Set the dist value to be the distance from this 3d point this means youll
- * always be able to zoom into it and panning wont go bad when dist was zero */
+ /* Set the dist value to be the distance from this 3d point this means you'll
+ * always be able to zoom into it and panning wont go bad when dist was zero. */
/* remove dist value */
upvec[0] = upvec[1] = 0;
@@ -1021,12 +1021,11 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_FINISHED;
}
- else {
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
}
static void viewrotate_cancel(bContext *C, wmOperator *op)
@@ -1848,12 +1847,11 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_FINISHED;
}
- else {
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
}
static void viewmove_cancel(bContext *C, wmOperator *op)
@@ -2407,18 +2405,17 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
viewops_data_free(C, op);
return OPERATOR_FINISHED;
}
- else {
- if (U.viewzoom == USER_ZOOM_CONT) {
- /* needs a timer to continue redrawing */
- vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
- vod->prev.time = PIL_check_seconds_timer();
- }
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
-
- return OPERATOR_RUNNING_MODAL;
+ if (U.viewzoom == USER_ZOOM_CONT) {
+ /* needs a timer to continue redrawing */
+ vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
+ vod->prev.time = PIL_check_seconds_timer();
}
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_FINISHED;
}
@@ -2500,9 +2497,7 @@ static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked");
return true;
}
- else {
- return false;
- }
+ return false;
}
static void view_dolly_to_vector_3d(ARegion *region, float orig_ofs[3], float dvec[3], float dfac)
@@ -2726,12 +2721,10 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
viewops_data_free(C, op);
return OPERATOR_FINISHED;
}
- else {
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_FINISHED;
}
@@ -2952,6 +2945,9 @@ static int view3d_all_exec(bContext *C, wmOperator *op)
}
if (center) {
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ WM_msg_publish_rna_prop(mbus, &scene->id, &scene->cursor, View3DCursor, location);
+
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
}
@@ -3170,9 +3166,8 @@ static int view_lock_clear_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_view_lock_clear(wmOperatorType *ot)
@@ -3228,9 +3223,8 @@ static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_view_lock_to_active(wmOperatorType *ot)
@@ -4500,10 +4494,9 @@ static int viewroll_exec(bContext *C, wmOperator *op)
viewops_data_free(C, op);
return OPERATOR_FINISHED;
}
- else {
- viewops_data_free(C, op);
- return OPERATOR_CANCELLED;
- }
+
+ viewops_data_free(C, op);
+ return OPERATOR_CANCELLED;
}
static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -4535,12 +4528,10 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
viewops_data_free(C, op);
return OPERATOR_FINISHED;
}
- else {
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_FINISHED;
}
@@ -4755,9 +4746,8 @@ static Camera *background_image_camera_from_context(bContext *C)
}
return NULL;
}
- else {
- return CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data;
- }
+
+ return CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data;
}
static int background_image_add_exec(bContext *C, wmOperator *UNUSED(op))
@@ -4848,9 +4838,7 @@ static int background_image_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_background_image_remove(wmOperatorType *ot)
@@ -4933,9 +4921,7 @@ static int view3d_clipping_invoke(bContext *C, wmOperator *op, const wmEvent *ev
rv3d->clipbb = NULL;
return OPERATOR_FINISHED;
}
- else {
- return WM_gesture_box_invoke(C, op, event);
- }
+ return WM_gesture_box_invoke(C, op, event);
}
void VIEW3D_OT_clip_border(wmOperatorType *ot)
@@ -4988,7 +4974,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..cc19ecf35a8 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;
@@ -190,7 +200,7 @@ typedef struct FlyInfo {
float grid;
/* compare between last state */
- /** Used to accelerate when using the mousewheel a lot. */
+ /** Used to accelerate when using the mouse-wheel a lot. */
double time_lastwheel;
/** Time between draws. */
double time_lastdraw;
@@ -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,
@@ -403,7 +425,7 @@ static int flyEnd(bContext *C, FlyInfo *fly)
if (fly->state == FLY_RUNNING) {
return OPERATOR_RUNNING_MODAL;
}
- else if (fly->state == FLY_CONFIRM) {
+ if (fly->state == FLY_CONFIRM) {
/* Needed for auto_keyframe. */
#ifdef WITH_INPUT_NDOF
if (fly->ndof) {
@@ -592,8 +614,8 @@ static void flyEvent(FlyInfo *fly, const wmEvent *event)
fly->axis = -1;
}
else {
- /* flip speed rather than stopping, game like motion,
- * else increase like mousewheel if we're already moving in that direction */
+ /* Flip speed rather than stopping, game like motion,
+ * else increase like mouse-wheel if we're already moving in that direction. */
if (fly->speed < 0.0f) {
fly->speed = -fly->speed;
}
@@ -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_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index f3bc0a8a15b..59b2e378955 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -397,9 +397,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
}
return true;
}
- else {
- return false;
- }
+ return false;
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
index 18617b4368f..3f258a0699a 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
@@ -126,9 +126,8 @@ static void WIDGETGROUP_tool_generic_refresh(const bContext *C, wmGizmoGroup *gz
WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
return;
}
- else {
- gzgroup->use_fallback_keymap = true;
- }
+
+ gzgroup->use_fallback_keymap = true;
/* skip, we don't draw anything anyway */
{
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..b7219290654 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -86,6 +86,11 @@ enum ePlace_Depth {
PLACE_DEPTH_CURSOR_VIEW = 3,
};
+enum ePlace_Orient {
+ PLACE_ORIENT_SURFACE = 1,
+ PLACE_ORIENT_DEFAULT = 2,
+};
+
struct InteractivePlaceData {
/* Window manager variables (set these even when waiting for input). */
Scene *scene;
@@ -145,16 +150,96 @@ struct InteractivePlaceData {
/* On-screen snap distance. */
#define MVAL_MAX_PX_DIST 12.0f
-static bool idp_snap_point_from_gizmo(wmGizmo *gz, float r_location[3])
+static bool idp_snap_point_from_gizmo_ex(wmGizmo *gz, const char *prop_id, float r_location[3])
{
if (gz->state & WM_GIZMO_STATE_HIGHLIGHT) {
- PropertyRNA *prop_location = RNA_struct_find_property(gz->ptr, "location");
+ PropertyRNA *prop_location = RNA_struct_find_property(gz->ptr, prop_id);
RNA_property_float_get_array(gz->ptr, prop_location, r_location);
return true;
}
return false;
}
+static bool idp_snap_point_from_gizmo(wmGizmo *gz, float r_location[3])
+{
+ return idp_snap_point_from_gizmo_ex(gz, "location", r_location);
+}
+
+static bool idp_snap_normal_from_gizmo(wmGizmo *gz, float r_normal[3])
+{
+ return idp_snap_point_from_gizmo_ex(gz, "normal", r_normal);
+}
+
+/**
+ * Calculate a 3x3 orientation matrix from the surface under the cursor.
+ */
+static bool idp_poject_surface_normal(SnapObjectContext *snap_context,
+ struct Depsgraph *depsgraph,
+ const float mval_fl[2],
+ const float mat_fallback[3][3],
+ const float normal_fallback[3],
+ float r_mat[3][3])
+{
+ bool success = false;
+ float normal[3] = {0.0f};
+ float co_dummy[3];
+ /* We could use the index to get the orientation from the face. */
+ Object *ob_snap;
+ float obmat[4][4];
+
+ if (ED_transform_snap_object_project_view3d_ex(snap_context,
+ depsgraph,
+ SCE_SNAP_MODE_FACE,
+ &(const struct SnapObjectParams){
+ .snap_select = SNAP_ALL,
+ .use_object_edit_cage = true,
+ },
+ mval_fl,
+ NULL,
+ NULL,
+ co_dummy,
+ normal,
+ NULL,
+ &ob_snap,
+ obmat)) {
+ /* pass */
+ }
+ else if (normal_fallback != NULL) {
+ copy_m4_m3(obmat, mat_fallback);
+ copy_v3_v3(normal, normal_fallback);
+ }
+
+ if (!is_zero_v3(normal)) {
+ float mat[3][3];
+ copy_m3_m4(mat, obmat);
+ normalize_m3(mat);
+
+ float dot_best = fabsf(dot_v3v3(mat[0], normal));
+ int i_best = 0;
+ for (int i = 1; i < 3; i++) {
+ float dot_test = fabsf(dot_v3v3(mat[i], normal));
+ if (dot_test > dot_best) {
+ i_best = i;
+ dot_best = dot_test;
+ }
+ }
+ if (dot_v3v3(mat[i_best], normal) < 0.0f) {
+ negate_v3(mat[(i_best + 1) % 3]);
+ negate_v3(mat[(i_best + 2) % 3]);
+ }
+ copy_v3_v3(mat[i_best], normal);
+ orthogonalize_m3(mat, i_best);
+ normalize_m3(mat);
+
+ copy_v3_v3(r_mat[0], mat[(i_best + 1) % 3]);
+ copy_v3_v3(r_mat[1], mat[(i_best + 2) % 3]);
+ copy_v3_v3(r_mat[2], mat[i_best]);
+ success = true;
+ }
+
+ return success;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -549,30 +634,69 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
const int plane_axis = RNA_enum_get(op->ptr, "plane_axis");
const enum ePlace_Depth plane_depth = RNA_enum_get(op->ptr, "plane_depth");
const enum ePlace_Origin plane_origin = RNA_enum_get(op->ptr, "plane_origin");
+ const enum ePlace_Orient plane_orient = RNA_enum_get(op->ptr, "plane_orientation");
+
+ const float mval_fl[2] = {UNPACK2(event->mval)};
struct InteractivePlaceData *ipd = op->customdata;
RegionView3D *rv3d = ipd->region->regiondata;
+ /* Assign snap gizmo which is may be used as part of the tool. */
+ {
+ wmGizmoMap *gzmap = ipd->region->gizmo_map;
+ wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, view3d_gzgt_placement_id) : NULL;
+ if ((gzgroup != NULL) && gzgroup->gizmos.first) {
+ ipd->snap_gizmo = gzgroup->gizmos.first;
+ }
+ }
+
ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type);
ED_transform_calc_orientation_from_type(C, ipd->matrix_orient);
+ /* Set the orientation. */
+ if (plane_orient == PLACE_ORIENT_SURFACE) {
+ bool snap_context_free = false;
+ SnapObjectContext *snap_context =
+ (ipd->snap_gizmo ? ED_gizmotypes_snap_3d_context_ensure(
+ ipd->scene, ipd->region, ipd->v3d, ipd->snap_gizmo) :
+ NULL);
+ if (snap_context == NULL) {
+ snap_context = ED_transform_snap_object_context_create_view3d(
+ ipd->scene, 0, ipd->region, ipd->v3d);
+ snap_context_free = true;
+ }
+
+ float matrix_orient_surface[3][3];
+
+ /* Use the snap normal as a fallback in case the cursor isn't over a surface
+ * but snapping is enabled. */
+ float normal_fallback[3];
+ bool use_normal_fallback = ipd->snap_gizmo ?
+ idp_snap_normal_from_gizmo(ipd->snap_gizmo, normal_fallback) :
+ false;
+
+ if (idp_poject_surface_normal(snap_context,
+ CTX_data_ensure_evaluated_depsgraph(C),
+ mval_fl,
+ use_normal_fallback ? ipd->matrix_orient : NULL,
+ use_normal_fallback ? normal_fallback : NULL,
+ matrix_orient_surface)) {
+ copy_m3_m3(ipd->matrix_orient, matrix_orient_surface);
+ }
+
+ if (snap_context_free) {
+ ED_transform_snap_object_context_destroy(snap_context);
+ }
+ }
+
ipd->orient_axis = plane_axis;
ipd->is_centered_init = (plane_origin == PLACE_ORIGIN_CENTER);
ipd->step[0].is_centered = ipd->is_centered_init;
ipd->step[1].is_centered = ipd->is_centered_init;
ipd->step_index = STEP_BASE;
- /* Assign snap gizmo which is may be used as part of the tool. */
- {
- wmGizmoMap *gzmap = ipd->region->gizmo_map;
- wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, view3d_gzgt_placement_id) : NULL;
- if ((gzgroup != NULL) && gzgroup->gizmos.first) {
- ipd->snap_gizmo = gzgroup->gizmos.first;
- }
- }
-
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "primitive_type");
if (RNA_property_is_set(op->ptr, prop)) {
@@ -618,8 +742,6 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
plane_from_point_normal_v3(
ipd->step[0].plane, ipd->scene->cursor.location, ipd->matrix_orient[ipd->orient_axis]);
- const float mval_fl[2] = {UNPACK2(event->mval)};
-
const bool is_snap_found = ipd->snap_gizmo ?
idp_snap_point_from_gizmo(ipd->snap_gizmo, ipd->co_src) :
false;
@@ -826,7 +948,7 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve
view3d_interactive_add_exit(C, op);
return OPERATOR_CANCELLED;
}
- else if (event->type == MOUSEMOVE) {
+ if (event->type == MOUSEMOVE) {
do_cursor_update = true;
}
@@ -936,6 +1058,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);
}
@@ -1100,6 +1224,25 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot)
RNA_def_property_enum_items(prop, origin_items);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ static const EnumPropertyItem plane_orientation_items[] = {
+ {PLACE_ORIENT_SURFACE,
+ "SURFACE",
+ ICON_SNAP_NORMAL,
+ "Surface",
+ "Use the surface normal (the transform orientation as a fallback)"},
+ {PLACE_ORIENT_DEFAULT,
+ "DEFAULT",
+ ICON_ORIENTATION_GLOBAL,
+ "Default",
+ "Use the current transform orientation"},
+ {0, NULL, 0, NULL, NULL},
+ };
+ prop = RNA_def_property(ot->srna, "plane_orientation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Orientation", "The initial depth used when placing the cursor");
+ RNA_def_property_enum_default(prop, PLACE_ORIENT_SURFACE);
+ RNA_def_property_enum_items(prop, plane_orientation_items);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
/* When not accessed via a tool. */
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
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..2c8ea396a99 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -97,7 +97,6 @@
#include "UI_interface.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "DEG_depsgraph.h"
@@ -1398,9 +1397,7 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op)
if (changed_multi) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
return OPERATOR_PASS_THROUGH;
}
@@ -1542,9 +1539,7 @@ static int object_select_menu_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_select_menu(wmOperatorType *ot)
@@ -1576,7 +1571,7 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
static Base *object_mouse_select_menu(bContext *C,
ViewContext *vc,
- uint *buffer,
+ const uint *buffer,
int hits,
const int mval[2],
bool extend,
@@ -1630,37 +1625,34 @@ static Base *object_mouse_select_menu(bContext *C,
BLI_linklist_free(linklist, NULL);
return base;
}
- else {
- /* UI, full in static array values that we later use in an enum function */
- LinkNode *node;
- int i;
- memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
+ /* UI, full in static array values that we later use in an enum function */
+ LinkNode *node;
+ int i;
- for (node = linklist, i = 0; node; node = node->next, i++) {
- Base *base = node->link;
- Object *ob = base->object;
- const char *name = ob->id.name + 2;
+ memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
- BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
- object_mouse_select_menu_data[i].icon = UI_icon_from_id(&ob->id);
- }
+ for (node = linklist, i = 0; node; node = node->next, i++) {
+ Base *base = node->link;
+ Object *ob = base->object;
+ const char *name = ob->id.name + 2;
- {
- wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
- PointerRNA ptr;
+ BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
+ object_mouse_select_menu_data[i].icon = UI_icon_from_id(&ob->id);
+ }
- WM_operator_properties_create_ptr(&ptr, ot);
- RNA_boolean_set(&ptr, "extend", extend);
- RNA_boolean_set(&ptr, "deselect", deselect);
- RNA_boolean_set(&ptr, "toggle", toggle);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
- WM_operator_properties_free(&ptr);
- }
+ wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
+ PointerRNA ptr;
- BLI_linklist_free(linklist, NULL);
- return NULL;
- }
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_boolean_set(&ptr, "extend", extend);
+ RNA_boolean_set(&ptr, "deselect", deselect);
+ RNA_boolean_set(&ptr, "toggle", toggle);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
+ WM_operator_properties_free(&ptr);
+
+ BLI_linklist_free(linklist, NULL);
+ return NULL;
}
static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
@@ -2504,9 +2496,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
- else {
- return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */
- }
+ return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */
}
static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -3007,7 +2997,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;
}
@@ -3116,12 +3106,10 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_
if (sel_a < sel_b) {
return -1;
}
- else if (sel_a > sel_b) {
+ if (sel_a > sel_b) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
@@ -3380,9 +3368,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
if (changed_multi) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_select_box(wmOperatorType *ot)
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index 437c0dd4035..265cb04c7b2 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;
}
@@ -869,9 +869,7 @@ static int snap_curs_to_sel_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot)
@@ -921,9 +919,7 @@ static int snap_curs_to_active_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot)
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 377e8c58ba3..bd2323f47f4 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;
}
}
@@ -113,9 +113,7 @@ Camera *ED_view3d_camera_data_get(View3D *v3d, RegionView3D *rv3d)
if ((rv3d->persp == RV3D_CAMOB) && v3d->camera && (v3d->camera->type == OB_CAMERA)) {
return v3d->camera->data;
}
- else {
- return NULL;
- }
+ return NULL;
}
void ED_view3d_dist_range_get(const View3D *v3d, float r_dist_range[2])
@@ -222,7 +220,7 @@ void view3d_region_operator_needs_opengl(wmWindow *UNUSED(win), ARegion *region)
}
/**
- * Use instead of: ``bglPolygonOffset(rv3d->dist, ...)`` see bug [#37727]
+ * Use instead of: ``GPU_polygon_offset(rv3d->dist, ...)`` see bug [#37727]
*/
void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist)
{
@@ -243,7 +241,7 @@ void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist)
}
}
- bglPolygonOffset(viewdist, dist);
+ GPU_polygon_offset(viewdist, dist);
}
bool ED_view3d_context_activate(bContext *C)
@@ -602,9 +600,7 @@ bool ED_view3d_camera_lock_sync(const Depsgraph *depsgraph, View3D *v3d, RegionV
return true;
}
- else {
- return false;
- }
+ return false;
}
bool ED_view3d_camera_autokey(const Scene *scene,
@@ -639,9 +635,7 @@ bool ED_view3d_camera_autokey(const Scene *scene,
return true;
}
- else {
- return false;
- }
+ return false;
}
/**
@@ -673,9 +667,7 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d,
return ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate);
}
- else {
- return false;
- }
+ return false;
}
/** \} */
@@ -1017,9 +1009,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
ED_view3d_win_to_3d_int(v3d, region, fallback_depth_pt, mval, mouse_worldloc);
return true;
}
- else {
- return false;
- }
+ return false;
}
void ED_view3d_autodist_init(Depsgraph *depsgraph, ARegion *region, View3D *v3d, int mode)
@@ -1038,8 +1028,11 @@ void ED_view3d_autodist_init(Depsgraph *depsgraph, ARegion *region, View3D *v3d,
}
/* no 4x4 sampling, run #ED_view3d_autodist_init first */
-bool ED_view3d_autodist_simple(
- ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, float *force_depth)
+bool ED_view3d_autodist_simple(ARegion *region,
+ const int mval[2],
+ float mouse_worldloc[3],
+ int margin,
+ const float *force_depth)
{
float depth;
@@ -1086,9 +1079,7 @@ static bool depth_segment_cb(int x, int y, void *userData)
data->depth = depth;
return 0;
}
- else {
- return 1;
- }
+ return 1;
}
bool ED_view3d_autodist_depth_seg(
@@ -1390,9 +1381,7 @@ bool ED_view3d_quat_from_axis_view(const char view, const char view_axis_roll, f
copy_qt_qt(quat, view3d_quat_axis[view - RV3D_VIEW_FRONT][view_axis_roll]);
return true;
}
- else {
- return false;
- }
+ return false;
}
bool ED_view3d_quat_to_axis_view(const float quat[4],
@@ -1472,7 +1461,7 @@ bool ED_view3d_lock(RegionView3D *rv3d)
* \param quat: The view rotation, quaternion normally from RegionView3D.viewquat.
* \param dist: The view distance from ofs, normally from RegionView3D.dist.
*/
-void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], float *dist)
+void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], const float *dist)
{
float nmat[3][3];
@@ -1575,10 +1564,9 @@ float ED_view3d_depth_read_cached(const ViewContext *vc, const int mval[2])
if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h) {
return vd->depths[y * vd->w + x];
}
- else {
- BLI_assert(1.0 <= vd->depth_range[1]);
- return 1.0f;
- }
+
+ BLI_assert(1.0 <= vd->depth_range[1]);
+ return 1.0f;
}
bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
@@ -1633,9 +1621,7 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
if (normalize_v3(r_normal) != 0.0f) {
return true;
}
- else {
- return false;
- }
+ return false;
}
bool ED_view3d_depth_unproject(const ARegion *region,
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index 3fc990160d2..ff9673a4262 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -50,7 +50,6 @@
#include "UI_resources.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
@@ -568,9 +567,7 @@ static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, camera_ob);
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_camera_to_view_selected(wmOperatorType *ot)
@@ -1106,10 +1103,6 @@ int view3d_opengl_select(ViewContext *vc,
GPU_depth_test(true);
}
- if (RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d)) {
- ED_view3d_clipping_set(vc->rv3d);
- }
-
/* If in xray mode, we select the wires in priority. */
if (XRAY_ACTIVE(v3d) && use_nearest) {
/* We need to call "GPU_select_*" API's inside DRW_draw_select_loop
@@ -1175,10 +1168,6 @@ int view3d_opengl_select(ViewContext *vc,
GPU_depth_test(false);
}
- if (RV3D_CLIPPING_ENABLED(v3d, vc->rv3d)) {
- ED_view3d_clipping_disable();
- }
-
DRW_opengl_context_disable();
finally:
@@ -1486,9 +1475,7 @@ static int localview_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
void VIEW3D_OT_localview(wmOperatorType *ot)
@@ -1538,10 +1525,9 @@ static int localview_remove_from_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
return OPERATOR_FINISHED;
}
- else {
- BKE_report(op->reports, RPT_ERROR, "No object selected");
- return OPERATOR_CANCELLED;
- }
+
+ BKE_report(op->reports, RPT_ERROR, "No object selected");
+ return OPERATOR_CANCELLED;
}
static bool localview_remove_from_poll(bContext *C)
diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c
index 7aefd173953..751e37f0235 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) {
@@ -598,7 +620,7 @@ static int walkEnd(bContext *C, WalkInfo *walk)
if (walk->state == WALK_RUNNING) {
return OPERATOR_RUNNING_MODAL;
}
- else if (walk->state == WALK_CONFIRM) {
+ if (walk->state == WALK_CONFIRM) {
/* Needed for auto_keyframe. */
#ifdef WITH_INPUT_NDOF
if (walk->ndof) {
@@ -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 d8503a28774..76cce5e725f 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);
}
@@ -670,10 +673,10 @@ static bool transform_modal_item_poll(const wmOperator *op, int value)
if (t->spacetype != SPACE_VIEW3D) {
return false;
}
- else if ((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) == 0) {
+ if ((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) == 0) {
return false;
}
- else if (!validSnap(t)) {
+ if (!validSnap(t)) {
return false;
}
break;
@@ -1414,9 +1417,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
if (handled || t->redraw) {
return 0;
}
- else {
- return OPERATOR_PASS_THROUGH;
- }
+ return OPERATOR_PASS_THROUGH;
}
bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
@@ -1591,17 +1592,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 +1627,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 +1649,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 +1702,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 +1785,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 +1836,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 */
@@ -1917,6 +1936,27 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
return 0;
}
+ /* When proportional editing is enabled, data_len_all can be non zero when
+ * nothing is selected, if this is the case we can end the transform early.
+ *
+ * By definition transform-data has selected items in beginning,
+ * so only the first item in each container needs to be checked
+ * when looking for the presence of selected data. */
+ if (t->flag & T_PROP_EDIT) {
+ bool has_selected_any = false;
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->data->flag & TD_SELECTED) {
+ has_selected_any = true;
+ break;
+ }
+ }
+
+ if (!has_selected_any) {
+ postTrans(C, t);
+ return 0;
+ }
+ }
+
if (event) {
/* keymap for shortcut header prints */
t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
@@ -2088,14 +2128,6 @@ int transformEnd(bContext *C, TransInfo *t)
if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
/* handle restoring objects */
if (t->state == TRANS_CANCEL) {
- /* 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);
- }
-
exit_code = OPERATOR_CANCELLED;
restoreTransObjects(t); // calls recalcData()
}
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 2bda04ad811..bbd18b4bdaf 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -81,6 +81,7 @@ typedef struct TransSnap {
bool snap_self;
bool peel;
bool snap_spatial_grid;
+ bool use_backface_culling;
char status;
/* Snapped Element Type (currently for objects only). */
char snapElem;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 0347522b8e8..e15239f37d4 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -42,7 +42,6 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
-#include "BKE_scene.h"
#include "ED_view3d.h"
@@ -80,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;
@@ -298,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);
+}
- float view_to_plane[3], plane_normal[3];
+/**
+ * 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);
+ }
+}
- getViewVector(t, t->center_global, view_to_plane);
+/**
+ * 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);
+ }
+}
- cross_v3_v3v3(plane_normal, constraint_vector[0], constraint_vector[1]);
- normalize_v3(plane_normal);
+/**
+ * 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);
+ }
+}
+
+/**
+ * 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;
}
@@ -362,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);
+ }
}
}
}
@@ -385,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);
@@ -412,10 +510,12 @@ static void applyObjectConstraintVec(
else {
/* Specific TransData's space. */
copy_v3_v3(out, in);
- mul_m3_v3(t->spacemtx_inv, out);
- mul_m3_v3(td->axismtx, out);
- if (t->flag & T_EDIT) {
- mul_m3_v3(tc->mat3_unit, out);
+ if (t->con.mode & CON_APPLY) {
+ mul_m3_v3(t->spacemtx_inv, out);
+ mul_m3_v3(td->axismtx, out);
+ if (t->flag & T_EDIT) {
+ mul_m3_v3(tc->mat3_unit, out);
+ }
}
}
}
@@ -780,12 +880,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 8496642185d..70004d27dfc 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -27,6 +27,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_kdtree.h"
+#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -55,7 +56,6 @@
#include "DEG_depsgraph_build.h"
#include "transform.h"
-#include "transform_mode.h"
#include "transform_snap.h"
/* Own include. */
@@ -70,41 +70,21 @@ bool transform_mode_use_local_origins(const TransInfo *t)
* Transforming around ourselves is no use, fallback to individual origins,
* useful for curve/armatures.
*/
-void transform_around_single_fallback(TransInfo *t)
+void transform_around_single_fallback_ex(TransInfo *t, int data_len_all)
{
if ((ELEM(t->around, V3D_AROUND_CENTER_BOUNDS, V3D_AROUND_CENTER_MEDIAN, V3D_AROUND_ACTIVE)) &&
transform_mode_use_local_origins(t)) {
-
- bool is_data_single = false;
- if (t->data_len_all == 1) {
- is_data_single = true;
- }
- else if (t->data_len_all == 3) {
- if (t->obedit_type == OB_CURVE) {
- /* Special case check for curve, if we have a single curve bezier triple selected
- * treat */
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (!tc->data_len) {
- continue;
- }
- if (tc->data_len == 3) {
- const TransData *td = tc->data;
- if ((td[0].flag | td[1].flag | td[2].flag) & TD_BEZTRIPLE) {
- if ((td[0].loc == td[1].loc) && (td[1].loc == td[2].loc)) {
- is_data_single = true;
- }
- }
- }
- break;
- }
- }
- }
- if (is_data_single) {
+ if (data_len_all == 1) {
t->around = V3D_AROUND_LOCAL_ORIGINS;
}
}
}
+void transform_around_single_fallback(TransInfo *t)
+{
+ transform_around_single_fallback_ex(t, t->data_len_all);
+}
+
/* -------------------------------------------------------------------- */
/** \name Proportional Editing
* \{ */
@@ -117,12 +97,10 @@ static int trans_data_compare_dist(const void *a, const void *b)
if (td_a->dist < td_b->dist) {
return -1;
}
- else if (td_a->dist > td_b->dist) {
+ if (td_a->dist > td_b->dist) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static int trans_data_compare_rdist(const void *a, const void *b)
@@ -133,12 +111,10 @@ static int trans_data_compare_rdist(const void *a, const void *b)
if (td_a->rdist < td_b->rdist) {
return -1;
}
- else if (td_a->rdist > td_b->rdist) {
+ if (td_a->rdist > td_b->rdist) {
return 1;
}
- else {
- return 0;
- }
+ return 0;
}
static void sort_trans_data_dist_container(const TransInfo *t, TransDataContainer *tc)
@@ -434,59 +410,66 @@ void transform_autoik_update(TransInfo *t, short mode)
/** \name Curve Surface
* \{ */
-void calc_distanceCurveVerts(TransData *head, TransData *tail)
+void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic)
{
- TransData *td, *td_near = NULL;
+ TransData *td;
+ BLI_LINKSTACK_DECLARE(queue, TransData *);
+ BLI_LINKSTACK_INIT(queue);
for (td = head; td <= tail; td++) {
if (td->flag & TD_SELECTED) {
- td_near = td;
td->dist = 0.0f;
+ BLI_LINKSTACK_PUSH(queue, td);
+ }
+ else {
+ td->dist = FLT_MAX;
+ }
+ }
+
+ while ((td = BLI_LINKSTACK_POP(queue))) {
+ float dist;
+ float vec[3];
+
+ TransData *next_td = NULL;
+
+ if (td + 1 <= tail) {
+ next_td = td + 1;
+ }
+ else if (cyclic) {
+ next_td = head;
}
- else if (td_near) {
- float dist;
- float vec[3];
- sub_v3_v3v3(vec, td_near->center, td->center);
+ if (next_td != NULL && !(next_td->flag & TD_NOTCONNECTED)) {
+ sub_v3_v3v3(vec, next_td->center, td->center);
mul_m3_v3(head->mtx, vec);
- dist = len_v3(vec);
+ dist = len_v3(vec) + td->dist;
- if (dist < (td - 1)->dist) {
- td->dist = (td - 1)->dist;
- }
- else {
- td->dist = dist;
+ if (dist < next_td->dist) {
+ next_td->dist = dist;
+ BLI_LINKSTACK_PUSH(queue, next_td);
}
}
- else {
- td->dist = FLT_MAX;
- td->flag |= TD_NOTCONNECTED;
+
+ next_td = NULL;
+
+ if (td - 1 >= head) {
+ next_td = td - 1;
}
- }
- td_near = NULL;
- for (td = tail; td >= head; td--) {
- if (td->flag & TD_SELECTED) {
- td_near = td;
- td->dist = 0.0f;
+ else if (cyclic) {
+ next_td = tail;
}
- else if (td_near) {
- float dist;
- float vec[3];
- sub_v3_v3v3(vec, td_near->center, td->center);
+ if (next_td != NULL && !(next_td->flag & TD_NOTCONNECTED)) {
+ sub_v3_v3v3(vec, next_td->center, td->center);
mul_m3_v3(head->mtx, vec);
- dist = len_v3(vec);
+ dist = len_v3(vec) + td->dist;
- if (td->flag & TD_NOTCONNECTED || dist < td->dist || (td + 1)->dist < td->dist) {
- td->flag &= ~TD_NOTCONNECTED;
- if (dist < (td + 1)->dist) {
- td->dist = (td + 1)->dist;
- }
- else {
- td->dist = dist;
- }
+ if (dist < next_td->dist) {
+ next_td->dist = dist;
+ BLI_LINKSTACK_PUSH(queue, next_td);
}
}
}
+ BLI_LINKSTACK_FREE(queue);
}
/* Utility function for getting the handle data from bezier's */
@@ -631,9 +614,7 @@ bool FrameOnMouseSide(char side, float frame, float cframe)
if (side == 'R') {
return (frame >= cframe);
}
- else {
- return (frame <= cframe);
- }
+ return (frame <= cframe);
}
/** \} */
@@ -691,7 +672,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand
found = true;
break;
}
- else if (rk->frame < bezt->vec[1][0]) {
+ if (rk->frame < bezt->vec[1][0]) {
/* Terminate early if have passed the supposed insertion point? */
break;
}
@@ -717,11 +698,10 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand
}
return;
}
- else {
- /* Compute the average values for each retained keyframe */
- LISTBASE_FOREACH (tRetainedKeyframe *, rk, &retained_keys) {
- rk->val = rk->val / (float)rk->tot_count;
- }
+
+ /* Compute the average values for each retained keyframe */
+ LISTBASE_FOREACH (tRetainedKeyframe *, rk, &retained_keys) {
+ rk->val = rk->val / (float)rk->tot_count;
}
/* 2) Delete all keyframes duplicating the "retained keys" found above
@@ -795,7 +775,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) {
@@ -946,13 +926,13 @@ int special_transform_moving(TransInfo *t)
if (t->spacetype == SPACE_SEQ) {
return G_TRANSFORM_SEQ;
}
- else if (t->spacetype == SPACE_GRAPH) {
+ if (t->spacetype == SPACE_GRAPH) {
return G_TRANSFORM_FCURVES;
}
- else if ((t->flag & T_EDIT) || (t->flag & T_POSE)) {
+ if ((t->flag & T_EDIT) || (t->flag & T_POSE)) {
return G_TRANSFORM_EDIT;
}
- else if (t->flag & (T_OBJECT | T_TEXTURE)) {
+ if (t->flag & (T_OBJECT | T_TEXTURE)) {
return G_TRANSFORM_OBJ;
}
@@ -1169,21 +1149,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;
}
@@ -1196,6 +1161,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:
@@ -1245,6 +1211,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);
@@ -1298,6 +1278,9 @@ void createTransData(bContext *C, TransInfo *t)
set_prop_dist(t, false);
}
}
+ else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) {
+ /* Already calculated by uv_set_connectivity_distance. */
+ }
else if (convert_type == TC_CURVE_VERTS && t->obedit_type == OB_CURVE) {
set_prop_dist(t, false);
}
@@ -1313,18 +1296,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 fdb6767a267..70017d5560b 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -47,8 +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);
-void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc, bool is_final);
+void mesh_customdatacorrect_init(TransInfo *t);
/* transform_convert_sequencer.c */
int transform_convert_sequencer_get_snap_bound(TransInfo *t);
@@ -84,10 +83,11 @@ typedef enum eTransConvertType {
/* transform_convert.c */
bool transform_mode_use_local_origins(const TransInfo *t);
+void transform_around_single_fallback_ex(TransInfo *t, int data_len_all);
void transform_around_single_fallback(TransInfo *t);
void posttrans_fcurve_clean(struct FCurve *fcu, const int sel_flag, const bool use_handle);
bool constraints_list_needinv(TransInfo *t, ListBase *list);
-void calc_distanceCurveVerts(TransData *head, TransData *tail);
+void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic);
struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt);
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe);
bool FrameOnMouseSide(char side, float frame, float cframe);
diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c
index 267acd5cb04..40e60544642 100644
--- a/source/blender/editors/transform/transform_convert_action.c
+++ b/source/blender/editors/transform/transform_convert_action.c
@@ -36,7 +36,6 @@
#include "BKE_key.h"
#include "BKE_mask.h"
#include "BKE_nla.h"
-#include "BKE_report.h"
#include "ED_anim_api.h"
#include "ED_keyframes_edit.h"
@@ -85,9 +84,7 @@ static int count_fcurve_keys(FCurve *fcu, char side, float cfra, bool is_prop_ed
if (is_prop_edit && count > 0) {
return count_all;
}
- else {
- return count;
- }
+ return count;
}
/* fully select selected beztriples, but only include if it's on the right side of cfra */
@@ -113,9 +110,7 @@ static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra, bool is_p
if (is_prop_edit && count > 0) {
return count_all;
}
- else {
- return count;
- }
+ return count;
}
/* fully select selected beztriples, but only include if it's on the right side of cfra */
@@ -142,9 +137,7 @@ static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, boo
if (is_prop_edit && count > 0) {
return count_all;
}
- else {
- return count;
- }
+ return count;
}
/* This function assigns the information to transdata */
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 75b51b3d2c4..9885c8fc3a6 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -115,7 +115,9 @@ static void autokeyframe_pose(
ToolSettings *ts = scene->toolsettings;
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
ListBase nla_cache = {NULL, NULL};
- float cfra = (float)CFRA;
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ (float)CFRA);
eInsertKeyFlags flag = 0;
/* flag is initialized from UserPref keyframing settings
@@ -146,7 +148,8 @@ static void autokeyframe_pose(
/* only insert into active keyingset? */
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
/* run the active Keying Set on the current datasource */
- ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
/* only insert into available channels? */
else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
@@ -169,7 +172,7 @@ static void autokeyframe_pose(
((fcu->grp) ? (fcu->grp->name) : (NULL)),
fcu->rna_path,
fcu->array_index,
- cfra,
+ &anim_eval_context,
ts->keyframe_type,
&nla_cache,
flag);
@@ -220,21 +223,25 @@ static void autokeyframe_pose(
if (do_loc) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
if (do_rot) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
if (do_scale) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
}
/* insert keyframe in all (transform) channels */
else {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
/* free temp info */
@@ -553,8 +560,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 +673,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 +838,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 +864,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 +988,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 c5e1e59b9e6..65b2c9f9382 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -64,7 +64,7 @@ static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const boo
* When a center point is being moved without the handles,
* leaving the handles stationary makes no sense and only causes strange behavior,
* where one handle is arbitrarily anchored, the other one is aligned and lengthened
- * based on where the center point is moved. Also a bug when cancelling, see: T52007.
+ * based on where the center point is moved. Also a bug when canceling, see: T52007.
*
* A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'.
* However that doesn't resolve odd behavior, so best transform the handles in this case.
@@ -87,6 +87,10 @@ void createTransCurveVerts(TransInfo *t)
t->data_len_all = 0;
+ /* 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;
+
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
Curve *cu = tc->obedit->data;
BLI_assert(cu->editnurb != NULL);
@@ -94,7 +98,9 @@ void createTransCurveVerts(TransInfo *t)
BPoint *bp;
int a;
int count = 0, countsel = 0;
+ int count_pt = 0, countsel_pt = 0;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+ const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
View3D *v3d = t->view;
short hide_handles = (v3d != NULL) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
false;
@@ -106,17 +112,21 @@ void createTransCurveVerts(TransInfo *t)
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
if (bezt->hide == 0) {
const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
- if (bezt_tx & SEL_F1) {
- countsel++;
- }
- if (bezt_tx & SEL_F2) {
- countsel++;
- }
- if (bezt_tx & SEL_F3) {
- countsel++;
+ if (bezt_tx & (SEL_F1 | SEL_F2 | SEL_F3)) {
+ if (bezt_tx & SEL_F1) {
+ countsel++;
+ }
+ if (bezt_tx & SEL_F2) {
+ countsel++;
+ }
+ if (bezt_tx & SEL_F3) {
+ countsel++;
+ }
+ countsel_pt++;
}
if (is_prop_edit) {
count += 3;
+ count_pt++;
}
}
}
@@ -124,34 +134,42 @@ void createTransCurveVerts(TransInfo *t)
else {
for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
if (bp->hide == 0) {
- if (is_prop_edit) {
- count++;
- }
if (bp->f1 & SELECT) {
countsel++;
+ countsel_pt++;
+ }
+ if (is_prop_edit) {
+ count++;
+ count_pt++;
}
}
}
}
}
- /* note: in prop mode we need at least 1 selected */
- if (countsel == 0) {
+
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
tc->data_len = 0;
continue;
}
+ int data_len_pt = 0;
+
if (is_prop_edit) {
tc->data_len = count;
+ data_len_pt = count_pt;
}
else {
tc->data_len = countsel;
+ data_len_pt = countsel_pt;
}
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Curve EditMode)");
t->data_len_all += tc->data_len;
+ data_len_all_pt += data_len_pt;
}
- transform_around_single_fallback(t);
+ transform_around_single_fallback_ex(t, data_len_all_pt);
t->data_len_all = -1;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -332,12 +350,14 @@ void createTransCurveVerts(TransInfo *t)
(void)hdata; /* quiet warning */
}
else if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
- head = tail;
+ tail->flag |= TD_NOTCONNECTED;
+ td++;
+ tail++;
}
}
if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
+ bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
+ calc_distanceCurveVerts(head, tail - 1, cyclic);
}
/* TODO - in the case of tilt and radius we can also avoid allocating the
@@ -407,12 +427,14 @@ void createTransCurveVerts(TransInfo *t)
}
}
else if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
- head = tail;
+ tail->flag |= TD_NOTCONNECTED;
+ td++;
+ tail++;
}
}
if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
+ bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
+ calc_distanceCurveVerts(head, tail - 1, cyclic);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index 2bfce9f8418..0eb12aeabed 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_ghash.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_colortools.h"
@@ -76,7 +77,6 @@ void createTransGPencil(bContext *C, TransInfo *t)
bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
Object *obact = CTX_data_active_object(C);
- bGPDlayer *gpl;
TransData *td = NULL;
float mtx[3][3], smtx[3][3];
@@ -110,15 +110,12 @@ void createTransGPencil(bContext *C, TransInfo *t)
/* First Pass: Count the number of data-points required for the strokes,
* (and additional info about the configuration - e.g. 2D/3D?).
*/
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- /* only editable and visible layers are considered */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* Only editable and visible layers are considered. */
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf;
bGPDstroke *gps;
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
for (gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
@@ -127,7 +124,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- /* check if the color is editable */
+ /* Check if the color is editable. */
if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
continue;
}
@@ -135,23 +132,22 @@ void createTransGPencil(bContext *C, TransInfo *t)
if (is_prop_edit) {
/* Proportional Editing... */
if (is_prop_edit_connected) {
- /* connected only - so only if selected */
+ /* Connected only - so only if selected. */
if (gps->flag & GP_STROKE_SELECT) {
tc->data_len += gps->totpoints;
}
}
else {
- /* everything goes - connection status doesn't matter */
+ /* Everything goes - connection status doesn't matter. */
tc->data_len += gps->totpoints;
}
}
else {
- /* only selected stroke points are considered */
+ /* Only selected stroke points are considered. */
if (gps->flag & GP_STROKE_SELECT) {
bGPDspoint *pt;
int i;
- // TODO: 2D vs 3D?
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
tc->data_len++;
@@ -161,7 +157,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
}
}
- /* if not multiedit out of loop */
+ /* If not multiedit out of loop. */
if (!is_multiedit) {
break;
}
@@ -169,7 +165,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
}
- /* Stop trying if nothing selected */
+ /* Stop trying if nothing selected. */
if (tc->data_len == 0) {
return;
}
@@ -181,21 +177,17 @@ void createTransGPencil(bContext *C, TransInfo *t)
unit_m3(smtx);
unit_m3(mtx);
- /* Second Pass: Build transdata array */
- for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* Second Pass: Build transdata array. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
float diff_mat[4][4];
float inverse_diff_mat[4][4];
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
- /* init multiframe falloff options */
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ /* Init multiframe falloff options. */
int f_init = 0;
int f_end = 0;
@@ -203,9 +195,9 @@ void createTransGPencil(bContext *C, TransInfo *t)
BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
}
- /* calculate difference matrix */
+ /* Calculate difference matrix. */
BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
- /* undo matrix */
+ /* Undo matrix. */
invert_m4_m4(inverse_diff_mat, diff_mat);
/* Make a new frame to work on if the layer's frame
@@ -214,7 +206,6 @@ void createTransGPencil(bContext *C, TransInfo *t)
* - This is useful when animating as it saves that "uh-oh" moment when you realize you've
* spent too much time editing the wrong frame...
*/
- // XXX: should this be allowed when framelock is enabled?
if ((gpf->framenum != cfra) && (!is_multiedit)) {
gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
/* in some weird situations (framelock enabled) return NULL */
@@ -239,7 +230,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
}
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
TransData *head = td;
TransData *tail = td;
bool stroke_ok;
@@ -286,20 +277,20 @@ void createTransGPencil(bContext *C, TransInfo *t)
/* include point? */
if (is_prop_edit) {
- /* Always all points in strokes that get included */
+ /* Always all points in strokes that get included. */
point_ok = true;
}
else {
- /* Only selected points in selected strokes */
+ /* Only selected points in selected strokes. */
point_ok = (pt->flag & GP_SPOINT_SELECT) != 0;
}
/* do point... */
if (point_ok) {
copy_v3_v3(td->iloc, &pt->x);
- /* only copy center in local origins.
+ /* Only copy center in local origins.
* This allows get interesting effects also when move
- * using proportional editing */
+ * using proportional editing. */
if ((gps->flag & GP_STROKE_SELECT) &&
(ts->transform_pivot_point == V3D_AROUND_LOCAL_ORIGINS)) {
copy_v3_v3(td->center, center);
@@ -316,8 +307,8 @@ void createTransGPencil(bContext *C, TransInfo *t)
td->flag |= TD_SELECTED;
}
- /* for other transform modes (e.g. shrink-fatten), need to additional data
- * but never for mirror
+ /* For other transform modes (e.g. shrink-fatten), need to additional data
+ * but never for mirror.
*/
if (t->mode != TFM_MIRROR) {
if (t->mode != TFM_GPENCIL_OPACITY) {
@@ -363,8 +354,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
/* March over these points, and calculate the proportional editing distances */
if (is_prop_edit && (head != tail)) {
- /* XXX: for now, we are similar enough that this works... */
- calc_distanceCurveVerts(head, tail - 1);
+ calc_distanceCurveVerts(head, tail - 1, false);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index b73a4c1ab2d..e564733266b 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -53,6 +53,7 @@ void createTransLatticeVerts(TransInfo *t)
int a;
int count = 0, countsel = 0;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+ const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
bp = latt->def;
a = latt->pntsu * latt->pntsv * latt->pntsw;
@@ -68,9 +69,10 @@ void createTransLatticeVerts(TransInfo *t)
bp++;
}
- /* note: in prop mode we need at least 1 selected */
- if (countsel == 0) {
- return;
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
+ tc->data_len = 0;
+ continue;
}
if (is_prop_edit) {
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index 7be0d43d854..6f34c49bdac 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -30,7 +30,6 @@
#include "BKE_context.h"
#include "BKE_mask.h"
-#include "BKE_report.h"
#include "ED_clip.h"
#include "ED_image.h"
@@ -150,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_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 447733357d6..ce5e4dade5a 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -47,6 +47,7 @@ void createTransMBallVerts(TransInfo *t)
float mtx[3][3], smtx[3][3];
int count = 0, countsel = 0;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+ const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
/* count totals */
for (ml = mb->editelems->first; ml; ml = ml->next) {
@@ -58,8 +59,9 @@ void createTransMBallVerts(TransInfo *t)
}
}
- /* note: in prop mode we need at least 1 selected */
- if (countsel == 0) {
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
+ tc->data_len = 0;
continue;
}
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 598604eac0d..2e0f7a3a113 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -28,6 +28,7 @@
#include "BLI_alloca.h"
#include "BLI_bitmap.h"
+#include "BLI_ghash.h"
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
@@ -50,10 +51,13 @@
/* Own include. */
#include "transform_convert.h"
-/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */
-#define TRANSFORM_MAXDIST_MIRROR 0.00002f
+#define USE_FACE_SUBSTITUTE
+
+/* -------------------------------------------------------------------- */
+/** \name Island Creation
+ *
+ * \{ */
-/* when transforming islands */
struct TransIslandData {
float (*center)[3];
float (*axismtx)[3][3];
@@ -61,8 +65,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 +425,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 +459,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 +592,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 +613,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,54 +663,23 @@ 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.
*
* \note ignore modes here, even in edge/face modes,
* transform data is created by selected vertices.
- * \note in prop mode we need at least 1 selected.
*/
- if (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);
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
+ continue;
}
int data_len = 0;
@@ -733,40 +689,41 @@ void createTransEditVerts(TransInfo *t)
data_len++;
}
}
-
- /* 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);
@@ -774,26 +731,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));
@@ -808,16 +797,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);
@@ -831,114 +821,127 @@ 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) {
if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
continue;
}
- else {
- int island_index = -1;
- if (island_data.island_vert_map) {
- const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
- island_index = island_data.island_vert_map[connected_index];
+
+ int island_index = -1;
+ if (island_data.island_vert_map) {
+ const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
+ island_index = island_data.island_vert_map[connected_index];
+ }
+
+ 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;
}
- 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);
+ 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++;
+ }
+ else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ float *bweight = (cd_vert_bweight_offset != -1) ?
+ BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) :
+ NULL;
+
+ /* 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);
+ if (tx) {
+ tx++;
+ }
- td_mirror++;
+ /* selected */
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ tob->flag |= TD_SELECTED;
}
- else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- float *bweight = (cd_vert_bweight_offset != -1) ?
- BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) :
- NULL;
-
- /* 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);
- if (tx) {
- tx++;
- }
- /* selected */
- if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- tob->flag |= TD_SELECTED;
+ if (prop_mode) {
+ if (prop_mode & T_PROP_CONNECTED) {
+ tob->dist = dists[a];
}
-
- if (prop_mode) {
- if (prop_mode & T_PROP_CONNECTED) {
- tob->dist = dists[a];
- }
- else {
- tob->flag |= TD_NOTCONNECTED;
- tob->dist = FLT_MAX;
- }
+ else {
+ tob->flag |= TD_NOTCONNECTED;
+ tob->dist = FLT_MAX;
}
+ }
- /* CrazySpace */
- const bool use_quats = quats && BM_elem_flag_test(eve, BM_ELEM_TAG);
- if (use_quats || defmats) {
- float mat[3][3], qmat[3][3], imat[3][3];
+ /* CrazySpace */
+ const bool use_quats = quats && BM_elem_flag_test(eve, BM_ELEM_TAG);
+ if (use_quats || defmats) {
+ float mat[3][3], qmat[3][3], imat[3][3];
- /* Use both or either quat and defmat correction. */
- if (use_quats) {
- quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]);
+ /* Use both or either quat and defmat correction. */
+ if (use_quats) {
+ quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]);
- if (defmats) {
- mul_m3_series(mat, defmats[a], qmat, mtx);
- }
- else {
- mul_m3_m3m3(mat, mtx, qmat);
- }
+ if (defmats) {
+ mul_m3_series(mat, defmats[a], qmat, mtx);
}
else {
- mul_m3_m3m3(mat, mtx, defmats[a]);
+ mul_m3_m3m3(mat, mtx, qmat);
}
-
- invert_m3_m3(imat, mat);
-
- copy_m3_m3(tob->smtx, imat);
- copy_m3_m3(tob->mtx, mat);
}
else {
- copy_m3_m3(tob->smtx, smtx);
- copy_m3_m3(tob->mtx, mtx);
+ mul_m3_m3m3(mat, mtx, defmats[a]);
}
- if (tc->use_mirror_axis_any) {
- if (tc->use_mirror_axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) {
- tob->flag |= TD_MIRROR_EDGE_X;
- }
- if (tc->use_mirror_axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) {
- tob->flag |= TD_MIRROR_EDGE_Y;
- }
- if (tc->use_mirror_axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) {
- tob->flag |= TD_MIRROR_EDGE_Z;
- }
- }
+ invert_m3_m3(imat, mat);
- tob++;
+ copy_m3_m3(tob->smtx, imat);
+ copy_m3_m3(tob->mtx, mat);
+ }
+ else {
+ copy_m3_m3(tob->smtx, smtx);
+ copy_m3_m3(tob->mtx, mtx);
}
+
+ if (tc->use_mirror_axis_any) {
+ if (tc->use_mirror_axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) {
+ tob->flag |= TD_MIRROR_EDGE_X;
+ }
+ if (tc->use_mirror_axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) {
+ tob->flag |= TD_MIRROR_EDGE_Y;
+ }
+ if (tc->use_mirror_axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) {
+ tob->flag |= TD_MIRROR_EDGE_Z;
+ }
+ }
+
+ tob++;
}
}
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);
}
@@ -951,50 +954,50 @@ void createTransEditVerts(TransInfo *t)
if (dists_index) {
MEM_freeN(dists_index);
}
- if (mirror_bitmap) {
- MEM_freeN(mirror_bitmap);
- }
}
}
/** \} */
/* -------------------------------------------------------------------- */
-/** \name CustomData Layer Correction (for meshes)
+/** \name CustomData Layer Correction
*
* \{ */
-struct TransCustomDataLayerVert {
- BMVert *v;
- float co_orig_3d[3];
+struct TransCustomDataMergeGroup {
+ /** map {BMVert: TransCustomDataLayerVert} */
struct LinkNode **cd_loop_groups;
};
struct TransCustomDataLayer {
BMesh *bm;
+ struct MemArena *arena;
- int cd_loop_mdisp_offset;
-
- /** map {BMVert: TransCustomDataLayerVert} */
- struct GHash *origverts;
struct GHash *origfaces;
struct BMesh *bm_origfaces;
- struct MemArena *arena;
- /** Number of math BMLoop layers. */
- int layer_math_map_num;
- /** Array size of 'layer_math_map_num'
- * maps TransCustomDataLayerVert.cd_group index to absolute CustomData layer index */
- int *layer_math_map;
-
- /* Array with all elements transformed. */
- struct TransCustomDataLayerVert *data;
- int data_len;
+ /* Special handle for multi-resolution. */
+ int cd_loop_mdisp_offset;
+
+ /* Optionally merge custom-data groups (this keeps UVs connected for example). */
+ struct {
+ /** map {BMVert: TransDataBasic} */
+ struct GHash *origverts;
+ struct TransCustomDataMergeGroup *data;
+ int data_len;
+ /** Array size of 'layer_math_map_len'
+ * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
+ int *customdatalayer_map;
+ /** Number of math BMLoop layers. */
+ int customdatalayer_map_len;
+ } merge_group;
+
+ bool use_merge_group;
};
-static void trans_mesh_customdata_free_cb(struct TransInfo *UNUSED(t),
- struct TransDataContainer *UNUSED(tc),
- struct TransCustomData *custom_data)
+static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t),
+ struct TransDataContainer *UNUSED(tc),
+ struct TransCustomData *custom_data)
{
struct TransCustomDataLayer *tcld = custom_data->data;
bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
@@ -1005,171 +1008,290 @@ static void trans_mesh_customdata_free_cb(struct TransInfo *UNUSED(t),
if (tcld->origfaces) {
BLI_ghash_free(tcld->origfaces, NULL, NULL);
}
- if (tcld->origverts) {
- BLI_ghash_free(tcld->origverts, NULL, NULL);
+ if (tcld->merge_group.origverts) {
+ BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
}
if (tcld->arena) {
BLI_memarena_free(tcld->arena);
}
- if (tcld->layer_math_map) {
- MEM_freeN(tcld->layer_math_map);
+ if (tcld->merge_group.customdatalayer_map) {
+ MEM_freeN(tcld->merge_group.customdatalayer_map);
}
MEM_freeN(tcld);
custom_data->data = NULL;
}
-static void create_trans_vert_customdata_layer(BMVert *v,
- struct TransCustomDataLayer *tcld,
- struct TransCustomDataLayerVert *r_tcld_vert)
+#ifdef USE_FACE_SUBSTITUTE
+
+# define FACE_SUBSTITUTE_INDEX INT_MIN
+
+/**
+ * Search for a neighboring face with area and preferably without selected vertex.
+ * Used to replace area-less faces in custom-data correction.
+ */
+static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
+{
+ BMFace *best_face = NULL;
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BMLoop *l_radial_next = l->radial_next;
+ BMFace *f_test = l_radial_next->f;
+ if (f_test == f) {
+ continue;
+ }
+ if (is_zero_v3(f_test->no)) {
+ continue;
+ }
+
+ /* Check the loops edge isn't selected. */
+ if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
+ !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
+ /* Prefer edges with unselected vertices.
+ * Useful for extrude. */
+ best_face = f_test;
+ break;
+ }
+ if (best_face == NULL) {
+ best_face = f_test;
+ }
+ }
+ return best_face;
+}
+
+static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
+ BMFace *f,
+ BMFace *f_copy)
+{
+ BLI_assert(is_zero_v3(f->no));
+ BMesh *bm = tcld->bm;
+ /* It is impossible to calculate the loops weights of a face without area.
+ * Find a substitute. */
+ BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f);
+ if (f_substitute) {
+ /* Copy the custom-data from the substitute face. */
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
+ } while ((l_iter = l_iter->next) != l_first);
+
+ /* Use the substitute face as the reference during the transformation. */
+ BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
+
+ /* Hack: reference substitute face in `f_copy->no`.
+ * `tcld->origfaces` is already used to restore the initial value. */
+ BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
+ *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
+ }
+}
+
+static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
+{
+ BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
+ return *((BMFace **)&f_copy->no[0]);
+}
+
+#endif /* USE_FACE_SUBSTITUTE */
+
+static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ const int index)
{
BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
BMIter liter;
int j, l_num;
float *loop_weights;
- /* copy face data */
// BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
l_num = liter.count;
- loop_weights = BLI_array_alloca(loop_weights, l_num);
+ loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
for (j = 0; j < l_num; j++) {
BMLoop *l = BM_iter_step(&liter);
BMLoop *l_prev, *l_next;
+
+ /* Generic custom-data correction. Copy face data. */
void **val_p;
if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
*val_p = f_copy;
+#ifdef USE_FACE_SUBSTITUTE
+ if (is_zero_v3(l->f->no)) {
+ mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
+ }
+#endif
}
- if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
- (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
- loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
- }
- else {
- loop_weights[j] = 0.0f;
+ if (tcld->use_merge_group) {
+ if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
+ (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
+ loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
}
}
- /* store cd_loop_groups */
- if (tcld->layer_math_map_num && (l_num != 0)) {
- r_tcld_vert->cd_loop_groups = BLI_memarena_alloc(tcld->arena,
- tcld->layer_math_map_num * sizeof(void *));
- for (j = 0; j < tcld->layer_math_map_num; j++) {
- const int layer_nr = tcld->layer_math_map[j];
- r_tcld_vert->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
- bm, v, layer_nr, loop_weights, tcld->arena);
+ if (tcld->use_merge_group) {
+ /* Store cd_loop_groups. */
+ struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
+ if (l_num != 0) {
+ merge_data->cd_loop_groups = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ const int layer_nr = tcld->merge_group.customdatalayer_map[j];
+ merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
+ bm, v, layer_nr, loop_weights, tcld->arena);
+ }
}
+ else {
+ merge_data->cd_loop_groups = NULL;
+ }
+
+ BLI_ghash_insert(tcld->merge_group.origverts, v, td);
}
- else {
- r_tcld_vert->cd_loop_groups = NULL;
- }
+}
+
+static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+
+ 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 custom-data. */
+ BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL);
+ tcld->origfaces = origfaces;
+ tcld->bm_origfaces = bm_origfaces;
- r_tcld_vert->v = v;
- copy_v3_v3(r_tcld_vert->co_orig_3d, v->co);
- BLI_ghash_insert(tcld->origverts, v, r_tcld_vert);
+ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+ tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
}
-void trans_mesh_customdata_correction_init(TransInfo *t)
+static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
+ struct TransCustomDataLayer *tcld)
{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->custom.type.data) {
- if (tc->custom.type.free_cb == trans_mesh_customdata_free_cb) {
- /* Custom data correction has initiated before. */
- continue;
- }
- else {
- BLI_assert(false);
- }
- }
- int i;
+ BMesh *bm = tcld->bm;
+ BLI_assert(CustomData_has_math(&bm->ldata));
- BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
- BMesh *bm = em->bm;
+ /* TODO: We don't need `layer_math_map` when there are no loops linked
+ * to one of the sliding vertices. */
- 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;
- }
+ /* Over allocate, only 'math' layers are indexed. */
+ int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
+ int layer_math_map_len = 0;
+ for (int i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ customdatalayer_map[layer_math_map_len++] = i;
}
+ }
+ BLI_assert(layer_math_map_len != 0);
+
+ tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
+ tcld->merge_group.customdatalayer_map = customdatalayer_map;
+ tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
+ tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
+ tcld->merge_group.data = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
+}
- if (use_origfaces) {
- /* create copies of faces for customdata projection */
- bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+static void mesh_customdatacorrect_init_container(TransDataContainer *tc,
+ const bool use_merge_group)
+{
+ if (tc->custom.type.data) {
+ /* The custom-data correction has been initiated before.
+ * Free since some modes have different settings. */
+ mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type);
+ }
- struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
- struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ BMesh *bm = em->bm;
- /* we need to have matching customdata */
- BM_mesh_copy_init_customdata(bm_origfaces, bm, NULL);
+ 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 custom-data projection. */
+ return;
+ }
+ if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
+ /* There is no custom-data to correct. */
+ return;
+ }
- 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;
- }
- }
- BLI_assert(layer_index_dst != 0);
- }
- }
+ struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
+ tcld->bm = bm;
+ tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
- 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__);
-
- int data_len = tc->data_len + tc->data_mirror_len;
- struct GHash *origverts = BLI_ghash_ptr_new_ex(__func__, data_len);
- tcld->origverts = origverts;
-
- 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];
-
- TransData *tob;
- for (i = tc->data_len, tob = tc->data; i--; tob++, tcld_vert_iter++) {
- BMVert *v = tob->extra;
- create_trans_vert_customdata_layer(v, tcld, tcld_vert_iter);
- }
+ /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
+ tcld->cd_loop_mdisp_offset = -1;
+ tcld->use_merge_group = use_merge_group;
- TransDataMirror *td_mirror = tc->data_mirror;
- for (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);
- }
+ mesh_customdatacorrect_init_container_generic(tc, tcld);
+
+ if (tcld->use_merge_group) {
+ mesh_customdatacorrect_init_container_merge_group(tc, tcld);
+ }
- tcld->data = tcld_vert;
- tcld->data_len = data_len;
+ {
+ /* Setup Verts. */
+ int i = 0;
+
+ TransData *tob = tc->data;
+ for (int j = tc->data_len; j--; tob++, i++) {
+ mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
+ mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
}
}
+
+ tc->custom.type.data = tcld;
+ tc->custom.type.free_cb = mesh_customdatacorrect_free_cb;
+}
+
+void mesh_customdatacorrect_init(TransInfo *t)
+{
+ bool use_merge_group = false;
+ if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = true;
+ }
+ else if (ELEM(t->mode,
+ TFM_TRANSLATION,
+ TFM_ROTATION,
+ TFM_RESIZE,
+ TFM_TOSPHERE,
+ TFM_SHEAR,
+ TFM_BEND,
+ TFM_SHRINKFATTEN,
+ TFM_TRACKBALL,
+ TFM_PUSHPULL,
+ TFM_ALIGN)) {
+ {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
+ }
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ mesh_customdatacorrect_init_container(tc, use_merge_group);
+ }
}
/**
@@ -1177,30 +1299,29 @@ void trans_mesh_customdata_correction_init(TransInfo *t)
*/
static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
{
- struct TransCustomDataLayerVert *tcld_vert = BLI_ghash_lookup(tcld->origverts, v);
- return tcld_vert ? tcld_vert->co_orig_3d : v->co;
+ TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
+ return td ? td->iloc : v->co;
}
-static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLayer *tcld,
- struct TransCustomDataLayerVert *tcld_vert,
- bool is_final)
+static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ struct TransCustomDataMergeGroup *merge_data,
+ bool do_loop_mdisps)
{
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;
+ BMVert *v = td->extra;
+ const float *co_orig_3d = td->iloc;
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->merge_group.customdatalayer_map_len;
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);
}
@@ -1214,6 +1335,14 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+#ifdef USE_FACE_SUBSTITUTE
+ /* In some faces it is not possible to calculate interpolation,
+ * so we use a substitute. */
+ if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
+ f_copy = mesh_customdatacorrect_face_substitute_get(f_copy);
+ }
+#endif
+
/* only loop data, no vertex data since that contains shape keys,
* and we do not want to mess up other shape keys */
BM_loop_interp_from_face(bm, l, f_copy, false, false);
@@ -1265,16 +1394,20 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
}
}
- if (tcld->layer_math_map_num && cd_loop_groups) {
- if (do_loop_weight) {
- for (j = 0; j < tcld->layer_math_map_num; j++) {
- BM_vert_loop_groups_data_layer_merge_weights(
- bm, cd_loop_groups[j], tcld->layer_math_map[j], loop_weights);
+ if (tcld->use_merge_group) {
+ struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
+ if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
+ if (do_loop_weight) {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge_weights(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ }
}
- }
- else {
- for (j = 0; j < tcld->layer_math_map_num; j++) {
- BM_vert_loop_groups_data_layer_merge(bm, cd_loop_groups[j], tcld->layer_math_map[j]);
+ else {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ }
}
}
}
@@ -1284,7 +1417,8 @@ 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.
* */
- if (do_loop_mdisps) {
+ const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
+ if (update_loop_mdisps) {
float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
BMLoop *l;
@@ -1313,19 +1447,63 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
}
}
-void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc, bool is_final)
+static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final)
{
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- if (!tcld) {
- return;
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (!tc->custom.type.data) {
+ continue;
+ }
+ struct TransCustomDataLayer *tcld = tc->custom.type.data;
+ const bool use_merge_group = tcld->use_merge_group;
+
+ struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
+ TransData *tob = tc->data;
+ for (int i = tc->data_len; i--; tob++) {
+ mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int i = tc->data_mirror_len; i--; td_mirror++) {
+ mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
}
+}
- const bool has_mdisps = (tcld->cd_loop_mdisp_offset != -1);
- struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0];
+static void mesh_customdatacorrect_restore(struct TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ struct TransCustomDataLayer *tcld = tc->custom.type.data;
+ if (!tcld) {
+ continue;
+ }
- for (int i = tcld->data_len; i--; tcld_vert_iter++) {
- if (tcld_vert_iter->cd_loop_groups || has_mdisps) {
- trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, is_final);
+ BMesh *bm = tcld->bm;
+ BMesh *bm_copy = tcld->bm_origfaces;
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tcld->origfaces) {
+ BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
+ BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
+ BLI_assert(f->len == f_copy->len);
+
+ BMLoop *l_iter, *l_first, *l_copy;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ l_copy = BM_FACE_FIRST_LOOP(f_copy);
+ do {
+ /* TODO: Restore only the elements that transform. */
+ BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
+ l_copy = l_copy->next;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ BM_elem_attrs_copy_ex(bm_copy, bm, f_copy, f, BM_ELEM_SELECT, CD_MASK_NORMAL);
}
}
}
@@ -1376,8 +1554,9 @@ static void transform_apply_to_mirror(TransInfo *t)
void recalcData_mesh(TransInfo *t)
{
+ bool is_cancelling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
- if (t->state != TRANS_CANCEL) {
+ if (!is_cancelling) {
/* apply clipping after so we never project past the clip plane [#25423] */
applyProject(t);
clipMirrorModifier(t);
@@ -1385,13 +1564,11 @@ void recalcData_mesh(TransInfo *t)
if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
transform_apply_to_mirror(t);
}
- }
- if (t->mode == TFM_EDGE_SLIDE) {
- projectEdgeSlideData(t, false);
+ mesh_customdatacorrect_apply(t, false);
}
- else if (t->mode == TFM_VERT_SLIDE) {
- projectVertSlideData(t, false);
+ else {
+ mesh_customdatacorrect_restore(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -1409,22 +1586,18 @@ void recalcData_mesh(TransInfo *t)
void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
{
- const bool canceled = (t->state == TRANS_CANCEL);
- if (t->mode == TFM_EDGE_SLIDE) {
- /* handle multires re-projection, done
+ const bool is_cancelling = (t->state == TRANS_CANCEL);
+ const bool use_automerge = !is_cancelling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
+
+ if (!is_cancelling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
+ /* Handle multires re-projection, done
* on transform completion since it's
- * really slow -joeedh */
- projectEdgeSlideData(t, !canceled);
- }
- else if (t->mode == TFM_VERT_SLIDE) {
- /* as above */
- projectVertSlideData(t, !canceled);
+ * really slow -joeedh. */
+ mesh_customdatacorrect_apply(t, true);
}
- 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_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c
index de90c515042..febfa4cd367 100644
--- a/source/blender/editors/transform/transform_convert_mesh_edge.c
+++ b/source/blender/editors/transform/transform_convert_mesh_edge.c
@@ -50,6 +50,7 @@ void createTransEdge(TransInfo *t)
float mtx[3][3], smtx[3][3];
int count = 0, countsel = 0;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+ const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
int cd_edge_float_offset;
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
@@ -63,7 +64,7 @@ void createTransEdge(TransInfo *t)
}
}
- if (countsel == 0) {
+ if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
tc->data_len = 0;
continue;
}
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index 337f7a83f9e..632769c167e 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_bitmap.h"
+#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BKE_context.h"
@@ -51,12 +52,13 @@ static void UVsToTransData(const float aspect[2],
TransData2D *td2d,
float *uv,
const float *center,
+ float calc_dist,
bool selected)
{
- /* uv coords are scaled by aspects. this is needed for rotations and
- * proportional editing to be consistent with the stretched uv coords
- * that are displayed. this also means that for display and numinput,
- * and when the uv coords are flushed, these are converted each time */
+ /* UV coords are scaled by aspects. this is needed for rotations and
+ * proportional editing to be consistent with the stretched UV coords
+ * that are displayed. this also means that for display and number-input,
+ * and when the UV coords are flushed, these are converted each time. */
td2d->loc[0] = uv[0] * aspect[0];
td2d->loc[1] = uv[1] * aspect[1];
td2d->loc[2] = 0.0f;
@@ -79,12 +81,175 @@ static void UVsToTransData(const float aspect[2],
td->dist = 0.0;
}
else {
- td->dist = FLT_MAX;
+ td->dist = calc_dist;
}
unit_m3(td->mtx);
unit_m3(td->smtx);
}
+/**
+ * \param dists: Store the closest connected distance to selected vertices.
+ */
+static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float aspect[2])
+{
+ /* Mostly copied from #editmesh_set_connectivity_distance. */
+ BLI_LINKSTACK_DECLARE(queue, BMLoop *);
+
+ /* Any BM_ELEM_TAG'd loop is added to 'queue_next', this makes sure that we don't add things
+ * twice. */
+ BLI_LINKSTACK_DECLARE(queue_next, BMLoop *);
+
+ BLI_LINKSTACK_INIT(queue);
+ BLI_LINKSTACK_INIT(queue_next);
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ BMIter fiter, liter;
+ BMVert *f;
+ BMLoop *l;
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ /* Visible faces was tagged in #createTransUVs. */
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ float dist;
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+ bool uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+
+ if (uv_vert_sel) {
+ BLI_LINKSTACK_PUSH(queue, l);
+ dist = 0.0f;
+ }
+ else {
+ dist = FLT_MAX;
+ }
+
+ /* Make sure all loops are in a clean tag state. */
+ BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
+
+ int loop_idx = BM_elem_index_get(l);
+
+ dists[loop_idx] = dist;
+ }
+ }
+
+ /* Need to be very careful of feedback loops here, store previous dist's to avoid feedback. */
+ float *dists_prev = MEM_dupallocN(dists);
+
+ do {
+ while ((l = BLI_LINKSTACK_POP(queue))) {
+ BLI_assert(dists[BM_elem_index_get(l)] != FLT_MAX);
+
+ BMLoop *l_other, *l_connected;
+ BMIter l_connected_iter;
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ float l_uv[2];
+
+ copy_v2_v2(l_uv, luv->uv);
+ mul_v2_v2(l_uv, aspect);
+
+ BM_ITER_ELEM (l_other, &liter, l->f, BM_LOOPS_OF_FACE) {
+ if (l_other == l) {
+ continue;
+ }
+ float other_uv[2], edge_vec[2];
+ MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_other, cd_loop_uv_offset);
+
+ copy_v2_v2(other_uv, luv_other->uv);
+ mul_v2_v2(other_uv, aspect);
+
+ sub_v2_v2v2(edge_vec, l_uv, other_uv);
+
+ const int i = BM_elem_index_get(l);
+ const int i_other = BM_elem_index_get(l_other);
+ float dist = len_v2(edge_vec) + dists_prev[i];
+
+ if (dist < dists[i_other]) {
+ dists[i_other] = dist;
+ }
+ else {
+ /* The face loop already has a shorter path to it. */
+ continue;
+ }
+
+ bool other_vert_sel, connected_vert_sel;
+
+ other_vert_sel = luv_other->flag & MLOOPUV_VERTSEL;
+
+ BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) {
+ if (l_connected == l_other) {
+ continue;
+ }
+ /* Visible faces was tagged in #createTransUVs. */
+ if (!BM_elem_flag_test(l_connected->f, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ MLoopUV *luv_connected = BM_ELEM_CD_GET_VOID_P(l_connected, cd_loop_uv_offset);
+ connected_vert_sel = luv_connected->flag & MLOOPUV_VERTSEL;
+
+ /* Check if this loop is connected in UV space.
+ * If the uv loops share the same selection state (if not, they are not connected as
+ * they have been ripped or other edit commands have separated them). */
+ bool connected = other_vert_sel == connected_vert_sel &&
+ equals_v2v2(luv_other->uv, luv_connected->uv);
+ if (!connected) {
+ continue;
+ }
+
+ /* The loop vert is occupying the same space, so it has the same distance. */
+ const int i_connected = BM_elem_index_get(l_connected);
+ dists[i_connected] = dist;
+
+ if (BM_elem_flag_test(l_connected, BM_ELEM_TAG) == 0) {
+ BM_elem_flag_enable(l_connected, BM_ELEM_TAG);
+ BLI_LINKSTACK_PUSH(queue_next, l_connected);
+ }
+ }
+ }
+ }
+
+ /* Clear elem flags for the next loop. */
+ for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
+ BMLoop *l_link = lnk->link;
+ const int i = BM_elem_index_get(l_link);
+
+ BM_elem_flag_disable(l_link, BM_ELEM_TAG);
+
+ /* Store all new dist values. */
+ dists_prev[i] = dists[i];
+ }
+
+ BLI_LINKSTACK_SWAP(queue, queue_next);
+
+ } while (BLI_LINKSTACK_SIZE(queue));
+
+#ifndef NDEBUG
+ /* Check that we didn't leave any loops tagged */
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ /* Visible faces was tagged in #createTransUVs. */
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
+ }
+ }
+#endif
+
+ BLI_LINKSTACK_FREE(queue);
+ BLI_LINKSTACK_FREE(queue_next);
+
+ MEM_freeN(dists_prev);
+}
+
void createTransUVs(bContext *C, TransInfo *t)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -103,12 +268,11 @@ void createTransUVs(bContext *C, TransInfo *t)
BMFace *efa;
BMIter iter, liter;
UvElementMap *elementmap = NULL;
- BLI_bitmap *island_enabled = NULL;
struct {
float co[2];
int co_num;
} *island_center = NULL;
- int count = 0, countsel = 0, count_rejected = 0;
+ int count = 0, countsel = 0;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
if (!ED_space_image_show_uvedit(sima, tc->obedit)) {
@@ -116,22 +280,15 @@ void createTransUVs(bContext *C, TransInfo *t)
}
/* count */
- if (is_prop_connected || is_island_center) {
+ if (is_island_center) {
/* create element map with island information */
const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
- const bool use_uvsel = !is_prop_connected;
- elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, use_uvsel, false, true);
+ elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true);
if (elementmap == NULL) {
continue;
}
- if (is_prop_connected) {
- island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)");
- }
-
- if (is_island_center) {
- island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
- }
+ island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -147,20 +304,14 @@ void createTransUVs(bContext *C, TransInfo *t)
if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
countsel++;
- if (is_prop_connected || island_center) {
+ if (island_center) {
UvElement *element = BM_uv_element_get(elementmap, efa, l);
- if (is_prop_connected) {
- BLI_BITMAP_ENABLE(island_enabled, element->island);
- }
-
- if (is_island_center) {
- if (element->flag == false) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- add_v2_v2(island_center[element->island].co, luv->uv);
- island_center[element->island].co_num++;
- element->flag = true;
- }
+ if (element->flag == false) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ add_v2_v2(island_center[element->island].co, luv->uv);
+ island_center[element->island].co_num++;
+ element->flag = true;
}
}
}
@@ -171,8 +322,8 @@ void createTransUVs(bContext *C, TransInfo *t)
}
}
- /* note: in prop mode we need at least 1 selected */
- if (countsel == 0) {
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
goto finally;
}
@@ -198,6 +349,14 @@ void createTransUVs(bContext *C, TransInfo *t)
td = tc->data;
td2d = tc->data_2d;
+ float *prop_dists = NULL;
+
+ if (is_prop_connected) {
+ prop_dists = MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)");
+
+ uv_set_connectivity_distance(em->bm, prop_dists, t->aspect);
+ }
+
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BMLoop *l;
@@ -209,52 +368,41 @@ void createTransUVs(bContext *C, TransInfo *t)
const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
MLoopUV *luv;
const float *center = NULL;
+ float prop_distance = FLT_MAX;
if (!is_prop_edit && !selected) {
continue;
}
- if (is_prop_connected || is_island_center) {
+ if (is_prop_connected) {
+ const int idx = BM_elem_index_get(l);
+ prop_distance = prop_dists[idx];
+ }
+
+ if (is_island_center) {
UvElement *element = BM_uv_element_get(elementmap, efa, l);
if (element) {
- if (is_prop_connected) {
- if (!BLI_BITMAP_TEST(island_enabled, element->island)) {
- count_rejected++;
- continue;
- }
- }
-
- if (is_island_center) {
- center = island_center[element->island].co;
- }
+ center = island_center[element->island].co;
}
}
- BM_elem_flag_enable(l, BM_ELEM_TAG);
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected);
+ UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, prop_distance, selected);
}
}
- if (is_prop_connected) {
- tc->data_len -= count_rejected;
- }
-
if (sima->flag & SI_LIVE_UNWRAP) {
ED_uvedit_live_unwrap_begin(t->scene, tc->obedit);
}
finally:
- if (is_prop_connected || is_island_center) {
+ if (is_prop_connected) {
+ MEM_freeN(prop_dists);
+ }
+ if (is_island_center) {
BM_uv_element_map_free(elementmap);
- if (is_prop_connected) {
- MEM_freeN(island_enabled);
- }
-
- if (island_center) {
- MEM_freeN(island_center);
- }
+ MEM_freeN(island_center);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c
index 03da979bbd3..241e65f3251 100644
--- a/source/blender/editors/transform/transform_convert_nla.c
+++ b/source/blender/editors/transform/transform_convert_nla.c
@@ -31,7 +31,6 @@
#include "BKE_context.h"
#include "BKE_nla.h"
-#include "BKE_report.h"
#include "ED_anim_api.h"
#include "ED_markers.h"
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index 92ed477eb25..2e92b4e5c09 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -794,7 +794,9 @@ static void autokeyframe_object(
ToolSettings *ts = scene->toolsettings;
KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
ListBase dsources = {NULL, NULL};
- float cfra = (float)CFRA; // xxx this will do for now
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ (float)CFRA);
eInsertKeyFlags flag = 0;
/* Get flags used for inserting keyframes. */
@@ -808,7 +810,8 @@ static void autokeyframe_object(
* NOTE: we assume here that the active Keying Set
* does not need to have its iterator overridden.
*/
- ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
AnimData *adt = ob->adt;
@@ -824,7 +827,7 @@ static void autokeyframe_object(
(fcu->grp ? fcu->grp->name : NULL),
fcu->rna_path,
fcu->array_index,
- cfra,
+ &anim_eval_context,
ts->keyframe_type,
&nla_cache,
flag);
@@ -872,21 +875,25 @@ static void autokeyframe_object(
/* insert keyframes for the affected sets of channels using the builtin KeyingSets found */
if (do_loc) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
if (do_rot) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
if (do_scale) {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
}
/* insert keyframe in all (transform) channels */
else {
KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
- ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ ANIM_apply_keyingset(
+ C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
/* free temp info */
diff --git a/source/blender/editors/transform/transform_convert_paintcurve.c b/source/blender/editors/transform/transform_convert_paintcurve.c
index 4dbe57fc143..47859896673 100644
--- a/source/blender/editors/transform/transform_convert_paintcurve.c
+++ b/source/blender/editors/transform/transform_convert_paintcurve.c
@@ -154,13 +154,11 @@ void createTransPaintCurveVerts(bContext *C, TransInfo *t)
total += 3;
continue;
}
- else {
- if (pcp->bez.f1 & SELECT) {
- total++;
- }
- if (pcp->bez.f3 & SELECT) {
- total++;
- }
+ if (pcp->bez.f1 & SELECT) {
+ total++;
+ }
+ if (pcp->bez.f3 & SELECT) {
+ total++;
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c
index 5feaa70ba19..fbe9c07ebe9 100644
--- a/source/blender/editors/transform/transform_convert_particle.c
+++ b/source/blender/editors/transform/transform_convert_particle.c
@@ -183,7 +183,7 @@ void createTransParticleVerts(bContext *C, TransInfo *t)
tail++;
}
if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
+ calc_distanceCurveVerts(head, tail - 1, false);
}
}
}
@@ -250,7 +250,7 @@ static void flushTransParticles(TransInfo *t)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Recalc Trasform Particles Data
+/** \name Recalc Transform Particles Data
*
* \{ */
diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c
index 36c6116b575..c3f48adca68 100644
--- a/source/blender/editors/transform/transform_convert_tracking.c
+++ b/source/blender/editors/transform/transform_convert_tracking.c
@@ -31,7 +31,6 @@
#include "BKE_context.h"
#include "BKE_movieclip.h"
#include "BKE_node.h"
-#include "BKE_report.h"
#include "BKE_tracking.h"
#include "ED_clip.h"
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..8f590d87f4c 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;
+ }
}
}
}
@@ -1130,7 +1116,7 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
if (t->spacetype != SPACE_VIEW3D) {
return false;
}
- else if (tc->obedit) {
+ if (tc->obedit) {
if (ED_object_calc_active_center_for_editmode(tc->obedit, select_only, r_center)) {
mul_m4_v3(tc->obedit->obmat, r_center);
return true;
diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c
index c63e90ac2b7..dacdb72806c 100644
--- a/source/blender/editors/transform/transform_gizmo_2d.c
+++ b/source/blender/editors/transform/transform_gizmo_2d.c
@@ -26,14 +26,12 @@
#include "BLI_math.h"
-#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "BKE_context.h"
-#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "RNA_access.h"
@@ -44,7 +42,6 @@
#include "WM_api.h"
#include "WM_message.h"
#include "WM_types.h"
-#include "wm.h" /* XXX */
#include "ED_gizmo_library.h"
#include "ED_gizmo_utils.h"
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 4c561037986..3878103fa4e 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -39,7 +39,6 @@
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
-#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_action.h"
@@ -48,25 +47,20 @@
#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
-#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_paint.h"
-#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_scene.h"
-#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_message.h"
-#include "WM_toolsystem.h"
#include "WM_types.h"
#include "wm.h"
#include "ED_armature.h"
-#include "ED_curve.h"
#include "ED_gizmo_library.h"
#include "ED_gizmo_utils.h"
#include "ED_gpencil.h"
@@ -90,8 +84,6 @@
#include "GPU_state.h"
-#include "DEG_depsgraph_query.h"
-
/* return codes for select, and drawing flags */
#define MAN_TRANS_X (1 << 0)
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index e6e74668c2c..7f6f3e53bc3 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -38,7 +38,6 @@
#include "ED_gizmo_utils.h"
#include "ED_screen.h"
#include "ED_transform.h"
-#include "ED_view3d.h"
#include "UI_resources.h"
diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c
index 078020e932e..9c8d99d0d24 100644
--- a/source/blender/editors/transform/transform_input.c
+++ b/source/blender/editors/transform/transform_input.c
@@ -124,8 +124,8 @@ static void InputVerticalRatio(TransInfo *t, MouseInput *mi, const double mval[2
{
const int winy = t->region ? t->region->winy : 1;
- /* Flip so dragging up increases (matching viewport zoom). */
- output[0] = ((mval[1] - mi->imval[1]) / winy) * -2.0f;
+ /* Dragging up increases (matching viewport zoom). */
+ output[0] = ((mval[1] - mi->imval[1]) / winy) * 2.0f;
}
/** Callback for #INPUT_VERTICAL_ABSOLUTE */
@@ -139,8 +139,8 @@ static void InputVerticalAbsolute(TransInfo *t,
InputVector(t, mi, mval, vec);
project_v3_v3v3(vec, vec, t->viewinv[1]);
- /* Flip so dragging up increases (matching viewport zoom). */
- output[0] = dot_v3v3(t->viewinv[1], vec) * -2.0f;
+ /* Dragging up increases (matching viewport zoom). */
+ output[0] = dot_v3v3(t->viewinv[1], vec) * 2.0f;
}
/** Callback for #INPUT_CUSTOM_RATIO_FLIP */
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index e1fd22f06be..38d49ab5efd 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -39,8 +39,6 @@
#include "RNA_access.h"
-#include "ED_screen.h"
-
#include "UI_interface.h"
#include "BLT_translation.h"
@@ -52,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) &&
@@ -433,21 +448,20 @@ static void constraintSizeLim(TransInfo *t, TransData *td)
/* scale val and reset size */
return; // TODO: fix this case
}
- else {
- /* Reset val if SINGLESIZE but using a constraint */
- if (td->flag & TD_SINGLESIZE) {
- return;
- }
- /* separate out sign to apply back later */
- for (i = 0; i < 3; i++) {
- size_sign[i] = signf(td->ext->size[i]);
- size_abs[i] = fabsf(td->ext->size[i]);
- }
+ /* Reset val if SINGLESIZE but using a constraint */
+ if (td->flag & TD_SINGLESIZE) {
+ return;
+ }
- size_to_mat4(cob.matrix, size_abs);
+ /* separate out sign to apply back later */
+ for (i = 0; i < 3; i++) {
+ size_sign[i] = signf(td->ext->size[i]);
+ size_abs[i] = fabsf(td->ext->size[i]);
}
+ size_to_mat4(cob.matrix, size_abs);
+
/* Evaluate valid constraints */
for (con = td->con; con; con = con->next) {
/* only consider constraint if enabled */
@@ -493,16 +507,15 @@ static void constraintSizeLim(TransInfo *t, TransData *td)
/* scale val and reset size */
return; // TODO: fix this case
}
- else {
- /* Reset val if SINGLESIZE but using a constraint */
- if (td->flag & TD_SINGLESIZE) {
- return;
- }
- /* extrace scale from matrix and apply back sign */
- mat4_to_size(td->ext->size, cob.matrix);
- mul_v3_v3(td->ext->size, size_sign);
+ /* Reset val if SINGLESIZE but using a constraint */
+ if (td->flag & TD_SINGLESIZE) {
+ return;
}
+
+ /* extrace scale from matrix and apply back sign */
+ mat4_to_size(td->ext->size, cob.matrix);
+ mul_v3_v3(td->ext->size, size_sign);
}
}
@@ -1176,25 +1189,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: {
@@ -1269,6 +1269,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. */
+ mesh_customdatacorrect_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 074e89390c2..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]);
@@ -94,9 +96,7 @@ void initNormalRotation(TransInfo *t);
void initSeqSlide(TransInfo *t);
/* transform_mode_edge_slide.c */
-void projectEdgeSlideData(TransInfo *t, bool is_final);
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,9 +153,7 @@ void initTrackball(TransInfo *t);
void initTranslation(TransInfo *t);
/* transform_mode_vert_slide.c */
-void projectVertSlideData(TransInfo *t, bool is_final);
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 9f9e5742ffe..4a648a77fe1 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);
@@ -276,15 +281,14 @@ static BMLoop *get_next_loop(
copy_v3_v3(r_slide_vec, vec_accum);
return l;
}
- else {
- /* accumulate the normalized edge vector,
- * normalize so some edges don't skew the result */
- float tvec[3];
- sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co);
- vec_accum_len += normalize_v3(tvec);
- add_v3_v3(vec_accum, tvec);
- i += 1;
- }
+
+ /* accumulate the normalized edge vector,
+ * normalize so some edges don't skew the result */
+ float tvec[3];
+ sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co);
+ vec_accum_len += normalize_v3(tvec);
+ add_v3_v3(vec_accum, tvec);
+ i += 1;
if (BM_loop_other_edge_loop(l, v)->e == e_next) {
if (i) {
@@ -519,7 +523,7 @@ static void calcEdgeSlide_even(TransInfo *t,
}
}
-static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc)
+static EdgeSlideData *createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc)
{
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
@@ -554,8 +558,9 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t
}
if (numsel == 0 || numsel > 2) {
+ /* Invalid edge selection. */
MEM_freeN(sld);
- return false; /* invalid edge selection */
+ return NULL;
}
}
}
@@ -566,7 +571,7 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t
if (!BM_edge_is_manifold(e) && !BM_edge_is_boundary(e)) {
/* can edges with at least once face user */
MEM_freeN(sld);
- return false;
+ return NULL;
}
}
}
@@ -595,7 +600,7 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t
if (!j) {
MEM_freeN(sld);
MEM_freeN(sv_table);
- return false;
+ return NULL;
}
sv_tot = j;
}
@@ -870,18 +875,16 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t
calcEdgeSlide_even(t, tc, sld, mval);
}
- tc->custom.mode.data = sld;
-
MEM_freeN(sv_table);
- return true;
+ return sld;
}
/**
* A simple version of #createEdgeSlideVerts_double_side
* Which assumes the longest unselected.
*/
-static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc)
+static EdgeSlideData *createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc)
{
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
@@ -933,7 +936,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *t
if (!j) {
MEM_freeN(sld);
- return false;
+ return NULL;
}
sv_tot = j;
@@ -1055,24 +1058,9 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *t
calcEdgeSlide_even(t, tc, sld, mval);
}
- tc->custom.mode.data = sld;
-
MEM_freeN(sv_table);
- return true;
-}
-
-void projectEdgeSlideData(TransInfo *t, bool is_final)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- EdgeSlideData *sld = tc->custom.mode.data;
-
- if (sld == NULL) {
- continue;
- }
-
- trans_mesh_customdata_correction_apply(tc, is_final);
- }
+ return sld;
}
static void freeEdgeSlideVerts(TransInfo *UNUSED(t),
@@ -1145,132 +1133,227 @@ 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);
+ normalize_v3(co_dir);
+ 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);
@@ -1381,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 */
@@ -1429,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__);
@@ -1448,14 +1534,13 @@ void initEdgeSlide_ex(
t->custom.mode.use_free = true;
}
- if (use_double_side) {
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- ok |= createEdgeSlideVerts_double_side(t, tc);
- }
- }
- else {
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- ok |= createEdgeSlideVerts_single_side(t, tc);
+ 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;
}
}
@@ -1464,16 +1549,6 @@ void initEdgeSlide_ex(
return;
}
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- sld = tc->custom.mode.data;
- if (!sld) {
- continue;
- }
- tc->custom.mode.free_cb = freeEdgeSlideVerts;
- }
-
- trans_mesh_customdata_correction_init(t);
-
/* set custom point first if you want value to be initialized by init */
calcEdgeSlideCustomPoints(t);
initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO_FLIP);
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_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c
index 78d3efa0d69..6302bc96330 100644
--- a/source/blender/editors/transform/transform_mode_shrink_fatten.c
+++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c
@@ -54,13 +54,13 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
char str[UI_MAX_DRAW_STR];
size_t ofs = 0;
- distance = -t->values[0];
+ distance = t->values[0];
snapGridIncrement(t, &distance);
applyNumInput(&t->num, &distance);
- t->values_final[0] = -distance;
+ t->values_final[0] = distance;
/* header print for NumInput */
ofs += BLI_strncpy_rlen(str + ofs, TIP_("Shrink/Fatten:"), sizeof(str) - ofs);
diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c
index c514df497bc..28eaff8c136 100644
--- a/source/blender/editors/transform/transform_mode_timetranslate.c
+++ b/source/blender/editors/transform/transform_mode_timetranslate.c
@@ -29,7 +29,6 @@
#include "BLI_string.h"
#include "BKE_context.h"
-#include "BKE_nla.h"
#include "BKE_unit.h"
#include "ED_screen.h"
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 60caa257a40..c083e1654dc 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"
@@ -67,15 +68,27 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
}
else {
float dvec[3];
-
- copy_v3_v3(dvec, vec);
- applyAspectRatio(t, dvec);
+ if (!(t->flag & T_2D_EDIT) && t->con.mode & CON_APPLY) {
+ int i = 0;
+ zero_v3(dvec);
+ if (t->con.mode & CON_AXIS0) {
+ dvec[i++] = vec[0];
+ }
+ if (t->con.mode & CON_AXIS1) {
+ dvec[i++] = vec[1];
+ }
+ if (t->con.mode & CON_AXIS2) {
+ dvec[i++] = vec[2];
+ }
+ }
+ else {
+ copy_v3_v3(dvec, vec);
+ applyAspectRatio(t, dvec);
+ }
dist = len_v3(vec);
if (!(t->flag & T_2D_EDIT) && t->scene->unit.system) {
- int i;
-
- for (i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
bUnit_AsString2(&tvec[NUM_STR_REP_LEN * i],
NUM_STR_REP_LEN,
dvec[i] * t->scene->unit.scale_length,
@@ -214,6 +227,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 +417,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 7bee48337f9..b396317ba7c 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"
@@ -197,7 +198,7 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2]
}
}
-static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc)
+static VertSlideData *createVertSlideVerts(TransInfo *t, const TransDataContainer *tc)
{
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
@@ -234,7 +235,7 @@ static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc)
if (!j) {
MEM_freeN(sld);
- return false;
+ return NULL;
}
sv_array = MEM_callocN(sizeof(TransDataVertSlideVert) * j, "sv_array");
@@ -272,8 +273,6 @@ static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc)
sld->sv = sv_array;
sld->totsv = j;
- tc->custom.mode.data = sld;
-
/* most likely will be set below */
unit_m4(sld->proj_mat);
@@ -288,13 +287,7 @@ static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc)
}
}
- /* XXX, calc vert slide across all objects */
- if (tc == t->data_container) {
- calcVertSlideMouseActiveVert(t, t->mval);
- calcVertSlideMouseActiveEdges(t, t->mval);
- }
-
- return true;
+ return sld;
}
static void freeVertSlideVerts(TransInfo *UNUSED(t),
@@ -381,13 +374,6 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven
return TREDRAW_NOTHING;
}
-void projectVertSlideData(TransInfo *t, bool is_final)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- trans_mesh_customdata_correction_apply(tc, is_final);
- }
-}
-
void drawVertSlide(TransInfo *t)
{
if ((t->mode == TFM_VERT_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) {
@@ -506,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;
@@ -514,6 +500,10 @@ void doVertSlide(TransInfo *t, float perc)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
VertSlideData *sld = tc->custom.mode.data;
+ if (sld == NULL) {
+ continue;
+ }
+
TransDataVertSlideVert *svlist = sld->sv, *sv;
int i;
@@ -553,6 +543,38 @@ 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[3];
+ sub_v3_v3v3(co_dir, co_curr_3d, co_orig_3d);
+ normalize_v3(co_dir);
+ 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_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];
@@ -566,6 +588,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 */
@@ -578,7 +601,7 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
t->values_final[0] = final;
/* header string */
- ofs += BLI_strncpy_rlen(str + ofs, TIP_("Vert Slide: "), sizeof(str) - ofs);
+ ofs += BLI_strncpy_rlen(str + ofs, TIP_("Vertex Slide: "), sizeof(str) - ofs);
if (hasNumInput(&t->num)) {
char c[NUM_STR_REP_LEN];
outputNumInput(&(t->num), c, &t->scene->unit);
@@ -611,6 +634,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__);
@@ -628,10 +653,11 @@ void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp)
bool ok = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- ok |= createVertSlideVerts(t, tc);
- VertSlideData *sld = tc->custom.mode.data;
+ VertSlideData *sld = createVertSlideVerts(t, tc);
if (sld) {
+ tc->custom.mode.data = sld;
tc->custom.mode.free_cb = freeVertSlideVerts;
+ ok = true;
}
}
@@ -640,7 +666,8 @@ void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp)
return;
}
- trans_mesh_customdata_correction_init(t);
+ calcVertSlideMouseActiveVert(t, t->mval);
+ calcVertSlideMouseActiveEdges(t, t->mval);
/* set custom point first if you want value to be initialized by init */
calcVertSlideCustomPoints(t);
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index d643244e6ca..ab9548ad52e 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -20,7 +20,6 @@
#include "MEM_guardedalloc.h"
-#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -32,7 +31,6 @@
#include "BKE_context.h"
#include "BKE_editmesh.h"
#include "BKE_global.h"
-#include "BKE_layer.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -518,22 +516,19 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if ((event == NULL) && RNA_struct_property_is_set(op->ptr, "value")) {
return transform_exec(C, op);
}
- else {
- /* add temp handler */
- WM_event_add_modal_handler(C, op);
- op->flag |= OP_IS_MODAL_GRAB_CURSOR; // XXX maybe we want this with the gizmo only?
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
- /* Use when modal input has some transformation to begin with. */
- {
- TransInfo *t = op->customdata;
- if (UNLIKELY(!is_zero_v4(t->values_modal_offset))) {
- transformApply(C, t);
- }
- }
+ op->flag |= OP_IS_MODAL_GRAB_CURSOR; // XXX maybe we want this with the gizmo only?
- return OPERATOR_RUNNING_MODAL;
+ /* Use when modal input has some transformation to begin with. */
+ TransInfo *t = op->customdata;
+ if (UNLIKELY(!is_zero_v4(t->values_modal_offset))) {
+ transformApply(C, t);
}
+
+ return OPERATOR_RUNNING_MODAL;
}
static bool transform_poll_property(const bContext *UNUSED(C),
@@ -1051,11 +1046,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_orientations.c b/source/blender/editors/transform/transform_orientations.c
index cd170b144d8..493b52495db 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -32,7 +32,6 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
-#include "DNA_workspace_types.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -47,7 +46,6 @@
#include "BKE_layer.h"
#include "BKE_report.h"
#include "BKE_scene.h"
-#include "BKE_workspace.h"
#include "BLT_translation.h"
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index c6d7b92fb42..a700dd320b7 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -22,34 +22,22 @@
*/
#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
#include "PIL_time.h"
-#include "DNA_meshdata_types.h" /* Temporary, for snapping to other unselected meshes */
-#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"
#include "BKE_context.h"
-#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
-#include "BKE_main.h"
#include "BKE_object.h"
+#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "RNA_access.h"
@@ -57,14 +45,10 @@
#include "WM_types.h"
#include "ED_gizmo_library.h"
-#include "ED_image.h"
#include "ED_markers.h"
#include "ED_node.h"
#include "ED_transform_snap_object_context.h"
#include "ED_uvedit.h"
-#include "ED_view3d.h"
-
-#include "DEG_depsgraph.h"
#include "UI_resources.h"
#include "UI_view2d.h"
@@ -86,10 +70,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);
@@ -98,10 +78,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]);
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -125,6 +101,24 @@ int BIF_snappingSupported(Object *obedit)
}
#endif
+static bool snap_use_backface_culling(const TransInfo *t)
+{
+ BLI_assert(t->spacetype == SPACE_VIEW3D);
+ View3D *v3d = t->view;
+ if ((v3d->shading.type == OB_SOLID) && (v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)) {
+ return true;
+ }
+ if (v3d->shading.type == OB_RENDER &&
+ (t->scene->display.shading.flag & V3D_SHADING_BACKFACE_CULLING) &&
+ BKE_scene_uses_blender_workbench(t->scene)) {
+ return true;
+ }
+ if (t->settings->snap_flag & SCE_SNAP_BACKFACE_CULLING) {
+ return true;
+ }
+ return false;
+}
+
bool validSnap(const TransInfo *t)
{
return (t->tsnap.status & (POINT_INIT | TARGET_INIT)) == (POINT_INIT | TARGET_INIT) ||
@@ -149,6 +143,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;
}
@@ -334,8 +334,7 @@ void applyProject(TransInfo *t)
.snap_select = t->tsnap.modeSelect,
.use_object_edit_cage = (t->flag & T_EDIT) != 0,
.use_occlusion_test = false,
- .use_backface_culling = (t->scene->toolsettings->snap_flag &
- SCE_SNAP_BACKFACE_CULLING) != 0,
+ .use_backface_culling = t->tsnap.use_backface_culling,
},
mval_fl,
NULL,
@@ -482,7 +481,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;
@@ -563,35 +561,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)
@@ -611,14 +606,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 */
@@ -631,15 +619,19 @@ static void initSnappingMode(TransInfo *t)
if (t->spacetype == SPACE_VIEW3D) {
if (t->tsnap.object_context == NULL) {
+ t->tsnap.use_backface_culling = snap_use_backface_culling(t);
t->tsnap.object_context = ED_transform_snap_object_context_create_view3d(
t->scene, 0, t->region, t->view);
- ED_transform_snap_object_context_set_editmesh_callbacks(
- t->tsnap.object_context,
- (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
- bm_edge_is_snap_target,
- bm_face_is_snap_target,
- POINTER_FROM_UINT((BM_ELEM_SELECT | BM_ELEM_HIDDEN)));
+ if (t->data_type == TC_MESH_VERTS) {
+ /* Ignore elements being transformed. */
+ ED_transform_snap_object_context_set_editmesh_callbacks(
+ t->tsnap.object_context,
+ (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
+ bm_edge_is_snap_target,
+ bm_face_is_snap_target,
+ POINTER_FROM_UINT((BM_ELEM_SELECT | BM_ELEM_HIDDEN)));
+ }
}
}
}
@@ -723,8 +715,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;
@@ -732,36 +729,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)
@@ -862,145 +829,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)
* \{ */
@@ -1311,13 +1139,12 @@ short snapObjectsTransform(
return ED_transform_snap_object_project_view3d_ex(
t->tsnap.object_context,
t->depsgraph,
- t->scene->toolsettings->snap_mode,
+ t->settings->snap_mode,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
.use_object_edit_cage = (t->flag & T_EDIT) != 0,
- .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE,
- .use_backface_culling = (t->scene->toolsettings->snap_flag &
- SCE_SNAP_BACKFACE_CULLING) != 0,
+ .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
+ .use_backface_culling = t->tsnap.use_backface_culling,
},
mval,
target,
@@ -1737,3 +1564,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 c30b8d59dc0..eb14c5bec28 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -44,7 +44,6 @@
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
-#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_object.h"
@@ -56,8 +55,6 @@
#include "ED_transform_snap_object_context.h"
#include "ED_view3d.h"
-#include "ED_transform.h"
-
/* -------------------------------------------------------------------- */
/** \name Internal Data Types
* \{ */
@@ -370,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,
@@ -396,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;
}
@@ -408,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;
}
}
@@ -424,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);
}
}
@@ -959,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;
@@ -1008,12 +1014,11 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
dt->r_hit_list);
break;
}
- else {
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- use_hide = true;
- }
+
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ if (em->mesh_eval_final) {
+ me = em->mesh_eval_final;
+ use_hide = true;
}
}
retval = raycastMesh(sctx,
@@ -1035,24 +1040,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;
}
}
@@ -1074,9 +1081,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
* ---------------
@@ -1101,11 +1106,13 @@ static bool raycastObjects(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
/* read/write args */
- float *ray_depth,
+ /* Parameters below cannot be const, because they are assigned to a
+ * non-const variable (readability-non-const-parameter). */
+ float *ray_depth /* NOLINT */,
/* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index,
+ float r_loc[3] /* NOLINT */,
+ float r_no[3] /* NOLINT */,
+ int *r_index /* NOLINT */,
Object **r_ob,
float r_obmat[4][4],
ListBase *r_hit_list)
@@ -1269,7 +1276,7 @@ static bool test_projected_edge_dist(const struct DistProjectedAABBPrecalc *prec
float r_co[3])
{
float near_co[3], lambda;
- if (!isect_ray_seg_v3(precalc->ray_origin, precalc->ray_direction, va, vb, &lambda)) {
+ if (!isect_ray_line_v3(precalc->ray_origin, precalc->ray_direction, va, vb, &lambda)) {
copy_v3_v3(near_co, va);
}
else {
@@ -1661,11 +1668,11 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
};
float lambda;
- if (!isect_ray_seg_v3(neasrest_precalc.ray_origin,
- neasrest_precalc.ray_direction,
- v_pair[0],
- v_pair[1],
- &lambda)) {
+ if (!isect_ray_line_v3(neasrest_precalc.ray_origin,
+ neasrest_precalc.ray_direction,
+ v_pair[0],
+ v_pair[1],
+ &lambda)) {
/* do nothing */
}
else {
@@ -2057,15 +2064,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;
@@ -2120,7 +2127,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;
@@ -2135,7 +2142,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;
@@ -2653,11 +2660,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;
@@ -2682,11 +2690,10 @@ static void sanp_obj_fn(SnapObjectContext *sctx,
dt->r_index);
break;
}
- else {
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- }
+
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ if (em->mesh_eval_final) {
+ me = em->mesh_eval_final;
}
}
else if (ob->dt == OB_BOUNDBOX) {
@@ -2732,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(
@@ -2784,11 +2791,13 @@ static short snapObjectsRay(SnapObjectContext *sctx,
SnapData *snapdata,
const struct SnapObjectParams *params,
/* read/write args */
- float *dist_px,
+ /* Parameters below cannot be const, because they are assigned to a
+ * non-const variable (readability-non-const-parameter). */
+ float *dist_px /* NOLINT */,
/* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index,
+ float r_loc[3] /* NOLINT */,
+ float r_no[3] /* NOLINT */,
+ int *r_index /* NOLINT */,
Object **r_ob,
float r_obmat[4][4])
{
@@ -2803,7 +2812,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;
}
@@ -3208,7 +3217,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/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index 6633e1c427c..50e0bb1f1c2 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -98,6 +98,14 @@ void ED_undo_push(bContext *C, const char *str)
if (steps <= 0) {
return;
}
+ if (G.background) {
+ /* Python developers may have explicitly created the undo stack in background mode,
+ * otherwise allow it to be NULL, see: T60934.
+ * Otherwise it must never be NULL, even when undo is disabled. */
+ if (wm->undo_stack == NULL) {
+ return;
+ }
+ }
/* Only apply limit if this is the last undo step. */
if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) {
@@ -360,7 +368,7 @@ bool ED_undo_is_legacy_compatible_for_property(struct bContext *C, ID *id)
CLOG_INFO(&LOG, 1, "skipping undo for paint-mode");
return false;
}
- else if (obact->mode & OB_MODE_EDIT) {
+ if (obact->mode & OB_MODE_EDIT) {
if ((id == NULL) || (obact->data == NULL) ||
(GS(id->name) != GS(((ID *)obact->data)->name))) {
/* No undo push on id type mismatch in edit-mode. */
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 17a90d10ca7..207606c2dcd 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -63,7 +63,6 @@ set(SRC
../include/ED_keyframes_edit.h
../include/ED_keyframing.h
../include/ED_lattice.h
- ../include/ED_logic.h
../include/ED_markers.h
../include/ED_mask.h
../include/ED_mball.h
@@ -96,6 +95,7 @@ set(SRC
../include/ED_util_imbuf.h
../include/ED_uvedit.h
../include/ED_view3d.h
+ ../include/ED_view3d_offscreen.h
../include/UI_icons.h
../include/UI_interface.h
../include/UI_interface_icons.h
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index af387e4f7c2..93762e6500c 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -58,7 +58,6 @@
#include "DEG_depsgraph.h"
#include "ED_armature.h"
-#include "ED_buttons.h"
#include "ED_image.h"
#include "ED_mesh.h"
#include "ED_node.h"
@@ -123,10 +122,10 @@ void ED_editors_init(bContext *C)
if (mode == OB_MODE_OBJECT) {
continue;
}
- else if (BKE_object_has_mode_data(ob, mode)) {
+ if (BKE_object_has_mode_data(ob, mode)) {
continue;
}
- else if (ob->type == OB_GPENCIL) {
+ if (ob->type == OB_GPENCIL) {
/* For multi-edit mode we may already have mode data (grease pencil does not need it).
* However we may have a non-active object stuck in a grease-pencil edit mode. */
if (ob != obact) {
diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c
index 132a63a8249..3e85342e0d7 100644
--- a/source/blender/editors/util/ed_util_imbuf.c
+++ b/source/blender/editors/util/ed_util_imbuf.c
@@ -447,15 +447,16 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info)
(float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin},
(float)(info->sample_size / 2.0f) * sima->zoom);
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_XOR);
+ GPU_logic_op_xor_set(true);
+
GPU_line_width(1.0f);
imm_draw_box_wire_2d(pos,
(float)sample_rect_fl.xmin,
(float)sample_rect_fl.ymin,
(float)sample_rect_fl.xmax,
(float)sample_rect_fl.ymax);
- glDisable(GL_COLOR_LOGIC_OP);
+
+ GPU_logic_op_xor_set(false);
immUnbindProgram();
}
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index be1319c37dd..384da6fb931 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -26,6 +26,8 @@
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_scene.h"
#include "BKE_unit.h"
@@ -239,13 +241,12 @@ bool applyNumInput(NumInput *n, float *vec)
#endif
return true;
}
- else {
- /* Else, we set the 'org' values for numinput! */
- for (j = 0; j <= n->idx_max; j++) {
- n->val[j] = n->val_org[j] = vec[j];
- }
- return false;
+
+ /* Else, we set the 'org' values for numinput! */
+ for (j = 0; j <= n->idx_max; j++) {
+ n->val[j] = n->val_org[j] = vec[j];
}
+ return false;
}
static void value_to_editstr(NumInput *n, int idx)
@@ -278,8 +279,12 @@ static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf
return true;
}
-bool user_string_to_number(
- bContext *C, const char *str, const UnitSettings *unit, int type, double *r_value)
+bool user_string_to_number(bContext *C,
+ const char *str,
+ const UnitSettings *unit,
+ int type,
+ const char *error_prefix,
+ double *r_value)
{
#ifdef WITH_PYTHON
double unit_scale = BKE_scene_unit_scale(unit, type, 1.0);
@@ -289,14 +294,14 @@ bool user_string_to_number(
bUnit_ReplaceString(
str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type);
- return BPY_execute_string_as_number(C, NULL, str_unit_convert, true, r_value);
- }
- else {
- int success = BPY_execute_string_as_number(C, NULL, str, true, r_value);
- *r_value *= bUnit_PreferredInputUnitScalar(unit, type);
- *r_value /= unit_scale;
- return success;
+ return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
}
+
+ int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value);
+ *r_value *= bUnit_PreferredInputUnitScalar(unit, type);
+ *r_value /= unit_scale;
+ return success;
+
#else
UNUSED_VARS(C, unit, type);
*r_value = atof(str);
@@ -309,12 +314,10 @@ static bool editstr_is_simple_numinput(const char ascii)
if (ascii >= '0' && ascii <= '9') {
return true;
}
- else if (ascii == '.') {
+ if (ascii == '.') {
return true;
}
- else {
- return false;
- }
+ return false;
}
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
@@ -348,7 +351,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
n->val_flag[idx] |= NUM_EDITED;
return true;
}
- else if (event->ctrl) {
+ if (event->ctrl) {
n->flag &= ~NUM_EDIT_FULL;
return true;
}
@@ -576,7 +579,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Scene *sce = CTX_data_scene(C);
double val;
- int success = user_string_to_number(C, n->str, &sce->unit, n->unit_type[idx], &val);
+ int success = user_string_to_number(
+ C, n->str, &sce->unit, n->unit_type[idx], IFACE_("Numeric input evaluation"), &val);
if (success) {
n->val[idx] = (float)val;
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index b40b82c50fb..a39234561c2 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -40,6 +40,8 @@ set(SRC
uvedit_draw.c
uvedit_ops.c
uvedit_parametrizer.c
+ uvedit_path.c
+ uvedit_rip.c
uvedit_select.c
uvedit_smart_stitch.c
uvedit_unwrap_ops.c
@@ -49,6 +51,7 @@ set(SRC
)
set(LIB
+ bf_bmesh
)
if(WITH_INTERNATIONAL)
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index 897e2f13774..df8d3cfb8db 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -86,16 +86,12 @@ static int draw_uvs_face_check(const ToolSettings *ts)
if (ts->selectmode == SCE_SELECT_FACE) {
return 2;
}
- else if (ts->selectmode & SCE_SELECT_FACE) {
+ if (ts->selectmode & SCE_SELECT_FACE) {
return 1;
}
- else {
- return 0;
- }
- }
- else {
- return (ts->uv_selectmode == UV_SELECT_FACE);
+ return 0;
}
+ return (ts->uv_selectmode == UV_SELECT_FACE);
}
/* ------------------------- */
@@ -409,70 +405,68 @@ 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);
+ {
+ /* We could modify the vbo's data filling
+ * instead of modifying the provoking vert. */
+ GPU_provoking_vertex(GPU_VERTEX_FIRST);
+
+ UI_GetThemeColor3fv(TH_EDGE_SELECT, col2);
+ col2[3] = overlay_alpha;
+
+ const float dash_width = (sima->dt_uv == SI_UVDT_DASH) ? (4.0f * UI_DPI_FAC) : 9999.0f;
+ eGPUBuiltinShader shader = (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH :
+ GPU_SHADER_2D_UV_EDGES;
+#ifdef __APPLE__
+ if (sima->dt_uv == SI_UVDT_OUTLINE) {
+ /* Apple drivers do not support wide line. This is a workaround awaiting the 2D view
+ * refactor. Limiting to OSX since this will slow down the drawing. (see T76806) */
+ GPU_batch_program_set_builtin(batch->edges, GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+
+ /* No U.pixelsize scaling for now since the inner line is not scalled. */
+ GPU_batch_uniform_1f(batch->edges, "lineWidth", 2.0f);
+ GPU_batch_uniform_4f(batch->edges, "color", 0.0f, 0.0f, 0.0f, 1.0f);
+ GPU_batch_uniform_2fv(batch->edges, "viewportSize", &viewport[2]);
+
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;
+#endif
- /* 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_draw(batch->edges);
- GPU_depth_test(false);
+ GPU_batch_program_set_builtin(batch->edges, shader);
- glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
- break;
+ if (sima->dt_uv == SI_UVDT_OUTLINE) {
+#ifndef __APPLE__
+ /* 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);
+#endif
+ 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);
+
+ GPU_provoking_vertex(GPU_VERTEX_LAST);
}
+
if (sima->flag & SI_SMOOTH_UV) {
GPU_line_smooth(false);
GPU_blend(false);
@@ -481,6 +475,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/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 31384d6df17..5ccc6e29026 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -88,6 +88,15 @@ bool uv_find_nearest_face_multi(struct Scene *scene,
const float co[2],
struct UvNearestHit *hit_final);
+BMLoop *uv_find_nearest_loop_from_vert(struct Scene *scene,
+ struct Object *obedit,
+ struct BMVert *v,
+ const float co[2]);
+BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
+ struct Object *obedit,
+ struct BMEdge *e,
+ const float co[2]);
+
/* utility tool functions */
void uvedit_live_unwrap_update(struct SpaceImage *sima,
@@ -106,7 +115,13 @@ void UV_OT_pack_islands(struct wmOperatorType *ot);
void UV_OT_reset(struct wmOperatorType *ot);
void UV_OT_sphere_project(struct wmOperatorType *ot);
void UV_OT_unwrap(struct wmOperatorType *ot);
+void UV_OT_rip(struct wmOperatorType *ot);
void UV_OT_stitch(struct wmOperatorType *ot);
+void UV_OT_smart_project(struct wmOperatorType *ot);
+
+/* uvedit_path.c */
+void UV_OT_shortest_path_pick(struct wmOperatorType *ot);
+void UV_OT_shortest_path_select(struct wmOperatorType *ot);
/* uvedit_select.c */
@@ -121,6 +136,7 @@ const float *uvedit_first_selected_uv_from_vertex(struct Scene *scene,
void UV_OT_select_all(struct wmOperatorType *ot);
void UV_OT_select(struct wmOperatorType *ot);
void UV_OT_select_loop(struct wmOperatorType *ot);
+void UV_OT_select_edge_ring(struct wmOperatorType *ot);
void UV_OT_select_linked(struct wmOperatorType *ot);
void UV_OT_select_linked_pick(struct wmOperatorType *ot);
void UV_OT_select_split(struct wmOperatorType *ot);
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 652d07f02db..ad98321c458 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -998,9 +998,7 @@ static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
if (RNA_boolean_get(op->ptr, "use_unselected")) {
return uv_remove_doubles_to_unselected(C, op);
}
- else {
- return uv_remove_doubles_to_selected(C, op);
- }
+ return uv_remove_doubles_to_selected(C, op);
}
static void UV_OT_remove_doubles(wmOperatorType *ot)
@@ -1845,7 +1843,6 @@ static int uv_seams_from_islands_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT};
const bool mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
bool changed_multi = false;
@@ -1886,23 +1883,10 @@ static int uv_seams_from_islands_exec(bContext *C, wmOperator *op)
continue;
}
- const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
- const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset);
-
bool mark = false;
BMLoop *l_other = l_iter->radial_next;
do {
- const MLoopUV *luv_other_curr = BM_ELEM_CD_GET_VOID_P(l_other, cd_loop_uv_offset);
- const MLoopUV *luv_other_next = BM_ELEM_CD_GET_VOID_P(l_other->next, cd_loop_uv_offset);
- if (l_iter->v != l_other->v) {
- SWAP(const MLoopUV *, luv_other_curr, luv_other_next);
- }
-
- if (!compare_ff(luv_curr->uv[0], luv_other_curr->uv[0], limit[0]) ||
- !compare_ff(luv_curr->uv[1], luv_other_curr->uv[1], limit[1]) ||
-
- !compare_ff(luv_next->uv[0], luv_other_next->uv[0], limit[0]) ||
- !compare_ff(luv_next->uv[1], luv_other_next->uv[1], limit[1])) {
+ if (!BM_loop_uv_share_edge_check(l_iter, l_other, cd_loop_uv_offset)) {
mark = true;
break;
}
@@ -2073,6 +2057,7 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_select_all);
WM_operatortype_append(UV_OT_select);
WM_operatortype_append(UV_OT_select_loop);
+ WM_operatortype_append(UV_OT_select_edge_ring);
WM_operatortype_append(UV_OT_select_linked);
WM_operatortype_append(UV_OT_select_linked_pick);
WM_operatortype_append(UV_OT_select_split);
@@ -2089,7 +2074,10 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_align);
+ WM_operatortype_append(UV_OT_rip);
WM_operatortype_append(UV_OT_stitch);
+ WM_operatortype_append(UV_OT_shortest_path_pick);
+ WM_operatortype_append(UV_OT_shortest_path_select);
WM_operatortype_append(UV_OT_seams_from_islands);
WM_operatortype_append(UV_OT_mark_seam);
@@ -2106,6 +2094,7 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_reset);
WM_operatortype_append(UV_OT_sphere_project);
WM_operatortype_append(UV_OT_unwrap);
+ WM_operatortype_append(UV_OT_smart_project);
WM_operatortype_append(UV_OT_reveal);
WM_operatortype_append(UV_OT_hide);
@@ -2113,6 +2102,21 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_cursor_set);
}
+void ED_operatormacros_uvedit(void)
+{
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+
+ ot = WM_operatortype_append_macro("UV_OT_rip_move",
+ "UV Rip Move",
+ "unstitch UV's and move the result",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ WM_operatortype_macro_define(ot, "UV_OT_rip");
+ otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
+ RNA_boolean_set(otmacro->ptr, "mirror", false);
+}
+
void ED_keymap_uvedit(wmKeyConfig *keyconf)
{
wmKeyMap *keymap;
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index da8e0efa522..a4ee9a294fe 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -321,7 +321,7 @@ static PHashLink *phash_lookup(PHash *ph, PHashKey key)
if (link->key == key) {
return link;
}
- else if (PHASH_hash(ph, link->key) != hash) {
+ if (PHASH_hash(ph, link->key) != hash) {
return NULL;
}
}
@@ -337,7 +337,7 @@ static PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link)
if (link->key == key) {
return link;
}
- else if (PHASH_hash(ph, link->key) != hash) {
+ if (PHASH_hash(ph, link->key) != hash) {
return NULL;
}
}
@@ -372,12 +372,10 @@ static float p_vec_angle(const float v1[3], const float v2[3], const float v3[3]
if (dot <= -1.0f) {
return (float)M_PI;
}
- else if (dot >= 1.0f) {
+ if (dot >= 1.0f) {
return 0.0f;
}
- else {
- return acosf(dot);
- }
+ return acosf(dot);
}
static float p_vec2_angle(const float v1[2], const float v2[2], const float v3[2])
@@ -489,7 +487,7 @@ static void p_chart_uv_scale_xy(PChart *chart, float x, float y)
}
}
-static void p_chart_uv_translate(PChart *chart, float trans[2])
+static void p_chart_uv_translate(PChart *chart, const float trans[2])
{
PVert *v;
@@ -790,9 +788,7 @@ static PVert *p_vert_lookup(PHandle *handle, PHashKey key, const float co[3], PE
if (v) {
return v;
}
- else {
- return p_vert_add(handle, key, co, e);
- }
+ return p_vert_add(handle, key, co, e);
}
static PVert *p_vert_copy(PChart *chart, PVert *v)
@@ -809,7 +805,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v)
return nv;
}
-static PEdge *p_edge_lookup(PHandle *handle, PHashKey *vkeys)
+static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys)
{
PHashKey key = PHASH_edge(vkeys[0], vkeys[1]);
PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key);
@@ -818,7 +814,7 @@ static PEdge *p_edge_lookup(PHandle *handle, PHashKey *vkeys)
if ((e->vert->u.key == vkeys[0]) && (e->next->vert->u.key == vkeys[1])) {
return e;
}
- else if ((e->vert->u.key == vkeys[1]) && (e->next->vert->u.key == vkeys[0])) {
+ if ((e->vert->u.key == vkeys[1]) && (e->next->vert->u.key == vkeys[0])) {
return e;
}
@@ -1150,14 +1146,14 @@ static PFace *p_face_add(PHandle *handle)
static PFace *p_face_add_construct(PHandle *handle,
ParamKey key,
- ParamKey *vkeys,
+ const ParamKey *vkeys,
float *co[4],
float *uv[4],
int i1,
int i2,
int i3,
- ParamBool *pin,
- ParamBool *select)
+ const ParamBool *pin,
+ const ParamBool *select)
{
PFace *f = p_face_add(handle);
PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
@@ -3353,11 +3349,10 @@ static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart)
p_chart_lscm_load_solution(chart);
return P_TRUE;
}
- else {
- for (v = chart->verts; v; v = v->nextlink) {
- v->uv[0] = 0.0f;
- v->uv[1] = 0.0f;
- }
+
+ for (v = chart->verts; v; v = v->nextlink) {
+ v->uv[0] = 0.0f;
+ v->uv[1] = 0.0f;
}
return P_FALSE;
@@ -3542,20 +3537,16 @@ static int p_compare_geometric_uv(const void *a, const void *b)
if (v1->uv[0] < v2->uv[0]) {
return -1;
}
- else if (v1->uv[0] == v2->uv[0]) {
+ if (v1->uv[0] == v2->uv[0]) {
if (v1->uv[1] < v2->uv[1]) {
return -1;
}
- else if (v1->uv[1] == v2->uv[1]) {
+ if (v1->uv[1] == v2->uv[1]) {
return 0;
}
- else {
- return 1;
- }
- }
- else {
return 1;
}
+ return 1;
}
static PBool p_chart_convex_hull(PChart *chart, PVert ***r_verts, int *r_nverts, int *r_right)
@@ -3935,14 +3926,11 @@ static PBool p_node_intersect(SmoothNode *node, float co[2])
return P_FALSE;
}
- else {
- if (co[node->axis] < node->split) {
- return p_node_intersect(node->c1, co);
- }
- else {
- return p_node_intersect(node->c2, co);
- }
+
+ if (co[node->axis] < node->split) {
+ return p_node_intersect(node->c1, co);
}
+ return p_node_intersect(node->c2, co);
}
/* smoothing */
@@ -3955,12 +3943,10 @@ static int p_compare_float(const void *a_, const void *b_)
if (a < b) {
return -1;
}
- else if (a == b) {
+ if (a == b) {
return 0;
}
- else {
- return 1;
- }
+ return 1;
}
static float p_smooth_median_edge_length(PChart *chart)
diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c
new file mode 100644
index 00000000000..546aad078aa
--- /dev/null
+++ b/source/blender/editors/uvedit/uvedit_path.c
@@ -0,0 +1,873 @@
+/*
+ * 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 eduv
+ *
+ * \note The logic in this file closely follows editmesh_path.c
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "BLI_linklist.h"
+#include "DNA_windowmanager_types.h"
+#include "MEM_guardedalloc.h"
+
+#include "BLI_ghash.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_math.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_editmesh.h"
+#include "BKE_layer.h"
+#include "BKE_mesh.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "ED_screen.h"
+#include "ED_transform.h"
+#include "ED_uvedit.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_view2d.h"
+
+#include "intern/bmesh_marking.h"
+#include "uvedit_intern.h"
+
+#include "bmesh_tools.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Local Utilities
+ * \{ */
+
+/**
+ * Support edge-path using vert-path calculation code.
+ *
+ * Cheat! Pick 2 closest loops and do vertex path,
+ * in practices only obscure/contrived cases will make give noticeably worse behavior.
+ *
+ * While the code below is a bit awkward, it's significantly less overhead than
+ * adding full edge selection which is nearly the same as vertex path in the case of UV's.
+ *
+ * \param use_nearest: When false use the post distant pair of loops,
+ * use when filling a region as we want both verts from each edge to be included in the region.
+ */
+static void bm_loop_calc_vert_pair_from_edge_pair(const bool use_nearest,
+ const int cd_loop_uv_offset,
+ const float aspect_y,
+ BMElem **ele_src_p,
+ BMElem **ele_dst_p,
+ BMElem **r_ele_dst_final)
+{
+ BMLoop *l_src = (BMLoop *)*ele_src_p;
+ BMLoop *l_dst = (BMLoop *)*ele_dst_p;
+
+ const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset);
+ const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset);
+ const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset);
+ const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset);
+
+ const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y};
+ const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y};
+ const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y};
+ const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y};
+
+ struct {
+ int src_index;
+ int dst_index;
+ float len_sq;
+ } tests[4] = {
+ {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)},
+ {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)},
+ {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)},
+ {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)},
+ };
+ int i_best = 0;
+ for (int i = 1; i < ARRAY_SIZE(tests); i++) {
+ if (use_nearest) {
+ if (tests[i].len_sq < tests[i_best].len_sq) {
+ i_best = i;
+ }
+ }
+ else {
+ if (tests[i].len_sq > tests[i_best].len_sq) {
+ i_best = i;
+ }
+ }
+ }
+
+ *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src);
+ *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst);
+
+ /* Ensure the edge is selected, not just the vertices up until we hit it. */
+ *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Path Select Struct & Properties
+ * \{ */
+
+struct PathSelectParams {
+ /** ensure the active element is the last selected item (handy for picking) */
+ bool track_active;
+ bool use_topology_distance;
+ bool use_face_step;
+ bool use_fill;
+ struct CheckerIntervalParams interval_params;
+};
+
+struct UserData_UV {
+ Scene *scene;
+ BMEditMesh *em;
+ uint cd_loop_uv_offset;
+};
+
+static void path_select_properties(wmOperatorType *ot)
+{
+ RNA_def_boolean(ot->srna,
+ "use_face_step",
+ false,
+ "Face Stepping",
+ "Traverse connected faces (includes diagonals and edge-rings)");
+ RNA_def_boolean(ot->srna,
+ "use_topology_distance",
+ false,
+ "Topology Distance",
+ "Find the minimum number of steps, ignoring spatial distance");
+ RNA_def_boolean(ot->srna,
+ "use_fill",
+ false,
+ "Fill Region",
+ "Select all paths between the source/destination elements");
+
+ WM_operator_properties_checker_interval(ot, true);
+}
+
+static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *op_params)
+{
+ op_params->track_active = false;
+ op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
+ op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
+ op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
+ WM_operator_properties_checker_interval_from_op(op, &op_params->interval_params);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Vert Path
+ * \{ */
+
+/* callbacks */
+static bool looptag_filter_cb(BMLoop *l, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ return uvedit_face_visible_test(user_data->scene, l->f);
+}
+static bool looptag_test_cb(BMLoop *l, void *user_data_v)
+{
+ /* All connected loops are selected or we return false. */
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
+ if (looptag_filter_cb(l_iter, user_data)) {
+ const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if (equals_v2v2(luv->uv, luv_iter->uv)) {
+ if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ BMEditMesh *em = user_data->em;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
+ if (looptag_filter_cb(l_iter, user_data)) {
+ MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if (equals_v2v2(luv->uv, luv_iter->uv)) {
+ uvedit_uv_select_set(scene, em, l_iter, val, false, cd_loop_uv_offset);
+ }
+ }
+ }
+}
+
+static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ const char uv_selectmode = ED_uvedit_select_mode_get(scene);
+ const bool use_fake_edge_select = (uv_selectmode & UV_SELECT_EDGE);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ int flush = 0;
+
+ /* Variables to use when `use_fake_edge_select` is set. */
+ struct {
+ BMLoop *l_dst_activate;
+ BMLoop *l_dst_add_to_path;
+ } fake_edge_select = {NULL};
+
+ if (use_fake_edge_select) {
+ fake_edge_select.l_dst_activate = l_dst;
+
+ /* Use most distant when doing region selection.
+ * without this we get dangling edges outside the region. */
+ bool use_neaerst = (op_params->use_fill == false);
+ BMElem *ele_src = (BMElem *)l_src;
+ BMElem *ele_dst = (BMElem *)l_dst;
+ BMElem *ele_dst_final = NULL;
+ bm_loop_calc_vert_pair_from_edge_pair(
+ use_neaerst, cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final);
+
+ if (op_params->use_fill == false) {
+ /* Always activate the item under the cursor. */
+ fake_edge_select.l_dst_add_to_path = (BMLoop *)ele_dst_final;
+ }
+
+ l_src = (BMLoop *)ele_src;
+ l_dst = (BMLoop *)ele_dst;
+ }
+
+ struct UserData_UV user_data = {
+ .scene = scene,
+ .em = em,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ const struct BMCalcPathUVParams params = {
+ .use_topology_distance = op_params->use_topology_distance,
+ .use_step_face = op_params->use_face_step,
+ .aspect_y = aspect_y,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ LinkNode *path = NULL;
+ bool is_path_ordered = false;
+
+ if (l_src != l_dst) {
+ if (op_params->use_fill) {
+ path = BM_mesh_calc_path_uv_region_vert(bm,
+ (BMElem *)l_src,
+ (BMElem *)l_dst,
+ params.cd_loop_uv_offset,
+ looptag_filter_cb,
+ &user_data);
+ }
+ else {
+ is_path_ordered = true;
+ path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, looptag_filter_cb, &user_data);
+ }
+ }
+
+ BMLoop *l_dst_last = l_dst;
+
+ if (path) {
+ if (use_fake_edge_select) {
+ if ((fake_edge_select.l_dst_add_to_path != NULL) &&
+ (BLI_linklist_index(path, fake_edge_select.l_dst_add_to_path) == -1)) {
+ /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */
+ LinkNode *path_last = BLI_linklist_find_last(path);
+ BLI_linklist_insert_after(&path_last, fake_edge_select.l_dst_add_to_path);
+ BLI_assert(BLI_linklist_find_last(path)->link == fake_edge_select.l_dst_add_to_path);
+ }
+ }
+
+ /* toggle the flag */
+ bool all_set = true;
+ LinkNode *node = path;
+ do {
+ if (!looptag_test_cb((BMLoop *)node->link, &user_data)) {
+ all_set = false;
+ break;
+ }
+ } while ((node = node->next));
+
+ int depth = -1;
+ node = path;
+ do {
+ if ((is_path_ordered == false) ||
+ WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
+ looptag_set_cb((BMLoop *)node->link, !all_set, &user_data);
+ if (is_path_ordered) {
+ l_dst_last = node->link;
+ }
+ }
+ } while ((void)depth++, (node = node->next));
+
+ BLI_linklist_free(path, NULL);
+ flush = all_set ? -1 : 1;
+ }
+ else {
+ const bool is_act = !looptag_test_cb(l_dst, &user_data);
+ looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
+ }
+
+ if (op_params->track_active) {
+ /* Fake edge selection. */
+ if (use_fake_edge_select) {
+ BMLoop *l_dst_activate = fake_edge_select.l_dst_activate;
+ /* TODO(campbell): Search for an active loop attached to 'l_dst'.
+ * when `BLI_linklist_index(path, l_dst_activate) == -1`
+ * In practice this rarely happens though. */
+ ED_uvedit_active_edge_loop_set(bm, l_dst_activate);
+ }
+ else {
+ ED_uvedit_active_vert_loop_set(bm, l_dst_last);
+ }
+ }
+ return flush;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Face Path
+ * \{ */
+
+/* callbacks */
+static bool facetag_filter_cb(BMFace *f, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ return uvedit_face_visible_test(user_data->scene, f);
+}
+static bool facetag_test_cb(BMFace *f, void *user_data_v)
+{
+ /* All connected loops are selected or we return false. */
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
+ if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) {
+ return false;
+ }
+ }
+ return true;
+}
+static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ BMEditMesh *em = user_data->em;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ uvedit_face_select_set(scene, em, f, val, false, cd_loop_uv_offset);
+}
+
+static int mouse_mesh_uv_shortest_path_face(Scene *scene,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMFace *f_src,
+ BMFace *f_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ int flush = 0;
+
+ struct UserData_UV user_data = {
+ .scene = scene,
+ .em = em,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ const struct BMCalcPathUVParams params = {
+ .use_topology_distance = op_params->use_topology_distance,
+ .use_step_face = op_params->use_face_step,
+ .aspect_y = aspect_y,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ LinkNode *path = NULL;
+ bool is_path_ordered = false;
+
+ if (f_src != f_dst) {
+ if (op_params->use_fill) {
+ path = BM_mesh_calc_path_uv_region_face(bm,
+ (BMElem *)f_src,
+ (BMElem *)f_dst,
+ params.cd_loop_uv_offset,
+ facetag_filter_cb,
+ &user_data);
+ }
+ else {
+ is_path_ordered = true;
+ path = BM_mesh_calc_path_uv_face(bm, f_src, f_dst, &params, facetag_filter_cb, &user_data);
+ }
+ }
+
+ BMFace *f_dst_last = f_dst;
+
+ if (path) {
+ /* toggle the flag */
+ bool all_set = true;
+ LinkNode *node = path;
+ do {
+ if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
+ all_set = false;
+ break;
+ }
+ } while ((node = node->next));
+
+ int depth = -1;
+ node = path;
+ do {
+ if ((is_path_ordered == false) ||
+ WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
+ facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
+ if (is_path_ordered) {
+ f_dst_last = node->link;
+ }
+ }
+ } while ((void)depth++, (node = node->next));
+
+ BLI_linklist_free(path, NULL);
+ flush = all_set ? -1 : 1;
+ }
+ else {
+ const bool is_act = !facetag_test_cb(f_dst, &user_data);
+ facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
+ }
+
+ if (op_params->track_active) {
+ /* Unlike other types, we can track active without it being selected. */
+ BM_mesh_active_face_set(bm, f_dst_last);
+ }
+ return flush;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Main Operator for vert/edge/face tag
+ * \{ */
+
+static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op);
+
+static bool uv_shortest_path_pick_ex(Scene *scene,
+ Depsgraph *depsgraph,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ const char uv_selectmode = ED_uvedit_select_mode_get(scene);
+ bool ok = false;
+ int flush = 0;
+
+ if (ELEM(NULL, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
+ /* pass */
+ }
+ else if (ele_src->head.htype == BM_FACE) {
+ flush = mouse_mesh_uv_shortest_path_face(scene,
+ obedit,
+ op_params,
+ (BMFace *)ele_src,
+ (BMFace *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ ok = true;
+ }
+ else if (ele_src->head.htype == BM_LOOP) {
+ flush = mouse_mesh_uv_shortest_path_vert(scene,
+ obedit,
+ op_params,
+ (BMLoop *)ele_src,
+ (BMLoop *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ ok = true;
+ }
+
+ if (ok) {
+ if (flush != 0) {
+ const bool select = (flush == 1);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ if (uv_selectmode & UV_SELECT_EDGE) {
+ /* Special case as we don't use true edge selection,
+ * flush the selection from the vertices. */
+ BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX);
+ }
+ }
+ ED_uvedit_select_sync_flush(scene->toolsettings, em, select);
+ }
+
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
+ }
+ else {
+ Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
+ BKE_mesh_batch_cache_dirty_tag(obedit_eval->data, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT);
+ }
+ /* Only for region redraw. */
+ WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
+ }
+
+ return ok;
+}
+
+static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Scene *scene = CTX_data_scene(C);
+ const ToolSettings *ts = scene->toolsettings;
+ const char uv_selectmode = ED_uvedit_select_mode_get(scene);
+
+ /* We could support this, it needs further testing. */
+ if (RNA_struct_property_is_set(op->ptr, "index")) {
+ return uv_shortest_path_pick_exec(C, op);
+ }
+
+ struct PathSelectParams op_params;
+ path_select_params_from_op(op, &op_params);
+
+ /* Set false if we support edge tagging. */
+ op_params.track_active = true;
+
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+
+ float co[2];
+
+ const ARegion *region = CTX_wm_region(C);
+
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ float aspect_y;
+ {
+ float aspx, aspy;
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ }
+
+ UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+
+ BMElem *ele_src = NULL, *ele_dst = NULL;
+
+ if (uv_selectmode == UV_SELECT_FACE) {
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ if (!uv_find_nearest_face(scene, obedit, co, &hit)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
+ /* Check selection? */
+
+ ele_src = (BMElem *)f_src;
+ ele_dst = (BMElem *)hit.efa;
+ }
+
+ else if (uv_selectmode & UV_SELECT_EDGE) {
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ if (!uv_find_nearest_edge(scene, obedit, co, &hit)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BMLoop *l_src = NULL;
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ BMEdge *e_src = BM_mesh_active_edge_get(bm);
+ if (e_src != NULL) {
+ l_src = uv_find_nearest_loop_from_edge(scene, obedit, e_src, co);
+ }
+ }
+ else {
+ l_src = ED_uvedit_active_edge_loop_get(bm);
+ if (l_src != NULL) {
+ if ((!uvedit_uv_select_test(scene, l_src, cd_loop_uv_offset)) &&
+ (!uvedit_uv_select_test(scene, l_src->next, cd_loop_uv_offset))) {
+ l_src = NULL;
+ }
+ ele_src = (BMElem *)l_src;
+ }
+ }
+ ele_src = (BMElem *)l_src;
+ ele_dst = (BMElem *)hit.l;
+ }
+ else {
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ if (!uv_find_nearest_vert(scene, obedit, co, 0.0f, &hit)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BMLoop *l_src = NULL;
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ BMVert *v_src = BM_mesh_active_vert_get(bm);
+ if (v_src != NULL) {
+ l_src = uv_find_nearest_loop_from_vert(scene, obedit, v_src, co);
+ }
+ }
+ else {
+ l_src = ED_uvedit_active_vert_loop_get(bm);
+ if (l_src != NULL) {
+ if (!uvedit_uv_select_test(scene, l_src, cd_loop_uv_offset)) {
+ l_src = NULL;
+ }
+ }
+ }
+ ele_src = (BMElem *)l_src;
+ ele_dst = (BMElem *)hit.l;
+ }
+
+ if (ele_src == NULL || ele_dst == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ uv_shortest_path_pick_ex(
+ scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset);
+
+ /* To support redo. */
+ int index;
+ if (uv_selectmode & UV_SELECT_FACE) {
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ index = BM_elem_index_get(ele_dst);
+ }
+ else if (uv_selectmode & UV_SELECT_EDGE) {
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+ index = BM_elem_index_get(ele_dst);
+ }
+ else {
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+ index = BM_elem_index_get(ele_dst);
+ }
+ RNA_int_set(op->ptr, "index", index);
+
+ return OPERATOR_FINISHED;
+}
+
+static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ const char uv_selectmode = ED_uvedit_select_mode_get(scene);
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ float aspect_y;
+ {
+ float aspx, aspy;
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ }
+
+ const int index = RNA_int_get(op->ptr, "index");
+
+ BMElem *ele_src, *ele_dst;
+
+ if (uv_selectmode & UV_SELECT_FACE) {
+ if (index < 0 || index >= bm->totface) {
+ return OPERATOR_CANCELLED;
+ }
+ if (!(ele_src = (BMElem *)BM_mesh_active_face_get(bm, false, false)) ||
+ !(ele_dst = (BMElem *)BM_face_at_index_find_or_table(bm, index))) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else if (uv_selectmode & UV_SELECT_EDGE) {
+ if (index < 0 || index >= bm->totloop) {
+ return OPERATOR_CANCELLED;
+ }
+ if (!(ele_src = (BMElem *)ED_uvedit_active_edge_loop_get(bm)) ||
+ !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index))) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ if (index < 0 || index >= bm->totloop) {
+ return OPERATOR_CANCELLED;
+ }
+ if (!(ele_src = (BMElem *)ED_uvedit_active_vert_loop_get(bm)) ||
+ !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index))) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ struct PathSelectParams op_params;
+ path_select_params_from_op(op, &op_params);
+ op_params.track_active = true;
+
+ if (!uv_shortest_path_pick_ex(
+ scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void UV_OT_shortest_path_pick(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Pick Shortest Path";
+ ot->idname = "UV_OT_shortest_path_pick";
+ ot->description = "Select shortest path between two selections";
+
+ /* api callbacks */
+ ot->invoke = uv_shortest_path_pick_invoke;
+ ot->exec = uv_shortest_path_pick_exec;
+ ot->poll = ED_operator_uvedit;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ path_select_properties(ot);
+
+ /* use for redo */
+ prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select Path Between Existing Selection
+ * \{ */
+
+static int uv_shortest_path_select_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ const char uv_selectmode = ED_uvedit_select_mode_get(scene);
+ bool found_valid_elements = false;
+
+ float aspect_y;
+ {
+ Object *obedit = CTX_data_edit_object(C);
+ float aspx, aspy;
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ }
+
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
+ view_layer, CTX_wm_view3d(C), &objects_len);
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ BMElem *ele_src = NULL, *ele_dst = NULL;
+
+ /* Find 2x elements. */
+ {
+ BMElem **ele_array = NULL;
+ int ele_array_len = 0;
+ if (uv_selectmode & UV_SELECT_FACE) {
+ ele_array = (BMElem **)ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len);
+ }
+ else if (uv_selectmode & UV_SELECT_EDGE) {
+ ele_array = (BMElem **)ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len);
+ }
+ else {
+ ele_array = (BMElem **)ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len);
+ }
+
+ if (ele_array_len == 2) {
+ ele_src = ele_array[0];
+ ele_dst = ele_array[1];
+ }
+ MEM_freeN(ele_array);
+ }
+
+ if (ele_src && ele_dst) {
+ struct PathSelectParams op_params;
+ path_select_params_from_op(op, &op_params);
+
+ uv_shortest_path_pick_ex(
+ scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset);
+
+ found_valid_elements = true;
+ }
+ }
+ MEM_freeN(objects);
+
+ if (!found_valid_elements) {
+ BKE_report(
+ op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void UV_OT_shortest_path_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Shortest Path";
+ ot->idname = "UV_OT_shortest_path_select";
+ ot->description = "Selected shortest path between two vertices/edges/faces";
+
+ /* api callbacks */
+ ot->exec = uv_shortest_path_select_exec;
+ ot->poll = ED_operator_editmesh;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ path_select_properties(ot);
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c
new file mode 100644
index 00000000000..07bec2da1ae
--- /dev/null
+++ b/source/blender/editors/uvedit/uvedit_rip.c
@@ -0,0 +1,981 @@
+/*
+ * 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 eduv
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_ghash.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_math.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_editmesh.h"
+#include "BKE_layer.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_screen.h"
+#include "ED_transform.h"
+#include "ED_uvedit.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_view2d.h"
+
+#include "uvedit_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name UV Loop Rip Data Struct
+ * \{ */
+
+/** Unordered loop data, stored in #BMLoop.head.index. */
+typedef struct ULData {
+ /** When this UV is selected as well as the next UV. */
+ uint is_select_edge : 1;
+ /**
+ * When only this UV is selected and none of the other UV's
+ * around the connected fan are attached to an edge.
+ *
+ * In this case there is no need to detect contiguous loops,
+ * each isolated case is handled on it's own, no need to walk over selected edges.
+ *
+ * \note This flag isn't flushed to other loops which could also have this enabled.
+ * Currently it's not necessary since we can start off on any one of these loops,
+ * then walk onto the other loops around the uv-fan, without having the flag to be
+ * set on all loops.
+ */
+ uint is_select_vert_single : 1;
+ /** This could be a face-tag. */
+ uint is_select_all : 1;
+ /** Use when building the rip-pairs stack. */
+ uint in_stack : 1;
+ /** Set once this has been added into a #UVRipPairs. */
+ uint in_rip_pairs : 1;
+ /** The side this loop is part of. */
+ uint side : 1;
+ /**
+ * Paranoid check to ensure we don't enter eternal loop swapping sides,
+ * this could happen with float precision error, making a swap to measure as slightly better
+ * depending on the order of addition.
+ */
+ uint side_was_swapped : 1;
+} ULData;
+
+/** Ensure this fits in an int (loop index). */
+BLI_STATIC_ASSERT(sizeof(ULData) <= sizeof(int), "");
+
+BLI_INLINE ULData *UL(BMLoop *l)
+{
+ return (ULData *)&l->head.index;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Utilities
+ * \{ */
+
+static BMLoop *bm_loop_find_other_radial_loop_with_visible_face(BMLoop *l_src,
+ const int cd_loop_uv_offset)
+{
+ BMLoop *l_other = NULL;
+ BMLoop *l_iter = l_src->radial_next;
+ if (l_iter != l_src) {
+ do {
+ if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) && UL(l_iter)->is_select_edge &&
+ BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) {
+ /* Check UV's are contiguous. */
+ if (l_other == NULL) {
+ l_other = l_iter;
+ }
+ else {
+ /* Only use when there is a single alternative. */
+ l_other = NULL;
+ break;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_src);
+ }
+ return l_other;
+}
+
+static BMLoop *bm_loop_find_other_fan_loop_with_visible_face(BMLoop *l_src,
+ BMVert *v_src,
+ const int cd_loop_uv_offset)
+{
+ BLI_assert(BM_vert_in_edge(l_src->e, v_src));
+ BMLoop *l_other = NULL;
+ BMLoop *l_iter = l_src->radial_next;
+ if (l_iter != l_src) {
+ do {
+ if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) &&
+ BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) {
+ /* Check UV's are contiguous. */
+ if (l_other == NULL) {
+ l_other = l_iter;
+ }
+ else {
+ /* Only use when there is a single alternative. */
+ l_other = NULL;
+ break;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_src);
+ }
+ if (l_other != NULL) {
+ if (l_other->v == v_src) {
+ /* do nothing. */
+ }
+ else if (l_other->next->v == v_src) {
+ l_other = l_other->next;
+ }
+ else if (l_other->prev->v == v_src) {
+ l_other = l_other->prev;
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ return l_other;
+}
+
+/**
+ * A version of #BM_vert_step_fan_loop that checks UV's.
+ */
+static BMLoop *bm_vert_step_fan_loop_uv(BMLoop *l, BMEdge **e_step, const int cd_loop_uv_offset)
+{
+ BMEdge *e_prev = *e_step;
+ BMLoop *l_next;
+ if (l->e == e_prev) {
+ l_next = l->prev;
+ }
+ else if (l->prev->e == e_prev) {
+ l_next = l;
+ }
+ else {
+ BLI_assert(0);
+ return NULL;
+ }
+
+ *e_step = l_next->e;
+
+ return bm_loop_find_other_fan_loop_with_visible_face(l_next, l->v, cd_loop_uv_offset);
+}
+
+static void bm_loop_uv_select_single_vert_validate(BMLoop *l_init, const int cd_loop_uv_offset)
+{
+ const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset);
+ BMIter liter;
+ BMLoop *l;
+ bool is_single_vert = true;
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ if (UL(l->prev)->is_select_edge || UL(l)->is_select_edge) {
+ is_single_vert = false;
+ break;
+ }
+ }
+ }
+ if (is_single_vert == false) {
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ if (UL(l)->is_select_vert_single) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ UL(l)->is_select_vert_single = false;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * The corner return values calculate the angle between both loops,
+ * the edge values pick the closest to the either edge (ignoring the center).
+ *
+ * \param dir: Direction to calculate the angle to (normalized and aspect corrected).
+ */
+static void bm_loop_calc_uv_angle_from_dir(BMLoop *l,
+ const float dir[2],
+ const float aspect_y,
+ const int cd_loop_uv_offset,
+ float *r_corner_angle,
+ float *r_edge_angle,
+ int *r_edge_index)
+{
+ /* Calculate 3 directions, return the shortest angle. */
+ float dir_test[3][2];
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ sub_v2_v2v2(dir_test[0], luv->uv, luv_prev->uv);
+ sub_v2_v2v2(dir_test[2], luv->uv, luv_next->uv);
+ dir_test[0][1] /= aspect_y;
+ dir_test[2][1] /= aspect_y;
+
+ normalize_v2(dir_test[0]);
+ normalize_v2(dir_test[2]);
+
+ /* Calculate the orthogonal line (same as negating one, then adding). */
+ sub_v2_v2v2(dir_test[1], dir_test[0], dir_test[2]);
+ normalize_v2(dir_test[1]);
+
+ /* Rotate 90 degrees. */
+ SWAP(float, dir_test[1][0], dir_test[1][1]);
+ dir_test[1][1] *= -1.0f;
+
+ if (BM_face_uv_calc_cross(l->f, cd_loop_uv_offset) > 0.0f) {
+ negate_v2(dir_test[1]);
+ }
+
+ const float angles[3] = {
+ angle_v2v2(dir, dir_test[0]),
+ angle_v2v2(dir, dir_test[1]),
+ angle_v2v2(dir, dir_test[2]),
+ };
+
+ /* Set the corner values. */
+ *r_corner_angle = angles[1];
+
+ /* Set the edge values. */
+ if (angles[0] < angles[2]) {
+ *r_edge_angle = angles[0];
+ *r_edge_index = -1;
+ }
+ else {
+ *r_edge_angle = angles[2];
+ *r_edge_index = 1;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Single
+ * \{ */
+
+typedef struct UVRipSingle {
+ /** Walk around the selected UV point, store #BMLoop. */
+ GSet *loops;
+} UVRipSingle;
+
+/**
+ * Handle single loop, the following cases:
+ *
+ * - An isolated fan, without a shared UV edge to other fans which share the same coordinate,
+ * in this case we just need to pick the closest fan to \a co.
+ *
+ * - In the case of contiguous loops (part of the same fan).
+ * Rip away the loops connected to the closest edge.
+ *
+ * - In the case of 2 contiguous loops.
+ * Rip the closest loop away.
+ *
+ * \note This matches the behavior of edit-mesh rip tool.
+ */
+static UVRipSingle *uv_rip_single_from_loop(BMLoop *l_init_orig,
+ const float co[2],
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ UVRipSingle *rip = MEM_callocN(sizeof(*rip), __func__);
+ const float *co_center =
+ (((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_init_orig, cd_loop_uv_offset))->uv);
+ rip->loops = BLI_gset_ptr_new(__func__);
+
+ /* Track the closest loop, start walking from this so in the event we have multiple
+ * disconnected fans, we can rip away loops connected to this one. */
+ BMLoop *l_init = NULL;
+ BMLoop *l_init_edge = NULL;
+ float corner_angle_best = FLT_MAX;
+ float edge_angle_best = FLT_MAX;
+ int edge_index_best = 0; /* -1 or +1 (never center). */
+
+ /* Calculate the direction from the cursor with aspect correction. */
+ float dir_co[2];
+ sub_v2_v2v2(dir_co, co_center, co);
+ dir_co[1] /= aspect_y;
+ if (UNLIKELY(normalize_v2(dir_co) == 0.0)) {
+ dir_co[1] = 1.0f;
+ }
+
+ int uv_fan_count_all = 0;
+ {
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, l_init_orig->v, BM_LOOPS_OF_VERT) {
+ if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(co_center, luv->uv)) {
+ uv_fan_count_all += 1;
+ /* Clear at the same time. */
+ UL(l)->is_select_vert_single = true;
+ UL(l)->side = 0;
+ BLI_gset_add(rip->loops, l);
+
+ /* Update `l_init_close` */
+ float corner_angle_test;
+ float edge_angle_test;
+ int edge_index_test;
+ bm_loop_calc_uv_angle_from_dir(l,
+ dir_co,
+ aspect_y,
+ cd_loop_uv_offset,
+ &corner_angle_test,
+ &edge_angle_test,
+ &edge_index_test);
+ if ((corner_angle_best == FLT_MAX) || (corner_angle_test < corner_angle_best)) {
+ corner_angle_best = corner_angle_test;
+ l_init = l;
+ }
+
+ /* Trick so we don't consider concave corners further away than they should be. */
+ edge_angle_test = min_ff(corner_angle_test, edge_angle_test);
+
+ if ((edge_angle_best == FLT_MAX) || (edge_angle_test < edge_angle_best)) {
+ edge_angle_best = edge_angle_test;
+ edge_index_best = edge_index_test;
+ l_init_edge = l;
+ }
+ }
+ }
+ }
+ }
+
+ /* Walk around the `l_init` in both directions of the UV fan. */
+ int uv_fan_count_contiguous = 1;
+ UL(l_init)->side = 1;
+ for (int i = 0; i < 2; i += 1) {
+ BMEdge *e_prev = i ? l_init->e : l_init->prev->e;
+ BMLoop *l_iter = l_init;
+ while (((l_iter = bm_vert_step_fan_loop_uv(l_iter, &e_prev, cd_loop_uv_offset)) != l_init) &&
+ (l_iter != NULL) && (UL(l_iter)->side == 0)) {
+ uv_fan_count_contiguous += 1;
+ /* Keep. */
+ UL(l_iter)->side = 1;
+ }
+ /* May be useful to know if the fan is closed, currently it's not needed. */
+#if 0
+ if (l_iter == l_init) {
+ is_closed = true;
+ }
+#endif
+ }
+
+ if (uv_fan_count_contiguous != uv_fan_count_all) {
+ /* Simply rip off the current fan, all tagging is done. */
+ }
+ else {
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l = BLI_gsetIterator_getKey(&gs_iter);
+ UL(l)->side = 0;
+ }
+
+ if (uv_fan_count_contiguous <= 2) {
+ /* Simple case, rip away the closest loop. */
+ UL(l_init)->side = 1;
+ }
+ else {
+ /* Rip away from the closest edge. */
+ BMLoop *l_radial_init = (edge_index_best == -1) ? l_init_edge->prev : l_init_edge;
+ BMLoop *l_radial_iter = l_radial_init;
+ do {
+ if (BM_loop_uv_share_edge_check(l_radial_init, l_radial_iter, cd_loop_uv_offset)) {
+ BMLoop *l = (l_radial_iter->v == l_init->v) ? l_radial_iter : l_radial_iter->next;
+ BLI_assert(l->v == l_init->v);
+ /* Keep. */
+ UL(l)->side = 1;
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_init);
+ }
+ }
+
+ return rip;
+}
+
+static void uv_rip_single_free(UVRipSingle *rip)
+{
+ BLI_gset_free(rip->loops, NULL);
+ MEM_freeN(rip);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Loop Pairs
+ * \{ */
+
+typedef struct UVRipPairs {
+ /** Walk along the UV selection, store #BMLoop. */
+ GSet *loops;
+} UVRipPairs;
+
+static void uv_rip_pairs_add(UVRipPairs *rip, BMLoop *l)
+{
+ ULData *ul = UL(l);
+ BLI_assert(!BLI_gset_haskey(rip->loops, l));
+ BLI_assert(ul->in_rip_pairs == false);
+ ul->in_rip_pairs = true;
+ BLI_gset_add(rip->loops, l);
+}
+
+static void uv_rip_pairs_remove(UVRipPairs *rip, BMLoop *l)
+{
+ ULData *ul = UL(l);
+ BLI_assert(BLI_gset_haskey(rip->loops, l));
+ BLI_assert(ul->in_rip_pairs == true);
+ ul->in_rip_pairs = false;
+ BLI_gset_remove(rip->loops, l, NULL);
+}
+
+/**
+ * \note While this isn't especially efficient,
+ * this is only needed for rip-pairs end-points (only two per contiguous selection loop).
+ */
+static float uv_rip_pairs_calc_uv_angle(BMLoop *l_init,
+ uint side,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ BMIter liter;
+ const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset);
+ float angle_of_side = 0.0f;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ if (UL(l)->in_rip_pairs) {
+ if (UL(l)->side == side) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ float dir_prev[2], dir_next[2];
+ sub_v2_v2v2(dir_prev, luv_prev->uv, luv->uv);
+ sub_v2_v2v2(dir_next, luv_next->uv, luv->uv);
+ dir_prev[1] /= aspect_y;
+ dir_next[1] /= aspect_y;
+ const float luv_angle = angle_v2v2(dir_prev, dir_next);
+ if (LIKELY(isfinite(luv_angle))) {
+ angle_of_side += luv_angle;
+ }
+ }
+ }
+ }
+ }
+ return angle_of_side;
+}
+
+static int uv_rip_pairs_loop_count_on_side(BMLoop *l_init, uint side, const int cd_loop_uv_offset)
+{
+ const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset);
+ int count = 0;
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ if (UL(l)->in_rip_pairs) {
+ if (UL(l)->side == side) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ count += 1;
+ }
+ }
+ }
+ }
+ return count;
+}
+
+static bool uv_rip_pairs_loop_change_sides_test(BMLoop *l_switch,
+ BMLoop *l_target,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ const int side_a = UL(l_switch)->side;
+ const int side_b = UL(l_target)->side;
+
+ BLI_assert(UL(l_switch)->side != UL(l_target)->side);
+
+ /* First, check if this is a simple grid topology,
+ * in that case always choose the adjacent edge. */
+ const int count_a = uv_rip_pairs_loop_count_on_side(l_switch, side_a, cd_loop_uv_offset);
+ const int count_b = uv_rip_pairs_loop_count_on_side(l_target, side_b, cd_loop_uv_offset);
+ if (count_a + count_b == 4) {
+ return count_a > count_b;
+ }
+ else {
+ const float angle_a_before = uv_rip_pairs_calc_uv_angle(
+ l_switch, side_a, aspect_y, cd_loop_uv_offset);
+ const float angle_b_before = uv_rip_pairs_calc_uv_angle(
+ l_target, side_b, aspect_y, cd_loop_uv_offset);
+
+ UL(l_switch)->side = side_b;
+
+ const float angle_a_after = uv_rip_pairs_calc_uv_angle(
+ l_switch, side_a, aspect_y, cd_loop_uv_offset);
+ const float angle_b_after = uv_rip_pairs_calc_uv_angle(
+ l_target, side_b, aspect_y, cd_loop_uv_offset);
+
+ UL(l_switch)->side = side_a;
+
+ return fabsf(angle_a_before - angle_b_before) > fabsf(angle_a_after - angle_b_after);
+ }
+}
+
+/**
+ * Create 2x sides of a UV rip-pairs, the result is unordered, supporting non-contiguous rails.
+ *
+ * \param l_init: A loop on a boundary which can be used to initialize flood-filling.
+ * This will always be added to the first side. Other loops will be added to the second side.
+ *
+ * \note We could have more than two sides, however in practice this almost never happens.
+ */
+static UVRipPairs *uv_rip_pairs_from_loop(BMLoop *l_init,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ UVRipPairs *rip = MEM_callocN(sizeof(*rip), __func__);
+ rip->loops = BLI_gset_ptr_new(__func__);
+
+ /* We can rely on this stack being small, as we're walking down two sides of an edge loop,
+ * so the stack wont be much larger than the total number of fans at any one vertex. */
+ BLI_SMALLSTACK_DECLARE(stack, BMLoop *);
+
+ /* Needed for cases when we walk onto loops which already have a side assigned,
+ * in this case we need to pick a better side (see #uv_rip_pairs_loop_change_sides_test)
+ * and put the loop back in the stack,
+ * which is needed in the case adjacent loops should also switch sides. */
+#define UV_SET_SIDE_AND_REMOVE_FROM_RAIL(loop, side_value) \
+ { \
+ BLI_assert(UL(loop)->side_was_swapped == false); \
+ BLI_assert(UL(loop)->side != side_value); \
+ if (!UL(loop)->in_stack) { \
+ BLI_SMALLSTACK_PUSH(stack, loop); \
+ UL(loop)->in_stack = true; \
+ } \
+ if (UL(loop)->in_rip_pairs) { \
+ uv_rip_pairs_remove(rip, loop); \
+ } \
+ UL(loop)->side = side_value; \
+ UL(loop)->side_was_swapped = true; \
+ }
+
+ /* Initialize the stack. */
+ BLI_SMALLSTACK_PUSH(stack, l_init);
+ UL(l_init)->in_stack = true;
+
+ BMLoop *l_step;
+ while ((l_step = BLI_SMALLSTACK_POP(stack))) {
+ int side = UL(l_step)->side;
+ UL(l_step)->in_stack = false;
+
+ /* Note that we could add all loops into the rip-pairs when adding into the stack,
+ * however this complicates removal, so add into the rip-pairs when popping from the stack. */
+ uv_rip_pairs_add(rip, l_step);
+
+ /* Add to the other side if it exists. */
+ if (UL(l_step)->is_select_edge) {
+ BMLoop *l_other = bm_loop_find_other_radial_loop_with_visible_face(l_step,
+ cd_loop_uv_offset);
+ if (l_other != NULL) {
+ if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
+ BLI_SMALLSTACK_PUSH(stack, l_other);
+ UL(l_other)->in_stack = true;
+ UL(l_other)->side = !side;
+ }
+ else {
+ if (UL(l_other)->side == side) {
+ if (UL(l_other)->side_was_swapped == false) {
+ UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, !side);
+ }
+ }
+ }
+ }
+
+ /* Add the next loop along the edge on the same side. */
+ l_other = l_step->next;
+ if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
+ BLI_SMALLSTACK_PUSH(stack, l_other);
+ UL(l_other)->in_stack = true;
+ UL(l_other)->side = side;
+ }
+ else {
+ if (UL(l_other)->side != side) {
+ if ((UL(l_other)->side_was_swapped == false) &&
+ uv_rip_pairs_loop_change_sides_test(l_other, l_step, aspect_y, cd_loop_uv_offset)) {
+ UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, side);
+ }
+ }
+ }
+ }
+
+ /* Walk over the fan of loops, starting from `l_step` in both directions. */
+ for (int i = 0; i < 2; i++) {
+ BMLoop *l_radial_first = i ? l_step : l_step->prev;
+ if (l_radial_first != l_radial_first->radial_next) {
+ BMEdge *e_radial = l_radial_first->e;
+ BMLoop *l_radial_iter = l_radial_first->radial_next;
+ do {
+ /* Not a boundary and visible. */
+ if (!UL(l_radial_iter)->is_select_edge &&
+ BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
+ BMLoop *l_other = (l_radial_iter->v == l_step->v) ? l_radial_iter :
+ l_radial_iter->next;
+ BLI_assert(l_other->v == l_step->v);
+ if (BM_edge_uv_share_vert_check(e_radial, l_other, l_step, cd_loop_uv_offset)) {
+ if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
+ BLI_SMALLSTACK_PUSH(stack, l_other);
+ UL(l_other)->in_stack = true;
+ UL(l_other)->side = side;
+ }
+ else {
+ if (UL(l_other)->side != side) {
+ if ((UL(l_other)->side_was_swapped == false) &&
+ uv_rip_pairs_loop_change_sides_test(
+ l_other, l_step, aspect_y, cd_loop_uv_offset)) {
+ UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, side);
+ }
+ }
+ }
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first);
+ }
+ }
+ }
+
+#undef UV_SET_SIDE_AND_REMOVE_FROM_RAIL
+
+ return rip;
+}
+
+static void uv_rip_pairs_free(UVRipPairs *rip)
+{
+ BLI_gset_free(rip->loops, NULL);
+ MEM_freeN(rip);
+}
+
+/**
+ * This is an approximation, it's easily good enough for our purpose.
+ */
+static bool uv_rip_pairs_calc_center_and_direction(UVRipPairs *rip,
+ const int cd_loop_uv_offset,
+ float r_center[2],
+ float r_dir_side[2][2])
+{
+ zero_v2(r_center);
+ int center_total = 0;
+ int side_total[2] = {0, 0};
+
+ for (int i = 0; i < 2; i++) {
+ zero_v2(r_dir_side[i]);
+ }
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l = BLI_gsetIterator_getKey(&gs_iter);
+ int side = UL(l)->side;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ add_v2_v2(r_center, luv->uv);
+
+ float dir[2];
+ if (!UL(l)->is_select_edge) {
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ sub_v2_v2v2(dir, luv_next->uv, luv->uv);
+ add_v2_v2(r_dir_side[side], dir);
+ }
+ if (!UL(l->prev)->is_select_edge) {
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ sub_v2_v2v2(dir, luv_prev->uv, luv->uv);
+ add_v2_v2(r_dir_side[side], dir);
+ }
+ side_total[side] += 1;
+ }
+ center_total += BLI_gset_len(rip->loops);
+
+ for (int i = 0; i < 2; i++) {
+ normalize_v2(r_dir_side[i]);
+ }
+ mul_v2_fl(r_center, 1.0f / center_total);
+
+ /* If only a single side is selected, don't handle this rip-pairs. */
+ return side_total[0] && side_total[1];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Main Function
+ * \{ */
+
+/**
+ * \return true when a change was made.
+ */
+static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const float aspect_y)
+{
+ Mesh *me = (Mesh *)obedit->data;
+ BMEditMesh *em = me->edit_mesh;
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BMFace *efa;
+ BMIter iter, liter;
+ BMLoop *l;
+
+ const ULData ul_clear = {0};
+
+ bool changed = false;
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_set(efa, BM_ELEM_TAG, uvedit_face_visible_test(scene, efa));
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ ULData *ul = UL(l);
+ *ul = ul_clear;
+ }
+ }
+ bm->elem_index_dirty |= BM_LOOP;
+
+ bool is_select_all_any = false;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ bool is_all = true;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ if (luv_next->flag & MLOOPUV_VERTSEL) {
+ UL(l)->is_select_edge = true;
+ }
+ else {
+ if ((luv_prev->flag & MLOOPUV_VERTSEL) == 0) {
+ /* #bm_loop_uv_select_single_vert_validate validates below. */
+ UL(l)->is_select_vert_single = true;
+ }
+ }
+ }
+ else {
+ is_all = false;
+ }
+ }
+ if (is_all) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ UL(l)->is_select_all = true;
+ }
+ is_select_all_any = true;
+ }
+ }
+ }
+
+ /* Remove #ULData.is_select_vert_single when connected to selected edges. */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (UL(l)->is_select_vert_single) {
+ bm_loop_uv_select_single_vert_validate(l, cd_loop_uv_offset);
+ }
+ }
+ }
+ }
+
+ /* Special case: if we have selected faces, isolated them.
+ * This isn't a rip, however it's useful for users as a quick way
+ * to detach the selection.
+ *
+ * We could also extract an edge loop from the boundary
+ * however in practice it's not that useful, see T78751. */
+ if (is_select_all_any) {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (!UL(l)->is_select_all) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ changed = true;
+ }
+ }
+ }
+ }
+ return changed;
+ }
+
+ /* Extract loop pairs or single loops. */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (UL(l)->is_select_edge) {
+ if (!UL(l)->in_rip_pairs) {
+ UVRipPairs *rip = uv_rip_pairs_from_loop(l, aspect_y, cd_loop_uv_offset);
+ float center[2];
+ float dir_cursor[2];
+ float dir_side[2][2];
+ int side_from_cursor = -1;
+ if (uv_rip_pairs_calc_center_and_direction(rip, cd_loop_uv_offset, center, dir_side)) {
+ for (int i = 0; i < 2; i++) {
+ sub_v2_v2v2(dir_cursor, center, co);
+ normalize_v2(dir_cursor);
+ }
+ side_from_cursor = (dot_v2v2(dir_side[0], dir_cursor) -
+ dot_v2v2(dir_side[1], dir_cursor)) < 0.0f;
+ }
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter);
+ ULData *ul = UL(l_iter);
+ if (ul->side == side_from_cursor) {
+ uvedit_uv_select_disable(scene, em, l_iter, cd_loop_uv_offset);
+ changed = true;
+ }
+ /* Ensure we don't operate on these again. */
+ *ul = ul_clear;
+ }
+ uv_rip_pairs_free(rip);
+ }
+ }
+ else if (UL(l)->is_select_vert_single) {
+ UVRipSingle *rip = uv_rip_single_from_loop(l, co, aspect_y, cd_loop_uv_offset);
+ /* We only ever use one side. */
+ const int side_from_cursor = 0;
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter);
+ ULData *ul = UL(l_iter);
+ if (ul->side == side_from_cursor) {
+ uvedit_uv_select_disable(scene, em, l_iter, cd_loop_uv_offset);
+ changed = true;
+ }
+ /* Ensure we don't operate on these again. */
+ *ul = ul_clear;
+ }
+ uv_rip_single_free(rip);
+ }
+ }
+ }
+ }
+ return changed;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Operator
+ * \{ */
+
+static int uv_rip_exec(bContext *C, wmOperator *op)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ bool changed_multi = false;
+
+ float co[2];
+ RNA_float_get_array(op->ptr, "location", co);
+
+ float aspx, aspy;
+ {
+ /* Note that we only want to run this on the */
+ Object *obedit = CTX_data_edit_object(C);
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ }
+ const float aspect_y = aspx / aspy;
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+
+ if (uv_rip_object(scene, obedit, co, aspect_y)) {
+ changed_multi = true;
+ uvedit_live_unwrap_update(sima, scene, obedit);
+ DEG_id_tag_update(obedit->data, 0);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+ }
+ }
+ MEM_freeN(objects);
+
+ if (!changed_multi) {
+ BKE_report(op->reports, RPT_ERROR, "Rip failed");
+ return OPERATOR_CANCELLED;
+ }
+ return OPERATOR_FINISHED;
+}
+
+static int uv_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ float co[2];
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+ RNA_float_set_array(op->ptr, "location", co);
+
+ return uv_rip_exec(C, op);
+}
+
+void UV_OT_rip(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "UV Rip";
+ ot->description = "Rip selected vertices or a selected region";
+ ot->idname = "UV_OT_rip";
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec = uv_rip_exec;
+ ot->invoke = uv_rip_invoke;
+ ot->poll = ED_operator_uvedit;
+
+ /* translation data */
+ Transform_Properties(ot, P_MIRROR_DUMMY);
+
+ /* properties */
+ RNA_def_float_vector(
+ ot->srna,
+ "location",
+ 2,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Location",
+ "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
+ -100.0f,
+ 100.0f);
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index cc9be9d48c1..4c354ab5940 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -88,12 +88,128 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
Object *obedit);
/* -------------------------------------------------------------------- */
+/** \name Active Selection Tracking
+ *
+ * Currently we don't store loops in the selection history,
+ * store face/edge/vert combinations (needed for UV path selection).
+ * \{ */
+
+void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
+{
+ BM_select_history_clear(bm);
+ BM_select_history_remove(bm, (BMElem *)l->f);
+ BM_select_history_remove(bm, (BMElem *)l->v);
+ BM_select_history_store_notest(bm, (BMElem *)l->f);
+ BM_select_history_store_notest(bm, (BMElem *)l->v);
+}
+
+BMLoop *ED_uvedit_active_vert_loop_get(BMesh *bm)
+{
+ BMEditSelection *ese = bm->selected.last;
+ if (ese && ese->prev) {
+ BMEditSelection *ese_prev = ese->prev;
+ if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) {
+ /* May be NULL. */
+ return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele);
+ }
+ }
+ return NULL;
+}
+
+void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
+{
+ BM_select_history_clear(bm);
+ BM_select_history_remove(bm, (BMElem *)l->f);
+ BM_select_history_remove(bm, (BMElem *)l->e);
+ BM_select_history_store_notest(bm, (BMElem *)l->f);
+ BM_select_history_store_notest(bm, (BMElem *)l->e);
+}
+
+BMLoop *ED_uvedit_active_edge_loop_get(BMesh *bm)
+{
+ BMEditSelection *ese = bm->selected.last;
+ if (ese && ese->prev) {
+ BMEditSelection *ese_prev = ese->prev;
+ if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) {
+ /* May be NULL. */
+ return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele);
+ }
+ }
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Visibility and Selection Utilities
* \{ */
-static void uv_select_island_limit_default(SpaceImage *sima, float r_limit[2])
+/**
+ * Intentionally don't return #UV_SELECT_ISLAND as it's not an element type.
+ * In this case return #UV_SELECT_VERTEX as a fallback.
+ */
+char ED_uvedit_select_mode_get(const Scene *scene)
{
- uvedit_pixel_to_float(sima, 0.05f, r_limit);
+ const ToolSettings *ts = scene->toolsettings;
+ char uv_selectmode = UV_SELECT_VERTEX;
+
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ if (ts->selectmode & SCE_SELECT_VERTEX) {
+ uv_selectmode = UV_SELECT_VERTEX;
+ }
+ else if (ts->selectmode & SCE_SELECT_EDGE) {
+ uv_selectmode = UV_SELECT_EDGE;
+ }
+ else if (ts->selectmode & SCE_SELECT_FACE) {
+ uv_selectmode = UV_SELECT_FACE;
+ }
+ }
+ else {
+ if (ts->uv_selectmode & UV_SELECT_VERTEX) {
+ uv_selectmode = UV_SELECT_VERTEX;
+ }
+ else if (ts->uv_selectmode & UV_SELECT_EDGE) {
+ uv_selectmode = UV_SELECT_EDGE;
+ }
+ else if (ts->uv_selectmode & UV_SELECT_FACE) {
+ uv_selectmode = UV_SELECT_FACE;
+ }
+ }
+ return uv_selectmode;
+}
+
+void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const bool select)
+{
+ /* bmesh API handles flushing but not on de-select */
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ if (ts->selectmode != SCE_SELECT_FACE) {
+ if (select == false) {
+ EDBM_deselect_flush(em);
+ }
+ else {
+ EDBM_select_flush(em);
+ }
+ }
+
+ if (select == false) {
+ BM_select_history_validate(em->bm);
+ }
+ }
+}
+
+/**
+ * Apply a penalty to elements that are already selected
+ * so elements that aren't already selected are prioritized.
+ *
+ * \note This is calculated in screen-space otherwise zooming in on a uv-vert and
+ * shift-selecting can consider an adjacent point close enough to add to
+ * the selection rather than de-selecting the closest.
+ */
+static float uv_select_penalty_default(SpaceImage *sima)
+{
+ float penalty[2];
+ uvedit_pixel_to_float(sima, 5.0f / (sima ? sima->zoom : 1.0f), penalty);
+ return len_v2(penalty);
}
static void uvedit_vertex_select_tagged(BMEditMesh *em,
@@ -108,7 +224,7 @@ static void uvedit_vertex_select_tagged(BMEditMesh *em,
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
}
}
}
@@ -119,9 +235,7 @@ bool uvedit_face_visible_test_ex(const ToolSettings *ts, BMFace *efa)
if (ts->uv_flag & UV_SYNC_SELECTION) {
return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
}
- else {
- return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT));
- }
+ return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT));
}
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
{
@@ -133,27 +247,47 @@ bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int c
if (ts->uv_flag & UV_SYNC_SELECTION) {
return (BM_elem_flag_test(efa, BM_ELEM_SELECT));
}
- else {
- BMLoop *l;
- MLoopUV *luv;
- BMIter liter;
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (!(luv->flag & MLOOPUV_VERTSEL)) {
- return false;
- }
- }
+ BMLoop *l;
+ MLoopUV *luv;
+ BMIter liter;
- return true;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (!(luv->flag & MLOOPUV_VERTSEL)) {
+ return false;
+ }
}
+ return true;
}
bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const int cd_loop_uv_offset)
{
return uvedit_face_select_test_ex(scene->toolsettings, efa, cd_loop_uv_offset);
}
-bool uvedit_face_select_set(const struct Scene *scene,
+void uvedit_face_select_set_with_sticky(const SpaceImage *sima,
+ const Scene *scene,
+ BMEditMesh *em,
+ BMFace *efa,
+ const bool select,
+ const bool do_history,
+ const int cd_loop_uv_offset)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
+ return;
+ }
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ uvedit_uv_select_set_with_sticky(
+ sima, scene, em, l_iter, select, do_history, cd_loop_uv_offset);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+void uvedit_face_select_set(const struct Scene *scene,
struct BMEditMesh *em,
struct BMFace *efa,
const bool select,
@@ -161,14 +295,14 @@ bool uvedit_face_select_set(const struct Scene *scene,
const int cd_loop_uv_offset)
{
if (select) {
- return uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset);
+ uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset);
}
else {
- return uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
}
}
-bool uvedit_face_select_enable(const Scene *scene,
+void uvedit_face_select_enable(const Scene *scene,
BMEditMesh *em,
BMFace *efa,
const bool do_history,
@@ -191,14 +325,10 @@ bool uvedit_face_select_enable(const Scene *scene,
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv->flag |= MLOOPUV_VERTSEL;
}
-
- return true;
}
-
- return false;
}
-bool uvedit_face_select_disable(const Scene *scene,
+void uvedit_face_select_disable(const Scene *scene,
BMEditMesh *em,
BMFace *efa,
const int cd_loop_uv_offset)
@@ -217,11 +347,7 @@ bool uvedit_face_select_disable(const Scene *scene,
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv->flag &= ~MLOOPUV_VERTSEL;
}
-
- return true;
}
-
- return false;
}
bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset)
@@ -230,30 +356,46 @@ bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_
if (ts->selectmode & SCE_SELECT_FACE) {
return BM_elem_flag_test(l->f, BM_ELEM_SELECT);
}
- else if (ts->selectmode == SCE_SELECT_EDGE) {
+ if (ts->selectmode == SCE_SELECT_EDGE) {
return BM_elem_flag_test(l->e, BM_ELEM_SELECT);
}
- else {
- return BM_elem_flag_test(l->v, BM_ELEM_SELECT) &&
- BM_elem_flag_test(l->next->v, BM_ELEM_SELECT);
- }
+ return BM_elem_flag_test(l->v, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(l->next->v, BM_ELEM_SELECT);
}
- else {
- MLoopUV *luv1, *luv2;
- luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ MLoopUV *luv1, *luv2;
- return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
- }
+ luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
}
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
{
return uvedit_edge_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset);
}
-void uvedit_edge_select_set(BMEditMesh *em,
- const Scene *scene,
+void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima,
+ const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l,
+ const bool select,
+ const bool do_history,
+ const uint cd_loop_uv_offset)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ uvedit_edge_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ return;
+ }
+
+ uvedit_uv_select_set_with_sticky(sima, scene, em, l, select, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_set_with_sticky(
+ sima, scene, em, l->next, select, do_history, cd_loop_uv_offset);
+}
+
+void uvedit_edge_select_set(const Scene *scene,
+ BMEditMesh *em,
BMLoop *l,
const bool select,
const bool do_history,
@@ -261,15 +403,15 @@ void uvedit_edge_select_set(BMEditMesh *em,
{
if (select) {
- uvedit_edge_select_enable(em, scene, l, do_history, cd_loop_uv_offset);
+ uvedit_edge_select_enable(scene, em, l, do_history, cd_loop_uv_offset);
}
else {
- uvedit_edge_select_disable(em, scene, l, cd_loop_uv_offset);
+ uvedit_edge_select_disable(scene, em, l, cd_loop_uv_offset);
}
}
-void uvedit_edge_select_enable(BMEditMesh *em,
- const Scene *scene,
+void uvedit_edge_select_enable(const Scene *scene,
+ BMEditMesh *em,
BMLoop *l,
const bool do_history,
const int cd_loop_uv_offset)
@@ -304,8 +446,8 @@ void uvedit_edge_select_enable(BMEditMesh *em,
}
}
-void uvedit_edge_select_disable(BMEditMesh *em,
- const Scene *scene,
+void uvedit_edge_select_disable(const Scene *scene,
+ BMEditMesh *em,
BMLoop *l,
const int cd_loop_uv_offset)
@@ -341,37 +483,90 @@ bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_lo
if (ts->selectmode & SCE_SELECT_FACE) {
return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT);
}
- else {
- return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT);
- }
- }
- else {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- return (luv->flag & MLOOPUV_VERTSEL) != 0;
+ return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT);
}
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ return (luv->flag & MLOOPUV_VERTSEL) != 0;
}
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
{
return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset);
}
-void uvedit_uv_select_set(BMEditMesh *em,
- const Scene *scene,
+void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima,
+ const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l,
+ const bool select,
+ const bool do_history,
+ const uint cd_loop_uv_offset)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ return;
+ }
+
+ const int sticky = sima->sticky;
+ switch (sticky) {
+ case SI_STICKY_DISABLE: {
+ uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ break;
+ }
+ default: {
+ /* #SI_STICKY_VERTEX or #SI_STICKY_LOC. */
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BMEdge *e_first, *e_iter;
+ e_first = e_iter = l->e;
+ do {
+ if (e_iter->l) {
+ BMLoop *l_radial_iter = e_iter->l;
+ do {
+ if (l_radial_iter->v == l->v) {
+ if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
+ bool do_select = false;
+ if (sticky == SI_STICKY_VERTEX) {
+ do_select = true;
+ }
+ else {
+ const MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_radial_iter,
+ cd_loop_uv_offset);
+ if (equals_v2v2(luv_other->uv, luv->uv)) {
+ do_select = true;
+ }
+ }
+
+ if (do_select) {
+ uvedit_uv_select_set(
+ scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
+ }
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
+ }
+ }
+}
+
+void uvedit_uv_select_set(const Scene *scene,
+ BMEditMesh *em,
BMLoop *l,
const bool select,
const bool do_history,
const int cd_loop_uv_offset)
{
if (select) {
- uvedit_uv_select_enable(em, scene, l, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_enable(scene, em, l, do_history, cd_loop_uv_offset);
}
else {
- uvedit_uv_select_disable(em, scene, l, cd_loop_uv_offset);
+ uvedit_uv_select_disable(scene, em, l, cd_loop_uv_offset);
}
}
-void uvedit_uv_select_enable(BMEditMesh *em,
- const Scene *scene,
+void uvedit_uv_select_enable(const Scene *scene,
+ BMEditMesh *em,
BMLoop *l,
const bool do_history,
const int cd_loop_uv_offset)
@@ -387,7 +582,7 @@ void uvedit_uv_select_enable(BMEditMesh *em,
}
if (do_history) {
- BM_select_history_remove(em->bm, (BMElem *)l->v);
+ BM_select_history_store(em->bm, (BMElem *)l->v);
}
}
else {
@@ -396,8 +591,8 @@ void uvedit_uv_select_enable(BMEditMesh *em,
}
}
-void uvedit_uv_select_disable(BMEditMesh *em,
- const Scene *scene,
+void uvedit_uv_select_disable(const Scene *scene,
+ BMEditMesh *em,
BMLoop *l,
const int cd_loop_uv_offset)
{
@@ -417,6 +612,31 @@ void uvedit_uv_select_disable(BMEditMesh *em,
}
}
+static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene,
+ BMLoop *l_src,
+ const int cd_loop_uv_offset)
+{
+ BMLoop *l_other = NULL;
+ BMLoop *l_iter = l_src->radial_next;
+ if (l_iter != l_src) {
+ do {
+ if (uvedit_face_visible_test(scene, l_iter->f) &&
+ BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) {
+ /* Check UV's are contiguous. */
+ if (l_other == NULL) {
+ l_other = l_iter;
+ }
+ else {
+ /* Only use when there is a single alternative. */
+ l_other = NULL;
+ break;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_src);
+ }
+ return l_other;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -667,9 +887,7 @@ bool ED_uvedit_nearest_uv(
*dist_sq = dist_best;
return true;
}
- else {
- return false;
- }
+ return false;
}
bool ED_uvedit_nearest_uv_multi(const Scene *scene,
@@ -692,7 +910,73 @@ bool ED_uvedit_nearest_uv_multi(const Scene *scene,
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Loop Select
+/** \name Find Nearest to Element
+ *
+ * These functions are quite specialized, useful when sync select is enabled
+ * and we want to pick an active UV vertex/edge from the active element which may
+ * have multiple UV's split out.
+ * \{ */
+
+BMLoop *uv_find_nearest_loop_from_vert(struct Scene *scene,
+ struct Object *obedit,
+ struct BMVert *v,
+ const float co[2])
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BMIter liter;
+ BMLoop *l;
+ BMLoop *l_found = NULL;
+ float dist_best_sq = FLT_MAX;
+
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ if (!uvedit_face_visible_test(scene, l->f)) {
+ continue;
+ }
+
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ const float dist_test_sq = len_squared_v2v2(co, luv->uv);
+ if (dist_test_sq < dist_best_sq) {
+ dist_best_sq = dist_test_sq;
+ l_found = l;
+ }
+ }
+ return l_found;
+}
+
+BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
+ struct Object *obedit,
+ struct BMEdge *e,
+ const float co[2])
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BMIter eiter;
+ BMLoop *l;
+ BMLoop *l_found = NULL;
+ float dist_best_sq = FLT_MAX;
+
+ BM_ITER_ELEM (l, &eiter, e, BM_LOOPS_OF_EDGE) {
+ if (!uvedit_face_visible_test(scene, l->f)) {
+ continue;
+ }
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
+ if (dist_test_sq < dist_best_sq) {
+ dist_best_sq = dist_test_sq;
+ l_found = l;
+ }
+ }
+ return l_found;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Edge Loop Select
* \{ */
static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first)
@@ -792,8 +1076,7 @@ static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em,
return true;
}
-static int uv_select_edgeloop(
- Scene *scene, Object *obedit, UvNearestHit *hit, const float limit[2], const bool extend)
+static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa;
@@ -809,7 +1092,7 @@ static int uv_select_edgeloop(
/* setup */
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
- vmap = BM_uv_vert_map_create(em->bm, limit, false, false);
+ vmap = BM_uv_vert_map_create(em->bm, false, false);
BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
@@ -882,7 +1165,7 @@ static int uv_select_edgeloop(
iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l);
if (iterv_curr->flag) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
}
}
}
@@ -896,13 +1179,89 @@ static int uv_select_edgeloop(
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Edge Ring Select
+ * \{ */
+
+static int uv_select_edgering(
+ const SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const bool use_face_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
+ (ts->selectmode & SCE_SELECT_FACE) :
+ (ts->uv_selectmode & UV_SELECT_FACE);
+ bool select;
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ if (!extend) {
+ uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ }
+
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false);
+
+ if (extend) {
+ select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ }
+ else {
+ select = true;
+ }
+
+ BMLoop *l_pair[2] = {
+ hit->l,
+ uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset),
+ };
+
+ for (int side = 0; side < 2; side++) {
+ BMLoop *l_step = l_pair[side];
+ /* Disable since we start from the same edge. */
+ BM_elem_flag_disable(hit->l->e, BM_ELEM_TAG);
+ while (l_step) {
+ if (!uvedit_face_visible_test(scene, l_step->f)) {
+ break;
+ }
+
+ if (use_face_select) {
+ uvedit_face_select_set_with_sticky(
+ sima, scene, em, l_step->f, select, false, cd_loop_uv_offset);
+ }
+ else {
+ uvedit_edge_select_set_with_sticky(
+ sima, scene, em, l_step, select, false, cd_loop_uv_offset);
+ }
+
+ BM_elem_flag_enable(l_step->e, BM_ELEM_TAG);
+ if (l_step->f->len == 4) {
+ BMLoop *l_step_opposite = l_step->next->next;
+ l_step = uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step_opposite, cd_loop_uv_offset);
+ if (l_step == NULL) {
+ /* Ensure we touch the opposite edge if we cant walk over it. */
+ l_step = l_step_opposite;
+ }
+ }
+ else {
+ l_step = NULL;
+ }
+
+ if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
+ break;
+ }
+ }
+ }
+
+ return (select) ? 1 : -1;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Select Linked
* \{ */
static void uv_select_linked_multi(Scene *scene,
Object **objects,
const uint objects_len,
- const float limit[2],
UvNearestHit *hit_final,
bool extend,
bool deselect,
@@ -937,7 +1296,7 @@ static void uv_select_linked_multi(Scene *scene,
*
* Better solve this by having a delimit option for select-linked operator,
* keeping island-select working as is. */
- vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, false);
+ vmap = BM_uv_vert_map_create(em->bm, !select_faces, false);
if (vmap == NULL) {
continue;
@@ -1011,7 +1370,7 @@ static void uv_select_linked_multi(Scene *scene,
if ((startv != iterv) && (iterv->separate)) {
break;
}
- else if (!flag[iterv->poly_index]) {
+ if (!flag[iterv->poly_index]) {
flag[iterv->poly_index] = 1;
stack[stacksize] = iterv->poly_index;
stacksize++;
@@ -1289,17 +1648,16 @@ bool uvedit_select_is_any_selected(Scene *scene, Object *obedit)
if (ts->uv_flag & UV_SYNC_SELECTION) {
return (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel);
}
- else {
- const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
- }
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (luv->flag & MLOOPUV_VERTSEL) {
- return true;
- }
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ return true;
}
}
}
@@ -1438,76 +1796,27 @@ void UV_OT_select_all(wmOperatorType *ot)
/** \name Mouse Select Operator
* \{ */
-static bool uv_sticky_select(
- float *limit, int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen)
-{
- int i;
-
- /* this function test if some vertex needs to selected
- * in addition to the existing ones due to sticky select */
- if (sticky == SI_STICKY_DISABLE) {
- return false;
- }
-
- for (i = 0; i < hitlen; i++) {
- if (hitv[i] == v) {
- if (sticky == SI_STICKY_LOC) {
- if (fabsf(hituv[i][0] - uv[0]) < limit[0] && fabsf(hituv[i][1] - uv[1]) < limit[1]) {
- return true;
- }
- }
- else if (sticky == SI_STICKY_VERTEX) {
- return true;
- }
- }
- }
-
- return false;
-}
-
static int uv_mouse_select_multi(bContext *C,
Object **objects,
uint objects_len,
const float co[2],
const bool extend,
- const bool deselect_all,
- const bool loop)
+ const bool deselect_all)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
const ToolSettings *ts = scene->toolsettings;
- BMFace *efa;
- BMLoop *l;
- BMIter iter, liter;
- MLoopUV *luv;
UvNearestHit hit = UV_NEAREST_HIT_INIT;
- int i, selectmode, sticky, sync, *hitv = NULL;
- bool select = true;
+ int selectmode, sticky;
bool found_item = false;
/* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */
int flush = 0;
- int hitlen = 0;
- float limit[2], **hituv = NULL;
- /* notice 'limit' is the same no matter the zoom level, since this is like
- * remove doubles and could annoying if it joined points when zoomed out.
- * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and
- * shift-selecting can consider an adjacent point close enough to add to
- * the selection rather than de-selecting the closest. */
-
- float penalty_dist;
- {
- float penalty[2];
- uvedit_pixel_to_float(sima, 0.05f, limit);
- uvedit_pixel_to_float(sima, 5.0f / (sima ? sima->zoom : 1.0f), penalty);
- penalty_dist = len_v2(penalty);
- }
+ const float penalty_dist = uv_select_penalty_default(sima);
/* retrieve operation mode */
if (ts->uv_flag & UV_SYNC_SELECTION) {
- sync = 1;
-
if (ts->selectmode & SCE_SELECT_FACE) {
selectmode = UV_SELECT_FACE;
}
@@ -1521,31 +1830,21 @@ static int uv_mouse_select_multi(bContext *C,
sticky = SI_STICKY_DISABLE;
}
else {
- sync = 0;
selectmode = ts->uv_selectmode;
- sticky = (sima) ? sima->sticky : 1;
+ sticky = (sima) ? sima->sticky : SI_STICKY_DISABLE;
}
/* find nearest element */
- if (loop) {
- /* find edge */
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
- }
- else if (selectmode == UV_SELECT_VERTEX) {
+ if (selectmode == UV_SELECT_VERTEX) {
/* find vertex */
found_item = uv_find_nearest_vert_multi(scene, objects, objects_len, co, penalty_dist, &hit);
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
if (found_item) {
- /* mark 1 vertex as being hit */
- hitv = BLI_array_alloca(hitv, hit.efa->len);
- hituv = BLI_array_alloca(hituv, hit.efa->len);
- copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF);
-
- hitv[hit.lindex] = BM_elem_index_get(hit.l->v);
- hituv[hit.lindex] = hit.luv->uv;
-
- hitlen = hit.efa->len;
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
+ BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
+ ED_uvedit_active_vert_loop_set(bm, hit.l);
+ }
}
}
else if (selectmode == UV_SELECT_EDGE) {
@@ -1554,17 +1853,10 @@ static int uv_mouse_select_multi(bContext *C,
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
if (found_item) {
- /* mark 2 edge vertices as being hit */
- hitv = BLI_array_alloca(hitv, hit.efa->len);
- hituv = BLI_array_alloca(hituv, hit.efa->len);
- copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF);
-
- hitv[hit.lindex] = BM_elem_index_get(hit.l->v);
- hitv[(hit.lindex + 1) % hit.efa->len] = BM_elem_index_get(hit.l->next->v);
- hituv[hit.lindex] = hit.luv->uv;
- hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv;
-
- hitlen = hit.efa->len;
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
+ BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
+ ED_uvedit_active_edge_loop_set(bm, hit.l);
+ }
}
}
else if (selectmode == UV_SELECT_FACE) {
@@ -1573,23 +1865,8 @@ static int uv_mouse_select_multi(bContext *C,
found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
if (found_item) {
- BMEditMesh *em = BKE_editmesh_from_object(hit.ob);
- const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
-
- /* make active */
- BM_mesh_active_face_set(em->bm, hit.efa);
-
- /* mark all face vertices as being hit */
-
- hitv = BLI_array_alloca(hitv, hit.efa->len);
- hituv = BLI_array_alloca(hituv, hit.efa->len);
- BM_ITER_ELEM_INDEX (l, &liter, hit.efa, BM_LOOPS_OF_FACE, i) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- hituv[i] = luv->uv;
- hitv[i] = BM_elem_index_get(l->v);
- }
-
- hitlen = hit.efa->len;
+ BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
+ BM_mesh_active_face_set(bm, hit.efa);
}
}
else if (selectmode == UV_SELECT_ISLAND) {
@@ -1616,44 +1893,39 @@ static int uv_mouse_select_multi(bContext *C,
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
/* do selection */
- if (loop) {
- if (!extend) {
- /* TODO(MULTI_EDIT): We only need to de-select non-active */
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
- }
- flush = uv_select_edgeloop(scene, obedit, &hit, limit, extend);
- }
- else if (selectmode == UV_SELECT_ISLAND) {
+ if (selectmode == UV_SELECT_ISLAND) {
if (!extend) {
/* TODO(MULTI_EDIT): We only need to de-select non-active */
uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
}
/* Current behavior of 'extend'
* is actually toggling, so pass extend flag as 'toggle' here */
- uv_select_linked_multi(scene, objects, objects_len, limit, &hit, false, false, extend, false);
+ uv_select_linked_multi(scene, objects, objects_len, &hit, false, false, extend, false);
}
else if (extend) {
+ bool select = true;
if (selectmode == UV_SELECT_VERTEX) {
/* (de)select uv vertex */
select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
- uvedit_uv_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
+ uvedit_uv_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
flush = 1;
}
else if (selectmode == UV_SELECT_EDGE) {
/* (de)select edge */
select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset));
- uvedit_edge_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
+ uvedit_edge_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
flush = 1;
}
else if (selectmode == UV_SELECT_FACE) {
/* (de)select face */
select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset));
- uvedit_face_select_set(scene, em, hit.efa, select, true, cd_loop_uv_offset);
+ uvedit_face_select_set_with_sticky(
+ sima, scene, em, hit.efa, select, true, cd_loop_uv_offset);
flush = -1;
}
/* de-selecting an edge may deselect a face too - validate */
- if (sync) {
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
if (select == false) {
BM_select_history_validate(em->bm);
}
@@ -1661,98 +1933,36 @@ static int uv_mouse_select_multi(bContext *C,
/* (de)select sticky uv nodes */
if (sticky != SI_STICKY_DISABLE) {
-
- BM_mesh_elem_index_ensure(em->bm, BM_VERT);
-
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
- }
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (uv_sticky_select(
- limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
- }
- }
- }
-
flush = select ? 1 : -1;
}
}
else {
+ const bool select = true;
/* deselect all */
uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
if (selectmode == UV_SELECT_VERTEX) {
/* select vertex */
- uvedit_uv_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
+ uvedit_uv_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
flush = 1;
}
else if (selectmode == UV_SELECT_EDGE) {
/* select edge */
- uvedit_edge_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
+ uvedit_edge_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
flush = 1;
}
else if (selectmode == UV_SELECT_FACE) {
/* select face */
- uvedit_face_select_enable(scene, em, hit.efa, true, cd_loop_uv_offset);
- }
-
- /* select sticky uvs */
- if (sticky != SI_STICKY_DISABLE) {
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
- }
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (sticky == SI_STICKY_DISABLE) {
- continue;
- }
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-
- if (uv_sticky_select(
- limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen)) {
- uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset);
- }
-
- flush = 1;
- }
- }
+ uvedit_face_select_set_with_sticky(
+ sima, scene, em, hit.efa, select, true, cd_loop_uv_offset);
+ flush = 1;
}
}
- if (sync) {
- /* flush for mesh selection */
-
- /* before bmesh */
-#if 0
- if (ts->selectmode != SCE_SELECT_FACE) {
- if (flush == 1) {
- EDBM_select_flush(em);
- }
- else if (flush == -1) {
- EDBM_deselect_flush(em);
- }
- }
-#else
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
if (flush != 0) {
- if (loop) {
- /* push vertex -> edge selection */
- if (select) {
- EDBM_select_flush(em);
- }
- else {
- EDBM_deselect_flush(em);
- }
- }
- else {
- EDBM_selectmode_flush(em);
- }
+ EDBM_selectmode_flush(em);
}
-#endif
}
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -1762,14 +1972,16 @@ static int uv_mouse_select_multi(bContext *C,
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
-static int uv_mouse_select(
- bContext *C, const float co[2], const bool extend, const bool deselect_all, const bool loop)
+static int uv_mouse_select(bContext *C,
+ const float co[2],
+ const bool extend,
+ const bool deselect_all)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, ((View3D *)NULL), &objects_len);
- int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all, loop);
+ int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all);
MEM_freeN(objects);
return ret;
}
@@ -1781,9 +1993,8 @@ static int uv_select_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "location", co);
const bool extend = RNA_boolean_get(op->ptr, "extend");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
- const bool loop = false;
- return uv_mouse_select(C, co, extend, deselect_all, loop);
+ return uv_mouse_select(C, co, extend, deselect_all);
}
static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -1840,7 +2051,89 @@ void UV_OT_select(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Loop Select Operator
+/** \name Shared Edge Loop/Ring Select Operator Functions
+ * \{ */
+
+enum eUVLoopGenericType {
+ UV_LOOP_SELECT = 1,
+ UV_RING_SELECT = 2,
+};
+
+static int uv_mouse_select_loop_generic_multi(bContext *C,
+ Object **objects,
+ uint objects_len,
+ const float co[2],
+ const bool extend,
+ enum eUVLoopGenericType loop_type)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ const ToolSettings *ts = scene->toolsettings;
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ bool found_item = false;
+ /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */
+ int flush = 0;
+
+ /* Find edge. */
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ if (!found_item) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Object *obedit = hit.ob;
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
+ /* Do selection. */
+ if (!extend) {
+ /* TODO(MULTI_EDIT): We only need to de-select non-active */
+ uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ }
+
+ if (loop_type == UV_LOOP_SELECT) {
+ flush = uv_select_edgeloop(scene, obedit, &hit, extend);
+ }
+ else if (loop_type == UV_RING_SELECT) {
+ flush = uv_select_edgering(sima, scene, obedit, &hit, extend);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ if (flush == 1) {
+ EDBM_select_flush(em);
+ }
+ else if (flush == -1) {
+ EDBM_deselect_flush(em);
+ }
+ }
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obiter = objects[ob_index];
+ uv_select_tag_update_for_object(depsgraph, ts, obiter);
+ }
+
+ return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
+}
+static int uv_mouse_select_loop_generic(bContext *C,
+ const float co[2],
+ const bool extend,
+ enum eUVLoopGenericType loop_type)
+{
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+ int ret = uv_mouse_select_loop_generic_multi(C, objects, objects_len, co, extend, loop_type);
+ MEM_freeN(objects);
+ return ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Edge Loop Select Operator
* \{ */
static int uv_select_loop_exec(bContext *C, wmOperator *op)
@@ -1849,10 +2142,8 @@ static int uv_select_loop_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "location", co);
const bool extend = RNA_boolean_get(op->ptr, "extend");
- const bool deselect_all = false;
- const bool loop = true;
- return uv_mouse_select(C, co, extend, deselect_all, loop);
+ return uv_mouse_select_loop_generic(C, co, extend, UV_LOOP_SELECT);
}
static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -1901,16 +2192,71 @@ void UV_OT_select_loop(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Edge Ring Select Operator
+ * \{ */
+
+static int uv_select_edge_ring_exec(bContext *C, wmOperator *op)
+{
+ float co[2];
+ RNA_float_get_array(op->ptr, "location", co);
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+ return uv_mouse_select_loop_generic(C, co, extend, UV_RING_SELECT);
+}
+
+static int uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const ARegion *region = CTX_wm_region(C);
+ float co[2];
+
+ UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+ RNA_float_set_array(op->ptr, "location", co);
+
+ return uv_select_edge_ring_exec(C, op);
+}
+
+void UV_OT_select_edge_ring(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Edge Ring Select";
+ ot->description = "Select an edge ring of connected UV vertices";
+ ot->idname = "UV_OT_select_edge_ring";
+ ot->flag = OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec = uv_select_edge_ring_exec;
+ ot->invoke = uv_select_edge_ring_invoke;
+ ot->poll = ED_operator_uvedit; /* requires space image */
+
+ /* properties */
+ RNA_def_boolean(ot->srna,
+ "extend",
+ 0,
+ "Extend",
+ "Extend selection rather than clearing the existing selection");
+ RNA_def_float_vector(
+ ot->srna,
+ "location",
+ 2,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Location",
+ "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
+ -100.0f,
+ 100.0f);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Select Linked Operator
* \{ */
static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
{
- SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
const ToolSettings *ts = scene->toolsettings;
ViewLayer *view_layer = CTX_data_view_layer(C);
- float limit[2];
bool extend = true;
bool deselect = false;
bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE);
@@ -1928,7 +2274,6 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent
extend = RNA_boolean_get(op->ptr, "extend");
deselect = RNA_boolean_get(op->ptr, "deselect");
}
- uv_select_island_limit_default(sima, limit);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
@@ -1959,15 +2304,8 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent
uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
}
- uv_select_linked_multi(scene,
- objects,
- objects_len,
- limit,
- pick ? &hit : NULL,
- extend,
- deselect,
- false,
- select_faces);
+ uv_select_linked_multi(
+ scene, objects, objects_len, pick ? &hit : NULL, extend, deselect, false, select_faces);
/* weak!, but works */
Object **objects_free = objects;
@@ -2076,6 +2414,7 @@ void UV_OT_select_linked_pick(wmOperatorType *ot)
*/
static int uv_select_split_exec(bContext *C, wmOperator *op)
{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const ToolSettings *ts = scene->toolsettings;
@@ -2142,6 +2481,7 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
if (changed) {
changed_multi = true;
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
+ uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
MEM_freeN(objects);
@@ -2162,25 +2502,6 @@ void UV_OT_select_split(wmOperatorType *ot)
ot->poll = ED_operator_uvedit; /* requires space image */
}
-static void uv_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const short select)
-{
- /* bmesh API handles flushing but not on de-select */
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- if (ts->selectmode != SCE_SELECT_FACE) {
- if (select == false) {
- EDBM_deselect_flush(em);
- }
- else {
- EDBM_select_flush(em);
- }
- }
-
- if (select == false) {
- BM_select_history_validate(em->bm);
- }
- }
-}
-
static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
const ToolSettings *ts,
Object *obedit)
@@ -2219,7 +2540,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene,
UvMapVert *start_vlist = NULL, *vlist_iter;
BMFace *efa_vlist;
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
vlist_iter = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
@@ -2250,7 +2571,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene,
l_other = BM_iter_at_index(
em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->loop_of_poly_index);
- uvedit_uv_select_set(em, scene, l_other, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l_other, select, false, cd_loop_uv_offset);
}
vlist_iter = vlist_iter->next;
}
@@ -2303,20 +2624,17 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
}
}
}
}
else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
struct UvVertMap *vmap;
- float limit[2];
uint efa_index;
- uv_select_island_limit_default(sima, limit);
-
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
- vmap = BM_uv_vert_map_create(em->bm, limit, false, false);
+ vmap = BM_uv_vert_map_create(em->bm, false, false);
if (vmap == NULL) {
return;
}
@@ -2390,20 +2708,17 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
}
}
}
}
else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
struct UvVertMap *vmap;
- float limit[2];
uint efa_index;
- uv_select_island_limit_default(sima, limit);
-
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
- vmap = BM_uv_vert_map_create(em->bm, limit, false, false);
+ vmap = BM_uv_vert_map_create(em->bm, false, false);
if (vmap == NULL) {
return;
}
@@ -2424,7 +2739,7 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima,
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
}
}
}
@@ -2451,7 +2766,6 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
MLoopUV *luv;
rctf rectf;
bool pinned;
- float limit[2];
const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_FACE) :
(ts->uv_selectmode == UV_SELECT_FACE));
@@ -2469,8 +2783,6 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
pinned = RNA_boolean_get(op->ptr, "pinned");
- uv_select_island_limit_default(sima, limit);
-
bool changed_multi = false;
uint objects_len = 0;
@@ -2532,8 +2844,8 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
if ((select != luv_select) || (select != luv_select_prev)) {
if (BLI_rctf_isect_pt_v(&rectf, luv->uv) &&
BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
- uvedit_uv_select_set(em, scene, l_prev, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG);
}
@@ -2564,14 +2876,14 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) {
/* UV_SYNC_SELECTION - can't do pinned selection */
if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
has_selected = true;
}
}
else if (pinned) {
if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
}
}
@@ -2582,8 +2894,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
.ob = obedit,
.efa = efa,
};
- uv_select_linked_multi(
- scene, objects, objects_len, limit, &hit, true, !select, false, false);
+ uv_select_linked_multi(scene, objects, objects_len, &hit, true, !select, false, false);
}
}
@@ -2595,7 +2906,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
if (changed || use_pre_deselect) {
changed_multi = true;
- uv_select_sync_flush(ts, em, select);
+ ED_uvedit_select_sync_flush(ts, em, select);
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
@@ -2678,7 +2989,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
MLoopUV *luv;
int x, y, radius, width, height;
float zoomx, zoomy;
- float limit[2], offset[2], ellipse[2];
+ float offset[2], ellipse[2];
const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_FACE) :
@@ -2702,8 +3013,6 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
- uv_select_island_limit_default(sima, limit);
-
bool changed_multi = false;
uint objects_len = 0;
@@ -2765,8 +3074,8 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
if ((select != luv_select) || (select != luv_select_prev)) {
if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) {
changed = true;
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
- uvedit_uv_select_set(em, scene, l_prev, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG);
}
@@ -2794,7 +3103,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (uv_circle_select_is_point_inside(luv->uv, offset, ellipse)) {
changed = true;
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
has_selected = true;
}
@@ -2805,8 +3114,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
.ob = obedit,
.efa = efa,
};
- uv_select_linked_multi(
- scene, objects, objects_len, limit, &hit, true, !select, false, false);
+ uv_select_linked_multi(scene, objects, objects_len, &hit, true, !select, false, false);
}
}
@@ -2818,7 +3126,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
if (changed || use_pre_deselect) {
changed_multi = true;
- uv_select_sync_flush(ts, em, select);
+ ED_uvedit_select_sync_flush(ts, em, select);
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
@@ -2897,12 +3205,9 @@ static bool do_lasso_select_mesh_uv(bContext *C,
BMFace *efa;
BMLoop *l;
- float limit[2];
bool changed_multi = false;
rcti rect;
- uv_select_island_limit_default(sima, limit);
-
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
uint objects_len = 0;
@@ -2962,8 +3267,8 @@ static bool do_lasso_select_mesh_uv(bContext *C,
region, &rect, mcoords, mcoords_len, luv->uv) &&
do_lasso_select_mesh_uv_is_point_inside(
region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
- uvedit_uv_select_set(em, scene, l_prev, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset);
changed = true;
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG);
@@ -2992,7 +3297,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (do_lasso_select_mesh_uv_is_point_inside(
region, &rect, mcoords, mcoords_len, luv->uv)) {
- uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
changed = true;
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
has_selected = true;
@@ -3004,8 +3309,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
.ob = obedit,
.efa = efa,
};
- uv_select_linked_multi(
- scene, objects, objects_len, limit, &hit, true, !select, false, false);
+ uv_select_linked_multi(scene, objects, objects_len, &hit, true, !select, false, false);
}
}
@@ -3017,7 +3321,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
if (changed || use_pre_deselect) {
changed_multi = true;
- uv_select_sync_flush(ts, em, select);
+ ED_uvedit_select_sync_flush(ts, em, select);
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
@@ -3099,7 +3403,7 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op))
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (luv->flag & MLOOPUV_PINNED) {
- uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset);
+ uvedit_uv_select_enable(scene, em, l, false, cd_loop_uv_offset);
changed = true;
}
}
@@ -3369,3 +3673,154 @@ void UV_OT_select_overlap(wmOperatorType *ot)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Selected Elements as Arrays (Vertex, Edge & Faces)
+ *
+ * These functions return single elements per connected vertex/edge.
+ * So an edge that has two connected edge loops only assigns one loop in the array.
+ * \{ */
+
+BMFace **ED_uvedit_selected_faces(Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ CLAMP_MAX(len_max, bm->totface);
+ int faces_len = 0;
+ BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__);
+
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (uvedit_face_visible_test(scene, f)) {
+ if (uvedit_face_select_test(scene, f, cd_loop_uv_offset)) {
+ faces[faces_len++] = f;
+ if (faces_len == len_max) {
+ goto finally;
+ }
+ }
+ }
+ }
+
+finally:
+ *r_faces_len = faces_len;
+ if (faces_len != len_max) {
+ faces = MEM_reallocN(faces, sizeof(*faces) * faces_len);
+ }
+ return faces;
+}
+
+BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ CLAMP_MAX(len_max, bm->totloop);
+ int edges_len = 0;
+ BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__);
+
+ BMIter iter;
+ BMFace *f;
+
+ /* Clear tag. */
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
+ }
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (uvedit_face_visible_test(scene, f)) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
+ const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset);
+ if ((luv_curr->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
+ BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
+
+ edges[edges_len++] = l_iter;
+ if (edges_len == len_max) {
+ goto finally;
+ }
+
+ /* Tag other connected loops so we don't consider them separate edges. */
+ if (l_iter != l_iter->radial_next) {
+ BMLoop *l_radial_iter = l_iter->radial_next;
+ do {
+ if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
+ }
+ }
+ }
+ }
+ }
+ }
+
+finally:
+ *r_edges_len = edges_len;
+ if (edges_len != len_max) {
+ edges = MEM_reallocN(edges, sizeof(*edges) * edges_len);
+ }
+ return edges;
+}
+
+BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ CLAMP_MAX(len_max, bm->totloop);
+ int verts_len = 0;
+ BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__);
+
+ BMIter iter;
+ BMFace *f;
+
+ /* Clear tag. */
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
+ }
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (uvedit_face_visible_test(scene, f)) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if ((luv->flag & MLOOPUV_VERTSEL)) {
+ BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
+
+ verts[verts_len++] = l_iter;
+ if (verts_len == len_max) {
+ goto finally;
+ }
+
+ /* Tag other connected loops so we don't consider them separate vertices. */
+ BMIter liter_disk;
+ BMLoop *l_disk_iter;
+ BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
+ if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+finally:
+ *r_verts_len = verts_len;
+ if (verts_len != len_max) {
+ verts = MEM_reallocN(verts, sizeof(*verts) * verts_len);
+ }
+ return verts;
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 594847b7249..f649ee528d4 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -320,9 +320,7 @@ static int getNumOfIslandUvs(UvElementMap *elementMap, int island)
if (island == elementMap->totalIslands - 1) {
return elementMap->totalUVs - elementMap->islandIndices[island];
}
- else {
- return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island];
- }
+ return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island];
}
static void stitch_uv_rotate(float mat[2][2], float medianPoint[2], float uv[2], float aspect)
@@ -367,13 +365,9 @@ static bool stitch_check_uvs_stitchable(UvElement *element,
fabsf(luv->uv[1] - luv_iter->uv[1]) < limit) {
return 1;
}
- else {
- return 0;
- }
- }
- else {
- return 1;
+ return 0;
}
+ return 1;
}
static bool stitch_check_edges_stitchable(UvEdge *edge,
@@ -411,13 +405,9 @@ static bool stitch_check_edges_stitchable(UvEdge *edge,
fabsf(luv_orig2->uv[1] - luv_iter2->uv[1]) < limit) {
return 1;
}
- else {
- return 0;
- }
- }
- else {
- return 1;
+ return 0;
}
+ return 1;
}
static bool stitch_check_uvs_state_stitchable(UvElement *element,
@@ -544,7 +534,7 @@ static void stitch_island_calculate_edge_rotation(UvEdge *edge,
StitchStateContainer *ssc,
StitchState *state,
UVVertAverage *uv_average,
- uint *uvfinal_map,
+ const uint *uvfinal_map,
IslandStitchData *island_stitch_data)
{
BMesh *bm = state->em->bm;
@@ -991,7 +981,7 @@ static void stitch_propagate_uv_final_position(Scene *scene,
if (final) {
copy_v2_v2(luv->uv, final_position[index].uv);
- uvedit_uv_select_enable(state->em, scene, l, false, cd_loop_uv_offset);
+ uvedit_uv_select_enable(scene, state->em, l, false, cd_loop_uv_offset);
}
else {
int face_preview_pos =
@@ -1945,7 +1935,7 @@ static StitchState *stitch_init(bContext *C,
return NULL;
}
- ED_uvedit_get_aspect(scene, obedit, em->bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
state->aspect = aspx / aspy;
/* Count 'unique' uvs */
@@ -2535,10 +2525,8 @@ static int stitch_exec(bContext *C, wmOperator *op)
stitch_exit(C, op, 1);
return OPERATOR_FINISHED;
}
- else {
- stitch_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
+ stitch_cancel(C, op);
+ return OPERATOR_CANCELLED;
}
static StitchState *stitch_select(bContext *C,
@@ -2619,14 +2607,12 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event)
stitch_exit(C, op, 1);
return OPERATOR_FINISHED;
}
- else {
- stitch_cancel(C, op);
- return OPERATOR_CANCELLED;
- }
- }
- else {
- return OPERATOR_PASS_THROUGH;
+
+ stitch_cancel(C, op);
+ return OPERATOR_CANCELLED;
}
+ return OPERATOR_PASS_THROUGH;
+
/* Increase limit */
case EVT_PADPLUSKEY:
case WHEELUPMOUSE:
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index c4dcaaaa8b2..2f30ef1caa6 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -35,7 +35,10 @@
#include "DNA_scene_types.h"
#include "BLI_alloca.h"
+#include "BLI_array.h"
+#include "BLI_linklist.h"
#include "BLI_math.h"
+#include "BLI_memarena.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BLI_uvproject.h"
@@ -147,7 +150,13 @@ typedef struct UnwrapOptions {
/** Connectivity based on UV coordinates instead of seams. */
bool topology_from_uvs;
/** Only affect selected faces. */
- bool only_selected;
+ bool only_selected_faces;
+ /**
+ * Only affect selected UV's.
+ * \note Disable this for operations that don't run in the image-window.
+ * Unwrapping from the 3D view for example, where only 'only_selected_faces' should be used.
+ */
+ bool only_selected_uvs;
/** Fill holes to better preserve shape. */
bool fill_holes;
/** Correct for mapped image texture aspect ratio. */
@@ -210,15 +219,16 @@ static bool uvedit_have_selection_multi(const Scene *scene,
return have_select;
}
-void ED_uvedit_get_aspect(
- const Scene *UNUSED(scene), Object *ob, BMesh *bm, float *r_aspx, float *r_aspy)
+void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
{
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BLI_assert(em != NULL);
bool sloppy = true;
bool selected = false;
BMFace *efa;
Image *ima;
- efa = BM_mesh_active_face_get(bm, sloppy, selected);
+ efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
if (efa) {
ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
@@ -285,7 +295,7 @@ static ParamHandle *construct_param_handle(const Scene *scene,
if (options->correct_aspect) {
float aspx, aspy;
- ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx != aspy) {
param_aspect_ratio(handle, aspx, aspy);
@@ -298,7 +308,7 @@ static ParamHandle *construct_param_handle(const Scene *scene,
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
- (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
+ (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
continue;
}
@@ -306,10 +316,12 @@ static ParamHandle *construct_param_handle(const Scene *scene,
bool is_loopsel = false;
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- is_loopsel = true;
- break;
+ if (options->only_selected_uvs &&
+ (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
+ continue;
}
+ is_loopsel = true;
+ break;
}
if (is_loopsel == false) {
continue;
@@ -354,11 +366,9 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
if (options->correct_aspect) {
Object *ob = objects[0];
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- BMesh *bm = em->bm;
float aspx, aspy;
- ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx != aspy) {
param_aspect_ratio(handle, aspx, aspy);
}
@@ -383,7 +393,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
- (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
+ (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
continue;
}
@@ -391,10 +401,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
bool is_loopsel = false;
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- is_loopsel = true;
- break;
+ if (options->only_selected_uvs &&
+ (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
+ continue;
}
+ is_loopsel = true;
+ break;
}
if (is_loopsel == false) {
continue;
@@ -500,7 +512,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
if (options->correct_aspect) {
float aspx, aspy;
- ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx != aspy) {
param_aspect_ratio(handle, aspx, aspy);
@@ -572,7 +584,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
}
else {
if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) ||
- (options->only_selected && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) {
+ (options->only_selected_faces && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) {
continue;
}
}
@@ -672,7 +684,8 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op)
const UnwrapOptions options = {
.topology_from_uvs = true,
.fill_holes = RNA_boolean_get(op->ptr, "fill_holes"),
- .only_selected = true,
+ .only_selected_faces = true,
+ .only_selected_uvs = true,
.correct_aspect = true,
};
@@ -934,7 +947,8 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm)
{
const UnwrapOptions options = {
.topology_from_uvs = true,
- .only_selected = false,
+ .only_selected_faces = false,
+ .only_selected_uvs = true,
.fill_holes = false,
.correct_aspect = false,
};
@@ -976,7 +990,8 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
const UnwrapOptions options = {
.topology_from_uvs = true,
- .only_selected = true,
+ .only_selected_faces = true,
+ .only_selected_uvs = true,
.fill_holes = false,
.correct_aspect = true,
};
@@ -1041,7 +1056,8 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
const UnwrapOptions options = {
.topology_from_uvs = true,
- .only_selected = true,
+ .only_selected_faces = true,
+ .only_selected_uvs = true,
.fill_holes = false,
.correct_aspect = true,
};
@@ -1115,7 +1131,8 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
const UnwrapOptions options = {
.topology_from_uvs = false,
- .only_selected = false,
+ .only_selected_faces = false,
+ .only_selected_uvs = true,
.fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0,
.correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0,
};
@@ -1413,7 +1430,7 @@ static void uv_transform_properties(wmOperatorType *ot, int radius)
}
}
-static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em)
+static void correct_uv_aspect(Object *ob, BMEditMesh *em)
{
BMLoop *l;
BMIter iter, liter;
@@ -1423,7 +1440,7 @@ static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em)
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx == aspy) {
return;
@@ -1472,18 +1489,21 @@ static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em)
/** \name UV Map Clip & Correct
* \{ */
-static void uv_map_clip_correct_properties(wmOperatorType *ot)
+static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds)
{
RNA_def_boolean(ot->srna,
"correct_aspect",
1,
"Correct Aspect",
"Map UVs taking image aspect ratio into account");
- RNA_def_boolean(ot->srna,
- "clip_to_bounds",
- 0,
- "Clip to Bounds",
- "Clip UV coordinates to bounds after unwrapping");
+ /* Optional, since not all unwrapping types need to be clipped. */
+ if (clip_to_bounds) {
+ RNA_def_boolean(ot->srna,
+ "clip_to_bounds",
+ 0,
+ "Clip to Bounds",
+ "Clip UV coordinates to bounds after unwrapping");
+ }
RNA_def_boolean(ot->srna,
"scale_to_bounds",
0,
@@ -1491,10 +1511,12 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
"Scale UV coordinates to bounds after unwrapping");
}
-static void uv_map_clip_correct_multi(const Scene *scene,
- Object **objects,
- uint objects_len,
- wmOperator *op)
+static void uv_map_clip_correct_properties(wmOperatorType *ot)
+{
+ uv_map_clip_correct_properties_ex(ot, true);
+}
+
+static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
{
BMFace *efa;
BMLoop *l;
@@ -1502,7 +1524,8 @@ static void uv_map_clip_correct_multi(const Scene *scene,
MLoopUV *luv;
float dx, dy, min[2], max[2];
const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
- const bool clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds");
+ const bool clip_to_bounds = (RNA_struct_find_property(op->ptr, "clip_to_bounds") &&
+ RNA_boolean_get(op->ptr, "clip_to_bounds"));
const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds");
INIT_MINMAX2(min, max);
@@ -1515,7 +1538,7 @@ static void uv_map_clip_correct_multi(const Scene *scene,
/* correct for image aspect ratio */
if (correct_aspect) {
- correct_uv_aspect(scene, ob, em);
+ correct_uv_aspect(ob, em);
}
if (scale_to_bounds) {
@@ -1580,9 +1603,9 @@ static void uv_map_clip_correct_multi(const Scene *scene,
}
}
-static void uv_map_clip_correct(const Scene *scene, Object *ob, wmOperator *op)
+static void uv_map_clip_correct(Object *ob, wmOperator *op)
{
- uv_map_clip_correct_multi(scene, &ob, 1, op);
+ uv_map_clip_correct_multi(&ob, 1, op);
}
/** \} */
@@ -1639,7 +1662,8 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
if (scene->toolsettings->edge_mode_live_unwrap) {
const UnwrapOptions options = {
.topology_from_uvs = false,
- .only_selected = false,
+ .only_selected_faces = false,
+ .only_selected_uvs = true,
.fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0,
.correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0,
};
@@ -1674,7 +1698,8 @@ static int unwrap_exec(bContext *C, wmOperator *op)
const UnwrapOptions options = {
.topology_from_uvs = false,
- .only_selected = true,
+ .only_selected_faces = true,
+ .only_selected_uvs = true,
.fill_holes = RNA_boolean_get(op->ptr, "fill_holes"),
.correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"),
};
@@ -1824,7 +1849,7 @@ void UV_OT_unwrap(wmOperatorType *ot)
ot->srna,
"use_subsurf_data",
0,
- "Use Subsurf Modifier",
+ "Use Subdivision Surface",
"Map UVs taking vertex position after Subdivision Surface modifier has been applied");
RNA_def_float_factor(
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
@@ -1833,6 +1858,365 @@ void UV_OT_unwrap(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Smart UV Project Operator
+ * \{ */
+
+/* Ignore all areas below this, as the UV's get zeroed. */
+static const float smart_uv_project_area_ignore = 1e-12f;
+
+typedef struct ThickFace {
+ float area;
+ BMFace *efa;
+} ThickFace;
+
+static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p)
+{
+
+ const ThickFace *tf_a = (ThickFace *)tf_a_p;
+ const ThickFace *tf_b = (ThickFace *)tf_b_p;
+
+ /* Ignore the area of small faces.
+ * Also, order checks so `!isfinite(...)` values are counted as zero area. */
+ if (!((tf_a->area > smart_uv_project_area_ignore) ||
+ (tf_b->area > smart_uv_project_area_ignore))) {
+ return 0;
+ }
+
+ if (tf_a->area < tf_b->area) {
+ return 1;
+ }
+ else if (tf_a->area > tf_b->area) {
+ return -1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static uint smart_uv_project_calculate_project_normals(const ThickFace *thick_faces,
+ const uint thick_faces_len,
+ BMesh *bm,
+ const float project_angle_limit_half_cos,
+ const float project_angle_limit_cos,
+ const float area_weight,
+ float (**r_project_normal_array)[3])
+{
+ if (UNLIKELY(thick_faces_len == 0)) {
+ *r_project_normal_array = NULL;
+ return 0;
+ }
+
+ const float *project_normal = thick_faces[0].efa->no;
+
+ const ThickFace **project_thick_faces = NULL;
+ BLI_array_declare(project_thick_faces);
+
+ float(*project_normal_array)[3] = NULL;
+ BLI_array_declare(project_normal_array);
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
+
+ while (true) {
+ for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
+ if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ if (dot_v3v3(thick_faces[f_index].efa->no, project_normal) > project_angle_limit_half_cos) {
+ BLI_array_append(project_thick_faces, &thick_faces[f_index]);
+ BM_elem_flag_set(thick_faces[f_index].efa, BM_ELEM_TAG, true);
+ }
+ }
+
+ float average_normal[3] = {0.0f, 0.0f, 0.0f};
+
+ if (area_weight <= 0.0f) {
+ for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces);
+ f_proj_index++) {
+ const ThickFace *tf = project_thick_faces[f_proj_index];
+ add_v3_v3(average_normal, tf->efa->no);
+ }
+ }
+ else if (area_weight >= 1.0f) {
+ for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces);
+ f_proj_index++) {
+ const ThickFace *tf = project_thick_faces[f_proj_index];
+ madd_v3_v3fl(average_normal, tf->efa->no, tf->area);
+ }
+ }
+ else {
+ for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces);
+ f_proj_index++) {
+ const ThickFace *tf = project_thick_faces[f_proj_index];
+ const float area_blend = (tf->area * area_weight) + (1.0f - area_weight);
+ madd_v3_v3fl(average_normal, tf->efa->no, area_blend);
+ }
+ }
+
+ /* Avoid NAN. */
+ if (normalize_v3(average_normal) != 0.0f) {
+ float(*normal)[3] = BLI_array_append_ret(project_normal_array);
+ copy_v3_v3(*normal, average_normal);
+ }
+
+ /* Find the most unique angle that points away from other normals. */
+ float anble_best = 1.0f;
+ uint angle_best_index = 0;
+
+ for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
+ if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ float angle_test = -1.0f;
+ for (int p_index = 0; p_index < BLI_array_len(project_normal_array); p_index++) {
+ angle_test = max_ff(angle_test,
+ dot_v3v3(project_normal_array[p_index], thick_faces[f_index].efa->no));
+ }
+
+ if (angle_test < anble_best) {
+ anble_best = angle_test;
+ angle_best_index = f_index;
+ }
+ }
+
+ if (anble_best < project_angle_limit_cos) {
+ project_normal = thick_faces[angle_best_index].efa->no;
+ BLI_array_clear(project_thick_faces);
+ BLI_array_append(project_thick_faces, &thick_faces[angle_best_index]);
+ BM_elem_flag_enable(thick_faces[angle_best_index].efa, BM_ELEM_TAG);
+ }
+ else {
+ if (BLI_array_len(project_normal_array) >= 1) {
+ break;
+ }
+ }
+ }
+
+ BLI_array_free(project_thick_faces);
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
+
+ *r_project_normal_array = project_normal_array;
+ return BLI_array_len(project_normal_array);
+}
+
+static int smart_project_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ /* May be NULL. */
+ View3D *v3d = CTX_wm_view3d(C);
+
+ const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit");
+ const float island_margin = RNA_float_get(op->ptr, "island_margin");
+ const float area_weight = RNA_float_get(op->ptr, "area_weight");
+
+ const float project_angle_limit_cos = cosf(project_angle_limit);
+ const float project_angle_limit_half_cos = cosf(project_angle_limit / 2);
+
+ /* Memory arena for list links (cleared for each object). */
+ MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, v3d, &objects_len);
+
+ Object **objects_changed = MEM_mallocN(sizeof(*objects_changed) * objects_len, __func__);
+ uint object_changed_len = 0;
+
+ BMFace *efa;
+ BMIter iter;
+ uint ob_index;
+
+ for (ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ bool changed = false;
+
+ const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ ThickFace *thick_faces = MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__);
+
+ uint thick_faces_len = 0;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
+ }
+ thick_faces[thick_faces_len].area = BM_face_calc_area(efa);
+ thick_faces[thick_faces_len].efa = efa;
+ thick_faces_len++;
+ }
+
+ qsort(thick_faces, thick_faces_len, sizeof(ThickFace), smart_uv_project_thickface_area_cmp_fn);
+
+ /* Remove all zero area faces. */
+ while ((thick_faces_len > 0) &&
+ !(thick_faces[thick_faces_len - 1].area > smart_uv_project_area_ignore)) {
+
+ /* Zero UV's so they don't overlap with other faces being unwrapped. */
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, thick_faces[thick_faces_len - 1].efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ zero_v2(luv->uv);
+ changed = true;
+ }
+
+ thick_faces_len -= 1;
+ }
+
+ float(*project_normal_array)[3] = NULL;
+ int project_normals_len = smart_uv_project_calculate_project_normals(
+ thick_faces,
+ thick_faces_len,
+ em->bm,
+ project_angle_limit_half_cos,
+ project_angle_limit_cos,
+ area_weight,
+ &project_normal_array);
+
+ if (project_normals_len == 0) {
+ MEM_freeN(thick_faces);
+ BLI_assert(project_normal_array == NULL);
+ continue;
+ }
+
+ /* After finding projection vectors, we find the uv positions. */
+ LinkNode **thickface_project_groups = MEM_callocN(
+ sizeof(*thickface_project_groups) * project_normals_len, __func__);
+
+ BLI_memarena_clear(arena);
+
+ for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
+ const float *f_normal = thick_faces[f_index].efa->no;
+
+ float angle_best = dot_v3v3(f_normal, project_normal_array[0]);
+ uint angle_best_index = 0;
+
+ for (int p_index = 1; p_index < project_normals_len; p_index++) {
+ const float angle_test = dot_v3v3(f_normal, project_normal_array[p_index]);
+ if (angle_test > angle_best) {
+ angle_best = angle_test;
+ angle_best_index = p_index;
+ }
+ }
+
+ BLI_linklist_prepend_arena(
+ &thickface_project_groups[angle_best_index], &thick_faces[f_index], arena);
+ }
+
+ for (int p_index = 0; p_index < project_normals_len; p_index++) {
+ if (thickface_project_groups[p_index] == NULL) {
+ continue;
+ }
+
+ float axis_mat[3][3];
+ axis_dominant_v3_to_m3_negate(axis_mat, project_normal_array[p_index]);
+
+ for (LinkNode *list = thickface_project_groups[p_index]; list; list = list->next) {
+ ThickFace *tf = list->link;
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, tf->efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ mul_v2_m3v3(luv->uv, axis_mat, l->v->co);
+ }
+ changed = true;
+ }
+ }
+
+ MEM_freeN(thick_faces);
+ MEM_freeN(project_normal_array);
+
+ /* No need to free the lists in 'thickface_project_groups' values as the 'arena' is used. */
+ MEM_freeN(thickface_project_groups);
+
+ if (changed) {
+ objects_changed[object_changed_len] = objects[ob_index];
+ object_changed_len += 1;
+ }
+ }
+
+ BLI_memarena_free(arena);
+
+ MEM_freeN(objects);
+
+ /* Pack islands & Stretch to UV bounds */
+ if (object_changed_len > 0) {
+
+ scene->toolsettings->uvcalc_margin = island_margin;
+ const UnwrapOptions options = {
+ .topology_from_uvs = true,
+ .only_selected_faces = true,
+ .only_selected_uvs = false,
+ .fill_holes = true,
+ .correct_aspect = false,
+ };
+
+ /* Depsgraph refresh functions are called here. */
+ uvedit_pack_islands_multi(scene, objects_changed, object_changed_len, &options, true, false);
+ uv_map_clip_correct_multi(objects_changed, object_changed_len, op);
+ }
+
+ MEM_freeN(objects_changed);
+
+ return OPERATOR_FINISHED;
+}
+
+void UV_OT_smart_project(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Smart UV Project";
+ ot->idname = "UV_OT_smart_project";
+ ot->description = "Projection unwraps the selected faces of mesh objects";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec = smart_project_exec;
+ ot->poll = ED_operator_uvmap;
+ ot->invoke = WM_operator_props_popup_confirm;
+
+ /* properties */
+ prop = RNA_def_float_rotation(ot->srna,
+ "angle_limit",
+ 0,
+ NULL,
+ DEG2RADF(0.0f),
+ DEG2RADF(90.0f),
+ "Angle Limit",
+ "Lower for more projection groups, higher for less distortion",
+ DEG2RADF(0.0f),
+ DEG2RADF(89.0f));
+ RNA_def_property_float_default(prop, DEG2RADF(66.0f));
+
+ RNA_def_float(ot->srna,
+ "island_margin",
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "Island Margin",
+ "Margin to reduce bleed from adjacent islands",
+ 0.0f,
+ 1.0f);
+ RNA_def_float(ot->srna,
+ "area_weight",
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "Area Weight",
+ "Weight projections vector by faces with larger areas",
+ 0.0f,
+ 1.0f);
+
+ uv_map_clip_correct_properties_ex(ot, false);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Project UV From View Operator
* \{ */
@@ -1973,7 +2357,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
}
if (changed_multi) {
- uv_map_clip_correct_multi(scene, objects, objects_len, op);
+ uv_map_clip_correct_multi(objects, objects_len, op);
}
MEM_freeN(objects);
@@ -1981,9 +2365,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
if (changed_multi) {
return OPERATOR_FINISHED;
}
- else {
- return OPERATOR_CANCELLED;
- }
+ return OPERATOR_CANCELLED;
}
static bool uv_from_view_poll(bContext *C)
@@ -2176,7 +2558,7 @@ static int sphere_project_exec(bContext *C, wmOperator *op)
uv_map_mirror(em, efa);
}
- uv_map_clip_correct(scene, obedit, op);
+ uv_map_clip_correct(obedit, op);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -2274,7 +2656,7 @@ static int cylinder_project_exec(bContext *C, wmOperator *op)
uv_map_mirror(em, efa);
}
- uv_map_clip_correct(scene, obedit, op);
+ uv_map_clip_correct(obedit, op);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -2397,7 +2779,7 @@ static int cube_project_exec(bContext *C, wmOperator *op)
uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
- uv_map_clip_correct(scene, obedit, op);
+ uv_map_clip_correct(obedit, op);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
diff --git a/source/blender/freestyle/intern/application/AppConfig.cpp b/source/blender/freestyle/intern/application/AppConfig.cpp
index a2f0a3395af..3aae4254b4b 100644
--- a/source/blender/freestyle/intern/application/AppConfig.cpp
+++ b/source/blender/freestyle/intern/application/AppConfig.cpp
@@ -26,9 +26,7 @@
using namespace std;
-extern "C" {
#include "BKE_appdir.h"
-}
namespace Freestyle {
@@ -46,17 +44,15 @@ Path::Path()
void Path::setRootDir(const string &iRootDir)
{
- _ProjectDir = iRootDir + string(DIR_SEP.c_str()) + "freestyle";
+ _ProjectDir = iRootDir + string(DIR_SEP) + "freestyle";
_ModelsPath = "";
- _PatternsPath = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) +
- "textures" + string(DIR_SEP.c_str()) + "variation_patterns" +
- string(DIR_SEP.c_str());
- _BrushesPath = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) +
- "textures" + string(DIR_SEP.c_str()) + "brushes" + string(DIR_SEP.c_str());
- _EnvMapDir = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) +
- "env_map" + string(DIR_SEP.c_str());
- _MapsDir = _ProjectDir + string(DIR_SEP.c_str()) + "data" + string(DIR_SEP.c_str()) + "maps" +
- string(DIR_SEP.c_str());
+ _PatternsPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "textures" +
+ string(DIR_SEP) + "variation_patterns" + string(DIR_SEP);
+ _BrushesPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "textures" +
+ string(DIR_SEP) + "brushes" + string(DIR_SEP);
+ _EnvMapDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "env_map" +
+ string(DIR_SEP);
+ _MapsDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "maps" + string(DIR_SEP);
}
void Path::setHomeDir(const string &iHomeDir)
diff --git a/source/blender/freestyle/intern/application/AppView.cpp b/source/blender/freestyle/intern/application/AppView.cpp
index 0956d33a20e..5cd30a61712 100644
--- a/source/blender/freestyle/intern/application/AppView.cpp
+++ b/source/blender/freestyle/intern/application/AppView.cpp
@@ -33,7 +33,6 @@
#include "../view_map/Silhouette.h"
#include "../view_map/ViewMap.h"
-extern "C" {
#include "BLI_blenlib.h"
#include "IMB_imbuf.h"
@@ -45,7 +44,6 @@ extern "C" {
#endif
#include "FRS_freestyle.h"
-}
namespace Freestyle {
diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp
index 253d62ea3dc..f9edadb2d4a 100644
--- a/source/blender/freestyle/intern/application/Controller.cpp
+++ b/source/blender/freestyle/intern/application/Controller.cpp
@@ -74,7 +74,7 @@ namespace Freestyle {
Controller::Controller()
{
- const string sep(Config::DIR_SEP.c_str());
+ const string sep(Config::DIR_SEP);
#if 0
const string filename = Config::Path::getInstance()->getHomeDir() + sep + Config::OPTIONS_DIR +
sep + Config::OPTIONS_CURRENT_DIRS_FILE;
@@ -469,7 +469,7 @@ void Controller::DeleteViewMap(bool freeCache)
void Controller::ComputeViewMap()
{
- if (!_ListOfModels.size()) {
+ if (_ListOfModels.empty()) {
return;
}
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
index 7bceb036846..e1763514e08 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
@@ -222,7 +222,7 @@ void BlenderFileLoader::clipTriangle(int numTris,
bool em1,
bool em2,
bool em3,
- int clip[3])
+ const int clip[3])
{
float *v[3], *n[3];
bool em[3];
@@ -746,7 +746,7 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
detriList.push_back(detri);
}
- if (detriList.size() > 0) {
+ if (!detriList.empty()) {
vector<detri_t>::iterator v;
for (v = detriList.begin(); v != detriList.end(); v++) {
detri_t detri = (*v);
@@ -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/BlenderFileLoader.h b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h
index ad6379d5f52..9d8b7e55fbb 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h
+++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h
@@ -37,7 +37,6 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -56,7 +55,6 @@ extern "C" {
#include "BLI_iterator.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
-}
#include "DEG_depsgraph_query.h"
@@ -128,7 +126,7 @@ class BlenderFileLoader {
bool em1,
bool em2,
bool em3,
- int clip[3]);
+ const int clip[3]);
void addTriangle(struct LoaderState *ls,
float v1[3],
float v2[3],
diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
index 0f4263162a6..51ac281e330 100644
--- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
+++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
@@ -34,8 +34,6 @@ using namespace Freestyle;
#include "MEM_guardedalloc.h"
-extern "C" {
-
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_freestyle_types.h"
@@ -65,6 +63,8 @@ extern "C" {
#include "FRS_freestyle.h"
+extern "C" {
+
#define DEFAULT_SPHERE_RADIUS 1.0f
#define DEFAULT_DKR_EPSILON 0.0f
@@ -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/geometry/Bezier.cpp b/source/blender/freestyle/intern/geometry/Bezier.cpp
index 9a832c04699..4ae30ff893c 100644
--- a/source/blender/freestyle/intern/geometry/Bezier.cpp
+++ b/source/blender/freestyle/intern/geometry/Bezier.cpp
@@ -109,9 +109,7 @@ BezierCurve::~BezierCurve()
delete *v;
}
}
- if (_currentSegment) {
- delete _currentSegment;
- }
+ delete _currentSegment;
}
void BezierCurve::AddControlPoint(const Vec2d &iPoint)
diff --git a/source/blender/freestyle/intern/geometry/Grid.cpp b/source/blender/freestyle/intern/geometry/Grid.cpp
index e3e25796de1..46e91197764 100644
--- a/source/blender/freestyle/intern/geometry/Grid.cpp
+++ b/source/blender/freestyle/intern/geometry/Grid.cpp
@@ -93,7 +93,7 @@ bool firstIntersectionGridVisitor::stop()
/////////////////
void Grid::clear()
{
- if (_occluders.size() != 0) {
+ if (!_occluders.empty()) {
for (OccludersSet::iterator it = _occluders.begin(); it != _occluders.end(); it++) {
delete (*it);
}
@@ -154,7 +154,7 @@ void Grid::configure(const Vec3r &orig, const Vec3r &size, unsigned nb)
void Grid::insertOccluder(Polygon3r *occluder)
{
const vector<Vec3r> vertices = occluder->getVertices();
- if (vertices.size() == 0) {
+ if (vertices.empty()) {
return;
}
diff --git a/source/blender/freestyle/intern/geometry/matrix_util.cpp b/source/blender/freestyle/intern/geometry/matrix_util.cpp
index 811b10813d1..9d25a240a63 100644
--- a/source/blender/freestyle/intern/geometry/matrix_util.cpp
+++ b/source/blender/freestyle/intern/geometry/matrix_util.cpp
@@ -248,7 +248,6 @@ void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec,
delete[] v;
delete[] index;
- return;
}
//_________________________________________________________
diff --git a/source/blender/freestyle/intern/image/GaussianFilter.cpp b/source/blender/freestyle/intern/image/GaussianFilter.cpp
index 87e2caee8a9..ca476bee0d3 100644
--- a/source/blender/freestyle/intern/image/GaussianFilter.cpp
+++ b/source/blender/freestyle/intern/image/GaussianFilter.cpp
@@ -55,9 +55,7 @@ GaussianFilter &GaussianFilter::operator=(const GaussianFilter &iBrother)
GaussianFilter::~GaussianFilter()
{
- if (0 != _mask) {
- delete[] _mask;
- }
+ delete[] _mask;
}
int GaussianFilter::computeMaskSize(float sigma)
@@ -78,9 +76,7 @@ void GaussianFilter::setSigma(float sigma)
void GaussianFilter::computeMask()
{
- if (0 != _mask) {
- delete[] _mask;
- }
+ delete[] _mask;
_maskSize = computeMaskSize(_sigma);
_storedMaskSize = (_maskSize + 1) >> 1;
diff --git a/source/blender/freestyle/intern/image/Image.h b/source/blender/freestyle/intern/image/Image.h
index c42e47a670b..cf8b0e33c6b 100644
--- a/source/blender/freestyle/intern/image/Image.h
+++ b/source/blender/freestyle/intern/image/Image.h
@@ -394,7 +394,7 @@ class GrayImage : public FrsImage {
/*! Sets the array.
* copy
- * If true, the array is copie, otherwise the pounsigneder is copied
+ * If true, the array is copied, otherwise the pounsigneder is copied
*/
void setArray(float *lvl,
unsigned width,
diff --git a/source/blender/freestyle/intern/python/BPy_BinaryPredicate0D.cpp b/source/blender/freestyle/intern/python/BPy_BinaryPredicate0D.cpp
index a631b45f6d4..42b13bf4920 100644
--- a/source/blender/freestyle/intern/python/BPy_BinaryPredicate0D.cpp
+++ b/source/blender/freestyle/intern/python/BPy_BinaryPredicate0D.cpp
@@ -84,9 +84,8 @@ static int BinaryPredicate0D___init__(BPy_BinaryPredicate0D *self, PyObject *arg
static void BinaryPredicate0D___dealloc__(BPy_BinaryPredicate0D *self)
{
- if (self->bp0D) {
- delete self->bp0D;
- }
+ delete self->bp0D;
+
Py_TYPE(self)->tp_free((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/BPy_BinaryPredicate1D.cpp b/source/blender/freestyle/intern/python/BPy_BinaryPredicate1D.cpp
index 9add706bea7..ed991695739 100644
--- a/source/blender/freestyle/intern/python/BPy_BinaryPredicate1D.cpp
+++ b/source/blender/freestyle/intern/python/BPy_BinaryPredicate1D.cpp
@@ -120,9 +120,7 @@ static int BinaryPredicate1D___init__(BPy_BinaryPredicate1D *self, PyObject *arg
static void BinaryPredicate1D___dealloc__(BPy_BinaryPredicate1D *self)
{
- if (self->bp1D) {
- delete self->bp1D;
- }
+ delete self->bp1D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
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/python/BPy_FrsMaterial.cpp b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
index 2c226e330d7..3c2f022adb6 100644
--- a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
+++ b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
@@ -108,7 +108,7 @@ static int FrsMaterial_init(BPy_FrsMaterial *self, PyObject *args, PyObject *kwd
self->m = new FrsMaterial(*m);
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O&O&O&O&O&fi",
diff --git a/source/blender/freestyle/intern/python/BPy_Id.cpp b/source/blender/freestyle/intern/python/BPy_Id.cpp
index 4f61ba847f6..eb0eb661e3d 100644
--- a/source/blender/freestyle/intern/python/BPy_Id.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Id.cpp
@@ -75,7 +75,7 @@ static int Id_init(BPy_Id *self, PyObject *args, PyObject *kwds)
if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_1, &Id_Type, &brother)) {
self->id = new Id(*(((BPy_Id *)brother)->id));
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args, kwds, "|ii", (char **)kwlist_2, &first, &second)) {
self->id = new Id(first, second);
}
diff --git a/source/blender/freestyle/intern/python/BPy_Iterator.cpp b/source/blender/freestyle/intern/python/BPy_Iterator.cpp
index 6e00a05b969..b8dedb6497a 100644
--- a/source/blender/freestyle/intern/python/BPy_Iterator.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Iterator.cpp
@@ -137,9 +137,7 @@ static int Iterator_init(BPy_Iterator *self, PyObject *args, PyObject *kwds)
static void Iterator_dealloc(BPy_Iterator *self)
{
- if (self->it) {
- delete self->it;
- }
+ delete self->it;
Py_TYPE(self)->tp_free((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/BPy_Operators.cpp b/source/blender/freestyle/intern/python/BPy_Operators.cpp
index 510e823ba55..56f95b8ecbb 100644
--- a/source/blender/freestyle/intern/python/BPy_Operators.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Operators.cpp
@@ -365,8 +365,8 @@ static PyObject *Operators_sequential_split(BPy_Operators * /*self*/,
return NULL;
}
}
- else if (PyErr_Clear(),
- (f = 0.0f),
+ else if ((void)PyErr_Clear(),
+ (void)(f = 0.0f),
PyArg_ParseTupleAndKeywords(
args, kwds, "O!|f", (char **)kwlist_2, &UnaryPredicate0D_Type, &obj1, &f)) {
if (!((BPy_UnaryPredicate0D *)obj1)->up0D) {
@@ -484,8 +484,8 @@ static PyObject *Operators_recursive_split(BPy_Operators * /*self*/,
return NULL;
}
}
- else if (PyErr_Clear(),
- (f = 0.0f),
+ else if ((void)PyErr_Clear(),
+ (void)(f = 0.0f),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!O!|f",
diff --git a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp
index 5f5407e82e3..214385f74ad 100644
--- a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp
+++ b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp
@@ -110,7 +110,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj
self->sa = new StrokeAttribute(*(((BPy_StrokeAttribute *)obj1)->sa));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!f",
@@ -123,7 +123,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj
self->sa = new StrokeAttribute(
*(((BPy_StrokeAttribute *)obj1)->sa), *(((BPy_StrokeAttribute *)obj2)->sa), t);
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"ffffff",
diff --git a/source/blender/freestyle/intern/python/BPy_StrokeShader.cpp b/source/blender/freestyle/intern/python/BPy_StrokeShader.cpp
index 993afc2e32e..5e5685153af 100644
--- a/source/blender/freestyle/intern/python/BPy_StrokeShader.cpp
+++ b/source/blender/freestyle/intern/python/BPy_StrokeShader.cpp
@@ -210,9 +210,7 @@ static int StrokeShader___init__(BPy_StrokeShader *self, PyObject *args, PyObjec
static void StrokeShader___dealloc__(BPy_StrokeShader *self)
{
- if (self->ss) {
- delete self->ss;
- }
+ delete self->ss;
Py_TYPE(self)->tp_free((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/BPy_UnaryPredicate0D.cpp b/source/blender/freestyle/intern/python/BPy_UnaryPredicate0D.cpp
index 3d4191c2c44..ddbb55fdb4d 100644
--- a/source/blender/freestyle/intern/python/BPy_UnaryPredicate0D.cpp
+++ b/source/blender/freestyle/intern/python/BPy_UnaryPredicate0D.cpp
@@ -97,9 +97,7 @@ static int UnaryPredicate0D___init__(BPy_UnaryPredicate0D *self, PyObject *args,
static void UnaryPredicate0D___dealloc__(BPy_UnaryPredicate0D *self)
{
- if (self->up0D) {
- delete self->up0D;
- }
+ delete self->up0D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/BPy_UnaryPredicate1D.cpp b/source/blender/freestyle/intern/python/BPy_UnaryPredicate1D.cpp
index ba68d21f941..11e77fa365a 100644
--- a/source/blender/freestyle/intern/python/BPy_UnaryPredicate1D.cpp
+++ b/source/blender/freestyle/intern/python/BPy_UnaryPredicate1D.cpp
@@ -155,9 +155,7 @@ static int UnaryPredicate1D___init__(BPy_UnaryPredicate1D *self, PyObject *args,
static void UnaryPredicate1D___dealloc__(BPy_UnaryPredicate1D *self)
{
- if (self->up1D) {
- delete self->up1D;
- }
+ delete self->up1D;
Py_TYPE(self)->tp_free((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/BPy_ViewMap.cpp b/source/blender/freestyle/intern/python/BPy_ViewMap.cpp
index b7b425abe23..67a4249ade8 100644
--- a/source/blender/freestyle/intern/python/BPy_ViewMap.cpp
+++ b/source/blender/freestyle/intern/python/BPy_ViewMap.cpp
@@ -69,9 +69,7 @@ static int ViewMap_init(BPy_ViewMap *self, PyObject *args, PyObject *kwds)
static void ViewMap_dealloc(BPy_ViewMap *self)
{
- if (self->vm) {
- delete self->vm;
- }
+ delete self->vm;
Py_TYPE(self)->tp_free((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
index f59ae15ae01..2adcae13e6d 100644
--- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
+++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
@@ -88,7 +88,7 @@ static int ViewShape_init(BPy_ViewShape *self, PyObject *args, PyObject *kwds)
self->py_ss = ((BPy_ViewShape *)obj)->py_ss;
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &SShape_Type, &obj)) {
BPy_SShape *py_ss = (BPy_SShape *)obj;
self->vs = new ViewShape(py_ss->ss);
diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
index 6af80c4bfd9..81dd79ff270 100644
--- a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
@@ -95,7 +95,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds)
self->cp = new CurvePoint(*(((BPy_CurvePoint *)obj1)->cp));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!f",
@@ -107,7 +107,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds)
&t2d)) {
self->cp = new CurvePoint(((BPy_SVertex *)obj1)->sv, ((BPy_SVertex *)obj2)->sv, t2d);
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!f",
diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp
index 71a87c2c01e..c01f1f17000 100644
--- a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp
@@ -72,7 +72,7 @@ static int SVertex_init(BPy_SVertex *self, PyObject *args, PyObject *kwds)
self->sv = new SVertex(*(((BPy_SVertex *)obj)->sv));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(
args, kwds, "O&O!", (char **)kwlist_2, convert_v3, v, &Id_Type, &obj)) {
Vec3r point_3d(v[0], v[1], v[2]);
diff --git a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
index b4c08714af0..519bd72db3b 100644
--- a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
+++ b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp
@@ -107,7 +107,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k
self->sv = new StrokeVertex(*(((BPy_StrokeVertex *)obj1)->sv));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!f",
@@ -129,7 +129,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k
}
self->sv = new StrokeVertex(sv1, sv2, t3d);
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(
args, kwds, "O!", (char **)kwlist_3, &CurvePoint_Type, &obj1)) {
CurvePoint *cp = ((BPy_CurvePoint *)obj1)->cp;
@@ -139,8 +139,8 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k
}
self->sv = new StrokeVertex(cp);
}
- else if (PyErr_Clear(),
- (obj2 = 0),
+ else if ((void)PyErr_Clear(),
+ (void)(obj2 = 0),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!|O!",
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp
index 8a09e7722ea..187ab94360a 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp
@@ -81,7 +81,7 @@ static int FEdge_init(BPy_FEdge *self, PyObject *args, PyObject *kwds)
self->fe = new FEdge(*(((BPy_FEdge *)obj1)->fe));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!",
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp
index fd434f9c4ef..788dfa78992 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp
@@ -72,7 +72,7 @@ static int FrsCurve_init(BPy_FrsCurve *self, PyObject *args, PyObject *kwds)
self->c = new Curve(*(((BPy_FrsCurve *)obj)->c));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Id_Type, &obj)) {
self->c = new Curve(*(((BPy_Id *)obj)->id));
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp
index 3a25ddb0252..b31efe1f923 100644
--- a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp
@@ -157,7 +157,8 @@ static PyObject *Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwd
return NULL;
}
}
- else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) {
+ else if ((void)PyErr_Clear(),
+ PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) {
if (self->s->Resample(f) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex interval) failed");
return NULL;
diff --git a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp
index 285100193d3..9cdc344081e 100644
--- a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp
@@ -71,7 +71,7 @@ static int Chain_init(BPy_Chain *self, PyObject *args, PyObject *kwds)
self->c = new Chain(*(((BPy_Chain *)obj)->c));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Id_Type, &obj)) {
self->c = new Chain(*(((BPy_Id *)obj)->id));
}
diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
index 725daa80b5e..5db75c84608 100644
--- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp
@@ -75,7 +75,7 @@ static int FEdgeSharp_init(BPy_FEdgeSharp *self, PyObject *args, PyObject *kwds)
self->fes = new FEdgeSharp(*(((BPy_FEdgeSharp *)obj1)->fes));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!",
diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
index 65d9dcbe01f..3fb739b18db 100644
--- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
+++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp
@@ -73,7 +73,7 @@ static int FEdgeSmooth_init(BPy_FEdgeSmooth *self, PyObject *args, PyObject *kwd
self->fes = new FEdgeSmooth(*(((BPy_FEdgeSmooth *)obj1)->fes));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!",
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp
index 74ae7809284..90e751333b9 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp
@@ -82,8 +82,8 @@ static int AdjacencyIterator_init(BPy_AdjacencyIterator *self, PyObject *args, P
self->at_start = ((BPy_AdjacencyIterator *)obj1)->at_start;
}
}
- else if (PyErr_Clear(),
- (obj2 = obj3 = 0),
+ else if ((void)PyErr_Clear(),
+ (void)(obj2 = obj3 = 0),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!|O!O!",
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp
index 164e1646934..1703fc2bddb 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp
@@ -114,8 +114,8 @@ static int ChainPredicateIterator_init(BPy_ChainPredicateIterator *self,
Py_INCREF(self->upred);
Py_INCREF(self->bpred);
}
- else if (PyErr_Clear(),
- (obj3 = obj4 = obj5 = obj6 = 0),
+ else if ((void)PyErr_Clear(),
+ (void)(obj3 = obj4 = obj5 = obj6 = 0),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!|O!O!O&O!",
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp
index 401959be0c0..d8ad82d667c 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp
@@ -91,8 +91,8 @@ static int ChainSilhouetteIterator_init(BPy_ChainSilhouetteIterator *self,
args, kwds, "O!", (char **)kwlist_1, &ChainSilhouetteIterator_Type, &obj1)) {
self->cs_it = new ChainSilhouetteIterator(*(((BPy_ChainSilhouetteIterator *)obj1)->cs_it));
}
- else if (PyErr_Clear(),
- (obj1 = obj2 = obj3 = 0),
+ else if ((void)PyErr_Clear(),
+ (void)(obj1 = obj2 = obj3 = 0),
PyArg_ParseTupleAndKeywords(args,
kwds,
"|O!O&O!",
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp
index b6d841c5b64..dbd6e8dd09d 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp
@@ -91,8 +91,8 @@ static int ChainingIterator___init__(BPy_ChainingIterator *self, PyObject *args,
args, kwds, "O!", (char **)kwlist_1, &ChainingIterator_Type, &obj1)) {
self->c_it = new ChainingIterator(*(((BPy_ChainingIterator *)obj1)->c_it));
}
- else if (PyErr_Clear(),
- (obj1 = obj2 = obj3 = obj4 = 0),
+ else if ((void)PyErr_Clear(),
+ (void)(obj1 = obj2 = obj3 = obj4 = 0),
PyArg_ParseTupleAndKeywords(args,
kwds,
"|O!O!O&O!",
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp
index 6ea61a060cb..6c496b0308b 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp
@@ -75,7 +75,8 @@ static int CurvePointIterator_init(BPy_CurvePointIterator *self, PyObject *args,
*(((BPy_CurvePointIterator *)brother)->cp_it));
}
}
- else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &step)) {
+ else if ((void)PyErr_Clear(),
+ PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &step)) {
self->cp_it = new CurveInternal::CurvePointIterator(step);
}
else {
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp
index 0dbef9f325c..f0d6acf461b 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp
@@ -81,14 +81,14 @@ static int Interface0DIterator_init(BPy_Interface0DIterator *self, PyObject *arg
self->at_start = true;
self->reversed = false;
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(
args, kwds, "O!", (char **)kwlist_2, &Interface1D_Type, &inter)) {
self->if0D_it = new Interface0DIterator(((BPy_Interface1D *)inter)->if1D->verticesBegin());
self->at_start = true;
self->reversed = false;
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(
args, kwds, "O!", (char **)kwlist_3, &Interface0DIterator_Type, &brother)) {
self->if0D_it = new Interface0DIterator(*(((BPy_Interface0DIterator *)brother)->if0D_it));
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp
index dd738b97473..4a5927ff6eb 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp
@@ -82,7 +82,7 @@ static int SVertexIterator_init(BPy_SVertexIterator *self, PyObject *args, PyObj
self->sv_it = new ViewEdgeInternal::SVertexIterator(*(((BPy_SVertexIterator *)obj1)->sv_it));
}
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(args,
kwds,
"O!O!O!O!f",
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp
index 84f57f1fe31..df03ecba96f 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp
@@ -74,7 +74,7 @@ static int StrokeVertexIterator_init(BPy_StrokeVertexIterator *self,
self->at_start = ((BPy_StrokeVertexIterator *)brother)->at_start;
}
- else if (PyErr_Clear(),
+ else if ((void)PyErr_Clear(),
PyArg_ParseTupleAndKeywords(
args, kwds, "|O!", (char **)kwlist_2, &Stroke_Type, &stroke)) {
if (!stroke) {
diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp
index c8a978784a4..3d0ed5d5a4d 100644
--- a/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp
+++ b/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp
@@ -78,8 +78,8 @@ static int ViewEdgeIterator_init(BPy_ViewEdgeIterator *self, PyObject *args, PyO
args, kwds, "O!", (char **)kwlist_1, &ViewEdgeIterator_Type, &obj1)) {
self->ve_it = new ViewEdgeInternal::ViewEdgeIterator(*(((BPy_ViewEdgeIterator *)obj1)->ve_it));
}
- else if (PyErr_Clear(),
- (obj1 = obj2 = 0),
+ else if ((void)PyErr_Clear(),
+ (void)(obj1 = obj2 = 0),
PyArg_ParseTupleAndKeywords(
args, kwds, "|O&O!", (char **)kwlist_2, check_begin, &obj1, &PyBool_Type, &obj2)) {
ViewEdge *begin = (!obj1 || obj1 == Py_None) ? NULL : ((BPy_ViewEdge *)obj1)->ve;
diff --git a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp
index 51a0c670d75..9e2726061c3 100644
--- a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp
+++ b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp
@@ -35,9 +35,9 @@ static char SmoothingShader___doc__[] =
"\n"
"[Geometry shader]\n"
"\n"
- ".. method:: __init__(num_iterations=100, factor_point=0.1,\n"
- " factor_curvature=0.0, factor_curvature_difference=0.2,\n"
- " aniso_point=0.0, aniso_normal=0.0, aniso_curvature=0.0,\n"
+ ".. method:: __init__(num_iterations=100, factor_point=0.1, \\\n"
+ " factor_curvature=0.0, factor_curvature_difference=0.2, \\\n"
+ " aniso_point=0.0, aniso_normal=0.0, aniso_curvature=0.0, \\\n"
" carricature_factor=1.0)\n"
"\n"
" Builds a SmoothingShader object.\n"
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.cpp
index d1ea13c90a7..9ced91f6867 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DDouble.cpp
@@ -145,9 +145,7 @@ static int UnaryFunction0DDouble___init__(BPy_UnaryFunction0DDouble *self,
static void UnaryFunction0DDouble___dealloc__(BPy_UnaryFunction0DDouble *self)
{
- if (self->uf0D_double) {
- delete self->uf0D_double;
- }
+ delete self->uf0D_double;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.cpp
index 00600c405ef..7520f647276 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DEdgeNature.cpp
@@ -83,9 +83,7 @@ static int UnaryFunction0DEdgeNature___init__(BPy_UnaryFunction0DEdgeNature *sel
static void UnaryFunction0DEdgeNature___dealloc__(BPy_UnaryFunction0DEdgeNature *self)
{
- if (self->uf0D_edgenature) {
- delete self->uf0D_edgenature;
- }
+ delete self->uf0D_edgenature;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.cpp
index 3961bf58bc4..7ee0d9bd71d 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DFloat.cpp
@@ -121,9 +121,7 @@ static int UnaryFunction0DFloat___init__(BPy_UnaryFunction0DFloat *self,
static void UnaryFunction0DFloat___dealloc__(BPy_UnaryFunction0DFloat *self)
{
- if (self->uf0D_float) {
- delete self->uf0D_float;
- }
+ delete self->uf0D_float;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.cpp
index 86e902bafe4..9c0f833d8fa 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DId.cpp
@@ -80,9 +80,7 @@ static int UnaryFunction0DId___init__(BPy_UnaryFunction0DId *self, PyObject *arg
static void UnaryFunction0DId___dealloc__(BPy_UnaryFunction0DId *self)
{
- if (self->uf0D_id) {
- delete self->uf0D_id;
- }
+ delete self->uf0D_id;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.cpp
index 551429add8b..95cdc1522ac 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DMaterial.cpp
@@ -82,9 +82,7 @@ static int UnaryFunction0DMaterial___init__(BPy_UnaryFunction0DMaterial *self,
static void UnaryFunction0DMaterial___dealloc__(BPy_UnaryFunction0DMaterial *self)
{
- if (self->uf0D_material) {
- delete self->uf0D_material;
- }
+ delete self->uf0D_material;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.cpp
index 8721e820fee..17ddd9773d2 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DUnsigned.cpp
@@ -83,9 +83,7 @@ static int UnaryFunction0DUnsigned___init__(BPy_UnaryFunction0DUnsigned *self,
static void UnaryFunction0DUnsigned___dealloc__(BPy_UnaryFunction0DUnsigned *self)
{
- if (self->uf0D_unsigned) {
- delete self->uf0D_unsigned;
- }
+ delete self->uf0D_unsigned;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.cpp
index 44a39af68dd..3ff73dca832 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec2f.cpp
@@ -89,9 +89,7 @@ static int UnaryFunction0DVec2f___init__(BPy_UnaryFunction0DVec2f *self,
static void UnaryFunction0DVec2f___dealloc__(BPy_UnaryFunction0DVec2f *self)
{
- if (self->uf0D_vec2f) {
- delete self->uf0D_vec2f;
- }
+ delete self->uf0D_vec2f;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.cpp
index 6a27fc6e140..afbd25df122 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVec3f.cpp
@@ -82,9 +82,7 @@ static int UnaryFunction0DVec3f___init__(BPy_UnaryFunction0DVec3f *self,
static void UnaryFunction0DVec3f___dealloc__(BPy_UnaryFunction0DVec3f *self)
{
- if (self->uf0D_vec3f) {
- delete self->uf0D_vec3f;
- }
+ delete self->uf0D_vec3f;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.cpp
index 88c050e243e..4fd6d949974 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DVectorViewShape.cpp
@@ -84,9 +84,7 @@ static int UnaryFunction0DVectorViewShape___init__(BPy_UnaryFunction0DVectorView
static void UnaryFunction0DVectorViewShape___dealloc__(BPy_UnaryFunction0DVectorViewShape *self)
{
- if (self->uf0D_vectorviewshape) {
- delete self->uf0D_vectorviewshape;
- }
+ delete self->uf0D_vectorviewshape;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.cpp b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.cpp
index 11f4e114342..0f623b0e765 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction0D/BPy_UnaryFunction0DViewShape.cpp
@@ -90,9 +90,7 @@ static int UnaryFunction0DViewShape___init__(BPy_UnaryFunction0DViewShape *self,
static void UnaryFunction0DViewShape___dealloc__(BPy_UnaryFunction0DViewShape *self)
{
- if (self->uf0D_viewshape) {
- delete self->uf0D_viewshape;
- }
+ delete self->uf0D_viewshape;
UnaryFunction0D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.cpp
index fb887371c3a..88705c41204 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DDouble.cpp
@@ -197,9 +197,7 @@ static int UnaryFunction1DDouble___init__(BPy_UnaryFunction1DDouble *self,
static void UnaryFunction1DDouble___dealloc__(BPy_UnaryFunction1DDouble *self)
{
- if (self->uf1D_double) {
- delete self->uf1D_double;
- }
+ delete self->uf1D_double;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.cpp
index e2a55d49a06..3cfc5464296 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DEdgeNature.cpp
@@ -103,9 +103,7 @@ static int UnaryFunction1DEdgeNature___init__(BPy_UnaryFunction1DEdgeNature *sel
static void UnaryFunction1DEdgeNature___dealloc__(BPy_UnaryFunction1DEdgeNature *self)
{
- if (self->uf1D_edgenature) {
- delete self->uf1D_edgenature;
- }
+ delete self->uf1D_edgenature;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.cpp
index 2eb4f9349da..4fdc68a2e89 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DFloat.cpp
@@ -93,9 +93,7 @@ static int UnaryFunction1DFloat___init__(BPy_UnaryFunction1DFloat *self,
static void UnaryFunction1DFloat___dealloc__(BPy_UnaryFunction1DFloat *self)
{
- if (self->uf1D_float) {
- delete self->uf1D_float;
- }
+ delete self->uf1D_float;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.cpp
index ff306652036..218afa3ff10 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DUnsigned.cpp
@@ -103,9 +103,7 @@ static int UnaryFunction1DUnsigned___init__(BPy_UnaryFunction1DUnsigned *self,
static void UnaryFunction1DUnsigned___dealloc__(BPy_UnaryFunction1DUnsigned *self)
{
- if (self->uf1D_unsigned) {
- delete self->uf1D_unsigned;
- }
+ delete self->uf1D_unsigned;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.cpp
index 1469b0a6e33..939ccfb8905 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec2f.cpp
@@ -108,9 +108,7 @@ static int UnaryFunction1DVec2f___init__(BPy_UnaryFunction1DVec2f *self,
static void UnaryFunction1DVec2f___dealloc__(BPy_UnaryFunction1DVec2f *self)
{
- if (self->uf1D_vec2f) {
- delete self->uf1D_vec2f;
- }
+ delete self->uf1D_vec2f;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.cpp
index 63cd0584adb..02b6373cce4 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVec3f.cpp
@@ -101,9 +101,7 @@ static int UnaryFunction1DVec3f___init__(BPy_UnaryFunction1DVec3f *self,
static void UnaryFunction1DVec3f___dealloc__(BPy_UnaryFunction1DVec3f *self)
{
- if (self->uf1D_vec3f) {
- delete self->uf1D_vec3f;
- }
+ delete self->uf1D_vec3f;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.cpp
index cb94d6b75e2..b2020e9f554 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVectorViewShape.cpp
@@ -118,9 +118,7 @@ static int UnaryFunction1DVectorViewShape___init__(BPy_UnaryFunction1DVectorView
static void UnaryFunction1DVectorViewShape___dealloc__(BPy_UnaryFunction1DVectorViewShape *self)
{
- if (self->uf1D_vectorviewshape) {
- delete self->uf1D_vectorviewshape;
- }
+ delete self->uf1D_vectorviewshape;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.cpp b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.cpp
index 216b1305556..7b25daa24f9 100644
--- a/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.cpp
+++ b/source/blender/freestyle/intern/python/UnaryFunction1D/BPy_UnaryFunction1DVoid.cpp
@@ -116,9 +116,7 @@ static int UnaryFunction1DVoid___init__(BPy_UnaryFunction1DVoid *self,
static void UnaryFunction1DVoid___dealloc__(BPy_UnaryFunction1DVoid *self)
{
- if (self->uf1D_void) {
- delete self->uf1D_void;
- }
+ delete self->uf1D_void;
UnaryFunction1D_Type.tp_dealloc((PyObject *)self);
}
diff --git a/source/blender/freestyle/intern/scene_graph/NodeShape.cpp b/source/blender/freestyle/intern/scene_graph/NodeShape.cpp
index e55b2fb625f..cf7a609b347 100644
--- a/source/blender/freestyle/intern/scene_graph/NodeShape.cpp
+++ b/source/blender/freestyle/intern/scene_graph/NodeShape.cpp
@@ -27,7 +27,7 @@ NodeShape::~NodeShape()
{
vector<Rep *>::iterator rep;
- if (0 != _Shapes.size()) {
+ if (!_Shapes.empty()) {
for (rep = _Shapes.begin(); rep != _Shapes.end(); ++rep) {
int refCount = (*rep)->destroy();
if (0 == refCount) {
diff --git a/source/blender/freestyle/intern/scene_graph/SceneHash.cpp b/source/blender/freestyle/intern/scene_graph/SceneHash.cpp
index b5d954664e4..e5f029d28b7 100644
--- a/source/blender/freestyle/intern/scene_graph/SceneHash.cpp
+++ b/source/blender/freestyle/intern/scene_graph/SceneHash.cpp
@@ -65,7 +65,7 @@ void SceneHash::visitIndexedFaceSet(IndexedFaceSet &ifs)
static const int MOD_ADLER = 65521;
-void SceneHash::adler32(unsigned char *data, int size)
+void SceneHash::adler32(const unsigned char *data, int size)
{
uint32_t sum1 = _sum & 0xffff;
uint32_t sum2 = (_sum >> 16) & 0xffff;
diff --git a/source/blender/freestyle/intern/scene_graph/SceneHash.h b/source/blender/freestyle/intern/scene_graph/SceneHash.h
index 53d1381da60..05c0880f806 100644
--- a/source/blender/freestyle/intern/scene_graph/SceneHash.h
+++ b/source/blender/freestyle/intern/scene_graph/SceneHash.h
@@ -67,7 +67,7 @@ class SceneHash : public SceneVisitor {
}
private:
- void adler32(unsigned char *data, int size);
+ void adler32(const unsigned char *data, int size);
uint32_t _sum;
uint32_t _prevSum;
diff --git a/source/blender/freestyle/intern/scene_graph/ScenePrettyPrinter.cpp b/source/blender/freestyle/intern/scene_graph/ScenePrettyPrinter.cpp
index 0c533232179..33aa368d755 100644
--- a/source/blender/freestyle/intern/scene_graph/ScenePrettyPrinter.cpp
+++ b/source/blender/freestyle/intern/scene_graph/ScenePrettyPrinter.cpp
@@ -39,42 +39,42 @@ VISIT(NodeLight)
VISIT(NodeDrawingStyle)
VISIT(NodeTransform)
-void ScenePrettyPrinter::visitNodeShapeBefore(NodeShape &)
+void ScenePrettyPrinter::visitNodeShapeBefore(NodeShape &UNUSED(shape))
{
increaseSpace();
}
-void ScenePrettyPrinter::visitNodeShapeAfter(NodeShape &)
+void ScenePrettyPrinter::visitNodeShapeAfter(NodeShape &UNUSED(shape))
{
decreaseSpace();
}
-void ScenePrettyPrinter::visitNodeGroupBefore(NodeGroup &)
+void ScenePrettyPrinter::visitNodeGroupBefore(NodeGroup &UNUSED(group))
{
increaseSpace();
}
-void ScenePrettyPrinter::visitNodeGroupAfter(NodeGroup &)
+void ScenePrettyPrinter::visitNodeGroupAfter(NodeGroup &UNUSED(group))
{
decreaseSpace();
}
-void ScenePrettyPrinter::visitNodeDrawingStyleBefore(NodeDrawingStyle &)
+void ScenePrettyPrinter::visitNodeDrawingStyleBefore(NodeDrawingStyle &UNUSED(style))
{
increaseSpace();
}
-void ScenePrettyPrinter::visitNodeDrawingStyleAfter(NodeDrawingStyle &)
+void ScenePrettyPrinter::visitNodeDrawingStyleAfter(NodeDrawingStyle &UNUSED(style))
{
decreaseSpace();
}
-void ScenePrettyPrinter::visitNodeTransformBefore(NodeTransform &)
+void ScenePrettyPrinter::visitNodeTransformBefore(NodeTransform &UNUSED(transform))
{
increaseSpace();
}
-void ScenePrettyPrinter::visitNodeTransformAfter(NodeTransform &)
+void ScenePrettyPrinter::visitNodeTransformAfter(NodeTransform &UNUSED(transform))
{
decreaseSpace();
}
diff --git a/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp
index d969e0e50a3..36234ad619c 100644
--- a/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp
+++ b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp
@@ -37,10 +37,8 @@
#include "BKE_global.h"
-extern "C" {
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
-}
namespace Freestyle {
diff --git a/source/blender/freestyle/intern/stroke/Canvas.cpp b/source/blender/freestyle/intern/stroke/Canvas.cpp
index 4386e64345f..14c74c0f127 100644
--- a/source/blender/freestyle/intern/stroke/Canvas.cpp
+++ b/source/blender/freestyle/intern/stroke/Canvas.cpp
@@ -90,9 +90,7 @@ Canvas::~Canvas()
}
_maps.clear();
}
- if (_steerableViewMap) {
- delete _steerableViewMap;
- }
+ delete _steerableViewMap;
}
void Canvas::preDraw()
diff --git a/source/blender/freestyle/intern/stroke/Stroke.cpp b/source/blender/freestyle/intern/stroke/Stroke.cpp
index 2f29eac83b1..93a870c26ad 100644
--- a/source/blender/freestyle/intern/stroke/Stroke.cpp
+++ b/source/blender/freestyle/intern/stroke/Stroke.cpp
@@ -484,9 +484,7 @@ Stroke &Stroke::operator=(const Stroke &iBrother)
_id = iBrother._id;
_ViewEdges = iBrother._ViewEdges;
_sampling = iBrother._sampling;
- if (_rep) {
- delete _rep;
- }
+ delete _rep;
if (iBrother._rep) {
_rep = new StrokeRep(*(iBrother._rep));
}
diff --git a/source/blender/freestyle/intern/system/PythonInterpreter.h b/source/blender/freestyle/intern/system/PythonInterpreter.h
index 4bc6ba9db38..8099ed63199 100644
--- a/source/blender/freestyle/intern/system/PythonInterpreter.h
+++ b/source/blender/freestyle/intern/system/PythonInterpreter.h
@@ -34,7 +34,6 @@ extern "C" {
#include "MEM_guardedalloc.h"
// soc
-extern "C" {
#include "DNA_text_types.h"
#include "BKE_context.h"
@@ -47,7 +46,6 @@ extern "C" {
#include "BPY_extern.h"
#include "bpy_capi_utils.h"
-}
namespace Freestyle {
diff --git a/source/blender/freestyle/intern/system/RenderMonitor.h b/source/blender/freestyle/intern/system/RenderMonitor.h
index 709df6c2d8e..eef4683328b 100644
--- a/source/blender/freestyle/intern/system/RenderMonitor.h
+++ b/source/blender/freestyle/intern/system/RenderMonitor.h
@@ -22,9 +22,7 @@
* \brief Classes defining the basic "Iterator" design pattern
*/
-extern "C" {
#include "render_types.h"
-}
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
diff --git a/source/blender/freestyle/intern/system/StringUtils.h b/source/blender/freestyle/intern/system/StringUtils.h
index aeacddd64c8..fd97ca5b032 100644
--- a/source/blender/freestyle/intern/system/StringUtils.h
+++ b/source/blender/freestyle/intern/system/StringUtils.h
@@ -28,10 +28,8 @@
#include <string>
#include <vector>
-extern "C" {
#include "BLI_path_util.h"
#include "BLI_string.h"
-}
using namespace std;
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/CulledOccluderSource.cpp b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
index 2df5ecd0867..cb3a297076a 100644
--- a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
+++ b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
@@ -94,7 +94,7 @@ static inline bool crossesProscenium(real proscenium[4], FEdge *fe)
return GeomUtils::intersect2dSeg2dArea(min, max, A, B);
}
-static inline bool insideProscenium(real proscenium[4], const Vec3r &point)
+static inline bool insideProscenium(const real proscenium[4], const Vec3r &point)
{
return !(point[0] < proscenium[0] || point[0] > proscenium[1] || point[1] < proscenium[2] ||
point[1] > proscenium[3]);
diff --git a/source/blender/freestyle/intern/view_map/Interface0D.cpp b/source/blender/freestyle/intern/view_map/Interface0D.cpp
index fc5a797cc87..2961b0f58e5 100644
--- a/source/blender/freestyle/intern/view_map/Interface0D.cpp
+++ b/source/blender/freestyle/intern/view_map/Interface0D.cpp
@@ -24,6 +24,8 @@ extern "C" {
#include "Interface0D.h"
+#include "BLI_utildefines.h"
+
namespace Freestyle {
real Interface0D::getX() const
@@ -74,7 +76,7 @@ Geometry::Vec2r Interface0D::getPoint2D() const
return 0;
}
-FEdge *Interface0D::getFEdge(Interface0D &)
+FEdge *Interface0D::getFEdge(Interface0D &UNUSED(element))
{
PyErr_SetString(PyExc_TypeError, "method getFEdge() not properly overridden");
return 0;
diff --git a/source/blender/freestyle/intern/view_map/SteerableViewMap.cpp b/source/blender/freestyle/intern/view_map/SteerableViewMap.cpp
index efda4b7fd2a..7549ebcb258 100644
--- a/source/blender/freestyle/intern/view_map/SteerableViewMap.cpp
+++ b/source/blender/freestyle/intern/view_map/SteerableViewMap.cpp
@@ -193,9 +193,7 @@ void SteerableViewMap::buildImagesPyramids(GrayImage **steerableBases,
{
for (unsigned int i = 0; i <= _nbOrientations; ++i) {
ImagePyramid *svm = (_imagesPyramids)[i];
- if (svm) {
- delete svm;
- }
+ delete svm;
if (copy) {
svm = new GaussianPyramid(*(steerableBases[i]), iNbLevels, iSigma);
}
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index 40ab3ada777..8ac272e92b5 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -633,7 +633,7 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap,
wFaces.clear();
}
- if (iRenderMonitor && vedges.size()) {
+ if (iRenderMonitor && !vedges.empty()) {
stringstream ss;
ss << "Freestyle: Visibility computations " << (100 * cnt / vedges.size()) << "%";
iRenderMonitor->setInfo(ss.str());
@@ -1084,7 +1084,7 @@ static inline bool crossesProscenium(real proscenium[4], FEdge *fe)
return GeomUtils::intersect2dSeg2dArea(min, max, A, B);
}
-static inline bool insideProscenium(real proscenium[4], const Vec3r &point)
+static inline bool insideProscenium(const real proscenium[4], const Vec3r &point)
{
return !(point[0] < proscenium[0] || point[0] > proscenium[1] || point[1] < proscenium[2] ||
point[1] > proscenium[3]);
@@ -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/freestyle/intern/view_map/ViewMapTesselator.cpp b/source/blender/freestyle/intern/view_map/ViewMapTesselator.cpp
index 8357231a73d..54de3321b80 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapTesselator.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapTesselator.cpp
@@ -25,7 +25,7 @@ namespace Freestyle {
NodeGroup *ViewMapTesselator::Tesselate(ViewMap *iViewMap)
{
- if (0 == iViewMap->ViewEdges().size()) {
+ if (iViewMap->ViewEdges().empty()) {
return NULL;
}
@@ -33,7 +33,7 @@ NodeGroup *ViewMapTesselator::Tesselate(ViewMap *iViewMap)
return Tesselate(viewedges.begin(), viewedges.end());
}
-NodeGroup *ViewMapTesselator::Tesselate(WShape *)
+NodeGroup *ViewMapTesselator::Tesselate(WShape *UNUSED(shape))
{
return NULL;
}
diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
index 96b313d4e01..1702a22c678 100644
--- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp
+++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp
@@ -59,7 +59,7 @@ static bool angle_obtuse(WVertex *v, WFace *f)
// FIXME
// WVvertex is useless but kept for history reasons
-static bool triangle_obtuse(WVertex *, WFace *f)
+static bool triangle_obtuse(WVertex *UNUSED(v), WFace *f)
{
bool b = false;
for (int i = 0; i < 3; i++) {
diff --git a/source/blender/freestyle/intern/winged_edge/WingedEdgeBuilder.cpp b/source/blender/freestyle/intern/winged_edge/WingedEdgeBuilder.cpp
index 620601df5e7..c989d77a730 100644
--- a/source/blender/freestyle/intern/winged_edge/WingedEdgeBuilder.cpp
+++ b/source/blender/freestyle/intern/winged_edge/WingedEdgeBuilder.cpp
@@ -64,11 +64,9 @@ void WingedEdgeBuilder::visitNodeTransform(NodeTransform &tn)
_current_matrix = new_matrix;
}
-void WingedEdgeBuilder::visitNodeTransformAfter(NodeTransform &)
+void WingedEdgeBuilder::visitNodeTransformAfter(NodeTransform &UNUSED(transform))
{
- if (_current_matrix) {
- delete _current_matrix;
- }
+ delete _current_matrix;
if (_matrices_stack.empty()) {
_current_matrix = NULL;
@@ -200,7 +198,7 @@ bool WingedEdgeBuilder::buildWShape(WShape &shape, IndexedFaceSet &ifs)
delete[] new_vertices;
delete[] new_normals;
- if (shape.GetFaceList().size() == 0) { // this may happen due to degenerate triangles
+ if (shape.GetFaceList().empty()) { // this may happen due to degenerate triangles
return false;
}
@@ -219,8 +217,8 @@ bool WingedEdgeBuilder::buildWShape(WShape &shape, IndexedFaceSet &ifs)
if ((*wv)->isBoundary()) {
continue;
}
- if ((*wv)->GetEdges().size() ==
- 0) { // This means that the WVertex has no incoming edges... (12-Sep-2011 T.K.)
+ if ((*wv)->GetEdges().empty()) {
+ // This means that the WVertex has no incoming edges... (12-Sep-2011 T.K.)
continue;
}
normalsSet.clear();
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index 9ce1d3ac2fe..ad29dbe6668 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -27,10 +27,29 @@ set(INC_SYS
)
set(SRC
+ intern/attributes_ref.cc
intern/cpp_types.cc
+ intern/multi_function.cc
+ intern/multi_function_builder.cc
+ intern/multi_function_network.cc
+ intern/multi_function_network_evaluation.cc
+ intern/multi_function_network_optimization.cc
+ FN_array_spans.hh
+ FN_attributes_ref.hh
FN_cpp_type.hh
- FN_cpp_types.hh
+ FN_generic_vector_array.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_network_optimization.hh
+ FN_multi_function_param_type.hh
+ FN_multi_function_params.hh
+ FN_multi_function_signature.hh
+ FN_spans.hh
)
set(LIB
@@ -38,3 +57,20 @@ set(LIB
)
blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/FN_array_spans_test.cc
+ tests/FN_attributes_ref_test.cc
+ tests/FN_cpp_type_test.cc
+ tests/FN_generic_vector_array_test.cc
+ tests/FN_multi_function_network_test.cc
+ tests/FN_multi_function_test.cc
+ tests/FN_spans_test.cc
+ )
+ set (TEST_LIB
+ bf_functions
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_functions_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
diff --git a/source/blender/functions/FN_array_spans.hh b/source/blender/functions/FN_array_spans.hh
new file mode 100644
index 00000000000..5f976711e06
--- /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::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:
+ int64_t virtual_size_;
+ VArraySpanCategory category_;
+
+ union {
+ struct {
+ const T *start;
+ int64_t size;
+ } single_array;
+ struct {
+ const T *const *starts;
+ const int64_t *sizes;
+ } starts_and_sizes;
+ } data_;
+
+ public:
+ bool is_single_array() const
+ {
+ switch (category_) {
+ case VArraySpanCategory::SingleArray:
+ return true;
+ case VArraySpanCategory::StartsAndSizes:
+ return virtual_size_ == 1;
+ }
+ BLI_assert(false);
+ return false;
+ }
+
+ bool is_empty() const
+ {
+ return this->virtual_size_ == 0;
+ }
+
+ int64_t size() const
+ {
+ return this->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->virtual_size_ = 0;
+ this->category_ = VArraySpanCategory::StartsAndSizes;
+ this->data_.starts_and_sizes.starts = nullptr;
+ this->data_.starts_and_sizes.sizes = nullptr;
+ }
+
+ VArraySpan(Span<T> span, int64_t virtual_size)
+ {
+ BLI_assert(virtual_size >= 0);
+ this->virtual_size_ = virtual_size;
+ this->category_ = VArraySpanCategory::SingleArray;
+ this->data_.single_array.start = span.data();
+ this->data_.single_array.size = span.size();
+ }
+
+ VArraySpan(Span<const T *> starts, Span<int64_t> sizes)
+ {
+ BLI_assert(starts.size() == sizes.size());
+ this->virtual_size_ = starts.size();
+ this->category_ = VArraySpanCategory::StartsAndSizes;
+ this->data_.starts_and_sizes.starts = starts.begin();
+ this->data_.starts_and_sizes.sizes = sizes.begin();
+ }
+
+ VSpan<T> operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->virtual_size_);
+ switch (this->category_) {
+ case VArraySpanCategory::SingleArray:
+ return VSpan<T>(Span<T>(this->data_.single_array.start, this->data_.single_array.size));
+ case VArraySpanCategory::StartsAndSizes:
+ return VSpan<T>(Span<T>(this->data_.starts_and_sizes.starts[index],
+ this->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 *type_;
+
+ GVArraySpan() = default;
+
+ public:
+ GVArraySpan(const CPPType &type)
+ {
+ this->type_ = &type;
+ this->virtual_size_ = 0;
+ this->category_ = VArraySpanCategory::StartsAndSizes;
+ this->data_.starts_and_sizes.starts = nullptr;
+ this->data_.starts_and_sizes.sizes = nullptr;
+ }
+
+ GVArraySpan(GSpan array, int64_t virtual_size)
+ {
+ this->type_ = &array.type();
+ this->virtual_size_ = virtual_size;
+ this->category_ = VArraySpanCategory::SingleArray;
+ this->data_.single_array.start = array.data();
+ this->data_.single_array.size = array.size();
+ }
+
+ GVArraySpan(const CPPType &type, Span<const void *> starts, Span<int64_t> sizes)
+ {
+ BLI_assert(starts.size() == sizes.size());
+ this->type_ = &type;
+ this->virtual_size_ = starts.size();
+ this->category_ = VArraySpanCategory::StartsAndSizes;
+ this->data_.starts_and_sizes.starts = (void **)starts.begin();
+ this->data_.starts_and_sizes.sizes = sizes.begin();
+ }
+
+ template<typename T> GVArraySpan(VArraySpan<T> other)
+ {
+ this->type_ = &CPPType::get<T>();
+ memcpy(this, &other, sizeof(VArraySpanBase<void>));
+ }
+
+ const CPPType &type() const
+ {
+ return *this->type_;
+ }
+
+ template<typename T> VArraySpan<T> typed() const
+ {
+ BLI_assert(type_->is<T>());
+ return VArraySpan<T>(*this);
+ }
+
+ GVSpan operator[](int64_t index) const
+ {
+ BLI_assert(index < virtual_size_);
+ switch (category_) {
+ case VArraySpanCategory::SingleArray:
+ return GVSpan(GSpan(*type_, data_.single_array.start, data_.single_array.size));
+ case VArraySpanCategory::StartsAndSizes:
+ return GVSpan(GSpan(
+ *type_, data_.starts_and_sizes.starts[index], data_.starts_and_sizes.sizes[index]));
+ }
+ BLI_assert(false);
+ return GVSpan(*type_);
+ }
+};
+
+} // namespace blender::fn
+
+#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..c694f11b7a7
--- /dev/null
+++ b/source/blender/functions/FN_attributes_ref.hh
@@ -0,0 +1,344 @@
+/*
+ * 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::fn {
+
+class AttributesInfo;
+
+class AttributesInfoBuilder : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> allocator_;
+ VectorSet<std::string> names_;
+ Vector<const CPPType *> types_;
+ Vector<void *> defaults_;
+
+ friend AttributesInfo;
+
+ public:
+ AttributesInfoBuilder() = default;
+ ~AttributesInfoBuilder();
+
+ template<typename T> bool add(StringRef name, const T &default_value)
+ {
+ return this->add(name, CPPType::get<T>(), (const void *)&default_value);
+ }
+
+ bool 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<> allocator_;
+ Map<StringRefNull, int> index_by_name_;
+ Vector<StringRefNull> name_by_index_;
+ Vector<const CPPType *> type_by_index_;
+ Vector<void *> defaults_;
+
+ public:
+ AttributesInfo() = default;
+ AttributesInfo(const AttributesInfoBuilder &builder);
+ ~AttributesInfo();
+
+ int size() const
+ {
+ return name_by_index_.size();
+ }
+
+ IndexRange index_range() const
+ {
+ return name_by_index_.index_range();
+ }
+
+ StringRefNull name_of(int index) const
+ {
+ return name_by_index_[index];
+ }
+
+ int index_of(StringRef name) const
+ {
+ return index_by_name_.lookup_as(name);
+ }
+
+ const void *default_of(int index) const
+ {
+ return defaults_[index];
+ }
+
+ const void *default_of(StringRef name) const
+ {
+ return this->default_of(this->index_of(name));
+ }
+
+ template<typename T> const T &default_of(int index) const
+ {
+ BLI_assert(type_by_index_[index]->is<T>());
+ return *(T *)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(int index) const
+ {
+ return *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 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(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 *info_;
+ Span<void *> buffers_;
+ IndexRange range_;
+
+ friend class AttributesRef;
+
+ public:
+ MutableAttributesRef(const AttributesInfo &info, Span<void *> buffers, int64_t size)
+ : MutableAttributesRef(info, buffers, IndexRange(size))
+ {
+ }
+
+ MutableAttributesRef(const AttributesInfo &info, Span<void *> buffers, IndexRange range)
+ : info_(&info), buffers_(buffers), range_(range)
+ {
+ }
+
+ int64_t size() const
+ {
+ return range_.size();
+ }
+
+ IndexRange index_range() const
+ {
+ return IndexRange(this->size());
+ }
+
+ const AttributesInfo &info() const
+ {
+ return *info_;
+ }
+
+ GMutableSpan get(int index) const
+ {
+ const CPPType &type = info_->type_of(index);
+ void *ptr = POINTER_OFFSET(buffers_[index], type.size() * range_.start());
+ return GMutableSpan(type, ptr, range_.size());
+ }
+
+ GMutableSpan get(StringRef name) const
+ {
+ return this->get(info_->index_of(name));
+ }
+
+ template<typename T> MutableSpan<T> get(int index) const
+ {
+ BLI_assert(info_->type_of(index).is<T>());
+ return MutableSpan<T>((T *)buffers_[index] + range_.start(), range_.size());
+ }
+
+ template<typename T> MutableSpan<T> get(StringRef name) const
+ {
+ return this->get<T>(info_->index_of(name));
+ }
+
+ std::optional<GMutableSpan> try_get(StringRef name, const CPPType &type) const
+ {
+ int index = info_->try_index_of(name, type);
+ if (index == -1) {
+ return {};
+ }
+ else {
+ return this->get(index);
+ }
+ }
+
+ template<typename T> std::optional<MutableSpan<T>> try_get(StringRef name) const
+ {
+ int index = info_->try_index_of(name);
+ if (index == -1) {
+ return {};
+ }
+ else if (info_->type_of(index).is<T>()) {
+ return this->get<T>(index);
+ }
+ else {
+ return {};
+ }
+ }
+
+ MutableAttributesRef slice(IndexRange range) const
+ {
+ return this->slice(range.start(), range.size());
+ }
+
+ MutableAttributesRef slice(int64_t start, int64_t size) const
+ {
+ return MutableAttributesRef(*info_, buffers_, range_.slice(start, size));
+ }
+};
+
+class AttributesRef {
+ private:
+ const AttributesInfo *info_;
+ Span<const void *> buffers_;
+ IndexRange range_;
+
+ public:
+ AttributesRef(const AttributesInfo &info, Span<const void *> buffers, int64_t size)
+ : AttributesRef(info, buffers, IndexRange(size))
+ {
+ }
+
+ AttributesRef(const AttributesInfo &info, Span<const void *> buffers, IndexRange range)
+ : info_(&info), buffers_(buffers), range_(range)
+ {
+ }
+
+ AttributesRef(MutableAttributesRef attributes)
+ : info_(attributes.info_), buffers_(attributes.buffers_), range_(attributes.range_)
+ {
+ }
+
+ int64_t size() const
+ {
+ return range_.size();
+ }
+
+ const AttributesInfo &info() const
+ {
+ return *info_;
+ }
+
+ GSpan get(int index) const
+ {
+ const CPPType &type = info_->type_of(index);
+ const void *ptr = POINTER_OFFSET(buffers_[index], type.size() * range_.start());
+ return GSpan(type, ptr, range_.size());
+ }
+
+ GSpan get(StringRef name) const
+ {
+ return this->get(info_->index_of(name));
+ }
+
+ template<typename T> Span<T> get(int index) const
+ {
+ BLI_assert(info_->type_of(index).is<T>());
+ return Span<T>((T *)buffers_[index] + range_.start(), range_.size());
+ }
+
+ template<typename T> Span<T> get(StringRef name) const
+ {
+ return this->get<T>(info_->index_of(name));
+ }
+
+ std::optional<GSpan> try_get(StringRef name, const CPPType &type) const
+ {
+ int64_t index = info_->try_index_of(name, type);
+ if (index == -1) {
+ return {};
+ }
+ else {
+ return this->get(index);
+ }
+ }
+
+ template<typename T> std::optional<Span<T>> try_get(StringRef name) const
+ {
+ int index = info_->try_index_of(name);
+ if (index == -1) {
+ return {};
+ }
+ else if (info_->type_of(index).is<T>()) {
+ return this->get<T>(index);
+ }
+ else {
+ return {};
+ }
+ }
+
+ AttributesRef slice(IndexRange range) const
+ {
+ return this->slice(range.start(), range.size());
+ }
+
+ AttributesRef slice(int64_t start, int64_t size) const
+ {
+ return AttributesRef(*info_, buffers_, range_.slice(start, size));
+ }
+};
+
+} // namespace blender::fn
+
+#endif /* __FN_ATTRIBUTES_REF_HH__ */
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index d580ce54f1d..531a9073784 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -18,12 +18,12 @@
#define __FN_CPP_TYPE_HH__
/** \file
- * \ingroup functions
+ * \ingroup fn
*
- * 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,
- * 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.
+ * The CPPType class is the core of the runtime-type-system used by the functions system. It can
+ * represent C++ types that are default-constructible, destructible, movable, copyable,
+ * equality comparable and hashable. In the future we might want to make some of these properties
+ * optional.
*
* Every type has a size and an alignment. Every function dealing with C++ types in a generic way,
* has to make sure that alignment rules are followed. The methods provided by a CPPType instance
@@ -38,11 +38,11 @@
* methods come in three variants. Using the construct-default methods as example:
* - construct_default(void *ptr):
* Constructs a single instance of that type at the given pointer.
- * - construct_default_n(void *ptr, uint n):
+ * - construct_default_n(void *ptr, int64_t n):
* Constructs n instances of that type in an array that starts at the given pointer.
- * - construct_default_indices(void *ptr, IndexMask index_mask):
+ * - construct_default_indices(void *ptr, IndexMask mask):
* Constructs multiple instances of that type in an array that starts at the given pointer.
- * Only the indices referenced by `index_mask` will by constructed.
+ * Only the indices referenced by `mask` will by constructed.
*
* In some cases default-construction does nothing (e.g. for trivial types like int). The
* `default_value` method provides some default value anyway that can be copied instead. What the
@@ -66,51 +66,97 @@
* pointers to virtual member functions.
*/
+#include "BLI_hash.hh"
#include "BLI_index_mask.hh"
#include "BLI_math_base.h"
#include "BLI_string_ref.hh"
+#include "BLI_utility_mixins.hh"
-namespace FN {
+namespace blender::fn {
-using blender::IndexMask;
-using blender::StringRef;
-using blender::StringRefNull;
-
-class CPPType {
+class CPPType : NonCopyable, NonMovable {
public:
using ConstructDefaultF = void (*)(void *ptr);
- using ConstructDefaultNF = void (*)(void *ptr, uint n);
- using ConstructDefaultIndicesF = void (*)(void *ptr, IndexMask index_mask);
+ using ConstructDefaultNF = void (*)(void *ptr, int64_t n);
+ using ConstructDefaultIndicesF = void (*)(void *ptr, IndexMask mask);
using DestructF = void (*)(void *ptr);
- using DestructNF = void (*)(void *ptr, uint n);
- using DestructIndicesF = void (*)(void *ptr, IndexMask index_mask);
+ using DestructNF = void (*)(void *ptr, int64_t n);
+ using DestructIndicesF = void (*)(void *ptr, IndexMask mask);
using CopyToInitializedF = void (*)(const void *src, void *dst);
- using CopyToInitializedNF = void (*)(const void *src, void *dst, uint n);
- using CopyToInitializedIndicesF = void (*)(const void *src, void *dst, IndexMask index_mask);
+ using CopyToInitializedNF = void (*)(const void *src, void *dst, int64_t n);
+ using CopyToInitializedIndicesF = void (*)(const void *src, void *dst, IndexMask mask);
using CopyToUninitializedF = void (*)(const void *src, void *dst);
- using CopyToUninitializedNF = void (*)(const void *src, void *dst, uint n);
- using CopyToUninitializedIndicesF = void (*)(const void *src, void *dst, IndexMask index_mask);
+ using CopyToUninitializedNF = void (*)(const void *src, void *dst, int64_t n);
+ using CopyToUninitializedIndicesF = void (*)(const void *src, void *dst, IndexMask mask);
using RelocateToInitializedF = void (*)(void *src, void *dst);
- using RelocateToInitializedNF = void (*)(void *src, void *dst, uint n);
- using RelocateToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask index_mask);
+ using RelocateToInitializedNF = void (*)(void *src, void *dst, int64_t n);
+ using RelocateToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
using RelocateToUninitializedF = void (*)(void *src, void *dst);
- using RelocateToUninitializedNF = void (*)(void *src, void *dst, uint n);
- using RelocateToUninitializedIndicesF = void (*)(void *src, void *dst, IndexMask index_mask);
+ using RelocateToUninitializedNF = void (*)(void *src, void *dst, int64_t n);
+ using RelocateToUninitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask);
+
+ using FillInitializedF = void (*)(const void *value, void *dst, int64_t n);
+ using FillInitializedIndicesF = void (*)(const void *value, void *dst, IndexMask mask);
+
+ using FillUninitializedF = void (*)(const void *value, void *dst, int64_t n);
+ using FillUninitializedIndicesF = void (*)(const void *value, void *dst, IndexMask mask);
+
+ using DebugPrintF = void (*)(const void *value, std::stringstream &ss);
+ using IsEqualF = bool (*)(const void *a, const void *b);
+ using HashF = uint64_t (*)(const void *value);
+
+ private:
+ int64_t size_;
+ int64_t alignment_;
+ uintptr_t alignment_mask_;
+ bool is_trivially_destructible_;
+
+ ConstructDefaultF construct_default_;
+ ConstructDefaultNF construct_default_n_;
+ ConstructDefaultIndicesF construct_default_indices_;
+
+ DestructF destruct_;
+ DestructNF destruct_n_;
+ DestructIndicesF destruct_indices_;
- using FillInitializedF = void (*)(const void *value, void *dst, uint n);
- using FillInitializedIndicesF = void (*)(const void *value, void *dst, IndexMask index_mask);
+ CopyToInitializedF copy_to_initialized_;
+ CopyToInitializedNF copy_to_initialized_n_;
+ CopyToInitializedIndicesF copy_to_initialized_indices_;
- using FillUninitializedF = void (*)(const void *value, void *dst, uint n);
- using FillUninitializedIndicesF = void (*)(const void *value, void *dst, IndexMask index_mask);
+ CopyToUninitializedF copy_to_uninitialized_;
+ CopyToUninitializedNF copy_to_uninitialized_n_;
+ CopyToUninitializedIndicesF copy_to_uninitialized_indices_;
+ RelocateToInitializedF relocate_to_initialized_;
+ RelocateToInitializedNF relocate_to_initialized_n_;
+ RelocateToInitializedIndicesF relocate_to_initialized_indices_;
+
+ RelocateToUninitializedF relocate_to_uninitialized_;
+ RelocateToUninitializedNF relocate_to_uninitialized_n_;
+ RelocateToUninitializedIndicesF relocate_to_uninitialized_indices_;
+
+ FillInitializedF fill_initialized_;
+ FillInitializedIndicesF fill_initialized_indices_;
+
+ FillUninitializedF fill_uninitialized_;
+ FillUninitializedIndicesF fill_uninitialized_indices_;
+
+ DebugPrintF debug_print_;
+ IsEqualF is_equal_;
+ HashF hash_;
+
+ const void *default_value_;
+ std::string name_;
+
+ public:
CPPType(std::string name,
- uint size,
- uint alignment,
+ int64_t size,
+ int64_t alignment,
bool is_trivially_destructible,
ConstructDefaultF construct_default,
ConstructDefaultNF construct_default_n,
@@ -134,46 +180,68 @@ class CPPType {
FillInitializedIndicesF fill_initialized_indices,
FillUninitializedF fill_uninitialized,
FillUninitializedIndicesF fill_uninitialized_indices,
+ DebugPrintF debug_print,
+ IsEqualF is_equal,
+ HashF hash,
const void *default_value)
- : m_size(size),
- m_alignment(alignment),
- m_is_trivially_destructible(is_trivially_destructible),
- m_construct_default(construct_default),
- m_construct_default_n(construct_default_n),
- m_construct_default_indices(construct_default_indices),
- m_destruct(destruct),
- m_destruct_n(destruct_n),
- m_destruct_indices(destruct_indices),
- m_copy_to_initialized(copy_to_initialized),
- m_copy_to_initialized_n(copy_to_initialized_n),
- m_copy_to_initialized_indices(copy_to_initialized_indices),
- m_copy_to_uninitialized(copy_to_uninitialized),
- m_copy_to_uninitialized_n(copy_to_uninitialized_n),
- m_copy_to_uninitialized_indices(copy_to_uninitialized_indices),
- m_relocate_to_initialized(relocate_to_initialized),
- m_relocate_to_initialized_n(relocate_to_initialized_n),
- m_relocate_to_initialized_indices(relocate_to_initialized_indices),
- m_relocate_to_uninitialized(relocate_to_uninitialized),
- m_relocate_to_uninitialized_n(relocate_to_uninitialized_n),
- m_relocate_to_uninitialized_indices(relocate_to_uninitialized_indices),
- m_fill_initialized(fill_initialized),
- m_fill_initialized_indices(fill_initialized_indices),
- m_fill_uninitialized(fill_uninitialized),
- m_fill_uninitialized_indices(fill_uninitialized_indices),
- m_default_value(default_value),
- m_name(name)
- {
- BLI_assert(is_power_of_2_i(m_alignment));
- m_alignment_mask = (uintptr_t)m_alignment - (uintptr_t)1;
+ : size_(size),
+ alignment_(alignment),
+ is_trivially_destructible_(is_trivially_destructible),
+ construct_default_(construct_default),
+ construct_default_n_(construct_default_n),
+ construct_default_indices_(construct_default_indices),
+ destruct_(destruct),
+ destruct_n_(destruct_n),
+ destruct_indices_(destruct_indices),
+ copy_to_initialized_(copy_to_initialized),
+ copy_to_initialized_n_(copy_to_initialized_n),
+ copy_to_initialized_indices_(copy_to_initialized_indices),
+ copy_to_uninitialized_(copy_to_uninitialized),
+ copy_to_uninitialized_n_(copy_to_uninitialized_n),
+ copy_to_uninitialized_indices_(copy_to_uninitialized_indices),
+ relocate_to_initialized_(relocate_to_initialized),
+ relocate_to_initialized_n_(relocate_to_initialized_n),
+ relocate_to_initialized_indices_(relocate_to_initialized_indices),
+ relocate_to_uninitialized_(relocate_to_uninitialized),
+ relocate_to_uninitialized_n_(relocate_to_uninitialized_n),
+ relocate_to_uninitialized_indices_(relocate_to_uninitialized_indices),
+ fill_initialized_(fill_initialized),
+ fill_initialized_indices_(fill_initialized_indices),
+ fill_uninitialized_(fill_uninitialized),
+ fill_uninitialized_indices_(fill_uninitialized_indices),
+ debug_print_(debug_print),
+ is_equal_(is_equal),
+ hash_(hash),
+ default_value_(default_value),
+ name_(name)
+ {
+ BLI_assert(is_power_of_2_i(alignment_));
+ alignment_mask_ = (uintptr_t)alignment_ - (uintptr_t)1;
}
/**
+ * Two types only compare equal when their pointer is equal. No two instances of CPPType for the
+ * same C++ type should be created.
+ */
+ friend bool operator==(const CPPType &a, const CPPType &b)
+ {
+ return &a == &b;
+ }
+
+ friend bool operator!=(const CPPType &a, const CPPType &b)
+ {
+ return !(&a == &b);
+ }
+
+ template<typename T> static const CPPType &get();
+
+ /**
* Returns the name of the type for debugging purposes. This name should not be used as
* identifier.
*/
StringRefNull name() const
{
- return m_name;
+ return name_;
}
/**
@@ -182,9 +250,9 @@ class CPPType {
* C++ equivalent:
* sizeof(T);
*/
- uint size() const
+ int64_t size() const
{
- return m_size;
+ return size_;
}
/**
@@ -193,9 +261,9 @@ class CPPType {
* C++ equivalent:
* alignof(T);
*/
- uint alignment() const
+ int64_t alignment() const
{
- return m_alignment;
+ return alignment_;
}
/**
@@ -203,19 +271,19 @@ class CPPType {
* for optimization purposes.
*
* C++ equivalent:
- * std::is_trivially_destructible<T>::value;
+ * std::is_trivially_destructible_v<T>;
*/
bool is_trivially_destructible() const
{
- return m_is_trivially_destructible;
+ return is_trivially_destructible_;
}
/**
- * Returns true, when the given pointer fullfills the alignment requirement of this type.
+ * Returns true, when the given pointer fulfills the alignment requirement of this type.
*/
bool pointer_has_valid_alignment(const void *ptr) const
{
- return ((uintptr_t)ptr & m_alignment_mask) == 0;
+ return ((uintptr_t)ptr & alignment_mask_) == 0;
}
bool pointer_can_point_to_instance(const void *ptr) const
@@ -235,21 +303,21 @@ class CPPType {
{
BLI_assert(this->pointer_can_point_to_instance(ptr));
- m_construct_default(ptr);
+ construct_default_(ptr);
}
- void construct_default_n(void *ptr, uint n) const
+ void construct_default_n(void *ptr, int64_t n) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(ptr));
- m_construct_default_n(ptr, n);
+ construct_default_n_(ptr, n);
}
- void construct_default_indices(void *ptr, IndexMask index_mask) const
+ void construct_default_indices(void *ptr, IndexMask mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr));
- m_construct_default_indices(ptr, index_mask);
+ construct_default_indices_(ptr, mask);
}
/**
@@ -264,21 +332,26 @@ class CPPType {
{
BLI_assert(this->pointer_can_point_to_instance(ptr));
- m_destruct(ptr);
+ destruct_(ptr);
}
- void destruct_n(void *ptr, uint n) const
+ void destruct_n(void *ptr, int64_t n) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(ptr));
- m_destruct_n(ptr, n);
+ destruct_n_(ptr, n);
}
- void destruct_indices(void *ptr, IndexMask index_mask) const
+ void destruct_indices(void *ptr, IndexMask mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(ptr));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr));
+
+ destruct_indices_(ptr, mask);
+ }
- m_destruct_indices(ptr, index_mask);
+ DestructF destruct_cb() const
+ {
+ return destruct_;
}
/**
@@ -293,25 +366,25 @@ class CPPType {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_copy_to_initialized(src, dst);
+ copy_to_initialized_(src, dst);
}
- void copy_to_initialized_n(const void *src, void *dst, uint n) const
+ void copy_to_initialized_n(const void *src, void *dst, int64_t 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(n == 0 || src != dst);
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
- m_copy_to_initialized_n(src, dst, n);
+ copy_to_initialized_n_(src, dst, n);
}
- void copy_to_initialized_indices(const void *src, void *dst, IndexMask index_mask) const
+ void copy_to_initialized_indices(const void *src, void *dst, IndexMask 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(mask.size() == 0 || src != dst);
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_copy_to_initialized_indices(src, dst, index_mask);
+ copy_to_initialized_indices_(src, dst, mask);
}
/**
@@ -328,25 +401,25 @@ class CPPType {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_copy_to_uninitialized(src, dst);
+ copy_to_uninitialized_(src, dst);
}
- void copy_to_uninitialized_n(const void *src, void *dst, uint n) const
+ void copy_to_uninitialized_n(const void *src, void *dst, int64_t 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(n == 0 || src != dst);
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
- m_copy_to_uninitialized_n(src, dst, n);
+ copy_to_uninitialized_n_(src, dst, n);
}
- void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask index_mask) const
+ void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask 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(mask.size() == 0 || src != dst);
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_copy_to_uninitialized_indices(src, dst, index_mask);
+ copy_to_uninitialized_indices_(src, dst, mask);
}
/**
@@ -363,25 +436,25 @@ class CPPType {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_relocate_to_initialized(src, dst);
+ relocate_to_initialized_(src, dst);
}
- void relocate_to_initialized_n(void *src, void *dst, uint n) const
+ void relocate_to_initialized_n(void *src, void *dst, int64_t 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(n == 0 || src != dst);
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
- m_relocate_to_initialized_n(src, dst, n);
+ relocate_to_initialized_n_(src, dst, n);
}
- void relocate_to_initialized_indices(void *src, void *dst, IndexMask index_mask) const
+ void relocate_to_initialized_indices(void *src, void *dst, IndexMask 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(mask.size() == 0 || src != dst);
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_relocate_to_initialized_indices(src, dst, index_mask);
+ relocate_to_initialized_indices_(src, dst, mask);
}
/**
@@ -398,25 +471,25 @@ class CPPType {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_relocate_to_uninitialized(src, dst);
+ relocate_to_uninitialized_(src, dst);
}
- void relocate_to_uninitialized_n(void *src, void *dst, uint n) const
+ void relocate_to_uninitialized_n(void *src, void *dst, int64_t 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(n == 0 || src != dst);
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
- m_relocate_to_uninitialized_n(src, dst, n);
+ relocate_to_uninitialized_n_(src, dst, n);
}
- void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask index_mask) const
+ void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask 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(mask.size() == 0 || src != dst);
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_relocate_to_uninitialized_indices(src, dst, index_mask);
+ relocate_to_uninitialized_indices_(src, dst, mask);
}
/**
@@ -424,20 +497,20 @@ class CPPType {
*
* Other instances of the same type should live in the array before this method is called.
*/
- void fill_initialized(const void *value, void *dst, uint n) const
+ void fill_initialized(const void *value, void *dst, int64_t n) const
{
- BLI_assert(this->pointer_can_point_to_instance(value));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(value));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
- m_fill_initialized(value, dst, n);
+ fill_initialized_(value, dst, n);
}
- void fill_initialized_indices(const void *value, void *dst, IndexMask index_mask) const
+ void fill_initialized_indices(const void *value, void *dst, IndexMask mask) const
{
- BLI_assert(this->pointer_can_point_to_instance(value));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_fill_initialized_indices(value, dst, index_mask);
+ fill_initialized_indices_(value, dst, mask);
}
/**
@@ -445,20 +518,39 @@ class CPPType {
*
* The array should be uninitialized before this method is called.
*/
- void fill_uninitialized(const void *value, void *dst, uint n) const
+ void fill_uninitialized(const void *value, void *dst, int64_t n) const
{
- BLI_assert(this->pointer_can_point_to_instance(value));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(value));
+ BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst));
+
+ fill_uninitialized_(value, dst, n);
+ }
+
+ void fill_uninitialized_indices(const void *value, void *dst, IndexMask mask) const
+ {
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value));
+ BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_fill_uninitialized(value, dst, n);
+ fill_uninitialized_indices_(value, dst, mask);
}
- void fill_uninitialized_indices(const void *value, void *dst, IndexMask index_mask) const
+ void debug_print(const void *value, std::stringstream &ss) const
{
BLI_assert(this->pointer_can_point_to_instance(value));
- BLI_assert(this->pointer_can_point_to_instance(dst));
+ debug_print_(value, ss);
+ }
- m_fill_uninitialized_indices(value, dst, index_mask);
+ bool is_equal(const void *a, const void *b) const
+ {
+ BLI_assert(this->pointer_can_point_to_instance(a));
+ BLI_assert(this->pointer_can_point_to_instance(b));
+ return is_equal_(a, b);
+ }
+
+ uint64_t hash(const void *value) const
+ {
+ BLI_assert(this->pointer_can_point_to_instance(value));
+ return hash_(value);
}
/**
@@ -467,135 +559,90 @@ class CPPType {
*/
const void *default_value() const
{
- return m_default_value;
+ return default_value_;
}
- /**
- * Two types only compare equal when their pointer is equal. No two instances of CPPType for the
- * same C++ type should be created.
- */
- friend bool operator==(const CPPType &a, const CPPType &b)
+ uint64_t hash() const
{
- return &a == &b;
+ return DefaultHash<const CPPType *>{}(this);
}
- friend bool operator!=(const CPPType &a, const CPPType &b)
+ template<typename T> bool is() const
{
- return !(&a == &b);
+ return this == &CPPType::get<T>();
}
-
- template<typename T> static const CPPType &get();
-
- private:
- uint m_size;
- uint m_alignment;
- uintptr_t m_alignment_mask;
- bool m_is_trivially_destructible;
-
- ConstructDefaultF m_construct_default;
- ConstructDefaultNF m_construct_default_n;
- ConstructDefaultIndicesF m_construct_default_indices;
-
- DestructF m_destruct;
- DestructNF m_destruct_n;
- DestructIndicesF m_destruct_indices;
-
- CopyToInitializedF m_copy_to_initialized;
- CopyToInitializedNF m_copy_to_initialized_n;
- CopyToInitializedIndicesF m_copy_to_initialized_indices;
-
- CopyToUninitializedF m_copy_to_uninitialized;
- CopyToUninitializedNF m_copy_to_uninitialized_n;
- CopyToUninitializedIndicesF m_copy_to_uninitialized_indices;
-
- RelocateToInitializedF m_relocate_to_initialized;
- RelocateToInitializedNF m_relocate_to_initialized_n;
- RelocateToInitializedIndicesF m_relocate_to_initialized_indices;
-
- RelocateToUninitializedF m_relocate_to_uninitialized;
- RelocateToUninitializedNF m_relocate_to_uninitialized_n;
- RelocateToUninitializedIndicesF m_relocate_to_uninitialized_indices;
-
- FillInitializedF m_fill_initialized;
- FillInitializedIndicesF m_fill_initialized_indices;
-
- FillUninitializedF m_fill_uninitialized;
- FillUninitializedIndicesF m_fill_uninitialized_indices;
-
- const void *m_default_value;
- std::string m_name;
};
/* --------------------------------------------------------------------
* Utility for creating CPPType instances for C++ types.
*/
-namespace CPPTypeUtil {
+namespace cpp_type_util {
template<typename T> void construct_default_cb(void *ptr)
{
new (ptr) T;
}
-template<typename T> void construct_default_n_cb(void *ptr, uint n)
+template<typename T> void construct_default_n_cb(void *ptr, int64_t n)
{
blender::default_construct_n((T *)ptr, n);
}
-template<typename T> void construct_default_indices_cb(void *ptr, IndexMask index_mask)
+template<typename T> void construct_default_indices_cb(void *ptr, IndexMask mask)
{
- index_mask.foreach_index([&](uint i) { new ((T *)ptr + i) T; });
+ mask.foreach_index([&](int64_t i) { new ((T *)ptr + i) T; });
}
template<typename T> void destruct_cb(void *ptr)
{
((T *)ptr)->~T();
}
-template<typename T> void destruct_n_cb(void *ptr, uint n)
+template<typename T> void destruct_n_cb(void *ptr, int64_t n)
{
blender::destruct_n((T *)ptr, n);
}
-template<typename T> void destruct_indices_cb(void *ptr, IndexMask index_mask)
+template<typename T> void destruct_indices_cb(void *ptr, IndexMask mask)
{
T *ptr_ = (T *)ptr;
- index_mask.foreach_index([&](uint i) { ptr_[i].~T(); });
+ mask.foreach_index([&](int64_t i) { ptr_[i].~T(); });
}
template<typename T> void copy_to_initialized_cb(const void *src, void *dst)
{
*(T *)dst = *(T *)src;
}
-template<typename T> void copy_to_initialized_n_cb(const void *src, void *dst, uint n)
+template<typename T> void copy_to_initialized_n_cb(const void *src, void *dst, int64_t n)
{
const T *src_ = (const T *)src;
T *dst_ = (T *)dst;
- for (uint i = 0; i < n; i++) {
+ for (int64_t i = 0; i < n; i++) {
dst_[i] = src_[i];
}
}
template<typename T>
-void copy_to_initialized_indices_cb(const void *src, void *dst, IndexMask index_mask)
+void copy_to_initialized_indices_cb(const void *src, void *dst, IndexMask mask)
{
const T *src_ = (const T *)src;
T *dst_ = (T *)dst;
- index_mask.foreach_index([&](uint i) { dst_[i] = src_[i]; });
+ mask.foreach_index([&](int64_t i) { dst_[i] = src_[i]; });
}
template<typename T> void copy_to_uninitialized_cb(const void *src, void *dst)
{
blender::uninitialized_copy_n((T *)src, 1, (T *)dst);
}
-template<typename T> void copy_to_uninitialized_n_cb(const void *src, void *dst, uint n)
+template<typename T> void copy_to_uninitialized_n_cb(const void *src, void *dst, int64_t n)
{
blender::uninitialized_copy_n((T *)src, n, (T *)dst);
}
template<typename T>
-void copy_to_uninitialized_indices_cb(const void *src, void *dst, IndexMask index_mask)
+void copy_to_uninitialized_indices_cb(const void *src, void *dst, IndexMask mask)
{
const T *src_ = (const T *)src;
T *dst_ = (T *)dst;
- index_mask.foreach_index([&](uint i) { new (dst_ + i) T(src_[i]); });
+ mask.foreach_index([&](int64_t i) { new (dst_ + i) T(src_[i]); });
}
template<typename T> void relocate_to_initialized_cb(void *src, void *dst)
@@ -606,17 +653,16 @@ template<typename T> void relocate_to_initialized_cb(void *src, void *dst)
*dst_ = std::move(*src_);
src_->~T();
}
-template<typename T> void relocate_to_initialized_n_cb(void *src, void *dst, uint n)
+template<typename T> void relocate_to_initialized_n_cb(void *src, void *dst, int64_t n)
{
blender::initialized_relocate_n((T *)src, n, (T *)dst);
}
-template<typename T>
-void relocate_to_initialized_indices_cb(void *src, void *dst, IndexMask index_mask)
+template<typename T> void relocate_to_initialized_indices_cb(void *src, void *dst, IndexMask mask)
{
T *src_ = (T *)src;
T *dst_ = (T *)dst;
- index_mask.foreach_index([&](uint i) {
+ mask.foreach_index([&](int64_t i) {
dst_[i] = std::move(src_[i]);
src_[i].~T();
});
@@ -630,68 +676,86 @@ template<typename T> void relocate_to_uninitialized_cb(void *src, void *dst)
new (dst_) T(std::move(*src_));
src_->~T();
}
-template<typename T> void relocate_to_uninitialized_n_cb(void *src, void *dst, uint n)
+template<typename T> void relocate_to_uninitialized_n_cb(void *src, void *dst, int64_t n)
{
blender::uninitialized_relocate_n((T *)src, n, (T *)dst);
}
template<typename T>
-void relocate_to_uninitialized_indices_cb(void *src, void *dst, IndexMask index_mask)
+void relocate_to_uninitialized_indices_cb(void *src, void *dst, IndexMask mask)
{
T *src_ = (T *)src;
T *dst_ = (T *)dst;
- index_mask.foreach_index([&](uint i) {
+ mask.foreach_index([&](int64_t i) {
new (dst_ + i) T(std::move(src_[i]));
src_[i].~T();
});
}
-template<typename T> void fill_initialized_cb(const void *value, void *dst, uint n)
+template<typename T> void fill_initialized_cb(const void *value, void *dst, int64_t n)
{
const T &value_ = *(const T *)value;
T *dst_ = (T *)dst;
- for (uint i = 0; i < n; i++) {
+ for (int64_t i = 0; i < n; i++) {
dst_[i] = value_;
}
}
-template<typename T>
-void fill_initialized_indices_cb(const void *value, void *dst, IndexMask index_mask)
+template<typename T> void fill_initialized_indices_cb(const void *value, void *dst, IndexMask mask)
{
const T &value_ = *(const T *)value;
T *dst_ = (T *)dst;
- index_mask.foreach_index([&](uint i) { dst_[i] = value_; });
+ mask.foreach_index([&](int64_t i) { dst_[i] = value_; });
}
-template<typename T> void fill_uninitialized_cb(const void *value, void *dst, uint n)
+template<typename T> void fill_uninitialized_cb(const void *value, void *dst, int64_t n)
{
const T &value_ = *(const T *)value;
T *dst_ = (T *)dst;
- for (uint i = 0; i < n; i++) {
+ for (int64_t i = 0; i < n; i++) {
new (dst_ + i) T(value_);
}
}
template<typename T>
-void fill_uninitialized_indices_cb(const void *value, void *dst, IndexMask index_mask)
+void fill_uninitialized_indices_cb(const void *value, void *dst, IndexMask mask)
{
const T &value_ = *(const T *)value;
T *dst_ = (T *)dst;
- index_mask.foreach_index([&](uint i) { new (dst_ + i) T(value_); });
+ mask.foreach_index([&](int64_t i) { new (dst_ + i) T(value_); });
+}
+
+template<typename T> void debug_print_cb(const void *value, std::stringstream &ss)
+{
+ const T &value_ = *(const T *)value;
+ ss << value_;
+}
+
+template<typename T> bool is_equal_cb(const void *a, const void *b)
+{
+ const T &a_ = *(T *)a;
+ const T &b_ = *(T *)b;
+ return a_ == b_;
+}
+
+template<typename T> uint64_t hash_cb(const void *value)
+{
+ const T &value_ = *(const T *)value;
+ return DefaultHash<T>{}(value_);
}
-} // namespace CPPTypeUtil
+} // namespace cpp_type_util
template<typename T>
-static std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &default_value)
+inline std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &default_value)
{
- using namespace CPPTypeUtil;
+ using namespace cpp_type_util;
const CPPType *type = new CPPType(name,
sizeof(T),
alignof(T),
- std::is_trivially_destructible<T>::value,
+ std::is_trivially_destructible_v<T>,
construct_default_cb<T>,
construct_default_n_cb<T>,
construct_default_indices_cb<T>,
@@ -714,20 +778,22 @@ static std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &d
fill_initialized_indices_cb<T>,
fill_uninitialized_cb<T>,
fill_uninitialized_indices_cb<T>,
+ debug_print_cb<T>,
+ is_equal_cb<T>,
+ hash_cb<T>,
(const void *)&default_value);
return std::unique_ptr<const CPPType>(type);
}
-} // namespace FN
+} // namespace blender::fn
#define MAKE_CPP_TYPE(IDENTIFIER, TYPE_NAME) \
- static TYPE_NAME default_value_##IDENTIFIER; \
- static std::unique_ptr<const FN::CPPType> CPPTYPE_##IDENTIFIER##_owner = \
- FN::create_cpp_type<TYPE_NAME>(STRINGIFY(IDENTIFIER), default_value_##IDENTIFIER); \
- const FN::CPPType &CPPType_##IDENTIFIER = *CPPTYPE_##IDENTIFIER##_owner; \
- template<> const FN::CPPType &FN::CPPType::get<TYPE_NAME>() \
+ template<> const blender::fn::CPPType &blender::fn::CPPType::get<TYPE_NAME>() \
{ \
- return CPPType_##IDENTIFIER; \
+ static TYPE_NAME default_value; \
+ static std::unique_ptr<const CPPType> cpp_type = blender::fn::create_cpp_type<TYPE_NAME>( \
+ STRINGIFY(IDENTIFIER), default_value); \
+ return *cpp_type; \
}
#endif /* __FN_CPP_TYPE_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..ee67db000e5
--- /dev/null
+++ b/source/blender/functions/FN_generic_vector_array.hh
@@ -0,0 +1,206 @@
+/*
+ * 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::fn {
+
+template<typename T> class GVectorArrayRef;
+
+class GVectorArray : NonCopyable, NonMovable {
+ private:
+ const CPPType &type_;
+ int64_t element_size_;
+ Array<void *, 1> starts_;
+ Array<int64_t, 1> lengths_;
+ Array<int64_t, 1> capacities_;
+ LinearAllocator<> allocator_;
+
+ template<typename T> friend class GVectorArrayRef;
+
+ public:
+ GVectorArray() = delete;
+
+ GVectorArray(const CPPType &type, int64_t array_size)
+ : type_(type),
+ element_size_(type.size()),
+ starts_(array_size),
+ lengths_(array_size),
+ capacities_(array_size)
+ {
+ starts_.as_mutable_span().fill(nullptr);
+ lengths_.as_mutable_span().fill(0);
+ capacities_.as_mutable_span().fill(0);
+ }
+
+ ~GVectorArray()
+ {
+ if (type_.is_trivially_destructible()) {
+ return;
+ }
+
+ for (int64_t i : starts_.index_range()) {
+ type_.destruct_n(starts_[i], lengths_[i]);
+ }
+ }
+
+ operator GVArraySpan() const
+ {
+ return GVArraySpan(type_, starts_, lengths_);
+ }
+
+ bool is_empty() const
+ {
+ return starts_.size() == 0;
+ }
+
+ int64_t size() const
+ {
+ return starts_.size();
+ }
+
+ const CPPType &type() const
+ {
+ return type_;
+ }
+
+ Span<const void *> starts() const
+ {
+ return starts_;
+ }
+
+ Span<int64_t> lengths() const
+ {
+ return lengths_;
+ }
+
+ void append(int64_t index, const void *src)
+ {
+ int64_t old_length = lengths_[index];
+ if (old_length == capacities_[index]) {
+ this->grow_at_least_one(index);
+ }
+
+ void *dst = POINTER_OFFSET(starts_[index], element_size_ * old_length);
+ type_.copy_to_uninitialized(src, dst);
+ lengths_[index]++;
+ }
+
+ void extend(int64_t index, GVSpan span)
+ {
+ BLI_assert(type_ == span.type());
+ for (int64_t i = 0; i < span.size(); i++) {
+ this->append(index, span[i]);
+ }
+ }
+
+ void extend(IndexMask mask, GVArraySpan array_span)
+ {
+ BLI_assert(type_ == array_span.type());
+ BLI_assert(mask.min_array_size() <= array_span.size());
+ for (int64_t i : mask) {
+ this->extend(i, array_span[i]);
+ }
+ }
+
+ GMutableSpan operator[](int64_t index)
+ {
+ BLI_assert(index < starts_.size());
+ return GMutableSpan(type_, starts_[index], lengths_[index]);
+ }
+ template<typename T> GVectorArrayRef<T> typed()
+ {
+ return GVectorArrayRef<T>(*this);
+ }
+
+ private:
+ void grow_at_least_one(int64_t index)
+ {
+ BLI_assert(lengths_[index] == capacities_[index]);
+ int64_t new_capacity = lengths_[index] * 2 + 1;
+
+ void *new_buffer = allocator_.allocate(element_size_ * new_capacity, type_.alignment());
+ type_.relocate_to_uninitialized_n(starts_[index], new_buffer, lengths_[index]);
+
+ starts_[index] = new_buffer;
+ capacities_[index] = new_capacity;
+ }
+};
+
+template<typename T> class GVectorArrayRef {
+ private:
+ GVectorArray *vector_array_;
+
+ public:
+ GVectorArrayRef(GVectorArray &vector_array) : vector_array_(&vector_array)
+ {
+ BLI_assert(vector_array.type_.is<T>());
+ }
+
+ void append(int64_t index, const T &value)
+ {
+ vector_array_->append(index, &value);
+ }
+
+ void extend(int64_t index, Span<T> values)
+ {
+ vector_array_->extend(index, values);
+ }
+
+ void extend(int64_t index, VSpan<T> values)
+ {
+ vector_array_->extend(index, GVSpan(values));
+ }
+
+ MutableSpan<T> operator[](int64_t index)
+ {
+ BLI_assert(index < vector_array_->starts_.size());
+ return MutableSpan<T>((T *)vector_array_->starts_[index], vector_array_->lengths_[index]);
+ }
+
+ int64_t size() const
+ {
+ return vector_array_->size();
+ }
+
+ bool is_empty() const
+ {
+ return vector_array_->is_empty();
+ }
+};
+
+} // namespace blender::fn
+
+#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..eaddcee7964
--- /dev/null
+++ b/source/blender/functions/FN_multi_function.hh
@@ -0,0 +1,128 @@
+/*
+ * 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 "BLI_hash.hh"
+
+#include "FN_multi_function_context.hh"
+#include "FN_multi_function_params.hh"
+
+namespace blender::fn {
+
+class MultiFunction {
+ private:
+ MFSignature signature_;
+
+ public:
+ virtual ~MultiFunction()
+ {
+ }
+
+ virtual void call(IndexMask mask, MFParams params, MFContext context) const = 0;
+
+ virtual uint64_t hash() const
+ {
+ return DefaultHash<const MultiFunction *>{}(this);
+ }
+
+ virtual bool equals(const MultiFunction &UNUSED(other)) const
+ {
+ return false;
+ }
+
+ int param_amount() const
+ {
+ return signature_.param_types.size();
+ }
+
+ IndexRange param_indices() const
+ {
+ return signature_.param_types.index_range();
+ }
+
+ MFParamType param_type(int param_index) const
+ {
+ return signature_.param_types[param_index];
+ }
+
+ StringRefNull param_name(int param_index) const
+ {
+ return signature_.param_names[param_index];
+ }
+
+ StringRefNull name() const
+ {
+ return signature_.function_name;
+ }
+
+ bool depends_on_context() const
+ {
+ return signature_.depends_on_context;
+ }
+
+ const MFSignature &signature() const
+ {
+ return signature_;
+ }
+
+ protected:
+ MFSignatureBuilder get_builder(std::string function_name)
+ {
+ signature_.function_name = std::move(function_name);
+ return MFSignatureBuilder(signature_);
+ }
+};
+
+inline MFParamsBuilder::MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size)
+ : MFParamsBuilder(fn.signature(), min_array_size)
+{
+}
+
+extern const MultiFunction &dummy_multi_function;
+
+} // namespace blender::fn
+
+#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..95e216558e7
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_builder.hh
@@ -0,0 +1,318 @@
+/*
+ * 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::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 function_;
+
+ public:
+ CustomMF_SI_SO(StringRef name, FunctionT function) : 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([&](int 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);
+ 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 function_;
+
+ public:
+ CustomMF_SI_SI_SO(StringRef name, FunctionT function) : 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([&](int 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);
+ 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 function_;
+
+ public:
+ CustomMF_SI_SI_SI_SO(StringRef name, FunctionT function) : 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(
+ [&](int 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);
+ 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 function_;
+
+ public:
+ CustomMF_SM(StringRef name, FunctionT function) : 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([&](int i) { element_fn(mut1[i]); });
+ };
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ MutableSpan<Mut1> mut1 = params.single_mutable<Mut1>(0);
+ function_(mask, mut1);
+ }
+};
+
+/**
+ * Generates a multi-function that converts between two types.
+ */
+template<typename From, typename To> class CustomMF_Convert : public MultiFunction {
+ public:
+ CustomMF_Convert()
+ {
+ std::string name = CPPType::get<From>().name() + " to " + CPPType::get<To>().name();
+ MFSignatureBuilder signature = this->get_builder(std::move(name));
+ signature.single_input<From>("Input");
+ signature.single_output<To>("Output");
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ VSpan<From> inputs = params.readonly_single_input<From>(0);
+ MutableSpan<To> outputs = params.uninitialized_single_output<To>(1);
+
+ for (int64_t i : mask) {
+ new ((void *)&outputs[i]) To(inputs[i]);
+ }
+ }
+};
+
+/**
+ * A multi-function that outputs the same value every time. The value is not owned by an instance
+ * of this function. The caller is responsible for destructing and freeing the value.
+ */
+class CustomMF_GenericConstant : public MultiFunction {
+ private:
+ const CPPType &type_;
+ const void *value_;
+
+ template<typename T> friend class CustomMF_Constant;
+
+ public:
+ CustomMF_GenericConstant(const CPPType &type, const void *value);
+ void call(IndexMask mask, MFParams params, MFContext context) const override;
+ uint64_t hash() const override;
+ bool equals(const MultiFunction &other) const override;
+};
+
+/**
+ * A multi-function that outputs the same array every time. The array is not owned by in instance
+ * of this function. The caller is responsible for destructing and freeing the values.
+ */
+class CustomMF_GenericConstantArray : public MultiFunction {
+ private:
+ GSpan array_;
+
+ public:
+ CustomMF_GenericConstantArray(GSpan array);
+ void call(IndexMask mask, MFParams params, MFContext context) const override;
+};
+
+/**
+ * Generates a multi-function that outputs a constant value.
+ */
+template<typename T> class CustomMF_Constant : public MultiFunction {
+ private:
+ T value_;
+
+ public:
+ template<typename U> CustomMF_Constant(U &&value) : value_(std::forward<U>(value))
+ {
+ MFSignatureBuilder signature = this->get_builder("Constant");
+ std::stringstream ss;
+ ss << 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([&](int i) { new (&output[i]) T(value_); });
+ }
+
+ uint64_t hash() const override
+ {
+ return DefaultHash<T>{}(value_);
+ }
+
+ bool equals(const MultiFunction &other) const override
+ {
+ const CustomMF_Constant *other1 = dynamic_cast<const CustomMF_Constant *>(&other);
+ if (other1 != nullptr) {
+ return value_ == other1->value_;
+ }
+ const CustomMF_GenericConstant *other2 = dynamic_cast<const CustomMF_GenericConstant *>(
+ &other);
+ if (other2 != nullptr) {
+ const CPPType &type = CPPType::get<T>();
+ if (type == other2->type_) {
+ return type.is_equal((const void *)&value_, other2->value_);
+ }
+ }
+ return false;
+ }
+};
+
+class CustomMF_DefaultOutput : public MultiFunction {
+ private:
+ int output_amount_;
+
+ public:
+ CustomMF_DefaultOutput(StringRef name,
+ Span<MFDataType> input_types,
+ Span<MFDataType> output_types);
+ void call(IndexMask mask, MFParams params, MFContext context) const override;
+};
+
+} // namespace blender::fn
+
+#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..8492fd86742
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_context.hh
@@ -0,0 +1,70 @@
+/*
+ * 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"
+
+#include "BLI_map.hh"
+
+namespace blender::fn {
+
+class MFContext;
+
+class MFContextBuilder {
+ private:
+ Map<std::string, const void *> global_contexts_;
+
+ friend MFContext;
+
+ public:
+ template<typename T> void add_global_context(std::string name, const T *context)
+ {
+ global_contexts_.add_new(std::move(name), (const void *)context);
+ }
+};
+
+class MFContext {
+ private:
+ MFContextBuilder &builder_;
+
+ public:
+ MFContext(MFContextBuilder &builder) : builder_(builder)
+ {
+ }
+
+ template<typename T> const T *get_global_context(StringRef name) const
+ {
+ const void *context = builder_.global_contexts_.lookup_default_as(name, nullptr);
+ /* TODO: Implement type checking. */
+ return (const T *)context;
+ }
+};
+
+} // namespace blender::fn
+
+#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..23a1c0e5680
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_data_type.hh
@@ -0,0 +1,130 @@
+/*
+ * 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::fn {
+
+class MFDataType {
+ public:
+ enum Category {
+ Single,
+ Vector,
+ };
+
+ private:
+ Category category_;
+ const CPPType *type_;
+
+ MFDataType(Category category, const CPPType &type) : category_(category), 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 category_ == Single;
+ }
+
+ bool is_vector() const
+ {
+ return category_ == Vector;
+ }
+
+ Category category() const
+ {
+ return category_;
+ }
+
+ const CPPType &single_type() const
+ {
+ BLI_assert(this->is_single());
+ return *type_;
+ }
+
+ const CPPType &vector_base_type() const
+ {
+ BLI_assert(this->is_vector());
+ return *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 (category_) {
+ case Single:
+ return type_->name();
+ case Vector:
+ return type_->name() + " Vector";
+ }
+ BLI_assert(false);
+ return "";
+ }
+
+ uint64_t hash() const
+ {
+ return DefaultHash<CPPType>{}(*type_) + (uint64_t)category_;
+ }
+};
+
+inline bool operator==(const MFDataType &a, const MFDataType &b)
+{
+ return a.category_ == b.category_ && a.type_ == b.type_;
+}
+
+inline bool operator!=(const MFDataType &a, const MFDataType &b)
+{
+ return !(a == b);
+}
+
+} // namespace blender::fn
+
+#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..20f8fb2ee43
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_network.hh
@@ -0,0 +1,539 @@
+/*
+ * 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::fn {
+
+class MFNode;
+class MFFunctionNode;
+class MFDummyNode;
+class MFSocket;
+class MFInputSocket;
+class MFOutputSocket;
+class MFNetwork;
+
+class MFNode : NonCopyable, NonMovable {
+ protected:
+ MFNetwork *network_;
+ Span<MFInputSocket *> inputs_;
+ Span<MFOutputSocket *> outputs_;
+ bool is_dummy_;
+ int id_;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ int 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(int index);
+ const MFInputSocket &input(int index) const;
+
+ MFOutputSocket &output(int index);
+ const MFOutputSocket &output(int index) const;
+
+ Span<MFInputSocket *> inputs();
+ Span<const MFInputSocket *> inputs() const;
+
+ Span<MFOutputSocket *> outputs();
+ Span<const MFOutputSocket *> outputs() const;
+
+ bool has_unlinked_inputs() const;
+
+ private:
+ void destruct_sockets();
+};
+
+class MFFunctionNode : public MFNode {
+ private:
+ const MultiFunction *function_;
+ Span<int> input_param_indices_;
+ Span<int> output_param_indices_;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ const MultiFunction &function() const;
+
+ const MFInputSocket &input_for_param(int param_index) const;
+ const MFOutputSocket &output_for_param(int param_index) const;
+};
+
+class MFDummyNode : public MFNode {
+ private:
+ StringRefNull name_;
+ MutableSpan<StringRefNull> input_names_;
+ MutableSpan<StringRefNull> output_names_;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ Span<StringRefNull> input_names() const;
+ Span<StringRefNull> output_names() const;
+};
+
+class MFSocket : NonCopyable, NonMovable {
+ protected:
+ MFNode *node_;
+ bool is_output_;
+ int index_;
+ MFDataType data_type_;
+ int id_;
+ StringRefNull name_;
+
+ friend MFNetwork;
+
+ public:
+ StringRefNull name() const;
+
+ int id() const;
+ int index() 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 *origin_;
+
+ friend MFNetwork;
+
+ public:
+ MFOutputSocket *origin();
+ const MFOutputSocket *origin() const;
+};
+
+class MFOutputSocket : public MFSocket {
+ private:
+ Vector<MFInputSocket *, 1> targets_;
+
+ friend MFNetwork;
+
+ public:
+ Span<MFInputSocket *> targets();
+ Span<const MFInputSocket *> targets() const;
+};
+
+class MFNetwork : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> allocator_;
+
+ VectorSet<MFFunctionNode *> function_nodes_;
+ VectorSet<MFDummyNode *> dummy_nodes_;
+
+ Vector<MFNode *> node_or_null_by_id_;
+ Vector<MFSocket *> 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);
+ void remove(Span<MFNode *> nodes);
+
+ int socket_id_amount() const;
+ int node_id_amount() const;
+
+ Span<MFDummyNode *> dummy_nodes();
+ Span<MFFunctionNode *> function_nodes();
+
+ MFNode *node_or_null_by_id(int id);
+ const MFNode *node_or_null_by_id(int id) const;
+
+ MFSocket *socket_or_null_by_id(int id);
+ const MFSocket *socket_or_null_by_id(int id) const;
+
+ void find_dependencies(Span<const MFInputSocket *> sockets,
+ VectorSet<const MFOutputSocket *> &r_dummy_sockets,
+ VectorSet<const MFInputSocket *> &r_unlinked_inputs) const;
+
+ bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const;
+
+ std::string to_dot(Span<const MFNode *> marked_nodes = {}) const;
+};
+
+/* --------------------------------------------------------------------
+ * MFNode inline methods.
+ */
+
+inline StringRefNull MFNode::name() const
+{
+ if (is_dummy_) {
+ return this->as_dummy().name();
+ }
+ else {
+ return this->as_function().name();
+ }
+}
+
+inline int MFNode::id() const
+{
+ return id_;
+}
+
+inline MFNetwork &MFNode::network()
+{
+ return *network_;
+}
+
+inline const MFNetwork &MFNode::network() const
+{
+ return *network_;
+}
+
+inline bool MFNode::is_dummy() const
+{
+ return is_dummy_;
+}
+
+inline bool MFNode::is_function() const
+{
+ return !is_dummy_;
+}
+
+inline MFDummyNode &MFNode::as_dummy()
+{
+ BLI_assert(is_dummy_);
+ return *(MFDummyNode *)this;
+}
+
+inline const MFDummyNode &MFNode::as_dummy() const
+{
+ BLI_assert(is_dummy_);
+ return *(const MFDummyNode *)this;
+}
+
+inline MFFunctionNode &MFNode::as_function()
+{
+ BLI_assert(!is_dummy_);
+ return *(MFFunctionNode *)this;
+}
+
+inline const MFFunctionNode &MFNode::as_function() const
+{
+ BLI_assert(!is_dummy_);
+ return *(const MFFunctionNode *)this;
+}
+
+inline MFInputSocket &MFNode::input(int index)
+{
+ return *inputs_[index];
+}
+
+inline const MFInputSocket &MFNode::input(int index) const
+{
+ return *inputs_[index];
+}
+
+inline MFOutputSocket &MFNode::output(int index)
+{
+ return *outputs_[index];
+}
+
+inline const MFOutputSocket &MFNode::output(int index) const
+{
+ return *outputs_[index];
+}
+
+inline Span<MFInputSocket *> MFNode::inputs()
+{
+ return inputs_;
+}
+
+inline Span<const MFInputSocket *> MFNode::inputs() const
+{
+ return inputs_;
+}
+
+inline Span<MFOutputSocket *> MFNode::outputs()
+{
+ return outputs_;
+}
+
+inline Span<const MFOutputSocket *> MFNode::outputs() const
+{
+ return outputs_;
+}
+
+inline bool MFNode::has_unlinked_inputs() const
+{
+ for (const MFInputSocket *socket : inputs_) {
+ if (socket->origin() == nullptr) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* --------------------------------------------------------------------
+ * MFFunctionNode inline methods.
+ */
+
+inline StringRefNull MFFunctionNode::name() const
+{
+ return function_->name();
+}
+
+inline const MultiFunction &MFFunctionNode::function() const
+{
+ return *function_;
+}
+
+inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const
+{
+ return this->input(input_param_indices_.first_index(param_index));
+}
+
+inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const
+{
+ return this->output(output_param_indices_.first_index(param_index));
+}
+
+/* --------------------------------------------------------------------
+ * MFDummyNode inline methods.
+ */
+
+inline StringRefNull MFDummyNode::name() const
+{
+ return name_;
+}
+
+inline Span<StringRefNull> MFDummyNode::input_names() const
+{
+ return input_names_;
+}
+
+inline Span<StringRefNull> MFDummyNode::output_names() const
+{
+ return output_names_;
+}
+
+/* --------------------------------------------------------------------
+ * MFSocket inline methods.
+ */
+
+inline StringRefNull MFSocket::name() const
+{
+ return name_;
+}
+
+inline int MFSocket::id() const
+{
+ return id_;
+}
+
+inline int MFSocket::index() const
+{
+ return index_;
+}
+
+inline const MFDataType &MFSocket::data_type() const
+{
+ return data_type_;
+}
+
+inline MFNode &MFSocket::node()
+{
+ return *node_;
+}
+
+inline const MFNode &MFSocket::node() const
+{
+ return *node_;
+}
+
+inline bool MFSocket::is_input() const
+{
+ return !is_output_;
+}
+
+inline bool MFSocket::is_output() const
+{
+ return 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 origin_;
+}
+
+inline const MFOutputSocket *MFInputSocket::origin() const
+{
+ return origin_;
+}
+
+/* --------------------------------------------------------------------
+ * MFOutputSocket inline methods.
+ */
+
+inline Span<MFInputSocket *> MFOutputSocket::targets()
+{
+ return targets_;
+}
+
+inline Span<const MFInputSocket *> MFOutputSocket::targets() const
+{
+ return targets_;
+}
+
+/* --------------------------------------------------------------------
+ * MFNetwork inline methods.
+ */
+
+inline Span<MFDummyNode *> MFNetwork::dummy_nodes()
+{
+ return dummy_nodes_;
+}
+
+inline Span<MFFunctionNode *> MFNetwork::function_nodes()
+{
+ return function_nodes_;
+}
+
+inline MFNode *MFNetwork::node_or_null_by_id(int id)
+{
+ return node_or_null_by_id_[id];
+}
+
+inline const MFNode *MFNetwork::node_or_null_by_id(int id) const
+{
+ return node_or_null_by_id_[id];
+}
+
+inline MFSocket *MFNetwork::socket_or_null_by_id(int id)
+{
+ return socket_or_null_by_id_[id];
+}
+
+inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const
+{
+ return socket_or_null_by_id_[id];
+}
+
+inline int MFNetwork::socket_id_amount() const
+{
+ return socket_or_null_by_id_.size();
+}
+
+inline int MFNetwork::node_id_amount() const
+{
+ return node_or_null_by_id_.size();
+}
+
+} // namespace blender::fn
+
+#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..11606869ca2
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_network_evaluation.hh
@@ -0,0 +1,64 @@
+/*
+ * 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::fn {
+
+class MFNetworkEvaluationStorage;
+
+class MFNetworkEvaluator : public MultiFunction {
+ private:
+ Vector<const MFOutputSocket *> inputs_;
+ Vector<const MFInputSocket *> 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 blender::fn
+
+#endif /* __FN_MULTI_FUNCTION_NETWORK_EVALUATION_HH__ */
diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh
new file mode 100644
index 00000000000..3cbabd72c2a
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_network_optimization.hh
@@ -0,0 +1,32 @@
+/*
+ * 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_OPTIMIZATION_HH__
+#define __FN_MULTI_FUNCTION_NETWORK_OPTIMIZATION_HH__
+
+#include "FN_multi_function_network.hh"
+
+#include "BLI_resource_collector.hh"
+
+namespace blender::fn::mf_network_optimization {
+
+void dead_node_removal(MFNetwork &network);
+void constant_folding(MFNetwork &network, ResourceCollector &resources);
+void common_subnetwork_elimination(MFNetwork &network);
+
+} // namespace blender::fn::mf_network_optimization
+
+#endif /* __FN_MULTI_FUNCTION_NETWORK_OPTIMIZATION_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..7c16b8cdf10
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_param_type.hh
@@ -0,0 +1,168 @@
+/*
+ * 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::fn {
+
+class MFParamType {
+ public:
+ enum InterfaceType {
+ Input,
+ Output,
+ Mutable,
+ };
+
+ enum Category {
+ SingleInput,
+ VectorInput,
+ SingleOutput,
+ VectorOutput,
+ SingleMutable,
+ VectorMutable,
+ };
+
+ private:
+ InterfaceType interface_type_;
+ MFDataType data_type_;
+
+ public:
+ MFParamType(InterfaceType interface_type, MFDataType data_type)
+ : interface_type_(interface_type), 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 data_type_;
+ }
+
+ InterfaceType interface_type() const
+ {
+ return interface_type_;
+ }
+
+ Category category() const
+ {
+ switch (data_type_.category()) {
+ case MFDataType::Single: {
+ switch (interface_type_) {
+ case Input:
+ return SingleInput;
+ case Output:
+ return SingleOutput;
+ case Mutable:
+ return SingleMutable;
+ }
+ break;
+ }
+ case MFDataType::Vector: {
+ switch (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(interface_type_, Input, Mutable);
+ }
+
+ bool is_output_or_mutable() const
+ {
+ return ELEM(interface_type_, Output, Mutable);
+ }
+
+ bool is_output() const
+ {
+ return interface_type_ == Output;
+ }
+
+ 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.interface_type_ == b.interface_type_ && a.data_type_ == b.data_type_;
+}
+
+inline bool operator!=(const MFParamType &a, const MFParamType &b)
+{
+ return !(a == b);
+}
+
+} // namespace blender::fn
+
+#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..ac4dca33cf0
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_params.hh
@@ -0,0 +1,251 @@
+/*
+ * 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::fn {
+
+class MFParamsBuilder {
+ private:
+ const MFSignature *signature_;
+ int64_t min_array_size_;
+ Vector<GVSpan> virtual_spans_;
+ Vector<GMutableSpan> mutable_spans_;
+ Vector<GVArraySpan> virtual_array_spans_;
+ Vector<GVectorArray *> vector_arrays_;
+
+ friend class MFParams;
+
+ public:
+ MFParamsBuilder(const MFSignature &signature, int64_t min_array_size)
+ : signature_(&signature), min_array_size_(min_array_size)
+ {
+ }
+
+ MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size);
+
+ template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
+ {
+ this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_),
+ expected_name);
+ }
+ void add_readonly_single_input(GVSpan ref, StringRef expected_name = "")
+ {
+ this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()), expected_name);
+ BLI_assert(ref.size() >= min_array_size_);
+ virtual_spans_.append(ref);
+ }
+
+ void add_readonly_vector_input(GVArraySpan ref, StringRef expected_name = "")
+ {
+ this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name);
+ BLI_assert(ref.size() >= min_array_size_);
+ virtual_array_spans_.append(ref);
+ }
+
+ template<typename T> void add_uninitialized_single_output(T *value, StringRef expected_name = "")
+ {
+ this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1),
+ expected_name);
+ }
+ void add_uninitialized_single_output(GMutableSpan ref, StringRef expected_name = "")
+ {
+ this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type()), expected_name);
+ BLI_assert(ref.size() >= min_array_size_);
+ mutable_spans_.append(ref);
+ }
+
+ void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "")
+ {
+ this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type()),
+ expected_name);
+ BLI_assert(vector_array.size() >= min_array_size_);
+ vector_arrays_.append(&vector_array);
+ }
+
+ void add_single_mutable(GMutableSpan ref, StringRef expected_name = "")
+ {
+ this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type()), expected_name);
+ BLI_assert(ref.size() >= min_array_size_);
+ mutable_spans_.append(ref);
+ }
+
+ void add_vector_mutable(GVectorArray &vector_array, StringRef expected_name = "")
+ {
+ this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type()),
+ expected_name);
+ BLI_assert(vector_array.size() >= min_array_size_);
+ vector_arrays_.append(&vector_array);
+ }
+
+ GMutableSpan computed_array(int param_index)
+ {
+ BLI_assert(ELEM(signature_->param_types[param_index].category(),
+ MFParamType::SingleOutput,
+ MFParamType::SingleMutable));
+ int data_index = signature_->data_index(param_index);
+ return mutable_spans_[data_index];
+ }
+
+ GVectorArray &computed_vector_array(int param_index)
+ {
+ BLI_assert(ELEM(signature_->param_types[param_index].category(),
+ MFParamType::VectorOutput,
+ MFParamType::VectorMutable));
+ int data_index = signature_->data_index(param_index);
+ return *vector_arrays_[data_index];
+ }
+
+ private:
+ void assert_current_param_type(MFParamType param_type, StringRef expected_name = "")
+ {
+ UNUSED_VARS_NDEBUG(param_type, expected_name);
+#ifdef DEBUG
+ int param_index = this->current_param_index();
+
+ if (expected_name != "") {
+ StringRef actual_name = signature_->param_names[param_index];
+ BLI_assert(actual_name == expected_name);
+ }
+
+ MFParamType expected_type = signature_->param_types[param_index];
+ BLI_assert(expected_type == param_type);
+#endif
+ }
+
+ int current_param_index() const
+ {
+ return virtual_spans_.size() + mutable_spans_.size() + virtual_array_spans_.size() +
+ vector_arrays_.size();
+ }
+};
+
+class MFParams {
+ private:
+ MFParamsBuilder *builder_;
+
+ public:
+ MFParams(MFParamsBuilder &builder) : builder_(&builder)
+ {
+ }
+
+ template<typename T> VSpan<T> readonly_single_input(int param_index, StringRef name = "")
+ {
+ return this->readonly_single_input(param_index, name).typed<T>();
+ }
+ GVSpan readonly_single_input(int param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::SingleInput);
+ int data_index = builder_->signature_->data_index(param_index);
+ return builder_->virtual_spans_[data_index];
+ }
+
+ template<typename T>
+ MutableSpan<T> uninitialized_single_output(int param_index, StringRef name = "")
+ {
+ return this->uninitialized_single_output(param_index, name).typed<T>();
+ }
+ GMutableSpan uninitialized_single_output(int param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::SingleOutput);
+ int data_index = builder_->signature_->data_index(param_index);
+ return builder_->mutable_spans_[data_index];
+ }
+
+ template<typename T> VArraySpan<T> readonly_vector_input(int param_index, StringRef name = "")
+ {
+ return this->readonly_vector_input(param_index, name).typed<T>();
+ }
+ GVArraySpan readonly_vector_input(int param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::VectorInput);
+ int data_index = builder_->signature_->data_index(param_index);
+ return builder_->virtual_array_spans_[data_index];
+ }
+
+ template<typename T> GVectorArrayRef<T> vector_output(int param_index, StringRef name = "")
+ {
+ return this->vector_output(param_index, name).typed<T>();
+ }
+ GVectorArray &vector_output(int param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::VectorOutput);
+ int data_index = builder_->signature_->data_index(param_index);
+ return *builder_->vector_arrays_[data_index];
+ }
+
+ template<typename T> MutableSpan<T> single_mutable(int param_index, StringRef name = "")
+ {
+ return this->single_mutable(param_index, name).typed<T>();
+ }
+ GMutableSpan single_mutable(int param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::SingleMutable);
+ int data_index = builder_->signature_->data_index(param_index);
+ return builder_->mutable_spans_[data_index];
+ }
+
+ template<typename T> GVectorArrayRef<T> vector_mutable(int param_index, StringRef name = "")
+ {
+ return this->vector_mutable(param_index, name).typed<T>();
+ }
+ GVectorArray &vector_mutable(int param_index, StringRef name = "")
+ {
+ this->assert_correct_param(param_index, name, MFParamType::VectorMutable);
+ int data_index = builder_->signature_->data_index(param_index);
+ return *builder_->vector_arrays_[data_index];
+ }
+
+ private:
+ void assert_correct_param(int param_index, StringRef name, MFParamType param_type)
+ {
+ UNUSED_VARS_NDEBUG(param_index, name, param_type);
+#ifdef DEBUG
+ BLI_assert(builder_->signature_->param_types[param_index] == param_type);
+ if (name.size() > 0) {
+ BLI_assert(builder_->signature_->param_names[param_index] == name);
+ }
+#endif
+ }
+
+ void assert_correct_param(int param_index, StringRef name, MFParamType::Category category)
+ {
+ UNUSED_VARS_NDEBUG(param_index, name, category);
+#ifdef DEBUG
+ BLI_assert(builder_->signature_->param_types[param_index].category() == category);
+ if (name.size() > 0) {
+ BLI_assert(builder_->signature_->param_names[param_index] == name);
+ }
+#endif
+ }
+};
+
+} // namespace blender::fn
+
+#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..3c09d1c961c
--- /dev/null
+++ b/source/blender/functions/FN_multi_function_signature.hh
@@ -0,0 +1,173 @@
+/*
+ * 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::fn {
+
+struct MFSignature {
+ std::string function_name;
+ Vector<std::string> param_names;
+ Vector<MFParamType> param_types;
+ Vector<int> param_data_indices;
+ bool depends_on_context = false;
+
+ int data_index(int param_index) const
+ {
+ return param_data_indices[param_index];
+ }
+};
+
+class MFSignatureBuilder {
+ private:
+ MFSignature &data_;
+ int span_count_ = 0;
+ int virtual_span_count_ = 0;
+ int virtual_array_span_count_ = 0;
+ int vector_array_count_ = 0;
+
+ public:
+ MFSignatureBuilder(MFSignature &data) : 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 Parameter 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)
+ {
+ data_.param_names.append(name);
+ data_.param_types.append(MFParamType(MFParamType::Input, data_type));
+
+ switch (data_type.category()) {
+ case MFDataType::Single:
+ data_.param_data_indices.append(virtual_span_count_++);
+ break;
+ case MFDataType::Vector:
+ data_.param_data_indices.append(virtual_array_span_count_++);
+ break;
+ }
+ }
+
+ /* Output Parameter 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)
+ {
+ data_.param_names.append(name);
+ data_.param_types.append(MFParamType(MFParamType::Output, data_type));
+
+ switch (data_type.category()) {
+ case MFDataType::Single:
+ data_.param_data_indices.append(span_count_++);
+ break;
+ case MFDataType::Vector:
+ data_.param_data_indices.append(vector_array_count_++);
+ break;
+ }
+ }
+
+ /* Mutable Parameter 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)
+ {
+ data_.param_names.append(name);
+ data_.param_types.append(MFParamType(MFParamType::Mutable, data_type));
+
+ switch (data_type.category()) {
+ case MFDataType::Single:
+ data_.param_data_indices.append(span_count_++);
+ break;
+ case MFDataType::Vector:
+ data_.param_data_indices.append(vector_array_count_++);
+ break;
+ }
+ }
+
+ /* Context */
+
+ /** This indicates that the function accesses the context. This disables optimizations that
+ * depend on the fact that the function always performers the same operation. */
+ void depends_on_context()
+ {
+ data_.depends_on_context = true;
+ }
+};
+
+} // namespace blender::fn
+
+#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..62fa7c8ed4b
--- /dev/null
+++ b/source/blender/functions/FN_spans.hh
@@ -0,0 +1,456 @@
+/*
+ * 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::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 *type_;
+ const void *data_;
+ int64_t size_;
+
+ public:
+ GSpan(const CPPType &type, const void *buffer, int64_t size)
+ : type_(&type), data_(buffer), size_(size)
+ {
+ BLI_assert(size >= 0);
+ 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 *type_;
+ }
+
+ bool is_empty() const
+ {
+ return size_ == 0;
+ }
+
+ int64_t size() const
+ {
+ return size_;
+ }
+
+ const void *data() const
+ {
+ return data_;
+ }
+
+ const void *operator[](int64_t index) const
+ {
+ BLI_assert(index < size_);
+ return POINTER_OFFSET(data_, type_->size() * index);
+ }
+
+ template<typename T> Span<T> typed() const
+ {
+ BLI_assert(type_->is<T>());
+ return Span<T>((const T *)data_, 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 *type_;
+ void *data_;
+ int64_t size_;
+
+ public:
+ GMutableSpan(const CPPType &type, void *buffer, int64_t size)
+ : type_(&type), data_(buffer), size_(size)
+ {
+ BLI_assert(size >= 0);
+ 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(*type_, data_, size_);
+ }
+
+ const CPPType &type() const
+ {
+ return *type_;
+ }
+
+ bool is_empty() const
+ {
+ return size_ == 0;
+ }
+
+ int64_t size() const
+ {
+ return size_;
+ }
+
+ void *data()
+ {
+ return data_;
+ }
+
+ void *operator[](int64_t index)
+ {
+ BLI_assert(index < size_);
+ return POINTER_OFFSET(data_, type_->size() * index);
+ }
+
+ template<typename T> MutableSpan<T> typed()
+ {
+ BLI_assert(type_->is<T>());
+ return MutableSpan<T>((T *)data_, size_);
+ }
+};
+
+enum class VSpanCategory {
+ Single,
+ FullArray,
+ FullPointerArray,
+};
+
+template<typename T> struct VSpanBase {
+ protected:
+ int64_t virtual_size_;
+ VSpanCategory category_;
+ union {
+ struct {
+ const T *data;
+ } single;
+ struct {
+ const T *data;
+ } full_array;
+ struct {
+ const T *const *data;
+ } full_pointer_array;
+ } data_;
+
+ public:
+ bool is_single_element() const
+ {
+ switch (category_) {
+ case VSpanCategory::Single:
+ return true;
+ case VSpanCategory::FullArray:
+ return virtual_size_ == 1;
+ case VSpanCategory::FullPointerArray:
+ return virtual_size_ == 1;
+ }
+ BLI_assert(false);
+ return false;
+ }
+
+ bool is_full_array() const
+ {
+ switch (category_) {
+ case VSpanCategory::Single:
+ return virtual_size_ == 1;
+ case VSpanCategory::FullArray:
+ return true;
+ case VSpanCategory::FullPointerArray:
+ return virtual_size_ <= 1;
+ }
+ BLI_assert(false);
+ return false;
+ }
+
+ bool is_empty() const
+ {
+ return this->virtual_size_ == 0;
+ }
+
+ int64_t size() const
+ {
+ return this->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->virtual_size_ = 0;
+ this->category_ = VSpanCategory::FullArray;
+ this->data_.full_array.data = nullptr;
+ }
+
+ VSpan(Span<T> values)
+ {
+ this->virtual_size_ = values.size();
+ this->category_ = VSpanCategory::FullArray;
+ this->data_.full_array.data = values.begin();
+ }
+
+ VSpan(MutableSpan<T> values) : VSpan(Span<T>(values))
+ {
+ }
+
+ VSpan(Span<const T *> values)
+ {
+ this->virtual_size_ = values.size();
+ this->category_ = VSpanCategory::FullPointerArray;
+ this->data_.full_pointer_array.data = values.begin();
+ }
+
+ static VSpan FromSingle(const T *value, int64_t virtual_size)
+ {
+ VSpan ref;
+ ref.virtual_size_ = virtual_size;
+ ref.category_ = VSpanCategory::Single;
+ ref.data_.single.data = value;
+ return ref;
+ }
+
+ const T &operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->virtual_size_);
+ switch (this->category_) {
+ case VSpanCategory::Single:
+ return *this->data_.single.data;
+ case VSpanCategory::FullArray:
+ return this->data_.full_array.data[index];
+ case VSpanCategory::FullPointerArray:
+ return *this->data_.full_pointer_array.data[index];
+ }
+ BLI_assert(false);
+ return *this->data_.single.data;
+ }
+
+ const T &as_single_element() const
+ {
+ BLI_assert(this->is_single_element());
+ return (*this)[0];
+ }
+
+ Span<T> as_full_array() const
+ {
+ BLI_assert(this->is_full_array());
+ if (this->virtual_size_ == 0) {
+ return Span<T>();
+ }
+ const T *data = &(*this)[0];
+ return Span<T>(data, this->virtual_size_);
+ }
+};
+
+/**
+ * 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 *type_;
+
+ GVSpan() = default;
+
+ public:
+ GVSpan(const CPPType &type)
+ {
+ this->type_ = &type;
+ this->virtual_size_ = 0;
+ this->category_ = VSpanCategory::FullArray;
+ this->data_.full_array.data = nullptr;
+ }
+
+ GVSpan(GSpan values)
+ {
+ this->type_ = &values.type();
+ this->virtual_size_ = values.size();
+ this->category_ = VSpanCategory::FullArray;
+ this->data_.full_array.data = values.data();
+ }
+
+ GVSpan(GMutableSpan values) : GVSpan(GSpan(values))
+ {
+ }
+
+ template<typename T> GVSpan(const VSpanBase<T> &values)
+ {
+ this->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, int64_t virtual_size)
+ {
+ GVSpan ref;
+ ref.type_ = &type;
+ ref.virtual_size_ = virtual_size;
+ ref.category_ = VSpanCategory::Single;
+ ref.data_.single.data = value;
+ return ref;
+ }
+
+ static GVSpan FromSingleWithMaxSize(const CPPType &type, const void *value)
+ {
+ return GVSpan::FromSingle(type, value, INT64_MAX);
+ }
+
+ static GVSpan FromDefault(const CPPType &type)
+ {
+ return GVSpan::FromSingleWithMaxSize(type, type.default_value());
+ }
+
+ static GVSpan FromFullPointerArray(const CPPType &type, const void *const *values, int64_t size)
+ {
+ GVSpan ref;
+ ref.type_ = &type;
+ ref.virtual_size_ = size;
+ ref.category_ = VSpanCategory::FullPointerArray;
+ ref.data_.full_pointer_array.data = values;
+ return ref;
+ }
+
+ const CPPType &type() const
+ {
+ return *this->type_;
+ }
+
+ const void *operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->virtual_size_);
+ switch (this->category_) {
+ case VSpanCategory::Single:
+ return this->data_.single.data;
+ case VSpanCategory::FullArray:
+ return POINTER_OFFSET(this->data_.full_array.data, index * type_->size());
+ case VSpanCategory::FullPointerArray:
+ return this->data_.full_pointer_array.data[index];
+ }
+ BLI_assert(false);
+ return this->data_.single.data;
+ }
+
+ template<typename T> VSpan<T> typed() const
+ {
+ BLI_assert(type_->is<T>());
+ return VSpan<T>(*this);
+ }
+
+ const void *as_single_element() const
+ {
+ BLI_assert(this->is_single_element());
+ return (*this)[0];
+ }
+
+ GSpan as_full_array() const
+ {
+ BLI_assert(this->is_full_array());
+ if (this->virtual_size_ == 0) {
+ return GSpan(*this->type_);
+ }
+ const void *data = (*this)[0];
+ return GSpan(*this->type_, data, this->virtual_size_);
+ }
+
+ void materialize_to_uninitialized(void *dst) const
+ {
+ this->materialize_to_uninitialized(IndexRange(virtual_size_), dst);
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, void *dst) const
+ {
+ BLI_assert(this->size() >= mask.min_array_size());
+
+ int64_t element_size = type_->size();
+ for (int64_t i : mask) {
+ type_->copy_to_uninitialized((*this)[i], POINTER_OFFSET(dst, element_size * i));
+ }
+ }
+};
+
+} // namespace blender::fn
+
+#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..4686e217911
--- /dev/null
+++ b/source/blender/functions/intern/attributes_ref.cc
@@ -0,0 +1,75 @@
+/*
+ * 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::fn {
+
+AttributesInfoBuilder::~AttributesInfoBuilder()
+{
+ for (int i : defaults_.index_range()) {
+ types_[i]->destruct(defaults_[i]);
+ }
+}
+
+bool AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value)
+{
+ if (names_.add_as(name)) {
+ types_.append(&type);
+
+ if (default_value == nullptr) {
+ default_value = type.default_value();
+ }
+ void *dst = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(default_value, dst);
+ defaults_.append(dst);
+ return true;
+ }
+ else {
+ const CPPType &stored_type = *types_[names_.index_of_as(name)];
+ if (stored_type != type) {
+ std::cout << "Warning: Tried to add an attribute twice with different types (" << name
+ << ": " << stored_type.name() << ", " << type.name() << ").\n";
+ }
+ return false;
+ }
+}
+
+AttributesInfo::AttributesInfo(const AttributesInfoBuilder &builder)
+{
+ for (int i : builder.types_.index_range()) {
+ StringRefNull name = allocator_.copy_string(builder.names_[i]);
+ const CPPType &type = *builder.types_[i];
+ const void *default_value = builder.defaults_[i];
+
+ index_by_name_.add_new(name, i);
+ name_by_index_.append(name);
+ type_by_index_.append(&type);
+
+ void *dst = allocator_.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(default_value, dst);
+ defaults_.append(dst);
+ }
+}
+
+AttributesInfo::~AttributesInfo()
+{
+ for (int i : defaults_.index_range()) {
+ type_by_index_[i]->destruct(defaults_[i]);
+ }
+}
+
+} // namespace blender::fn
diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc
index 6e548d65192..3bf4683d815 100644
--- a/source/blender/functions/intern/cpp_types.cc
+++ b/source/blender/functions/intern/cpp_types.cc
@@ -14,14 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "FN_cpp_types.hh"
+#include "FN_cpp_type.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
-namespace FN {
+namespace blender::fn {
MAKE_CPP_TYPE(bool, bool)
@@ -38,4 +38,4 @@ MAKE_CPP_TYPE(Color4b, blender::Color4b)
MAKE_CPP_TYPE(string, std::string)
-} // namespace FN
+} // namespace blender::fn
diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.h b/source/blender/functions/intern/multi_function.cc
index c6a3c399b66..a41c6bdf3cd 100644
--- a/source/blender/io/alembic/intern/abc_writer_nurbs.h
+++ b/source/blender/functions/intern/multi_function.cc
@@ -14,29 +14,25 @@
* 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::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 blender::fn
diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc
new file mode 100644
index 00000000000..c9e8b88ba03
--- /dev/null
+++ b/source/blender/functions/intern/multi_function_builder.cc
@@ -0,0 +1,119 @@
+/*
+ * 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_multi_function_builder.hh"
+
+#include "BLI_hash.hh"
+
+namespace blender::fn {
+
+CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const void *value)
+ : type_(type), value_(value)
+{
+ MFSignatureBuilder signature = this->get_builder("Constant " + type.name());
+ std::stringstream ss;
+ type.debug_print(value, ss);
+ signature.single_output(ss.str(), type);
+}
+
+void CustomMF_GenericConstant::call(IndexMask mask,
+ MFParams params,
+ MFContext UNUSED(context)) const
+{
+ GMutableSpan output = params.uninitialized_single_output(0);
+ type_.fill_uninitialized_indices(value_, output.data(), mask);
+}
+
+uint64_t CustomMF_GenericConstant::hash() const
+{
+ return type_.hash(value_);
+}
+
+bool CustomMF_GenericConstant::equals(const MultiFunction &other) const
+{
+ const CustomMF_GenericConstant *_other = dynamic_cast<const CustomMF_GenericConstant *>(&other);
+ if (_other == nullptr) {
+ return false;
+ }
+ if (type_ != _other->type_) {
+ return false;
+ }
+ return type_.is_equal(value_, _other->value_);
+}
+
+static std::string gspan_to_string(GSpan array)
+{
+ std::stringstream ss;
+ ss << "[";
+ const int64_t max_amount = 5;
+ for (int64_t i : IndexRange(std::min(max_amount, array.size()))) {
+ array.type().debug_print(array[i], ss);
+ ss << ", ";
+ }
+ if (max_amount < array.size()) {
+ ss << "...";
+ }
+ ss << "]";
+ return ss.str();
+}
+
+CustomMF_GenericConstantArray::CustomMF_GenericConstantArray(GSpan array) : array_(array)
+{
+ const CPPType &type = array.type();
+ MFSignatureBuilder signature = this->get_builder("Constant " + type.name() + " Vector");
+ signature.vector_output(gspan_to_string(array), type);
+}
+
+void CustomMF_GenericConstantArray::call(IndexMask mask,
+ MFParams params,
+ MFContext UNUSED(context)) const
+{
+ GVectorArray &vectors = params.vector_output(0);
+ for (int64_t i : mask) {
+ vectors.extend(i, array_);
+ }
+}
+
+CustomMF_DefaultOutput::CustomMF_DefaultOutput(StringRef name,
+ Span<MFDataType> input_types,
+ Span<MFDataType> output_types)
+ : output_amount_(output_types.size())
+{
+ MFSignatureBuilder signature = this->get_builder(name);
+ for (MFDataType data_type : input_types) {
+ signature.input("Input", data_type);
+ }
+ for (MFDataType data_type : output_types) {
+ signature.output("Output", data_type);
+ }
+}
+void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const
+{
+ for (int param_index : this->param_indices()) {
+ MFParamType param_type = this->param_type(param_index);
+ if (!param_type.is_output()) {
+ continue;
+ }
+
+ if (param_type.data_type().is_single()) {
+ GMutableSpan span = params.uninitialized_single_output(param_index);
+ const CPPType &type = span.type();
+ type.fill_uninitialized_indices(type.default_value(), span.data(), mask);
+ }
+ }
+}
+
+} // namespace blender::fn
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..cd3c38dd09f
--- /dev/null
+++ b/source/blender/functions/intern/multi_function_network.cc
@@ -0,0 +1,330 @@
+/*
+ * 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 "BLI_stack.hh"
+
+#include "FN_multi_function_network.hh"
+
+namespace blender::fn {
+
+MFNetwork::~MFNetwork()
+{
+ for (MFFunctionNode *node : function_nodes_) {
+ node->destruct_sockets();
+ node->~MFFunctionNode();
+ }
+ for (MFDummyNode *node : dummy_nodes_) {
+ node->destruct_sockets();
+ node->~MFDummyNode();
+ }
+}
+
+void MFNode::destruct_sockets()
+{
+ for (MFInputSocket *socket : inputs_) {
+ socket->~MFInputSocket();
+ }
+ for (MFOutputSocket *socket : 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<int, 16> input_param_indices, output_param_indices;
+
+ for (int 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 = *allocator_.construct<MFFunctionNode>();
+ function_nodes_.add_new(&node);
+
+ node.network_ = this;
+ node.is_dummy_ = false;
+ node.id_ = node_or_null_by_id_.append_and_get_index(&node);
+ node.function_ = &function;
+ node.input_param_indices_ = allocator_.construct_array_copy<int>(input_param_indices);
+ node.output_param_indices_ = allocator_.construct_array_copy<int>(output_param_indices);
+
+ node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
+ input_param_indices.size());
+ node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
+ output_param_indices.size());
+
+ for (int i : input_param_indices.index_range()) {
+ int param_index = input_param_indices[i];
+ MFParamType param = function.param_type(param_index);
+ BLI_assert(param.is_input_or_mutable());
+
+ MFInputSocket &socket = *node.inputs_[i];
+ socket.data_type_ = param.data_type();
+ socket.node_ = &node;
+ socket.index_ = i;
+ socket.is_output_ = false;
+ socket.name_ = function.param_name(param_index);
+ socket.origin_ = nullptr;
+ socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
+ }
+
+ for (int i : output_param_indices.index_range()) {
+ int param_index = output_param_indices[i];
+ MFParamType param = function.param_type(param_index);
+ BLI_assert(param.is_output_or_mutable());
+
+ MFOutputSocket &socket = *node.outputs_[i];
+ socket.data_type_ = param.data_type();
+ socket.node_ = &node;
+ socket.index_ = i;
+ socket.is_output_ = true;
+ socket.name_ = function.param_name(param_index);
+ socket.id_ = 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 = *allocator_.construct<MFDummyNode>();
+ dummy_nodes_.add_new(&node);
+
+ node.network_ = this;
+ node.is_dummy_ = true;
+ node.name_ = allocator_.copy_string(name);
+ node.id_ = node_or_null_by_id_.append_and_get_index(&node);
+
+ node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
+ input_types.size());
+ node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
+ output_types.size());
+
+ node.input_names_ = allocator_.allocate_array<StringRefNull>(input_types.size());
+ node.output_names_ = allocator_.allocate_array<StringRefNull>(output_types.size());
+
+ for (int i : input_types.index_range()) {
+ MFInputSocket &socket = *node.inputs_[i];
+ socket.data_type_ = input_types[i];
+ socket.node_ = &node;
+ socket.index_ = i;
+ socket.is_output_ = false;
+ socket.name_ = allocator_.copy_string(input_names[i]);
+ socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
+ node.input_names_[i] = socket.name_;
+ }
+
+ for (int i : output_types.index_range()) {
+ MFOutputSocket &socket = *node.outputs_[i];
+ socket.data_type_ = output_types[i];
+ socket.node_ = &node;
+ socket.index_ = i;
+ socket.is_output_ = true;
+ socket.name_ = allocator_.copy_string(output_names[i]);
+ socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
+ node.output_names_[i] = socket.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.origin_ == nullptr);
+ BLI_assert(from.node_->network_ == to.node_->network_);
+ BLI_assert(from.data_type_ == to.data_type_);
+ from.targets_.append(&to);
+ to.origin_ = &from;
+}
+
+MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type)
+{
+ return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0);
+}
+
+MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type)
+{
+ return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0);
+}
+
+void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output)
+{
+ BLI_assert(&old_output != &new_output);
+ BLI_assert(old_output.data_type_ == new_output.data_type_);
+ for (MFInputSocket *input : old_output.targets()) {
+ input->origin_ = &new_output;
+ }
+ new_output.targets_.extend(old_output.targets_);
+ old_output.targets_.clear();
+}
+
+void MFNetwork::remove(MFNode &node)
+{
+ for (MFInputSocket *socket : node.inputs_) {
+ if (socket->origin_ != nullptr) {
+ socket->origin_->targets_.remove_first_occurrence_and_reorder(socket);
+ }
+ socket_or_null_by_id_[socket->id_] = nullptr;
+ }
+ for (MFOutputSocket *socket : node.outputs_) {
+ for (MFInputSocket *other : socket->targets_) {
+ other->origin_ = nullptr;
+ }
+ socket_or_null_by_id_[socket->id_] = nullptr;
+ }
+ node.destruct_sockets();
+ if (node.is_dummy()) {
+ MFDummyNode &dummy_node = node.as_dummy();
+ dummy_node.~MFDummyNode();
+ dummy_nodes_.remove_contained(&dummy_node);
+ }
+ else {
+ MFFunctionNode &function_node = node.as_function();
+ function_node.~MFFunctionNode();
+ function_nodes_.remove_contained(&function_node);
+ }
+ node_or_null_by_id_[node.id_] = nullptr;
+}
+
+void MFNetwork::remove(Span<MFNode *> nodes)
+{
+ for (MFNode *node : nodes) {
+ this->remove(*node);
+ }
+}
+
+void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets,
+ VectorSet<const MFOutputSocket *> &r_dummy_sockets,
+ VectorSet<const MFInputSocket *> &r_unlinked_inputs) const
+{
+ Set<const MFNode *> visited_nodes;
+ Stack<const MFInputSocket *> sockets_to_check;
+ sockets_to_check.push_multiple(sockets);
+
+ while (!sockets_to_check.is_empty()) {
+ const MFInputSocket &socket = *sockets_to_check.pop();
+ const MFOutputSocket *origin_socket = socket.origin();
+ if (origin_socket == nullptr) {
+ r_unlinked_inputs.add(&socket);
+ continue;
+ }
+
+ const MFNode &origin_node = origin_socket->node();
+
+ if (origin_node.is_dummy()) {
+ r_dummy_sockets.add(origin_socket);
+ continue;
+ }
+
+ if (visited_nodes.add(&origin_node)) {
+ sockets_to_check.push_multiple(origin_node.inputs());
+ }
+ }
+}
+
+bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const
+{
+ VectorSet<const MFOutputSocket *> dummy_sockets;
+ VectorSet<const MFInputSocket *> unlinked_inputs;
+ this->find_dependencies(sockets, dummy_sockets, unlinked_inputs);
+ return dummy_sockets.size() + unlinked_inputs.size() > 0;
+}
+
+std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) 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(function_nodes_.as_span());
+ all_nodes.extend(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->inputs_) {
+ input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")");
+ }
+ for (const MFOutputSocket *socket : node->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 MFDummyNode *node : dummy_nodes_) {
+ dot_nodes.lookup(node).node().set_background_color("#77EE77");
+ }
+ for (const MFNode *node : marked_nodes) {
+ dot_nodes.lookup(node).node().set_background_color("#7777EE");
+ }
+
+ for (const MFNode *to_node : all_nodes) {
+ dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node);
+
+ for (const MFInputSocket *to_socket : to_node->inputs_) {
+ const MFOutputSocket *from_socket = to_socket->origin_;
+ if (from_socket != nullptr) {
+ const MFNode *from_node = from_socket->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_));
+ }
+ }
+ }
+
+ return digraph.to_dot_string();
+}
+
+} // namespace blender::fn
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..25e983d2eeb
--- /dev/null
+++ b/source/blender/functions/intern/multi_function_network_evaluation.cc
@@ -0,0 +1,1060 @@
+/*
+ * 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::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<> allocator_;
+ IndexMask mask_;
+ Array<Value *> value_per_output_id_;
+ int64_t min_array_size_;
+
+ public:
+ MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount);
+ ~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)
+ : inputs_(std::move(inputs)), outputs_(std::move(outputs))
+{
+ BLI_assert(outputs_.size() > 0);
+ MFSignatureBuilder signature = this->get_builder("Function Tree");
+
+ for (const MFOutputSocket *socket : inputs_) {
+ BLI_assert(socket->node().is_dummy());
+
+ MFDataType type = socket->data_type();
+ switch (type.category()) {
+ case MFDataType::Single:
+ signature.single_input(socket->name(), type.single_type());
+ break;
+ case MFDataType::Vector:
+ signature.vector_input(socket->name(), type.vector_base_type());
+ break;
+ }
+ }
+
+ for (const MFInputSocket *socket : outputs_) {
+ BLI_assert(socket->node().is_dummy());
+
+ MFDataType type = socket->data_type();
+ switch (type.category()) {
+ case MFDataType::Single:
+ signature.single_output(socket->name(), type.single_type());
+ break;
+ case MFDataType::Vector:
+ signature.vector_output(socket->name(), type.vector_base_type());
+ break;
+ }
+ }
+}
+
+void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const
+{
+ if (mask.size() == 0) {
+ return;
+ }
+
+ const MFNetwork &network = outputs_[0]->node().network();
+ Storage storage(mask, network.socket_id_amount());
+
+ 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 (int input_index : inputs_.index_range()) {
+ int param_index = input_index + 0;
+ const MFOutputSocket &socket = *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 (int output_index : outputs_.index_range()) {
+ int param_index = output_index + inputs_.size();
+ const MFInputSocket &socket = *outputs_[output_index];
+ const MFOutputSocket &origin = *socket.origin();
+
+ if (origin.node().is_dummy()) {
+ BLI_assert(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 : outputs_) {
+ sockets_to_compute.push(socket->origin());
+ }
+
+ /* 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.has_unlinked_inputs());
+ const MFFunctionNode &function_node = node.as_function();
+
+ bool all_origins_are_computed = true;
+ for (const MFInputSocket *input_socket : function_node.inputs()) {
+ const MFOutputSocket *origin = input_socket->origin();
+ if (origin != nullptr) {
+ if (!storage.socket_is_computed(*origin)) {
+ sockets_to_compute.push(origin);
+ all_origins_are_computed = false;
+ }
+ }
+ }
+
+ if (all_origins_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 (int 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 (int 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) {
+ int param_index = inputs_.size() + 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.data());
+ 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, int socket_id_amount)
+ : mask_(mask),
+ value_per_output_id_(socket_id_amount, nullptr),
+ min_array_size_(mask.min_array_size())
+{
+}
+
+MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage()
+{
+ for (Value *any_value : 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.data());
+ }
+ else {
+ type.destruct_indices(span.data(), mask_);
+ MEM_freeN(span.data());
+ }
+ }
+ else if (any_value->type == ValueType::OwnVector) {
+ OwnVectorValue *value = (OwnVectorValue *)any_value;
+ delete value->vector_array;
+ }
+ }
+}
+
+IndexMask MFNetworkEvaluationStorage::mask() const
+{
+ return mask_;
+}
+
+bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket)
+{
+ Value *any_value = 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 = 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 = 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 = 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 = 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.data());
+ }
+ else {
+ type.destruct_indices(span.data(), mask_);
+ MEM_freeN(span.data());
+ }
+ 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;
+ value_per_output_id_[origin.id()] = nullptr;
+ }
+ break;
+ }
+ }
+}
+
+void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket,
+ GVSpan virtual_span)
+{
+ BLI_assert(value_per_output_id_[socket.id()] == nullptr);
+ BLI_assert(virtual_span.size() >= min_array_size_);
+
+ auto *value = allocator_.construct<InputSingleValue>(virtual_span);
+ value_per_output_id_[socket.id()] = value;
+}
+
+void MFNetworkEvaluationStorage::add_vector_input_from_caller(const MFOutputSocket &socket,
+ GVArraySpan virtual_array_span)
+{
+ BLI_assert(value_per_output_id_[socket.id()] == nullptr);
+ BLI_assert(virtual_array_span.size() >= min_array_size_);
+
+ auto *value = allocator_.construct<InputVectorValue>(virtual_array_span);
+ value_per_output_id_[socket.id()] = value;
+}
+
+void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket,
+ GMutableSpan span)
+{
+ BLI_assert(value_per_output_id_[socket.id()] == nullptr);
+ BLI_assert(span.size() >= min_array_size_);
+
+ auto *value = allocator_.construct<OutputSingleValue>(span);
+ value_per_output_id_[socket.id()] = value;
+}
+
+void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket,
+ GVectorArray &vector_array)
+{
+ BLI_assert(value_per_output_id_[socket.id()] == nullptr);
+ BLI_assert(vector_array.size() >= min_array_size_);
+
+ auto *value = allocator_.construct<OutputVectorValue>(vector_array);
+ value_per_output_id_[socket.id()] = value;
+}
+
+GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket)
+{
+ Value *any_value = value_per_output_id_[socket.id()];
+ if (any_value == nullptr) {
+ const CPPType &type = socket.data_type().single_type();
+ void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
+ GMutableSpan span(type, buffer, min_array_size_);
+
+ auto *value = allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false);
+ 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 = value_per_output_id_[socket.id()];
+ if (any_value == nullptr) {
+ const CPPType &type = socket.data_type().single_type();
+ void *buffer = allocator_.allocate(type.size(), type.alignment());
+ GMutableSpan span(type, buffer, 1);
+
+ auto *value = allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true);
+ 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 = 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, min_array_size_);
+
+ auto *value = allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size());
+ 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 = 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 = allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size());
+ 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 = value_per_output_id_[from.id()];
+ Value *to_any_value = 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(mask_, span.data());
+ 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) {
+ value_per_output_id_[to.id()] = value;
+ 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(min_array_size_ * type.size(), type.alignment(), AT);
+ GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
+ virtual_span.materialize_to_uninitialized(mask_, new_array_ref.data());
+
+ OwnSingleValue *new_value = allocator_.construct<OwnSingleValue>(
+ new_array_ref, to.targets().size(), false);
+ 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 = value_per_output_id_[from.id()];
+ Value *to_any_value = 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) {
+ value_per_output_id_[to.id()] = value;
+ 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 = 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 = allocator_.construct<OwnSingleValue>(
+ new_array_ref, to.targets().size(), true);
+ 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 = value_per_output_id_[from.id()];
+ Value *to_any_value = 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(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) {
+ value_per_output_id_[to.id()] = value;
+ 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, min_array_size_);
+ new_vector_array->extend(mask_, virtual_array_span);
+
+ OwnVectorValue *new_value = allocator_.construct<OwnVectorValue>(*new_vector_array,
+ to.targets().size());
+ 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 = value_per_output_id_[from.id()];
+ Value *to_any_value = 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) {
+ value_per_output_id_[to.id()] = value;
+ 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 = allocator_.construct<OwnVectorValue>(*new_vector_array,
+ to.targets().size());
+ 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 = 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.data(), 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 = 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 = 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, 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 = 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 blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc
new file mode 100644
index 00000000000..af1e77aa355
--- /dev/null
+++ b/source/blender/functions/intern/multi_function_network_optimization.cc
@@ -0,0 +1,504 @@
+/*
+ * 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
+ */
+
+/* Used to check if two multi-functions have the exact same type. */
+#include <typeinfo>
+
+#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_network_evaluation.hh"
+#include "FN_multi_function_network_optimization.hh"
+
+#include "BLI_disjoint_set.hh"
+#include "BLI_ghash.h"
+#include "BLI_map.hh"
+#include "BLI_multi_value_map.hh"
+#include "BLI_rand.h"
+#include "BLI_stack.hh"
+
+namespace blender::fn::mf_network_optimization {
+
+/* -------------------------------------------------------------------- */
+/** \name Utility functions to find nodes in a network.
+ *
+ * \{ */
+
+static bool set_tag_and_check_if_modified(bool &tag, bool new_value)
+{
+ if (tag != new_value) {
+ tag = new_value;
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes)
+{
+ Array<bool> is_to_the_left(network.node_id_amount(), false);
+ Stack<MFNode *> nodes_to_check;
+
+ for (MFNode *node : nodes) {
+ is_to_the_left[node->id()] = true;
+ nodes_to_check.push(node);
+ }
+
+ while (!nodes_to_check.is_empty()) {
+ MFNode &node = *nodes_to_check.pop();
+
+ for (MFInputSocket *input_socket : node.inputs()) {
+ MFOutputSocket *origin = input_socket->origin();
+ if (origin != nullptr) {
+ MFNode &origin_node = origin->node();
+ if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) {
+ nodes_to_check.push(&origin_node);
+ }
+ }
+ }
+ }
+
+ return is_to_the_left;
+}
+
+static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes)
+{
+ Array<bool> is_to_the_right(network.node_id_amount(), false);
+ Stack<MFNode *> nodes_to_check;
+
+ for (MFNode *node : nodes) {
+ is_to_the_right[node->id()] = true;
+ nodes_to_check.push(node);
+ }
+
+ while (!nodes_to_check.is_empty()) {
+ MFNode &node = *nodes_to_check.pop();
+
+ for (MFOutputSocket *output_socket : node.outputs()) {
+ for (MFInputSocket *target_socket : output_socket->targets()) {
+ MFNode &target_node = target_socket->node();
+ if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) {
+ nodes_to_check.push(&target_node);
+ }
+ }
+ }
+ }
+
+ return is_to_the_right;
+}
+
+static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network,
+ Span<bool> id_mask,
+ bool mask_value)
+{
+ Vector<MFNode *> nodes;
+ for (int id : id_mask.index_range()) {
+ if (id_mask[id] == mask_value) {
+ MFNode *node = network.node_or_null_by_id(id);
+ if (node != nullptr) {
+ nodes.append(node);
+ }
+ }
+ }
+ return nodes;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Dead Node Removal
+ *
+ * \{ */
+
+/**
+ * Unused nodes are all those nodes that no dummy node depends upon.
+ */
+void dead_node_removal(MFNetwork &network)
+{
+ Array<bool> node_is_used_mask = mask_nodes_to_the_left(network, network.dummy_nodes());
+ Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false);
+ network.remove(nodes_to_remove);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Constant Folding
+ *
+ * \{ */
+
+static bool function_node_can_be_constant(MFFunctionNode *node)
+{
+ if (node->has_unlinked_inputs()) {
+ return false;
+ }
+ if (node->function().depends_on_context()) {
+ return false;
+ }
+ return true;
+}
+
+static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network)
+{
+ Vector<MFNode *> non_constant_nodes;
+ non_constant_nodes.extend(network.dummy_nodes());
+
+ for (MFFunctionNode *node : network.function_nodes()) {
+ if (!function_node_can_be_constant(node)) {
+ non_constant_nodes.append(node);
+ }
+ }
+ return non_constant_nodes;
+}
+
+static bool output_has_non_constant_target_node(MFOutputSocket *output_socket,
+ Span<bool> is_not_constant_mask)
+{
+ for (MFInputSocket *target_socket : output_socket->targets()) {
+ MFNode &target_node = target_socket->node();
+ bool target_is_not_constant = is_not_constant_mask[target_node.id()];
+ if (target_is_not_constant) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket)
+{
+ for (MFInputSocket *target_socket : output_socket->targets()) {
+ if (target_socket->node().is_dummy()) {
+ return target_socket;
+ }
+ }
+ return nullptr;
+}
+
+static Vector<MFInputSocket *> find_constant_inputs_to_fold(
+ MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes)
+{
+ Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network);
+ Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes);
+ Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false);
+
+ Vector<MFInputSocket *> sockets_to_compute;
+ for (MFNode *node : constant_nodes) {
+ if (node->inputs().size() == 0) {
+ continue;
+ }
+
+ for (MFOutputSocket *output_socket : node->outputs()) {
+ MFDataType data_type = output_socket->data_type();
+ if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) {
+ MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket);
+ if (dummy_target == nullptr) {
+ dummy_target = &network.add_output("Dummy", data_type);
+ network.add_link(*output_socket, *dummy_target);
+ r_temporary_nodes.append(&dummy_target->node().as_dummy());
+ }
+
+ sockets_to_compute.append(dummy_target);
+ }
+ }
+ }
+ return sockets_to_compute;
+}
+
+static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
+ MFParamsBuilder &params,
+ ResourceCollector &resources)
+{
+ for (int param_index : network_fn.param_indices()) {
+ MFParamType param_type = network_fn.param_type(param_index);
+ MFDataType data_type = param_type.data_type();
+
+ switch (data_type.category()) {
+ case MFDataType::Single: {
+ /* Allocates memory for a single constant folded value. */
+ const CPPType &cpp_type = data_type.single_type();
+ void *buffer = resources.linear_allocator().allocate(cpp_type.size(),
+ cpp_type.alignment());
+ GMutableSpan array{cpp_type, buffer, 1};
+ params.add_uninitialized_single_output(array);
+ break;
+ }
+ case MFDataType::Vector: {
+ /* Allocates memory for a constant folded vector. */
+ const CPPType &cpp_type = data_type.vector_base_type();
+ GVectorArray &vector_array = resources.construct<GVectorArray>(AT, cpp_type, 1);
+ params.add_vector_output(vector_array);
+ break;
+ }
+ }
+ }
+}
+
+static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
+ MFParamsBuilder &params,
+ ResourceCollector &resources,
+ MFNetwork &network)
+{
+ Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
+
+ for (int param_index : network_fn.param_indices()) {
+ MFParamType param_type = network_fn.param_type(param_index);
+ MFDataType data_type = param_type.data_type();
+
+ const MultiFunction *constant_fn = nullptr;
+
+ switch (data_type.category()) {
+ case MFDataType::Single: {
+ const CPPType &cpp_type = data_type.single_type();
+ GMutableSpan array = params.computed_array(param_index);
+ void *buffer = array.data();
+ resources.add(buffer, array.type().destruct_cb(), AT);
+
+ constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
+ break;
+ }
+ case MFDataType::Vector: {
+ GVectorArray &vector_array = params.computed_vector_array(param_index);
+ GSpan array = vector_array[0];
+ constant_fn = &resources.construct<CustomMF_GenericConstantArray>(AT, array);
+ break;
+ }
+ }
+
+ MFFunctionNode &folded_node = network.add_function(*constant_fn);
+ folded_sockets[param_index] = &folded_node.output(0);
+ }
+ return folded_sockets;
+}
+
+static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
+ MFNetwork &network,
+ Span<const MFInputSocket *> sockets_to_compute,
+ ResourceCollector &resources)
+{
+ MFNetworkEvaluator network_fn{{}, sockets_to_compute};
+
+ MFContextBuilder context;
+ MFParamsBuilder params{network_fn, 1};
+ prepare_params_for_constant_folding(network_fn, params, resources);
+ network_fn.call({0}, params, context);
+ return add_constant_folded_sockets(network_fn, params, resources, network);
+}
+
+/**
+ * Find function nodes that always output the same value and replace those with constant nodes.
+ */
+void constant_folding(MFNetwork &network, ResourceCollector &resources)
+{
+ Vector<MFDummyNode *> temporary_nodes;
+ Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
+ if (inputs_to_fold.size() == 0) {
+ return;
+ }
+
+ Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
+ network, inputs_to_fold, resources);
+
+ for (int i : inputs_to_fold.index_range()) {
+ MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
+ network.relink(original_socket, *folded_sockets[i]);
+ }
+
+ network.remove(temporary_nodes);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Common Sub-network Elimination
+ *
+ * \{ */
+
+static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes)
+{
+ if (node.function().depends_on_context()) {
+ return BLI_rng_get_uint(rng);
+ }
+ if (node.has_unlinked_inputs()) {
+ return BLI_rng_get_uint(rng);
+ }
+
+ uint64_t combined_inputs_hash = 394659347u;
+ for (MFInputSocket *input_socket : node.inputs()) {
+ MFOutputSocket *origin_socket = input_socket->origin();
+ uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()],
+ origin_socket->index());
+ combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash);
+ }
+
+ uint64_t function_hash = node.function().hash();
+ uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash);
+ return node_hash;
+}
+
+/**
+ * Produces a hash for every node. Two nodes with the same hash should have a high probability of
+ * outputting the same values.
+ */
+static Array<uint64_t> compute_node_hashes(MFNetwork &network)
+{
+ RNG *rng = BLI_rng_new(0);
+ Array<uint64_t> node_hashes(network.node_id_amount());
+ Array<bool> node_is_hashed(network.node_id_amount(), false);
+
+ /* No dummy nodes are not assumed to output the same values. */
+ for (MFDummyNode *node : network.dummy_nodes()) {
+ uint64_t node_hash = BLI_rng_get_uint(rng);
+ node_hashes[node->id()] = node_hash;
+ node_is_hashed[node->id()] = true;
+ }
+
+ Stack<MFFunctionNode *> nodes_to_check;
+ nodes_to_check.push_multiple(network.function_nodes());
+
+ while (!nodes_to_check.is_empty()) {
+ MFFunctionNode &node = *nodes_to_check.peek();
+ if (node_is_hashed[node.id()]) {
+ nodes_to_check.pop();
+ continue;
+ }
+
+ /* Make sure that origin nodes are hashed first. */
+ bool all_dependencies_ready = true;
+ for (MFInputSocket *input_socket : node.inputs()) {
+ MFOutputSocket *origin_socket = input_socket->origin();
+ if (origin_socket != nullptr) {
+ MFNode &origin_node = origin_socket->node();
+ if (!node_is_hashed[origin_node.id()]) {
+ all_dependencies_ready = false;
+ nodes_to_check.push(&origin_node.as_function());
+ }
+ }
+ }
+ if (!all_dependencies_ready) {
+ continue;
+ }
+
+ uint64_t node_hash = compute_node_hash(node, rng, node_hashes);
+ node_hashes[node.id()] = node_hash;
+ node_is_hashed[node.id()] = true;
+ nodes_to_check.pop();
+ }
+
+ BLI_rng_free(rng);
+ return node_hashes;
+}
+
+static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network,
+ Span<uint64_t> node_hashes)
+{
+ MultiValueMap<uint64_t, MFNode *> nodes_by_hash;
+ for (int id : IndexRange(network.node_id_amount())) {
+ MFNode *node = network.node_or_null_by_id(id);
+ if (node != nullptr) {
+ uint64_t node_hash = node_hashes[id];
+ nodes_by_hash.add(node_hash, node);
+ }
+ }
+ return nodes_by_hash;
+}
+
+static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b)
+{
+ if (&a == &b) {
+ return true;
+ }
+ if (typeid(a) == typeid(b)) {
+ return a.equals(b);
+ }
+ return false;
+}
+
+static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b)
+{
+ if (cache.in_same_set(a.id(), b.id())) {
+ return true;
+ }
+
+ if (a.is_dummy() || b.is_dummy()) {
+ return false;
+ }
+ if (!functions_are_equal(a.as_function().function(), b.as_function().function())) {
+ return false;
+ }
+ for (int i : a.inputs().index_range()) {
+ const MFOutputSocket *origin_a = a.input(i).origin();
+ const MFOutputSocket *origin_b = b.input(i).origin();
+ if (origin_a == nullptr || origin_b == nullptr) {
+ return false;
+ }
+ if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) {
+ return false;
+ }
+ }
+
+ cache.join(a.id(), b.id());
+ return true;
+}
+
+static void relink_duplicate_nodes(MFNetwork &network,
+ MultiValueMap<uint64_t, MFNode *> &nodes_by_hash)
+{
+ DisjointSet same_node_cache{network.node_id_amount()};
+
+ for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) {
+ if (nodes_with_same_hash.size() <= 1) {
+ continue;
+ }
+
+ Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash;
+ while (nodes_to_check.size() >= 2) {
+ Vector<MFNode *, 16> remaining_nodes;
+
+ MFNode &deduplicated_node = *nodes_to_check[0];
+ for (MFNode *node : nodes_to_check.as_span().drop_front(1)) {
+ /* This is true with fairly high probability, but hash collisions can happen. So we have to
+ * check if the node actually output the same values. */
+ if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) {
+ for (int i : deduplicated_node.outputs().index_range()) {
+ network.relink(node->output(i), deduplicated_node.output(i));
+ }
+ }
+ else {
+ remaining_nodes.append(node);
+ }
+ }
+ nodes_to_check = std::move(remaining_nodes);
+ }
+ }
+}
+
+/**
+ * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node
+ * groups were used to create the network.
+ */
+void common_subnetwork_elimination(MFNetwork &network)
+{
+ Array<uint64_t> node_hashes = compute_node_hashes(network);
+ MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes);
+ relink_duplicate_nodes(network, nodes_by_hash);
+}
+
+/** \} */
+
+} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc
new file mode 100644
index 00000000000..9a632b58be8
--- /dev/null
+++ b/source/blender/functions/tests/FN_array_spans_test.cc
@@ -0,0 +1,132 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "FN_array_spans.hh"
+#include "FN_generic_vector_array.hh"
+
+#include "BLI_array.hh"
+
+namespace blender::fn::tests {
+
+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<int64_t, 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::get<int32_t>()};
+ 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::get<std::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::get<int32_t>(), 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 blender::fn::tests
diff --git a/source/blender/functions/tests/FN_attributes_ref_test.cc b/source/blender/functions/tests/FN_attributes_ref_test.cc
new file mode 100644
index 00000000000..3a5e4743c1e
--- /dev/null
+++ b/source/blender/functions/tests/FN_attributes_ref_test.cc
@@ -0,0 +1,97 @@
+/* Apache License, Version 2.0 */
+
+#include "BLI_float3.hh"
+#include "FN_attributes_ref.hh"
+
+#include "testing/testing.h"
+
+namespace blender::fn::tests {
+
+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};
+
+ int 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").data(), positions.data() + 1);
+ EXPECT_EQ(attributes.get("ID").data(), ids.data() + 1);
+ EXPECT_EQ(attributes.get("Size").data(), sizes.data() + 1);
+ EXPECT_EQ(attributes.get("Name").data(), 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 blender::fn::tests
diff --git a/tests/gtests/functions/FN_cpp_type_test.cc b/source/blender/functions/tests/FN_cpp_type_test.cc
index ca8583bdc92..29368b251cc 100644
--- a/tests/gtests/functions/FN_cpp_type_test.cc
+++ b/source/blender/functions/tests/FN_cpp_type_test.cc
@@ -1,24 +1,11 @@
-/*
- * 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.
- *
- */
+/* Apache License, Version 2.0 */
#include "testing/testing.h"
#include "FN_cpp_type.hh"
+namespace blender::fn::tests {
+
static const int default_constructed_value = 1;
static const int copy_constructed_value = 2;
static const int move_constructed_value = 3;
@@ -49,7 +36,7 @@ struct TestType {
other.value = copy_constructed_from_value;
}
- TestType(TestType &&other)
+ TestType(TestType &&other) noexcept
{
value = move_constructed_value;
other.value = move_constructed_from_value;
@@ -62,15 +49,37 @@ struct TestType {
return *this;
}
- TestType &operator=(TestType &&other)
+ TestType &operator=(TestType &&other) noexcept
{
value = move_assigned_value;
other.value = move_assigned_from_value;
return *this;
}
+
+ friend std::ostream &operator<<(std::ostream &stream, const TestType &value)
+ {
+ stream << value.value;
+ return stream;
+ }
+
+ friend bool operator==(const TestType &UNUSED(a), const TestType &UNUSED(b))
+ {
+ return false;
+ }
+
+ uint64_t hash() const
+ {
+ return 0;
+ }
};
-MAKE_CPP_TYPE(TestType, TestType)
+} // namespace blender::fn::tests
+
+MAKE_CPP_TYPE(TestType, blender::fn::tests::TestType)
+
+namespace blender::fn::tests {
+
+const CPPType &CPPType_TestType = CPPType::get<TestType>();
TEST(cpp_type, Size)
{
@@ -82,6 +91,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};
@@ -297,3 +312,14 @@ TEST(cpp_type, FillUninitialized)
EXPECT_EQ(buffer2[8], copy_constructed_value);
EXPECT_EQ(buffer2[9], 0);
}
+
+TEST(cpp_type, DebugPrint)
+{
+ int value = 42;
+ std::stringstream ss;
+ CPPType::get<int32_t>().debug_print((void *)&value, ss);
+ std::string text = ss.str();
+ EXPECT_EQ(text, "42");
+}
+
+} // namespace blender::fn::tests
diff --git a/source/blender/functions/tests/FN_generic_vector_array_test.cc b/source/blender/functions/tests/FN_generic_vector_array_test.cc
new file mode 100644
index 00000000000..77ec05f12dc
--- /dev/null
+++ b/source/blender/functions/tests/FN_generic_vector_array_test.cc
@@ -0,0 +1,101 @@
+/* Apache License, Version 2.0 */
+
+#include "FN_generic_vector_array.hh"
+
+#include "testing/testing.h"
+
+namespace blender::fn::tests {
+
+TEST(generic_vector_array, Constructor)
+{
+ GVectorArray vectors{CPPType::get<int32_t>(), 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::get<int32_t>());
+}
+
+TEST(generic_vector_array, Append)
+{
+ GVectorArray vectors{CPPType::get<std::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::get<int32_t>(), 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::get<int32_t>());
+ 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::get<int32_t>(), 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::get<int32_t>(), 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 blender::fn::tests
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
new file mode 100644
index 00000000000..53290e9170c
--- /dev/null
+++ b/source/blender/functions/tests/FN_multi_function_network_test.cc
@@ -0,0 +1,253 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_network.hh"
+#include "FN_multi_function_network_evaluation.hh"
+
+namespace blender::fn::tests {
+
+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 (int64_t 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 (int64_t 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 (int64_t i : mask) {
+ int sum = 0;
+ VSpan<int> vector = vectors[i];
+ for (int 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 (int64_t 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::get<int32_t>(), 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::get<int32_t>(), 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::get<int32_t>(), 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 blender::fn::tests
diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc
new file mode 100644
index 00000000000..6acb6e22b01
--- /dev/null
+++ b/source/blender/functions/tests/FN_multi_function_test.cc
@@ -0,0 +1,385 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "FN_multi_function.hh"
+#include "FN_multi_function_builder.hh"
+
+namespace blender::fn::tests {
+
+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 (int64_t 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 (int64_t 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 (int64_t i : mask) {
+ uint size = sizes[i];
+ for (uint j : IndexRange(size)) {
+ ranges.append(i, j);
+ }
+ }
+ }
+};
+
+TEST(multi_function, CreateRangeFunction)
+{
+ CreateRangeFunction fn;
+
+ GVectorArray ranges(CPPType::get<int32_t>(), 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 (int64_t i : mask) {
+ vectors.append(i, values[i]);
+ }
+ }
+};
+
+TEST(multi_function, GenericAppendFunction)
+{
+ GenericAppendFunction fn(CPPType::get<int32_t>());
+
+ GVectorArray vectors(CPPType::get<int32_t>(), 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);
+}
+
+TEST(multi_function, CustomMF_GenericConstant)
+{
+ int value = 42;
+ CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value};
+ EXPECT_EQ(fn.param_name(0), "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, 1, 2}, params, context);
+
+ EXPECT_EQ(outputs[0], 42);
+ EXPECT_EQ(outputs[1], 42);
+ EXPECT_EQ(outputs[2], 42);
+ EXPECT_EQ(outputs[3], 0);
+}
+
+TEST(multi_function, CustomMF_GenericConstantArray)
+{
+ std::array<int, 4> values = {3, 4, 5, 6};
+ CustomMF_GenericConstantArray fn{GSpan(Span(values))};
+ EXPECT_EQ(fn.param_name(0), "[3, 4, 5, 6, ]");
+
+ GVectorArray g_vector_array{CPPType::get<int32_t>(), 4};
+ GVectorArrayRef<int> vector_array = g_vector_array;
+
+ MFParamsBuilder params(fn, g_vector_array.size());
+ params.add_vector_output(g_vector_array);
+
+ MFContextBuilder context;
+
+ fn.call({1, 2, 3}, params, context);
+
+ EXPECT_EQ(vector_array[0].size(), 0);
+ EXPECT_EQ(vector_array[1].size(), 4);
+ EXPECT_EQ(vector_array[2].size(), 4);
+ EXPECT_EQ(vector_array[3].size(), 4);
+ for (int i = 1; i < 4; i++) {
+ EXPECT_EQ(vector_array[i][0], 3);
+ EXPECT_EQ(vector_array[i][1], 4);
+ EXPECT_EQ(vector_array[i][2], 5);
+ EXPECT_EQ(vector_array[i][3], 6);
+ }
+}
+
+TEST(multi_function, CustomMF_Convert)
+{
+ CustomMF_Convert<float, int> fn;
+
+ Array<float> inputs = {5.4f, 7.1f, 9.0f};
+ Array<int> outputs(inputs.size(), 0);
+
+ MFParamsBuilder params(fn, inputs.size());
+ params.add_readonly_single_input(inputs.as_span());
+ params.add_uninitialized_single_output(outputs.as_mutable_span());
+
+ MFContextBuilder context;
+ fn.call({0, 2}, params, context);
+
+ EXPECT_EQ(outputs[0], 5);
+ EXPECT_EQ(outputs[1], 0);
+ EXPECT_EQ(outputs[2], 9);
+}
+
+} // namespace blender::fn::tests
diff --git a/source/blender/functions/tests/FN_spans_test.cc b/source/blender/functions/tests/FN_spans_test.cc
new file mode 100644
index 00000000000..fbcf1fda71e
--- /dev/null
+++ b/source/blender/functions/tests/FN_spans_test.cc
@@ -0,0 +1,222 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "FN_spans.hh"
+
+namespace blender::fn::tests {
+
+TEST(generic_span, TypeConstructor)
+{
+ GSpan span(CPPType::get<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::get<int32_t>(), 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::get<int32_t>());
+ 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::get<int32_t>(), 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());
+ EXPECT_TRUE(span.is_full_array());
+
+ 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());
+ EXPECT_TRUE(virtual_span.is_full_array());
+
+ 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());
+ EXPECT_FALSE(span.is_full_array());
+
+ 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());
+ EXPECT_FALSE(span.is_full_array());
+
+ 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::get<int32_t>());
+ EXPECT_EQ(span.size(), 0);
+ EXPECT_TRUE(span.is_empty());
+ EXPECT_FALSE(span.is_single_element());
+ EXPECT_TRUE(span.is_full_array());
+
+ 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::get<int32_t>(), 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());
+ EXPECT_TRUE(span.is_full_array());
+
+ 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::get<int32_t>());
+ 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());
+ EXPECT_TRUE(span.is_full_array());
+
+ 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::get<int32_t>(), &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);
+ EXPECT_FALSE(span.is_full_array());
+
+ 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 blender::fn::tests
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index d3c85b891c6..497cb4a10a5 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
)
@@ -39,7 +42,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
@@ -62,6 +65,8 @@ set(SRC
intern/MOD_gpenciltint.c
MOD_gpencil_modifiertypes.h
+ intern/MOD_gpencil_ui_common.h
+ intern/MOD_gpencil_util.h
)
set(LIB
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..a9afbc4b6ae
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -0,0 +1,446 @@
+/* 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 "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 */
+
+/**
+ * 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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 2d5e01ced94..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,19 +37,28 @@
#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"
-#include "BKE_lattice.h"
#include "BKE_lib_query.h"
#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"
@@ -69,39 +80,34 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
static void gpencil_deform_verts(ArmatureGpencilModifierData *mmd, Object *target, bGPDstroke *gps)
{
bGPDspoint *pt = gps->points;
- float *all_vert_coords = MEM_callocN(sizeof(float) * 3 * gps->totpoints, __func__);
+ float(*vert_coords)[3] = MEM_mallocN(sizeof(float[3]) * gps->totpoints, __func__);
int i;
BKE_gpencil_dvert_ensure(gps);
/* prepare array of points */
for (i = 0; i < gps->totpoints; i++, pt++) {
- float *pt_coords = &all_vert_coords[3 * i];
- float co[3];
- copy_v3_v3(co, &pt->x);
- copy_v3_v3(pt_coords, co);
+ copy_v3_v3(vert_coords[i], &pt->x);
}
/* deform verts */
- armature_deform_verts(mmd->object,
- target,
- NULL,
- (float(*)[3])all_vert_coords,
- NULL,
- gps->totpoints,
- mmd->deformflag,
- (float(*)[3])mmd->prevCos,
- mmd->vgname,
- gps);
+ BKE_armature_deform_coords_with_gpencil_stroke(mmd->object,
+ target,
+ vert_coords,
+ NULL,
+ gps->totpoints,
+ mmd->deformflag,
+ mmd->vert_coords_prev,
+ mmd->vgname,
+ gps);
/* Apply deformed coordinates */
pt = gps->points;
for (i = 0; i < gps->totpoints; i++, pt++) {
- float *pt_coords = &all_vert_coords[3 * i];
- copy_v3_v3(&pt->x, pt_coords);
+ copy_v3_v3(&pt->x, vert_coords[i]);
}
- MEM_SAFE_FREE(all_vert_coords);
+ MEM_freeN(vert_coords);
}
/* deform stroke */
@@ -186,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",
@@ -207,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..56d94611b5d 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)
@@ -108,7 +119,6 @@ static void gpf_clear_all_strokes(bGPDframe *gpf)
/* Reduce the number of points in the stroke
*
* Note: This won't be called if all points are present/removed
- * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions)
*/
static void reduce_stroke_points(bGPDstroke *gps,
const int num_points,
@@ -121,7 +131,6 @@ static void reduce_stroke_points(bGPDstroke *gps,
}
/* Which end should points be removed from */
- // TODO: free stroke weights
switch (transition) {
case GP_BUILD_TRANSITION_GROW: /* Show in forward order =
* Remove ungrown-points from end of stroke. */
@@ -268,7 +277,6 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
}
else {
/* Some proportion of stroke is visible */
- /* XXX: Will the transition settings still be valid now? */
if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) {
/* Do nothing - whole stroke is visible */
}
@@ -292,8 +300,6 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
/* --------------------------------------------- */
/* Concurrent - Show multiple strokes at once */
-// TODO: Allow random offsets to start times
-// TODO: Allow varying speeds? Scaling of progress?
static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac)
{
bGPDstroke *gps, *gps_next;
@@ -331,8 +337,7 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
/* Build effect occurs over when fac = 0, to fac = relative_len */
if (fac <= relative_len) {
/* Scale fac to fit relative_len */
- /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */
- const float scaled_fac = fac / relative_len;
+ const float scaled_fac = fac / MAX2(relative_len, PSEUDOINVERSE_EPSILON);
if (reverse) {
num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints);
@@ -360,8 +365,7 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
const float start_fac = 1.0f - relative_len;
if (fac >= start_fac) {
- /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */
- const float scaled_fac = (fac - start_fac) / relative_len;
+ const float scaled_fac = (fac - start_fac) / MAX2(relative_len, PSEUDOINVERSE_EPSILON);
if (reverse) {
num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints);
@@ -382,8 +386,6 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
break;
}
-
- /* TODO... */
}
/* Modify the stroke geometry */
@@ -537,6 +539,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 +645,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..4761dc878c0 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"
@@ -103,7 +114,7 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
}
/* calculate factor of fallof */
-static float gp_hook_falloff(const struct GPHookData_cb *tData, const float len_sq)
+static float gpencil_hook_falloff(const struct GPHookData_cb *tData, const float len_sq)
{
BLI_assert(tData->falloff_sq);
if (len_sq > tData->falloff_sq) {
@@ -156,7 +167,7 @@ static float gp_hook_falloff(const struct GPHookData_cb *tData, const float len_
}
/* apply point deformation */
-static void gp_hook_co_apply(struct GPHookData_cb *tData, float weight, bGPDspoint *pt)
+static void gpencil_hook_co_apply(struct GPHookData_cb *tData, float weight, bGPDspoint *pt)
{
float fac;
@@ -172,7 +183,7 @@ static void gp_hook_co_apply(struct GPHookData_cb *tData, float weight, bGPDspoi
len_sq = len_squared_v3v3(tData->cent, &pt->x);
}
- fac = gp_hook_falloff(tData, len_sq);
+ fac = gpencil_hook_falloff(tData, len_sq);
}
else {
fac = tData->fac_orig;
@@ -260,7 +271,7 @@ static void deformStroke(GpencilModifierData *md,
if (weight < 0.0f) {
continue;
}
- gp_hook_co_apply(&tData, weight, pt);
+ gpencil_hook_co_apply(&tData, weight, pt);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
@@ -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 5d5d673ca55..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"
@@ -105,7 +116,8 @@ static void deformStroke(GpencilModifierData *md,
if (weight < 0.0f) {
continue;
}
- calc_latt_deform((struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight);
+ BKE_lattice_deform_data_eval_co(
+ (struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
@@ -147,7 +159,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData
/* free lingering data */
ldata = (struct LatticeDeformData *)mmd->cache_data;
if (ldata) {
- end_latt_deform(ldata);
+ BKE_lattice_deform_data_destroy(ldata);
mmd->cache_data = NULL;
}
@@ -162,7 +174,7 @@ static void freeData(GpencilModifierData *md)
struct LatticeDeformData *ldata = (struct LatticeDeformData *)mmd->cache_data;
/* free deform data */
if (ldata) {
- end_latt_deform(ldata);
+ BKE_lattice_deform_data_destroy(ldata);
}
}
@@ -207,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",
@@ -229,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..c99ca64325c 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"
@@ -86,52 +97,19 @@ static void update_mirror_object(Object *ob,
bGPDstroke *gps,
int axis)
{
- /* Calculate local matrix transformation. */
- float mat[3][3], inv_mat[3][3];
- BKE_object_to_mat3(ob, mat);
- invert_m3_m3(inv_mat, mat);
-
- int i;
- bGPDspoint *pt;
- float factor[3] = {1.0f, 1.0f, 1.0f};
- factor[axis] = -1.0f;
-
- float clear[3] = {0.0f, 0.0f, 0.0f};
- clear[axis] = 1.0f;
-
- float ob_origin[3];
- float pt_origin[3];
- float half_origin[3];
- float rot_mat[3][3];
-
- float eul[3];
- mat4_to_eul(eul, mmd->object->obmat);
- mul_v3_fl(eul, 2.0f);
- eul_to_mat3(rot_mat, eul);
- sub_v3_v3v3(ob_origin, ob->obmat[3], mmd->object->obmat[3]);
-
- /* Only works with current axis. */
- mul_v3_v3(ob_origin, clear);
-
- /* Invert the origin. */
- mul_v3_v3fl(pt_origin, ob_origin, -2.0f);
- mul_v3_v3fl(half_origin, pt_origin, 0.5f);
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* Apply any local transformation. */
- mul_m3_v3(mat, &pt->x);
-
- /* Apply mirror effect. */
- mul_v3_v3(&pt->x, factor);
- /* Apply location. */
- add_v3_v3(&pt->x, pt_origin);
- /* Apply rotation (around new center). */
- sub_v3_v3(&pt->x, half_origin);
- mul_m3_v3(rot_mat, &pt->x);
- add_v3_v3(&pt->x, half_origin);
-
- /* Undo local transformation to avoid double transform in drawing. */
- mul_m3_v3(inv_mat, &pt->x);
+ float mtx[4][4];
+ unit_m4(mtx);
+ mtx[axis][axis] = -1.0f;
+
+ float tmp[4][4];
+ float itmp[4][4];
+ invert_m4_m4(tmp, mmd->object->obmat);
+ mul_m4_m4m4(tmp, tmp, ob->obmat);
+ invert_m4_m4(itmp, tmp);
+ mul_m4_series(mtx, itmp, mtx, tmp);
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ mul_m4_v3(mtx, &gps->points[i].x);
}
}
@@ -254,6 +232,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 +288,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..0d8a5f7914e 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, true);
+}
+
+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..75f929e979e 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)
@@ -94,19 +105,23 @@ static void deformStroke(GpencilModifierData *md,
bGPDspoint *pt = &gps->points[i];
MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
- /* verify vertex group */
+ /* Verify vertex group. */
const float weight = get_modifier_point_weight(
dvert, (mmd->flag & GP_OFFSET_INVERT_VGROUP) != 0, def_nr);
if (weight < 0.0f) {
continue;
}
- /* calculate matrix */
+ /* Calculate matrix. */
mul_v3_v3fl(loc, mmd->loc, weight);
mul_v3_v3fl(rot, mmd->rot, weight);
mul_v3_v3fl(scale, mmd->scale, weight);
add_v3_fl(scale, 1.0);
loc_eul_size_to_mat4(mat, loc, rot, scale);
+ /* Apply scale to thickness. */
+ float unit_scale = (scale[0] + scale[1] + scale[2]) / 3.0f;
+ pt->pressure *= unit_scale;
+
mul_m4_v3(mat, &pt->x);
}
/* Calc geometry data. */
@@ -136,6 +151,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 +202,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..1e75c5926cd 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)
@@ -72,7 +83,7 @@ static void deformStroke(GpencilModifierData *md,
mmd->material,
mmd->pass_index,
mmd->layer_pass,
- mmd->mode == GP_SIMPLIFY_SAMPLE ? 3 : 4,
+ mmd->mode == GP_SIMPLIFY_SAMPLE ? 2 : 4,
gpl,
gps,
mmd->flag & GP_SIMPLIFY_INVERT_LAYER,
@@ -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..98a885f88e1 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -52,38 +52,37 @@ set(INC_SYS
)
set(SRC
- intern/gpu_attr_binding.c
- intern/gpu_batch.c
+ intern/gpu_attr_binding.cc
+ intern/gpu_batch.cc
intern/gpu_batch_presets.c
intern/gpu_batch_utils.c
intern/gpu_buffers.c
intern/gpu_codegen.c
- intern/gpu_context.cpp
- intern/gpu_debug.c
- intern/gpu_draw.c
- intern/gpu_draw_smoke.c
- intern/gpu_element.c
- intern/gpu_extensions.c
- intern/gpu_framebuffer.c
- intern/gpu_immediate.c
+ intern/gpu_context.cc
+ intern/gpu_debug.cc
+ intern/gpu_element.cc
+ intern/gpu_extensions.cc
+ intern/gpu_framebuffer.cc
+ intern/gpu_immediate.cc
intern/gpu_immediate_util.c
intern/gpu_init_exit.c
intern/gpu_material.c
intern/gpu_material_library.c
intern/gpu_matrix.c
intern/gpu_node_graph.c
- intern/gpu_platform.c
+ intern/gpu_platform.cc
intern/gpu_primitive.c
intern/gpu_select.c
intern/gpu_select_pick.c
intern/gpu_select_sample_query.c
- intern/gpu_shader.c
+ intern/gpu_shader.cc
+ intern/gpu_shader_builtin.c
intern/gpu_shader_interface.c
- intern/gpu_state.c
- intern/gpu_texture.c
- intern/gpu_uniformbuffer.c
- intern/gpu_vertex_buffer.c
- intern/gpu_vertex_format.c
+ intern/gpu_state.cc
+ intern/gpu_texture.cc
+ intern/gpu_uniformbuffer.cc
+ intern/gpu_vertex_buffer.cc
+ intern/gpu_vertex_format.cc
intern/gpu_viewport.c
GPU_attr_binding.h
@@ -94,7 +93,6 @@ set(SRC
GPU_common.h
GPU_context.h
GPU_debug.h
- GPU_draw.h
GPU_element.h
GPU_extensions.h
GPU_framebuffer.h
@@ -171,9 +169,6 @@ data_to_c_simple(shaders/gpu_shader_image_desaturate_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_image_overlays_merge_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_image_shuffle_color_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_image_mask_uniform_color_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_image_modulate_alpha_frag.glsl SRC)
-data_to_c_simple(shaders/gpu_shader_image_alpha_color_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_image_color_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_image_varying_color_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_3D_image_vert.glsl SRC)
@@ -210,6 +205,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)
@@ -218,6 +214,8 @@ data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC)
data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC)
+data_to_c_simple(shaders/gpu_shader_codegen_lib.glsl SRC)
+
data_to_c_simple(shaders/gpu_shader_geometry.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_add_shader.glsl SRC)
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 5f55b512695..85d9c037b38 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -127,9 +127,8 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);
#define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false)
-void GPU_batch_program_set_no_use(GPUBatch *, uint32_t program, const GPUShaderInterface *);
-void GPU_batch_program_set(GPUBatch *, uint32_t program, const GPUShaderInterface *);
-void GPU_batch_program_set_shader(GPUBatch *, GPUShader *shader);
+void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader);
+void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader);
void GPU_batch_program_set_imm_shader(GPUBatch *batch);
void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id);
void GPU_batch_program_set_builtin_with_config(GPUBatch *batch,
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index ab16bfc43c4..c7e74040568 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;
@@ -66,7 +67,7 @@ GPU_PBVH_Buffers *GPU_pbvh_bmesh_buffers_build(bool smooth_shading);
void GPU_pbvh_bmesh_buffers_update_free(GPU_PBVH_Buffers *buffers);
void GPU_pbvh_grid_buffers_update_free(GPU_PBVH_Buffers *buffers,
const struct DMFlagMat *grid_flag_mats,
- int *grid_indices);
+ const int *grid_indices);
/* Update mesh buffers without topology changes. Threaded. */
enum {
@@ -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_debug.h b/source/blender/gpu/GPU_debug.h
index 8928581ee08..fc6ca791664 100644
--- a/source/blender/gpu/GPU_debug.h
+++ b/source/blender/gpu/GPU_debug.h
@@ -24,8 +24,6 @@
#ifndef __GPU_DEBUG_H__
#define __GPU_DEBUG_H__
-#include "GPU_glew.h"
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h
deleted file mode 100644
index ab507d852e8..00000000000
--- a/source/blender/gpu/GPU_draw.h
+++ /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.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup gpu
- */
-
-#ifndef __GPU_DRAW_H__
-#define __GPU_DRAW_H__
-
-#include "BLI_utildefines.h"
-#include "DNA_object_enums.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct FluidModifierData;
-struct ImBuf;
-struct Image;
-struct ImageUser;
-struct Main;
-
-/* OpenGL drawing functions related to shading. */
-
-/* Mipmap settings
- * - these will free textures on changes */
-
-void GPU_set_mipmap(struct Main *bmain, bool mipmap);
-bool GPU_get_mipmap(void);
-void GPU_set_linear_mipmap(bool linear);
-bool GPU_get_linear_mipmap(void);
-void GPU_paint_set_mipmap(struct Main *bmain, bool mipmap);
-
-/* Anisotropic filtering settings
- * - these will free textures on changes */
-void GPU_set_anisotropic(float value);
-float GPU_get_anisotropic(void);
-
-/* Image updates and free
- * - these deal with images bound as opengl textures */
-
-void GPU_paint_update_image(
- struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h);
-void GPU_create_gl_tex(unsigned int *bind,
- unsigned int *rect,
- float *frect,
- int rectw,
- int recth,
- int textarget,
- bool mipmap,
- bool half_float,
- bool use_srgb,
- struct Image *ima);
-void GPU_create_gl_tex_compressed(unsigned int *bind,
- int textarget,
- struct Image *ima,
- struct ImBuf *ibuf);
-bool GPU_upload_dxt_texture(struct ImBuf *ibuf, bool use_srgb);
-void GPU_free_image(struct Image *ima);
-void GPU_free_images(struct Main *bmain);
-void GPU_free_images_anim(struct Main *bmain);
-void GPU_free_images_old(struct Main *bmain);
-
-/* gpu_draw_smoke.c */
-void GPU_free_smoke(struct FluidModifierData *mmd);
-void GPU_free_smoke_velocity(struct FluidModifierData *mmd);
-void GPU_create_smoke(struct FluidModifierData *mmd, int highres);
-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);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h
index ab54148a2ff..0d8954dc16c 100644
--- a/source/blender/gpu/GPU_extensions.h
+++ b/source/blender/gpu/GPU_extensions.h
@@ -52,11 +52,15 @@ bool GPU_context_local_shaders_workaround(void);
bool GPU_texture_copy_workaround(void);
bool GPU_crappy_amd_driver(void);
+int GPU_texture_size_with_limit(int res);
+
bool GPU_mem_stats_supported(void);
void GPU_mem_stats_get(int *totalmem, int *freemem);
void GPU_code_generate_glsl_lib(void);
+bool GPU_stereo_quadbuffer_support(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index 213cbe30794..db531cdef9b 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -24,15 +24,15 @@
#ifndef __GPU_FRAMEBUFFER_H__
#define __GPU_FRAMEBUFFER_H__
+#include "GPU_texture.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-struct GPUTexture;
-
typedef struct GPUAttachment {
struct GPUTexture *tex;
- int mip, layer;
+ int layer, mip;
} GPUAttachment;
typedef enum eGPUFrameBufferBits {
@@ -41,6 +41,12 @@ typedef enum eGPUFrameBufferBits {
GPU_STENCIL_BIT = (1 << 2),
} eGPUFrameBufferBits;
+typedef enum eGPUBackBuffer {
+ GPU_BACKBUFFER = 0,
+ GPU_BACKBUFFER_RIGHT,
+ GPU_BACKBUFFER_LEFT,
+} eGPUBackBuffer;
+
typedef struct GPUFrameBuffer GPUFrameBuffer;
typedef struct GPUOffScreen GPUOffScreen;
@@ -114,35 +120,35 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi
#define GPU_ATTACHMENT_NONE \
{ \
- .tex = NULL, .layer = -1, .mip = 0, \
+ NULL, -1, 0, \
}
#define GPU_ATTACHMENT_LEAVE \
{ \
- .tex = NULL, .layer = -1, .mip = -1, \
+ NULL, -1, -1, \
}
#define GPU_ATTACHMENT_TEXTURE(_tex) \
{ \
- .tex = _tex, .layer = -1, .mip = 0, \
+ _tex, -1, 0, \
}
#define GPU_ATTACHMENT_TEXTURE_MIP(_tex, _mip) \
{ \
- .tex = _tex, .layer = -1, .mip = _mip, \
+ _tex, -1, _mip, \
}
#define GPU_ATTACHMENT_TEXTURE_LAYER(_tex, _layer) \
{ \
- .tex = _tex, .layer = _layer, .mip = 0, \
+ _tex, _layer, 0, \
}
#define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_tex, _layer, _mip) \
{ \
- .tex = _tex, .layer = _layer, .mip = _mip, \
+ _tex, _layer, _mip, \
}
#define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_tex, _face) \
{ \
- .tex = _tex, .layer = _face, .mip = 0, \
+ _tex, _face, 0, \
}
#define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_tex, _face, _mip) \
{ \
- .tex = _tex, .layer = _face, .mip = _mip, \
+ _tex, _face, _mip, \
}
/* Framebuffer operations */
@@ -176,8 +182,15 @@ void GPU_framebuffer_clear(GPUFrameBuffer *fb,
void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4]);
void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data);
-void GPU_framebuffer_read_color(
- GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data);
+void GPU_framebuffer_read_color(GPUFrameBuffer *fb,
+ int x,
+ int y,
+ int w,
+ int h,
+ int channels,
+ int slot,
+ eGPUDataFormat format,
+ void *data);
void GPU_framebuffer_blit(GPUFrameBuffer *fb_read,
int read_slot,
@@ -195,11 +208,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
*/
GPUOffScreen *GPU_offscreen_create(
- int width, int height, int samples, bool depth, bool high_bitdepth, char err_out[256]);
+ int width, int height, bool depth, bool high_bitdepth, char err_out[256]);
void GPU_offscreen_free(GPUOffScreen *ofs);
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save);
void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore);
-void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels);
+void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels);
void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y);
int GPU_offscreen_width(const GPUOffScreen *ofs);
int GPU_offscreen_height(const GPUOffScreen *ofs);
@@ -214,6 +227,11 @@ void GPU_clear_color(float red, float green, float blue, float alpha);
void GPU_clear_depth(float depth);
void GPU_clear(eGPUFrameBufferBits flags);
+void GPU_frontbuffer_read_pixels(
+ int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data);
+
+void GPU_backbuffer_bind(eGPUBackBuffer buffer);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h
index d1db1d5f3e7..869c1fff29d 100644
--- a/source/blender/gpu/GPU_immediate.h
+++ b/source/blender/gpu/GPU_immediate.h
@@ -31,6 +31,7 @@
#include "GPU_primitive.h"
#include "GPU_shader.h"
#include "GPU_shader_interface.h"
+#include "GPU_texture.h"
#include "GPU_vertex_format.h"
#ifdef __cplusplus
@@ -41,7 +42,7 @@ extern "C" {
GPUVertFormat *immVertexFormat(void);
/** Every immBegin must have a program bound first. */
-void immBindProgram(uint32_t program, const GPUShaderInterface *);
+void immBindShader(GPUShader *shader);
/** Call after your last immEnd, or before binding another program. */
void immUnbindProgram(void);
@@ -115,6 +116,9 @@ void immUniform4fv(const char *name, const float data[4]);
void immUniformArray4fv(const char *bare_name, const float *data, int count);
void immUniformMatrix4fv(const char *name, const float data[4][4]);
+void immBindTexture(const char *name, GPUTexture *tex);
+void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state);
+
/* Convenience functions for setting "uniform vec4 color". */
/* The rgb functions have implicit alpha = 1.0. */
void immUniformColor4f(float r, float g, float b, float a);
@@ -130,7 +134,7 @@ void immUniformColor3ubvAlpha(const unsigned char rgb[3], unsigned char a);
void immUniformColor4ubv(const unsigned char rgba[4]);
/**
- * Extend #immBindProgram to use Blender’s library of built-in shader programs.
+ * Extend #immBindShader to use Blender’s library of built-in shader programs.
* Use #immUnbindProgram() when done.
*/
void immBindBuiltinProgram(eGPUBuiltinShader shader_id);
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index eeb2d2caef2..7512fac6410 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -110,6 +110,7 @@ typedef enum eGPUMatFlag {
GPU_MATFLAG_GLOSSY = (1 << 1),
GPU_MATFLAG_REFRACT = (1 << 2),
GPU_MATFLAG_SSS = (1 << 3),
+ GPU_MATFLAG_BARYCENTRIC = (1 << 4),
} eGPUMatFlag;
typedef enum eGPUBlendMode {
@@ -137,6 +138,13 @@ typedef enum eGPUMaterialStatus {
GPU_MAT_SUCCESS,
} eGPUMaterialStatus;
+typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat,
+ int options,
+ const char **vert_code,
+ const char **geom_code,
+ const char **frag_lib,
+ const char **defines);
+
GPUNodeLink *GPU_constant(const float *num);
GPUNodeLink *GPU_uniform(const float *num);
GPUNodeLink *GPU_attribute(GPUMaterial *mat, CustomDataType type, const char *name);
@@ -169,8 +177,8 @@ void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link);
void GPU_material_sss_profile_create(GPUMaterial *material,
float radii[3],
- short *falloff_type,
- float *sharpness);
+ const short *falloff_type,
+ const float *sharpness);
struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material,
int sample_len,
struct GPUTexture **tex_profile);
@@ -190,7 +198,8 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene,
const char *geom_code,
const char *frag_lib,
const char *defines,
- const char *name);
+ const char *name,
+ GPUMaterialEvalCallbackFn callback);
void GPU_material_compile(GPUMaterial *mat);
void GPU_material_free(struct ListBase *gpumaterial);
diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h
index 2899fba46e4..eabfb5d2dc3 100644
--- a/source/blender/gpu/GPU_matrix.h
+++ b/source/blender/gpu/GPU_matrix.h
@@ -102,11 +102,15 @@ struct GPUMatrixUnproject_Precalc {
float model_inverted[4][4];
float view[4];
bool is_persp;
- /** Result of 'projmat_dimensions'. */
+ /**
+ * Result of #projmat_dimensions_db.
+ * Using double precision here is important as far clipping ranges
+ * can cause divide-by-zero when using float, see: T66937.
+ */
struct {
- float xmin, xmax;
- float ymin, ymax;
- float zmin, zmax;
+ double xmin, xmax;
+ double ymin, ymax;
+ double zmin, zmax;
} dims;
};
@@ -147,6 +151,10 @@ const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3];
void GPU_matrix_bind(const struct GPUShaderInterface *);
bool GPU_matrix_dirty_get(void); /* since last bind */
+/* own working polygon offset */
+float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float dist);
+void GPU_polygon_offset(float viewdist, float dist);
+
/* Python API needs to be able to inspect the stack so errors raise exceptions
* instead of crashing. */
#ifdef USE_GPU_PY_MATRIX_API
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
index f199a748cb5..104d5ef0ddc 100644
--- a/source/blender/gpu/GPU_platform.h
+++ b/source/blender/gpu/GPU_platform.h
@@ -25,10 +25,7 @@
#define __GPU_PLATFORM_H__
#include "BLI_sys_types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "BLI_utildefines.h"
/* GPU platform support */
@@ -43,6 +40,8 @@ typedef enum eGPUDeviceType {
GPU_DEVICE_ANY = (0xff),
} eGPUDeviceType;
+ENUM_OPERATORS(eGPUDeviceType)
+
typedef enum eGPUOSType {
GPU_OS_WIN = (1 << 8),
GPU_OS_MAC = (1 << 9),
@@ -63,6 +62,10 @@ typedef enum eGPUSupportLevel {
GPU_SUPPORT_LEVEL_UNSUPPORTED,
} eGPUSupportLevel;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
eGPUSupportLevel GPU_platform_support_level(void);
const char *GPU_platform_support_level_key(void);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 0ad472113c9..b51a63a1c1f 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -87,8 +87,6 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader);
int GPU_shader_get_program(GPUShader *shader);
-void *GPU_shader_get_interface(GPUShader *shader);
-
void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface);
int GPU_shader_get_uniform(GPUShader *shader, const char *name);
@@ -144,8 +142,6 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_2D_IMAGE,
GPU_SHADER_2D_IMAGE_COLOR,
GPU_SHADER_2D_IMAGE_DESATURATE_COLOR,
- GPU_SHADER_2D_IMAGE_ALPHA_COLOR,
- GPU_SHADER_2D_IMAGE_ALPHA,
GPU_SHADER_2D_IMAGE_RECT_COLOR,
GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR,
GPU_SHADER_2D_CHECKER,
@@ -209,16 +205,6 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE,
GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE,
GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR,
- GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR,
- /**
- * Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex.
- *
- * \param alpha: uniform float
- * \param image: uniform sampler2D
- * \param texCoord: in vec2
- * \param pos: in vec3
- */
- GPU_SHADER_3D_IMAGE_MODULATE_ALPHA,
/* points */
/**
* Draw round points with a hardcoded size.
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 4daf3f8dba5..a71b44507ba 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -40,6 +40,17 @@ typedef enum eGPUFilterFunction {
GPU_LINEAR,
} eGPUFilterFunction;
+typedef enum eGPUFaceCull {
+ GPU_CULL_NONE = 0, /* Culling disabled. */
+ GPU_CULL_FRONT,
+ GPU_CULL_BACK,
+} eGPUFaceCull;
+
+typedef enum eGPUProvokingVertex {
+ GPU_VERTEX_FIRST = 0,
+ GPU_VERTEX_LAST, /* Default */
+} eGPUProvokingVertex;
+
/* Initialize
* - sets the default Blender opengl state, if in doubt, check
* the contents of this function
@@ -52,9 +63,13 @@ void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb,
eGPUBlendFunction dst_rgb,
eGPUBlendFunction src_alpha,
eGPUBlendFunction dst_alpha);
+void GPU_face_culling(eGPUFaceCull culling);
+void GPU_front_facing(bool invert);
+void GPU_provoking_vertex(eGPUProvokingVertex vert);
void GPU_depth_range(float near, float far);
void GPU_depth_test(bool enable);
bool GPU_depth_test_enabled(void);
+void GPU_scissor_test(bool enable);
void GPU_line_smooth(bool enable);
void GPU_line_width(float width);
void GPU_point_size(float size);
@@ -63,13 +78,21 @@ void GPU_program_point_size(bool enable);
void GPU_scissor(int x, int y, int width, int height);
void GPU_scissor_get_f(float coords[4]);
void GPU_scissor_get_i(int coords[4]);
+void GPU_viewport(int x, int y, int width, int height);
void GPU_viewport_size_get_f(float coords[4]);
void GPU_viewport_size_get_i(int coords[4]);
+void GPU_color_mask(bool r, bool g, bool b, bool a);
+void GPU_depth_mask(bool depth);
+bool GPU_depth_mask_get(void);
+void GPU_stencil_mask(uint stencil);
+void GPU_unpack_row_length_set(uint len);
+void GPU_clip_distances(int enabled_len);
+bool GPU_mipmap_enabled(void);
void GPU_flush(void);
void GPU_finish(void);
-void GPU_logic_op_invert_set(bool enable);
+void GPU_logic_op_xor_set(bool enable);
/* Attribute push & pop. */
typedef enum eGPUAttrMask {
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index a13f61177e6..1fbcfd41dec 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -24,11 +24,9 @@
#ifndef __GPU_TEXTURE_H__
#define __GPU_TEXTURE_H__
-#include "GPU_state.h"
+#include "BLI_utildefines.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "GPU_state.h"
struct GPUVertBuf;
struct ImBuf;
@@ -46,7 +44,6 @@ typedef struct GPUTexture GPUTexture;
* - Internally used by textures.
* - All states are created at startup to avoid runtime costs.
*/
-
typedef enum eGPUSamplerState {
GPU_SAMPLER_FILTER = (1 << 0),
GPU_SAMPLER_MIPMAP = (1 << 1),
@@ -60,6 +57,12 @@ typedef enum eGPUSamplerState {
GPU_SAMPLER_MAX = (1 << 8),
} eGPUSamplerState;
+ENUM_OPERATORS(eGPUSamplerState)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define GPU_SAMPLER_DEFAULT GPU_SAMPLER_FILTER
#define GPU_SAMPLER_REPEAT (GPU_SAMPLER_REPEAT_S | GPU_SAMPLER_REPEAT_T | GPU_SAMPLER_REPEAT_R)
@@ -120,7 +123,6 @@ typedef enum eGPUTextureFormat {
#if 0
GPU_RGB10_A2,
GPU_RGB10_A2UI,
- GPU_SRGB8_A8,
#endif
GPU_R11F_G11F_B10F,
GPU_DEPTH32F_STENCIL8,
@@ -149,7 +151,13 @@ typedef enum eGPUTextureFormat {
GPU_R8_SNORM,
#endif
-/* Special formats texture only */
+ /* Special formats texture only */
+ GPU_SRGB8_A8_DXT1,
+ GPU_SRGB8_A8_DXT3,
+ GPU_SRGB8_A8_DXT5,
+ GPU_RGBA8_DXT1,
+ GPU_RGBA8_DXT3,
+ GPU_RGBA8_DXT5,
#if 0
GPU_SRGB8,
GPU_RGB9_E5,
@@ -222,17 +230,10 @@ GPUTexture *GPU_texture_create_cube_array(
GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert);
GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer);
-GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode);
-GPUTexture *GPU_texture_from_blender(struct Image *ima,
- struct ImageUser *iuser,
- struct ImBuf *ibuf,
- int textarget);
+GPUTexture *GPU_texture_create_compressed(
+ int w, int h, int miplen, eGPUTextureFormat format, const void *data);
-/* movie clip drawing */
-GPUTexture *GPU_texture_from_movieclip(struct MovieClip *clip,
- struct MovieClipUser *cuser,
- int textarget);
-void GPU_free_texture_movieclip(struct MovieClip *clip);
+GPUTexture *GPU_texture_create_error(int dimension, bool array);
void GPU_texture_add_mipmap(GPUTexture *tex,
eGPUDataFormat gpu_data_format,
@@ -268,14 +269,12 @@ void GPU_texture_unbind_all(void);
void GPU_texture_copy(GPUTexture *dst, GPUTexture *src);
void GPU_texture_generate_mipmap(GPUTexture *tex);
+void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso);
void GPU_texture_compare_mode(GPUTexture *tex, bool use_compare);
void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter);
void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter);
void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp);
-void GPU_texture_filters(GPUTexture *tex,
- eGPUFilterFunction min_filter,
- eGPUFilterFunction mag_filter);
-void GPU_texture_swizzle_channel_auto(GPUTexture *tex, int channels);
+void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]);
void GPU_texture_attach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment);
int GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb);
@@ -298,6 +297,8 @@ int GPU_texture_opengl_bindcode(const GPUTexture *tex);
void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size);
+void GPU_sampler_icon_bind(int number);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h
index b221ae035d3..6862c1d960d 100644
--- a/source/blender/gpu/GPU_uniformbuffer.h
+++ b/source/blender/gpu/GPU_uniformbuffer.h
@@ -42,8 +42,7 @@ void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_);
void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number);
void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo);
-
-int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo);
+void GPU_uniformbuffer_unbind_all(void);
bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo);
bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo);
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index fe45ec7b78b..d1693852c1a 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -59,10 +59,10 @@ typedef struct GPUVertBuf {
/** 0 indicates not yet allocated. */
uint32_t vbo_id;
/** Usage hint for GL optimisation. */
- uint usage : 2;
+ GPUUsageType usage;
/** Data has been touched and need to be reuploaded to GPU. */
- uint dirty : 1;
- unsigned char *data; /* NULL indicates data in VRAM (unmapped) */
+ bool dirty;
+ uchar *data; /* NULL indicates data in VRAM (unmapped) */
} GPUVertBuf;
GPUVertBuf *GPU_vertbuf_create(GPUUsageType);
@@ -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..cf0f52e3950 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -42,7 +42,7 @@ extern "C" {
#define GPU_MAX_SAFE_ATTR_NAME 12
typedef enum {
- GPU_COMP_I8,
+ GPU_COMP_I8 = 0,
GPU_COMP_U8,
GPU_COMP_I16,
GPU_COMP_U16,
@@ -52,17 +52,21 @@ typedef enum {
GPU_COMP_F32,
GPU_COMP_I10,
+ /* Warning! adjust GPUVertAttr if changing. */
} GPUVertCompType;
typedef enum {
- GPU_FETCH_FLOAT,
+ GPU_FETCH_FLOAT = 0,
GPU_FETCH_INT,
GPU_FETCH_INT_TO_FLOAT_UNIT, /* 127 (ubyte) -> 0.5 (and so on for other int types) */
GPU_FETCH_INT_TO_FLOAT, /* 127 (any int type) -> 127.0 */
+ /* Warning! adjust GPUVertAttr if changing. */
} GPUVertFetchMode;
typedef struct GPUVertAttr {
+ /* GPUVertFetchMode */
uint fetch_mode : 2;
+ /* GPUVertCompType */
uint comp_type : 3;
/* 1 to 4 or 8 or 12 or 16 */
uint comp_len : 5;
@@ -72,8 +76,6 @@ typedef struct GPUVertAttr {
uint offset : 11;
/* up to GPU_VERT_ATTR_MAX_NAMES */
uint name_len : 3;
- uint gl_comp_type;
- /* -- 8 Bytes -- */
uchar names[GPU_VERT_ATTR_MAX_NAMES];
} GPUVertAttr;
@@ -124,6 +126,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_attr_binding.c b/source/blender/gpu/intern/gpu_attr_binding.cc
index 6cb60884620..6cb60884620 100644
--- a/source/blender/gpu/intern/gpu_attr_binding.c
+++ b/source/blender/gpu/intern/gpu_attr_binding.cc
diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h
index 301ec3333dd..7df403a3ea5 100644
--- a/source/blender/gpu/intern/gpu_attr_binding_private.h
+++ b/source/blender/gpu/intern/gpu_attr_binding_private.h
@@ -29,6 +29,11 @@
#include "GPU_shader_interface.h"
#include "GPU_vertex_format.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* TODO(fclem) remove, use shaderface directly. */
void AttrBinding_clear(GPUAttrBinding *binding);
void get_attr_locations(const GPUVertFormat *format,
@@ -36,4 +41,8 @@ void get_attr_locations(const GPUVertFormat *format,
const GPUShaderInterface *shaderface);
uint read_attr_location(const GPUAttrBinding *binding, uint a_idx);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __GPU_ATTR_BINDING_PRIVATE_H__ */
diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.cc
index 5f77f13c135..9f9adcacfa6 100644
--- a/source/blender/gpu/intern/gpu_batch.c
+++ b/source/blender/gpu/intern/gpu_batch.cc
@@ -37,6 +37,7 @@
#include "gpu_context_private.h"
#include "gpu_primitive_private.h"
#include "gpu_shader_private.h"
+#include "gpu_vertex_format_private.h"
#include <limits.h>
#include <stdlib.h>
@@ -89,7 +90,7 @@ GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type,
GPUIndexBuf *elem,
uint owns_flag)
{
- GPUBatch *batch = MEM_callocN(sizeof(GPUBatch), "GPUBatch");
+ GPUBatch *batch = (GPUBatch *)MEM_callocN(sizeof(GPUBatch), "GPUBatch");
GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag);
return batch;
}
@@ -327,10 +328,10 @@ static GLuint batch_vao_get(GPUBatch *batch)
}
/* Init dynamic arrays and let the branch below set the values. */
batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT;
- batch->dynamic_vaos.interfaces = MEM_callocN(
+ batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN(
batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces");
- batch->dynamic_vaos.vao_ids = MEM_callocN(batch->dynamic_vaos.count * sizeof(GLuint),
- "dyn vaos ids");
+ batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(
+ batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids");
}
}
@@ -346,11 +347,11 @@ static GLuint batch_vao_get(GPUBatch *batch)
/* Not enough place, realloc the array. */
i = batch->dynamic_vaos.count;
batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT;
- batch->dynamic_vaos.interfaces = MEM_recallocN((void *)batch->dynamic_vaos.interfaces,
- sizeof(GPUShaderInterface *) *
- batch->dynamic_vaos.count);
- batch->dynamic_vaos.vao_ids = MEM_recallocN(batch->dynamic_vaos.vao_ids,
- sizeof(GLuint) * batch->dynamic_vaos.count);
+ batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN(
+ (void *)batch->dynamic_vaos.interfaces,
+ sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count);
+ batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(
+ batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count);
}
batch->dynamic_vaos.interfaces[i] = batch->interface;
batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc();
@@ -370,22 +371,20 @@ static GLuint batch_vao_get(GPUBatch *batch)
return new_vao;
}
-void GPU_batch_program_set_no_use(GPUBatch *batch,
- uint32_t program,
- const GPUShaderInterface *shaderface)
+void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader)
{
#if TRUST_NO_ONE
- assert(glIsProgram(program));
+ assert(glIsProgram(shader->program));
assert(batch->program_in_use == 0);
#endif
- batch->interface = shaderface;
- batch->program = program;
+ batch->interface = shader->interface;
+ batch->program = shader->program;
batch->vao_id = batch_vao_get(batch);
}
-void GPU_batch_program_set(GPUBatch *batch, uint32_t program, const GPUShaderInterface *shaderface)
+void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader)
{
- GPU_batch_program_set_no_use(batch, program, shaderface);
+ GPU_batch_set_shader_no_bind(batch, shader);
GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */
}
@@ -440,6 +439,7 @@ static void create_bindings(GPUVertBuf *verts,
}
const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride;
+ const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type));
for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
@@ -452,19 +452,13 @@ static void create_bindings(GPUVertBuf *verts,
*attr_mask &= ~(1 << input->location);
if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) {
-#if TRUST_NO_ONE
- assert(a->fetch_mode == GPU_FETCH_FLOAT);
- assert(a->gl_comp_type == GL_FLOAT);
-#endif
+ BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT);
+ BLI_assert(a->comp_type == GPU_COMP_F32);
for (int i = 0; i < a->comp_len / 4; i++) {
glEnableVertexAttribArray(input->location + i);
glVertexAttribDivisor(input->location + i, (use_instancing) ? 1 : 0);
- glVertexAttribPointer(input->location + i,
- 4,
- a->gl_comp_type,
- GL_FALSE,
- stride,
- (const GLubyte *)pointer + i * 16);
+ glVertexAttribPointer(
+ input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16);
}
}
else {
@@ -474,15 +468,13 @@ static void create_bindings(GPUVertBuf *verts,
switch (a->fetch_mode) {
case GPU_FETCH_FLOAT:
case GPU_FETCH_INT_TO_FLOAT:
- glVertexAttribPointer(
- input->location, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer);
+ glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer);
break;
case GPU_FETCH_INT_TO_FLOAT_UNIT:
- glVertexAttribPointer(
- input->location, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer);
+ glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer);
break;
case GPU_FETCH_INT:
- glVertexAttribIPointer(input->location, a->comp_len, a->gl_comp_type, stride, pointer);
+ glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer);
break;
}
}
@@ -839,7 +831,7 @@ struct GPUDrawList {
GPUDrawList *GPU_draw_list_create(int length)
{
- GPUDrawList *list = MEM_callocN(sizeof(GPUDrawList), "GPUDrawList");
+ GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList");
/* Alloc the biggest possible command list which is indexed. */
list->buffer_size = sizeof(GPUDrawCommandIndexed) * length;
if (USE_MULTI_DRAW_INDIRECT) {
@@ -848,7 +840,7 @@ GPUDrawList *GPU_draw_list_create(int length)
glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW);
}
else {
- list->commands = MEM_mallocN(list->buffer_size, "GPUDrawList data");
+ list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data");
}
return list;
}
@@ -880,7 +872,7 @@ void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch)
list->cmd_offset = 0;
}
GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
- list->commands = glMapBufferRange(
+ list->commands = (GPUDrawCommand *)glMapBufferRange(
GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags);
}
}
@@ -989,17 +981,12 @@ void GPU_draw_list_submit(GPUDrawList *list)
/** \name Utilities
* \{ */
-void GPU_batch_program_set_shader(GPUBatch *batch, GPUShader *shader)
-{
- GPU_batch_program_set(batch, shader->program, shader->interface);
-}
-
void GPU_batch_program_set_builtin_with_config(GPUBatch *batch,
eGPUBuiltinShader shader_id,
eGPUShaderConfig sh_cfg)
{
GPUShader *shader = GPU_shader_get_builtin_shader_with_config(shader_id, sh_cfg);
- GPU_batch_program_set(batch, shader->program, shader->interface);
+ GPU_batch_set_shader(batch, shader);
}
void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id)
@@ -1012,10 +999,7 @@ void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id)
* DO NOT DRAW WITH THE BATCH BEFORE CALLING immUnbindProgram. */
void GPU_batch_program_set_imm_shader(GPUBatch *batch)
{
- GLuint program;
- GPUShaderInterface *interface;
- immGetProgram(&program, &interface);
- GPU_batch_program_set(batch, program, interface);
+ GPU_batch_set_shader(batch, immGetShader());
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index cef90d57ef5..10d5a860f6a 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -34,9 +34,11 @@
#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"
+#include "DNA_userdef_types.h"
#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
@@ -227,12 +229,14 @@ 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 && U.experimental.use_sculpt_vertex_colors)) &&
+ (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
bool empty_mask = true;
bool default_face_set = true;
@@ -244,8 +248,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 +316,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 && U.experimental.use_sculpt_vertex_colors) {
+ 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. */
@@ -440,7 +452,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly,
static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers,
SubdivCCG *UNUSED(subdiv_ccg),
const int *UNUSED(face_sets),
- int *grid_indices,
+ const int *grid_indices,
uint visible_quad_len,
int totgrid,
int gridsize)
@@ -571,7 +583,7 @@ static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers,
void GPU_pbvh_grid_buffers_update_free(GPU_PBVH_Buffers *buffers,
const struct DMFlagMat *grid_flag_mats,
- int *grid_indices)
+ const int *grid_indices)
{
const bool smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH;
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index c1e7933d7ba..e15e4e08159 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -41,7 +41,6 @@
#include "BKE_material.h"
#include "GPU_extensions.h"
-#include "GPU_glew.h"
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_uniformbuffer.h"
@@ -56,8 +55,8 @@
#include <stdarg.h>
#include <string.h>
+extern char datatoc_gpu_shader_codegen_lib_glsl[];
extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
/* -------------------- GPUPass Cache ------------------ */
/**
@@ -282,18 +281,15 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin)
static void codegen_set_unique_ids(GPUNodeGraph *graph)
{
- GPUNode *node;
- GPUInput *input;
- GPUOutput *output;
int id = 1;
- for (node = graph->nodes.first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
+ LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
/* set id for unique names of uniform variables */
input->id = id++;
}
- for (output = node->outputs.first; output; output = output->next) {
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
/* set id for unique names of tmp variables storing output */
output->id = id++;
}
@@ -307,17 +303,10 @@ static int codegen_process_uniforms_functions(GPUMaterial *material,
DynStr *ds,
GPUNodeGraph *graph)
{
- GPUNode *node;
- GPUInput *input;
const char *name;
int builtins = 0;
ListBase ubo_inputs = {NULL, NULL};
- /* Attributes */
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- BLI_dynstr_appendf(ds, "in %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
- }
-
/* Textures */
LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) {
if (tex->colorband) {
@@ -339,8 +328,9 @@ static int codegen_process_uniforms_functions(GPUMaterial *material,
}
/* Print other uniforms */
- for (node = graph->nodes.first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
+
+ LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
if (input->source == GPU_SOURCE_BUILTIN) {
/* only define each builtin uniform/varying once */
if (!(builtins & input->builtin)) {
@@ -382,8 +372,8 @@ static int codegen_process_uniforms_functions(GPUMaterial *material,
BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME);
LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) {
- input = link->data;
- BLI_dynstr_appendf(ds, "\t%s unf%d;\n", gpu_data_type_to_string(input->type), input->id);
+ GPUInput *input = (GPUInput *)(link->data);
+ BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id);
}
BLI_dynstr_append(ds, "};\n");
BLI_freelistN(&ubo_inputs);
@@ -396,34 +386,26 @@ static int codegen_process_uniforms_functions(GPUMaterial *material,
static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph)
{
- GPUNode *node;
- GPUOutput *output;
-
- for (node = graph->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
/* declare temporary variables for node output storage */
- for (output = node->outputs.first; output; output = output->next) {
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
if (output->type == GPU_CLOSURE) {
- BLI_dynstr_appendf(ds, "\tClosure tmp%d;\n", output->id);
+ BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id);
}
else {
- BLI_dynstr_appendf(ds, "\t%s tmp%d;\n", gpu_data_type_to_string(output->type), output->id);
+ BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id);
}
}
}
-
BLI_dynstr_append(ds, "\n");
}
static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *finaloutput)
{
- GPUNode *node;
- GPUInput *input;
- GPUOutput *output;
-
- for (node = graph->nodes.first; node; node = node->next) {
- BLI_dynstr_appendf(ds, "\t%s(", node->name);
+ LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
+ BLI_dynstr_appendf(ds, " %s(", node->name);
- for (input = node->inputs.first; input; input = input->next) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
if (input->source == GPU_SOURCE_TEX) {
BLI_dynstr_append(ds, input->texture->sampler_name);
}
@@ -504,7 +486,7 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f
BLI_dynstr_append(ds, ", ");
}
- for (output = node->outputs.first; output; output = output->next) {
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
BLI_dynstr_appendf(ds, "tmp%d", output->id);
if (output->next) {
BLI_dynstr_append(ds, ", ");
@@ -514,21 +496,24 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f
BLI_dynstr_append(ds, ");\n");
}
- BLI_dynstr_appendf(ds, "\n\treturn tmp%d", finaloutput->id);
- BLI_dynstr_append(ds, ";\n");
+ BLI_dynstr_appendf(ds, "\n return tmp%d;\n", finaloutput->id);
}
-static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph)
+static char *code_generate_fragment(GPUMaterial *material,
+ GPUNodeGraph *graph,
+ const char *interface_str)
{
DynStr *ds = BLI_dynstr_new();
char *code;
int builtins;
-#if 0
- BLI_dynstr_append(ds, FUNCTION_PROTOTYPES);
-#endif
-
codegen_set_unique_ids(graph);
+
+ /* Attributes, Shader stage interface. */
+ if (interface_str) {
+ BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str);
+ }
+
builtins = codegen_process_uniforms_functions(material, ds, graph);
if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) {
@@ -536,73 +521,61 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph)
}
if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "in vec2 barycentricTexCo;\n");
- }
-
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "flat in vec3 barycentricDist;\n");
+ BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
}
BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n");
if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds,
- "\tvec2 barytexco = vec2((fract(barycentricTexCo.y) != 0.0)\n"
- "\t ? barycentricTexCo.x\n"
- "\t : 1.0 - barycentricTexCo.x,\n"
- "\t 0.0);\n");
- BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, "\tvec2 barytexco = barycentricTexCo;\n");
- BLI_dynstr_append(ds, "#endif\n");
+ BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n");
}
/* TODO(fclem) get rid of that. */
if (builtins & GPU_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "\t#define viewmat ViewMatrix\n");
+ BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n");
}
if (builtins & GPU_CAMERA_TEXCO_FACTORS) {
- BLI_dynstr_append(ds, "\t#define camtexfac CameraTexCoFactors\n");
+ BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n");
}
if (builtins & GPU_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, "\t#define objmat ModelMatrix\n");
+ BLI_dynstr_append(ds, " #define objmat ModelMatrix\n");
}
if (builtins & GPU_INVERSE_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, "\t#define objinv ModelMatrixInverse\n");
+ BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n");
}
if (builtins & GPU_INVERSE_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "\t#define viewinv ViewMatrixInverse\n");
+ BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n");
}
if (builtins & GPU_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "\t#define localtoviewmat (ViewMatrix * ModelMatrix)\n");
+ BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n");
}
if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
BLI_dynstr_append(ds,
- "\t#define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n");
+ " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n");
}
if (builtins & GPU_VIEW_NORMAL) {
BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, "\tvec3 n;\n");
- BLI_dynstr_append(ds, "\tworld_normals_get(n);\n");
- BLI_dynstr_append(ds, "\tvec3 facingnormal = transform_direction(ViewMatrix, n);\n");
+ BLI_dynstr_append(ds, " vec3 n;\n");
+ BLI_dynstr_append(ds, " world_normals_get(n);\n");
+ BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n");
BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n");
+ BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n");
BLI_dynstr_append(ds, "#endif\n");
}
if (builtins & GPU_WORLD_NORMAL) {
- BLI_dynstr_append(ds, "\tvec3 facingwnormal;\n");
+ BLI_dynstr_append(ds, " vec3 facingwnormal;\n");
if (builtins & GPU_VIEW_NORMAL) {
BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, "\tfacingwnormal = n;\n");
+ BLI_dynstr_append(ds, " facingwnormal = n;\n");
BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n");
+ BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n");
BLI_dynstr_append(ds, "#endif\n");
}
else {
- BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n");
+ BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n");
}
}
if (builtins & GPU_VIEW_POSITION) {
- BLI_dynstr_append(ds, "\t#define viewposition viewPosition\n");
+ BLI_dynstr_append(ds, " #define viewposition viewPosition\n");
}
codegen_declare_tmps(ds, graph);
@@ -610,21 +583,6 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph)
BLI_dynstr_append(ds, "}\n");
- /* XXX This cannot go into gpu_shader_material.glsl because main()
- * would be parsed and generate error */
- /* Old glsl mode compat. */
- /* TODO(fclem) This is only used by world shader now. get rid of it? */
- BLI_dynstr_append(ds, "#ifndef NODETREE_EXEC\n");
- BLI_dynstr_append(ds, "out vec4 fragColor;\n");
- BLI_dynstr_append(ds, "void main()\n");
- BLI_dynstr_append(ds, "{\n");
- BLI_dynstr_append(ds, "\tClosure cl = nodetree_exec();\n");
- BLI_dynstr_append(ds,
- "\tfragColor = vec4(cl.radiance, "
- "saturate(1.0 - avg(cl.transmittance)));\n");
- BLI_dynstr_append(ds, "}\n");
- BLI_dynstr_append(ds, "#endif\n\n");
-
/* create shader */
code = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
@@ -649,6 +607,8 @@ static const char *attr_prefix_get(CustomDataType type)
return "t";
case CD_MCOL:
return "c";
+ case CD_PROP_COLOR:
+ return "c";
case CD_AUTO_FROM_NAME:
return "a";
default:
@@ -657,23 +617,48 @@ static const char *attr_prefix_get(CustomDataType type)
}
}
-static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bool use_geom)
+/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */
+static char *code_generate_interface(GPUNodeGraph *graph, int builtins)
{
+ if (BLI_listbase_is_empty(&graph->attributes) &&
+ (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) {
+ return NULL;
+ }
+
DynStr *ds = BLI_dynstr_new();
- GPUNode *node;
- GPUInput *input;
- char *code;
- int builtins = 0;
- /* Hairs uv and col attributes are passed by bufferTextures. */
- BLI_dynstr_append(ds,
- "#ifdef HAIR_SHADER\n"
- "#define DEFINE_ATTR(type, attr) uniform samplerBuffer attr\n"
- "#else\n"
- "#define DEFINE_ATTR(type, attr) in type attr\n"
- "#endif\n");
+ BLI_dynstr_append(ds, "\n");
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
+ BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
+ }
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n");
+ }
+ if (builtins & GPU_BARYCENTRIC_DIST) {
+ BLI_dynstr_append(ds, "vec3 barycentricDist;\n");
+ }
+
+ char *code = BLI_dynstr_get_cstring(ds);
+
+ BLI_dynstr_free(ds);
+
+ return code;
+}
+
+static char *code_generate_vertex(GPUNodeGraph *graph,
+ const char *interface_str,
+ const char *vert_code,
+ int builtins)
+{
+ DynStr *ds = BLI_dynstr_new();
+
+ BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
+
+ /* Inputs */
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
+ const char *type_str = gpu_data_type_to_string(attr->gputype);
+ const char *prefix = attr_prefix_get(attr->type);
/* XXX FIXME : see notes in mesh_render_data_create() */
/* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
if (attr->type == CD_ORCO) {
@@ -682,188 +667,58 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo
BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
}
else if (attr->name[0] == '\0') {
- BLI_dynstr_appendf(ds,
- "DEFINE_ATTR(%s, %s);\n",
- gpu_data_type_to_string(attr->gputype),
- attr_prefix_get(attr->type));
- BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, attr_prefix_get(attr->type));
+ BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix);
+ BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix);
}
else {
char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- BLI_dynstr_appendf(ds,
- "DEFINE_ATTR(%s, %s%s);\n",
- gpu_data_type_to_string(attr->gputype),
- attr_prefix_get(attr->type),
- attr_safe_name);
- BLI_dynstr_appendf(
- ds, "#define att%d %s%s\n", attr->id, attr_prefix_get(attr->type), attr_safe_name);
+ BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name);
+ BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name);
}
- BLI_dynstr_appendf(ds,
- "out %s var%d%s;\n",
- gpu_data_type_to_string(attr->gputype),
- attr->id,
- use_geom ? "g" : "");
- }
-
- for (node = graph->nodes.first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
- if (input->source == GPU_SOURCE_BUILTIN) {
- builtins |= input->builtin;
- }
- }
- }
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_appendf(ds, "out vec2 barycentricTexCo%s;\n", use_geom ? "g" : "");
- BLI_dynstr_append(ds, "#endif\n");
}
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "out vec3 barycentricPosg;\n");
+ /* Outputs interface */
+ if (interface_str) {
+ BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str);
}
- BLI_dynstr_append(ds, "\n#define USE_ATTR\n");
-
- /* Prototype, defined later (this is because of matrices definition). */
- BLI_dynstr_append(ds, "void pass_attr(in vec3 position);\n");
-
- BLI_dynstr_append(ds, "\n");
-
- if (use_geom) {
- /* XXX HACK: Eevee specific. */
- char *vert_new, *vert_new2;
- vert_new = BLI_str_replaceN(vert_code, "worldPosition", "worldPositiong");
- vert_new2 = vert_new;
- vert_new = BLI_str_replaceN(vert_new2, "viewPosition", "viewPositiong");
- MEM_freeN(vert_new2);
- vert_new2 = vert_new;
- vert_new = BLI_str_replaceN(vert_new2, "worldNormal", "worldNormalg");
- MEM_freeN(vert_new2);
- vert_new2 = vert_new;
- vert_new = BLI_str_replaceN(vert_new2, "viewNormal", "viewNormalg");
- MEM_freeN(vert_new2);
-
- BLI_dynstr_append(ds, vert_new);
-
- MEM_freeN(vert_new);
- }
- else {
- BLI_dynstr_append(ds, vert_code);
- }
+ /* Prototype. Needed for hair functions. */
+ BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n");
+ BLI_dynstr_append(ds, "#define USE_ATTR\n\n");
+ BLI_dynstr_append(ds, vert_code);
BLI_dynstr_append(ds, "\n");
- BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n");
-
- /* Prototype because defined later. */
- BLI_dynstr_append(ds,
- "vec2 hair_get_customdata_vec2(const samplerBuffer);\n"
- "vec3 hair_get_customdata_vec3(const samplerBuffer);\n"
- "vec4 hair_get_customdata_vec4(const samplerBuffer);\n"
- "vec3 hair_get_strand_pos(void);\n"
- "int hair_get_base_id(void);\n"
- "\n");
-
- BLI_dynstr_append(ds, "void pass_attr(in vec3 position) {\n");
-
- BLI_dynstr_append(ds, use_geom ? "\tPASS_RESOURCE_ID_GEOM\n" : "\tPASS_RESOURCE_ID\n");
-
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- /* To match cycles without breaking into individual segment we encode if we need to invert
- * the first component into the second component. We invert if the barycentricTexCo.y
- * is NOT 0.0 or 1.0. */
- BLI_dynstr_append(ds, "\tint _base_id = hair_get_base_id();\n");
- BLI_dynstr_appendf(
- ds, "\tbarycentricTexCo%s.x = float((_base_id %% 2) == 1);\n", use_geom ? "g" : "");
- BLI_dynstr_appendf(
- ds, "\tbarycentricTexCo%s.y = float(((_base_id %% 4) %% 3) > 0);\n", use_geom ? "g" : "");
- }
-
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "\tbarycentricPosg = position;\n");
- }
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- if (attr->type == CD_TANGENT) {
- /* Not supported by hairs */
- BLI_dynstr_appendf(ds, "\tvar%d%s = vec4(0.0);\n", attr->id, use_geom ? "g" : "");
- }
- else if (attr->type == CD_ORCO) {
- BLI_dynstr_appendf(ds,
- "\tvar%d%s = OrcoTexCoFactors[0].xyz + (ModelMatrixInverse * "
- "vec4(hair_get_strand_pos(), 1.0)).xyz * OrcoTexCoFactors[1].xyz;\n",
- attr->id,
- use_geom ? "g" : "");
- /* TODO: fix ORCO with modifiers. */
- }
- else {
- BLI_dynstr_appendf(ds,
- "\tvar%d%s = hair_get_customdata_%s(att%d);\n",
- attr->id,
- use_geom ? "g" : "",
- gpu_data_type_to_string(attr->gputype),
- attr->id);
- }
- }
-
- BLI_dynstr_append(ds, "#else /* MESH_SHADER */\n");
+ BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n");
/* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID
* for MESH_SHADER because of indexed drawing. In this case a
* geometry shader is needed. */
-
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n");
+ }
if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "\tbarycentricPosg = (ModelMatrix * vec4(position, 1.0)).xyz;\n");
+ BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n");
}
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
if (attr->type == CD_TANGENT) { /* silly exception */
- BLI_dynstr_appendf(ds,
- "\tvar%d%s.xyz = transpose(mat3(ModelMatrixInverse)) * att%d.xyz;\n",
- attr->id,
- use_geom ? "g" : "",
- attr->id);
- BLI_dynstr_appendf(ds, "\tvar%d%s.w = att%d.w;\n", attr->id, use_geom ? "g" : "", attr->id);
- /* Normalize only if vector is not null. */
- BLI_dynstr_appendf(ds,
- "\tfloat lvar%d = dot(var%d%s.xyz, var%d%s.xyz);\n",
- attr->id,
- attr->id,
- use_geom ? "g" : "",
- attr->id,
- use_geom ? "g" : "");
- BLI_dynstr_appendf(ds,
- "\tvar%d%s.xyz *= (lvar%d > 0.0) ? inversesqrt(lvar%d) : 1.0;\n",
- attr->id,
- use_geom ? "g" : "",
- attr->id,
- attr->id);
+ BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id);
}
else if (attr->type == CD_ORCO) {
- BLI_dynstr_appendf(ds,
- "\tvar%d%s = OrcoTexCoFactors[0].xyz + position *"
- " OrcoTexCoFactors[1].xyz;\n",
- attr->id,
- use_geom ? "g" : "");
- /* See mesh_create_loop_orco() for explanation. */
- BLI_dynstr_appendf(ds,
- "\tif (orco.w == 0.0) { var%d%s = orco.xyz * 0.5 + 0.5; }\n",
- attr->id,
- use_geom ? "g" : "");
+ BLI_dynstr_appendf(
+ ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id);
}
else {
- BLI_dynstr_appendf(ds, "\tvar%d%s = att%d;\n", attr->id, use_geom ? "g" : "", attr->id);
+ const char *type_str = gpu_data_type_to_string(attr->gputype);
+ BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id);
}
}
- BLI_dynstr_append(ds, "#endif /* HAIR_SHADER */\n");
BLI_dynstr_append(ds, "}\n");
- code = BLI_dynstr_get_cstring(ds);
+ char *code = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
@@ -877,146 +732,46 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo
}
static char *code_generate_geometry(GPUNodeGraph *graph,
+ const char *interface_str,
const char *geom_code,
- const char *defines)
+ int builtins)
{
- DynStr *ds = BLI_dynstr_new();
- GPUNode *node;
- GPUInput *input;
- char *code;
- int builtins = 0;
-
- /* XXX we should not make specific eevee cases here. */
- bool is_hair_shader = (strstr(defines, "HAIR_SHADER") != NULL);
-
- /* Create prototype because attributes cannot be declared before layout. */
- BLI_dynstr_append(ds, "void pass_attr(in int vert);\n");
- BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2);\n");
- BLI_dynstr_append(ds, "#define USE_ATTR\n");
-
- /* Generate varying declarations. */
- for (node = graph->nodes.first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
- if (input->source == GPU_SOURCE_BUILTIN) {
- builtins |= input->builtin;
- }
- }
- }
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- BLI_dynstr_appendf(ds, "in %s var%dg[];\n", gpu_data_type_to_string(attr->gputype), attr->id);
- BLI_dynstr_appendf(ds, "out %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
+ if (!geom_code) {
+ return NULL;
}
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, "in vec2 barycentricTexCog[];\n");
- BLI_dynstr_append(ds, "#endif\n");
-
- BLI_dynstr_append(ds, "out vec2 barycentricTexCo;\n");
- }
+ DynStr *ds = BLI_dynstr_new();
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "in vec3 barycentricPosg[];\n");
- BLI_dynstr_append(ds, "flat out vec3 barycentricDist;\n");
+ /* Attributes, Shader interface; */
+ if (interface_str) {
+ BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str);
+ BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str);
}
- if (geom_code == NULL) {
- /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used.
- * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */
- if ((builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0 || is_hair_shader) {
- /* Early out */
- BLI_dynstr_free(ds);
- return NULL;
- }
- else {
- /* Force geom shader usage */
- /* TODO put in external file. */
- BLI_dynstr_append(ds, "layout(triangles) in;\n");
- BLI_dynstr_append(ds, "layout(triangle_strip, max_vertices=3) out;\n");
-
- BLI_dynstr_append(ds, "in vec3 worldPositiong[];\n");
- BLI_dynstr_append(ds, "in vec3 viewPositiong[];\n");
- BLI_dynstr_append(ds, "in vec3 worldNormalg[];\n");
- BLI_dynstr_append(ds, "in vec3 viewNormalg[];\n");
-
- BLI_dynstr_append(ds, "out vec3 worldPosition;\n");
- BLI_dynstr_append(ds, "out vec3 viewPosition;\n");
- BLI_dynstr_append(ds, "out vec3 worldNormal;\n");
- BLI_dynstr_append(ds, "out vec3 viewNormal;\n");
-
- BLI_dynstr_append(ds, datatoc_common_view_lib_glsl);
-
- BLI_dynstr_append(ds, "void main(){\n");
-
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds,
- "\tcalc_barycentric_distances(barycentricPosg[0], barycentricPosg[1], "
- "barycentricPosg[2]);\n");
- }
-
- for (int i = 0; i < 3; i++) {
- BLI_dynstr_appendf(ds, "\tgl_Position = gl_in[%d].gl_Position;\n", i);
- BLI_dynstr_appendf(ds, "\tgl_ClipDistance[0] = gl_in[%d].gl_ClipDistance[0];\n", i);
- BLI_dynstr_appendf(ds, "\tpass_attr(%d);\n", i);
- BLI_dynstr_append(ds, "\tEmitVertex();\n");
- }
- BLI_dynstr_append(ds, "}\n");
- }
- }
- else {
- BLI_dynstr_append(ds, geom_code);
- }
+ BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) {\n");
- BLI_dynstr_append(ds, "\tvec3 edge21 = pos2 - pos1;\n");
- BLI_dynstr_append(ds, "\tvec3 edge10 = pos1 - pos0;\n");
- BLI_dynstr_append(ds, "\tvec3 edge02 = pos0 - pos2;\n");
- BLI_dynstr_append(ds, "\tvec3 d21 = normalize(edge21);\n");
- BLI_dynstr_append(ds, "\tvec3 d10 = normalize(edge10);\n");
- BLI_dynstr_append(ds, "\tvec3 d02 = normalize(edge02);\n");
-
- BLI_dynstr_append(ds, "\tfloat d = dot(d21, edge02);\n");
- BLI_dynstr_append(ds, "\tbarycentricDist.x = sqrt(dot(edge02, edge02) - d * d);\n");
- BLI_dynstr_append(ds, "\td = dot(d02, edge10);\n");
- BLI_dynstr_append(ds, "\tbarycentricDist.y = sqrt(dot(edge10, edge10) - d * d);\n");
- BLI_dynstr_append(ds, "\td = dot(d10, edge21);\n");
- BLI_dynstr_append(ds, "\tbarycentricDist.z = sqrt(dot(edge21, edge21) - d * d);\n");
- BLI_dynstr_append(ds, "}\n");
+ /* geom_code should do something with this, but may not. */
+ BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n");
}
- BLI_dynstr_append(ds, "RESOURCE_ID_VARYING\n");
-
/* Generate varying assignments. */
- BLI_dynstr_append(ds, "void pass_attr(in int vert) {\n");
-
- BLI_dynstr_append(ds, "\tPASS_RESOURCE_ID(vert)\n");
-
- /* XXX HACK: Eevee specific. */
- if (geom_code == NULL) {
- BLI_dynstr_append(ds, "\tworldPosition = worldPositiong[vert];\n");
- BLI_dynstr_append(ds, "\tviewPosition = viewPositiong[vert];\n");
- BLI_dynstr_append(ds, "\tworldNormal = worldNormalg[vert];\n");
- BLI_dynstr_append(ds, "\tviewNormal = viewNormalg[vert];\n");
- }
+ BLI_dynstr_append(ds, "#define USE_ATTR\n");
+ BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n");
if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, "\tbarycentricTexCo = barycentricTexCog[vert];\n");
- BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, "\tbarycentricTexCo.x = float((vert % 3) == 0);\n");
- BLI_dynstr_append(ds, "\tbarycentricTexCo.y = float((vert % 3) == 1);\n");
- BLI_dynstr_append(ds, "#endif\n");
+ BLI_dynstr_append(ds, " dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\n");
}
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
/* TODO let shader choose what to do depending on what the attribute is. */
- BLI_dynstr_appendf(ds, "\tvar%d = var%dg[vert];\n", attr->id, attr->id);
+ BLI_dynstr_appendf(ds, " dataAttrOut.var%d = dataAttrIn[vert].var%d;\n", attr->id, attr->id);
}
- BLI_dynstr_append(ds, "}\n");
+ BLI_dynstr_append(ds, "}\n\n");
- code = BLI_dynstr_get_cstring(ds);
+ BLI_dynstr_append(ds, geom_code);
+
+ char *code = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
return code;
@@ -1046,8 +801,17 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
* generated VBOs are ready to accept the future shader. */
gpu_node_graph_prune_unused(graph);
+ int builtins = 0;
+ LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ if (input->source == GPU_SOURCE_BUILTIN) {
+ builtins |= input->builtin;
+ }
+ }
+ }
/* generate code */
- char *fragmentgen = code_generate_fragment(material, graph);
+ char *interface_str = code_generate_interface(graph, builtins);
+ char *fragmentgen = code_generate_fragment(material, graph, interface_str);
/* Cache lookup: Reuse shaders already compiled */
uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes);
@@ -1055,6 +819,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) {
/* No collision, just return the pass. */
+ MEM_SAFE_FREE(interface_str);
MEM_freeN(fragmentgen);
if (!gpu_pass_is_valid(pass_hash)) {
/* Shader has already been created but failed to compile. */
@@ -1069,10 +834,11 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
GSet *used_libraries = gpu_material_used_libraries(material);
char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib);
- char *geometrycode = code_generate_geometry(graph, geom_code, defines);
- char *vertexcode = code_generate_vertex(graph, vert_code, (geometrycode != NULL));
+ char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins);
+ char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins);
char *fragmentcode = BLI_strdupcat(tmp, fragmentgen);
+ MEM_SAFE_FREE(interface_str);
MEM_freeN(fragmentgen);
MEM_freeN(tmp);
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index ce20f495ba3..18ff4c3241d 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -26,6 +26,10 @@
#ifndef __GPU_CODEGEN_H__
#define __GPU_CODEGEN_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct GPUMaterial;
struct GPUNodeGraph;
struct GPUOutput;
@@ -68,4 +72,8 @@ void GPU_pass_release(GPUPass *pass);
void gpu_codegen_init(void);
void gpu_codegen_exit(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __GPU_CODEGEN_H__ */
diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cc
index 0b9104e5349..0b9104e5349 100644
--- a/source/blender/gpu/intern/gpu_context.cpp
+++ b/source/blender/gpu/intern/gpu_context.cc
diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.cc
index f7d6236071d..f7d6236071d 100644
--- a/source/blender/gpu/intern/gpu_debug.c
+++ b/source/blender/gpu/intern/gpu_debug.cc
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
deleted file mode 100644
index 1c346217e9f..00000000000
--- a/source/blender/gpu/intern/gpu_draw.c
+++ /dev/null
@@ -1,1452 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup gpu
- *
- * Utility functions for dealing with OpenGL texture & material context,
- * mipmap generation and light objects.
- *
- * These are some obscure rendering functions shared between the game engine (not anymore)
- * and the blender, in this module to avoid duplication
- * and abstract them away from the rest a bit.
- */
-
-#include <string.h>
-
-#include "BLI_blenlib.h"
-#include "BLI_boxpack_2d.h"
-#include "BLI_linklist.h"
-#include "BLI_math.h"
-#include "BLI_threads.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_image_types.h"
-#include "DNA_movieclip_types.h"
-#include "DNA_userdef_types.h"
-
-#include "MEM_guardedalloc.h"
-
-#include "IMB_colormanagement.h"
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-
-#include "BKE_global.h"
-#include "BKE_image.h"
-#include "BKE_main.h"
-#include "BKE_movieclip.h"
-
-#include "GPU_draw.h"
-#include "GPU_extensions.h"
-#include "GPU_glew.h"
-#include "GPU_platform.h"
-#include "GPU_texture.h"
-
-#include "PIL_time.h"
-
-static void gpu_free_image_immediate(Image *ima);
-
-//* Checking powers of two for images since OpenGL ES requires it */
-#ifdef WITH_DDS
-static bool is_power_of_2_resolution(int w, int h)
-{
- return is_power_of_2_i(w) && is_power_of_2_i(h);
-}
-#endif
-
-static bool is_over_resolution_limit(GLenum textarget, int w, int h)
-{
- int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size();
- int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size;
-
- return (w > reslimit || h > reslimit);
-}
-
-static int smaller_power_of_2_limit(int num)
-{
- int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, GPU_max_texture_size()) :
- GPU_max_texture_size();
- /* take texture clamping into account */
- if (num > reslimit) {
- return reslimit;
- }
-
- return power_of_2_min_i(num);
-}
-
-/* Current OpenGL state caching for GPU_set_tpage */
-
-static struct GPUTextureState {
- /* also controls min/mag filtering */
- bool domipmap;
- /* only use when 'domipmap' is set */
- bool linearmipmap;
- /* store this so that new images created while texture painting won't be set to mipmapped */
- bool texpaint;
-
- float anisotropic;
-} GTS = {1, 0, 0, 1.0f};
-
-/* Mipmap settings */
-
-void GPU_set_mipmap(Main *bmain, bool mipmap)
-{
- if (GTS.domipmap != mipmap) {
- GPU_free_images(bmain);
- GTS.domipmap = mipmap;
- }
-}
-
-void GPU_set_linear_mipmap(bool linear)
-{
- if (GTS.linearmipmap != linear) {
- GTS.linearmipmap = linear;
- }
-}
-
-bool GPU_get_mipmap(void)
-{
- return GTS.domipmap && !GTS.texpaint;
-}
-
-bool GPU_get_linear_mipmap(void)
-{
- return GTS.linearmipmap;
-}
-
-static GLenum gpu_get_mipmap_filter(bool mag)
-{
- /* linearmipmap is off by default *when mipmapping is off,
- * use unfiltered display */
- if (mag) {
- if (GTS.domipmap) {
- return GL_LINEAR;
- }
- else {
- return GL_NEAREST;
- }
- }
- else {
- if (GTS.domipmap) {
- if (GTS.linearmipmap) {
- return GL_LINEAR_MIPMAP_LINEAR;
- }
- else {
- return GL_LINEAR_MIPMAP_NEAREST;
- }
- }
- else {
- return GL_NEAREST;
- }
- }
-}
-
-/* Anisotropic filtering settings */
-void GPU_set_anisotropic(float value)
-{
- if (GTS.anisotropic != value) {
- GPU_samplers_free();
-
- /* Clamp value to the maximum value the graphics card supports */
- const float max = GPU_max_texture_anisotropy();
- if (value > max) {
- value = max;
- }
-
- GTS.anisotropic = value;
-
- GPU_samplers_init();
- }
-}
-
-float GPU_get_anisotropic(void)
-{
- return GTS.anisotropic;
-}
-
-/* Set OpenGL state for an MTFace */
-
-static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget, const int multiview_eye)
-{
- if (textarget == GL_TEXTURE_2D) {
- return &(ima->gputexture[TEXTARGET_TEXTURE_2D][multiview_eye]);
- }
- else if (textarget == GL_TEXTURE_CUBE_MAP) {
- return &(ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP][multiview_eye]);
- }
- else if (textarget == GL_TEXTURE_2D_ARRAY) {
- return &(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]);
- }
- else if (textarget == GL_TEXTURE_1D_ARRAY) {
- return &(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][multiview_eye]);
- }
-
- return NULL;
-}
-
-static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye)
-{
- GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye];
-
- if (tilearray == NULL) {
- return 0;
- }
-
- float array_w = GPU_texture_width(tilearray);
- float array_h = GPU_texture_height(tilearray);
-
- ImageTile *last_tile = ima->tiles.last;
- /* Tiles are sorted by number. */
- int max_tile = last_tile->tile_number - 1001;
-
- /* create image */
- int bindcode;
- glGenTextures(1, (GLuint *)&bindcode);
- glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode);
-
- int width = max_tile + 1;
- float *data = MEM_callocN(width * 8 * sizeof(float), __func__);
- for (int i = 0; i < width; i++) {
- data[4 * i] = -1.0f;
- }
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- int i = tile->tile_number - 1001;
- data[4 * i] = tile->runtime.tilearray_layer;
-
- float *tile_info = &data[4 * width + 4 * i];
- tile_info[0] = tile->runtime.tilearray_offset[0] / array_w;
- tile_info[1] = tile->runtime.tilearray_offset[1] / array_h;
- tile_info[2] = tile->runtime.tilearray_size[0] / array_w;
- tile_info[3] = tile->runtime.tilearray_size[1] / array_h;
- }
-
- glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, width, 2, 0, GL_RGBA, GL_FLOAT, data);
- MEM_freeN(data);
-
- glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glBindTexture(GL_TEXTURE_1D_ARRAY, 0);
-
- return bindcode;
-}
-
-typedef struct PackTile {
- FixedSizeBoxPack boxpack;
- ImageTile *tile;
- float pack_score;
-} PackTile;
-
-static int compare_packtile(const void *a, const void *b)
-{
- const PackTile *tile_a = a;
- const PackTile *tile_b = b;
-
- return tile_a->pack_score < tile_b->pack_score;
-}
-
-static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
-{
- int arraywidth = 0, arrayheight = 0;
-
- ListBase boxes = {NULL};
-
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- ImageUser iuser;
- BKE_imageuser_default(&iuser);
- iuser.tile = tile->tile_number;
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
-
- if (ibuf) {
- PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__);
- packtile->tile = tile;
- packtile->boxpack.w = ibuf->x;
- packtile->boxpack.h = ibuf->y;
-
- if (is_over_resolution_limit(
- GL_TEXTURE_2D_ARRAY, packtile->boxpack.w, packtile->boxpack.h)) {
- packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w);
- packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h);
- }
- arraywidth = max_ii(arraywidth, packtile->boxpack.w);
- arrayheight = max_ii(arrayheight, packtile->boxpack.h);
-
- /* We sort the tiles by decreasing size, with an additional penalty term
- * for high aspect ratios. This improves packing efficiency. */
- float w = packtile->boxpack.w, h = packtile->boxpack.h;
- packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h;
-
- BKE_image_release_ibuf(ima, ibuf, NULL);
- BLI_addtail(&boxes, packtile);
- }
- }
-
- BLI_assert(arraywidth > 0 && arrayheight > 0);
-
- BLI_listbase_sort(&boxes, compare_packtile);
- int arraylayers = 0;
- /* Keep adding layers until all tiles are packed. */
- while (boxes.first != NULL) {
- ListBase packed = {NULL};
- BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed);
- BLI_assert(packed.first != NULL);
-
- LISTBASE_FOREACH (PackTile *, packtile, &packed) {
- ImageTile *tile = packtile->tile;
- int *tileoffset = tile->runtime.tilearray_offset;
- int *tilesize = tile->runtime.tilearray_size;
-
- tileoffset[0] = packtile->boxpack.x;
- tileoffset[1] = packtile->boxpack.y;
- tilesize[0] = packtile->boxpack.w;
- tilesize[1] = packtile->boxpack.h;
- tile->runtime.tilearray_layer = arraylayers;
- }
-
- BLI_freelistN(&packed);
- arraylayers++;
- }
-
- /* create image */
- int bindcode;
- glGenTextures(1, (GLuint *)&bindcode);
- glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode);
-
- GLenum data_type, internal_format;
- if (main_ibuf->rect_float) {
- data_type = GL_FLOAT;
- internal_format = (!(main_ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)) ?
- GL_RGBA32F :
- GL_RGBA16F;
- }
- else {
- data_type = GL_UNSIGNED_BYTE;
- internal_format = GL_RGBA8;
- if (!IMB_colormanagement_space_is_data(main_ibuf->rect_colorspace) &&
- !IMB_colormanagement_space_is_scene_linear(main_ibuf->rect_colorspace)) {
- internal_format = GL_SRGB8_ALPHA8;
- }
- }
-
- glTexImage3D(GL_TEXTURE_2D_ARRAY,
- 0,
- internal_format,
- arraywidth,
- arrayheight,
- arraylayers,
- 0,
- GL_RGBA,
- data_type,
- NULL);
-
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- int tilelayer = tile->runtime.tilearray_layer;
- int *tileoffset = tile->runtime.tilearray_offset;
- int *tilesize = tile->runtime.tilearray_size;
-
- if (tilesize[0] == 0 || tilesize[1] == 0) {
- continue;
- }
-
- ImageUser iuser;
- BKE_imageuser_default(&iuser);
- iuser.tile = tile->tile_number;
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
-
- if (ibuf) {
- bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]);
-
- ImBuf *scale_ibuf = NULL;
- if (ibuf->rect_float) {
- float *rect_float = ibuf->rect_float;
-
- const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT;
- if (ibuf->channels != 4 || !store_premultiplied) {
- rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
- IMB_colormanagement_imbuf_to_float_texture(
- rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
- }
-
- float *pixeldata = rect_float;
- if (needs_scale) {
- scale_ibuf = IMB_allocFromBuffer(NULL, rect_float, ibuf->x, ibuf->y, 4);
- IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]);
- pixeldata = scale_ibuf->rect_float;
- }
-
- glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
- 0,
- tileoffset[0],
- tileoffset[1],
- tilelayer,
- tilesize[0],
- tilesize[1],
- 1,
- GL_RGBA,
- GL_FLOAT,
- pixeldata);
-
- if (rect_float != ibuf->rect_float) {
- MEM_freeN(rect_float);
- }
- }
- else {
- unsigned int *rect = ibuf->rect;
-
- if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
- rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__);
- IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect,
- 0,
- 0,
- ibuf->x,
- ibuf->y,
- ibuf,
- internal_format == GL_SRGB8_ALPHA8,
- ima->alpha_mode == IMA_ALPHA_PREMUL);
- }
-
- unsigned int *pixeldata = rect;
- if (needs_scale) {
- scale_ibuf = IMB_allocFromBuffer(rect, NULL, ibuf->x, ibuf->y, 4);
- IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]);
- pixeldata = scale_ibuf->rect;
- }
- glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
- 0,
- tileoffset[0],
- tileoffset[1],
- tilelayer,
- tilesize[0],
- tilesize[1],
- 1,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- pixeldata);
-
- if (rect != ibuf->rect) {
- MEM_freeN(rect);
- }
- }
- if (scale_ibuf != NULL) {
- IMB_freeImBuf(scale_ibuf);
- }
- }
-
- BKE_image_release_ibuf(ima, ibuf, NULL);
- }
-
- if (GPU_get_mipmap()) {
- glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
- if (ima) {
- ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
- }
- }
-
- glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
-
- return bindcode;
-}
-
-static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget)
-{
- uint bindcode = 0;
- const bool mipmap = GPU_get_mipmap();
- const bool half_float = (ibuf->flags & IB_halffloat) != 0;
-
-#ifdef WITH_DDS
- if (ibuf->ftype == IMB_FTYPE_DDS) {
- /* DDS is loaded directly in compressed form. */
- GPU_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf);
- return bindcode;
- }
-#endif
-
- /* Regular uncompressed texture. */
- float *rect_float = ibuf->rect_float;
- uchar *rect = (uchar *)ibuf->rect;
- bool compress_as_srgb = false;
-
- if (rect_float == NULL) {
- /* Byte image is in original colorspace from the file. If the file is sRGB
- * scene linear, or non-color data no conversion is needed. Otherwise we
- * compress as scene linear + sRGB transfer function to avoid precision loss
- * in common cases.
- *
- * We must also convert to premultiplied for correct texture interpolation
- * and consistency with float images. */
- if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
- compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace);
-
- rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__);
- if (rect == NULL) {
- return bindcode;
- }
-
- /* Texture storage of images is defined by the alpha mode of the image. The
- * downside of this is that there can be artifacts near alpha edges. However,
- * this allows us to use sRGB texture formats and preserves color values in
- * zero alpha areas, and appears generally closer to what game engines that we
- * want to be compatible with do. */
- const bool store_premultiplied = ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true;
- IMB_colormanagement_imbuf_to_byte_texture(
- rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied);
- }
- }
- else {
- /* Float image is already in scene linear colorspace or non-color data by
- * convention, no colorspace conversion needed. But we do require 4 channels
- * currently. */
- const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false;
-
- if (ibuf->channels != 4 || !store_premultiplied) {
- rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
- if (rect_float == NULL) {
- return bindcode;
- }
- IMB_colormanagement_imbuf_to_float_texture(
- rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
- }
- }
-
- /* Create OpenGL texture. */
- GPU_create_gl_tex(&bindcode,
- (uint *)rect,
- rect_float,
- ibuf->x,
- ibuf->y,
- textarget,
- mipmap,
- half_float,
- compress_as_srgb,
- ima);
-
- /* Free buffers if needed. */
- if (rect && rect != (uchar *)ibuf->rect) {
- MEM_freeN(rect);
- }
- if (rect_float && rect_float != ibuf->rect_float) {
- MEM_freeN(rect_float);
- }
-
- return bindcode;
-}
-
-static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip,
- MovieClipUser *cuser,
- GLenum textarget)
-{
- MovieClip_RuntimeGPUTexture *tex;
- for (tex = clip->runtime.gputextures.first; tex; tex = tex->next) {
- if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) {
- break;
- }
- }
-
- if (tex == NULL) {
- tex = MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), __func__);
-
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- tex->gputexture[i] = NULL;
- }
-
- memcpy(&tex->user, cuser, sizeof(MovieClipUser));
- BLI_addtail(&clip->runtime.gputextures, tex);
- }
-
- if (textarget == GL_TEXTURE_2D) {
- return &tex->gputexture[TEXTARGET_TEXTURE_2D];
- }
- else if (textarget == GL_TEXTURE_CUBE_MAP) {
- return &tex->gputexture[TEXTARGET_TEXTURE_CUBE_MAP];
- }
-
- return NULL;
-}
-
-static ImBuf *update_do_scale(uchar *rect,
- float *rect_float,
- int *x,
- int *y,
- int *w,
- int *h,
- int limit_w,
- int limit_h,
- int full_w,
- int full_h)
-{
- /* Partial update with scaling. */
- float xratio = limit_w / (float)full_w;
- float yratio = limit_h / (float)full_h;
-
- int part_w = *w, part_h = *h;
-
- /* Find sub coordinates in scaled image. Take ceiling because we will be
- * losing 1 pixel due to rounding errors in x,y. */
- *x *= xratio;
- *y *= yratio;
- *w = (int)ceil(xratio * (*w));
- *h = (int)ceil(yratio * (*h));
-
- /* ...but take back if we are over the limit! */
- if (*x + *w > limit_w) {
- (*w)--;
- }
- if (*y + *h > limit_h) {
- (*h)--;
- }
-
- /* Scale pixels. */
- ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4);
- IMB_scaleImBuf(ibuf, *w, *h);
-
- return ibuf;
-}
-
-static void gpu_texture_update_scaled_array(uchar *rect,
- float *rect_float,
- int full_w,
- int full_h,
- int x,
- int y,
- int layer,
- const int *tile_offset,
- const int *tile_size,
- int w,
- int h)
-{
- ImBuf *ibuf = update_do_scale(
- rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h);
-
- /* Shift to account for tile packing. */
- x += tile_offset[0];
- y += tile_offset[1];
-
- if (ibuf->rect_float) {
- glTexSubImage3D(
- GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float);
- }
- else {
- glTexSubImage3D(
- GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
- }
-
- IMB_freeImBuf(ibuf);
-}
-
-static void gpu_texture_update_scaled(
- uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h)
-{
- /* Partial update with scaling. */
- int limit_w = smaller_power_of_2_limit(full_w);
- int limit_h = smaller_power_of_2_limit(full_h);
-
- ImBuf *ibuf = update_do_scale(
- rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h);
-
- if (ibuf->rect_float) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, ibuf->rect_float);
- }
- else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
- }
-
- IMB_freeImBuf(ibuf);
-}
-
-static void gpu_texture_update_unscaled(uchar *rect,
- float *rect_float,
- int x,
- int y,
- int layer,
- int w,
- int h,
- GLint tex_stride,
- GLint tex_offset)
-{
- /* Partial update without scaling. Stride and offset are used to copy only a
- * subset of a possible larger buffer than what we are updating. */
- GLint row_length;
- glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, tex_stride);
-
- if (layer >= 0) {
- if (rect_float == NULL) {
- glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
- 0,
- x,
- y,
- layer,
- w,
- h,
- 1,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- rect + tex_offset);
- }
- else {
- glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
- 0,
- x,
- y,
- layer,
- w,
- h,
- 1,
- GL_RGBA,
- GL_FLOAT,
- rect_float + tex_offset);
- }
- }
- else {
- if (rect_float == NULL) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset);
- }
- else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset);
- }
- }
-
- glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
-}
-
-static void gpu_texture_update_from_ibuf(
- GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h)
-{
- /* Partial update of texture for texture painting. This is often much
- * quicker than fully updating the texture for high resolution images. */
- GPU_texture_bind(tex, 0);
-
- bool scaled;
- if (tile != NULL) {
- int *tilesize = tile->runtime.tilearray_size;
- scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]);
- }
- else {
- scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y);
- }
-
- if (scaled) {
- /* Extra padding to account for bleed from neighboring pixels. */
- const int padding = 4;
- const int xmax = min_ii(x + w + padding, ibuf->x);
- const int ymax = min_ii(y + h + padding, ibuf->y);
- x = max_ii(x - padding, 0);
- y = max_ii(y - padding, 0);
- w = xmax - x;
- h = ymax - y;
- }
-
- /* Get texture data pointers. */
- float *rect_float = ibuf->rect_float;
- uchar *rect = (uchar *)ibuf->rect;
- GLint tex_stride = ibuf->x;
- GLint tex_offset = ibuf->channels * (y * ibuf->x + x);
-
- if (rect_float == NULL) {
- /* Byte pixels. */
- if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
- const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(
- ibuf->rect_colorspace);
-
- rect = MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__);
- if (rect == NULL) {
- return;
- }
-
- tex_stride = w;
- tex_offset = 0;
-
- /* Convert to scene linear with sRGB compression, and premultiplied for
- * correct texture interpolation. */
- const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL);
- IMB_colormanagement_imbuf_to_byte_texture(
- rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied);
- }
- }
- else {
- /* Float pixels. */
- const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT);
-
- if (ibuf->channels != 4 || scaled || !store_premultiplied) {
- rect_float = MEM_mallocN(sizeof(float) * 4 * w * h, __func__);
- if (rect_float == NULL) {
- return;
- }
-
- tex_stride = w;
- tex_offset = 0;
-
- IMB_colormanagement_imbuf_to_float_texture(
- rect_float, x, y, w, h, ibuf, store_premultiplied);
- }
- }
-
- if (scaled) {
- /* Slower update where we first have to scale the input pixels. */
- if (tile != NULL) {
- int *tileoffset = tile->runtime.tilearray_offset;
- int *tilesize = tile->runtime.tilearray_size;
- int tilelayer = tile->runtime.tilearray_layer;
- gpu_texture_update_scaled_array(
- rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h);
- }
- else {
- gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h);
- }
- }
- else {
- /* Fast update at same resolution. */
- if (tile != NULL) {
- int *tileoffset = tile->runtime.tilearray_offset;
- int tilelayer = tile->runtime.tilearray_layer;
- gpu_texture_update_unscaled(rect,
- rect_float,
- x + tileoffset[0],
- y + tileoffset[1],
- tilelayer,
- w,
- h,
- tex_stride,
- tex_offset);
- }
- else {
- gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset);
- }
- }
-
- /* Free buffers if needed. */
- if (rect && rect != (uchar *)ibuf->rect) {
- MEM_freeN(rect);
- }
- if (rect_float && rect_float != ibuf->rect_float) {
- MEM_freeN(rect_float);
- }
-
- if (GPU_get_mipmap()) {
- glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D);
- }
- else {
- ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
- }
-
- GPU_texture_unbind(tex);
-}
-
-/* Get the GPUTexture for a given `Image`.
- *
- * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already
- * available. It is also required when requesting the GPUTexture for a render result. */
-GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, int textarget)
-{
-#ifndef GPU_STANDALONE
- if (ima == NULL) {
- return NULL;
- }
-
- /* currently, gpu refresh tagging is used by ima sequences */
- if (ima->gpuflag & IMA_GPU_REFRESH) {
- gpu_free_image_immediate(ima);
- ima->gpuflag &= ~IMA_GPU_REFRESH;
- }
-
- /* Tag as in active use for garbage collector. */
- BKE_image_tag_time(ima);
-
- /* Test if we already have a texture. */
- GPUTexture **tex = gpu_get_image_gputexture(ima, textarget, iuser ? iuser->multiview_eye : 0);
- if (*tex) {
- return *tex;
- }
-
- /* Check if we have a valid image. If not, we return a dummy
- * texture with zero bindcode so we don't keep trying. */
- uint bindcode = 0;
- ImageTile *tile = BKE_image_get_tile(ima, 0);
- if (tile == NULL || tile->ok == 0) {
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
- }
-
- /* check if we have a valid image buffer */
- ImBuf *ibuf_intern = ibuf;
- if (ibuf_intern == NULL) {
- ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL);
- if (ibuf_intern == NULL) {
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
- }
- }
-
- if (textarget == GL_TEXTURE_2D_ARRAY) {
- bindcode = gpu_texture_create_tile_array(ima, ibuf_intern);
- }
- else if (textarget == GL_TEXTURE_1D_ARRAY) {
- bindcode = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0);
- }
- else {
- bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget);
- }
-
- /* if `ibuf` was given, we don't own the `ibuf_intern` */
- if (ibuf == NULL) {
- BKE_image_release_ibuf(ima, ibuf_intern, NULL);
- }
-
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
-
- GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
-
- return *tex;
-#endif
- return NULL;
-}
-
-GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, MovieClipUser *cuser, int textarget)
-{
-#ifndef GPU_STANDALONE
- if (clip == NULL) {
- return NULL;
- }
-
- GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, textarget);
- if (*tex) {
- return *tex;
- }
-
- /* check if we have a valid image buffer */
- uint bindcode = 0;
- ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser);
- if (ibuf == NULL) {
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
- }
-
- bindcode = gpu_texture_create_from_ibuf(NULL, ibuf, textarget);
- IMB_freeImBuf(ibuf);
-
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
-#else
- return NULL;
-#endif
-}
-
-void GPU_free_texture_movieclip(struct MovieClip *clip)
-{
- /* number of gpu textures to keep around as cache
- * We don't want to keep too many GPU textures for
- * movie clips around, as they can be large.*/
- const int MOVIECLIP_NUM_GPUTEXTURES = 1;
-
- while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) {
- MovieClip_RuntimeGPUTexture *tex = BLI_pophead(&clip->runtime.gputextures);
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- /* free glsl image binding */
- if (tex->gputexture[i]) {
- GPU_texture_free(tex->gputexture[i]);
- tex->gputexture[i] = NULL;
- }
- }
- MEM_freeN(tex);
- }
-}
-
-static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth)
-{
- size_t block_size = frect ? sizeof(float[4]) : sizeof(uchar[4]);
- void **sides = NULL;
- int h = recth / 2;
- int w = rectw / 3;
-
- if (w != h) {
- return sides;
- }
-
- /* PosX, NegX, PosY, NegY, PosZ, NegZ */
- sides = MEM_mallocN(sizeof(void *) * 6, "");
- for (int i = 0; i < 6; i++) {
- sides[i] = MEM_mallocN(block_size * w * h, "");
- }
-
- /* divide image into six parts */
- /* ______________________
- * | | | |
- * | NegX | NegY | PosX |
- * |______|______|______|
- * | | | |
- * | NegZ | PosZ | PosY |
- * |______|______|______|
- */
- if (frect) {
- float(*frectb)[4] = (float(*)[4])frect;
- float(**fsides)[4] = (float(**)[4])sides;
-
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- memcpy(&fsides[0][x * h + y], &frectb[(recth - y - 1) * rectw + 2 * w + x], block_size);
- memcpy(&fsides[1][x * h + y], &frectb[(y + h) * rectw + w - 1 - x], block_size);
- memcpy(
- &fsides[3][y * w + x], &frectb[(recth - y - 1) * rectw + 2 * w - 1 - x], block_size);
- memcpy(&fsides[5][y * w + x], &frectb[(h - y - 1) * rectw + w - 1 - x], block_size);
- }
- memcpy(&fsides[2][y * w], frectb[y * rectw + 2 * w], block_size * w);
- memcpy(&fsides[4][y * w], frectb[y * rectw + w], block_size * w);
- }
- }
- else {
- uint **isides = (uint **)sides;
-
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- isides[0][x * h + y] = rect[(recth - y - 1) * rectw + 2 * w + x];
- isides[1][x * h + y] = rect[(y + h) * rectw + w - 1 - x];
- isides[3][y * w + x] = rect[(recth - y - 1) * rectw + 2 * w - 1 - x];
- isides[5][y * w + x] = rect[(h - y - 1) * rectw + w - 1 - x];
- }
- memcpy(&isides[2][y * w], &rect[y * rectw + 2 * w], block_size * w);
- memcpy(&isides[4][y * w], &rect[y * rectw + w], block_size * w);
- }
- }
-
- return sides;
-}
-
-static void gpu_del_cube_map(void **cube_map)
-{
- int i;
- if (cube_map == NULL) {
- return;
- }
- for (i = 0; i < 6; i++) {
- MEM_freeN(cube_map[i]);
- }
- MEM_freeN(cube_map);
-}
-
-/* Image *ima can be NULL */
-void GPU_create_gl_tex(uint *bind,
- uint *rect,
- float *frect,
- int rectw,
- int recth,
- int textarget,
- bool mipmap,
- bool half_float,
- bool use_srgb,
- Image *ima)
-{
- ImBuf *ibuf = NULL;
-
- if (textarget == GL_TEXTURE_2D && is_over_resolution_limit(textarget, rectw, recth)) {
- int tpx = rectw;
- int tpy = recth;
- rectw = smaller_power_of_2_limit(rectw);
- recth = smaller_power_of_2_limit(recth);
-
- if (frect) {
- ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy, 4);
- IMB_scaleImBuf(ibuf, rectw, recth);
-
- frect = ibuf->rect_float;
- }
- else {
- ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy, 4);
- IMB_scaleImBuf(ibuf, rectw, recth);
-
- rect = ibuf->rect;
- }
- }
-
- /* create image */
- glGenTextures(1, (GLuint *)bind);
- glBindTexture(textarget, *bind);
-
- GLenum float_format = (!half_float && (ima && (ima->flag & IMA_HIGH_BITDEPTH))) ? GL_RGBA32F :
- GL_RGBA16F;
- GLenum internal_format = (frect) ? float_format : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
-
- if (textarget == GL_TEXTURE_2D) {
- if (frect) {
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect);
- }
- else {
- glTexImage2D(
- GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect);
- }
-
- if (GPU_get_mipmap() && mipmap) {
- glGenerateMipmap(GL_TEXTURE_2D);
- if (ima) {
- ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
- }
- }
- }
- else if (textarget == GL_TEXTURE_CUBE_MAP) {
- int w = rectw / 3, h = recth / 2;
-
- if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) {
- void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth);
- GLenum type = frect ? GL_FLOAT : GL_UNSIGNED_BYTE;
-
- if (cube_map) {
- for (int i = 0; i < 6; i++) {
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
- 0,
- internal_format,
- w,
- h,
- 0,
- GL_RGBA,
- type,
- cube_map[i]);
- }
- }
-
- if (GPU_get_mipmap() && mipmap) {
- glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
-
- if (ima) {
- ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
- }
- }
-
- gpu_del_cube_map(cube_map);
- }
- else {
- printf("Incorrect envmap size\n");
- }
- }
-
- glBindTexture(textarget, 0);
-
- if (ibuf) {
- IMB_freeImBuf(ibuf);
- }
-}
-
-/**
- * GPU_upload_dxt_texture() assumes that the texture is already bound and ready to go.
- * This is so the viewport and the BGE can share some code.
- * Returns false if the provided ImBuf doesn't have a supported DXT compression format
- */
-bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb)
-{
-#ifdef WITH_DDS
- GLint format = 0;
- int blocksize, height, width, i, size, offset = 0;
-
- width = ibuf->x;
- height = ibuf->y;
-
- if (GLEW_EXT_texture_compression_s3tc) {
- if (ibuf->dds_data.fourcc == FOURCC_DXT1) {
- format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT :
- GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
- }
- else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
- format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT :
- GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- }
- else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
- format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT :
- GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- }
- }
-
- if (format == 0) {
- fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n");
- return false;
- }
-
- if (!is_power_of_2_resolution(width, height)) {
- fprintf(
- stderr,
- "Unable to load non-power-of-two DXT image resolution, falling back to uncompressed\n");
- return false;
- }
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0));
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
-
- blocksize = (ibuf->dds_data.fourcc == FOURCC_DXT1) ? 8 : 16;
- for (i = 0; i < ibuf->dds_data.nummipmaps && (width || height); i++) {
- if (width == 0) {
- width = 1;
- }
- if (height == 0) {
- height = 1;
- }
-
- size = ((width + 3) / 4) * ((height + 3) / 4) * blocksize;
-
- glCompressedTexImage2D(
- GL_TEXTURE_2D, i, format, width, height, 0, size, ibuf->dds_data.data + offset);
-
- offset += size;
- width >>= 1;
- height >>= 1;
- }
-
- /* set number of mipmap levels we have, needed in case they don't go down to 1x1 */
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1);
-
- return true;
-#else
- UNUSED_VARS(ibuf, use_srgb);
- return false;
-#endif
-}
-
-void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima, ImBuf *ibuf)
-{
- /* For DDS we only support data, scene linear and sRGB. Converting to
- * different colorspace would break the compression. */
- const bool use_srgb = !(IMB_colormanagement_space_is_data(ibuf->rect_colorspace) ||
- IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
- const bool mipmap = GPU_get_mipmap();
- const bool half_float = (ibuf->flags & IB_halffloat) != 0;
-
-#ifndef WITH_DDS
- (void)ibuf;
- /* Fall back to uncompressed if DDS isn't enabled */
- GPU_create_gl_tex(
- bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima);
-#else
- glGenTextures(1, (GLuint *)bind);
- glBindTexture(textarget, *bind);
-
- if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf, use_srgb) == 0) {
- glDeleteTextures(1, (GLuint *)bind);
- GPU_create_gl_tex(
- bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima);
- }
-
- glBindTexture(textarget, 0);
-#endif
-}
-
-/* these two functions are called on entering and exiting texture paint mode,
- * temporary disabling/enabling mipmapping on all images for quick texture
- * updates with glTexSubImage2D. images that didn't change don't have to be
- * re-uploaded to OpenGL */
-void GPU_paint_set_mipmap(Main *bmain, bool mipmap)
-{
-#ifndef GPU_STANDALONE
- if (!GTS.domipmap) {
- return;
- }
-
- GTS.texpaint = !mipmap;
-
- if (mipmap) {
- for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
- if (BKE_image_has_opengl_texture(ima)) {
- if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) {
- for (int eye = 0; eye < 2; eye++) {
- for (int a = 0; a < TEXTARGET_COUNT; a++) {
- if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) {
- GPUTexture *tex = ima->gputexture[a][eye];
- if (tex != NULL) {
- GPU_texture_bind(tex, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0));
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
- GPU_texture_unbind(tex);
- }
- }
- }
- }
- }
- else {
- GPU_free_image(ima);
- }
- }
- else {
- ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
- }
- }
- }
- else {
- for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
- if (BKE_image_has_opengl_texture(ima)) {
- for (int eye = 0; eye < 2; eye++) {
- for (int a = 0; a < TEXTARGET_COUNT; a++) {
- if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) {
- GPUTexture *tex = ima->gputexture[a][eye];
- if (tex != NULL) {
- GPU_texture_bind(tex, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
- GPU_texture_unbind(tex);
- }
- }
- }
- }
- }
- else {
- ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
- }
- }
- }
-#endif /* GPU_STANDALONE */
-}
-
-void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
-{
-#ifndef GPU_STANDALONE
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
- ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
-
- if ((ibuf == NULL) || (w == 0) || (h == 0)) {
- /* Full reload of texture. */
- GPU_free_image(ima);
- }
-
- GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0];
- /* Check if we need to update the main gputexture. */
- if (tex != NULL && tile == ima->tiles.first) {
- gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h);
- }
-
- /* Check if we need to update the array gputexture. */
- tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][0];
- if (tex != NULL) {
- gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h);
- }
-
- BKE_image_release_ibuf(ima, ibuf, NULL);
-#endif
-}
-
-static LinkNode *image_free_queue = NULL;
-static ThreadMutex img_queue_mutex = BLI_MUTEX_INITIALIZER;
-
-static void gpu_queue_image_for_free(Image *ima)
-{
- 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()) {
- return;
- }
-
- BLI_mutex_lock(&img_queue_mutex);
-
- /* images */
- for (LinkNode *node = image_free_queue; node; node = node->next) {
- Image *ima = node->link;
-
- /* check in case it was freed in the meantime */
- if (bmain && BLI_findindex(&bmain->images, ima) != -1) {
- GPU_free_image(ima);
- }
- }
-
- BLI_linklist_free(image_free_queue, NULL);
- image_free_queue = NULL;
-
- BLI_mutex_unlock(&img_queue_mutex);
-}
-
-static void gpu_free_image_immediate(Image *ima)
-{
- 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]);
- ima->gputexture[i][eye] = NULL;
- }
- }
- }
-
- ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE);
-}
-
-void GPU_free_image(Image *ima)
-{
- if (!BLI_thread_is_main()) {
- gpu_queue_image_for_free(ima);
- return;
- }
-
- gpu_free_image_immediate(ima);
-}
-
-void GPU_free_images(Main *bmain)
-{
- if (bmain) {
- for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
- GPU_free_image(ima);
- }
- }
-}
-
-/* same as above but only free animated images */
-void GPU_free_images_anim(Main *bmain)
-{
- if (bmain) {
- for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
- if (BKE_image_is_animated(ima)) {
- GPU_free_image(ima);
- }
- }
- }
-}
-
-void GPU_free_images_old(Main *bmain)
-{
- static int lasttime = 0;
- int ctime = (int)PIL_check_seconds_timer();
-
- /*
- * Run garbage collector once for every collecting period of time
- * if textimeout is 0, that's the option to NOT run the collector
- */
- if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) {
- return;
- }
-
- /* of course not! */
- if (G.is_rendering) {
- return;
- }
-
- lasttime = ctime;
-
- Image *ima = bmain->images.first;
- while (ima) {
- if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) {
- /* If it's in GL memory, deallocate and set time tag to current time
- * This gives textures a "second chance" to be used before dying. */
- if (BKE_image_has_opengl_texture(ima)) {
- GPU_free_image(ima);
- ima->lastused = ctime;
- }
- /* Otherwise, just kill the buffers */
- else {
- BKE_image_free_buffers(ima);
- }
- }
- ima = ima->id.next;
- }
-}
diff --git a/source/blender/gpu/intern/gpu_element.c b/source/blender/gpu/intern/gpu_element.cc
index 036588b4a48..9f104ab3fec 100644
--- a/source/blender/gpu/intern/gpu_element.c
+++ b/source/blender/gpu/intern/gpu_element.cc
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "GPU_element.h"
+#include "GPU_glew.h"
#include "gpu_context_private.h"
@@ -37,21 +38,18 @@
static GLenum convert_index_type_to_gl(GPUIndexBufType type)
{
- static const GLenum table[] = {
- [GPU_INDEX_U16] = GL_UNSIGNED_SHORT,
- [GPU_INDEX_U32] = GL_UNSIGNED_INT,
- };
- return table[type];
+#if GPU_TRACK_INDEX_RANGE
+ return (type == GPU_INDEX_U32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
+#else
+ return GL_UNSIGNED_INT;
+#endif
}
uint GPU_indexbuf_size_get(const GPUIndexBuf *elem)
{
#if GPU_TRACK_INDEX_RANGE
- static const uint table[] = {
- [GPU_INDEX_U16] = sizeof(GLushort),
- [GPU_INDEX_U32] = sizeof(GLuint),
- };
- return elem->index_len * table[elem->index_type];
+ return elem->index_len *
+ ((elem->index_type == GPU_INDEX_U32) ? sizeof(GLuint) : sizeof(GLshort));
#else
return elem->index_len * sizeof(GLuint);
#endif
@@ -86,7 +84,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder,
builder->max_index_len = index_len;
builder->index_len = 0; // start empty
builder->prim_type = prim_type;
- builder->data = MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data");
+ builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data");
}
void GPU_indexbuf_init(GPUIndexBufBuilder *builder,
@@ -241,7 +239,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length)
{
- GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
+ GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
GPU_indexbuf_create_subrange_in_place(elem, elem_src, start, length);
return elem;
}
@@ -331,7 +329,7 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder,
GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder)
{
- GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
+ GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf");
GPU_indexbuf_build_in_place(builder, elem);
return elem;
}
diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.cc
index 469abefca68..32c1bf6e2d3 100644
--- a/source/blender/gpu/intern/gpu_extensions.c
+++ b/source/blender/gpu/intern/gpu_extensions.cc
@@ -31,6 +31,8 @@
#include "BKE_global.h"
#include "MEM_guardedalloc.h"
+#include "DNA_userdef_types.h"
+
#include "GPU_extensions.h"
#include "GPU_framebuffer.h"
#include "GPU_glew.h"
@@ -71,7 +73,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 +86,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 +98,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};
@@ -104,7 +106,8 @@ static struct GPUGlobal {
static void gpu_detect_mip_render_workaround(void)
{
int cube_size = 2;
- float *source_pix = MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, __func__);
+ float *source_pix = (float *)MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size,
+ __func__);
float clear_color[4] = {1.0f, 0.5f, 0.0f, 0.0f};
GPUTexture *tex = GPU_texture_create_cube(cube_size, GPU_RGBA16F, source_pix, NULL);
@@ -123,7 +126,7 @@ static void gpu_detect_mip_render_workaround(void)
GPU_framebuffer_restore();
GPU_framebuffer_free(fb);
- float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 1);
+ float *data = (float *)GPU_texture_read(tex, GPU_DATA_FLOAT, 1);
GG.mip_render_workaround = !equals_v4v4(clear_color, data);
MEM_freeN(data);
@@ -238,6 +241,13 @@ bool GPU_crappy_amd_driver(void)
return GG.broken_amd_driver;
}
+int GPU_texture_size_with_limit(int res)
+{
+ int size = GPU_max_texture_size();
+ int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size;
+ return min_ii(reslimit, res);
+}
+
void gpu_extensions_init(void)
{
/* during 2.8 development each platform has its own OpenGL minimum requirements
@@ -288,6 +298,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")) {
@@ -298,13 +321,20 @@ void gpu_extensions_init(void)
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) {
/* Limit this fix to older hardware with GL < 4.5. This means Broadwell GPUs are
* covered since they only support GL 4.4 on windows.
- * This fixes some issues with workbench antialiasing on Win + Intel GPU. (see T76273) */
+ * This fixes some issues with workbench anti-aliasing on Win + Intel GPU. (see T76273) */
if (!GLEW_VERSION_4_5) {
GG.texture_copy_workaround = true;
}
}
- GG.glew_arb_base_instance_is_supported = GLEW_ARB_base_instance;
+ /* Limit support for GLEW_ARB_base_instance to OpenGL 4.0 and higher. NVIDIA Quadro FX 4800
+ * (TeraScale) report that they support GLEW_ARB_base_instance, but the driver does not support
+ * GLEW_ARB_draw_indirect as it has an OpenGL3 context what also matches the minimum needed
+ * requirements.
+ *
+ * We use it as a target for glMapBuffer(Range) what is part of the OpenGL 4 API. So better
+ * disable it when we don't have an OpenGL4 context (See T77657) */
+ GG.glew_arb_base_instance_is_supported = GLEW_ARB_base_instance && GLEW_VERSION_4_0;
GG.glew_arb_texture_cube_map_array_is_supported = GLEW_ARB_texture_cube_map_array;
gpu_detect_mip_render_workaround();
@@ -319,7 +349,6 @@ void gpu_extensions_init(void)
GG.depth_blitting_workaround = true;
GG.unused_fb_slot_workaround = true;
GG.texture_copy_workaround = true;
- GG.context_local_shaders_workaround = GLEW_ARB_get_program_binary;
}
/* Special fix for theses specific GPUs.
@@ -327,7 +356,13 @@ void gpu_extensions_init(void)
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) &&
(strstr(renderer, "HD Graphics 620") || strstr(renderer, "HD Graphics 630"))) {
GG.mip_render_workaround = true;
- GG.context_local_shaders_workaround = GLEW_ARB_get_program_binary;
+ }
+
+ /* Intel Ivy Bridge GPU's seems to have buggy cube-map array support. (see T75943) */
+ if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) &&
+ (strstr(renderer, "HD Graphics 4000") || strstr(renderer, "HD Graphics 4400") ||
+ strstr(renderer, "HD Graphics 2500"))) {
+ GG.glew_arb_texture_cube_map_array_is_supported = false;
}
/* df/dy calculation factors, those are dependent on driver */
@@ -385,7 +420,7 @@ void gpu_extensions_exit(void)
bool GPU_mem_stats_supported(void)
{
#ifndef GPU_STANDALONE
- return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo) && (G.debug & G_DEBUG_GPU_MEM);
+ return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo);
#else
return false;
#endif
@@ -413,3 +448,11 @@ void GPU_mem_stats_get(int *totalmem, int *freemem)
*freemem = 0;
}
}
+
+/* Return support for the active context + window. */
+bool GPU_stereo_quadbuffer_support(void)
+{
+ GLboolean stereo = GL_FALSE;
+ glGetBooleanv(GL_STEREO, &stereo);
+ return stereo == GL_TRUE;
+}
diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.cc
index 5af9364b92c..056a449ac6d 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.c
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -28,7 +28,6 @@
#include "BLI_utildefines.h"
#include "GPU_batch.h"
-#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_framebuffer.h"
#include "GPU_shader.h"
@@ -53,6 +52,10 @@ typedef enum {
GPU_FB_MAX_ATTACHEMENT,
} GPUAttachmentType;
+#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \
+ for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \
+ att = static_cast<GPUAttachmentType>(att + 1))
+
#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0)
#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15)
@@ -74,17 +77,25 @@ struct GPUFrameBuffer {
static GLenum convert_attachment_type_to_gl(GPUAttachmentType type)
{
- static const GLenum table[] = {
- [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT,
- [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT,
- [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0,
- [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1,
- [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2,
- [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3,
- [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4,
- [GPU_FB_COLOR_ATTACHMENT5] = GL_COLOR_ATTACHMENT5,
- };
- return table[type];
+#define ATTACHMENT(type) \
+ case GPU_FB_##type: { \
+ return GL_##type; \
+ } \
+ ((void)0)
+
+ switch (type) {
+ ATTACHMENT(DEPTH_ATTACHMENT);
+ ATTACHMENT(DEPTH_STENCIL_ATTACHMENT);
+ ATTACHMENT(COLOR_ATTACHMENT0);
+ ATTACHMENT(COLOR_ATTACHMENT1);
+ ATTACHMENT(COLOR_ATTACHMENT2);
+ ATTACHMENT(COLOR_ATTACHMENT3);
+ ATTACHMENT(COLOR_ATTACHMENT4);
+ ATTACHMENT(COLOR_ATTACHMENT5);
+ default:
+ BLI_assert(0);
+ return GL_COLOR_ATTACHMENT0;
+ }
}
static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot)
@@ -98,7 +109,7 @@ static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot)
case GPU_DEPTH32F_STENCIL8:
return GPU_FB_DEPTH_STENCIL_ATTACHMENT;
default:
- return GPU_FB_COLOR_ATTACHMENT0 + slot;
+ return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot);
}
}
@@ -198,7 +209,7 @@ GPUFrameBuffer *GPU_framebuffer_create(void)
{
/* We generate the FB object later at first use in order to
* create the framebuffer in the right opengl context. */
- return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
+ return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
}
static void gpu_framebuffer_init(GPUFrameBuffer *fb)
@@ -210,7 +221,8 @@ static void gpu_framebuffer_init(GPUFrameBuffer *fb)
void GPU_framebuffer_free(GPUFrameBuffer *fb)
{
- for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) {
+ for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) {
+ GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
if (fb->attachments[type].tex != NULL) {
GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex);
}
@@ -304,7 +316,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, in
void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex)
{
- GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb);
+ GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb);
GPU_framebuffer_texture_detach_slot(fb, tex, type);
}
@@ -387,8 +399,8 @@ static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb)
BLI_assert(GPU_framebuffer_active_get() == fb);
/* Update attachments */
- for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) {
-
+ FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT)
+ {
if (type >= GPU_FB_COLOR_ATTACHMENT0) {
if (fb->attachments[type].tex) {
gl_attachments[numslots] = convert_attachment_type_to_gl(type);
@@ -438,7 +450,8 @@ static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuff
BLI_assert(GPU_framebuffer_active_get() == fb);
/* Update attachments */
- for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT; type--;) {
+ for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) {
+ GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
GPUTexture *tex = fb->attachments[type].tex;
if (type >= GPU_FB_COLOR_ATTACHMENT0) {
@@ -623,8 +636,9 @@ void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0;
- for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) {
+ int i_type = GPU_FB_COLOR_ATTACHMENT0;
+ for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) {
+ GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
if (fb->attachments[type].tex != NULL) {
glClearBufferfv(GL_COLOR, i, clear_cols[i]);
}
@@ -640,31 +654,70 @@ void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h,
glReadPixels(x, y, w, h, type, GL_FLOAT, data);
}
-void GPU_framebuffer_read_color(
- GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data)
+static GLenum gpu_get_gl_datatype(eGPUDataFormat format)
{
- CHECK_FRAMEBUFFER_IS_BOUND(fb);
+ switch (format) {
+ case GPU_DATA_FLOAT:
+ return GL_FLOAT;
+ case GPU_DATA_INT:
+ return GL_INT;
+ case GPU_DATA_UNSIGNED_INT:
+ return GL_UNSIGNED_INT;
+ case GPU_DATA_UNSIGNED_BYTE:
+ return GL_UNSIGNED_BYTE;
+ case GPU_DATA_UNSIGNED_INT_24_8:
+ return GL_UNSIGNED_INT_24_8;
+ case GPU_DATA_10_11_11_REV:
+ return GL_UNSIGNED_INT_10F_11F_11F_REV;
+ default:
+ BLI_assert(!"Unhandled data format");
+ return GL_FLOAT;
+ }
+}
- GLenum type;
+static GLenum gpu_get_gl_channel_type(int channels)
+{
switch (channels) {
case 1:
- type = GL_RED;
- break;
+ return GL_RED;
case 2:
- type = GL_RG;
- break;
+ return GL_RG;
case 3:
- type = GL_RGB;
- break;
+ return GL_RGB;
case 4:
- type = GL_RGBA;
- break;
+ return GL_RGBA;
default:
- BLI_assert(false && "wrong number of read channels");
- return;
+ BLI_assert(!"Wrong number of read channels");
+ return GL_RED;
}
- glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
- glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
+
+static void gpu_framebuffer_read_color_ex(
+ int x, int y, int w, int h, int channels, GLenum readfb, eGPUDataFormat format, float *data)
+{
+ GLenum type = gpu_get_gl_channel_type(channels);
+ GLenum gl_format = gpu_get_gl_datatype(format);
+ /* TODO: needed for selection buffers to work properly, this should be handled better. */
+ if (type == GL_RED && gl_format == GL_UNSIGNED_INT) {
+ type = GL_RED_INTEGER;
+ }
+ glReadBuffer(readfb);
+ glReadPixels(x, y, w, h, type, gl_format, data);
+}
+
+void GPU_framebuffer_read_color(GPUFrameBuffer *fb,
+ int x,
+ int y,
+ int w,
+ int h,
+ int channels,
+ int slot,
+ eGPUDataFormat format,
+ void *data)
+{
+ CHECK_FRAMEBUFFER_IS_BOUND(fb);
+ gpu_framebuffer_read_color_ex(
+ x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data);
}
/* read_slot and write_slot are only used for color buffers. */
@@ -749,9 +802,9 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read,
}
/**
- * Use this if you need to custom down-sample your texture and use the previous mip level as input.
- * This function only takes care of the correct texture handling.
- * It execute the callback for each texture level.
+ * Use this if you need to custom down-sample your texture and use the previous mip level as
+ * input. This function only takes care of the correct texture handling. It execute the callback
+ * for each texture level.
*/
void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
int max_lvl,
@@ -777,7 +830,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
current_dim[0] = max_ii(current_dim[0] / 2, 1);
current_dim[1] = max_ii(current_dim[1] / 2, 1);
- for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) {
+ for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) {
+ GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
if (fb->attachments[type].tex != NULL) {
/* Some Intel HDXXX have issue with rendering to a mipmap that is below
* the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case
@@ -806,7 +860,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
}
}
- for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) {
+ for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) {
+ GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type);
if (fb->attachments[type].tex != NULL) {
/* reset mipmap level range */
GPUTexture *tex = fb->attachments[type].tex;
@@ -847,9 +902,11 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs)
for (int i = 0; i < MAX_CTX_FB_LEN; i++) {
if (ofs->framebuffers[i].fb == NULL) {
ofs->framebuffers[i].ctx = ctx;
- GPU_framebuffer_ensure_config(
- &ofs->framebuffers[i].fb,
- {GPU_ATTACHMENT_TEXTURE(ofs->depth), GPU_ATTACHMENT_TEXTURE(ofs->color)});
+ GPU_framebuffer_ensure_config(&ofs->framebuffers[i].fb,
+ {
+ GPU_ATTACHMENT_TEXTURE(ofs->depth),
+ GPU_ATTACHMENT_TEXTURE(ofs->color),
+ });
}
if (ofs->framebuffers[i].ctx == ctx) {
@@ -876,23 +933,20 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs)
}
GPUOffScreen *GPU_offscreen_create(
- int width, int height, int samples, bool depth, bool high_bitdepth, char err_out[256])
+ int width, int height, bool depth, bool high_bitdepth, char err_out[256])
{
- GPUOffScreen *ofs;
-
- ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen");
+ GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__);
/* Sometimes areas can have 0 height or width and this will
* create a 1D texture which we don't want. */
height = max_ii(1, height);
width = max_ii(1, width);
- ofs->color = GPU_texture_create_2d_multisample(
- width, height, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, samples, err_out);
+ ofs->color = GPU_texture_create_2d(
+ width, height, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, err_out);
if (depth) {
- ofs->depth = GPU_texture_create_2d_multisample(
- width, height, GPU_DEPTH24_STENCIL8, NULL, samples, err_out);
+ ofs->depth = GPU_texture_create_2d(width, height, GPU_DEPTH24_STENCIL8, NULL, err_out);
}
if ((depth && !ofs->depth) || !ofs->color) {
@@ -938,14 +992,14 @@ void GPU_offscreen_free(GPUOffScreen *ofs)
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save)
{
if (save) {
- gpuPushAttr(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT);
+ gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT));
GPUFrameBuffer *fb = GPU_framebuffer_active_get();
gpuPushFrameBuffer(fb);
}
- glDisable(GL_SCISSOR_TEST);
GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
GPU_framebuffer_bind(ofs_fb);
glDisable(GL_FRAMEBUFFER_SRGB);
+ GPU_scissor_test(false);
GPU_shader_set_framebuffer_srgb_target(false);
}
@@ -986,55 +1040,15 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y)
glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default());
}
-void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
+void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels)
{
const int w = GPU_texture_width(ofs->color);
const int h = GPU_texture_height(ofs->color);
- BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT);
-
- if (GPU_texture_target(ofs->color) == GL_TEXTURE_2D_MULTISAMPLE) {
- /* For a multi-sample texture,
- * we need to create an intermediate buffer to blit to,
- * before its copied using 'glReadPixels' */
- GLuint fbo_blit = 0;
- GLuint tex_blit = 0;
-
- /* create texture for new 'fbo_blit' */
- glGenTextures(1, &tex_blit);
- glBindTexture(GL_TEXTURE_2D, tex_blit);
- glTexImage2D(
- GL_TEXTURE_2D, 0, (type == GL_FLOAT) ? GL_RGBA16F : GL_RGBA8, w, h, 0, GL_RGBA, type, 0);
-
- /* write into new single-sample buffer */
- glGenFramebuffers(1, &fbo_blit);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_blit, 0);
-
- GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- goto finally;
- }
-
- /* perform the copy */
- glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
-
- /* read the results */
- glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_blit);
- glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
-
- /* restore the original frame-bufer */
- GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs);
- glBindFramebuffer(GL_FRAMEBUFFER, ofs_fb->object);
+ BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT));
+ GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE;
- finally:
- /* cleanup */
- glDeleteTextures(1, &tex_blit);
- glDeleteFramebuffers(1, &fbo_blit);
- }
- else {
- glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
- }
+ glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels);
}
int GPU_offscreen_width(const GPUOffScreen *ofs)
@@ -1077,3 +1091,23 @@ void GPU_clear(eGPUFrameBufferBits flags)
{
glClear(convert_buffer_bits_to_gl(flags));
}
+
+void GPU_frontbuffer_read_pixels(
+ int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data)
+{
+ gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data);
+}
+
+/* For stereo rendering. */
+void GPU_backbuffer_bind(eGPUBackBuffer buffer)
+{
+ if (buffer == GPU_BACKBUFFER) {
+ glDrawBuffer(GL_BACK);
+ }
+ else if (buffer == GPU_BACKBUFFER_LEFT) {
+ glDrawBuffer(GL_BACK_LEFT);
+ }
+ else if (buffer == GPU_BACKBUFFER_RIGHT) {
+ glDrawBuffer(GL_BACK_RIGHT);
+ }
+}
diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.cc
index 9ea273f33cf..ac3e653c9ff 100644
--- a/source/blender/gpu/intern/gpu_immediate.c
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -29,6 +29,8 @@
#include "GPU_attr_binding.h"
#include "GPU_immediate.h"
+#include "GPU_matrix.h"
+#include "GPU_texture.h"
#include "gpu_attr_binding_private.h"
#include "gpu_context_private.h"
@@ -39,10 +41,6 @@
#include <stdlib.h>
#include <string.h>
-/* necessary functions from matrix API */
-extern void GPU_matrix_bind(const GPUShaderInterface *);
-extern bool GPU_matrix_dirty_get(void);
-
typedef struct ImmediateDrawBuffer {
GLuint vbo_id;
GLubyte *buffer_data;
@@ -74,7 +72,7 @@ typedef struct {
GLuint vao_id;
- GLuint bound_program;
+ GPUShader *bound_program;
const GPUShaderInterface *shader_interface;
GPUAttrBinding attr_binding;
uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */
@@ -145,48 +143,47 @@ GPUVertFormat *immVertexFormat(void)
return &imm.vertex_format;
}
-void immBindProgram(GLuint program, const GPUShaderInterface *shaderface)
+void immBindShader(GPUShader *shader)
{
#if TRUST_NO_ONE
- assert(imm.bound_program == 0);
- assert(glIsProgram(program));
+ assert(imm.bound_program == NULL);
+ assert(glIsProgram(shader->program));
#endif
- imm.bound_program = program;
- imm.shader_interface = shaderface;
+ imm.bound_program = shader;
+ imm.shader_interface = shader->interface;
if (!imm.vertex_format.packed) {
VertexFormat_pack(&imm.vertex_format);
}
- glUseProgram(program);
- get_attr_locations(&imm.vertex_format, &imm.attr_binding, shaderface);
- GPU_matrix_bind(shaderface);
- GPU_shader_set_srgb_uniform(shaderface);
+ GPU_shader_bind(shader);
+ get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface);
+ GPU_matrix_bind(imm.shader_interface);
+ GPU_shader_set_srgb_uniform(imm.shader_interface);
}
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
{
GPUShader *shader = GPU_shader_get_builtin_shader(shader_id);
- immBindProgram(shader->program, shader->interface);
+ immBindShader(shader);
}
void immUnbindProgram(void)
{
#if TRUST_NO_ONE
- assert(imm.bound_program != 0);
+ assert(imm.bound_program != NULL);
#endif
#if PROGRAM_NO_OPTI
glUseProgram(0);
#endif
- imm.bound_program = 0;
+ imm.bound_program = NULL;
}
/* XXX do not use it. Special hack to use OCIO with batch API. */
-void immGetProgram(GLuint *program, GPUShaderInterface **shaderface)
+GPUShader *immGetShader(void)
{
- *program = imm.bound_program;
- *shaderface = (GPUShaderInterface *)imm.shader_interface;
+ return imm.bound_program;
}
#if TRUST_NO_ONE
@@ -280,7 +277,7 @@ void immBegin(GPUPrimType prim_type, uint vertex_len)
}
#endif
- active_buffer->buffer_data = glMapBufferRange(
+ active_buffer->buffer_data = (GLubyte *)glMapBufferRange(
GL_ARRAY_BUFFER,
active_buffer->buffer_offset,
bytes_needed,
@@ -366,17 +363,18 @@ static void immDrawSetup(void)
const GLvoid *pointer = (const GLubyte *)0 + offset;
const uint loc = read_attr_location(&imm.attr_binding, a_idx);
+ const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type));
switch (a->fetch_mode) {
case GPU_FETCH_FLOAT:
case GPU_FETCH_INT_TO_FLOAT:
- glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer);
+ glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer);
break;
case GPU_FETCH_INT_TO_FLOAT_UNIT:
- glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer);
+ glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer);
break;
case GPU_FETCH_INT:
- glVertexAttribIPointer(loc, a->comp_len, a->gl_comp_type, stride, pointer);
+ glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer);
}
}
@@ -424,7 +422,7 @@ void immEnd(void)
GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len);
/* TODO: resize only if vertex count is much smaller */
}
- GPU_batch_program_set(imm.batch, imm.bound_program, imm.shader_interface);
+ GPU_batch_set_shader(imm.batch, imm.bound_program);
imm.batch->phase = GPU_BATCH_READY_TO_DRAW;
imm.batch = NULL; /* don't free, batch belongs to caller */
}
@@ -853,6 +851,18 @@ void immUniform4iv(const char *name, const int data[4])
glUniform4iv(uniform->location, 1, data);
}
+void immBindTexture(const char *name, GPUTexture *tex)
+{
+ GET_UNIFORM
+ GPU_texture_bind(tex, uniform->binding);
+}
+
+void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state)
+{
+ GET_UNIFORM
+ GPU_texture_bind_ex(tex, state, uniform->binding, true);
+}
+
/* --- convenience functions for setting "uniform vec4 color" --- */
void immUniformColor4f(float r, float g, float b, float a)
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index d2384b9c065..f3477b6f3a4 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -85,7 +85,7 @@ struct GPUMaterial {
bool has_surface_output;
/* Only used by Eevee to know which bsdf are used. */
- int flag;
+ eGPUMatFlag flag;
/* Used by 2.8 pipeline */
GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
@@ -497,8 +497,8 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd,
void GPU_material_sss_profile_create(GPUMaterial *material,
float radii[3],
- short *falloff_type,
- float *sharpness)
+ const short *falloff_type,
+ const float *sharpness)
{
copy_v3_v3(material->sss_radii, radii);
material->sss_falloff = (falloff_type) ? *falloff_type : 0.0;
@@ -659,7 +659,8 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
const char *geom_code,
const char *frag_lib,
const char *defines,
- const char *name)
+ const char *name,
+ GPUMaterialEvalCallbackFn callback)
{
LinkData *link;
bool has_volume_output, has_surface_output;
@@ -696,6 +697,9 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
mat->has_volume_output = has_volume_output;
if (mat->graph.outlink) {
+ if (callback) {
+ callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines);
+ }
/* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */
if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
defines = BLI_string_joinN(defines,
diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c
index 2687f56ad27..669bf56b726 100644
--- a/source/blender/gpu/intern/gpu_matrix.c
+++ b/source/blender/gpu/intern/gpu_matrix.c
@@ -536,13 +536,13 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
const int view[4])
{
precalc->is_persp = proj[3][3] == 0.0f;
- projmat_dimensions(proj,
- &precalc->dims.xmin,
- &precalc->dims.xmax,
- &precalc->dims.ymin,
- &precalc->dims.ymax,
- &precalc->dims.zmin,
- &precalc->dims.zmax);
+ projmat_dimensions_db(proj,
+ &precalc->dims.xmin,
+ &precalc->dims.xmax,
+ &precalc->dims.ymin,
+ &precalc->dims.ymax,
+ &precalc->dims.zmin,
+ &precalc->dims.zmax);
if (isinf(precalc->dims.zmax)) {
/* We cannot retrieve the actual value of the clip_end.
* Use `FLT_MAX` to avoid nans. */
@@ -738,3 +738,69 @@ int GPU_matrix_stack_level_get_projection(void)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Polygon Offset Hack
+ *
+ * Workaround the fact that polygon-offset is implementation dependent.
+ * We modify the projection matrix \a winmat in order to change the final depth a tiny amount.
+ * \{ */
+
+float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float dist)
+{
+ /* Seems like we have a factor of 2 more offset than 2.79 for some reason. Correct for this. */
+ dist *= 0.5f;
+
+ if (winmat[3][3] > 0.5f) {
+#if 1
+ return 0.00001f * dist * viewdist; // ortho tweaking
+#else
+ static float depth_fac = 0.0f;
+ if (depth_fac == 0.0f) {
+ int depthbits;
+ glGetIntegerv(GL_DEPTH_BITS, &depthbits);
+ depth_fac = 1.0f / (float)((1 << depthbits) - 1);
+ }
+ offs = (-1.0 / winmat[2][2]) * dist * depth_fac;
+
+ UNUSED_VARS(viewdist);
+#endif
+ }
+
+ /* This adjustment effectively results in reducing the Z value by 0.25%.
+ *
+ * winmat[4][3] actually evaluates to `-2 * far * near / (far - near)`,
+ * is very close to -0.2 with default clip range,
+ * and is used as the coefficient multiplied by `w / z`,
+ * thus controlling the z dependent part of the depth value.
+ */
+ return winmat[3][2] * -0.0025f * dist;
+}
+
+/**
+ * \note \a viewdist is only for ortho at the moment.
+ */
+void GPU_polygon_offset(float viewdist, float dist)
+{
+ static float winmat[4][4], offset = 0.0f;
+
+ if (dist != 0.0f) {
+ /* hack below is to mimic polygon offset */
+ GPU_matrix_projection_get(winmat);
+
+ /* dist is from camera to center point */
+
+ float offs = GPU_polygon_offset_calc(winmat, viewdist, dist);
+
+ winmat[3][2] -= offs;
+ offset += offs;
+ }
+ else {
+ winmat[3][2] += offset;
+ offset = 0.0;
+ }
+
+ GPU_matrix_projection_set(winmat);
+}
+
+/** \} */
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index 3067be1c485..a133dd1ccdc 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -29,7 +29,6 @@
#include "DNA_customdata_types.h"
#include "DNA_listBase.h"
-#include "GPU_glew.h"
#include "GPU_material.h"
#include "GPU_shader.h"
@@ -116,7 +115,7 @@ typedef struct GPUInput {
struct GPUInput *next, *prev;
GPUNode *node;
- eGPUType type; /* datatype */
+ eGPUType type; /* data-type. */
GPUNodeLink *link;
int id; /* unique id as created by code generator */
diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.cc
index a758787466f..5cabde61bc3 100644
--- a/source/blender/gpu/intern/gpu_platform.c
+++ b/source/blender/gpu/intern/gpu_platform.cc
@@ -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_primitive.c b/source/blender/gpu/intern/gpu_primitive.c
index 2285c1ab95b..3b11b38db87 100644
--- a/source/blender/gpu/intern/gpu_primitive.c
+++ b/source/blender/gpu/intern/gpu_primitive.c
@@ -26,35 +26,6 @@
#include "GPU_primitive.h"
#include "gpu_primitive_private.h"
-GPUPrimClass GPU_primtype_class(GPUPrimType prim_type)
-{
- static const GPUPrimClass classes[] = {
- [GPU_PRIM_POINTS] = GPU_PRIM_CLASS_POINT,
- [GPU_PRIM_LINES] = GPU_PRIM_CLASS_LINE,
- [GPU_PRIM_LINE_STRIP] = GPU_PRIM_CLASS_LINE,
- [GPU_PRIM_LINE_LOOP] = GPU_PRIM_CLASS_LINE,
- [GPU_PRIM_TRIS] = GPU_PRIM_CLASS_SURFACE,
- [GPU_PRIM_TRI_STRIP] = GPU_PRIM_CLASS_SURFACE,
- [GPU_PRIM_TRI_FAN] = GPU_PRIM_CLASS_SURFACE,
-
- [GPU_PRIM_LINES_ADJ] = GPU_PRIM_CLASS_LINE,
- [GPU_PRIM_LINE_STRIP_ADJ] = GPU_PRIM_CLASS_LINE,
- [GPU_PRIM_TRIS_ADJ] = GPU_PRIM_CLASS_SURFACE,
-
- [GPU_PRIM_NONE] = GPU_PRIM_CLASS_NONE,
- };
-
- return classes[prim_type];
-}
-
-bool GPU_primtype_belongs_to_class(GPUPrimType prim_type, GPUPrimClass prim_class)
-{
- if (prim_class == GPU_PRIM_CLASS_NONE && prim_type == GPU_PRIM_NONE) {
- return true;
- }
- return prim_class & GPU_primtype_class(prim_type);
-}
-
GLenum convert_prim_type_to_gl(GPUPrimType prim_type)
{
#if TRUST_NO_ONE
diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/gpu/intern/gpu_primitive_private.h
index abefa6abd20..b7d7b262128 100644
--- a/source/blender/gpu/intern/gpu_primitive_private.h
+++ b/source/blender/gpu/intern/gpu_primitive_private.h
@@ -26,6 +26,15 @@
#ifndef __GPU_PRIMITIVE_PRIVATE_H__
#define __GPU_PRIMITIVE_PRIVATE_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* TODO(fclem) move to OGL backend */
GLenum convert_prim_type_to_gl(GPUPrimType);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __GPU_PRIMITIVE_PRIVATE_H__ */
diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h
index 7846bff87f4..ed729dd399c 100644
--- a/source/blender/gpu/intern/gpu_private.h
+++ b/source/blender/gpu/intern/gpu_private.h
@@ -21,6 +21,10 @@
#ifndef __GPU_PRIVATE_H__
#define __GPU_PRIVATE_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* call this before running any of the functions below */
void gpu_platform_init(void);
void gpu_platform_exit(void);
@@ -41,4 +45,8 @@ void gpu_framebuffer_module_exit(void);
void gpu_pbvh_init(void);
void gpu_pbvh_exit(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __GPU_PRIVATE_H__ */
diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c
index 5766a176a96..c069cbe012f 100644
--- a/source/blender/gpu/intern/gpu_select.c
+++ b/source/blender/gpu/intern/gpu_select.c
@@ -26,7 +26,6 @@
#include <stdlib.h>
#include <string.h>
-#include "GPU_glew.h"
#include "GPU_select.h"
#include "MEM_guardedalloc.h"
diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c
index 4b38cd333a1..3025b5d66da 100644
--- a/source/blender/gpu/intern/gpu_select_pick.c
+++ b/source/blender/gpu/intern/gpu_select_pick.c
@@ -27,7 +27,6 @@
#include <stdlib.h>
#include <string.h>
-#include "GPU_draw.h"
#include "GPU_glew.h"
#include "GPU_immediate.h"
#include "GPU_select.h"
@@ -310,7 +309,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT);
/* disable writing to the framebuffer */
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ GPU_color_mask(false, false, false, false);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
@@ -320,7 +319,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
glDepthFunc(GL_LEQUAL);
float viewport[4];
- glGetFloatv(GL_VIEWPORT, viewport);
+ GPU_viewport_size_get_f(viewport);
ps->src.clip_rect = *input;
ps->src.rect_len = rect_len;
@@ -330,7 +329,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c
ps->gl.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect);
ps->gl.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect);
- glViewport(UNPACK4(ps->gl.clip_readpixels));
+ GPU_viewport(UNPACK4(ps->gl.clip_readpixels));
/* It's possible we don't want to clear depth buffer,
* so existing elements are masked by current z-buffer. */
@@ -539,7 +538,7 @@ uint gpu_select_pick_end(void)
gpu_select_pick_load_id(ps->gl.prev_id, true);
}
gpuPopAttr();
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(true, true, true, true);
}
/* assign but never free directly since it may be in cache */
diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c
index c82d1e17c66..70ad2f6759e 100644
--- a/source/blender/gpu/intern/gpu_select_sample_query.c
+++ b/source/blender/gpu/intern/gpu_select_sample_query.c
@@ -88,14 +88,14 @@ void gpu_select_query_begin(
gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT);
/* disable writing to the framebuffer */
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ GPU_color_mask(false, false, false, false);
/* In order to save some fill rate we minimize the viewport using rect.
* We need to get the region of the viewport so that our geometry doesn't
* get rejected before the depth test. Should probably cull rect against
* the viewport but this is a rare case I think */
- glGetFloatv(GL_VIEWPORT, viewport);
- glViewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input));
+ GPU_viewport_size_get_f(viewport);
+ GPU_viewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input));
/* occlusion queries operates on fragments that pass tests and since we are interested on all
* objects in the view frustum independently of their order, we need to disable the depth test */
@@ -206,7 +206,7 @@ uint gpu_select_query_end(void)
MEM_freeN(g_query_state.queries);
MEM_freeN(g_query_state.id);
gpuPopAttr();
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(true, true, true, true);
return hits;
}
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
new file mode 100644
index 00000000000..2b54e733bf5
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -0,0 +1,839 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math_base.h"
+#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_appdir.h"
+#include "BKE_global.h"
+
+#include "DNA_space_types.h"
+
+#include "GPU_extensions.h"
+#include "GPU_matrix.h"
+#include "GPU_platform.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
+
+#include "gpu_shader_private.h"
+
+extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[];
+
+/* Adjust these constants as needed. */
+#define MAX_DEFINE_LENGTH 256
+#define MAX_EXT_DEFINE_LENGTH 512
+
+#ifndef NDEBUG
+static uint g_shaderid = 0;
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Convenience functions
+ * \{ */
+
+static void shader_print_errors(const char *task, const char *log, const char **code, int totcode)
+{
+ int line = 1;
+
+ fprintf(stderr, "GPUShader: %s error:\n", task);
+
+ for (int i = 0; i < totcode; i++) {
+ const char *c, *pos, *end = code[i] + strlen(code[i]);
+
+ if (G.debug & G_DEBUG) {
+ fprintf(stderr, "===== shader string %d ====\n", i + 1);
+
+ c = code[i];
+ while ((c < end) && (pos = strchr(c, '\n'))) {
+ fprintf(stderr, "%2d ", line);
+ fwrite(c, (pos + 1) - c, 1, stderr);
+ c = pos + 1;
+ line++;
+ }
+
+ fprintf(stderr, "%s", c);
+ }
+ }
+
+ fprintf(stderr, "%s\n", log);
+}
+
+static const char *gpu_shader_version(void)
+{
+ return "#version 330\n";
+}
+
+static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH])
+{
+ /* enable extensions for features that are not part of our base GLSL version
+ * don't use an extension for something already available!
+ */
+
+ if (GLEW_ARB_texture_gather) {
+ /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
+ * is reported to be supported but yield a compile error (see T55802). */
+ if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) {
+ strcat(defines, "#extension GL_ARB_texture_gather: enable\n");
+
+ /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
+ * shader so double check the preprocessor define (see T56544). */
+ if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
+ strcat(defines, "#ifdef GL_ARB_texture_gather\n");
+ strcat(defines, "# define GPU_ARB_texture_gather\n");
+ strcat(defines, "#endif\n");
+ }
+ else {
+ strcat(defines, "#define GPU_ARB_texture_gather\n");
+ }
+ }
+ }
+ if (GLEW_ARB_texture_query_lod) {
+ /* a #version 400 feature, but we use #version 330 maximum so use extension */
+ strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n");
+ }
+ if (GLEW_ARB_shader_draw_parameters) {
+ strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n");
+ }
+ if (GPU_arb_texture_cube_map_array_is_supported()) {
+ strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n");
+ strcat(defines, "#define GPU_ARB_texture_cube_map_array\n");
+ }
+}
+
+static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH])
+{
+ /* some useful defines to detect GPU type */
+ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ strcat(defines, "#define GPU_ATI\n");
+ if (GPU_crappy_amd_driver()) {
+ strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n");
+ }
+ }
+ else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ strcat(defines, "#define GPU_NVIDIA\n");
+ }
+ else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ strcat(defines, "#define GPU_INTEL\n");
+ }
+
+ /* some useful defines to detect OS type */
+ if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) {
+ strcat(defines, "#define OS_WIN\n");
+ }
+ else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
+ strcat(defines, "#define OS_MAC\n");
+ }
+ else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
+ strcat(defines, "#define OS_UNIX\n");
+ }
+
+ float derivatives_factors[2];
+ GPU_get_dfdy_factors(derivatives_factors);
+ if (derivatives_factors[0] == 1.0f) {
+ strcat(defines, "#define DFDX_SIGN 1.0\n");
+ }
+ else {
+ strcat(defines, "#define DFDX_SIGN -1.0\n");
+ }
+
+ if (derivatives_factors[1] == 1.0f) {
+ strcat(defines, "#define DFDY_SIGN 1.0\n");
+ }
+ else {
+ strcat(defines, "#define DFDY_SIGN -1.0\n");
+ }
+}
+
+#define DEBUG_SHADER_NONE ""
+#define DEBUG_SHADER_VERTEX "vert"
+#define DEBUG_SHADER_FRAGMENT "frag"
+#define DEBUG_SHADER_GEOMETRY "geom"
+
+/**
+ * Dump GLSL shaders to disk
+ *
+ * This is used for profiling shader performance externally and debug if shader code is correct.
+ * If called with no code, it simply bumps the shader index, so different shaders for the same
+ * program share the same index.
+ */
+static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension)
+{
+ if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) {
+ return;
+ }
+
+ /* We use the same shader index for shaders in the same program.
+ * So we call this function once before calling for the individual shaders. */
+ static int shader_index = 0;
+ if (code == NULL) {
+ shader_index++;
+ BLI_assert(STREQ(DEBUG_SHADER_NONE, extension));
+ return;
+ }
+
+ /* Determine the full path of the new shader. */
+ char shader_path[FILE_MAX];
+
+ char file_name[512] = {'\0'};
+ sprintf(file_name, "%04d.%s", shader_index, extension);
+
+ BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name);
+
+ /* Write shader to disk. */
+ FILE *f = fopen(shader_path, "w");
+ if (f == NULL) {
+ printf("Error writing to file: %s\n", shader_path);
+ }
+ for (int j = 0; j < num_shaders; j++) {
+ fprintf(f, "%s", code[j]);
+ }
+ fclose(f);
+ printf("Shader file written to disk: %s\n", shader_path);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Creation / Destruction
+ * \{ */
+
+GPUShader *GPU_shader_create(const char *vertexcode,
+ const char *fragcode,
+ const char *geocode,
+ const char *libcode,
+ const char *defines,
+ const char *shname)
+{
+ return GPU_shader_create_ex(
+ vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname);
+}
+
+GPUShader *GPU_shader_create_from_python(const char *vertexcode,
+ const char *fragcode,
+ const char *geocode,
+ const char *libcode,
+ const char *defines)
+{
+ char *libcodecat = NULL;
+
+ if (libcode == NULL) {
+ libcode = datatoc_gpu_shader_colorspace_lib_glsl;
+ }
+ else {
+ libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl);
+ }
+
+ GPUShader *sh = GPU_shader_create_ex(
+ vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL);
+
+ MEM_SAFE_FREE(libcodecat);
+ return sh;
+}
+
+GPUShader *GPU_shader_load_from_binary(const char *binary,
+ const int binary_format,
+ const int binary_len,
+ const char *shname)
+{
+ BLI_assert(GL_ARB_get_program_binary);
+ int success;
+ int program = glCreateProgram();
+
+ glProgramBinary(program, binary_format, binary, binary_len);
+ glGetProgramiv(program, GL_LINK_STATUS, &success);
+
+ if (success) {
+ glUseProgram(program);
+
+ GPUShader *shader = (GPUShader *)MEM_callocN(sizeof(*shader), __func__);
+ shader->interface = GPU_shaderinterface_create(program);
+ shader->program = program;
+
+#ifndef NDEBUG
+ BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
+#else
+ UNUSED_VARS(shname);
+#endif
+
+ return shader;
+ }
+
+ glDeleteProgram(program);
+ return NULL;
+}
+
+GPUShader *GPU_shader_create_ex(const char *vertexcode,
+ const char *fragcode,
+ const char *geocode,
+ const char *libcode,
+ const char *defines,
+ const eGPUShaderTFBType tf_type,
+ const char **tf_names,
+ const int tf_count,
+ const char *shname)
+{
+ GLint status;
+ GLchar log[5000];
+ GLsizei length = 0;
+ GPUShader *shader;
+ char standard_defines[MAX_DEFINE_LENGTH] = "";
+ char standard_extensions[MAX_EXT_DEFINE_LENGTH] = "";
+
+ shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader");
+ gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE);
+
+#ifndef NDEBUG
+ BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
+#else
+ UNUSED_VARS(shname);
+#endif
+
+ /* At least a vertex shader and a fragment shader are required. */
+ BLI_assert((fragcode != NULL) && (vertexcode != NULL));
+
+ if (vertexcode) {
+ shader->vertex = glCreateShader(GL_VERTEX_SHADER);
+ }
+ if (fragcode) {
+ shader->fragment = glCreateShader(GL_FRAGMENT_SHADER);
+ }
+ if (geocode) {
+ shader->geometry = glCreateShader(GL_GEOMETRY_SHADER);
+ }
+
+ shader->program = glCreateProgram();
+
+ if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) ||
+ (geocode && !shader->geometry)) {
+ fprintf(stderr, "GPUShader, object creation failed.\n");
+ GPU_shader_free(shader);
+ return NULL;
+ }
+
+ gpu_shader_standard_defines(standard_defines);
+ gpu_shader_standard_extensions(standard_extensions);
+
+ if (vertexcode) {
+ const char *source[7];
+ /* custom limit, may be too small, beware */
+ int num_source = 0;
+
+ source[num_source++] = gpu_shader_version();
+ source[num_source++] =
+ "#define GPU_VERTEX_SHADER\n"
+ "#define IN_OUT out\n";
+ source[num_source++] = standard_extensions;
+ source[num_source++] = standard_defines;
+
+ if (geocode) {
+ source[num_source++] = "#define USE_GEOMETRY_SHADER\n";
+ }
+ if (defines) {
+ source[num_source++] = defines;
+ }
+ source[num_source++] = vertexcode;
+
+ gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX);
+
+ glAttachShader(shader->program, shader->vertex);
+ glShaderSource(shader->vertex, num_source, source, NULL);
+
+ glCompileShader(shader->vertex);
+ glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status);
+
+ if (!status) {
+ glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log);
+ shader_print_errors("compile", log, source, num_source);
+
+ GPU_shader_free(shader);
+ return NULL;
+ }
+ }
+
+ if (fragcode) {
+ const char *source[8];
+ int num_source = 0;
+
+ source[num_source++] = gpu_shader_version();
+ source[num_source++] =
+ "#define GPU_FRAGMENT_SHADER\n"
+ "#define IN_OUT in\n";
+ source[num_source++] = standard_extensions;
+ source[num_source++] = standard_defines;
+
+ if (geocode) {
+ source[num_source++] = "#define USE_GEOMETRY_SHADER\n";
+ }
+ if (defines) {
+ source[num_source++] = defines;
+ }
+ if (libcode) {
+ source[num_source++] = libcode;
+ }
+ source[num_source++] = fragcode;
+
+ gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT);
+
+ glAttachShader(shader->program, shader->fragment);
+ glShaderSource(shader->fragment, num_source, source, NULL);
+
+ glCompileShader(shader->fragment);
+ glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status);
+
+ if (!status) {
+ glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log);
+ shader_print_errors("compile", log, source, num_source);
+
+ GPU_shader_free(shader);
+ return NULL;
+ }
+ }
+
+ if (geocode) {
+ const char *source[6];
+ int num_source = 0;
+
+ source[num_source++] = gpu_shader_version();
+ source[num_source++] = "#define GPU_GEOMETRY_SHADER\n";
+ source[num_source++] = standard_extensions;
+ source[num_source++] = standard_defines;
+
+ if (defines) {
+ source[num_source++] = defines;
+ }
+ source[num_source++] = geocode;
+
+ gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY);
+
+ glAttachShader(shader->program, shader->geometry);
+ glShaderSource(shader->geometry, num_source, source, NULL);
+
+ glCompileShader(shader->geometry);
+ glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status);
+
+ if (!status) {
+ glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log);
+ shader_print_errors("compile", log, source, num_source);
+
+ GPU_shader_free(shader);
+ return NULL;
+ }
+ }
+
+ if (tf_names != NULL) {
+ glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS);
+ /* Primitive type must be setup */
+ BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
+ shader->feedback_transform_type = tf_type;
+ }
+
+ glLinkProgram(shader->program);
+ glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ glGetProgramInfoLog(shader->program, sizeof(log), &length, log);
+ /* print attached shaders in pipeline order */
+ if (defines) {
+ shader_print_errors("linking", log, &defines, 1);
+ }
+ if (vertexcode) {
+ shader_print_errors("linking", log, &vertexcode, 1);
+ }
+ if (geocode) {
+ shader_print_errors("linking", log, &geocode, 1);
+ }
+ if (libcode) {
+ shader_print_errors("linking", log, &libcode, 1);
+ }
+ if (fragcode) {
+ shader_print_errors("linking", log, &fragcode, 1);
+ }
+
+ GPU_shader_free(shader);
+ return NULL;
+ }
+
+ glUseProgram(shader->program);
+ shader->interface = GPU_shaderinterface_create(shader->program);
+
+ return shader;
+}
+
+#undef DEBUG_SHADER_GEOMETRY
+#undef DEBUG_SHADER_FRAGMENT
+#undef DEBUG_SHADER_VERTEX
+#undef DEBUG_SHADER_NONE
+
+void GPU_shader_free(GPUShader *shader)
+{
+#if 0 /* Would be nice to have, but for now the Deferred compilation \
+ * does not have a GPUContext. */
+ BLI_assert(GPU_context_active_get() != NULL);
+#endif
+ BLI_assert(shader);
+
+ if (shader->vertex) {
+ glDeleteShader(shader->vertex);
+ }
+ if (shader->geometry) {
+ glDeleteShader(shader->geometry);
+ }
+ if (shader->fragment) {
+ glDeleteShader(shader->fragment);
+ }
+ if (shader->program) {
+ glDeleteProgram(shader->program);
+ }
+
+ if (shader->interface) {
+ GPU_shaderinterface_discard(shader->interface);
+ }
+
+ MEM_freeN(shader);
+}
+
+static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc)
+{
+ bool is_alloc = false;
+ if (str_arr == NULL) {
+ *r_is_alloc = false;
+ return NULL;
+ }
+ /* Skip empty strings (avoid alloc if we can). */
+ while (str_arr[0] && str_arr[0][0] == '\0') {
+ str_arr++;
+ }
+ int i;
+ for (i = 0; str_arr[i]; i++) {
+ if (i != 0 && str_arr[i][0] != '\0') {
+ is_alloc = true;
+ }
+ }
+ *r_is_alloc = is_alloc;
+ if (is_alloc) {
+ return BLI_string_join_arrayN(str_arr, i);
+ }
+ else {
+ return str_arr[0];
+ }
+}
+
+/**
+ * Use via #GPU_shader_create_from_arrays macro (avoids passing in param).
+ *
+ * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader.
+ *
+ * It has the advantage that each item can be conditionally included
+ * without having to build the string inline, then free it.
+ *
+ * \param params: NULL terminated arrays of strings.
+ *
+ * Example:
+ * \code{.c}
+ * sh = GPU_shader_create_from_arrays({
+ * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL},
+ * .geom = (const char *[]){shader_geom_glsl, NULL},
+ * .frag = (const char *[]){shader_frag_glsl, NULL},
+ * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL},
+ * });
+ * \endcode
+ */
+struct GPUShader *GPU_shader_create_from_arrays_impl(
+ const struct GPU_ShaderCreateFromArray_Params *params)
+{
+ struct {
+ const char *str;
+ bool is_alloc;
+ } str_dst[4] = {{0}};
+ const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs};
+
+ for (int i = 0; i < ARRAY_SIZE(str_src); i++) {
+ str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc);
+ }
+
+ GPUShader *sh = GPU_shader_create(
+ str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__);
+
+ for (int i = 0; i < ARRAY_SIZE(str_dst); i++) {
+ if (str_dst[i].is_alloc) {
+ MEM_freeN((void *)str_dst[i].str);
+ }
+ }
+ return sh;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Binding
+ * \{ */
+
+void GPU_shader_bind(GPUShader *shader)
+{
+ BLI_assert(shader && shader->program);
+
+ glUseProgram(shader->program);
+ GPU_matrix_bind(shader->interface);
+ GPU_shader_set_srgb_uniform(shader->interface);
+}
+
+void GPU_shader_unbind(void)
+{
+ glUseProgram(0);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform feedback
+ * \{ */
+
+bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id)
+{
+ if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) {
+ return false;
+ }
+
+ glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id);
+
+ switch (shader->feedback_transform_type) {
+ case GPU_SHADER_TFB_POINTS:
+ glBeginTransformFeedback(GL_POINTS);
+ return true;
+ case GPU_SHADER_TFB_LINES:
+ glBeginTransformFeedback(GL_LINES);
+ return true;
+ case GPU_SHADER_TFB_TRIANGLES:
+ glBeginTransformFeedback(GL_TRIANGLES);
+ return true;
+ default:
+ return false;
+ }
+}
+
+void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
+{
+ glEndTransformFeedback();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Uniforms / Resource location
+ * \{ */
+
+int GPU_shader_get_uniform(GPUShader *shader, const char *name)
+{
+ BLI_assert(shader && shader->program);
+ const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name);
+ return uniform ? uniform->location : -1;
+}
+
+int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin)
+{
+ BLI_assert(shader && shader->program);
+ return GPU_shaderinterface_uniform_builtin(shader->interface,
+ static_cast<GPUUniformBuiltin>(builtin));
+}
+
+int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
+{
+ BLI_assert(shader && shader->program);
+ return GPU_shaderinterface_block_builtin(shader->interface,
+ static_cast<GPUUniformBlockBuiltin>(builtin));
+}
+
+int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
+{
+ BLI_assert(shader && shader->program);
+ const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
+ return ubo ? ubo->location : -1;
+}
+
+int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name)
+{
+ BLI_assert(shader && shader->program);
+ const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
+ return ubo ? ubo->binding : -1;
+}
+
+int GPU_shader_get_texture_binding(GPUShader *shader, const char *name)
+{
+ BLI_assert(shader && shader->program);
+ const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name);
+ return tex ? tex->binding : -1;
+}
+
+int GPU_shader_get_attribute(GPUShader *shader, const char *name)
+{
+ BLI_assert(shader && shader->program);
+ const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name);
+ return attr ? attr->location : -1;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Getters
+ * \{ */
+
+/* Clement : Temp */
+int GPU_shader_get_program(GPUShader *shader)
+{
+ return (int)shader->program;
+}
+
+char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len)
+{
+ BLI_assert(GLEW_ARB_get_program_binary);
+ char *r_binary;
+ int binary_len = 0;
+
+ glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len);
+ r_binary = (char *)MEM_mallocN(binary_len, __func__);
+ glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary);
+
+ if (r_binary_len) {
+ *r_binary_len = binary_len;
+ }
+
+ return r_binary;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Uniforms setters
+ * \{ */
+
+void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value)
+{
+ if (location == -1) {
+ return;
+ }
+
+ glUniform1f(location, value);
+}
+
+void GPU_shader_uniform_vector(
+ GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value)
+{
+ if (location == -1 || value == NULL) {
+ return;
+ }
+
+ switch (length) {
+ case 1:
+ glUniform1fv(location, arraysize, value);
+ break;
+ case 2:
+ glUniform2fv(location, arraysize, value);
+ break;
+ case 3:
+ glUniform3fv(location, arraysize, value);
+ break;
+ case 4:
+ glUniform4fv(location, arraysize, value);
+ break;
+ case 9:
+ glUniformMatrix3fv(location, arraysize, 0, value);
+ break;
+ case 16:
+ glUniformMatrix4fv(location, arraysize, 0, value);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+}
+
+void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value)
+{
+ if (location == -1) {
+ return;
+ }
+
+ glUniform1i(location, value);
+}
+
+void GPU_shader_uniform_vector_int(
+ GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value)
+{
+ if (location == -1) {
+ return;
+ }
+
+ switch (length) {
+ case 1:
+ glUniform1iv(location, arraysize, value);
+ break;
+ case 2:
+ glUniform2iv(location, arraysize, value);
+ break;
+ case 3:
+ glUniform3iv(location, arraysize, value);
+ break;
+ case 4:
+ glUniform4iv(location, arraysize, value);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name sRGB Rendering Workaround
+ *
+ * The viewport overlay frame-buffer is sRGB and will expect shaders to output display referred
+ * Linear colors. But other frame-buffers (i.e: the area frame-buffers) are not sRGB and require
+ * the shader output color to be in sRGB space
+ * (assumed display encoded color-space as the time of writing).
+ * For this reason we have a uniform to switch the transform on and off depending on the current
+ * frame-buffer color-space.
+ * \{ */
+
+static int g_shader_builtin_srgb_transform = 0;
+
+void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface)
+{
+ int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM);
+ if (loc != -1) {
+ glUniform1i(loc, g_shader_builtin_srgb_transform);
+ }
+}
+
+void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear)
+{
+ g_shader_builtin_srgb_transform = use_srgb_to_linear;
+}
+
+/** \} */
diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index 8c03567b95f..9c0692b76e2 100644
--- a/source/blender/gpu/intern/gpu_shader.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -81,10 +81,7 @@ extern char datatoc_gpu_shader_image_overlays_stereo_merge_frag_glsl[];
extern char datatoc_gpu_shader_image_color_frag_glsl[];
extern char datatoc_gpu_shader_image_desaturate_frag_glsl[];
extern char datatoc_gpu_shader_image_varying_color_frag_glsl[];
-extern char datatoc_gpu_shader_image_alpha_color_frag_glsl[];
extern char datatoc_gpu_shader_image_shuffle_color_frag_glsl[];
-extern char datatoc_gpu_shader_image_mask_uniform_color_frag_glsl[];
-extern char datatoc_gpu_shader_image_modulate_alpha_frag_glsl[];
extern char datatoc_gpu_shader_3D_vert_glsl[];
extern char datatoc_gpu_shader_3D_normal_vert_glsl[];
extern char datatoc_gpu_shader_3D_flat_color_vert_glsl[];
@@ -117,6 +114,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[];
@@ -155,11 +153,6 @@ const struct GPUShaderConfigData GPU_shader_cfg_data[GPU_SHADER_CFG_LEN] = {
/* cache of built-in shaders (each is created on first use) */
static GPUShader *builtin_shaders[GPU_SHADER_CFG_LEN][GPU_SHADER_BUILTIN_LEN] = {{NULL}};
-static int g_shader_builtin_srgb_transform = 0;
-
-#ifndef NDEBUG
-static uint g_shaderid = 0;
-#endif
typedef struct {
const char *vert;
@@ -170,725 +163,6 @@ typedef struct {
const char *defs;
} GPUShaderStages;
-static void shader_print_errors(const char *task, const char *log, const char **code, int totcode)
-{
- int line = 1;
-
- fprintf(stderr, "GPUShader: %s error:\n", task);
-
- for (int i = 0; i < totcode; i++) {
- const char *c, *pos, *end = code[i] + strlen(code[i]);
-
- if (G.debug & G_DEBUG) {
- fprintf(stderr, "===== shader string %d ====\n", i + 1);
-
- c = code[i];
- while ((c < end) && (pos = strchr(c, '\n'))) {
- fprintf(stderr, "%2d ", line);
- fwrite(c, (pos + 1) - c, 1, stderr);
- c = pos + 1;
- line++;
- }
-
- fprintf(stderr, "%s", c);
- }
- }
-
- fprintf(stderr, "%s\n", log);
-}
-
-static const char *gpu_shader_version(void)
-{
- return "#version 330\n";
-}
-
-static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH])
-{
- /* enable extensions for features that are not part of our base GLSL version
- * don't use an extension for something already available!
- */
-
- if (GLEW_ARB_texture_gather) {
- /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
- * is reported to be supported but yield a compile error (see T55802). */
- if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) {
- strcat(defines, "#extension GL_ARB_texture_gather: enable\n");
-
- /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
- * shader so double check the preprocessor define (see T56544). */
- if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
- strcat(defines, "#ifdef GL_ARB_texture_gather\n");
- strcat(defines, "# define GPU_ARB_texture_gather\n");
- strcat(defines, "#endif\n");
- }
- else {
- strcat(defines, "#define GPU_ARB_texture_gather\n");
- }
- }
- }
- if (GLEW_ARB_texture_query_lod) {
- /* a #version 400 feature, but we use #version 330 maximum so use extension */
- strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n");
- }
- if (GLEW_ARB_shader_draw_parameters) {
- strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n");
- }
- if (GPU_arb_texture_cube_map_array_is_supported()) {
- strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n");
- strcat(defines, "#define GPU_ARB_texture_cube_map_array\n");
- }
-}
-
-static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH])
-{
- /* some useful defines to detect GPU type */
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- strcat(defines, "#define GPU_ATI\n");
- if (GPU_crappy_amd_driver()) {
- strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n");
- }
- }
- else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- strcat(defines, "#define GPU_NVIDIA\n");
- }
- else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- strcat(defines, "#define GPU_INTEL\n");
- }
-
- /* some useful defines to detect OS type */
- if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) {
- strcat(defines, "#define OS_WIN\n");
- }
- else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
- strcat(defines, "#define OS_MAC\n");
- }
- else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
- strcat(defines, "#define OS_UNIX\n");
- }
-
- float derivatives_factors[2];
- GPU_get_dfdy_factors(derivatives_factors);
- if (derivatives_factors[0] == 1.0f) {
- strcat(defines, "#define DFDX_SIGN 1.0\n");
- }
- else {
- strcat(defines, "#define DFDX_SIGN -1.0\n");
- }
-
- if (derivatives_factors[1] == 1.0f) {
- strcat(defines, "#define DFDY_SIGN 1.0\n");
- }
- else {
- strcat(defines, "#define DFDY_SIGN -1.0\n");
- }
-
- return;
-}
-
-GPUShader *GPU_shader_create(const char *vertexcode,
- const char *fragcode,
- const char *geocode,
- const char *libcode,
- const char *defines,
- const char *shname)
-{
- return GPU_shader_create_ex(
- vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname);
-}
-
-GPUShader *GPU_shader_create_from_python(const char *vertexcode,
- const char *fragcode,
- const char *geocode,
- const char *libcode,
- const char *defines)
-{
- char *libcodecat = NULL;
-
- if (libcode == NULL) {
- libcode = datatoc_gpu_shader_colorspace_lib_glsl;
- }
- else {
- libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl);
- }
-
- GPUShader *sh = GPU_shader_create_ex(
- vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL);
-
- MEM_SAFE_FREE(libcodecat);
- return sh;
-}
-
-GPUShader *GPU_shader_load_from_binary(const char *binary,
- const int binary_format,
- const int binary_len,
- const char *shname)
-{
- BLI_assert(GL_ARB_get_program_binary);
- int success;
- int program = glCreateProgram();
-
- glProgramBinary(program, binary_format, binary, binary_len);
- glGetProgramiv(program, GL_LINK_STATUS, &success);
-
- if (success) {
- glUseProgram(program);
-
- GPUShader *shader = MEM_callocN(sizeof(*shader), __func__);
- shader->interface = GPU_shaderinterface_create(program);
- shader->program = program;
-
-#ifndef NDEBUG
- BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
-#else
- UNUSED_VARS(shname);
-#endif
-
- return shader;
- }
-
- glDeleteProgram(program);
- return NULL;
-}
-
-#define DEBUG_SHADER_NONE ""
-#define DEBUG_SHADER_VERTEX "vert"
-#define DEBUG_SHADER_FRAGMENT "frag"
-#define DEBUG_SHADER_GEOMETRY "geom"
-
-/**
- * Dump GLSL shaders to disk
- *
- * This is used for profiling shader performance externally and debug if shader code is correct.
- * If called with no code, it simply bumps the shader index, so different shaders for the same
- * program share the same index.
- */
-static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension)
-{
- if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) {
- return;
- }
-
- /* We use the same shader index for shaders in the same program.
- * So we call this function once before calling for the individual shaders. */
- static int shader_index = 0;
- if (code == NULL) {
- shader_index++;
- BLI_assert(STREQ(DEBUG_SHADER_NONE, extension));
- return;
- }
-
- /* Determine the full path of the new shader. */
- char shader_path[FILE_MAX];
-
- char file_name[512] = {'\0'};
- sprintf(file_name, "%04d.%s", shader_index, extension);
-
- BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name);
-
- /* Write shader to disk. */
- FILE *f = fopen(shader_path, "w");
- if (f == NULL) {
- printf("Error writing to file: %s\n", shader_path);
- }
- for (int j = 0; j < num_shaders; j++) {
- fprintf(f, "%s", code[j]);
- }
- fclose(f);
- printf("Shader file written to disk: %s\n", shader_path);
-}
-
-GPUShader *GPU_shader_create_ex(const char *vertexcode,
- const char *fragcode,
- const char *geocode,
- const char *libcode,
- const char *defines,
- const eGPUShaderTFBType tf_type,
- const char **tf_names,
- const int tf_count,
- const char *shname)
-{
- GLint status;
- GLchar log[5000];
- GLsizei length = 0;
- GPUShader *shader;
- char standard_defines[MAX_DEFINE_LENGTH] = "";
- char standard_extensions[MAX_EXT_DEFINE_LENGTH] = "";
-
- shader = MEM_callocN(sizeof(GPUShader), "GPUShader");
- gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE);
-
-#ifndef NDEBUG
- BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
-#else
- UNUSED_VARS(shname);
-#endif
-
- /* At least a vertex shader and a fragment shader are required. */
- BLI_assert((fragcode != NULL) && (vertexcode != NULL));
-
- if (vertexcode) {
- shader->vertex = glCreateShader(GL_VERTEX_SHADER);
- }
- if (fragcode) {
- shader->fragment = glCreateShader(GL_FRAGMENT_SHADER);
- }
- if (geocode) {
- shader->geometry = glCreateShader(GL_GEOMETRY_SHADER);
- }
-
- shader->program = glCreateProgram();
-
- if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) ||
- (geocode && !shader->geometry)) {
- fprintf(stderr, "GPUShader, object creation failed.\n");
- GPU_shader_free(shader);
- return NULL;
- }
-
- gpu_shader_standard_defines(standard_defines);
- gpu_shader_standard_extensions(standard_extensions);
-
- if (vertexcode) {
- const char *source[6];
- /* custom limit, may be too small, beware */
- int num_source = 0;
-
- source[num_source++] = gpu_shader_version();
- source[num_source++] = "#define GPU_VERTEX_SHADER\n";
- source[num_source++] = standard_extensions;
- source[num_source++] = standard_defines;
-
- if (defines) {
- source[num_source++] = defines;
- }
- source[num_source++] = vertexcode;
-
- gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX);
-
- glAttachShader(shader->program, shader->vertex);
- glShaderSource(shader->vertex, num_source, source, NULL);
-
- glCompileShader(shader->vertex);
- glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status);
-
- if (!status) {
- glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log);
- shader_print_errors("compile", log, source, num_source);
-
- GPU_shader_free(shader);
- return NULL;
- }
- }
-
- if (fragcode) {
- const char *source[7];
- int num_source = 0;
-
- source[num_source++] = gpu_shader_version();
- source[num_source++] = "#define GPU_FRAGMENT_SHADER\n";
- source[num_source++] = standard_extensions;
- source[num_source++] = standard_defines;
-
- if (defines) {
- source[num_source++] = defines;
- }
- if (libcode) {
- source[num_source++] = libcode;
- }
- source[num_source++] = fragcode;
-
- gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT);
-
- glAttachShader(shader->program, shader->fragment);
- glShaderSource(shader->fragment, num_source, source, NULL);
-
- glCompileShader(shader->fragment);
- glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status);
-
- if (!status) {
- glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log);
- shader_print_errors("compile", log, source, num_source);
-
- GPU_shader_free(shader);
- return NULL;
- }
- }
-
- if (geocode) {
- const char *source[6];
- int num_source = 0;
-
- source[num_source++] = gpu_shader_version();
- source[num_source++] = "#define GPU_GEOMETRY_SHADER\n";
- source[num_source++] = standard_extensions;
- source[num_source++] = standard_defines;
-
- if (defines) {
- source[num_source++] = defines;
- }
- source[num_source++] = geocode;
-
- gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY);
-
- glAttachShader(shader->program, shader->geometry);
- glShaderSource(shader->geometry, num_source, source, NULL);
-
- glCompileShader(shader->geometry);
- glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status);
-
- if (!status) {
- glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log);
- shader_print_errors("compile", log, source, num_source);
-
- GPU_shader_free(shader);
- return NULL;
- }
- }
-
- if (tf_names != NULL) {
- glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS);
- /* Primitive type must be setup */
- BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
- shader->feedback_transform_type = tf_type;
- }
-
- glLinkProgram(shader->program);
- glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
- if (!status) {
- glGetProgramInfoLog(shader->program, sizeof(log), &length, log);
- /* print attached shaders in pipeline order */
- if (vertexcode) {
- shader_print_errors("linking", log, &vertexcode, 1);
- }
- if (geocode) {
- shader_print_errors("linking", log, &geocode, 1);
- }
- if (libcode) {
- shader_print_errors("linking", log, &libcode, 1);
- }
- if (fragcode) {
- shader_print_errors("linking", log, &fragcode, 1);
- }
-
- GPU_shader_free(shader);
- return NULL;
- }
-
- glUseProgram(shader->program);
- shader->interface = GPU_shaderinterface_create(shader->program);
-
- return shader;
-}
-
-#undef DEBUG_SHADER_GEOMETRY
-#undef DEBUG_SHADER_FRAGMENT
-#undef DEBUG_SHADER_VERTEX
-#undef DEBUG_SHADER_NONE
-
-static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc)
-{
- bool is_alloc = false;
- if (str_arr == NULL) {
- *r_is_alloc = false;
- return NULL;
- }
- /* Skip empty strings (avoid alloc if we can). */
- while (str_arr[0] && str_arr[0][0] == '\0') {
- str_arr++;
- }
- int i;
- for (i = 0; str_arr[i]; i++) {
- if (i != 0 && str_arr[i][0] != '\0') {
- is_alloc = true;
- }
- }
- *r_is_alloc = is_alloc;
- if (is_alloc) {
- return BLI_string_join_arrayN(str_arr, i);
- }
- else {
- return str_arr[0];
- }
-}
-
-/**
- * Use via #GPU_shader_create_from_arrays macro (avoids passing in param).
- *
- * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader.
- *
- * It has the advantage that each item can be conditionally included
- * without having to build the string inline, then free it.
- *
- * \param params: NULL terminated arrays of strings.
- *
- * Example:
- * \code{.c}
- * sh = GPU_shader_create_from_arrays({
- * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL},
- * .geom = (const char *[]){shader_geom_glsl, NULL},
- * .frag = (const char *[]){shader_frag_glsl, NULL},
- * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL},
- * });
- * \endcode
- */
-struct GPUShader *GPU_shader_create_from_arrays_impl(
- const struct GPU_ShaderCreateFromArray_Params *params)
-{
- struct {
- const char *str;
- bool is_alloc;
- } str_dst[4] = {{0}};
- const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs};
-
- for (int i = 0; i < ARRAY_SIZE(str_src); i++) {
- str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc);
- }
-
- GPUShader *sh = GPU_shader_create(
- str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__);
-
- for (int i = 0; i < ARRAY_SIZE(str_dst); i++) {
- if (str_dst[i].is_alloc) {
- MEM_freeN((void *)str_dst[i].str);
- }
- }
- return sh;
-}
-
-void GPU_shader_bind(GPUShader *shader)
-{
- BLI_assert(shader && shader->program);
-
- glUseProgram(shader->program);
- GPU_matrix_bind(shader->interface);
- GPU_shader_set_srgb_uniform(shader->interface);
-}
-
-void GPU_shader_unbind(void)
-{
- glUseProgram(0);
-}
-
-bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id)
-{
- if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) {
- return false;
- }
-
- glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id);
-
- switch (shader->feedback_transform_type) {
- case GPU_SHADER_TFB_POINTS:
- glBeginTransformFeedback(GL_POINTS);
- return true;
- case GPU_SHADER_TFB_LINES:
- glBeginTransformFeedback(GL_LINES);
- return true;
- case GPU_SHADER_TFB_TRIANGLES:
- glBeginTransformFeedback(GL_TRIANGLES);
- return true;
- default:
- return false;
- }
-}
-
-void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
-{
- glEndTransformFeedback();
-}
-
-void GPU_shader_free(GPUShader *shader)
-{
-#if 0 /* Would be nice to have, but for now the Deferred compilation \
- * does not have a GPUContext. */
- BLI_assert(GPU_context_active_get() != NULL);
-#endif
- BLI_assert(shader);
-
- if (shader->vertex) {
- glDeleteShader(shader->vertex);
- }
- if (shader->geometry) {
- glDeleteShader(shader->geometry);
- }
- if (shader->fragment) {
- glDeleteShader(shader->fragment);
- }
- if (shader->program) {
- glDeleteProgram(shader->program);
- }
-
- if (shader->interface) {
- GPU_shaderinterface_discard(shader->interface);
- }
-
- MEM_freeN(shader);
-}
-
-int GPU_shader_get_uniform(GPUShader *shader, const char *name)
-{
- BLI_assert(shader && shader->program);
- const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name);
- return uniform ? uniform->location : -1;
-}
-
-int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin)
-{
- BLI_assert(shader && shader->program);
- return GPU_shaderinterface_uniform_builtin(shader->interface, builtin);
-}
-
-int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
-{
- BLI_assert(shader && shader->program);
- return GPU_shaderinterface_block_builtin(shader->interface, builtin);
-}
-
-int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
-{
- BLI_assert(shader && shader->program);
- const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
- return ubo ? ubo->location : -1;
-}
-
-int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name)
-{
- BLI_assert(shader && shader->program);
- const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
- return ubo ? ubo->binding : -1;
-}
-
-int GPU_shader_get_texture_binding(GPUShader *shader, const char *name)
-{
- BLI_assert(shader && shader->program);
- const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name);
- return tex ? tex->binding : -1;
-}
-
-void *GPU_shader_get_interface(GPUShader *shader)
-{
- return shader->interface;
-}
-
-/* Clement : Temp */
-int GPU_shader_get_program(GPUShader *shader)
-{
- return (int)shader->program;
-}
-
-void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value)
-{
- if (location == -1) {
- return;
- }
-
- glUniform1f(location, value);
-}
-
-void GPU_shader_uniform_vector(
- GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value)
-{
- if (location == -1 || value == NULL) {
- return;
- }
-
- switch (length) {
- case 1:
- glUniform1fv(location, arraysize, value);
- break;
- case 2:
- glUniform2fv(location, arraysize, value);
- break;
- case 3:
- glUniform3fv(location, arraysize, value);
- break;
- case 4:
- glUniform4fv(location, arraysize, value);
- break;
- case 9:
- glUniformMatrix3fv(location, arraysize, 0, value);
- break;
- case 16:
- glUniformMatrix4fv(location, arraysize, 0, value);
- break;
- default:
- BLI_assert(0);
- break;
- }
-}
-
-void GPU_shader_uniform_vector_int(
- GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value)
-{
- if (location == -1) {
- return;
- }
-
- switch (length) {
- case 1:
- glUniform1iv(location, arraysize, value);
- break;
- case 2:
- glUniform2iv(location, arraysize, value);
- break;
- case 3:
- glUniform3iv(location, arraysize, value);
- break;
- case 4:
- glUniform4iv(location, arraysize, value);
- break;
- default:
- BLI_assert(0);
- break;
- }
-}
-
-void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value)
-{
- if (location == -1) {
- return;
- }
-
- glUniform1i(location, value);
-}
-
-void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface)
-{
- int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM);
- if (loc != -1) {
- glUniform1i(loc, g_shader_builtin_srgb_transform);
- }
-}
-
-int GPU_shader_get_attribute(GPUShader *shader, const char *name)
-{
- BLI_assert(shader && shader->program);
- const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name);
- return attr ? attr->location : -1;
-}
-
-char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len)
-{
- BLI_assert(GLEW_ARB_get_program_binary);
- char *r_binary;
- int binary_len = 0;
-
- glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len);
- r_binary = MEM_mallocN(binary_len, __func__);
- glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary);
-
- if (r_binary_len) {
- *r_binary_len = binary_len;
- }
-
- return r_binary;
-}
-
-void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear)
-{
- g_shader_builtin_srgb_transform = use_srgb_to_linear;
-}
-
static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
[GPU_SHADER_TEXT] =
{
@@ -906,16 +180,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.frag = datatoc_gpu_shader_simple_lighting_frag_glsl,
},
- [GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR] =
- {
- .vert = datatoc_gpu_shader_3D_image_vert_glsl,
- .frag = datatoc_gpu_shader_image_mask_uniform_color_frag_glsl,
- },
- [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] =
- {
- .vert = datatoc_gpu_shader_3D_image_vert_glsl,
- .frag = datatoc_gpu_shader_image_modulate_alpha_frag_glsl,
- },
[GPU_SHADER_2D_CHECKER] =
{
.vert = datatoc_gpu_shader_2D_vert_glsl,
@@ -968,11 +232,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.vert = datatoc_gpu_shader_2D_image_vert_glsl,
.frag = datatoc_gpu_shader_image_desaturate_frag_glsl,
},
- [GPU_SHADER_2D_IMAGE_ALPHA_COLOR] =
- {
- .vert = datatoc_gpu_shader_2D_image_vert_glsl,
- .frag = datatoc_gpu_shader_image_alpha_color_frag_glsl,
- },
[GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR] =
{
.vert = datatoc_gpu_shader_2D_image_vert_glsl,
@@ -1171,12 +430,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] =
@@ -1257,6 +516,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader,
return *sh_p;
}
+
GPUShader *GPU_shader_get_builtin_shader(eGPUBuiltinShader shader)
{
return GPU_shader_get_builtin_shader_with_config(shader, GPU_SHADER_CFG_DEFAULT);
diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h
index e4443e79a8d..2c52568ac25 100644
--- a/source/blender/gpu/intern/gpu_shader_private.h
+++ b/source/blender/gpu/intern/gpu_shader_private.h
@@ -21,9 +21,12 @@
#ifndef __GPU_SHADER_PRIVATE_H__
#define __GPU_SHADER_PRIVATE_H__
-#include "GPU_glew.h"
#include "GPU_shader_interface.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct GPUShader {
/** Handle for full program (links shader stages below). */
GLuint program;
@@ -45,6 +48,10 @@ struct GPUShader {
};
/* XXX do not use it. Special hack to use OCIO with batch API. */
-void immGetProgram(GLuint *program, GPUShaderInterface **shaderface);
+GPUShader *immGetShader(void);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* __GPU_SHADER_PRIVATE_H__ */
diff --git a/source/blender/gpu/intern/gpu_state.c b/source/blender/gpu/intern/gpu_state.cc
index 908f5fa5771..794c7a3eb97 100644
--- a/source/blender/gpu/intern/gpu_state.c
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -78,6 +78,28 @@ void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb,
gpu_get_gl_blendfunction(dst_alpha));
}
+void GPU_face_culling(eGPUFaceCull culling)
+{
+ if (culling == GPU_CULL_NONE) {
+ glDisable(GL_CULL_FACE);
+ }
+ else {
+ glEnable(GL_CULL_FACE);
+ glCullFace((culling == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK);
+ }
+}
+
+void GPU_front_facing(bool invert)
+{
+ glFrontFace((invert) ? GL_CW : GL_CCW);
+}
+
+void GPU_provoking_vertex(eGPUProvokingVertex vert)
+{
+ glProvokingVertex((vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION :
+ GL_LAST_VERTEX_CONVENTION);
+}
+
void GPU_depth_range(float near, float far)
{
/* glDepthRangef is only for OpenGL 4.1 or higher */
@@ -146,11 +168,26 @@ void GPU_program_point_size(bool enable)
}
}
+void GPU_scissor_test(bool enable)
+{
+ if (enable) {
+ glEnable(GL_SCISSOR_TEST);
+ }
+ else {
+ glDisable(GL_SCISSOR_TEST);
+ }
+}
+
void GPU_scissor(int x, int y, int width, int height)
{
glScissor(x, y, width, height);
}
+void GPU_viewport(int x, int y, int width, int height)
+{
+ glViewport(x, y, width, height);
+}
+
void GPU_scissor_get_f(float coords[4])
{
glGetFloatv(GL_SCISSOR_BOX, coords);
@@ -181,20 +218,62 @@ void GPU_finish(void)
glFinish();
}
-void GPU_logic_op_invert_set(bool enable)
+void GPU_unpack_row_length_set(uint len)
+{
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, len);
+}
+
+void GPU_logic_op_xor_set(bool enable)
{
if (enable) {
- glLogicOp(GL_INVERT);
+ glLogicOp(GL_XOR);
glEnable(GL_COLOR_LOGIC_OP);
- glDisable(GL_DITHER);
}
else {
- glLogicOp(GL_COPY);
glDisable(GL_COLOR_LOGIC_OP);
- glEnable(GL_DITHER);
}
}
+void GPU_color_mask(bool r, bool g, bool b, bool a)
+{
+ glColorMask(r, g, b, a);
+}
+
+void GPU_depth_mask(bool depth)
+{
+ glDepthMask(depth);
+}
+
+bool GPU_depth_mask_get(void)
+{
+ GLint mask;
+ glGetIntegerv(GL_DEPTH_WRITEMASK, &mask);
+ return mask == GL_TRUE;
+}
+
+void GPU_stencil_mask(uint stencil)
+{
+ glStencilMask(stencil);
+}
+
+void GPU_clip_distances(int distances_new)
+{
+ static int distances_enabled = 0;
+ for (int i = 0; i < distances_new; i++) {
+ glEnable(GL_CLIP_DISTANCE0 + i);
+ }
+ for (int i = distances_new; i < distances_enabled; i++) {
+ glDisable(GL_CLIP_DISTANCE0 + i);
+ }
+ distances_enabled = distances_new;
+}
+
+bool GPU_mipmap_enabled(void)
+{
+ /* TODO(fclem) this used to be a userdef option. */
+ return true;
+}
+
/** \name GPU Push/Pop State
* \{ */
@@ -203,34 +282,18 @@ void GPU_logic_op_invert_set(bool enable)
typedef struct {
eGPUAttrMask mask;
- /* GL_ENABLE_BIT */
+ /* GL_BLEND_BIT */
uint is_blend : 1;
- uint is_cull_face : 1;
- uint is_depth_test : 1;
- uint is_dither : 1;
- uint is_lighting : 1;
- uint is_line_smooth : 1;
- uint is_color_logic_op : 1;
- uint is_multisample : 1;
- uint is_polygon_offset_line : 1;
- uint is_polygon_offset_fill : 1;
- uint is_polygon_smooth : 1;
- uint is_sample_alpha_to_coverage : 1;
- uint is_scissor_test : 1;
- uint is_stencil_test : 1;
- uint is_framebuffer_srgb : 1;
-
- bool is_clip_plane[6];
/* GL_DEPTH_BUFFER_BIT */
- /* uint is_depth_test : 1; */
+ uint is_depth_test : 1;
int depth_func;
double depth_clear_value;
bool depth_write_mask;
/* GL_SCISSOR_BIT */
int scissor_box[4];
- /* uint is_scissor_test : 1; */
+ uint is_scissor_test : 1;
/* GL_VIEWPORT_BIT */
int viewport[4];
@@ -243,7 +306,8 @@ typedef struct {
} GPUAttrStack;
static GPUAttrStack state = {
- .top = 0,
+ {},
+ 0,
};
#define AttrStack state
@@ -266,27 +330,6 @@ void gpuPushAttr(eGPUAttrMask mask)
glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask);
}
- if ((mask & GPU_ENABLE_BIT) != 0) {
- Attr.is_blend = glIsEnabled(GL_BLEND);
-
- for (int i = 0; i < 6; i++) {
- Attr.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i);
- }
-
- Attr.is_cull_face = glIsEnabled(GL_CULL_FACE);
- Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST);
- Attr.is_dither = glIsEnabled(GL_DITHER);
- Attr.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH);
- Attr.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP);
- Attr.is_multisample = glIsEnabled(GL_MULTISAMPLE);
- Attr.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE);
- Attr.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL);
- Attr.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH);
- Attr.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
- Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
- Attr.is_stencil_test = glIsEnabled(GL_STENCIL_TEST);
- }
-
if ((mask & GPU_SCISSOR_BIT) != 0) {
Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box);
@@ -295,7 +338,6 @@ void gpuPushAttr(eGPUAttrMask mask)
if ((mask & GPU_VIEWPORT_BIT) != 0) {
glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far);
glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport);
- Attr.is_framebuffer_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB);
}
if ((mask & GPU_BLEND_BIT) != 0) {
@@ -330,31 +372,9 @@ void gpuPopAttr(void)
glDepthMask(Attr.depth_write_mask);
}
- if ((mask & GPU_ENABLE_BIT) != 0) {
- restore_mask(GL_BLEND, Attr.is_blend);
-
- for (int i = 0; i < 6; i++) {
- restore_mask(GL_CLIP_PLANE0 + i, Attr.is_clip_plane[i]);
- }
-
- restore_mask(GL_CULL_FACE, Attr.is_cull_face);
- restore_mask(GL_DEPTH_TEST, Attr.is_depth_test);
- restore_mask(GL_DITHER, Attr.is_dither);
- restore_mask(GL_LINE_SMOOTH, Attr.is_line_smooth);
- restore_mask(GL_COLOR_LOGIC_OP, Attr.is_color_logic_op);
- restore_mask(GL_MULTISAMPLE, Attr.is_multisample);
- restore_mask(GL_POLYGON_OFFSET_LINE, Attr.is_polygon_offset_line);
- restore_mask(GL_POLYGON_OFFSET_FILL, Attr.is_polygon_offset_fill);
- restore_mask(GL_POLYGON_SMOOTH, Attr.is_polygon_smooth);
- restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Attr.is_sample_alpha_to_coverage);
- restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test);
- restore_mask(GL_STENCIL_TEST, Attr.is_stencil_test);
- }
-
if ((mask & GPU_VIEWPORT_BIT) != 0) {
glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]);
glDepthRange(Attr.near_far[0], Attr.near_far[1]);
- restore_mask(GL_FRAMEBUFFER_SRGB, Attr.is_framebuffer_srgb);
}
if ((mask & GPU_SCISSOR_BIT) != 0) {
@@ -396,6 +416,9 @@ void GPU_state_init(void)
glCullFace(GL_BACK);
glDisable(GL_CULL_FACE);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
/* Is default but better be explicit. */
glEnable(GL_MULTISAMPLE);
diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.cc
index a985d45162c..8a3a8a382f8 100644
--- a/source/blender/gpu/intern/gpu_texture.c
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_image_types.h"
+#include "DNA_userdef_types.h"
#include "BLI_blenlib.h"
#include "BLI_math_base.h"
@@ -36,7 +37,6 @@
#include "GPU_batch.h"
#include "GPU_context.h"
#include "GPU_debug.h"
-#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_framebuffer.h"
#include "GPU_glew.h"
@@ -45,6 +45,16 @@
#include "gpu_context_private.h"
+#define WARN_NOT_BOUND(_tex) \
+ { \
+ if (_tex->number == -1) { \
+ fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \
+ BLI_assert(0); \
+ return; \
+ } \
+ } \
+ ((void)0)
+
static struct GPUTextureGlobal {
/** Texture used in place of invalid textures (not loaded correctly, missing). */
GPUTexture *invalid_tex_1D;
@@ -52,6 +62,7 @@ static struct GPUTextureGlobal {
GPUTexture *invalid_tex_3D;
/** Sampler objects used to replace internal texture parameters. */
GLuint samplers[GPU_SAMPLER_MAX];
+ GLuint icon_sampler;
} GG = {NULL};
/* Maximum number of FBOs a texture can be attached to. */
@@ -70,6 +81,8 @@ typedef enum eGPUTextureFormatFlag {
GPU_FORMAT_ARRAY = (1 << 14),
} eGPUTextureFormatFlag;
+ENUM_OPERATORS(eGPUTextureFormatFlag)
+
/* GPUTexture */
struct GPUTexture {
int w, h, d; /* width/height/depth */
@@ -102,7 +115,7 @@ static void gpu_texture_framebuffer_ensure(GPUTexture *tex);
/* ------ Memory Management ------- */
/* Records every texture allocation / free
* to estimate the Texture Pool Memory consumption */
-static uint memory_usage;
+static uint memory_usage = 0;
static uint gpu_texture_memory_footprint_compute(GPUTexture *tex)
{
@@ -160,54 +173,58 @@ uint GPU_texture_memory_usage_get(void)
static const char *gl_enum_to_str(GLenum e)
{
-#define ENUM_TO_STRING(e) [GL_##e] = STRINGIFY_ARG(e)
- static const char *enum_strings[] = {
- ENUM_TO_STRING(TEXTURE_CUBE_MAP),
- ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY),
- ENUM_TO_STRING(TEXTURE_2D),
- ENUM_TO_STRING(TEXTURE_2D_ARRAY),
- ENUM_TO_STRING(TEXTURE_1D),
- ENUM_TO_STRING(TEXTURE_1D_ARRAY),
- ENUM_TO_STRING(TEXTURE_3D),
- ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE),
- ENUM_TO_STRING(RGBA32F),
- ENUM_TO_STRING(RGBA16F),
- ENUM_TO_STRING(RGBA16UI),
- ENUM_TO_STRING(RGBA16I),
- ENUM_TO_STRING(RGBA16),
- ENUM_TO_STRING(RGBA8UI),
- ENUM_TO_STRING(RGBA8I),
- ENUM_TO_STRING(RGBA8),
- ENUM_TO_STRING(RGB16F),
- ENUM_TO_STRING(RG32F),
- ENUM_TO_STRING(RG16F),
- ENUM_TO_STRING(RG16UI),
- ENUM_TO_STRING(RG16I),
- ENUM_TO_STRING(RG16),
- ENUM_TO_STRING(RG8UI),
- ENUM_TO_STRING(RG8I),
- ENUM_TO_STRING(RG8),
- ENUM_TO_STRING(R8UI),
- ENUM_TO_STRING(R8I),
- ENUM_TO_STRING(R8),
- ENUM_TO_STRING(R32F),
- ENUM_TO_STRING(R32UI),
- ENUM_TO_STRING(R32I),
- ENUM_TO_STRING(R16F),
- ENUM_TO_STRING(R16UI),
- ENUM_TO_STRING(R16I),
- ENUM_TO_STRING(R16),
- ENUM_TO_STRING(R11F_G11F_B10F),
- ENUM_TO_STRING(SRGB8_ALPHA8),
- ENUM_TO_STRING(DEPTH24_STENCIL8),
- ENUM_TO_STRING(DEPTH32F_STENCIL8),
- ENUM_TO_STRING(DEPTH_COMPONENT32F),
- ENUM_TO_STRING(DEPTH_COMPONENT24),
- ENUM_TO_STRING(DEPTH_COMPONENT16),
+#define ENUM_TO_STRING(e) \
+ case GL_##e: { \
+ return STRINGIFY_ARG(e); \
+ }
+
+ switch (e) {
+ ENUM_TO_STRING(TEXTURE_CUBE_MAP);
+ ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY);
+ ENUM_TO_STRING(TEXTURE_2D);
+ ENUM_TO_STRING(TEXTURE_2D_ARRAY);
+ ENUM_TO_STRING(TEXTURE_1D);
+ ENUM_TO_STRING(TEXTURE_1D_ARRAY);
+ ENUM_TO_STRING(TEXTURE_3D);
+ ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE);
+ ENUM_TO_STRING(RGBA32F);
+ ENUM_TO_STRING(RGBA16F);
+ ENUM_TO_STRING(RGBA16UI);
+ ENUM_TO_STRING(RGBA16I);
+ ENUM_TO_STRING(RGBA16);
+ ENUM_TO_STRING(RGBA8UI);
+ ENUM_TO_STRING(RGBA8I);
+ ENUM_TO_STRING(RGBA8);
+ ENUM_TO_STRING(RGB16F);
+ ENUM_TO_STRING(RG32F);
+ ENUM_TO_STRING(RG16F);
+ ENUM_TO_STRING(RG16UI);
+ ENUM_TO_STRING(RG16I);
+ ENUM_TO_STRING(RG16);
+ ENUM_TO_STRING(RG8UI);
+ ENUM_TO_STRING(RG8I);
+ ENUM_TO_STRING(RG8);
+ ENUM_TO_STRING(R8UI);
+ ENUM_TO_STRING(R8I);
+ ENUM_TO_STRING(R8);
+ ENUM_TO_STRING(R32F);
+ ENUM_TO_STRING(R32UI);
+ ENUM_TO_STRING(R32I);
+ ENUM_TO_STRING(R16F);
+ ENUM_TO_STRING(R16UI);
+ ENUM_TO_STRING(R16I);
+ ENUM_TO_STRING(R16);
+ ENUM_TO_STRING(R11F_G11F_B10F);
+ ENUM_TO_STRING(SRGB8_ALPHA8);
+ ENUM_TO_STRING(DEPTH24_STENCIL8);
+ ENUM_TO_STRING(DEPTH32F_STENCIL8);
+ ENUM_TO_STRING(DEPTH_COMPONENT32F);
+ ENUM_TO_STRING(DEPTH_COMPONENT24);
+ ENUM_TO_STRING(DEPTH_COMPONENT16);
+ default:
+ return "Unkown enum";
};
#undef ENUM_TO_STRING
-
- return enum_strings[e];
}
static int gpu_get_component_count(eGPUTextureFormat format)
@@ -422,6 +439,13 @@ static uint gpu_get_bytesize(eGPUTextureFormat data_type)
case GPU_R8:
case GPU_R8UI:
return 1;
+ case GPU_SRGB8_A8_DXT1:
+ case GPU_SRGB8_A8_DXT3:
+ case GPU_SRGB8_A8_DXT5:
+ case GPU_RGBA8_DXT1:
+ case GPU_RGBA8_DXT3:
+ case GPU_RGBA8_DXT5:
+ return 1; /* Incorrect but actual size is fractional. */
default:
BLI_assert(!"Texture format incorrect or unsupported\n");
return 0;
@@ -507,7 +531,18 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format)
case GPU_RGB16F:
return GL_RGB16F;
/* Special formats texture only */
- /* ** Add Format here */
+ case GPU_SRGB8_A8_DXT1:
+ return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
+ case GPU_SRGB8_A8_DXT3:
+ return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
+ case GPU_SRGB8_A8_DXT5:
+ return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+ case GPU_RGBA8_DXT1:
+ return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ case GPU_RGBA8_DXT3:
+ return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ case GPU_RGBA8_DXT5:
+ return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
/* Depth Formats */
case GPU_DEPTH_COMPONENT32F:
return GL_DEPTH_COMPONENT32F;
@@ -521,99 +556,6 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format)
}
}
-static eGPUTextureFormat gl_internalformat_to_gpu_format(const GLint glformat)
-{
- /* You can add any of the available type to this list
- * For available types see GPU_texture.h */
- switch (glformat) {
- /* Formats texture & renderbuffer */
- case GL_RGBA8UI:
- return GPU_RGBA8UI;
- case GL_RGBA8I:
- return GPU_RGBA8I;
- case GL_RGBA8:
- return GPU_RGBA8;
- case GL_RGBA32UI:
- return GPU_RGBA32UI;
- case GL_RGBA32I:
- return GPU_RGBA32I;
- case GL_RGBA32F:
- return GPU_RGBA32F;
- case GL_RGBA16UI:
- return GPU_RGBA16UI;
- case GL_RGBA16I:
- return GPU_RGBA16I;
- case GL_RGBA16F:
- return GPU_RGBA16F;
- case GL_RGBA16:
- return GPU_RGBA16;
- case GL_RG8UI:
- return GPU_RG8UI;
- case GL_RG8I:
- return GPU_RG8I;
- case GL_RG8:
- return GPU_RG8;
- case GL_RG32UI:
- return GPU_RG32UI;
- case GL_RG32I:
- return GPU_RG32I;
- case GL_RG32F:
- return GPU_RG32F;
- case GL_RG16UI:
- return GPU_RG16UI;
- case GL_RG16I:
- return GPU_RG16I;
- case GL_RG16F:
- return GPU_RGBA32F;
- case GL_RG16:
- return GPU_RG16;
- case GL_R8UI:
- return GPU_R8UI;
- case GL_R8I:
- return GPU_R8I;
- case GL_R8:
- return GPU_R8;
- case GL_R32UI:
- return GPU_R32UI;
- case GL_R32I:
- return GPU_R32I;
- case GL_R32F:
- return GPU_R32F;
- case GL_R16UI:
- return GPU_R16UI;
- case GL_R16I:
- return GPU_R16I;
- case GL_R16F:
- return GPU_R16F;
- case GL_R16:
- return GPU_R16;
- /* Special formats texture & renderbuffer */
- case GL_R11F_G11F_B10F:
- return GPU_R11F_G11F_B10F;
- case GL_DEPTH32F_STENCIL8:
- return GPU_DEPTH32F_STENCIL8;
- case GL_DEPTH24_STENCIL8:
- return GPU_DEPTH24_STENCIL8;
- case GL_SRGB8_ALPHA8:
- return GPU_SRGB8_A8;
- /* Texture only format */
- case GL_RGB16F:
- return GPU_RGB16F;
- /* Special formats texture only */
- /* ** Add Format here */
- /* Depth Formats */
- case GL_DEPTH_COMPONENT32F:
- return GPU_DEPTH_COMPONENT32F;
- case GL_DEPTH_COMPONENT24:
- return GPU_DEPTH_COMPONENT24;
- case GL_DEPTH_COMPONENT16:
- return GPU_DEPTH_COMPONENT16;
- default:
- BLI_assert(!"Internal format incorrect or unsupported\n");
- }
- return -1;
-}
-
static GLenum gpu_get_gl_datatype(eGPUDataFormat format)
{
switch (format) {
@@ -639,8 +581,8 @@ static float *GPU_texture_rescale_3d(
GPUTexture *tex, int w, int h, int d, int channels, const float *fpixels)
{
const uint xf = w / tex->w, yf = h / tex->h, zf = d / tex->d;
- float *nfpixels = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d,
- "GPUTexture Rescaled 3Dtex");
+ float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d,
+ "GPUTexture Rescaled 3Dtex");
if (nfpixels) {
GPU_print_error_debug("You need to scale a 3D texture, feel the pain!");
@@ -677,12 +619,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())) {
@@ -825,7 +769,7 @@ GPUTexture *GPU_texture_create_nD(int w,
tex_format = GPU_DEPTH32F_STENCIL8;
}
- GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
+ GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__);
tex->w = w;
tex->h = h;
tex->d = d;
@@ -834,7 +778,7 @@ GPUTexture *GPU_texture_create_nD(int w,
tex->format = tex_format;
tex->components = gpu_get_component_count(tex_format);
tex->mipmaps = 0;
- tex->format_flag = 0;
+ tex->format_flag = static_cast<eGPUTextureFormatFlag>(0);
tex->number = -1;
if (n == 2) {
@@ -918,7 +862,7 @@ GPUTexture *GPU_texture_create_nD(int w,
data_type,
tex->components,
can_rescale,
- pixels,
+ (float *)pixels,
&rescaled_pixels);
if (G.debug & G_DEBUG_GPU || !valid) {
@@ -949,7 +893,7 @@ GPUTexture *GPU_texture_create_nD(int w,
gpu_texture_memory_footprint_add(tex);
/* Upload Texture */
- const float *pix = (rescaled_pixels) ? rescaled_pixels : pixels;
+ const void *pix = (rescaled_pixels) ? rescaled_pixels : pixels;
if (tex->target == GL_TEXTURE_2D || tex->target == GL_TEXTURE_2D_MULTISAMPLE ||
tex->target == GL_TEXTURE_1D_ARRAY) {
@@ -999,7 +943,7 @@ GPUTexture *GPU_texture_cube_create(int w,
eGPUDataFormat gpu_data_format,
char err_out[256])
{
- GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
+ GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__);
tex->w = w;
tex->h = w;
tex->d = d;
@@ -1123,11 +1067,11 @@ GPUTexture *GPU_texture_cube_create(int w,
/* Special buffer textures. tex_format must be compatible with the buffer content. */
GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint buffer)
{
- GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
+ GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__);
tex->refcount = 1;
tex->format = tex_format;
tex->components = gpu_get_component_count(tex_format);
- tex->format_flag = 0;
+ tex->format_flag = static_cast<eGPUTextureFormatFlag>(0);
tex->target_base = tex->target = GL_TEXTURE_BUFFER;
tex->mipmaps = 0;
tex->number = -1;
@@ -1173,44 +1117,87 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint
return tex;
}
-GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode)
+static GLenum convert_target_to_gl(int dimension, bool is_array)
+{
+ switch (dimension) {
+ case 1:
+ return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY;
+ case 2:
+ return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY;
+ case 3:
+ return GL_TEXTURE_3D;
+ default:
+ BLI_assert(0);
+ return GL_TEXTURE_2D;
+ }
+}
+
+/* Create an error texture that will bind an invalid texture (pink) at draw time. */
+GPUTexture *GPU_texture_create_error(int dimension, bool is_array)
{
- GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
- tex->bindcode = bindcode;
+ GLenum textarget = convert_target_to_gl(dimension, is_array);
+
+ GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__);
+ tex->bindcode = 0;
tex->refcount = 1;
tex->target = textarget;
tex->target_base = textarget;
tex->samples = 0;
- tex->sampler_state = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO;
- if (GPU_get_mipmap()) {
- tex->sampler_state |= (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER);
- }
+ tex->sampler_state = GPU_SAMPLER_DEFAULT;
tex->number = -1;
- if (!glIsTexture(tex->bindcode)) {
- GPU_print_error_debug("Blender Texture Not Loaded");
+ GPU_print_error_debug("Blender Texture Not Loaded");
+ return tex;
+}
+
+/* DDS texture loading. Return NULL if support is not available. */
+GPUTexture *GPU_texture_create_compressed(
+ int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data)
+{
+ if (!GLEW_EXT_texture_compression_s3tc) {
+ return NULL;
}
- else {
- GLint w, h, gl_format;
- GLenum gettarget;
- gettarget = (textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : textarget;
-
- glBindTexture(textarget, tex->bindcode);
- glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w);
- glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h);
- glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_format);
- tex->w = w;
- tex->h = h;
- tex->format = gl_internalformat_to_gpu_format(gl_format);
- tex->components = gpu_get_component_count(tex->format);
- glBindTexture(textarget, 0);
-
- /* Depending on how this bindcode was obtained, the memory used here could
- * already have been computed.
- * But that is not the case currently. */
- gpu_texture_memory_footprint_add(tex);
+
+ GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__);
+ tex->w = w;
+ tex->h = h;
+ tex->refcount = 1;
+ tex->target = tex->target_base = GL_TEXTURE_2D;
+ tex->format_flag = static_cast<eGPUTextureFormatFlag>(0);
+ tex->components = gpu_get_component_count(tex_format);
+ tex->mipmaps = miplen - 1;
+ tex->sampler_state = GPU_SAMPLER_DEFAULT;
+ tex->number = -1;
+
+ GLenum internalformat = gpu_format_to_gl_internalformat(tex_format);
+
+ glGenTextures(1, &tex->bindcode);
+ glBindTexture(tex->target, tex->bindcode);
+
+ /* Reset to opengl Defaults. (Untested, might not be needed) */
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+ int blocksize = (ELEM(tex_format, GPU_RGBA8_DXT1, GPU_SRGB8_A8_DXT1)) ? 8 : 16;
+
+ size_t ofs = 0;
+ for (int mip = 0; mip < miplen && (w || h); mip++, w >>= 1, h >>= 1) {
+ w = max_ii(1, w);
+ h = max_ii(1, h);
+ size_t size = ((w + 3) / 4) * ((h + 3) / 4) * blocksize;
+
+ glCompressedTexImage2D(tex->target, mip, internalformat, w, h, 0, size, (uchar *)data + ofs);
+
+ ofs += size;
}
+ /* Restore Blender default. */
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, tex->mipmaps);
+ glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glBindTexture(tex->target, 0);
+
return tex;
}
@@ -1462,18 +1449,11 @@ void GPU_texture_update_sub(GPUTexture *tex,
BLI_assert((int)tex->format > -1);
BLI_assert(tex->components > -1);
- const uint bytesize = gpu_get_bytesize(tex->format);
GLenum data_format = gpu_get_gl_dataformat(tex->format, &tex->format_flag);
GLenum data_type = gpu_get_gl_datatype(gpu_data_format);
- GLint alignment;
- /* The default pack size for textures is 4, which won't work for byte based textures */
- if (bytesize == 1) {
- glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- }
+ WARN_NOT_BOUND(tex);
- glBindTexture(tex->target, tex->bindcode);
switch (tex->target) {
case GL_TEXTURE_1D:
glTexSubImage1D(tex->target, 0, offset_x, width, data_format, data_type, pixels);
@@ -1501,12 +1481,6 @@ void GPU_texture_update_sub(GPUTexture *tex,
default:
BLI_assert(!"tex->target mode not supported");
}
-
- if (bytesize == 1) {
- glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
- }
-
- glBindTexture(tex->target, 0);
}
void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat gpu_data_format, int miplvl)
@@ -1772,8 +1746,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;
}
@@ -1795,16 +1774,6 @@ void GPU_texture_unbind_all(void)
glActiveTexture(GL_TEXTURE0);
}
-#define WARN_NOT_BOUND(_tex) \
- { \
- if (_tex->number == -1) { \
- fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \
- BLI_assert(0); \
- return; \
- } \
- } \
- ((void)0)
-
void GPU_texture_generate_mipmap(GPUTexture *tex)
{
WARN_NOT_BOUND(tex);
@@ -1948,21 +1917,54 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter)
SET_FLAG_FROM_TEST(tex->sampler_state, use_filter, GPU_SAMPLER_FILTER);
}
+void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso)
+{
+ /* Stencil and integer format does not support filtering. */
+ BLI_assert(!(use_aniso) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex)));
+
+ SET_FLAG_FROM_TEST(tex->sampler_state, use_aniso, GPU_SAMPLER_ANISO);
+}
+
void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp)
{
SET_FLAG_FROM_TEST(tex->sampler_state, use_repeat, GPU_SAMPLER_REPEAT);
SET_FLAG_FROM_TEST(tex->sampler_state, !use_clamp, GPU_SAMPLER_CLAMP_BORDER);
}
-void GPU_texture_swizzle_channel_auto(GPUTexture *tex, int channels)
+static int gpu_texture_swizzle_to_enum(const char swizzle)
+{
+ switch (swizzle) {
+ case 'w':
+ case 'a':
+ return GL_ALPHA;
+ case 'z':
+ case 'b':
+ return GL_BLUE;
+ case 'y':
+ case 'g':
+ return GL_GREEN;
+ case '0':
+ return GL_ZERO;
+ case '1':
+ return GL_ONE;
+ case 'x':
+ case 'r':
+ default:
+ return GL_RED;
+ }
+}
+
+void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4])
{
WARN_NOT_BOUND(tex);
+ GLint gl_swizzle[4] = {gpu_texture_swizzle_to_enum(swizzle[0]),
+ gpu_texture_swizzle_to_enum(swizzle[1]),
+ gpu_texture_swizzle_to_enum(swizzle[2]),
+ gpu_texture_swizzle_to_enum(swizzle[3])};
+
glActiveTexture(GL_TEXTURE0 + tex->number);
- glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_R, GL_RED);
- glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_G, (channels >= 2) ? GL_GREEN : GL_RED);
- glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_B, (channels >= 3) ? GL_BLUE : GL_RED);
- glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_A, (channels >= 4) ? GL_ALPHA : GL_ONE);
+ glTexParameteriv(tex->target_base, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle);
}
void GPU_texture_free(GPUTexture *tex)
@@ -2137,7 +2139,7 @@ void GPU_samplers_init(void)
{
glGenSamplers(GPU_SAMPLER_MAX, GG.samplers);
for (int i = 0; i < GPU_SAMPLER_MAX; i++) {
- eGPUSamplerState state = i;
+ eGPUSamplerState state = static_cast<eGPUSamplerState>(i);
GLenum clamp_type = (state & GPU_SAMPLER_CLAMP_BORDER) ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE;
GLenum wrap_s = (state & GPU_SAMPLER_REPEAT_S) ? GL_REPEAT : clamp_type;
GLenum wrap_t = (state & GPU_SAMPLER_REPEAT_T) ? GL_REPEAT : clamp_type;
@@ -2147,8 +2149,9 @@ void GPU_samplers_init(void)
((state & GPU_SAMPLER_MIPMAP) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) :
((state & GPU_SAMPLER_MIPMAP) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST);
GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE;
+ /* TODO(fclem) Anisotropic level should be a render engine parameter. */
float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ?
- GPU_get_anisotropic() :
+ U.anisotropic_filter :
1.0f;
glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s);
@@ -2169,11 +2172,23 @@ void GPU_samplers_init(void)
* - GL_TEXTURE_LOD_BIAS is 0.0f.
**/
}
+
+ /* Custom sampler for icons. */
+ glGenSamplers(1, &GG.icon_sampler);
+ glSamplerParameteri(GG.icon_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+ glSamplerParameteri(GG.icon_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glSamplerParameteri(GG.icon_sampler, GL_TEXTURE_LOD_BIAS, -0.5f);
+}
+
+void GPU_sampler_icon_bind(int unit)
+{
+ glBindSampler(unit, GG.icon_sampler);
}
void GPU_samplers_free(void)
{
glDeleteSamplers(GPU_SAMPLER_MAX, GG.samplers);
+ glDeleteSamplers(1, &GG.icon_sampler);
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.cc
index 130e8fe7da1..b2e31894f9d 100644
--- a/source/blender/gpu/intern/gpu_uniformbuffer.c
+++ b/source/blender/gpu/intern/gpu_uniformbuffer.cc
@@ -25,6 +25,7 @@
#include <string.h>
#include "BLI_blenlib.h"
+#include "BLI_math_base.h"
#include "gpu_context_private.h"
#include "gpu_node_graph.h"
@@ -34,217 +35,63 @@
#include "GPU_material.h"
#include "GPU_uniformbuffer.h"
-typedef enum eGPUUniformBufferFlag {
- GPU_UBO_FLAG_INITIALIZED = (1 << 0),
- GPU_UBO_FLAG_DIRTY = (1 << 1),
-} eGPUUniformBufferFlag;
-
-typedef enum eGPUUniformBufferType {
- GPU_UBO_STATIC = 0,
- GPU_UBO_DYNAMIC = 1,
-} eGPUUniformBufferType;
-
-struct GPUUniformBuffer {
- int size; /* in bytes */
- GLuint bindcode; /* opengl identifier for UBO */
- int bindpoint; /* current binding point */
- eGPUUniformBufferType type;
-};
-
-#define GPUUniformBufferStatic GPUUniformBuffer
-
-typedef struct GPUUniformBufferDynamic {
- GPUUniformBuffer buffer;
- void *data; /* Continuous memory block to copy to GPU. */
- char flag;
-} GPUUniformBufferDynamic;
-
-/* Prototypes */
-static eGPUType get_padded_gpu_type(struct LinkData *link);
-static void gpu_uniformbuffer_inputs_sort(struct ListBase *inputs);
-
-/* Only support up to this type, if you want to extend it, make sure the
- * padding logic is correct for the new types. */
-#define MAX_UBO_GPU_TYPE GPU_MAT4
-
-static void gpu_uniformbuffer_initialize(GPUUniformBuffer *ubo, const void *data)
-{
- glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
- glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
-}
+typedef struct GPUUniformBuffer {
+ /** Data size in bytes. */
+ int size;
+ /** GL handle for UBO. */
+ GLuint bindcode;
+ /** Current binding point. */
+ int bindpoint;
+ /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */
+ void *data;
+} GPUUniformBuffer;
GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256])
{
/* Make sure that UBO is padded to size of vec4 */
BLI_assert((size % 16) == 0);
- GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBufferStatic), "GPUUniformBufferStatic");
- ubo->size = size;
- ubo->bindpoint = -1;
-
- /* Generate Buffer object */
- ubo->bindcode = GPU_buf_alloc();
-
- if (!ubo->bindcode) {
- if (err_out) {
- BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256);
- }
- GPU_uniformbuffer_free(ubo);
- return NULL;
- }
-
- if (ubo->size > GPU_max_ubo_size()) {
+ if (size > GPU_max_ubo_size()) {
if (err_out) {
BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256);
}
- GPU_uniformbuffer_free(ubo);
- return NULL;
- }
-
- gpu_uniformbuffer_initialize(ubo, data);
- return ubo;
-}
-
-/**
- * Create dynamic UBO from parameters
- * Return NULL if failed to create or if \param inputs: is empty.
- *
- * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
- */
-GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256])
-{
- /* There is no point on creating an UBO if there is no arguments. */
- if (BLI_listbase_is_empty(inputs)) {
return NULL;
}
- GPUUniformBufferDynamic *ubo = MEM_callocN(sizeof(GPUUniformBufferDynamic),
- "GPUUniformBufferDynamic");
- ubo->buffer.type = GPU_UBO_DYNAMIC;
- ubo->buffer.bindpoint = -1;
- ubo->flag = GPU_UBO_FLAG_DIRTY;
-
- /* Generate Buffer object. */
- ubo->buffer.bindcode = GPU_buf_alloc();
-
- if (!ubo->buffer.bindcode) {
- if (err_out) {
- BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256);
- }
- GPU_uniformbuffer_free(&ubo->buffer);
- return NULL;
- }
-
- if (ubo->buffer.size > GPU_max_ubo_size()) {
- if (err_out) {
- BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256);
- }
- GPU_uniformbuffer_free(&ubo->buffer);
- return NULL;
- }
-
- /* Make sure we comply to the ubo alignment requirements. */
- gpu_uniformbuffer_inputs_sort(inputs);
-
- LISTBASE_FOREACH (LinkData *, link, inputs) {
- const eGPUType gputype = get_padded_gpu_type(link);
- ubo->buffer.size += gputype * sizeof(float);
- }
-
- /* Round up to size of vec4 */
- ubo->buffer.size = ((ubo->buffer.size + 15) / 16) * 16;
-
- /* Allocate the data. */
- ubo->data = MEM_mallocN(ubo->buffer.size, __func__);
+ GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__);
+ ubo->size = size;
+ ubo->data = NULL;
+ ubo->bindcode = 0;
+ ubo->bindpoint = -1;
- /* Now that we know the total ubo size we can start populating it. */
- float *offset = ubo->data;
- LISTBASE_FOREACH (LinkData *, link, inputs) {
- GPUInput *input = link->data;
- memcpy(offset, input->vec, input->type * sizeof(float));
- offset += get_padded_gpu_type(link);
+ /* Direct init. */
+ if (data != NULL) {
+ GPU_uniformbuffer_update(ubo, data);
}
- /* Note since we may create the UBOs in the CPU in a different thread than the main drawing one,
- * we don't create the UBO in the GPU here. This will happen when we first bind the UBO.
- */
-
- return &ubo->buffer;
-}
-
-/**
- * Free the data
- */
-static void gpu_uniformbuffer_dynamic_free(GPUUniformBuffer *ubo_)
-{
- BLI_assert(ubo_->type == GPU_UBO_DYNAMIC);
- GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_;
-
- ubo->buffer.size = 0;
- if (ubo->data) {
- MEM_freeN(ubo->data);
- }
+ return ubo;
}
void GPU_uniformbuffer_free(GPUUniformBuffer *ubo)
{
- if (ubo->type == GPU_UBO_DYNAMIC) {
- gpu_uniformbuffer_dynamic_free(ubo);
- }
-
+ MEM_SAFE_FREE(ubo->data);
GPU_buf_free(ubo->bindcode);
MEM_freeN(ubo);
}
-static void gpu_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
-{
- glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
- glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
-}
-
-void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
-{
- BLI_assert(ubo->type == GPU_UBO_STATIC);
- gpu_uniformbuffer_update(ubo, data);
-}
-
-/**
- * We need to recalculate the internal data, and re-generate it
- * from its populated items.
- */
-void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_)
-{
- BLI_assert(ubo_->type == GPU_UBO_DYNAMIC);
- GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_;
-
- if (ubo->flag & GPU_UBO_FLAG_INITIALIZED) {
- gpu_uniformbuffer_update(ubo_, ubo->data);
- }
- else {
- ubo->flag |= GPU_UBO_FLAG_INITIALIZED;
- gpu_uniformbuffer_initialize(ubo_, ubo->data);
- }
-
- ubo->flag &= ~GPU_UBO_FLAG_DIRTY;
-}
-
/**
* We need to pad some data types (vec3) on the C side
* To match the GPU expected memory block alignment.
*/
static eGPUType get_padded_gpu_type(LinkData *link)
{
- GPUInput *input = link->data;
+ GPUInput *input = (GPUInput *)link->data;
eGPUType gputype = input->type;
-
/* Unless the vec3 is followed by a float we need to treat it as a vec4. */
if (gputype == GPU_VEC3 && (link->next != NULL) &&
(((GPUInput *)link->next->data)->type != GPU_FLOAT)) {
gputype = GPU_VEC4;
}
-
return gputype;
}
@@ -254,8 +101,9 @@ static eGPUType get_padded_gpu_type(LinkData *link)
*/
static int inputs_cmp(const void *a, const void *b)
{
- const LinkData *link_a = a, *link_b = b;
- const GPUInput *input_a = link_a->data, *input_b = link_b->data;
+ const LinkData *link_a = (const LinkData *)a, *link_b = (const LinkData *)b;
+ const GPUInput *input_a = (const GPUInput *)link_a->data;
+ const GPUInput *input_b = (const GPUInput *)link_b->data;
return input_a->type < input_b->type ? 1 : 0;
}
@@ -265,15 +113,19 @@ static int inputs_cmp(const void *a, const void *b)
*/
static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
{
+/* Only support up to this type, if you want to extend it, make sure the
+ * padding logic is correct for the new types. */
+#define MAX_UBO_GPU_TYPE GPU_MAT4
+
/* Order them as mat4, vec4, vec3, vec2, float. */
BLI_listbase_sort(inputs, inputs_cmp);
/* Creates a lookup table for the different types; */
LinkData *inputs_lookup[MAX_UBO_GPU_TYPE + 1] = {NULL};
- eGPUType cur_type = MAX_UBO_GPU_TYPE + 1;
+ eGPUType cur_type = static_cast<eGPUType>(MAX_UBO_GPU_TYPE + 1);
LISTBASE_FOREACH (LinkData *, link, inputs) {
- GPUInput *input = link->data;
+ GPUInput *input = (GPUInput *)link->data;
if (input->type == GPU_MAT3) {
/* Alignment for mat3 is not handled currently, so not supported */
@@ -319,6 +171,74 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs)
link = link_next;
}
+#undef MAX_UBO_GPU_TYPE
+}
+
+/**
+ * Create dynamic UBO from parameters
+ * Return NULL if failed to create or if \param inputs: is empty.
+ *
+ * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
+ */
+GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256])
+{
+ /* There is no point on creating an UBO if there is no arguments. */
+ if (BLI_listbase_is_empty(inputs)) {
+ return NULL;
+ }
+ /* Make sure we comply to the ubo alignment requirements. */
+ gpu_uniformbuffer_inputs_sort(inputs);
+
+ size_t buffer_size = 0;
+
+ LISTBASE_FOREACH (LinkData *, link, inputs) {
+ const eGPUType gputype = get_padded_gpu_type(link);
+ buffer_size += gputype * sizeof(float);
+ }
+ /* Round up to size of vec4. (Opengl Requirement) */
+ size_t alignment = sizeof(float[4]);
+ buffer_size = divide_ceil_u(buffer_size, alignment) * alignment;
+ void *data = MEM_mallocN(buffer_size, __func__);
+
+ /* Now that we know the total ubo size we can start populating it. */
+ float *offset = (float *)data;
+ LISTBASE_FOREACH (LinkData *, link, inputs) {
+ GPUInput *input = (GPUInput *)link->data;
+ memcpy(offset, input->vec, input->type * sizeof(float));
+ offset += get_padded_gpu_type(link);
+ }
+
+ /* Pass data as NULL for late init. */
+ GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out);
+ /* Data will be update just before binding. */
+ ubo->data = data;
+ return ubo;
+}
+
+static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo)
+{
+ BLI_assert(ubo->bindcode == 0);
+ ubo->bindcode = GPU_buf_alloc();
+
+ if (ubo->bindcode == 0) {
+ fprintf(stderr, "GPUUniformBuffer: UBO create failed");
+ BLI_assert(0);
+ return;
+ }
+
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
+ glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW);
+}
+
+void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
+{
+ if (ubo->bindcode == 0) {
+ gpu_uniformbuffer_init(ubo);
+ }
+
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode);
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number)
@@ -328,28 +248,30 @@ void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number)
return;
}
- if (ubo->type == GPU_UBO_DYNAMIC) {
- GPUUniformBufferDynamic *ubo_dynamic = (GPUUniformBufferDynamic *)ubo;
- if (ubo_dynamic->flag & GPU_UBO_FLAG_DIRTY) {
- GPU_uniformbuffer_dynamic_update(ubo);
- }
+ if (ubo->bindcode == 0) {
+ gpu_uniformbuffer_init(ubo);
}
- if (ubo->bindcode != 0) {
- glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode);
+ if (ubo->data != NULL) {
+ GPU_uniformbuffer_update(ubo, ubo->data);
+ MEM_SAFE_FREE(ubo->data);
}
+ glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode);
ubo->bindpoint = number;
}
void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo)
{
- ubo->bindpoint = -1;
+#ifndef NDEBUG
+ glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0);
+#endif
+ ubo->bindpoint = 0;
}
-int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo)
+void GPU_uniformbuffer_unbind_all(void)
{
- return ubo->bindpoint;
-}
-
-#undef MAX_UBO_GPU_TYPE
+ for (int i = 0; i < GPU_max_ubo_binds(); i++) {
+ glBindBufferBase(GL_UNIFORM_BUFFER, i, 0);
+ }
+} \ No newline at end of file
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.c b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 25daabe601d..eda6d1c7300 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.c
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -39,17 +39,22 @@ static uint vbo_memory_usage;
static GLenum convert_usage_type_to_gl(GPUUsageType type)
{
- static const GLenum table[] = {
- [GPU_USAGE_STREAM] = GL_STREAM_DRAW,
- [GPU_USAGE_STATIC] = GL_STATIC_DRAW,
- [GPU_USAGE_DYNAMIC] = GL_DYNAMIC_DRAW,
- };
- return table[type];
+ switch (type) {
+ case GPU_USAGE_STREAM:
+ return GL_STREAM_DRAW;
+ case GPU_USAGE_DYNAMIC:
+ return GL_DYNAMIC_DRAW;
+ case GPU_USAGE_STATIC:
+ return GL_STATIC_DRAW;
+ default:
+ BLI_assert(0);
+ return GL_STATIC_DRAW;
+ }
}
GPUVertBuf *GPU_vertbuf_create(GPUUsageType usage)
{
- GPUVertBuf *verts = MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf");
+ GPUVertBuf *verts = (GPUVertBuf *)MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf");
GPU_vertbuf_init(verts, usage);
return verts;
}
@@ -85,6 +90,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 = (uchar *)MEM_dupallocN(verts->data);
+ }
+ return verts_dst;
+}
+
/** Same as discard but does not free. */
void GPU_vertbuf_clear(GPUVertBuf *verts)
{
@@ -132,7 +166,7 @@ void GPU_vertbuf_data_alloc(GPUVertBuf *verts, uint v_len)
#endif
verts->dirty = true;
verts->vertex_len = verts->vertex_alloc = v_len;
- verts->data = MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), "GPUVertBuf data");
+ verts->data = (uchar *)MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), __func__);
}
/* resize buffer keeping existing data */
@@ -149,7 +183,7 @@ void GPU_vertbuf_data_resize(GPUVertBuf *verts, uint v_len)
#endif
verts->dirty = true;
verts->vertex_len = verts->vertex_alloc = v_len;
- verts->data = MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts));
+ verts->data = (uchar *)MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts));
}
/* Set vertex count but does not change allocation.
diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.cc
index b84a7e0f554..a59a6a468ce 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.c
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -63,21 +63,29 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src)
memcpy(dest, src, sizeof(GPUVertFormat));
}
-static GLenum convert_comp_type_to_gl(GPUVertCompType type)
+GLenum convert_comp_type_to_gl(GPUVertCompType type)
{
- static const GLenum table[] = {
- [GPU_COMP_I8] = GL_BYTE,
- [GPU_COMP_U8] = GL_UNSIGNED_BYTE,
- [GPU_COMP_I16] = GL_SHORT,
- [GPU_COMP_U16] = GL_UNSIGNED_SHORT,
- [GPU_COMP_I32] = GL_INT,
- [GPU_COMP_U32] = GL_UNSIGNED_INT,
-
- [GPU_COMP_F32] = GL_FLOAT,
-
- [GPU_COMP_I10] = GL_INT_2_10_10_10_REV,
- };
- return table[type];
+ switch (type) {
+ case GPU_COMP_I8:
+ return GL_BYTE;
+ case GPU_COMP_U8:
+ return GL_UNSIGNED_BYTE;
+ case GPU_COMP_I16:
+ return GL_SHORT;
+ case GPU_COMP_U16:
+ return GL_UNSIGNED_SHORT;
+ case GPU_COMP_I32:
+ return GL_INT;
+ case GPU_COMP_U32:
+ return GL_UNSIGNED_INT;
+ case GPU_COMP_F32:
+ return GL_FLOAT;
+ case GPU_COMP_I10:
+ return GL_INT_2_10_10_10_REV;
+ default:
+ BLI_assert(0);
+ return GL_FLOAT;
+ }
}
static uint comp_sz(GPUVertCompType type)
@@ -94,7 +102,7 @@ static uint attr_sz(const GPUVertAttr *a)
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
}
- return a->comp_len * comp_sz(a->comp_type);
+ return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type));
}
static uint attr_align(const GPUVertAttr *a)
@@ -102,7 +110,7 @@ static uint attr_align(const GPUVertAttr *a)
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
}
- uint c = comp_sz(a->comp_type);
+ uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type));
if (a->comp_len == 3 && c <= 2) {
return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */
}
@@ -185,7 +193,6 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format,
attr->names[attr->name_len++] = copy_attr_name(format, name);
attr->comp_type = comp_type;
- attr->gl_comp_type = convert_comp_type_to_gl(comp_type);
attr->comp_len = (comp_type == GPU_COMP_I10) ?
4 :
comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */
@@ -262,10 +269,24 @@ 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])
{
- char safe_chars[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+ char safe_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
uint64_t in = *(uint64_t *)data;
for (int i = 0; i < 11; i++) {
@@ -354,14 +375,6 @@ static void show_pack(uint a_idx, uint sz, uint pad)
void VertexFormat_pack(GPUVertFormat *format)
{
- /* For now, attributes are packed in the order they were added,
- * making sure each attribute is naturally aligned (add padding where necessary)
- * Later we can implement more efficient packing w/ reordering
- * (keep attribute ID order, adjust their offsets to reorder in buffer). */
-
- /* TODO: realloc just enough to hold the final combo string. And just enough to
- * hold used attributes, not all 16. */
-
GPUVertAttr *a0 = &format->attrs[0];
a0->offset = 0;
uint offset = a0->sz;
@@ -498,7 +511,6 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
attr->sz = attr->comp_len * 4;
attr->fetch_mode = fetch_mode;
attr->comp_type = comp_type;
- attr->gl_comp_type = convert_comp_type_to_gl(comp_type);
attr += 1;
}
}
diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h
index a850d17a1dd..7b560a35bd0 100644
--- a/source/blender/gpu/intern/gpu_vertex_format_private.h
+++ b/source/blender/gpu/intern/gpu_vertex_format_private.h
@@ -26,8 +26,17 @@
#ifndef __GPU_VERTEX_FORMAT_PRIVATE_H__
#define __GPU_VERTEX_FORMAT_PRIVATE_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void VertexFormat_pack(GPUVertFormat *format);
uint padding(uint offset, uint alignment);
uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len);
+GLenum convert_comp_type_to_gl(GPUVertCompType type);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* __GPU_VERTEX_FORMAT_PRIVATE_H__ */
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index 753da8544ea..ed5297f0a4a 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -38,7 +38,6 @@
#include "DNA_vec_types.h"
#include "GPU_framebuffer.h"
-#include "GPU_glew.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_texture.h"
@@ -630,13 +629,13 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo
if (settings == S3D_DISPLAY_ANAGLYPH) {
switch (stereo_format->anaglyph_type) {
case S3D_ANAGLYPH_REDCYAN:
- glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(false, true, true, true);
break;
case S3D_ANAGLYPH_GREENMAGENTA:
- glColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(true, false, true, true);
break;
case S3D_ANAGLYPH_YELLOWBLUE:
- glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(false, false, true, true);
break;
}
}
@@ -666,7 +665,7 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo
GPU_matrix_pop();
if (settings == S3D_DISPLAY_ANAGLYPH) {
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ GPU_color_mask(true, true, true, true);
}
GPU_framebuffer_restore();
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..d15f48c8f8a 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];
@@ -129,123 +9,182 @@ uniform vec4 parameters[MAX_PARAM * MAX_INSTANCE];
uniform vec4 parameters[MAX_PARAM];
#endif
-/* gl_InstanceID is 0 if not drawing instances. */
-#define recti parameters[gl_InstanceID * MAX_PARAM + 0]
-#define rect parameters[gl_InstanceID * MAX_PARAM + 1]
-#define radsi parameters[gl_InstanceID * MAX_PARAM + 2].x
-#define rads parameters[gl_InstanceID * MAX_PARAM + 2].y
-#define faci parameters[gl_InstanceID * MAX_PARAM + 2].zw
-#define roundCorners parameters[gl_InstanceID * MAX_PARAM + 3]
-#define colorInner1 parameters[gl_InstanceID * MAX_PARAM + 4]
-#define colorInner2 parameters[gl_InstanceID * MAX_PARAM + 5]
-#define colorEdge parameters[gl_InstanceID * MAX_PARAM + 6]
-#define colorEmboss parameters[gl_InstanceID * MAX_PARAM + 7]
-#define colorTria parameters[gl_InstanceID * MAX_PARAM + 8]
-#define tria1Center parameters[gl_InstanceID * MAX_PARAM + 9].xy
-#define tria2Center parameters[gl_InstanceID * MAX_PARAM + 9].zw
-#define tria1Size parameters[gl_InstanceID * MAX_PARAM + 10].x
-#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
+/* gl_InstanceID is supposed to be 0 if not drawing instances, but this seems
+ * to be violated in some drivers. For example, macOS 10.15.4 and Intel Iris
+ * causes T78307 when using gl_InstanceID outside of instance. */
+#ifdef USE_INSTANCE
+# define widgetID gl_InstanceID
+#else
+# define widgetID 0
+#endif
+
+#define recti parameters[widgetID * MAX_PARAM + 0]
+#define rect parameters[widgetID * MAX_PARAM + 1]
+#define radsi parameters[widgetID * MAX_PARAM + 2].x
+#define rads parameters[widgetID * MAX_PARAM + 2].y
+#define faci parameters[widgetID * MAX_PARAM + 2].zw
+#define roundCorners parameters[widgetID * MAX_PARAM + 3]
+#define colorInner1 parameters[widgetID * MAX_PARAM + 4]
+#define colorInner2 parameters[widgetID * MAX_PARAM + 5]
+#define colorEdge parameters[widgetID * MAX_PARAM + 6]
+#define colorEmboss parameters[widgetID * MAX_PARAM + 7]
+#define colorTria parameters[widgetID * MAX_PARAM + 8]
+#define tria1Center parameters[widgetID * MAX_PARAM + 9].xy
+#define tria2Center parameters[widgetID * MAX_PARAM + 9].zw
+#define tria1Size parameters[widgetID * MAX_PARAM + 10].x
+#define tria2Size parameters[widgetID * MAX_PARAM + 10].y
+#define shadeDir parameters[widgetID * MAX_PARAM + 10].z
+#define alphaDiscard parameters[widgetID * MAX_PARAM + 10].w
+#define triaType parameters[widgetID * 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;
+#ifdef OS_MAC
+in float dummy;
+#endif
+
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;
- }
- 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);
+ 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 /* (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_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
new file mode 100644
index 00000000000..f7bf3d33361
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
@@ -0,0 +1,87 @@
+
+vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2)
+{
+ vec3 edge21 = pos2 - pos1;
+ vec3 edge10 = pos1 - pos0;
+ vec3 edge02 = pos0 - pos2;
+ vec3 d21 = normalize(edge21);
+ vec3 d10 = normalize(edge10);
+ vec3 d02 = normalize(edge02);
+
+ vec3 dists;
+ float d = dot(d21, edge02);
+ dists.x = sqrt(dot(edge02, edge02) - d * d);
+ d = dot(d02, edge10);
+ dists.y = sqrt(dot(edge10, edge10) - d * d);
+ d = dot(d10, edge21);
+ dists.z = sqrt(dot(edge21, edge21) - d * d);
+ return dists;
+}
+
+vec2 calc_barycentric_co(int vertid)
+{
+ vec2 bary;
+ bary.x = float((vertid % 3) == 0);
+ bary.y = float((vertid % 3) == 1);
+ return bary;
+}
+
+#ifdef HAIR_SHADER
+
+/* Hairs uv and col attributes are passed by bufferTextures. */
+# define DEFINE_ATTR(type, attr) uniform samplerBuffer attr
+# define GET_ATTR(type, attr) hair_get_customdata_##type(attr)
+
+# define barycentric_get() hair_get_barycentric()
+# define barycentric_resolve(bary) hair_resolve_barycentric(bary)
+
+vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const samplerBuffer orco_samp)
+{
+ /* TODO: fix ORCO with modifiers. */
+ vec3 orco = (modelmatinv * vec4(local_pos, 1.0)).xyz;
+ return orco_madd[0].xyz + orco * orco_madd[1].xyz;
+}
+
+vec4 tangent_get(const samplerBuffer attr, mat3 normalmat)
+{
+ /* Unsupported */
+ return vec4(0.0);
+}
+
+#else /* MESH_SHADER */
+
+# define DEFINE_ATTR(type, attr) in type attr
+# define GET_ATTR(type, attr) attr
+
+/* Calculated in geom shader later with calc_barycentric_co. */
+# define barycentric_get() vec2(0)
+# define barycentric_resolve(bary) bary
+
+vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco)
+{
+ /* If the object does not have any deformation, the orco layer calculation is done on the fly
+ * using the orco_madd factors.
+ * We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
+ * attrib (which is [0,0,0,1]). */
+ if (orco.w == 0.0) {
+ return orco.xyz * 0.5 + 0.5;
+ }
+ else {
+ return orco_madd[0].xyz + local_pos * orco_madd[1].xyz;
+ }
+}
+
+vec4 tangent_get(vec4 attr, mat3 normalmat)
+{
+ vec4 tangent;
+ tangent.xyz = normalmat * attr.xyz;
+ tangent.w = attr.w;
+ float len_sqr = dot(tangent.xyz, tangent.xyz);
+ /* Normalize only if vector is not null. */
+ if (len_sqr > 0.0) {
+ tangent.xyz *= inversesqrt(len_sqr);
+ }
+ return tangent;
+}
+
+#endif
diff --git a/source/blender/gpu/shaders/gpu_shader_image_alpha_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_alpha_color_frag.glsl
deleted file mode 100644
index cf71d4ac0c5..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_image_alpha_color_frag.glsl
+++ /dev/null
@@ -1,14 +0,0 @@
-
-in vec2 texCoord_interp;
-out vec4 fragColor;
-
-uniform vec4 color;
-uniform sampler2D image;
-
-void main()
-{
- fragColor = texture(image, texCoord_interp).r * color.rgba;
- /* Premul by alpha (not texture alpha)
- * Use blending function GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); */
- fragColor.rgb *= color.a;
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_image_mask_uniform_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_mask_uniform_color_frag.glsl
deleted file mode 100644
index e5078d722b4..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_image_mask_uniform_color_frag.glsl
+++ /dev/null
@@ -1,12 +0,0 @@
-
-in vec2 texCoord_interp;
-out vec4 fragColor;
-
-uniform sampler2D image;
-uniform vec4 color;
-
-void main()
-{
- fragColor.a = texture(image, texCoord_interp).a * color.a;
- fragColor.rgb = color.rgb;
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl
deleted file mode 100644
index 613352b4ac8..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl
+++ /dev/null
@@ -1,12 +0,0 @@
-
-in vec2 texCoord_interp;
-out vec4 fragColor;
-
-uniform float alpha;
-uniform sampler2D image;
-
-void main()
-{
- fragColor = texture(image, texCoord_interp);
- fragColor.a *= alpha;
-}
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/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
index cc12e3f78a5..d85884e0a25 100644
--- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
@@ -136,4 +136,5 @@ void main()
}
fragColor.a *= color_flat.a;
+ 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..eea8d19efce 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
@@ -3,11 +3,11 @@ void node_ambient_occlusion(
vec4 color, float distance, vec3 normal, out vec4 result_color, out float result_ao)
{
vec3 bent_normal;
- vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0);
+ vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal);
result_color = result_ao * color;
}
#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_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 3b23ac976ae..6330daa4391 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -1,3 +1,15 @@
+
+float wang_hash_noise(uint s)
+{
+ s = (s ^ 61u) ^ (s >> 16u);
+ s *= 9u;
+ s = s ^ (s >> 4u);
+ s *= 0x27d4eb2du;
+ s = s ^ (s >> 15u);
+
+ return fract(float(s) / 4294967296.0);
+}
+
void node_hair_info(out float is_strand,
out float intercept,
out float thickness,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
index ba391df185e..27ca96501ae 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
@@ -3,8 +3,10 @@ uniform float backgroundAlpha;
void node_output_world(Closure surface, Closure volume, out Closure result)
{
#ifndef VOLUMETRICS
- result.radiance = surface.radiance * backgroundAlpha;
- result.transmittance = vec3(1.0 - backgroundAlpha);
+ float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha;
+ result = CLOSURE_DEFAULT;
+ result.radiance = surface.radiance * alpha;
+ result.transmittance = vec3(1.0 - alpha);
#else
result = volume;
#endif /* VOLUMETRICS */
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_tex_sky.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl
index 981d17b4283..b6aad5904ff 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl
@@ -1,4 +1,150 @@
-void node_tex_sky(vec3 co, out vec4 color)
+float sky_angle_between(float thetav, float phiv, float theta, float phi)
+{
+ float cospsi = sin(thetav) * sin(theta) * cos(phi - phiv) + cos(thetav) * cos(theta);
+
+ if (cospsi > 1.0) {
+ return 0.0;
+ }
+ if (cospsi < -1.0) {
+ return M_PI;
+ }
+
+ return acos(cospsi);
+}
+
+vec3 sky_spherical_coordinates(vec3 dir)
+{
+ return vec3(M_PI_2 - atan(dir.z, length(dir.xy)), atan(dir.x, dir.y), 0);
+}
+
+/* Preetham */
+/* lam03+lam4: 5 floats passed as vec4+float */
+float sky_perez_function(vec4 lam03, float lam4, float theta, float gamma)
+{
+ float ctheta = cos(theta);
+ float cgamma = cos(gamma);
+
+ return (1.0 + lam03[0] * exp(lam03[1] / ctheta)) *
+ (1.0 + lam03[2] * exp(lam03[3] * gamma) + lam4 * cgamma * cgamma);
+}
+
+vec3 xyY_to_xyz(float x, float y, float Y)
+{
+ float X, Z;
+
+ if (y != 0.0) {
+ X = (x / y) * Y;
+ }
+ else {
+ X = 0.0;
+ }
+
+ if (y != 0.0 && Y != 0.0) {
+ Z = ((1.0 - x - y) / y) * Y;
+ }
+ else {
+ Z = 0.0;
+ }
+
+ return vec3(X, Y, Z);
+}
+
+void node_tex_sky_preetham(vec3 co,
+ vec4 config_Y03,
+ float config_Y4,
+ vec4 config_x03,
+ float config_x4,
+ vec4 config_y03,
+ float config_y4,
+ vec2 sun_angles,
+ vec3 radiance,
+ vec3 xyz_to_r,
+ vec3 xyz_to_g,
+ vec3 xyz_to_b,
+ out vec4 color)
+{
+ /* convert vector to spherical coordinates */
+ vec3 spherical = sky_spherical_coordinates(co);
+ float theta = spherical[0];
+ float phi = spherical[1];
+
+ float suntheta = sun_angles[0];
+ float sunphi = sun_angles[1];
+
+ /* angle between sun direction and dir */
+ float gamma = sky_angle_between(theta, phi, suntheta, sunphi);
+
+ /* clamp theta to horizon */
+ theta = min(theta, M_PI_2 - 0.001);
+
+ /* compute xyY color space values */
+ float Y = radiance[0] * sky_perez_function(config_Y03, config_Y4, theta, gamma);
+ float x = radiance[1] * sky_perez_function(config_x03, config_x4, theta, gamma);
+ float y = radiance[2] * sky_perez_function(config_y03, config_y4, theta, gamma);
+
+ /* convert to RGB */
+ vec3 xyz = xyY_to_xyz(x, y, Y);
+ color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1);
+}
+
+/* Hosek / Wilkie */
+float sky_radiance_hosekwilkie(
+ vec4 config03, vec4 config47, float config8, float theta, float gamma)
+{
+ float ctheta = cos(theta);
+ float cgamma = cos(gamma);
+
+ float expM = exp(config47[0] * gamma);
+ float rayM = cgamma * cgamma;
+ float mieM = (1.0 + rayM) / pow((1.0 + config8 * config8 - 2.0 * config8 * cgamma), 1.5);
+ float zenith = sqrt(ctheta);
+
+ return (1.0 + config03[0] * exp(config03[1] / (ctheta + 0.01))) *
+ (config03[2] + config03[3] * expM + config47[1] * rayM + config47[2] * mieM +
+ config47[3] * zenith);
+}
+
+void node_tex_sky_hosekwilkie(vec3 co,
+ vec4 config_x03,
+ vec4 config_x47,
+ vec4 config_y03,
+ vec4 config_y47,
+ vec4 config_z03,
+ vec4 config_z47,
+ vec3 config_xyz8,
+ vec2 sun_angles,
+ vec3 radiance,
+ vec3 xyz_to_r,
+ vec3 xyz_to_g,
+ vec3 xyz_to_b,
+ out vec4 color)
+{
+ /* convert vector to spherical coordinates */
+ vec3 spherical = sky_spherical_coordinates(co);
+ float theta = spherical[0];
+ float phi = spherical[1];
+
+ float suntheta = sun_angles[0];
+ float sunphi = sun_angles[1];
+
+ /* angle between sun direction and dir */
+ float gamma = sky_angle_between(theta, phi, suntheta, sunphi);
+
+ /* clamp theta to horizon */
+ theta = min(theta, M_PI_2 - 0.001);
+
+ vec3 xyz;
+ xyz.x = sky_radiance_hosekwilkie(config_x03, config_x47, config_xyz8[0], theta, gamma) *
+ radiance.x;
+ xyz.y = sky_radiance_hosekwilkie(config_y03, config_y47, config_xyz8[1], theta, gamma) *
+ radiance.y;
+ xyz.z = sky_radiance_hosekwilkie(config_z03, config_z47, config_xyz8[2], theta, gamma) *
+ radiance.z;
+
+ color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1);
+}
+
+void node_tex_sky_nishita(vec3 co, out vec4 color)
{
color = vec4(1.0);
}
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/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
index f9691beee6f..d33465fa846 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
@@ -6,7 +6,7 @@ void world_normals_get(out vec3 N)
vec3 B = normalize(cross(worldNormal, hairTangent));
float cos_theta;
if (hairThicknessRes == 1) {
- vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0);
+ vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
/* Random cosine normal distribution on the hair surface. */
cos_theta = rand.x * 2.0 - 1.0;
}
diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp
index 66ed0dd0fa5..8f84d04f602 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.cpp
+++ b/source/blender/ikplugin/intern/itasc_plugin.cpp
@@ -117,12 +117,8 @@ struct IK_Target {
}
~IK_Target()
{
- if (constraint) {
- delete constraint;
- }
- if (target) {
- delete target;
- }
+ delete constraint;
+ delete target;
}
};
@@ -196,29 +192,17 @@ struct IK_Scene {
~IK_Scene()
{
// delete scene first
- if (scene) {
- delete scene;
- }
+ delete scene;
for (std::vector<IK_Target *>::iterator it = targets.begin(); it != targets.end(); ++it) {
delete (*it);
}
targets.clear();
- if (channels) {
- delete[] channels;
- }
- if (solver) {
- delete solver;
- }
- if (armature) {
- delete armature;
- }
- if (base) {
- delete base;
- }
+ delete[] channels;
+ delete solver;
+ delete armature;
+ delete base;
// delete cache last
- if (cache) {
- delete cache;
- }
+ delete cache;
}
};
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index cad0be659ec..cf637a06405 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -23,6 +23,7 @@ set(INC
../blenkernel
../blenlib
../blenloader
+ ../gpu
../makesdna
../makesrna
../../../intern/guardedalloc
@@ -63,6 +64,7 @@ set(SRC
intern/thumbs_blend.c
intern/thumbs_font.c
intern/util.c
+ intern/util_gpu.c
intern/writeimage.c
IMB_colormanagement.h
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index 3158e3419b0..4530f6c9fc0 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -72,6 +72,7 @@ BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]);
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]);
BLI_INLINE void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3]);
BLI_INLINE void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3]);
+const float *IMB_colormangement_get_xyz_to_rgb(void);
/* ** Color space transformation functions ** */
void IMB_colormanagement_transform(float *buffer,
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 83ef910d0bb..ed56268e436 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -90,6 +90,12 @@ struct Stereo3dFormat;
/**
*
+ * \attention defined in GPU_texture.h
+ */
+struct GPUTexture;
+
+/**
+ *
* \attention Defined in allocimbuf.c
*/
void IMB_init(void);
@@ -291,7 +297,7 @@ void IMB_rectblend_threaded(struct ImBuf *dbuf,
*/
typedef enum IMB_Timecode_Type {
- /** Don't use timecode files at all. */
+ /** Don't use time-code files at all. */
IMB_TC_NONE = 0,
/** use images in the order as they are recorded
* (currently, this is the only one implemented
@@ -318,7 +324,7 @@ typedef enum IMB_Proxy_Size {
IMB_PROXY_MAX_SLOT = 4,
} IMB_Proxy_Size;
-/* defaults to BL_proxy within the directory of the animation */
+/* Defaults to BL_proxy within the directory of the animation. */
void IMB_anim_set_index_dir(struct anim *anim, const char *dir);
void IMB_anim_get_fname(struct anim *anim, char *file, int size);
@@ -328,7 +334,7 @@ IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim);
struct IndexBuildContext;
-/* prepare context for proxies/imecodes builder */
+/* Prepare context for proxies/time-codes builder. */
struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
IMB_Timecode_Type tcs_in_use,
IMB_Proxy_Size proxy_sizes_in_use,
@@ -336,13 +342,13 @@ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
const bool overwrite,
struct GSet *file_list);
-/* will rebuild all used indices and proxies at once */
+/* Will rebuild all used indices and proxies at once. */
void IMB_anim_index_rebuild(struct IndexBuildContext *context,
short *stop,
short *do_update,
float *progress);
-/* finish rebuilding proxises/timecodes and free temporary contexts used */
+/* Finish rebuilding proxies/time-codes and free temporary contexts used. */
void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop);
/**
@@ -367,6 +373,7 @@ struct anim *IMB_open_anim(const char *name,
void IMB_suffix_anim(struct anim *anim, const char *suffix);
void IMB_close_anim(struct anim *anim);
void IMB_close_anim_proxies(struct anim *anim);
+bool IMB_anim_can_produce_frames(const struct anim *anim);
/**
*
@@ -390,7 +397,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
/**
*
* \attention Defined in anim_movie.c
- * fetches a define previewframe, usually half way into the movie
+ * fetches a define preview-frame, usually half way into the movie.
*/
struct ImBuf *IMB_anim_previewframe(struct anim *anim);
@@ -411,7 +418,7 @@ void IMB_free_anim(struct anim *anim);
void IMB_filter(struct ImBuf *ibuf);
void IMB_mask_filter_extend(char *mask, int width, int height);
-void IMB_mask_clear(struct ImBuf *ibuf, char *mask, int val);
+void IMB_mask_clear(struct ImBuf *ibuf, const char *mask, int val);
void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter);
void IMB_makemipmap(struct ImBuf *ibuf, int use_filter);
void IMB_remakemipmap(struct ImBuf *ibuf, int use_filter);
@@ -599,7 +606,7 @@ void bilinear_interpolation_color_wrap(
struct ImBuf *in, unsigned char col[4], float col_float[4], float u, float v);
void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]);
-void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol[3]);
+void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3]);
void IMB_sampleImageAtLocation(
struct ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]);
@@ -728,6 +735,25 @@ const char *IMB_ffmpeg_last_error(void);
/**
*
+ * \attention defined in util_gpu.c
+ */
+struct GPUTexture *IMB_create_gpu_texture(struct ImBuf *ibuf,
+ bool use_high_bitdepth,
+ bool use_premult);
+struct GPUTexture *IMB_touch_gpu_texture(
+ struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth);
+void IMB_update_gpu_texture_sub(struct GPUTexture *tex,
+ struct ImBuf *ibuf,
+ int x,
+ int y,
+ int z,
+ int w,
+ int h,
+ bool use_high_bitdepth,
+ bool use_premult);
+
+/**
+ *
* \attention defined in stereoimbuf.c
*/
void IMB_stereo3d_write_dimensions(const char mode,
diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h
index 4a5f8cc1bfe..ddc8394264a 100644
--- a/source/blender/imbuf/IMB_imbuf_types.h
+++ b/source/blender/imbuf/IMB_imbuf_types.h
@@ -209,7 +209,7 @@ typedef struct ImBuf {
*/
float *rect_float;
- /* resolution - pixels per meter */
+ /** Resolution in pixels per meter. Multiply by `0.0254` for DPI. */
double ppm[2];
/* tiled pixel storage */
diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h
index 61bb50aff38..446aaa0655e 100644
--- a/source/blender/imbuf/intern/IMB_indexer.h
+++ b/source/blender/imbuf/intern/IMB_indexer.h
@@ -31,9 +31,9 @@
/*
* separate animation index files to solve the following problems:
*
- * a) different timecodes within one file (like DTS/PTS, Timecode-Track,
- * "implicit" timecodes within DV-files and HDV-files etc.)
- * b) seeking difficulties within ffmpeg for files with timestamp holes
+ * a) different time-codes within one file (like DTS/PTS, Time-code-Track,
+ * "implicit" time-codes within DV-files and HDV-files etc.)
+ * b) seeking difficulties within FFMPEG for files with timestamp holes
* c) broken files that miss several frames / have varying framerates
* d) use proxies accordingly
*
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 11b30a24cde..9fab450cc76 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -304,6 +304,25 @@ struct anim *IMB_open_anim(const char *name,
return (anim);
}
+bool IMB_anim_can_produce_frames(const struct anim *anim)
+{
+#if !(defined(WITH_AVI) || defined(WITH_FFMPEG))
+ UNUSED_VARS(anim);
+#endif
+
+#ifdef WITH_AVI
+ if (anim->avi != NULL) {
+ return true;
+ }
+#endif
+#ifdef WITH_FFMPEG
+ if (anim->pCodecCtx != NULL) {
+ return true;
+ }
+#endif
+ return false;
+}
+
void IMB_suffix_anim(struct anim *anim, const char *suffix)
{
BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix));
@@ -1186,7 +1205,29 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
}
IMB_freeImBuf(anim->last_frame);
- anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, IB_rect);
+
+ /* Certain versions of FFmpeg have a bug in libswscale which ends up in crash
+ * when destination buffer is not properly aligned. For example, this happens
+ * in FFmpeg 4.3.1. It got fixed later on, but for compatibility reasons is
+ * still best to avoid crash.
+ *
+ * This is achieved by using own allocation call rather than relying on
+ * IMB_allocImBuf() to do so since the IMB_allocImBuf() is not guaranteed
+ * to perform aligned allocation.
+ *
+ * In theory this could give better performance, since SIMD operations on
+ * aligned data are usually faster.
+ *
+ * Note that even though sometimes vertical flip is required it does not
+ * affect on alignment of data passed to sws_scale because if the X dimension
+ * is not 32 byte aligned special intermediate buffer is allocated.
+ *
+ * The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker
+ * and is fixed in the newer versions than 4.3.1. */
+ anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0);
+ anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
+ anim->last_frame->mall |= IB_rect;
+
anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
ffmpeg_postprocess(anim);
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 3f5a0f25cc5..c9b3db39976 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -698,7 +698,7 @@ void colormanagement_init(void)
OCIO_configRelease(config);
}
- /* If there're no valid display/views, use fallback mode. */
+ /* If there are no valid display/views, use fallback mode. */
if (global_tot_display == 0 || global_tot_view == 0) {
printf("Color management: no displays/views in the config, using fallback mode instead\n");
@@ -1456,6 +1456,11 @@ bool IMB_colormanagement_space_name_is_data(const char *name)
return (colorspace && colorspace->is_data);
}
+const float *IMB_colormangement_get_xyz_to_rgb()
+{
+ return &imbuf_xyz_to_rgb[0][0];
+}
+
/*********************** Threaded display buffer transform routines *************************/
typedef struct DisplayBufferThread {
@@ -1718,7 +1723,7 @@ static void *do_display_buffer_apply_thread(void *handle_v)
}
static void display_buffer_apply_threaded(ImBuf *ibuf,
- float *buffer,
+ const float *buffer,
unsigned char *byte_buffer,
float *display_buffer,
unsigned char *display_buffer_byte,
diff --git a/source/blender/imbuf/intern/dds/BlockDXT.cpp b/source/blender/imbuf/intern/dds/BlockDXT.cpp
index 4397c1febab..9fd6d71e091 100644
--- a/source/blender/imbuf/intern/dds/BlockDXT.cpp
+++ b/source/blender/imbuf/intern/dds/BlockDXT.cpp
@@ -241,7 +241,7 @@ void BlockDXT1::decodeBlockNV5x(ColorBlock *block) const
}
}
-void BlockDXT1::setIndices(int *idx)
+void BlockDXT1::setIndices(const int *idx)
{
indices = 0;
for (uint i = 0; i < 16; i++) {
@@ -580,7 +580,7 @@ void BlockCTX1::decodeBlock(ColorBlock *block) const
}
}
-void BlockCTX1::setIndices(int *idx)
+void BlockCTX1::setIndices(const int *idx)
{
indices = 0;
for (uint i = 0; i < 16; i++) {
diff --git a/source/blender/imbuf/intern/dds/BlockDXT.h b/source/blender/imbuf/intern/dds/BlockDXT.h
index 16937bce042..57430dbaea2 100644
--- a/source/blender/imbuf/intern/dds/BlockDXT.h
+++ b/source/blender/imbuf/intern/dds/BlockDXT.h
@@ -76,7 +76,7 @@ struct BlockDXT1 {
void decodeBlock(ColorBlock *block) const;
void decodeBlockNV5x(ColorBlock *block) const;
- void setIndices(int *idx);
+ void setIndices(const int *idx);
void flip4();
void flip2();
@@ -289,7 +289,7 @@ struct BlockCTX1 {
};
void evaluatePalette(Color32 color_array[4]) const;
- void setIndices(int *idx);
+ void setIndices(const int *idx);
void decodeBlock(ColorBlock *block) const;
diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
index ab68d933c6f..9730153819e 100644
--- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
+++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
@@ -501,7 +501,7 @@ struct FormatDescriptor {
uint amask;
};
-static const FormatDescriptor s_d3dFormats[] = {
+const FormatDescriptor s_d3dFormats[] = {
{D3DFMT_R8G8B8, 24, 0xFF0000, 0xFF00, 0xFF, 0},
{D3DFMT_A8R8G8B8, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000}, /* DXGI_FORMAT_B8G8R8A8_UNORM */
{D3DFMT_X8R8G8B8, 32, 0xFF0000, 0xFF00, 0xFF, 0}, /* DXGI_FORMAT_B8G8R8X8_UNORM */
@@ -524,7 +524,7 @@ static const FormatDescriptor s_d3dFormats[] = {
{D3DFMT_L16, 16, 16, 0, 0, 0}, /* DXGI_FORMAT_R16_UNORM */
};
-static const uint s_d3dFormatCount = sizeof(s_d3dFormats) / sizeof(s_d3dFormats[0]);
+const uint s_d3dFormatCount = sizeof(s_d3dFormats) / sizeof(s_d3dFormats[0]);
} // namespace
diff --git a/source/blender/imbuf/intern/dds/Image.cpp b/source/blender/imbuf/intern/dds/Image.cpp
index d08a61a5a60..31a9927557b 100644
--- a/source/blender/imbuf/intern/dds/Image.cpp
+++ b/source/blender/imbuf/intern/dds/Image.cpp
@@ -51,9 +51,7 @@ void Image::allocate(uint w, uint h)
void Image::free()
{
- if (m_data) {
- delete[] m_data;
- }
+ delete[] m_data;
m_data = NULL;
}
diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c
index e36088f8eac..d8a5096af71 100644
--- a/source/blender/imbuf/intern/filter.c
+++ b/source/blender/imbuf/intern/filter.c
@@ -363,7 +363,7 @@ void IMB_mask_filter_extend(char *mask, int width, int height)
MEM_freeN(temprect);
}
-void IMB_mask_clear(ImBuf *ibuf, char *mask, int val)
+void IMB_mask_clear(ImBuf *ibuf, const char *mask, int val)
{
int x, y;
if (ibuf->rect_float) {
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index 7ebbd1a7409..bf58f047773 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -456,7 +456,7 @@ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[
}
}
-void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol[3])
+void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3])
{
size_t a = ((size_t)x) * y;
unsigned char *cp = rect;
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index 985a8e977ca..7cc31b99077 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -894,7 +894,7 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
}
static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
- short *stop,
+ const short *stop,
short *do_update,
float *progress)
{
@@ -1090,7 +1090,7 @@ static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context,
}
static void index_rebuild_fallback(FallbackIndexBuilderContext *context,
- short *stop,
+ const short *stop,
short *do_update,
float *progress)
{
diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c
index bfcd1ec2cee..2516df22151 100644
--- a/source/blender/imbuf/intern/iris.c
+++ b/source/blender/imbuf/intern/iris.c
@@ -122,7 +122,7 @@ static int expandrow2(
static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n);
static void interleaverow2(float *lptr, const uchar *cptr, int z, int n);
static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int cnt);
-static void lumrow(uchar *rgbptr, uchar *lumptr, int n);
+static void lumrow(const uchar *rgbptr, uchar *lumptr, int n);
/*
* byte order independent read/write of shorts and ints.
@@ -900,7 +900,7 @@ static int output_iris(uint *lptr, int xsize, int ysize, int zsize, const char *
/* static utility functions for output_iris */
-static void lumrow(uchar *rgbptr, uchar *lumptr, int n)
+static void lumrow(const uchar *rgbptr, uchar *lumptr, int n)
{
lumptr += CHANOFFSET(0);
while (n--) {
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 882808cbc14..9b614eab0dc 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -390,7 +390,8 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
}
if (ibuf->ppm[0] > 0.0) {
- addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */
+ /* Convert meters to inches. */
+ addXDensity(*header, ibuf->ppm[0] * 0.0254);
}
}
@@ -815,7 +816,7 @@ void IMB_exr_add_channel(void *handle,
if (layname && layname[0] != '\0') {
imb_exr_insert_view_name(echan->name, echan->m->name.c_str(), echan->m->view.c_str());
}
- else if (data->multiView->size() >= 1) {
+ else if (!data->multiView->empty()) {
std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id);
BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name));
}
@@ -1065,7 +1066,7 @@ float *IMB_exr_channel_rect(void *handle,
imb_exr_insert_view_name(temp_buf, name, viewname);
BLI_strncpy(name, temp_buf, sizeof(name));
}
- else if (data->multiView->size() >= 1) {
+ else if (!data->multiView->empty()) {
const int view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname));
std::string raw_name = insertViewName(name, *data->multiView, view_id);
BLI_strncpy(name, raw_name.c_str(), sizeof(name));
@@ -1208,7 +1209,7 @@ void IMB_exr_read_channels(void *handle)
ExrHandle *data = (ExrHandle *)handle;
int numparts = data->ifile->parts();
- /* check if exr was saved with previous versions of blender which flipped images */
+ /* Check if EXR was saved with previous versions of blender which flipped images. */
const StringAttribute *ta = data->ifile->header(0).findTypedAttribute<StringAttribute>(
"BlenderMultiChannel");
@@ -1299,7 +1300,7 @@ void IMB_exr_multilayer_convert(void *handle,
ExrPass *pass;
/* RenderResult needs at least one RenderView */
- if (data->multiView->size() == 0) {
+ if (data->multiView->empty()) {
addview(base, "");
}
else {
@@ -1722,11 +1723,25 @@ static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *ch
return chan;
}
-static bool exr_has_rgb(MultiPartInputFile &file)
+static int exr_has_rgb(MultiPartInputFile &file, const char *rgb_channels[3])
{
- return file.header(0).channels().findChannel("R") != NULL &&
- file.header(0).channels().findChannel("G") != NULL &&
- file.header(0).channels().findChannel("B") != NULL;
+ /* Common names for RGB-like channels in order. */
+ static const char *channel_names[] = {
+ "R", "Red", "G", "Green", "B", "Blue", "AR", "RA", "AG", "GA", "AB", "BA", NULL};
+
+ const Header &header = file.header(0);
+ int num_channels = 0;
+
+ for (int i = 0; channel_names[i]; i++) {
+ if (header.channels().findChannel(channel_names[i])) {
+ rgb_channels[num_channels++] = channel_names[i];
+ if (num_channels == 3) {
+ break;
+ }
+ }
+ }
+
+ return num_channels;
}
static bool exr_has_luma(MultiPartInputFile &file)
@@ -1734,23 +1749,27 @@ static bool exr_has_luma(MultiPartInputFile &file)
/* Y channel is the luma and should always present fir luma space images,
* optionally it could be also channels for chromas called BY and RY.
*/
- return file.header(0).channels().findChannel("Y") != NULL;
+ const Header &header = file.header(0);
+ return header.channels().findChannel("Y") != NULL;
}
static bool exr_has_chroma(MultiPartInputFile &file)
{
- return file.header(0).channels().findChannel("BY") != NULL &&
- file.header(0).channels().findChannel("RY") != NULL;
+ const Header &header = file.header(0);
+ return header.channels().findChannel("BY") != NULL &&
+ header.channels().findChannel("RY") != NULL;
}
static bool exr_has_zbuffer(MultiPartInputFile &file)
{
- return !(file.header(0).channels().findChannel("Z") == NULL);
+ const Header &header = file.header(0);
+ return !(header.channels().findChannel("Z") == NULL);
}
static bool exr_has_alpha(MultiPartInputFile &file)
{
- return !(file.header(0).channels().findChannel("A") == NULL);
+ const Header &header = file.header(0);
+ return !(header.channels().findChannel("A") == NULL);
}
static bool exr_is_half_float(MultiPartInputFile &file)
@@ -1774,7 +1793,7 @@ static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
* channels without a layer name will be single layer. */
channels.layers(layerNames);
- return (layerNames.size() > 0);
+ return (!layerNames.empty());
}
static void imb_exr_type_by_channels(ChannelList &channels,
@@ -1791,7 +1810,7 @@ static void imb_exr_type_by_channels(ChannelList &channels,
/* will not include empty layer names */
channels.layers(layerNames);
- if (views.size() && views[0] != "") {
+ if (!views.empty() && !views[0].empty()) {
*r_multiview = true;
}
else {
@@ -1801,13 +1820,13 @@ static void imb_exr_type_by_channels(ChannelList &channels,
return;
}
- if (layerNames.size()) {
- /* if layerNames is not empty, it means at least one layer is non-empty,
+ if (!layerNames.empty()) {
+ /* If `layerNames` is not empty, it means at least one layer is non-empty,
* but it also could be layers without names in the file and such case
- * shall be considered a multilayer exr
+ * shall be considered a multi-layer EXR.
*
- * that's what we do here: test whether there're empty layer names together
- * with non-empty ones in the file
+ * That's what we do here: test whether there are empty layer names together
+ * with non-empty ones in the file.
*/
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) {
for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) {
@@ -1920,7 +1939,8 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
ibuf->flags |= exr_is_half_float(*file) ? IB_halffloat : 0;
if (hasXDensity(file->header(0))) {
- ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f;
+ /* Convert inches to meters. */
+ ibuf->ppm[0] = (double)xDensity(file->header(0)) / 0.0254;
ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio();
}
@@ -1955,7 +1975,8 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
}
}
else {
- const bool has_rgb = exr_has_rgb(*file);
+ const char *rgb_channels[3];
+ const int num_rgb_channels = exr_has_rgb(*file, rgb_channels);
const bool has_luma = exr_has_luma(*file);
FrameBuffer frameBuffer;
float *first;
@@ -1970,13 +1991,11 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
/* but, since we read y-flipped (negative y stride) we move to last scanline */
first += 4 * (height - 1) * width;
- if (has_rgb) {
- frameBuffer.insert(exr_rgba_channelname(*file, "R"),
- Slice(Imf::FLOAT, (char *)first, xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(*file, "G"),
- Slice(Imf::FLOAT, (char *)(first + 1), xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(*file, "B"),
- Slice(Imf::FLOAT, (char *)(first + 2), xstride, ystride));
+ if (num_rgb_channels > 0) {
+ for (int i = 0; i < num_rgb_channels; i++) {
+ frameBuffer.insert(exr_rgba_channelname(*file, rgb_channels[i]),
+ Slice(Imf::FLOAT, (char *)(first + i), xstride, ystride));
+ }
}
else if (has_luma) {
frameBuffer.insert(exr_rgba_channelname(*file, "Y"),
@@ -2019,24 +2038,27 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
// IMB_rect_from_float(ibuf);
// }
- if (!has_rgb && has_luma) {
- size_t a;
- if (exr_has_chroma(*file)) {
- for (a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
- float *color = ibuf->rect_float + a * 4;
- ycc_to_rgb(color[0] * 255.0f,
- color[1] * 255.0f,
- color[2] * 255.0f,
- &color[0],
- &color[1],
- &color[2],
- BLI_YCC_ITU_BT709);
- }
+ if (num_rgb_channels == 0 && has_luma && exr_has_chroma(*file)) {
+ for (size_t a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
+ float *color = ibuf->rect_float + a * 4;
+ ycc_to_rgb(color[0] * 255.0f,
+ color[1] * 255.0f,
+ color[2] * 255.0f,
+ &color[0],
+ &color[1],
+ &color[2],
+ BLI_YCC_ITU_BT709);
}
- else {
- for (a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
- float *color = ibuf->rect_float + a * 4;
- color[1] = color[2] = color[0];
+ }
+ else if (num_rgb_channels <= 1) {
+ /* Convert 1 to 3 channels. */
+ for (size_t a = 0; a < (size_t)ibuf->x * ibuf->y; a++) {
+ float *color = ibuf->rect_float + a * 4;
+ if (num_rgb_channels <= 1) {
+ color[1] = color[0];
+ }
+ if (num_rgb_channels <= 2) {
+ color[2] = color[0];
}
}
}
diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c
index 46d07e74ce3..6ed01c73f04 100644
--- a/source/blender/imbuf/intern/radiance_hdr.c
+++ b/source/blender/imbuf/intern/radiance_hdr.c
@@ -176,7 +176,7 @@ static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol)
}
/* float color -> rgbe */
-static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
+static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
{
int e;
float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN];
@@ -308,7 +308,8 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
}
/* ImBuf write */
-static int fwritecolrs(FILE *file, int width, int channels, unsigned char *ibufscan, float *fpscan)
+static int fwritecolrs(
+ FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan)
{
int beg, c2, cnt = 0;
fCOLOR fcol;
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 309de25db03..715f2aaf621 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -81,14 +81,24 @@ typedef struct ImbTIFFMemFile {
* Function implementations. *
*****************************/
-static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size)
+static void imb_tiff_DummyUnmapProc(
+ thandle_t fd,
+ tdata_t base,
+ /* Cannot be const, because this function implements #TIFFUnmapFileProc.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ toff_t size)
{
(void)fd;
(void)base;
(void)size;
}
-static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize)
+static int imb_tiff_DummyMapProc(
+ thandle_t fd,
+ tdata_t *pbase,
+ /* Cannot be const, because this function implements #TIFFMapFileProc.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ toff_t *psize)
{
(void)fd;
(void)pbase;
@@ -100,7 +110,7 @@ static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize)
/**
* Reads data from an in-memory TIFF file.
*
- * \param handle: Handle of the TIFF file (pointer to ImbTIFFMemFile).
+ * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
* \param data: Buffer to contain data (treat as (void *)).
* \param n: Number of bytes to read.
*
diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c
new file mode 100644
index 00000000000..5d0c6fe2e52
--- /dev/null
+++ b/source/blender/imbuf/intern/util_gpu.c
@@ -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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ * util.c
+ */
+
+/** \file
+ * \ingroup imbuf
+ */
+
+#include "imbuf.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "MEM_guardedalloc.h"
+
+#include "BKE_global.h"
+
+#include "GPU_extensions.h"
+#include "GPU_texture.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+/* gpu ibuf utils */
+
+static void imb_gpu_get_format(const ImBuf *ibuf,
+ bool high_bitdepth,
+ eGPUDataFormat *r_data_format,
+ eGPUTextureFormat *r_texture_format)
+{
+ const bool float_rect = (ibuf->rect_float != NULL);
+ const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) &&
+ !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
+ high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
+
+ *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE;
+
+ if (float_rect) {
+ *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F;
+ }
+ else {
+ *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8;
+ }
+}
+
+/* Return false if no suitable format was found. */
+#ifdef WITH_DDS
+static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
+{
+ /* For DDS we only support data, scene linear and sRGB. Converting to
+ * different colorspace would break the compression. */
+ const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) &&
+ !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
+
+ if (ibuf->dds_data.fourcc == FOURCC_DXT1) {
+ *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1;
+ }
+ else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
+ *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3;
+ }
+ else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
+ *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5;
+ }
+ else {
+ return false;
+ }
+ return true;
+}
+#endif
+
+/**
+ * Apply colormanagement and scale buffer if needed.
+ * *r_freedata is set to true if the returned buffer need to be manually freed.
+ **/
+static void *imb_gpu_get_data(const ImBuf *ibuf,
+ const bool do_rescale,
+ const int rescale_size[2],
+ const bool compress_as_srgb,
+ const bool store_premultiplied,
+ bool *r_freedata)
+{
+ const bool is_float_rect = (ibuf->rect_float != NULL);
+ void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect;
+
+ if (is_float_rect) {
+ /* Float image is already in scene linear colorspace or non-color data by
+ * convention, no colorspace conversion needed. But we do require 4 channels
+ * currently. */
+ if (ibuf->channels != 4 || !store_premultiplied) {
+ data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
+ *r_freedata = true;
+
+ if (data_rect == NULL) {
+ return NULL;
+ }
+
+ IMB_colormanagement_imbuf_to_float_texture(
+ (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
+ }
+ }
+ else {
+ /* Byte image is in original colorspace from the file. If the file is sRGB
+ * scene linear, or non-color data no conversion is needed. Otherwise we
+ * compress as scene linear + sRGB transfer function to avoid precision loss
+ * in common cases.
+ *
+ * We must also convert to premultiplied for correct texture interpolation
+ * and consistency with float images. */
+ if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
+ data_rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__);
+ *r_freedata = true;
+
+ if (data_rect == NULL) {
+ return NULL;
+ }
+
+ /* Texture storage of images is defined by the alpha mode of the image. The
+ * downside of this is that there can be artifacts near alpha edges. However,
+ * this allows us to use sRGB texture formats and preserves color values in
+ * zero alpha areas, and appears generally closer to what game engines that we
+ * want to be compatible with do. */
+ IMB_colormanagement_imbuf_to_byte_texture(
+ (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied);
+ }
+ }
+
+ if (do_rescale) {
+ uint *rect = (is_float_rect) ? NULL : (uint *)data_rect;
+ float *rect_float = (is_float_rect) ? (float *)data_rect : NULL;
+
+ ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4);
+ IMB_scaleImBuf(scale_ibuf, UNPACK2(rescale_size));
+
+ data_rect = (is_float_rect) ? (void *)scale_ibuf->rect_float : (void *)scale_ibuf->rect;
+ *r_freedata = true;
+ /* Steal the rescaled buffer to avoid double free. */
+ scale_ibuf->rect_float = NULL;
+ scale_ibuf->rect = NULL;
+ IMB_freeImBuf(scale_ibuf);
+ }
+ return data_rect;
+}
+
+/* The ibuf is only here to detect the storage type. The produced texture will have undefined
+ * content. It will need to be populated by using IMB_update_gpu_texture_sub(). */
+GPUTexture *IMB_touch_gpu_texture(ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth)
+{
+ eGPUDataFormat data_format;
+ eGPUTextureFormat tex_format;
+ imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
+
+ GPUTexture *tex = GPU_texture_create_nD(
+ w, h, layers, 2, NULL, tex_format, data_format, 0, false, NULL);
+
+ GPU_texture_anisotropic_filter(tex, true);
+ return tex;
+}
+
+/* Will update a GPUTexture using the content of the ImBuf. Only one layer will be updated.
+ * Will resize the ibuf if needed.
+ * z is the layer to update. Unused if the texture is 2D. */
+void IMB_update_gpu_texture_sub(GPUTexture *tex,
+ ImBuf *ibuf,
+ int x,
+ int y,
+ int z,
+ int w,
+ int h,
+ bool use_high_bitdepth,
+ bool use_premult)
+{
+ const bool do_rescale = (ibuf->x != w || ibuf->y != h);
+ int size[2] = {w, h};
+
+ eGPUDataFormat data_format;
+ eGPUTextureFormat tex_format;
+ imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
+
+ const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8);
+ bool freebuf = false;
+
+ void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf);
+
+ /* Update Texture. */
+ GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1);
+
+ if (freebuf) {
+ MEM_freeN(data);
+ }
+}
+
+GPUTexture *IMB_create_gpu_texture(ImBuf *ibuf, bool use_high_bitdepth, bool use_premult)
+{
+ GPUTexture *tex = NULL;
+ int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)};
+ bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]);
+
+#ifdef WITH_DDS
+ if (ibuf->ftype == IMB_FTYPE_DDS) {
+ eGPUTextureFormat compressed_format;
+ if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) {
+ fprintf(stderr, "Unable to find a suitable DXT compression,");
+ }
+ else if (do_rescale) {
+ fprintf(stderr, "Unable to load DXT image resolution,");
+ }
+ else if (!is_power_of_2_i(ibuf->x) || !is_power_of_2_i(ibuf->y)) {
+ fprintf(stderr, "Unable to load non-power-of-two DXT image resolution,");
+ }
+ else {
+ tex = GPU_texture_create_compressed(
+ ibuf->x, ibuf->y, ibuf->dds_data.nummipmaps, compressed_format, ibuf->dds_data.data);
+
+ if (tex != NULL) {
+ return tex;
+ }
+ else {
+ fprintf(stderr, "ST3C support not found,");
+ }
+ }
+ /* Fallback to uncompressed texture. */
+ fprintf(stderr, " falling back to uncompressed.\n");
+ }
+#endif
+
+ eGPUDataFormat data_format;
+ eGPUTextureFormat tex_format;
+ imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
+
+ const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8);
+ bool freebuf = false;
+
+ void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf);
+
+ /* Create Texture. */
+ tex = GPU_texture_create_nD(UNPACK2(size), 0, 2, data, tex_format, data_format, 0, false, NULL);
+
+ GPU_texture_anisotropic_filter(tex, true);
+
+ if (freebuf) {
+ MEM_freeN(data);
+ }
+
+ return tex;
+} \ No newline at end of file
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 ccc53c589fb..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
@@ -35,11 +33,6 @@ struct bContext;
typedef struct AbcArchiveHandle AbcArchiveHandle;
-enum {
- ABC_ARCHIVE_OGAWA = 0,
- ABC_ARCHIVE_HDF5 = 1,
-};
-
int ABC_get_version(void);
struct AlembicExportParams {
@@ -68,8 +61,6 @@ struct AlembicExportParams {
bool export_hair;
bool export_particles;
- unsigned int compression_type : 1;
-
/* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx
* in DNA_modifier_types.h */
int quad_method;
@@ -140,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 6de7d327d4e..681d6d04acd 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
)
@@ -36,14 +38,12 @@ set(INC
set(INC_SYS
${ALEMBIC_INCLUDE_DIRS}
${BOOST_INCLUDE_DIR}
- ${HDF5_INCLUDE_DIRS}
${OPENEXR_INCLUDE_DIRS}
)
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
@@ -53,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_mball.cc
+ exporter/abc_writer_mesh.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
@@ -78,33 +81,30 @@ 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_mball.h
+ exporter/abc_writer_mesh.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}
)
-if(WITH_ALEMBIC_HDF5)
- add_definitions(-DWITH_ALEMBIC_HDF5)
- list(APPEND LIB
- ${HDF5_LIBRARIES}
- )
-endif()
-
list(APPEND LIB
${BOOST_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..98c551c635c
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -0,0 +1,225 @@
+/*
+ * 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,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ 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..5b1b1b60b48
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -0,0 +1,264 @@
+/*
+ * 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::release_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 ObjectIdentifier::for_graph_root();
+ }
+
+ return AbstractHierarchyIterator::determine_graph_index_object(context);
+}
+
+AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::determine_graph_index_dupli(
+ const HierarchyContext *context,
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder)
+{
+ if (params_.flatten_hierarchy) {
+ return ObjectIdentifier::for_graph_root();
+ }
+
+ return AbstractHierarchyIterator::determine_graph_index_dupli(
+ context, dupli_object, dupli_parent_finder);
+}
+
+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..bd7e3f27c67
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
@@ -0,0 +1,92 @@
+/*
+ * 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 DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder) 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 release_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..6f185020b58 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;
@@ -67,9 +80,9 @@ void AbcCurveWriter::do_write()
std::vector<uint8_t> orders;
Imath::V3f temp_vert;
- Alembic::AbcGeom::BasisType curve_basis;
- Alembic::AbcGeom::CurveType curve_type;
- Alembic::AbcGeom::CurvePeriodicity periodicity;
+ Alembic::AbcGeom::BasisType curve_basis = Alembic::AbcGeom::kNoBasis;
+ Alembic::AbcGeom::CurveType curve_type = Alembic::AbcGeom::kVariableOrder;
+ Alembic::AbcGeom::CurvePeriodicity periodicity = Alembic::AbcGeom::kNonPeriodic;
Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
for (; nurbs; nurbs = nurbs->next) {
@@ -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..f9b4619b263 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;
}
@@ -265,10 +282,10 @@ void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1]));
}
else {
- if (uv_values.size()) {
+ if (!uv_values.empty()) {
uv_values.push_back(uv_values[pc->parent]);
}
- if (norm_values.size()) {
+ if (!norm_values.empty()) {
norm_values.push_back(norm_values[pc->parent]);
}
}
@@ -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..89cb76db9a6 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;
@@ -556,10 +443,10 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
geo_groups[name].push_back(i);
}
- if (geo_groups.size() == 0) {
- Material *mat = BKE_object_material_get(m_object, 1);
+ if (geo_groups.empty()) {
+ 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..94499935181
--- /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 override;
+
+ 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..14cb7773e67 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, 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 82b0e98b376..03800b80acf 100644
--- a/source/blender/io/alembic/intern/abc_writer_archive.h
+++ b/source/blender/io/alembic/exporter/abc_writer_points.h
@@ -16,43 +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/AbcGeom/OPoints.h>
-#ifdef WITH_ALEMBIC_HDF5
-# include <Alembic/AbcCoreHDF5/All.h>
-#endif
+namespace blender {
+namespace io {
+namespace alembic {
-#include <Alembic/AbcCoreOgawa/All.h>
+class ABCPointsWriter : public ABCAbstractWriter {
+ Alembic::AbcGeom::OPoints abc_points_;
+ Alembic::AbcGeom::OPointsSchema abc_points_schema_;
-#include <fstream>
-
-struct Main;
-struct Scene;
-
-/* 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,
- bool do_ogawa);
+ 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..39af99c142c
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.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.
+ */
+
+/** \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;
+ }
+ if (check_has_physics(context)) {
+ 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 dbf24452b78..00000000000
--- a/source/blender/io/alembic/intern/abc_exporter.cc
+++ /dev/null
@@ -1,675 +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),
- export_ogawa(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, m_settings.export_ogawa);
-
- /* 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 398004d2ec5..00000000000
--- a/source/blender/io/alembic/intern/abc_exporter.h
+++ /dev/null
@@ -1,128 +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 export_ogawa;
- 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 563466d81bc..d7f1095f0fd 100644
--- a/source/blender/io/alembic/intern/abc_reader_archive.cc
+++ b/source/blender/io/alembic/intern/abc_reader_archive.cc
@@ -39,12 +39,13 @@ 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,
- bool &is_hdf5)
+ const std::vector<std::istream *> &input_streams)
{
- is_hdf5 = false;
-
try {
Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams);
@@ -53,22 +54,7 @@ static IArchive open_archive(const std::string &filename,
catch (const Exception &e) {
std::cerr << e.what() << '\n';
-#ifdef WITH_ALEMBIC_HDF5
- try {
- is_hdf5 = true;
- Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
-
- return IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
- filename.c_str(),
- ErrorHandler::kThrowPolicy,
- cache_ptr);
- }
- catch (const Exception &) {
- std::cerr << e.what() << '\n';
- return IArchive();
- }
-#else
- /* Inspect the file to see whether it's really a HDF5 file. */
+ /* Inspect the file to see whether it's actually a HDF5 file. */
char header[4]; /* char(0x89) + "HDF" */
std::ifstream the_file(filename.c_str(), std::ios::in | std::ios::binary);
if (!the_file) {
@@ -81,16 +67,12 @@ static IArchive open_archive(const std::string &filename,
std::cerr << filename << " has an unknown file format, unable to read." << std::endl;
}
else {
- is_hdf5 = true;
std::cerr << filename << " is in the obsolete HDF5 format, unable to read." << std::endl;
}
if (the_file.is_open()) {
the_file.close();
}
-
- return IArchive();
-#endif
}
return IArchive();
@@ -113,18 +95,7 @@ ArchiveReader::ArchiveReader(struct Main *bmain, const char *filename)
m_streams.push_back(&m_infile);
- m_archive = open_archive(abs_filename, m_streams, m_is_hdf5);
-
- /* We can't open an HDF5 file from a stream, so close it. */
- if (m_is_hdf5) {
- m_infile.close();
- m_streams.clear();
- }
-}
-
-bool ArchiveReader::is_hdf5() const
-{
- return m_is_hdf5;
+ m_archive = open_archive(abs_filename, m_streams);
}
bool ArchiveReader::valid() const
@@ -136,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 35273e10108..aea62b46cce 100644
--- a/source/blender/io/alembic/intern/abc_reader_archive.h
+++ b/source/blender/io/alembic/intern/abc_reader_archive.h
@@ -16,20 +16,13 @@
* 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>
-
-#ifdef WITH_ALEMBIC_HDF5
-# include <Alembic/AbcCoreHDF5/All.h>
-#endif
-
#include <Alembic/AbcCoreOgawa/All.h>
#include <fstream>
@@ -37,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.
@@ -46,22 +43,15 @@ class ArchiveReader {
Alembic::Abc::IArchive m_archive;
std::ifstream m_infile;
std::vector<std::istream *> m_streams;
- bool m_is_hdf5;
public:
ArchiveReader(struct Main *bmain, const char *filename);
bool valid() const;
- /**
- * Returns true when either Blender is compiled with HDF5 support and
- * the archive was successfully opened (valid() will also return true),
- * or when Blender was built without HDF5 support but a HDF5 file was
- * detected (valid() will return false).
- */
- bool is_hdf5() const;
-
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..98130eb28cd 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 */
@@ -99,7 +104,7 @@ static void assign_materials(Main *bmain,
for (; it != mat_index_map.end(); ++it) {
std::string mat_name = it->first;
- mat_iter = mat_map.find(mat_name.c_str());
+ mat_iter = mat_map.find(mat_name);
Material *assigned_mat;
@@ -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..06b0c07f4c5 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(""),
@@ -232,7 +236,7 @@ Alembic::AbcGeom::IXform AbcObjectReader::xform()
* parent Alembic object should contain the transform. */
IObject abc_parent = m_iobject.getParent();
- /* The archive's top object can be recognised by not having a parent. */
+ /* The archive's top object can be recognized by not having a parent. */
if (abc_parent.getParent() && IXform::matches(abc_parent.getMetaData())) {
try {
return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting);
@@ -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 e7dee536cb9..00000000000
--- a/source/blender/io/alembic/intern/abc_writer_archive.cc
+++ /dev/null
@@ -1,113 +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 &filename,
- const std::string &scene_name,
- double scene_fps,
- bool ogawa)
-{
- 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;
-
-#ifdef WITH_ALEMBIC_HDF5
- if (!ogawa) {
- return OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, abc_metadata, policy);
- }
-#else
- static_cast<void>(filename);
- static_cast<void>(ogawa);
-#endif
-
- 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,
- bool do_ogawa)
-{
- /* Use stream to support unicode character paths on Windows. */
- if (do_ogawa) {
-#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, filename, abc_scene_name, FPS, do_ogawa);
-}
-
-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 d45af2eed4c..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) {
- end_latt_deform(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 6ca9e82a26c..7cde2d4fe73 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,196 +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.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA);
- 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 ********************** */
/**
@@ -582,7 +373,7 @@ static std::pair<bool, AbcObjectReader *> visit_object(
}
}
else if (object.getParent()) {
- if (claiming_child_readers.size() > 0) {
+ if (!claiming_child_readers.empty()) {
/* The first claiming child will serve just fine as parent to
* our non-claiming children. Since all claiming children share
* the same XForm, it doesn't really matter which one we pick. */
@@ -620,10 +411,10 @@ static std::pair<bool, AbcObjectReader *> visit_object(
enum {
ABC_NO_ERROR = 0,
ABC_ARCHIVE_FAIL,
- ABC_UNSUPPORTED_HDF5,
};
struct ImportJobData {
+ bContext *C;
Main *bmain;
Scene *scene;
ViewLayer *view_layer;
@@ -642,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)
@@ -659,11 +451,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
ArchiveReader *archive = new ArchiveReader(data->bmain, data->filename);
if (!archive->valid()) {
-#ifndef WITH_ALEMBIC_HDF5
- data->error_code = archive->is_hdf5() ? ABC_UNSUPPORTED_HDF5 : ABC_ARCHIVE_FAIL;
-#else
data->error_code = ABC_ARCHIVE_FAIL;
-#endif
delete archive;
return;
}
@@ -691,7 +479,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent);
/* There shouldn't be any orphans. */
- BLI_assert(assign_as_parent.size() == 0);
+ BLI_assert(assign_as_parent.empty());
if (G.is_break) {
data->was_cancelled = true;
@@ -829,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) {
@@ -850,9 +644,6 @@ static void import_endjob(void *user_data)
case ABC_ARCHIVE_FAIL:
WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
break;
- case ABC_UNSUPPORTED_HDF5:
- WM_report(RPT_ERROR, "Alembic archive in obsolete HDF5 format is not supported.");
- break;
}
WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
@@ -877,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);
@@ -893,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/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c
index 70ddca28060..75059c202e5 100644
--- a/source/blender/io/avi/intern/avi_mjpeg.c
+++ b/source/blender/io/avi/intern/avi_mjpeg.c
@@ -20,7 +20,7 @@
/** \file
* \ingroup avi
*
- * This is external code. Converts between avi and mpeg/jpeg.
+ * This is external code. Converts between AVI and MPEG/JPEG.
*/
#include <stdlib.h>
@@ -39,7 +39,9 @@
#include "avi_mjpeg.h"
static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize);
-static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize);
+static void jpegmemsrcmgr_build(j_decompress_ptr dinfo,
+ const unsigned char *buffer,
+ size_t bufsize);
static size_t numbytes;
@@ -381,7 +383,10 @@ static void deinterlace(int odd, unsigned char *to, unsigned char *from, int wid
}
}
-void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size)
+void *avi_converter_from_mjpeg(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size)
{
int deint;
unsigned char *buf;
@@ -553,7 +558,9 @@ static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo)
MEM_freeN(dinfo->src);
}
-static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize)
+static void jpegmemsrcmgr_build(j_decompress_ptr dinfo,
+ const unsigned char *buffer,
+ size_t bufsize)
{
dinfo->src = MEM_mallocN(sizeof(*(dinfo->src)), "avi.jpegmemsrcmgr_build");
diff --git a/source/blender/io/avi/intern/avi_mjpeg.h b/source/blender/io/avi/intern/avi_mjpeg.h
index 30e46bf1d0c..13153fa41f0 100644
--- a/source/blender/io/avi/intern/avi_mjpeg.h
+++ b/source/blender/io/avi/intern/avi_mjpeg.h
@@ -24,7 +24,10 @@
#ifndef __AVI_MJPEG_H__
#define __AVI_MJPEG_H__
-void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
+void *avi_converter_from_mjpeg(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size);
void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
#endif /* __AVI_MJPEG_H__ */
diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c
index 6f4f33d72d1..44542af96ae 100644
--- a/source/blender/io/avi/intern/avi_rgb.c
+++ b/source/blender/io/avi/intern/avi_rgb.c
@@ -37,7 +37,10 @@
/* implementation */
-void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size)
+void *avi_converter_from_avi_rgb(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size)
{
unsigned char *buf;
AviBitmapInfoHeader *bi;
diff --git a/source/blender/io/avi/intern/avi_rgb.h b/source/blender/io/avi/intern/avi_rgb.h
index 7c8ce590d27..3a37fad94e1 100644
--- a/source/blender/io/avi/intern/avi_rgb.h
+++ b/source/blender/io/avi/intern/avi_rgb.h
@@ -24,7 +24,10 @@
#ifndef __AVI_RGB_H__
#define __AVI_RGB_H__
-void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
+void *avi_converter_from_avi_rgb(AviMovie *movie,
+ int stream,
+ unsigned char *buffer,
+ const size_t *size);
void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size);
#endif /* __AVI_RGB_H__ */
diff --git a/source/blender/io/collada/AnimationExporter.cpp b/source/blender/io/collada/AnimationExporter.cpp
index 9f0c1924bb9..c25b4ea543b 100644
--- a/source/blender/io/collada/AnimationExporter.cpp
+++ b/source/blender/io/collada/AnimationExporter.cpp
@@ -222,7 +222,7 @@ void AnimationExporter::export_matrix_animation(Object *ob, BCAnimationSampler &
std::vector<float> frames;
sampler.get_object_frames(frames, ob);
- if (frames.size() > 0) {
+ if (!frames.empty()) {
BCMatrixSampleMap samples;
bool is_animated = sampler.get_object_samples(samples, ob);
if (keep_flat_curves || is_animated) {
@@ -264,7 +264,7 @@ void AnimationExporter::export_bone_animations_recursive(Object *ob,
std::vector<float> frames;
sampler.get_bone_frames(frames, ob, bone);
- if (frames.size()) {
+ if (!frames.empty()) {
BCMatrixSampleMap samples;
bool is_animated = sampler.get_bone_samples(samples, ob, bone);
if (keep_flat_curves || is_animated) {
@@ -542,7 +542,7 @@ void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNa
param.push_back("ANGLE");
}
else {
- if (axis != "") {
+ if (!axis.empty()) {
param.push_back(axis);
}
else if (transform) {
@@ -836,11 +836,11 @@ std::string AnimationExporter::get_collada_sid(const BCAnimationCurve &curve,
bool is_angle = curve.is_rotation_curve();
- if (tm_name.size()) {
+ if (!tm_name.empty()) {
if (is_angle) {
return tm_name + std::string(axis_name) + ".ANGLE";
}
- else if (axis_name != "") {
+ else if (!axis_name.empty()) {
return tm_name + "." + std::string(axis_name);
}
else {
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index edac84e2aaa..b53aa95a11f 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -52,7 +52,7 @@
template<class T> static const char *bc_get_joint_name(T *node)
{
const std::string &id = node->getName();
- return id.size() ? id.c_str() : node->getOriginalId().c_str();
+ return id.empty() ? node->getOriginalId().c_str() : id.c_str();
}
FCurve *AnimationImporter::create_fcurve(int array_index, const char *rna_path)
@@ -277,7 +277,7 @@ AnimationImporter::~AnimationImporter()
BKE_fcurve_free(*it);
}
- if (unused_curves.size()) {
+ if (!unused_curves.empty()) {
fprintf(stderr, "removed %d unused curves\n", (int)unused_curves.size());
}
}
@@ -306,7 +306,7 @@ bool AnimationImporter::write_animation(const COLLADAFW::Animation *anim)
animation_to_fcurves(curve);
break;
default:
- /* TODO there're also CARDINAL, HERMITE, BSPLINE and STEP types */
+ /* TODO there are also CARDINAL, HERMITE, BSPLINE and STEP types. */
fprintf(stderr,
"CARDINAL, HERMITE and BSPLINE anim interpolation types not supported yet.\n");
break;
@@ -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];
@@ -736,7 +738,7 @@ void AnimationImporter::Assign_float_animations(const COLLADAFW::UniqueId &listi
/* NOTE: Do NOT convert if imported file was made by blender <= 2.69.10
* Reason: old blender versions stored spot_size in radians (was a bug)
*/
- if (this->import_from_version == "" ||
+ if (this->import_from_version.empty() ||
BLI_strcasecmp_natural(this->import_from_version.c_str(), "2.69.10") != -1) {
fcurve_deg_to_rad(fcu);
}
@@ -876,7 +878,7 @@ void AnimationImporter::apply_matrix_curves(Object *ob,
newcu[i]->totvert = frames.size();
}
- if (frames.size() == 0) {
+ if (frames.empty()) {
return;
}
@@ -965,8 +967,6 @@ void AnimationImporter::apply_matrix_curves(Object *ob,
else {
ob->rotmode = ROT_MODE_QUAT;
}
-
- return;
}
/*
@@ -1331,7 +1331,7 @@ void AnimationImporter::add_bone_animation_sampled(Object *ob,
newcu[i]->totvert = frames.size();
}
- if (frames.size() == 0) {
+ if (frames.empty()) {
return;
}
@@ -1639,7 +1639,7 @@ Object *AnimationImporter::translate_animation_OLD(
job = get_joint_object(root, node, par_job);
#endif
- if (frames.size() == 0) {
+ if (frames.empty()) {
return job;
}
@@ -1874,7 +1874,7 @@ void AnimationImporter::evaluate_transform_at_frame(float mat[4][4],
unit_m4(m);
- std::string nodename = node->getName().size() ? node->getName() : node->getOriginalId();
+ std::string nodename = node->getName().empty() ? node->getOriginalId() : node->getName();
if (!evaluate_animation(tm, m, fra, nodename.c_str())) {
switch (type) {
case COLLADAFW::Transformation::ROTATE:
diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp
index a69500432e8..bd5bd913a18 100644
--- a/source/blender/io/collada/ArmatureImporter.cpp
+++ b/source/blender/io/collada/ArmatureImporter.cpp
@@ -41,7 +41,7 @@
template<class T> static const char *bc_get_joint_name(T *node)
{
const std::string &id = node->getName();
- return id.size() ? id.c_str() : node->getOriginalId().c_str();
+ return id.empty() ? node->getOriginalId().c_str() : id.c_str();
}
ArmatureImporter::ArmatureImporter(UnitConverter *conv,
@@ -606,7 +606,7 @@ Object *ArmatureImporter::create_armature_bones(Main *bmain, SkinInfo &skin)
}
}
- if (!shared && this->joint_parent_map.size() > 0) {
+ if (!shared && !this->joint_parent_map.empty()) {
/* All armatures have been created while creating the Node tree.
* The Collada exporter currently does not create a
* strict relationship between geometries and armatures
@@ -969,8 +969,8 @@ void ArmatureImporter::make_shape_keys(bContext *C)
/* insert other shape keys */
for (int i = 0; i < morphTargetIds.getCount(); i++) {
- /* better to have a separate map of morph objects,
- * This'll do for now since only mesh morphing is imported */
+ /* Better to have a separate map of morph objects,
+ * This will do for now since only mesh morphing is imported. */
Mesh *me = this->mesh_importer->get_mesh_by_geom_uid(morphTargetIds[i]);
diff --git a/source/blender/io/collada/BCAnimationSampler.cpp b/source/blender/io/collada/BCAnimationSampler.cpp
index 4aea74cd82f..f44e3e8385d 100644
--- a/source/blender/io/collada/BCAnimationSampler.cpp
+++ b/source/blender/io/collada/BCAnimationSampler.cpp
@@ -78,7 +78,7 @@ void BCAnimationSampler::add_object(Object *ob)
BCAnimationCurveMap *BCAnimationSampler::get_curves(Object *ob)
{
BCAnimation &animation = *objects[ob];
- if (animation.curve_map.size() == 0) {
+ if (animation.curve_map.empty()) {
initialize_curves(animation.curve_map, ob);
}
return &animation.curve_map;
@@ -279,7 +279,7 @@ void BCAnimationSampler::find_depending_animated(std::set<Object *> &animated_ob
break;
}
}
- } while (found_more && candidates.size() > 0);
+ } while (found_more && !candidates.empty());
}
void BCAnimationSampler::get_animated_from_export_set(std::set<Object *> &animated_objects,
diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp
index ec9977c1469..a03e2ad6488 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"
@@ -70,27 +72,30 @@ BCMatrix::BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis
set_transform(mat);
}
-void BCMatrix::add_transform(const Matrix &mat, bool inverse)
+void BCMatrix::add_transform(const Matrix &mat, bool inverted)
{
- add_transform(this->matrix, mat, this->matrix, inverse);
+ add_transform(this->matrix, mat, this->matrix, inverted);
}
-void BCMatrix::add_transform(const BCMatrix &mat, bool inverse)
+void BCMatrix::add_transform(const BCMatrix &mat, bool inverted)
{
- add_transform(this->matrix, mat.matrix, this->matrix, inverse);
+ add_transform(this->matrix, mat.matrix, this->matrix, inverted);
}
-void BCMatrix::apply_transform(const BCMatrix &mat, bool inverse)
+void BCMatrix::apply_transform(const BCMatrix &mat, bool inverted)
{
- apply_transform(this->matrix, mat.matrix, this->matrix, inverse);
+ apply_transform(this->matrix, mat.matrix, this->matrix, inverted);
}
-void BCMatrix::add_transform(Matrix &to, const Matrix &transform, const Matrix &from, bool inverse)
+void BCMatrix::add_transform(Matrix &to,
+ const Matrix &transform,
+ const Matrix &from,
+ bool inverted)
{
- if (inverse) {
+ if (inverted) {
Matrix globinv;
invert_m4_m4(globinv, transform);
- add_transform(to, globinv, from, /*inverse=*/false);
+ add_transform(to, globinv, from, /*inverted=*/false);
}
else {
mul_m4_m4m4(to, transform, from);
@@ -105,7 +110,7 @@ void BCMatrix::apply_transform(Matrix &to,
Matrix globinv;
invert_m4_m4(globinv, transform);
if (inverse) {
- add_transform(to, globinv, from, /*inverse=*/false);
+ add_transform(to, globinv, from, /*inverted=*/false);
}
else {
mul_m4_m4m4(to, transform, from);
diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp
index 2305072a6eb..0f84db79c28 100644
--- a/source/blender/io/collada/DocumentImporter.cpp
+++ b/source/blender/io/collada/DocumentImporter.cpp
@@ -251,7 +251,7 @@ void DocumentImporter::finish()
}
}
- if (libnode_ob.size()) {
+ if (!libnode_ob.empty()) {
fprintf(stderr, "| Cleanup: free %d library nodes\n", (int)libnode_ob.size());
/* free all library_nodes */
@@ -667,7 +667,7 @@ std::vector<Object *> *DocumentImporter::write_node(COLLADAFW::Node *node,
for (std::vector<Object *>::iterator it = objects_done->begin(); it != objects_done->end();
++it) {
ob = *it;
- std::string nodename = node->getName().size() ? node->getName() : node->getOriginalId();
+ std::string nodename = node->getName().empty() ? node->getOriginalId() : node->getName();
BKE_libblock_rename(bmain, &ob->id, (char *)nodename.c_str());
object_map.insert(std::pair<COLLADAFW::UniqueId, Object *>(node->getUniqueId(), ob));
node_map[node->getUniqueId()] = node;
@@ -699,11 +699,11 @@ std::vector<Object *> *DocumentImporter::write_node(COLLADAFW::Node *node,
}
}
- if (objects_done->size() > 0) {
- ob = *objects_done->begin();
+ if (objects_done->empty()) {
+ ob = NULL;
}
else {
- ob = NULL;
+ ob = *objects_done->begin();
}
for (unsigned int i = 0; i < child_nodes.getCount(); i++) {
@@ -792,7 +792,8 @@ bool DocumentImporter::writeMaterial(const COLLADAFW::Material *cmat)
}
Main *bmain = CTX_data_main(mContext);
- const std::string &str_mat_id = cmat->getName().size() ? cmat->getName() : cmat->getOriginalId();
+ const std::string &str_mat_id = cmat->getName().empty() ? cmat->getOriginalId() :
+ cmat->getName();
Material *ma = BKE_material_add(bmain, (char *)str_mat_id.c_str());
this->uid_effect_map[cmat->getInstantiatedEffect()] = ma;
@@ -880,11 +881,11 @@ bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera)
ExtraTags *et = getExtraTags(camera->getUniqueId());
cam_id = camera->getOriginalId();
cam_name = camera->getName();
- if (cam_name.size()) {
- cam = (Camera *)BKE_camera_add(bmain, (char *)cam_name.c_str());
+ if (cam_name.empty()) {
+ cam = (Camera *)BKE_camera_add(bmain, (char *)cam_id.c_str());
}
else {
- cam = (Camera *)BKE_camera_add(bmain, (char *)cam_id.c_str());
+ cam = (Camera *)BKE_camera_add(bmain, (char *)cam_name.c_str());
}
if (!cam) {
@@ -1042,11 +1043,11 @@ bool DocumentImporter::writeLight(const COLLADAFW::Light *light)
la_id = light->getOriginalId();
la_name = light->getName();
- if (la_name.size()) {
- lamp = (Light *)BKE_light_add(bmain, (char *)la_name.c_str());
+ if (la_name.empty()) {
+ lamp = (Light *)BKE_light_add(bmain, (char *)la_id.c_str());
}
else {
- lamp = (Light *)BKE_light_add(bmain, (char *)la_id.c_str());
+ lamp = (Light *)BKE_light_add(bmain, (char *)la_name.c_str());
}
if (!lamp) {
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/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp
index 6683f07bf65..9fbb7324f8f 100644
--- a/source/blender/io/collada/MeshImporter.cpp
+++ b/source/blender/io/collada/MeshImporter.cpp
@@ -50,7 +50,7 @@
// get node name, or fall back to original id if not present (name is optional)
template<class T> static const std::string bc_get_dae_name(T *node)
{
- return node->getName().size() ? node->getName() : node->getOriginalId();
+ return node->getName().empty() ? node->getOriginalId() : node->getName();
}
static const char *bc_primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type)
@@ -205,7 +205,7 @@ MeshImporter::MeshImporter(
}
bool MeshImporter::set_poly_indices(
- MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count)
+ MPoly *mpoly, MLoop *mloop, int loop_index, const unsigned int *indices, int loop_count)
{
mpoly->loopstart = loop_index;
mpoly->totloop = loop_count;
@@ -1126,7 +1126,7 @@ Object *MeshImporter::create_mesh_object(
}
// name Object
- const std::string &id = node->getName().size() ? node->getName() : node->getOriginalId();
+ const std::string &id = node->getName().empty() ? node->getOriginalId() : node->getName();
const char *name = (id.length()) ? id.c_str() : NULL;
// add object
@@ -1185,8 +1185,8 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
return true;
}
- const std::string &str_geom_id = mesh->getName().size() ? mesh->getName() :
- mesh->getOriginalId();
+ const std::string &str_geom_id = mesh->getName().empty() ? mesh->getOriginalId() :
+ mesh->getName();
Mesh *me = BKE_mesh_add(m_bmain, (char *)str_geom_id.c_str());
id_us_min(&me->id); // is already 1 here, but will be set later in BKE_mesh_assign_object
diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h
index 2f2a18ff11a..18e56e8f9df 100644
--- a/source/blender/io/collada/MeshImporter.h
+++ b/source/blender/io/collada/MeshImporter.h
@@ -105,7 +105,7 @@ class MeshImporter : public MeshImporterBase {
std::multimap<COLLADAFW::UniqueId, COLLADAFW::UniqueId> materials_mapped_to_geom;
bool set_poly_indices(
- MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count);
+ MPoly *mpoly, MLoop *mloop, int loop_index, const unsigned int *indices, int loop_count);
void set_face_uv(MLoopUV *mloopuv,
UVDataWrapper &uvs,
diff --git a/source/blender/io/collada/SkinInfo.cpp b/source/blender/io/collada/SkinInfo.cpp
index 317e0b37796..5692d11d0c3 100644
--- a/source/blender/io/collada/SkinInfo.cpp
+++ b/source/blender/io/collada/SkinInfo.cpp
@@ -49,7 +49,7 @@
template<class T> static const char *bc_get_joint_name(T *node)
{
const std::string &id = node->getName();
- return id.size() ? id.c_str() : node->getOriginalId().c_str();
+ return id.empty() ? node->getOriginalId().c_str() : id.c_str();
}
/* This is used to store data passed in write_controller_data.
diff --git a/source/blender/io/collada/TransformWriter.cpp b/source/blender/io/collada/TransformWriter.cpp
index 0a66db72cb9..b7455837379 100644
--- a/source/blender/io/collada/TransformWriter.cpp
+++ b/source/blender/io/collada/TransformWriter.cpp
@@ -129,9 +129,9 @@ void TransformWriter::add_node_transform_identity(COLLADASW::Node &node,
}
void TransformWriter::add_transform(COLLADASW::Node &node,
- float loc[3],
- float rot[3],
- float scale[3])
+ const float loc[3],
+ const float rot[3],
+ const float scale[3])
{
node.addScale("scale", scale[0], scale[1], scale[2]);
node.addRotateZ("rotationZ", RAD2DEGF(rot[2]));
diff --git a/source/blender/io/collada/TransformWriter.h b/source/blender/io/collada/TransformWriter.h
index 3c71fc9d36e..db8ef3f5ee2 100644
--- a/source/blender/io/collada/TransformWriter.h
+++ b/source/blender/io/collada/TransformWriter.h
@@ -42,7 +42,10 @@ class TransformWriter {
void add_node_transform_identity(COLLADASW::Node &node, BCExportSettings &export_settings);
private:
- void add_transform(COLLADASW::Node &node, float loc[3], float rot[3], float scale[3]);
+ void add_transform(COLLADASW::Node &node,
+ const float loc[3],
+ const float rot[3],
+ const float scale[3]);
};
#endif
diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp
index 7e834045795..f123e8ea31f 100644
--- a/source/blender/io/collada/collada_internal.cpp
+++ b/source/blender/io/collada/collada_internal.cpp
@@ -221,7 +221,7 @@ std::string translate_id(const char *idString)
std::string translate_id(const std::string &id)
{
- if (id.size() == 0) {
+ if (id.empty()) {
return id;
}
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index d2e05c7ae5b..2c54a49198a 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -528,9 +528,7 @@ BoneExtensionManager::~BoneExtensionManager()
for (BoneExtensionMap::iterator ext_it = extended_bones->begin();
ext_it != extended_bones->end();
++ext_it) {
- if (ext_it->second != NULL) {
- delete ext_it->second;
- }
+ delete ext_it->second;
}
extended_bones->clear();
delete extended_bones;
@@ -605,7 +603,7 @@ float BoneExtended::get_roll()
return this->roll;
}
-void BoneExtended::set_tail(float vec[])
+void BoneExtended::set_tail(const float vec[])
{
this->tail[0] = vec[0];
this->tail[1] = vec[1];
diff --git a/source/blender/io/collada/collada_utils.h b/source/blender/io/collada/collada_utils.h
index b1ec2c8b81a..11a9376294b 100644
--- a/source/blender/io/collada/collada_utils.h
+++ b/source/blender/io/collada/collada_utils.h
@@ -343,7 +343,7 @@ class BoneExtended {
bool has_roll();
float get_roll();
- void set_tail(float vec[]);
+ void set_tail(const float vec[]);
float *get_tail();
bool has_tail();
diff --git a/tests/gtests/blenkernel/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index 5cf4c7a27af..d3341767f8b 100644
--- a/tests/gtests/blenkernel/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -20,25 +20,45 @@
set(INC
.
- ..
- ../../../source/blender/blenkernel
- ../../../source/blender/blenlib
- ../../../source/blender/editors/include
- ../../../source/blender/makesdna
- ../../../source/blender/makesrna
- ../../../intern/guardedalloc
- ../../../intern/atomic
+ ../../blenkernel
+ ../../blenlib
+ ../../depsgraph
+ ../../makesdna
)
-setup_libdirs()
-include_directories(${INC})
+set(INC_SYS
+)
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
-set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
+set(SRC
+ intern/abstract_hierarchy_iterator.cc
+ intern/dupli_parent_finder.cc
+ intern/dupli_persistent_id.cc
+ intern/object_identifier.cc
-if(WITH_BUILDINFO)
- set(BUILDINFO buildinfoobj)
-endif()
+ IO_abstract_hierarchy_iterator.h
+ IO_dupli_persistent_id.hh
+ intern/dupli_parent_finder.hh
+)
+
+set(LIB
+ bf_blenkernel
+ bf_blenlib
+)
+
+blender_add_lib(bf_io_common "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
-BLENDER_TEST(BKE_armature "bf_blenloader;bf_blenkernel;bf_blenlib;${BUILDINFO}")
-BLENDER_TEST(BKE_fcurve "bf_blenloader;bf_blenkernel;bf_editor_animation;${BUILDINFO}")
+target_link_libraries(bf_io_common INTERFACE)
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ intern/abstract_hierarchy_iterator_test.cc
+ intern/hierarchy_context_order_test.cc
+ intern/object_identifier_test.cc
+ )
+ set(TEST_LIB
+ bf_blenloader_test
+ bf_io_common
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_io_common_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index e31d5c91252..d289d86b397 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -36,6 +36,8 @@
#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__
#define __ABSTRACT_HIERARCHY_ITERATOR_H__
+#include "IO_dupli_persistent_id.hh"
+
#include <map>
#include <set>
#include <string>
@@ -48,9 +50,11 @@ struct Object;
struct ParticleSystem;
struct ViewLayer;
-namespace USD {
+namespace blender {
+namespace io {
class AbstractHierarchyWriter;
+class DupliParentFinder;
/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext
* struct contains everything necessary to export a single object to a file. */
@@ -59,6 +63,7 @@ struct HierarchyContext {
Object *object; /* Evaluated object. */
Object *export_parent;
Object *duplicator;
+ PersistentID persistent_id;
float matrix_world[4][4];
std::string export_name;
@@ -90,6 +95,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. */
@@ -116,9 +129,78 @@ class AbstractHierarchyWriter {
// but wasn't used while exporting the current frame (for example, a particle-instanced mesh of
// which the particle is no longer alive).
protected:
+ /* Return true if the data written by this writer changes over time.
+ * Note that this function assumes this is an object data writer. Transform writers should not
+ * call this but implement their own logic. */
virtual bool check_is_animated(const HierarchyContext &context) const;
+
+ /* Helper functions for animation checks. */
+ static bool check_has_physics(const HierarchyContext &context);
+ static bool check_has_deforming_physics(const HierarchyContext &context);
+};
+
+/* 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->();
+};
+
+/* Unique identifier for a (potentially duplicated) object.
+ *
+ * Instances of this class serve as key in the export graph of the
+ * AbstractHierarchyIterator. */
+class ObjectIdentifier {
+ public:
+ Object *object;
+ Object *duplicated_by; /* nullptr for real objects. */
+ PersistentID persistent_id;
+
+ protected:
+ ObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id);
+
+ public:
+ ObjectIdentifier(const ObjectIdentifier &other);
+ ~ObjectIdentifier();
+
+ static ObjectIdentifier for_graph_root();
+ static ObjectIdentifier for_real_object(Object *object);
+ static ObjectIdentifier for_hierarchy_context(const HierarchyContext *context);
+ static ObjectIdentifier for_duplicated_object(const DupliObject *dupli_object,
+ Object *duplicated_by);
+
+ bool is_root() const;
+};
+
+bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
+bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b);
+
/* 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.
*
@@ -130,14 +212,10 @@ class AbstractHierarchyIterator {
public:
/* Mapping from export path to writer. */
typedef std::map<std::string, AbstractHierarchyWriter *> WriterMap;
- /* Pair of a (potentially duplicated) object and its duplicator (or nullptr).
- * This is typically used to store a pair of HierarchyContext::object and
- * HierarchyContext::duplicator. */
- typedef std::pair<Object *, Object *> DupliAndDuplicator;
/* All the children of some object, as per the export hierarchy. */
typedef std::set<HierarchyContext *> ExportChildren;
/* Mapping from an object and its duplicator to the object's export-children. */
- typedef std::map<DupliAndDuplicator, ExportChildren> ExportGraph;
+ typedef std::map<ObjectIdentifier, ExportChildren> ExportGraph;
/* Mapping from ID to its export path. This is used for instancing; given an
* instanced datablock, the export path of the original can be looked up. */
typedef std::map<ID *, std::string> ExportPathMap;
@@ -147,6 +225,7 @@ class AbstractHierarchyIterator {
ExportPathMap duplisource_export_path_;
Depsgraph *depsgraph_;
WriterMap writers_;
+ ExportSubset export_subset_;
public:
explicit AbstractHierarchyIterator(Depsgraph *depsgraph);
@@ -154,12 +233,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;
@@ -185,9 +273,8 @@ class AbstractHierarchyIterator {
void visit_object(Object *object, Object *export_parent, bool weak_export);
void visit_dupli_object(DupliObject *dupli_object,
Object *duplicator,
- const std::set<Object *> &dupli_set);
+ const DupliParentFinder &dupli_parent_finder);
- ExportChildren &graph_children(const HierarchyContext *parent_context);
void context_update_for_graph_index(HierarchyContext *context,
const ExportGraph::key_type &graph_index) const;
@@ -200,18 +287,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
@@ -237,8 +327,10 @@ class AbstractHierarchyIterator {
virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const;
virtual ExportGraph::key_type determine_graph_index_object(const HierarchyContext *context);
- virtual ExportGraph::key_type determine_graph_index_dupli(const HierarchyContext *context,
- const std::set<Object *> &dupli_set);
+ virtual ExportGraph::key_type determine_graph_index_dupli(
+ const HierarchyContext *context,
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder);
/* These functions should create an AbstractHierarchyWriter subclass instance, or return
* nullptr if the object or its data should not be exported. Returning a nullptr for
@@ -255,9 +347,13 @@ class AbstractHierarchyIterator {
virtual AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) = 0;
/* Called by release_writers() to free what the create_XXX_writer() functions allocated. */
- virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0;
+ virtual void release_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/common/IO_dupli_persistent_id.hh b/source/blender/io/common/IO_dupli_persistent_id.hh
new file mode 100644
index 00000000000..5dc54164684
--- /dev/null
+++ b/source/blender/io/common/IO_dupli_persistent_id.hh
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+#ifndef __IO_COMMON_DUPLI_PERSISTENT_ID_H__
+#define __IO_COMMON_DUPLI_PERSISTENT_ID_H__
+
+#include "BKE_duplilist.h"
+
+#include "DNA_object_types.h" /* For MAX_DUPLI_RECUR */
+
+#include <array>
+#include <optional>
+#include <ostream>
+
+namespace blender::io {
+
+/* Wrapper for DupliObject::persistent_id that can act as a map key. */
+class PersistentID {
+ protected:
+ constexpr static int array_length_ = MAX_DUPLI_RECUR;
+ typedef std::array<int, array_length_> PIDArray;
+ PIDArray persistent_id_;
+
+ explicit PersistentID(const PIDArray &persistent_id_values);
+
+ public:
+ PersistentID();
+ explicit PersistentID(const DupliObject *dupli_ob);
+
+ /* Return true iff the persistent IDs are the same, ignoring the first digit. */
+ bool is_from_same_instancer_as(const PersistentID &other) const;
+
+ /* Construct the persistent ID of this instance's instancer. */
+ PersistentID instancer_pid() const;
+
+ /* Construct a string representation by reversing the persistent ID.
+ * In case of a duplicator that is duplicated itself as well, this
+ * results in strings like:
+ * "3" for the duplicated duplicator, and
+ * "3-0", "3-1", etc. for its duplis. */
+ std::string as_object_name_suffix() const;
+
+ friend bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b);
+ friend bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b);
+ friend std::ostream &operator<<(std::ostream &os, const PersistentID &persistent_id);
+
+ private:
+ void copy_values_from(const PIDArray &persistent_id_values);
+};
+
+} // namespace blender::io
+
+#endif // __IO_COMMON_DUPLI_PARENT_FINDER_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..fbefc8c8e7e 100644
--- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
@@ -16,7 +16,8 @@
* The Original Code is Copyright (C) 2019 Blender Foundation.
* All rights reserved.
*/
-#include "abstract_hierarchy_iterator.h"
+#include "IO_abstract_hierarchy_iterator.h"
+#include "dupli_parent_finder.hh"
#include <iostream>
#include <limits.h>
@@ -38,10 +39,12 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
+#include "DNA_rigidbody_types.h"
#include "DEG_depsgraph_query.h"
-namespace USD {
+namespace blender {
+namespace io {
const HierarchyContext *HierarchyContext::root()
{
@@ -74,6 +77,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()
{
}
@@ -88,6 +128,9 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context)
if (BKE_key_from_object(object) != nullptr) {
return true;
}
+ if (check_has_deforming_physics(context)) {
+ return true;
+ }
/* Test modifiers. */
/* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on
@@ -103,13 +146,31 @@ bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context)
return false;
}
+bool AbstractHierarchyWriter::check_has_physics(const HierarchyContext &context)
+{
+ const RigidBodyOb *rbo = context.object->rigidbody_object;
+ return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE;
+}
+
+bool AbstractHierarchyWriter::check_has_deforming_physics(const HierarchyContext &context)
+{
+ const RigidBodyOb *rbo = context.object->rigidbody_object;
+ return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0;
+}
+
AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph)
- : depsgraph_(depsgraph), writers_()
+ : depsgraph_(depsgraph), writers_(), export_subset_({true, true})
{
}
AbstractHierarchyIterator::~AbstractHierarchyIterator()
{
+ /* release_writers() cannot be called here directly, as it calls into the pure-virtual
+ * release_writer() function. By the time this destructor is called, the subclass that implements
+ * that pure-virtual function is already destructed. */
+ BLI_assert(
+ writers_.empty() ||
+ !"release_writers() should be called before the AbstractHierarchyIterator goes out of scope");
}
void AbstractHierarchyIterator::iterate_and_write()
@@ -126,11 +187,16 @@ void AbstractHierarchyIterator::iterate_and_write()
void AbstractHierarchyIterator::release_writers()
{
for (WriterMap::value_type it : writers_) {
- delete_object_writer(it.second);
+ release_writer(it.second);
}
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;
@@ -157,9 +223,9 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap
{
size_t total_graph_size = 0;
for (const ExportGraph::value_type &map_iter : graph) {
- const DupliAndDuplicator &parent_info = map_iter.first;
- Object *const export_parent = parent_info.first;
- Object *const duplicator = parent_info.second;
+ const ObjectIdentifier &parent_info = map_iter.first;
+ const Object *const export_parent = parent_info.object;
+ const Object *const duplicator = parent_info.duplicated_by;
if (duplicator != nullptr) {
printf(" DU %s (as dupped by %s):\n",
@@ -174,24 +240,24 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap
for (HierarchyContext *child_ctx : map_iter.second) {
if (child_ctx->duplicator == nullptr) {
printf(" - %s%s%s\n",
- child_ctx->object->id.name + 2,
+ child_ctx->export_name.c_str(),
child_ctx->weak_export ? " (weak)" : "",
- child_ctx->original_export_path.size() ?
- (std::string("ref ") + child_ctx->original_export_path).c_str() :
- "");
+ child_ctx->original_export_path.empty() ?
+ "" :
+ (std::string("ref ") + child_ctx->original_export_path).c_str());
}
else {
printf(" - %s (dup by %s%s) %s\n",
- child_ctx->object->id.name + 2,
+ child_ctx->export_name.c_str(),
child_ctx->duplicator->id.name + 2,
child_ctx->weak_export ? ", weak" : "",
- child_ctx->original_export_path.size() ?
- (std::string("ref ") + child_ctx->original_export_path).c_str() :
- "");
+ child_ctx->original_export_path.empty() ?
+ "" :
+ (std::string("ref ") + child_ctx->original_export_path).c_str());
}
}
}
- printf(" (Total graph size: %zu objects\n", total_graph_size);
+ printf(" (Total graph size: %zu objects)\n", total_graph_size);
}
void AbstractHierarchyIterator::export_graph_construct()
@@ -214,22 +280,21 @@ void AbstractHierarchyIterator::export_graph_construct()
// Export the duplicated objects instanced by this object.
ListBase *lb = object_duplilist(depsgraph_, scene, object);
if (lb) {
- // Construct the set of duplicated objects, so that later we can determine whether a parent
- // is also duplicated itself.
- std::set<Object *> dupli_set;
+ DupliParentFinder dupli_parent_finder;
+
LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
+ PersistentID persistent_id(dupli_object);
if (!should_visit_dupli_object(dupli_object)) {
continue;
}
- dupli_set.insert(dupli_object->ob);
+ dupli_parent_finder.insert(dupli_object);
}
LISTBASE_FOREACH (DupliObject *, dupli_object, lb) {
if (!should_visit_dupli_object(dupli_object)) {
continue;
}
-
- visit_dupli_object(dupli_object, object, dupli_set);
+ visit_dupli_object(dupli_object, object, dupli_parent_finder);
}
}
@@ -248,29 +313,30 @@ void AbstractHierarchyIterator::connect_loose_objects()
for (const ExportGraph::value_type &map_iter : export_graph_) {
for (const HierarchyContext *child : map_iter.second) {
// An object that is marked as a child of another object is not considered 'loose'.
- loose_objects_graph.erase(std::make_pair(child->object, child->duplicator));
+ ObjectIdentifier child_oid = ObjectIdentifier::for_hierarchy_context(child);
+ loose_objects_graph.erase(child_oid);
}
}
// The root of the hierarchy is always found, so it's never considered 'loose'.
- loose_objects_graph.erase(std::make_pair(nullptr, nullptr));
+ loose_objects_graph.erase(ObjectIdentifier::for_graph_root());
// Iterate over the loose objects and connect them to their export parent.
for (const ExportGraph::value_type &map_iter : loose_objects_graph) {
- const DupliAndDuplicator &export_info = map_iter.first;
- Object *object = export_info.first;
+ const ObjectIdentifier &graph_key = map_iter.first;
+ Object *object = graph_key.object;
while (true) {
// Loose objects will all be real objects, as duplicated objects always have
// their duplicator or other exported duplicated object as ancestor.
ExportGraph::iterator found_parent_iter = export_graph_.find(
- std::make_pair(object->parent, nullptr));
+ ObjectIdentifier::for_real_object(object->parent));
visit_object(object, object->parent, true);
if (found_parent_iter != export_graph_.end()) {
break;
}
// 'object->parent' will never be nullptr here, as the export graph contains the
- // tuple <nullptr, nullptr> as root and thus will cause a break.
+ // root as nullptr and thus will cause a break above.
BLI_assert(object->parent != nullptr);
object = object->parent;
@@ -283,10 +349,8 @@ static bool remove_weak_subtrees(const HierarchyContext *context,
const AbstractHierarchyIterator::ExportGraph &input_graph)
{
bool all_is_weak = context != nullptr && context->weak_export;
- Object *object = context != nullptr ? context->object : nullptr;
- Object *duplicator = context != nullptr ? context->duplicator : nullptr;
+ const ObjectIdentifier map_key = ObjectIdentifier::for_hierarchy_context(context);
- const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator);
AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator;
child_iterator = input_graph.find(map_key);
@@ -341,6 +405,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);
@@ -354,7 +420,7 @@ void AbstractHierarchyIterator::visit_object(Object *object,
// check on whether an object is part of the export, rather than having to check all objects in
// the map. Note that it's not possible to simply search for (object->parent, nullptr), as the
// object's parent in Blender may not be the same as its export-parent.
- ExportGraph::key_type object_key = std::make_pair(object, nullptr);
+ ExportGraph::key_type object_key = ObjectIdentifier::for_real_object(object);
if (export_graph_.find(object_key) == export_graph_.end()) {
export_graph_[object_key] = ExportChildren();
}
@@ -363,16 +429,17 @@ void AbstractHierarchyIterator::visit_object(Object *object,
AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator::
determine_graph_index_object(const HierarchyContext *context)
{
- return std::make_pair(context->export_parent, nullptr);
+ return ObjectIdentifier::for_real_object(context->export_parent);
}
void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
Object *duplicator,
- const std::set<Object *> &dupli_set)
+ const DupliParentFinder &dupli_parent_finder)
{
HierarchyContext *context = new HierarchyContext();
context->object = dupli_object->ob;
context->duplicator = duplicator;
+ context->persistent_id = PersistentID(dupli_object);
context->weak_export = false;
context->export_path = "";
context->original_export_path = "";
@@ -382,38 +449,36 @@ void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object,
copy_m4_m4(context->matrix_world, dupli_object->mat);
// Construct export name for the dupli-instance.
- std::stringstream suffix_stream;
- suffix_stream << std::hex;
- for (int i = 0; i < MAX_DUPLI_RECUR && dupli_object->persistent_id[i] != INT_MAX; i++) {
- suffix_stream << "-" << dupli_object->persistent_id[i];
- }
- context->export_name = make_valid_name(get_object_name(context->object) + suffix_stream.str());
+ std::stringstream export_name_stream;
+ export_name_stream << get_object_name(context->object) << "-"
+ << context->persistent_id.as_object_name_suffix();
+ context->export_name = make_valid_name(export_name_stream.str());
- ExportGraph::key_type graph_index = determine_graph_index_dupli(context, dupli_set);
+ ExportGraph::key_type graph_index = determine_graph_index_dupli(
+ context, dupli_object, dupli_parent_finder);
context_update_for_graph_index(context, graph_index);
+
export_graph_[graph_index].insert(context);
}
AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator::
determine_graph_index_dupli(const HierarchyContext *context,
- const std::set<Object *> &dupli_set)
+ const DupliObject *dupli_object,
+ const DupliParentFinder &dupli_parent_finder)
{
- /* If the dupli-object's parent is also instanced by this object, use that as the
- * export parent. Otherwise use the dupli-parent as export parent. */
+ const DupliObject *dupli_parent = dupli_parent_finder.find_suitable_export_parent(dupli_object);
- Object *parent = context->object->parent;
- if (parent != nullptr && dupli_set.find(parent) != dupli_set.end()) {
- // The parent object is part of the duplicated collection.
- return std::make_pair(parent, context->duplicator);
+ if (dupli_parent != nullptr) {
+ return ObjectIdentifier::for_duplicated_object(dupli_parent, context->duplicator);
}
- return std::make_pair(context->duplicator, nullptr);
+ return ObjectIdentifier::for_real_object(context->duplicator);
}
void AbstractHierarchyIterator::context_update_for_graph_index(
HierarchyContext *context, const ExportGraph::key_type &graph_index) const
{
// Update the HierarchyContext so that it is consistent with the graph index.
- context->export_parent = graph_index.first;
+ context->export_parent = graph_index.object;
if (context->export_parent != context->object->parent) {
/* The parent object in Blender is NOT used as the export parent. This means
* that the world transform of this object can be influenced by objects that
@@ -425,11 +490,7 @@ void AbstractHierarchyIterator::context_update_for_graph_index(
AbstractHierarchyIterator::ExportChildren &AbstractHierarchyIterator::graph_children(
const HierarchyContext *context)
{
- if (context == nullptr) {
- return export_graph_[std::make_pair(nullptr, nullptr)];
- }
-
- return export_graph_[std::make_pair(context->object, context->duplicator)];
+ return export_graph_[ObjectIdentifier::for_hierarchy_context(context)];
}
void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context)
@@ -494,7 +555,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 +567,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 +601,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 +627,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 +650,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 +665,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 +698,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 +731,5 @@ bool AbstractHierarchyIterator::should_visit_dupli_object(const DupliObject *dup
return !dupli_object->no_draw;
}
-} // namespace USD
+} // namespace io
+} // namespace blender
diff --git a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
index 160dd52f39a..2bc7560e177 100644
--- a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
@@ -16,30 +16,30 @@
* 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"
#include "DEG_depsgraph.h"
#include "DNA_object_types.h"
-}
#include <map>
#include <set>
+namespace blender::io {
+
+namespace {
+
/* 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;
-
-using namespace USD;
+typedef std::map<std::string, std::set<std::string>> used_writers;
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,51 +57,42 @@ class TestHierarchyWriter : public AbstractHierarchyWriter {
}
};
-void debug_print_writers(const char *label, const created_writers &writers_map)
-{
- printf("%s:\n", label);
- for (auto idname_writers : writers_map) {
- printf(" %s:\n", idname_writers.first.c_str());
- for (const std::string &export_path : idname_writers.second) {
- printf(" - %s\n", export_path.c_str());
- }
- }
-}
+} // namespace
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)
{
}
virtual ~TestingHierarchyIterator()
{
+ release_writers();
}
protected:
- AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) override
+ AbstractHierarchyWriter *create_transform_writer(const HierarchyContext * /*context*/) override
{
return new TestHierarchyWriter("transform", transform_writers);
}
- AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override
+ AbstractHierarchyWriter *create_data_writer(const HierarchyContext * /*context*/) override
{
return new TestHierarchyWriter("data", data_writers);
}
- AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override
+ AbstractHierarchyWriter *create_hair_writer(const HierarchyContext * /*context*/) override
{
return new TestHierarchyWriter("hair", hair_writers);
}
- AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) override
+ AbstractHierarchyWriter *create_particle_writer(const HierarchyContext * /*context*/) override
{
return new TestHierarchyWriter("particle", particle_writers);
}
- void delete_object_writer(AbstractHierarchyWriter *writer) override
+ void release_writer(AbstractHierarchyWriter *writer) override
{
delete writer;
}
@@ -151,7 +142,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 +168,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 +195,125 @@ 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);
}
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/dupli_parent_finder.cc b/source/blender/io/common/intern/dupli_parent_finder.cc
new file mode 100644
index 00000000000..73e33eff164
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_parent_finder.cc
@@ -0,0 +1,104 @@
+/*
+ * 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 "dupli_parent_finder.hh"
+
+#include "BLI_utildefines.h"
+
+#include <iostream>
+
+namespace blender::io {
+
+DupliParentFinder::DupliParentFinder()
+{
+}
+
+DupliParentFinder::~DupliParentFinder()
+{
+}
+
+void DupliParentFinder::insert(const DupliObject *dupli_ob)
+{
+ dupli_set_.insert(dupli_ob->ob);
+
+ PersistentID dupli_pid(dupli_ob);
+ pid_to_dupli_[dupli_pid] = dupli_ob;
+ instancer_pid_to_duplis_[dupli_pid.instancer_pid()].insert(dupli_ob);
+}
+
+bool DupliParentFinder::is_duplicated(const Object *object) const
+{
+ return dupli_set_.find(object) != dupli_set_.end();
+}
+
+const DupliObject *DupliParentFinder::find_suitable_export_parent(
+ const DupliObject *dupli_ob) const
+{
+ if (dupli_ob->ob->parent != nullptr) {
+ const DupliObject *parent = find_duplicated_parent(dupli_ob);
+ if (parent != nullptr) {
+ return parent;
+ }
+ }
+
+ return find_instancer(dupli_ob);
+}
+
+const DupliObject *DupliParentFinder::find_duplicated_parent(const DupliObject *dupli_ob) const
+{
+ const PersistentID dupli_pid(dupli_ob);
+ PersistentID parent_pid = dupli_pid.instancer_pid();
+
+ const Object *parent_ob = dupli_ob->ob->parent;
+ BLI_assert(parent_ob != nullptr);
+
+ InstancerPIDToDuplisMap::const_iterator found = instancer_pid_to_duplis_.find(parent_pid);
+ if (found == instancer_pid_to_duplis_.end()) {
+ /* Unexpected, as there should be at least one entry here, for the dupli_ob itself. */
+ return nullptr;
+ }
+
+ for (const DupliObject *potential_parent_dupli : found->second) {
+ if (potential_parent_dupli->ob != parent_ob) {
+ continue;
+ }
+
+ PersistentID potential_parent_pid(potential_parent_dupli);
+ if (potential_parent_pid.is_from_same_instancer_as(dupli_pid)) {
+ return potential_parent_dupli;
+ }
+ }
+ return nullptr;
+}
+
+const DupliObject *DupliParentFinder::find_instancer(const DupliObject *dupli_ob) const
+{
+ PersistentID dupli_pid(dupli_ob);
+ PersistentID parent_pid = dupli_pid.instancer_pid();
+
+ PIDToDupliMap::const_iterator found = pid_to_dupli_.find(parent_pid);
+ if (found == pid_to_dupli_.end()) {
+ return nullptr;
+ }
+
+ const DupliObject *instancer_dupli = found->second;
+ return instancer_dupli;
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/dupli_parent_finder.hh b/source/blender/io/common/intern/dupli_parent_finder.hh
new file mode 100644
index 00000000000..e7e628665ee
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_parent_finder.hh
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#ifndef __IO_COMMON_DUPLI_PARENT_FINDER_H__
+#define __IO_COMMON_DUPLI_PARENT_FINDER_H__
+
+#include "IO_dupli_persistent_id.hh"
+
+#include "BKE_duplilist.h"
+
+#include <map>
+#include <set>
+
+namespace blender::io {
+
+/* Find relations between duplicated objects. This class should be instanced for a single real
+ * object, and fed its dupli-objects. */
+class DupliParentFinder final {
+ private:
+ /* To check whether an Object * is instanced by this duplicator. */
+ std::set<const Object *> dupli_set_;
+
+ /* To find the DupliObject given its Persistent ID. */
+ typedef std::map<const PersistentID, const DupliObject *> PIDToDupliMap;
+ PIDToDupliMap pid_to_dupli_;
+
+ /* Mapping from instancer PID to duplis instanced by it. */
+ typedef std::map<const PersistentID, std::set<const DupliObject *>> InstancerPIDToDuplisMap;
+ InstancerPIDToDuplisMap instancer_pid_to_duplis_;
+
+ public:
+ DupliParentFinder();
+ ~DupliParentFinder();
+
+ void insert(const DupliObject *dupli_ob);
+
+ bool is_duplicated(const Object *object) const;
+ const DupliObject *find_suitable_export_parent(const DupliObject *dupli_ob) const;
+
+ private:
+ const DupliObject *find_duplicated_parent(const DupliObject *dupli_ob) const;
+ const DupliObject *find_instancer(const DupliObject *dupli_ob) const;
+};
+
+} // namespace blender::io
+
+#endif \ No newline at end of file
diff --git a/source/blender/io/common/intern/dupli_persistent_id.cc b/source/blender/io/common/intern/dupli_persistent_id.cc
new file mode 100644
index 00000000000..b40cd83ef44
--- /dev/null
+++ b/source/blender/io/common/intern/dupli_persistent_id.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "dupli_parent_finder.hh"
+
+#include <climits>
+#include <cstring>
+#include <ostream>
+#include <sstream>
+
+namespace blender::io {
+
+PersistentID::PersistentID()
+{
+ persistent_id_[0] = INT_MAX;
+}
+
+PersistentID::PersistentID(const DupliObject *dupli_ob)
+{
+ for (int index = 0; index < array_length_; ++index) {
+ persistent_id_[index] = dupli_ob->persistent_id[index];
+ }
+}
+
+PersistentID::PersistentID(const PIDArray &persistent_id_values)
+{
+ persistent_id_ = persistent_id_values;
+}
+
+bool PersistentID::is_from_same_instancer_as(const PersistentID &other) const
+{
+ if (persistent_id_[0] == INT_MAX || other.persistent_id_[0] == INT_MAX) {
+ /* Either one or the other is not instanced at all, so definitely not from the same instancer.
+ */
+ return false;
+ }
+
+ /* Start at index 1 to skip the first digit. */
+ for (int index = 1; index < array_length_; ++index) {
+ const int pid_digit_a = persistent_id_[index];
+ const int pid_digit_b = other.persistent_id_[index];
+
+ if (pid_digit_a != pid_digit_b) {
+ return false;
+ }
+
+ if (pid_digit_a == INT_MAX) {
+ /* Both persistent IDs were identical so far, and this marks the end of the useful data. */
+ break;
+ }
+ }
+ return true;
+}
+
+PersistentID PersistentID::instancer_pid() const
+{
+ if (persistent_id_[0] == INT_MAX) {
+ return PersistentID();
+ }
+
+ /* Left-shift the entire PID by 1. */
+ PIDArray new_pid_values;
+ int index;
+ for (index = 0; index < array_length_ - 1; ++index) {
+ new_pid_values[index] = persistent_id_[index + 1];
+ }
+ new_pid_values[index] = INT_MAX;
+
+ return PersistentID(new_pid_values);
+}
+
+std::string PersistentID::as_object_name_suffix() const
+{
+ std::stringstream stream;
+
+ /* Find one past the last index. */
+ int index;
+ for (index = 0; index < array_length_ && persistent_id_[index] < INT_MAX; ++index) {
+ ;
+ }
+
+ /* Iterate backward to construct the string. */
+ --index;
+ for (; index >= 0; --index) {
+ stream << persistent_id_[index];
+ if (index > 0) {
+ stream << "-";
+ }
+ }
+
+ return stream.str();
+}
+
+bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
+{
+ const PersistentID::PIDArray &pid_a = persistent_id_a.persistent_id_;
+ const PersistentID::PIDArray &pid_b = persistent_id_b.persistent_id_;
+
+ for (int index = 0; index < PersistentID::array_length_; ++index) {
+ const int pid_digit_a = pid_a[index];
+ const int pid_digit_b = pid_b[index];
+
+ if (pid_digit_a != pid_digit_b) {
+ return pid_digit_a < pid_digit_b;
+ }
+
+ if (pid_a[index] == INT_MAX) {
+ break;
+ }
+ }
+ /* Both Persistent IDs were equal, so not less-than. */
+ return false;
+}
+
+bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b)
+{
+ const PersistentID::PIDArray &pid_a = persistent_id_a.persistent_id_;
+ const PersistentID::PIDArray &pid_b = persistent_id_b.persistent_id_;
+
+ for (int index = 0; index < PersistentID::array_length_; ++index) {
+ const int pid_digit_a = pid_a[index];
+ const int pid_digit_b = pid_b[index];
+
+ if (pid_digit_a != pid_digit_b) {
+ return false;
+ }
+
+ if (pid_a[index] == INT_MAX) {
+ break;
+ }
+ }
+ return true;
+}
+
+std::ostream &operator<<(std::ostream &os, const PersistentID &persistent_id)
+{
+ if (persistent_id.persistent_id_[0] == INT_MAX) {
+ return os;
+ }
+
+ const PersistentID::PIDArray &pid_array = persistent_id.persistent_id_;
+ for (int index = 0; index < PersistentID::array_length_ && pid_array[index] < INT_MAX; ++index) {
+ if (index > 0) {
+ os << "-";
+ }
+ os << pid_array[index];
+ }
+ return os;
+}
+
+} // namespace blender::io
diff --git a/tests/gtests/usd/hierarchy_context_order_test.cc b/source/blender/io/common/intern/hierarchy_context_order_test.cc
index 4111fc7511e..f4e2b81b39b 100644
--- a/tests/gtests/usd/hierarchy_context_order_test.cc
+++ b/source/blender/io/common/intern/hierarchy_context_order_test.cc
@@ -16,24 +16,26 @@
* 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"
-extern "C" {
#include "BLI_utildefines.h"
-}
-using namespace USD;
+namespace blender::io {
-class HierarchyContextOrderTest : public testing::Test {
-};
+namespace {
-static Object *fake_pointer(int value)
+Object *fake_pointer(int value)
{
return static_cast<Object *>(POINTER_FROM_INT(value));
}
+} // namespace
+
+class HierarchyContextOrderTest : public testing::Test {
+};
+
TEST_F(HierarchyContextOrderTest, ObjectPointerTest)
{
HierarchyContext ctx_a = {0};
@@ -121,3 +123,5 @@ TEST_F(HierarchyContextOrderTest, TransitiveTest)
EXPECT_FALSE(ctx_d < ctx_b);
EXPECT_FALSE(ctx_d < ctx_c);
}
+
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/object_identifier.cc b/source/blender/io/common/intern/object_identifier.cc
new file mode 100644
index 00000000000..696bc5d2c34
--- /dev/null
+++ b/source/blender/io/common/intern/object_identifier.cc
@@ -0,0 +1,116 @@
+/*
+ * 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) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include "BKE_duplilist.h"
+
+extern "C" {
+#include <limits.h> /* For INT_MAX. */
+}
+#include <cstring>
+#include <sstream>
+
+namespace blender {
+namespace io {
+
+ObjectIdentifier::ObjectIdentifier(Object *object,
+ Object *duplicated_by,
+ const PersistentID &persistent_id)
+ : object(object), duplicated_by(duplicated_by), persistent_id(persistent_id)
+{
+}
+
+ObjectIdentifier::ObjectIdentifier(const ObjectIdentifier &other)
+ : object(other.object), duplicated_by(other.duplicated_by), persistent_id(other.persistent_id)
+{
+}
+
+ObjectIdentifier::~ObjectIdentifier()
+{
+}
+
+ObjectIdentifier ObjectIdentifier::for_real_object(Object *object)
+{
+ return ObjectIdentifier(object, nullptr, PersistentID());
+}
+
+ObjectIdentifier ObjectIdentifier::for_hierarchy_context(const HierarchyContext *context)
+{
+ if (context == nullptr) {
+ return for_graph_root();
+ }
+ if (context->duplicator != nullptr) {
+ return ObjectIdentifier(context->object, context->duplicator, context->persistent_id);
+ }
+ return for_real_object(context->object);
+}
+
+ObjectIdentifier ObjectIdentifier::for_duplicated_object(const DupliObject *dupli_object,
+ Object *duplicated_by)
+{
+ return ObjectIdentifier(dupli_object->ob, duplicated_by, PersistentID(dupli_object));
+}
+
+ObjectIdentifier ObjectIdentifier::for_graph_root()
+{
+ return ObjectIdentifier(nullptr, nullptr, PersistentID());
+}
+
+bool ObjectIdentifier::is_root() const
+{
+ return object == nullptr;
+}
+
+bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b)
+{
+ if (obj_ident_a.object != obj_ident_b.object) {
+ return obj_ident_a.object < obj_ident_b.object;
+ }
+
+ if (obj_ident_a.duplicated_by != obj_ident_b.duplicated_by) {
+ return obj_ident_a.duplicated_by < obj_ident_b.duplicated_by;
+ }
+
+ if (obj_ident_a.duplicated_by == nullptr) {
+ /* Both are real objects, no need to check the persistent ID. */
+ return false;
+ }
+
+ /* Same object, both are duplicated, use the persistent IDs to determine order. */
+ return obj_ident_a.persistent_id < obj_ident_b.persistent_id;
+}
+
+bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b)
+{
+ if (obj_ident_a.object != obj_ident_b.object) {
+ return false;
+ }
+ if (obj_ident_a.duplicated_by != obj_ident_b.duplicated_by) {
+ return false;
+ }
+ if (obj_ident_a.duplicated_by == nullptr) {
+ return true;
+ }
+
+ /* Same object, both are duplicated, use the persistent IDs to determine equality. */
+ return obj_ident_a.persistent_id == obj_ident_b.persistent_id;
+}
+
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/common/intern/object_identifier_test.cc b/source/blender/io/common/intern/object_identifier_test.cc
new file mode 100644
index 00000000000..2b565876f22
--- /dev/null
+++ b/source/blender/io/common/intern/object_identifier_test.cc
@@ -0,0 +1,234 @@
+/*
+ * 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) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include "testing/testing.h"
+
+#include "BLI_utildefines.h"
+
+#include <climits>
+
+namespace blender::io {
+
+namespace {
+
+/* Return object pointer for use in tests. This makes it possible to reliably test for
+ * order/equality functions while using hard-coded values for simplicity. */
+Object *fake_pointer(int value)
+{
+ return static_cast<Object *>(POINTER_FROM_INT(value));
+}
+
+/* PersistentID subclass for use in tests, making it easier to construct test values. */
+class TestPersistentID : public PersistentID {
+ public:
+ TestPersistentID(int value0,
+ int value1,
+ int value2,
+ int value3,
+ int value4,
+ int value5,
+ int value6,
+ int value7)
+ {
+ persistent_id_[0] = value0;
+ persistent_id_[1] = value1;
+ persistent_id_[2] = value2;
+ persistent_id_[3] = value3;
+ persistent_id_[4] = value4;
+ persistent_id_[5] = value5;
+ persistent_id_[6] = value6;
+ persistent_id_[7] = value7;
+ }
+ TestPersistentID(int value0, int value1, int value2)
+ : TestPersistentID(value0, value1, value2, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX)
+ {
+ }
+ TestPersistentID(int value0, int value1) : TestPersistentID(value0, value1, INT_MAX)
+ {
+ }
+ explicit TestPersistentID(int value0) : TestPersistentID(value0, INT_MAX)
+ {
+ }
+};
+
+/* ObjectIdentifier subclass for use in tests, making it easier to construct test values. */
+class TestObjectIdentifier : public ObjectIdentifier {
+ public:
+ TestObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id)
+ : ObjectIdentifier(object, duplicated_by, persistent_id)
+ {
+ }
+};
+
+} // namespace
+
+class ObjectIdentifierOrderTest : public testing::Test {
+};
+
+TEST_F(ObjectIdentifierOrderTest, graph_root)
+{
+ ObjectIdentifier id_root_1 = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_root_2 = ObjectIdentifier::for_graph_root();
+ EXPECT_TRUE(id_root_1 == id_root_2);
+ EXPECT_FALSE(id_root_1 < id_root_2);
+ EXPECT_FALSE(id_root_2 < id_root_1);
+
+ ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ EXPECT_FALSE(id_root_1 == id_a);
+ EXPECT_TRUE(id_root_1 < id_a);
+ EXPECT_FALSE(id_a < id_root_1);
+
+ ObjectIdentifier id_accidental_root = ObjectIdentifier::for_real_object(nullptr);
+ EXPECT_TRUE(id_root_1 == id_accidental_root);
+ EXPECT_FALSE(id_root_1 < id_accidental_root);
+ EXPECT_FALSE(id_accidental_root < id_root_1);
+}
+
+TEST_F(ObjectIdentifierOrderTest, real_objects)
+{
+ ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ ObjectIdentifier id_b = ObjectIdentifier::for_real_object(fake_pointer(2));
+ EXPECT_FALSE(id_a == id_b);
+ EXPECT_TRUE(id_a < id_b);
+}
+
+TEST_F(ObjectIdentifierOrderTest, duplicated_objects)
+{
+ ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ TestObjectIdentifier id_different_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(1));
+
+ EXPECT_FALSE(id_real_a == id_dupli_a);
+ EXPECT_FALSE(id_dupli_a == id_dupli_b);
+ EXPECT_TRUE(id_real_a < id_dupli_a);
+ EXPECT_TRUE(id_real_a < id_dupli_b);
+ EXPECT_TRUE(id_dupli_a < id_dupli_b);
+ EXPECT_TRUE(id_dupli_a < id_different_dupli_b);
+
+ EXPECT_FALSE(id_dupli_b == id_different_dupli_b);
+ EXPECT_FALSE(id_dupli_a == id_different_dupli_b);
+ EXPECT_TRUE(id_dupli_b < id_different_dupli_b);
+ EXPECT_FALSE(id_different_dupli_b < id_dupli_b);
+}
+
+TEST_F(ObjectIdentifierOrderTest, behaviour_as_map_keys)
+{
+ ObjectIdentifier id_root = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_another_root = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ AbstractHierarchyIterator::ExportGraph graph;
+
+ /* This inserts the keys with default values. */
+ graph[id_root];
+ graph[id_real_a];
+ graph[id_dupli_a];
+ graph[id_dupli_b];
+ graph[id_another_root];
+
+ EXPECT_EQ(4, graph.size());
+
+ graph.erase(id_another_root);
+ EXPECT_EQ(3, graph.size());
+
+ TestObjectIdentifier id_another_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ graph.erase(id_another_dupli_b);
+ EXPECT_EQ(2, graph.size());
+}
+
+TEST_F(ObjectIdentifierOrderTest, map_copy_and_update)
+{
+ ObjectIdentifier id_root = ObjectIdentifier::for_graph_root();
+ ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
+ TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
+ TestObjectIdentifier id_dupli_c(fake_pointer(1), fake_pointer(3), TestPersistentID(1));
+ AbstractHierarchyIterator::ExportGraph graph;
+
+ /* This inserts the keys with default values. */
+ graph[id_root];
+ graph[id_real_a];
+ graph[id_dupli_a];
+ graph[id_dupli_b];
+ graph[id_dupli_c];
+ EXPECT_EQ(5, graph.size());
+
+ AbstractHierarchyIterator::ExportGraph graph_copy = graph;
+ EXPECT_EQ(5, graph_copy.size());
+
+ // Updating a value in a copy should not update the original.
+ HierarchyContext ctx1;
+ HierarchyContext ctx2;
+ ctx1.object = fake_pointer(1);
+ ctx2.object = fake_pointer(2);
+
+ graph_copy[id_root].insert(&ctx1);
+ EXPECT_EQ(0, graph[id_root].size());
+
+ // Deleting a key in the copy should not update the original.
+ graph_copy.erase(id_dupli_c);
+ EXPECT_EQ(4, graph_copy.size());
+ EXPECT_EQ(5, graph.size());
+}
+
+class PersistentIDTest : public testing::Test {
+};
+
+TEST_F(PersistentIDTest, is_from_same_instancer)
+{
+ PersistentID child_id_a = TestPersistentID(42, 327);
+ PersistentID child_id_b = TestPersistentID(17, 327);
+ PersistentID child_id_c = TestPersistentID(17);
+
+ EXPECT_TRUE(child_id_a.is_from_same_instancer_as(child_id_b));
+ EXPECT_FALSE(child_id_a.is_from_same_instancer_as(child_id_c));
+}
+
+TEST_F(PersistentIDTest, instancer_id)
+{
+ PersistentID child_id = TestPersistentID(42, 327);
+
+ PersistentID expect_instancer_id = TestPersistentID(327);
+ EXPECT_EQ(expect_instancer_id, child_id.instancer_pid());
+
+ PersistentID empty_id;
+ EXPECT_EQ(empty_id, child_id.instancer_pid().instancer_pid());
+
+ EXPECT_LT(child_id, expect_instancer_id);
+ EXPECT_LT(expect_instancer_id, empty_id);
+}
+
+TEST_F(PersistentIDTest, as_object_name_suffix)
+{
+ EXPECT_EQ("", PersistentID().as_object_name_suffix());
+ EXPECT_EQ("47", TestPersistentID(47).as_object_name_suffix());
+ EXPECT_EQ("327-47", TestPersistentID(47, 327).as_object_name_suffix());
+ EXPECT_EQ("42-327-47", TestPersistentID(47, 327, 42).as_object_name_suffix());
+
+ EXPECT_EQ("7-6-5-4-3-2-1-0", TestPersistentID(0, 1, 2, 3, 4, 5, 6, 7).as_object_name_suffix());
+
+ EXPECT_EQ("0-0-0", TestPersistentID(0, 0, 0).as_object_name_suffix());
+ EXPECT_EQ("0-0", TestPersistentID(0, 0).as_object_name_suffix());
+ EXPECT_EQ("-3--2--1", TestPersistentID(-1, -2, -3).as_object_name_suffix());
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 732a638a255..79b15c60b94 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,12 +100,24 @@ 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()
target_link_libraries(bf_usd INTERFACE ${TBB_LIBRARIES})
+
+if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/usd_stage_creation_test.cc
+ )
+ set(TEST_INC
+ )
+ set(TEST_LIB
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_io_usd_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+endif()
diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc
index cf962446d04..98aef62f38e 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;
@@ -57,7 +59,12 @@ struct ExportJobData {
bool export_ok;
};
-static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+static void export_startjob(void *customdata,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
ExportJobData *data = static_cast<ExportJobData *>(customdata);
data->export_ok = false;
@@ -157,7 +164,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 +176,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 +195,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 +208,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..39fbef70e81 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc
@@ -34,6 +34,7 @@
#include "BKE_duplilist.h"
#include "BLI_assert.h"
+#include "BLI_utildefines.h"
#include "DEG_depsgraph_query.h"
@@ -41,7 +42,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,
@@ -58,7 +61,7 @@ bool USDHierarchyIterator::mark_as_weak_export(const Object *object) const
return false;
}
-void USDHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer)
+void USDHierarchyIterator::release_writer(AbstractHierarchyWriter *writer)
{
delete static_cast<USDAbstractWriter *>(writer);
}
@@ -70,7 +73,7 @@ std::string USDHierarchyIterator::make_valid_name(const std::string &name) const
void USDHierarchyIterator::set_export_frame(float frame_nr)
{
- // The USD stage is already set up to have FPS timecodes per frame.
+ /* The USD stage is already set up to have FPS time-codes per frame. */
export_time_ = pxr::UsdTimeCode(frame_nr);
}
@@ -140,9 +143,12 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_hair_writer(const Hierarch
return new USDHairWriter(create_usd_export_context(context));
}
-AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(const HierarchyContext *)
+AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(
+ const HierarchyContext *UNUSED(context))
{
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..2836ff0b17f 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:
@@ -60,12 +66,14 @@ class USDHierarchyIterator : public AbstractHierarchyIterator {
virtual AbstractHierarchyWriter *create_particle_writer(
const HierarchyContext *context) override;
- virtual void delete_object_writer(AbstractHierarchyWriter *writer) override;
+ virtual void release_writer(AbstractHierarchyWriter *writer) override;
private:
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..0fd5c4ce727 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)
{
@@ -80,9 +82,11 @@ void USDHairWriter::do_write(HierarchyContext &context)
}
}
-bool USDHairWriter::check_is_animated(const HierarchyContext &) const
+bool USDHairWriter::check_is_animated(const HierarchyContext &UNUSED(context)) 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 841501bcf42..bd2c549e729 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)
{
@@ -335,14 +337,15 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context,
* which is why we always bind the first material to the entire mesh. See
* https://github.com/PixarAnimationStudios/USD/issues/542 for more info. */
bool mesh_material_bound = false;
- for (short mat_num = 0; mat_num < context.object->totcol; mat_num++) {
+ pxr::UsdShadeMaterialBindingAPI material_binding_api(usd_mesh.GetPrim());
+ for (int mat_num = 0; mat_num < context.object->totcol; mat_num++) {
Material *material = BKE_object_material_get(context.object, mat_num + 1);
if (material == nullptr) {
continue;
}
pxr::UsdShadeMaterial usd_material = ensure_usd_material(material);
- usd_material.Bind(usd_mesh.GetPrim());
+ material_binding_api.Bind(usd_material);
/* USD seems to support neither per-material nor per-face-group double-sidedness, so we just
* use the flag from the first non-empty material slot. */
@@ -378,9 +381,9 @@ void USDGenericMeshWriter::assign_materials(const HierarchyContext &context,
pxr::UsdShadeMaterial usd_material = ensure_usd_material(material);
pxr::TfToken material_name = usd_material.GetPath().GetNameToken();
- pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(usd_mesh);
- pxr::UsdGeomSubset usd_face_subset = api.CreateMaterialBindSubset(material_name, face_indices);
- usd_material.Bind(usd_face_subset.GetPrim());
+ pxr::UsdGeomSubset usd_face_subset = material_binding_api.CreateMaterialBindSubset(
+ material_name, face_indices);
+ pxr::UsdShadeMaterialBindingAPI(usd_face_subset.GetPrim()).Bind(usd_material);
}
}
@@ -483,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..49983115455 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)
{
@@ -56,7 +58,12 @@ bool USDTransformWriter::check_is_animated(const HierarchyContext &context) cons
* depsgraph whether this object instance has a time source. */
return true;
}
+ if (check_has_physics(context)) {
+ return true;
+ }
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/tests/gtests/usd/usd_stage_creation_test.cc b/source/blender/io/usd/tests/usd_stage_creation_test.cc
index b262e21f053..e6bd0bab6ce 100644
--- a/tests/gtests/usd/usd_stage_creation_test.cc
+++ b/source/blender/io/usd/tests/usd_stage_creation_test.cc
@@ -21,42 +21,50 @@
#include <string>
-extern "C" {
#include "BLI_path_util.h"
#include "BLI_utildefines.h"
#include "BKE_appdir.h"
+extern "C" {
/* Workaround to make it possible to pass a path at runtime to USD. See creator.c. */
void usd_initialise_plugin_path(const char *datafiles_usd_path);
}
-DEFINE_string(test_usd_datafiles_dir, "", "The bin/{BLENDER_VERSION}/datafiles/usd directory.");
+namespace blender::io::usd {
class USDStageCreationTest : public testing::Test {
};
TEST_F(USDStageCreationTest, JSONFileLoadingTest)
{
- if (FLAGS_test_usd_datafiles_dir.empty()) {
- FAIL() << "Pass the --test-usd-datafiles-dir flag";
+ const std::string &release_dir = blender::tests::flags_test_release_dir();
+ if (release_dir.empty()) {
+ FAIL();
}
- usd_initialise_plugin_path(FLAGS_test_usd_datafiles_dir.c_str());
+ char usd_datafiles_dir[FILE_MAX];
+ BLI_path_join(usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr);
+
+ usd_initialise_plugin_path(usd_datafiles_dir);
/* Simply the ability to create a USD Stage for a specific filename means that the extension
- * has been recognised by the USD library, and that a USD plugin has been loaded to write such
+ * has been recognized by the USD library, and that a USD plugin has been loaded to write such
* files. Practically, this is a test to see whether the USD JSON files can be found and
* loaded. */
std::string filename = "usd-stage-creation-test.usdc";
pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(filename);
if (usd_stage != nullptr) {
- /* Even though we don't call usd_stage->SaveFile(), a file is still created on the filesystem
- * when we call CreateNew(). It's immediately closed, though, so we can safely call unlink()
- * here. */
+ /* Even though we don't call `usd_stage->SaveFile()`, a file is still created on the
+ * file-system when we call CreateNew(). It's immediately closed, though,
+ * so we can safely call `unlink()` here. */
unlink(filename.c_str());
}
else {
- FAIL() << "unable to find suitable USD plugin to write " << filename;
+ FAIL() << "unable to find suitable USD plugin to write " << filename
+ << "; re-run with the environment variable PXR_PATH_DEBUG non-empty to see which paths "
+ "are considered.";
}
}
+
+} // namespace blender::io::usd
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 4f2bbc4ee73..4ec622574cc 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -206,7 +206,10 @@ typedef struct IDOverrideLibraryProperty {
/** Runtime, tags are common to both IDOverrideProperty and IDOverridePropertyOperation. */
short tag;
- char _pad0[6];
+ char _pad[2];
+
+ /** The property type matching the rna_path. */
+ unsigned int rna_prop_type;
} IDOverrideLibraryProperty;
/* IDOverrideProperty->tag and IDOverridePropertyOperation->tag. */
@@ -215,8 +218,18 @@ enum {
IDOVERRIDE_LIBRARY_TAG_UNUSED = 1 << 0,
};
-/* We do not need a full struct for that currently, just a GHash. */
-typedef struct GHash IDOverrideLibraryRuntime;
+#
+#
+typedef struct IDOverrideLibraryRuntime {
+ struct GHash *rna_path_to_override_properties;
+ uint tag;
+} IDOverrideLibraryRuntime;
+
+/* IDOverrideLibraryRuntime->tag. */
+enum {
+ /** This override needs to be reloaded. */
+ IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD = 1 << 0,
+};
/* Main container for all overriding data info of a data-block. */
typedef struct IDOverrideLibrary {
@@ -225,9 +238,6 @@ typedef struct IDOverrideLibrary {
/** List of IDOverrideProperty structs. */
ListBase properties;
- short flag;
- char _pad[6];
-
/* Read/write data. */
/* Temp ID storing extra override data (used for differential operations only currently).
* Always NULL outside of read/write context. */
@@ -236,10 +246,6 @@ typedef struct IDOverrideLibrary {
IDOverrideLibraryRuntime *runtime;
} IDOverrideLibrary;
-enum eOverrideLibrary_Flag {
- OVERRIDE_LIBRARY_AUTO = 1 << 0, /* Allow automatic generation of overriding rules. */
-};
-
/* watch it: Sequence has identical beginning. */
/**
* ID is the first thing included in all serializable types. It
@@ -309,16 +315,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;
@@ -455,37 +462,41 @@ 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)
/* 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 && \
+ (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 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)
-#define ID_IS_OVERRIDE_LIBRARY_AUTO(_id) \
- (!ID_IS_LINKED((_id)) && ID_IS_OVERRIDE_LIBRARY((_id)) && \
- (((ID *)(_id))->override_library->flag & OVERRIDE_LIBRARY_AUTO))
-
/* Check whether datablock type is covered by copy-on-write. */
#define ID_TYPE_IS_COW(_id_type) (!ELEM(_id_type, ID_BR, ID_PAL, ID_IM))
@@ -519,6 +530,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,
};
/**
@@ -680,6 +696,15 @@ typedef enum IDRecalcFlag {
* input file or for color space changes. */
ID_RECALC_SOURCE = (1 << 23),
+ /* Virtual recalc tag/marker required for undo in some cases, where actual data does not change
+ * and hence do not require an update, but conceptually we are dealing with something new.
+ *
+ * Current known case: linked IDs made local without requiring any copy. While their users do not
+ * require any update, they have actually been 'virtually' remapped from the linked ID to the
+ * local one.
+ */
+ ID_RECALC_TAG_FOR_UNDO = (1 << 24),
+
/***************************************************************************
* Pseudonyms, to have more semantic meaning in the actual code without
* using too much low-level and implementation specific tags. */
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..8b42f1a936d 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -118,7 +118,8 @@ typedef struct BrushGpencilSettings {
int sculpt_mode_flag;
/** Preset type (used to reset brushes - internal). */
short preset_type;
- char _pad3[2];
+ /** Brush preselected mode (Active/Material/Vertexcolor). */
+ short brush_draw_mode;
/** Randomness for Hue. */
float random_hue;
@@ -258,6 +259,13 @@ typedef enum eGP_BrushEraserMode {
GP_BRUSH_ERASER_STROKE = 2,
} eGP_BrushEraserMode;
+/* BrushGpencilSettings->brush_draw_mode */
+typedef enum eGP_BrushMode {
+ GP_BRUSH_MODE_ACTIVE = 0,
+ GP_BRUSH_MODE_MATERIAL = 1,
+ GP_BRUSH_MODE_VERTEXCOLOR = 2,
+} eGP_BrushMode;
+
/* BrushGpencilSettings default brush icons */
typedef enum eGP_BrushIcons {
GP_BRUSH_ICON_PENCIL = 1,
@@ -343,6 +351,18 @@ typedef enum eBrushPoseOriginType {
BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2,
} eBrushPoseOriginType;
+typedef enum eBrushSmearDeformType {
+ BRUSH_SMEAR_DEFORM_DRAG = 0,
+ BRUSH_SMEAR_DEFORM_PINCH = 1,
+ BRUSH_SMEAR_DEFORM_EXPAND = 2,
+} eBrushSmearDeformType;
+
+typedef enum eBrushSlideDeformType {
+ BRUSH_SLIDE_DEFORM_DRAG = 0,
+ BRUSH_SLIDE_DEFORM_PINCH = 1,
+ BRUSH_SLIDE_DEFORM_EXPAND = 2,
+} eBrushSlideDeformType;
+
/* Gpencilsettings.Vertex_mode */
typedef enum eGp_Vertex_Mode {
/* Affect to Stroke only. */
@@ -382,6 +402,19 @@ typedef enum eAutomasking_flag {
BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3),
} eAutomasking_flag;
+typedef enum ePaintBrush_flag {
+ BRUSH_PAINT_HARDNESS_PRESSURE = (1 << 0),
+ BRUSH_PAINT_HARDNESS_PRESSURE_INVERT = (1 << 1),
+ BRUSH_PAINT_FLOW_PRESSURE = (1 << 2),
+ BRUSH_PAINT_FLOW_PRESSURE_INVERT = (1 << 3),
+ BRUSH_PAINT_WET_MIX_PRESSURE = (1 << 4),
+ BRUSH_PAINT_WET_MIX_PRESSURE_INVERT = (1 << 5),
+ BRUSH_PAINT_WET_PERSISTENCE_PRESSURE = (1 << 6),
+ BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT = (1 << 7),
+ BRUSH_PAINT_DENSITY_PRESSURE = (1 << 8),
+ BRUSH_PAINT_DENSITY_PRESSURE_INVERT = (1 << 9),
+} ePaintBrush_flag;
+
typedef struct Brush {
ID id;
@@ -439,6 +472,22 @@ 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;
+ int paint_flags;
+
+ /** 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];
@@ -459,7 +508,7 @@ typedef struct Brush {
/** Source for fill tool color gradient application. */
char gradient_fill_mode;
- char _pad0[1];
+ char _pad0[5];
/** Projection shape (sphere, circle). */
char falloff_shape;
@@ -485,7 +534,7 @@ typedef struct Brush {
char gpencil_sculpt_tool;
/** Active grease pencil weight tool. */
char gpencil_weight_tool;
- char _pad1[2];
+ char _pad1[6];
float autosmooth_factor;
@@ -503,16 +552,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;
@@ -533,6 +580,8 @@ typedef struct Brush {
float cloth_sim_limit;
float cloth_sim_falloff;
+ float cloth_constraint_softbody_strength;
+
/* smooth */
int smooth_deform_type;
float surface_smooth_shape_preservation;
@@ -542,6 +591,12 @@ typedef struct Brush {
/* multiplane scrape */
float multiplane_scrape_angle;
+ /* smear */
+ int smear_deform_type;
+
+ /* slide/relax */
+ int slide_deform_type;
+
/* overlay */
int texture_overlay_alpha;
int mask_overlay_alpha;
@@ -670,6 +725,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 +776,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 +820,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_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h
index 8f3a26cf9c0..9d9ee711339 100644
--- a/source/blender/makesdna/DNA_cloth_types.h
+++ b/source/blender/makesdna/DNA_cloth_types.h
@@ -109,8 +109,11 @@ typedef struct ClothSimSettings {
pressure=( (current_volume/target_volume) - 1 + uniform_pressure_force) *
pressure_factor */
float pressure_factor;
+ /* Density of the fluid inside or outside the object for use in the hydrostatic pressure
+ * gradient. */
+ float fluid_density;
short vgroup_pressure;
- char _pad7[2];
+ char _pad7[6];
/* XXX various hair stuff
* should really be separate, this struct is a horrible mess already
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_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index b2902407a15..9f724973b6c 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -274,9 +274,12 @@ typedef struct Curve {
int selstart, selend;
/* text data */
- /** Number of characters (strinfo). */
- int len_wchar;
- /** Number of bytes (str - utf8). */
+ /**
+ * Number of characters (unicode code-points)
+ * This is the length of #Curve.strinfo and the result of `BLI_strlen_utf8(cu->str)`.
+ */
+ int len_char32;
+ /** Number of bytes: `strlen(Curve.str)`. */
int len;
char *str;
struct EditFont *editfont;
diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h
index 63e1049b636..7af8c3cf3ca 100644
--- a/source/blender/makesdna/DNA_curveprofile_types.h
+++ b/source/blender/makesdna/DNA_curveprofile_types.h
@@ -31,7 +31,7 @@
/** Number of table points per control point. */
#define PROF_RESOL 16
/** Dynamic size of widget's high resolution table. Input should be profile->totpoint. */
-#define PROF_N_TABLE(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1)
+#define PROF_TABLE_LEN(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1)
/**
* Each control point that makes up the profile.
@@ -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..7a9744ed1c7 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -76,7 +76,7 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we cant use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
- int typemap[48];
+ int typemap[50];
char _pad[4];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
@@ -148,15 +148,16 @@ 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,
+ CD_PROP_FLOAT3 = 48,
+ CD_PROP_FLOAT2 = 49,
- CD_NUMTYPES = 48,
+ CD_NUMTYPES = 50,
} CustomDataType;
/* Bits for CustomDataMask */
@@ -205,10 +206,14 @@ 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)
+#define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3)
+#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
/** 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)
+#define CD_MASK_GENERIC_DATA \
+ (CD_MASK_PROP_FLOAT | CD_MASK_PROP_INT32 | CD_MASK_PROP_STRING | CD_MASK_PROP_FLOAT3 | \
+ CD_MASK_PROP_FLOAT2)
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h
index e8a22d8c821..90440d9af8a 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;
@@ -405,7 +427,7 @@ typedef struct FluidDomainSettings {
/* -- Runtime-only fields (from here on). -- */
- struct FluidModifierData *mmd; /* For fast RNA access. */
+ struct FluidModifierData *fmd; /* For fast RNA access. */
struct MANTA *fluid;
struct MANTA *fluid_old; /* Adaptive domain needs access to old fluid state. */
void *fluid_mutex;
@@ -449,9 +471,10 @@ typedef struct FluidDomainSettings {
int res_max[3]; /* Cell max. */
int res[3]; /* Data resolution (res_max-res_min). */
int total_cells;
- float dx; /* 1.0f / res. */
- float scale; /* Largest domain size. */
- int boundary_width; /* Usually this is just 1. */
+ float dx; /* 1.0f / res. */
+ float scale; /* Largest domain size. */
+ int boundary_width; /* Usually this is just 1. */
+ float gravity_final[3]; /* Scene or domain gravity multiplied with gravity weight. */
/* -- User-accesible fields (from here on). -- */
@@ -459,7 +482,6 @@ typedef struct FluidDomainSettings {
int adapt_margin;
int adapt_res;
float adapt_threshold;
- char _pad1[4]; /* Unused. */
/* Fluid domain options */
int maxres; /* Longest axis on the BB gets this resolution assigned. */
@@ -502,8 +524,9 @@ typedef struct FluidDomainSettings {
float particle_band_width;
float fractions_threshold;
float flip_ratio;
+ int sys_particle_maximum;
short simulation_method;
- char _pad4[6];
+ char _pad4[2];
/* Diffusion options. */
float surface_tension;
@@ -558,6 +581,7 @@ typedef struct FluidDomainSettings {
int cache_frame_pause_mesh;
int cache_frame_pause_particles;
int cache_frame_pause_guide;
+ int cache_frame_offset;
int cache_flag;
char cache_mesh_format;
char cache_data_format;
@@ -566,7 +590,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[2];
/* Time options. */
float dt;
@@ -591,17 +616,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
@@ -665,7 +690,7 @@ typedef struct FluidFlowSettings {
/* -- Runtime-only fields (from here on). -- */
/* For fast RNA access. */
- struct FluidModifierData *mmd;
+ struct FluidModifierData *fmd;
struct Mesh *mesh;
struct ParticleSystem *psys;
struct Tex *noise_texture;
@@ -741,7 +766,7 @@ typedef struct FluidEffectorSettings {
/* -- Runtime-only fields (from here on). -- */
/* For fast RNA access. */
- struct FluidModifierData *mmd;
+ struct FluidModifierData *fmd;
struct Mesh *mesh;
float *verts_old;
int numverts;
diff --git a/source/blender/makesdna/DNA_genfile.h b/source/blender/makesdna/DNA_genfile.h
index db65da6fa75..6366412f0cf 100644
--- a/source/blender/makesdna/DNA_genfile.h
+++ b/source/blender/makesdna/DNA_genfile.h
@@ -29,6 +29,10 @@
struct SDNA;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* DNAstr contains the prebuilt SDNA structure defining the layouts of the types
* used by this version of Blender. It is defined in a file dna.c, which is
@@ -134,4 +138,8 @@ bool DNA_struct_alias_elem_find(const struct SDNA *sdna,
const char *name);
void DNA_sdna_alias_data_ensure_structs_map(struct SDNA *sdna);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __DNA_GENFILE_H__ */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 125423cd061..5d35db1a960 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -24,6 +24,8 @@
#include "DNA_defs.h"
#include "DNA_listBase.h"
+struct LatticeDeformData;
+
/* WARNING ALERT! TYPEDEF VALUES ARE WRITTEN IN FILES! SO DO NOT CHANGE!
* (ONLY ADD NEW ITEMS AT THE END)
*/
@@ -56,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 {
@@ -70,7 +72,7 @@ typedef struct GpencilModifierData {
int type, mode;
int stackindex;
short flag;
- short _pad;
+ short ui_expand_flag;
/** MAX_NAME. */
char name[64];
@@ -445,8 +447,8 @@ typedef struct LatticeGpencilModifierData {
float strength;
/** Custom index for passes. */
int layer_pass;
- /** Runtime only (LatticeDeformData). */
- void *cache_data;
+ /** Runtime only. */
+ struct LatticeDeformData *cache_data;
} LatticeGpencilModifierData;
typedef enum eLatticeGpencil_Flag {
@@ -655,12 +657,12 @@ typedef enum eSmoothGpencil_Flag {
typedef struct ArmatureGpencilModifierData {
GpencilModifierData modifier;
- /** Deformflag replaces armature->deformflag. */
+ /** #eArmature_DeformFlag use instead of #bArmature.deformflag. */
short deformflag, multi;
int _pad;
struct Object *object;
- /** Stored input of previous modifier, for vertexgroup blending. */
- float *prevCos;
+ /** Stored input of previous modifier, for vertex-group blending. */
+ float (*vert_coords_prev)[3];
/** MAX_VGROUP_NAME. */
char vgname[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..70512c5094b 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -117,24 +117,24 @@ typedef struct ImageTile {
#define IMA_NEED_FRAME_RECALC (1 << 3)
#define IMA_SHOW_STEREO (1 << 4)
-enum {
- TEXTARGET_TEXTURE_2D = 0,
- TEXTARGET_TEXTURE_CUBE_MAP = 1,
- TEXTARGET_TEXTURE_2D_ARRAY = 2,
- TEXTARGET_TEXTURE_TILE_MAPPING = 3,
- TEXTARGET_COUNT = 4,
-};
+/* Used to get the correct gpu texture from an Image datablock. */
+typedef enum eGPUTextureTarget {
+ TEXTARGET_2D = 0,
+ TEXTARGET_2D_ARRAY,
+ TEXTARGET_TILE_MAPPING,
+ TEXTARGET_COUNT,
+} eGPUTextureTarget;
typedef struct Image {
ID id;
/** File path, 1024 = FILE_MAX. */
- char name[1024];
+ char filepath[1024];
/** Not written in file. */
struct MovieCache *cache;
- /** Not written in file 4 = TEXTARGET_COUNT, 2 = stereo eyes. */
- struct GPUTexture *gputexture[4][2];
+ /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */
+ struct GPUTexture *gputexture[3][2];
/* sources from: */
ListBase anims;
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_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index 6348dc5f03d..cb604fd6681 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -115,7 +115,6 @@ typedef struct ViewLayer {
ListBase object_bases;
/** Default allocated now. */
struct SceneStats *stats;
- char footer_str[128];
struct Base *basact;
/** A view layer has one top level layer collection, because a scene has only one top level
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 a2b96ac2772..8c564bda3d0 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -155,6 +155,7 @@ typedef enum {
/* DEPRECATED, ONLY USED FOR DO-VERSIONS */
eSubsurfModifierFlag_SubsurfUv_DEPRECATED = (1 << 3),
eSubsurfModifierFlag_UseCrease = (1 << 4),
+ eSubsurfModifierFlag_UseCustomNormals = (1 << 5),
} SubsurfModifierFlag;
typedef enum {
@@ -394,6 +395,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 +410,9 @@ typedef struct BevelModifierData {
short miter_outer;
/** The method to use for creating >2-way intersections */
short vmesh_method;
+ /** Whether to affect vertices or edges. */
+ char affect_type;
+ char _pad;
/** Controls profile shape (0->1, .5 is round). */
float profile;
/** if the MOD_BEVEL_ANGLE is set,
@@ -417,6 +423,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;
@@ -424,17 +431,22 @@ typedef struct BevelModifierData {
/* BevelModifierData->flags and BevelModifierData->lim_flags */
enum {
- MOD_BEVEL_VERT = (1 << 1),
+#ifdef DNA_DEPRECATED_ALLOW
+ MOD_BEVEL_VERT_DEPRECATED = (1 << 1),
+#endif
MOD_BEVEL_INVERT_VGROUP = (1 << 2),
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 +458,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 */
@@ -475,6 +494,12 @@ enum {
MOD_BEVEL_VMESH_CUTOFF = 1,
};
+/* BevelModifier->affect_type */
+enum {
+ MOD_BEVEL_AFFECT_VERTICES = 0,
+ MOD_BEVEL_AFFECT_EDGES = 1,
+};
+
typedef struct FluidModifierData {
ModifierData modifier;
@@ -693,12 +718,12 @@ enum {
typedef struct ArmatureModifierData {
ModifierData modifier;
- /** Deformflag replaces armature->deformflag. */
+ /** #eArmature_DeformFlag use instead of #bArmature.deformflag. */
short deformflag, multi;
char _pad2[4];
struct Object *object;
- /** Stored input of previous modifier, for vertexgroup blending. */
- float *prevCos;
+ /** Stored input of previous modifier, for vertex-group blending. */
+ float (*vert_coords_prev)[3];
/** MAX_VGROUP_NAME. */
char defgrp_name[64];
} ArmatureModifierData;
@@ -1002,6 +1027,7 @@ typedef enum {
/* DEPRECATED, only used for versioning. */
eMultiresModifierFlag_PlainUv_DEPRECATED = (1 << 1),
eMultiresModifierFlag_UseCrease = (1 << 2),
+ eMultiresModifierFlag_UseCustomNormals = (1 << 3),
} MultiresModifierFlag;
/* DEPRECATED, only used for versioning. */
@@ -1288,6 +1314,7 @@ typedef struct OceanModifierData {
char cachepath[1024];
/** MAX_CUSTOMDATA_LAYER_NAME. */
char foamlayername[64];
+ char spraylayername[64];
char cached;
char geometry_mode;
@@ -1322,6 +1349,8 @@ enum {
enum {
MOD_OCEAN_GENERATE_FOAM = (1 << 0),
MOD_OCEAN_GENERATE_NORMALS = (1 << 1),
+ MOD_OCEAN_GENERATE_SPRAY = (1 << 2),
+ MOD_OCEAN_INVERT_SPRAY = (1 << 3),
};
typedef struct WarpModifierData {
@@ -2129,7 +2158,7 @@ typedef struct SimulationModifierData {
ModifierData modifier;
struct Simulation *simulation;
- char data_path[64];
+ char *data_path;
} SimulationModifierData;
#ifdef __cplusplus
diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h
index 94d11095108..063e5d8af09 100644
--- a/source/blender/makesdna/DNA_movieclip_types.h
+++ b/source/blender/makesdna/DNA_movieclip_types.h
@@ -64,8 +64,8 @@ typedef struct MovieClipProxy {
typedef struct MovieClip_RuntimeGPUTexture {
void *next, *prev;
MovieClipUser user;
- /** Not written in file 4 = TEXTARGET_COUNT. */
- struct GPUTexture *gputexture[4];
+ /** Not written in file 3 = TEXTARGET_COUNT. */
+ struct GPUTexture *gputexture[3];
} MovieClip_RuntimeGPUTexture;
typedef struct MovieClip_Runtime {
@@ -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..42ccbc657d8 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -844,6 +844,16 @@ typedef struct NodeTexSky {
float sun_direction[3];
float turbidity;
float ground_albedo;
+ float sun_size;
+ float sun_intensity;
+ float sun_elevation;
+ float sun_rotation;
+ float altitude;
+ float air_density;
+ float dust_density;
+ float ozone_density;
+ char sun_disc;
+ char _pad[7];
} NodeTexSky;
typedef struct NodeTexImage {
@@ -1171,8 +1181,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_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 47e6db835c9..9424100e1c8 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -564,7 +564,7 @@ enum {
/* for solid+wire display */
OB_DRAWWIRE = 1 << 5,
/* for overdraw s*/
- OB_DRAWXRAY = 1 << 6,
+ OB_DRAW_IN_FRONT = 1 << 6,
/* enable transparent draw */
OB_DRAWTRANSP = 1 << 7,
OB_DRAW_ALL_EDGES = 1 << 8, /* only for meshes currently */
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_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h
index fcdc22ca56d..3c7fc9031de 100644
--- a/source/blender/makesdna/DNA_pointcache_types.h
+++ b/source/blender/makesdna/DNA_pointcache_types.h
@@ -50,6 +50,7 @@ extern "C" {
#define BPHYS_TOT_DATA 8
#define BPHYS_EXTRA_FLUID_SPRINGS 1
+#define BPHYS_EXTRA_CLOTH_ACCELERATION 2
typedef struct PTCacheExtra {
struct PTCacheExtra *next, *prev;
diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h
index 7ad50dc04de..facf4c6636f 100644
--- a/source/blender/makesdna/DNA_rigidbody_types.h
+++ b/source/blender/makesdna/DNA_rigidbody_types.h
@@ -214,7 +214,7 @@ typedef enum eRigidBody_Shape {
RB_SHAPE_TRIMESH = 6,
/* concave mesh approximated using primitives */
- // RB_SHAPE_COMPOUND,
+ RB_SHAPE_COMPOUND = 7,
} eRigidBody_Shape;
typedef enum eRigidBody_MeshSource {
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..a3f5a95f231 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,14 @@ 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)
+/** Keep equal values merged while correcting custom-data. */
+#define UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED (1 << 5)
/* 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..bf5c097322f 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. */
@@ -68,6 +69,8 @@ typedef struct bScreen {
/** User-setting for which editors get redrawn during anim playback. */
short redraws_flag;
+ char statusbar_info[256];
+
/** Temp screen in a temp window, don't save (like user prefs). */
char temp;
/** Temp screen for image render display or fileselect. */
@@ -135,6 +138,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. */
@@ -157,9 +169,8 @@ typedef struct Panel {
/** Panel size excluding children. */
int blocksizex, blocksizey;
short labelofs;
- char _pad[2];
+ char _pad[4];
short flag, runtime_flag;
- short control;
short snap;
/** Panels are aligned according to increasing sort-order. */
int sortorder;
@@ -427,7 +438,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. */
@@ -529,8 +540,8 @@ enum {
PNL_CLOSEDX = (1 << 1),
PNL_CLOSEDY = (1 << 2),
PNL_CLOSED = (PNL_CLOSEDX | PNL_CLOSEDY),
- /*PNL_TABBED = (1 << 3), */ /*UNUSED*/
- PNL_OVERLAP = (1 << 4),
+ /* PNL_TABBED = (1 << 3), */ /*UNUSED*/
+ /* PNL_OVERLAP = (1 << 4), */ /*UNUSED*/
PNL_PIN = (1 << 5),
PNL_POPOVER = (1 << 6),
/** The panel has been drag-drop reordered and the instanced panel list needs to be rebuilt. */
@@ -609,7 +620,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 +636,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_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h
index 93ba9c425f0..afc59b422c5 100644
--- a/source/blender/makesdna/DNA_simulation_types.h
+++ b/source/blender/makesdna/DNA_simulation_types.h
@@ -30,50 +30,69 @@ typedef struct Simulation {
struct bNodeTree *nodetree;
- int flag;
- int _pad;
+ uint32_t flag;
+
+ /** This is the frame in scene time, that the states correspond to. */
+ float current_frame;
+
+ /** Time since the start of the simulation in simulation time (which might differ from scene
+ * time). */
+ float current_simulation_time;
+ char _pad[4];
/** List containing SimulationState objects. */
struct ListBase states;
+
+ /** List containing SimulationDependency objects. */
+ struct ListBase dependencies;
} Simulation;
typedef struct SimulationState {
struct SimulationState *next;
struct SimulationState *prev;
- /** This is only initialized on cow copies of the simulation. It points to the state on the
- * original data block. That is where the cache is stored. */
- struct SimulationState *orig_state;
-
- /** eSimulationStateType */
- int type;
- int _pad;
-
- char name[64];
+ char *type;
+ char *name;
} SimulationState;
typedef struct ParticleSimulationState {
SimulationState head;
- /** Contains the state of the particles at time current_frame. */
- float current_frame;
- int tot_particles;
+ /** Contains the state of the particles at time Simulation->current_frame. */
+ int32_t tot_particles;
+ int32_t next_particle_id;
struct CustomData attributes;
-
- /** Caches the state of the particles over time. The cache only exists on the original data
- * block, not on cow copies. */
- struct PointCache *point_cache;
- struct ListBase ptcaches;
} ParticleSimulationState;
+typedef struct ParticleMeshEmitterSimulationState {
+ SimulationState head;
+
+ float last_birth_time;
+ char _pad[4];
+} ParticleMeshEmitterSimulationState;
+
+/** Stores a reference to data that the simulation depends on. This is partially derived from the
+ * simulation node tree. */
+typedef struct SimulationDependency {
+ struct SimulationDependency *next;
+ struct SimulationDependency *prev;
+ struct ID *id;
+ int32_t handle;
+ uint32_t flag;
+} SimulationDependency;
+
/* Simulation.flag */
enum {
SIM_DS_EXPAND = (1 << 0),
};
-/* SimulationCache.type */
-typedef enum eSimulationStateType {
- SIM_STATE_TYPE_PARTICLES = 0,
-} eSimulationStateType;
+/* SimulationDependency.flag */
+enum {
+ SIM_DEPENDS_ON_TRANSFORM = (1 << 0),
+ SIM_DEPENDS_ON_GEOMETRY = (1 << 1),
+};
+
+#define SIM_TYPE_NAME_PARTICLE_SIMULATION "Particle Simulation"
+#define SIM_TYPE_NAME_PARTICLE_MESH_EMITTER "Particle Mesh Emitter"
#endif /* __DNA_SIMULATION_TYPES_H__ */
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 31a8d967ccf..0892eff6de9 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -1148,8 +1148,9 @@ typedef enum eSpaceImage_Flag {
SI_FLAG_UNUSED_17 = (1 << 17), /* cleared */
SI_FLAG_UNUSED_18 = (1 << 18), /* cleared */
- /* this means that the image is drawn until it reaches the view edge,
- * in the image view, it's unrelated to the 'tile' mode for texface
+ /**
+ * This means that the image is drawn until it reaches the view edge,
+ * in the image view, it's unrelated to UDIM tiles.
*/
SI_DRAW_TILE = (1 << 19),
SI_SMOOTH_UV = (1 << 20),
@@ -1220,7 +1221,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..d50c0055499 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -619,8 +619,12 @@ 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;
+ char use_sculpt_vertex_colors;
/** `makesdna` does not allow empty structs. */
- char _pad0[7];
+ char _pad[3];
} UserDef_Experimental;
#define USER_EXPERIMENTAL_TEST(userdef, member) \
@@ -633,12 +637,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];
@@ -657,9 +661,9 @@ typedef struct UserDef {
char anim_player[1024];
int anim_player_preset;
- /** Minimum spacing between gridlines in View2D grids. */
+ /** Minimum spacing between grid-lines in View2D grids. */
short v2d_min_gridsize;
- /** #eTimecodeStyles, style of timecode display. */
+ /** #eTimecodeStyles, style of time-code display. */
short timecode_style;
short versions;
@@ -690,7 +694,7 @@ typedef struct UserDef {
float ui_scale;
/** Setting for UI line width. */
int ui_line_width;
- /** Runtime, full DPI divided by pixelsize. */
+ /** Runtime, full DPI divided by `pixelsize`. */
int dpi;
/** Runtime, multiplier to scale UI elements based on DPI. */
float dpi_fac;
@@ -700,7 +704,7 @@ typedef struct UserDef {
/** Deprecated, for forward compatibility. */
int virtual_pixel;
- /** Console scrollback limit. */
+ /** Console scroll-back limit. */
int scrollback;
/** Node insert offset (aka auto-offset) margin, but might be useful for later stuff as well. */
char node_margin;
@@ -729,7 +733,7 @@ typedef struct UserDef {
char _pad1[2];
int undomemory;
float gpu_viewport_quality DNA_DEPRECATED;
- short gp_manhattendist, gp_euclideandist, gp_eraser;
+ short gp_manhattandist, gp_euclideandist, gp_eraser;
/** #eGP_UserdefSettings. */
short gp_settings;
char _pad13[4];
@@ -876,7 +880,9 @@ typedef struct UserDef {
char _pad5[2];
float collection_instance_empty_size;
- char _pad10[4];
+ char _pad10[3];
+
+ char statusbar_flag; /* eUserpref_StatusBar_Flag */
struct WalkNavigation walk_navigation;
@@ -1075,6 +1081,14 @@ typedef enum eUserpref_APP_Flag {
USER_APP_LOCK_UI_LAYOUT = (1 << 0),
} eUserpref_APP_Flag;
+/** #UserDef.statusbar_flag */
+typedef enum eUserpref_StatusBar_Flag {
+ STATUSBAR_SHOW_MEMORY = (1 << 0),
+ STATUSBAR_SHOW_VRAM = (1 << 1),
+ STATUSBAR_SHOW_STATS = (1 << 2),
+ STATUSBAR_SHOW_VERSION = (1 << 3),
+} eUserpref_StatusBar_Flag;
+
/**
* Auto-Keying mode.
* #UserDef.autokey_mode
@@ -1150,6 +1164,15 @@ 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. */
+
+ /* Duplicate (and hence make local) linked data. */
+ USER_DUP_LINKED_ID = (1 << 30),
} 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/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index ef174f5858f..4bb32fedd1d 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -191,6 +191,7 @@ typedef struct View3DShading {
int render_pass;
struct IDProperty *prop;
+ void *_pad2;
} View3DShading;
/** 3D Viewport Overlay settings. */
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index e1e8d808c60..a07b8f81d96 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -265,7 +265,7 @@ typedef struct wmWindow {
short modalcursor;
/** Cursor grab mode. */
short grabcursor;
- /** Internal: tag this for extra mousemove event,
+ /** Internal: tag this for extra mouse-move event,
* makes cursors/buttons active on UI switching. */
char addmousemove;
char tag_cursor_refresh;
diff --git a/source/blender/makesdna/DNA_world_types.h b/source/blender/makesdna/DNA_world_types.h
index f9198194a89..dff07f515b1 100644
--- a/source/blender/makesdna/DNA_world_types.h
+++ b/source/blender/makesdna/DNA_world_types.h
@@ -51,12 +51,10 @@ typedef struct World {
float horr, horg, horb;
/**
- * Exposure= mult factor. unused now, but maybe back later. Kept in to be upward compat.
- * New is exp/range control. linfac & logfac are constants... don't belong in
- * file, but allocating 8 bytes for temp mem isn't useful either.
+ * Exposure is a multiplication factor. Unused now, but maybe back later.
+ * Kept in to be upward compatible.
*/
float exposure, exp, range;
- float linfac, logfac;
/**
* Some world modes
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index 01e3971a216..737ea9a7e12 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -40,6 +40,7 @@ set(SRC
../../blenlib/intern/BLI_memarena.c
../../blenlib/intern/BLI_mempool.c
../../blenlib/intern/hash_mm2a.c # needed by 'BLI_ghash_utils.c', not used directly.
+ ../../../../intern/guardedalloc/intern/leak_detector.cc
../../../../intern/guardedalloc/intern/mallocn.c
../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -96,6 +97,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..a73fc747f84 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -67,6 +67,7 @@ DNA_STRUCT_RENAME_ELEM(Bone, scaleOut, scale_out_x)
DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness)
DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio)
DNA_STRUCT_RENAME_ELEM(Camera, YF_dofdist, dof_distance)
+DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32)
DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end)
DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start)
DNA_STRUCT_RENAME_ELEM(Collection, dupli_ofs, instance_offset)
@@ -77,6 +78,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 +88,11 @@ 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(UserDef, gp_manhattendist, gp_manhattandist)
+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 +107,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/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c
index 6708365a4c8..3cf5c52a4c6 100644
--- a/source/blender/makesdna/intern/dna_utils.c
+++ b/source/blender/makesdna/intern/dna_utils.c
@@ -232,6 +232,23 @@ void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash
for (int i = 0; i < ARRAY_SIZE(data); i++) {
BLI_ghash_insert(struct_map, (void *)data[i][elem_key], (void *)data[i][elem_val]);
}
+
+ if (version_dir == DNA_RENAME_STATIC_FROM_ALIAS) {
+ const char *renames[][2] = {
+ /* Disable 'int8_t' until we support 'signed char', since changing negative
+ * values to a different type isn't supported and will change the value. */
+ /* {"int8_t", "char"}, */
+ {"uint8_t", "uchar"},
+ {"int16_t", "short"},
+ {"uint16_t", "ushort"},
+ {"int32_t", "int"},
+ {"uint32_t", "int"},
+ };
+ for (int i = 0; i < ARRAY_SIZE(renames); i++) {
+ BLI_ghash_insert(struct_map, (void *)renames[i][0], (void *)renames[i][1]);
+ }
+ }
+
*r_struct_map = struct_map;
/* We know the direction of this, for local use. */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 083806350e7..48258f39604 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);
@@ -360,7 +360,7 @@ static int add_type(const char *str, int size)
}
else if (strchr(str, '*')) {
/* note: this is valid C syntax but we can't parse, complain!
- * 'struct SomeStruct* somevar;' <-- correct but we cant handle right now. */
+ * `struct SomeStruct* some_var;` <-- correct but we cant handle right now. */
return -1;
}
@@ -1530,12 +1530,21 @@ int main(int argc, char **argv)
#endif /* if 0 */
-/* even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit,
- * use int or int64_t instead.
+/**
+ * Disable types:
+ *
+ * - 'long': even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit,
+ * use int, int32_t or int64_t instead.
+ * - 'int8_t': as DNA doesn't yet support 'signed char' types,
+ * all char types are assumed to be unsigned.
+ * We should be able to support this, it's just not something which has been added yet.
+ *
* Only valid use would be as a runtime variable if an API expected a long,
- * but so far we dont have this happening. */
+ * but so far we don't have this happening.
+ */
#ifdef __GNUC__
# pragma GCC poison long
+# pragma GCC poison int8_t
#endif
#include "DNA_ID.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 65c43ebc151..6acd9d16f80 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -387,6 +387,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;
@@ -819,12 +821,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 */
@@ -1465,6 +1467,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);
@@ -1481,15 +1484,24 @@ void RNA_struct_override_apply(struct Main *bmain,
struct PointerRNA *ptr_storage,
struct IDOverrideLibrary *override);
-struct IDOverrideLibraryProperty *RNA_property_override_property_find(PointerRNA *ptr,
- PropertyRNA *prop);
-struct IDOverrideLibraryProperty *RNA_property_override_property_get(PointerRNA *ptr,
+struct IDOverrideLibraryProperty *RNA_property_override_property_find(struct Main *bmain,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ struct ID **r_owner_id);
+struct IDOverrideLibraryProperty *RNA_property_override_property_get(struct Main *bmain,
+ PointerRNA *ptr,
PropertyRNA *prop,
bool *r_created);
struct IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_find(
- PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict);
+ struct Main *bmain,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ const int index,
+ const bool strict,
+ bool *r_strict);
struct IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get(
+ struct Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
const short operation,
@@ -1498,7 +1510,8 @@ struct IDOverrideLibraryPropertyOperation *RNA_property_override_property_operat
bool *r_strict,
bool *r_created);
-eRNAOverrideStatus RNA_property_override_library_status(PointerRNA *ptr,
+eRNAOverrideStatus RNA_property_override_library_status(struct Main *bmainm,
+ PointerRNA *ptr,
PropertyRNA *prop,
const int index);
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index b6bae805636..0c462ba6766 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -225,7 +225,6 @@ extern const EnumPropertyItem rna_enum_dt_mix_mode_items[];
extern const EnumPropertyItem rna_enum_dt_layers_select_src_items[];
extern const EnumPropertyItem rna_enum_dt_layers_select_dst_items[];
-extern const EnumPropertyItem rna_enum_abc_compression_items[];
extern const EnumPropertyItem rna_enum_context_mode_items[];
extern const EnumPropertyItem rna_enum_curveprofile_preset_items[];
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 024bdbe5ed5..0b43a5a6653 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -47,6 +47,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
@@ -68,6 +69,7 @@ set(DEFSRC
rna_packedfile.c
rna_palette.c
rna_particle.c
+ rna_pointcloud.c
rna_pose.c
rna_render.c
rna_rigidbody.c
@@ -77,6 +79,7 @@ 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
@@ -96,20 +99,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
@@ -162,6 +151,16 @@ endif()
unset(GENSRC_CFLAGS)
+# NOTE: Disable clang-tidy because generated files are stored outside of the source,
+# so the clang-tidy can not find our .clang-tidy and fall-backs to own set of rules
+# which are too noisy for Blender.
+#
+# In the future clang-tidy would either need to be inlined checks and passed via the
+# command line (instead of using .clang-tidy file). Or, maybe, there is a way to
+# pass configuration file to the clang-tidy command.
+unset(CMAKE_C_CLANG_TIDY)
+unset(CMAKE_CXX_CLANG_TIDY)
+
set(SRC_RNA_INC
../RNA_access.h
../RNA_define.h
@@ -176,6 +175,7 @@ set(SRC
${DEFSRC}
${APISRC}
../../../../intern/clog/clog.c
+ ../../../../intern/guardedalloc/intern/leak_detector.cc
../../../../intern/guardedalloc/intern/mallocn.c
../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -345,15 +345,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(
.
@@ -370,7 +361,7 @@ blender_include_dirs(
../../imbuf
../../makesdna
../../nodes/
- ../../physics
+ ../../simulation
../../windowmanager
../../editors/include
../../render/extern/include
@@ -443,3 +434,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 2960cea7f53..045d098bf6a 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -96,8 +96,9 @@ static void rna_generate_static_parameter_prototypes(FILE *f,
/* helpers */
#define WRITE_COMMA \
{ \
- if (!first) \
+ if (!first) { \
fprintf(f, ", "); \
+ } \
first = 0; \
} \
(void)0
@@ -118,10 +119,12 @@ static int replace_if_different(const char *tmpfile, const char *dep_files[])
FILE *file_test = fopen(orgfile, "rb"); \
if (file_test) { \
fclose(file_test); \
- if (fp_org) \
+ if (fp_org) { \
fclose(fp_org); \
- if (fp_new) \
+ } \
+ if (fp_new) { \
fclose(fp_new); \
+ } \
if (remove(orgfile) != 0) { \
CLOG_ERROR(&LOG, "remove error (%s): \"%s\"", strerror(errno), orgfile); \
return -1; \
@@ -1080,21 +1083,26 @@ static char *rna_def_property_set_func(
if (prop->flag & PROP_ID_SELF_CHECK) {
rna_print_id_get(f, dp);
- fprintf(f, " if (id == value.data) return;\n\n");
+ fprintf(f, " if (id == value.data) {\n");
+ fprintf(f, " return;\n");
+ fprintf(f, " }\n");
}
if (prop->flag & PROP_ID_REFCOUNT) {
- fprintf(f, "\n if (data->%s)\n", dp->dnaname);
+ fprintf(f, "\n if (data->%s) {\n", dp->dnaname);
fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname);
- fprintf(f, " if (value.data)\n");
- fprintf(f, " id_us_plus((ID *)value.data);\n\n");
+ fprintf(f, " }\n");
+ fprintf(f, " if (value.data) {\n");
+ fprintf(f, " id_us_plus((ID *)value.data);\n");
+ fprintf(f, " }\n");
}
else {
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : NULL;
if (type && (type->flag & STRUCT_ID)) {
- fprintf(f, " if (value.data)\n");
- fprintf(f, " id_lib_extern((ID *)value.data);\n\n");
+ fprintf(f, " if (value.data) {\n");
+ fprintf(f, " id_lib_extern((ID *)value.data);\n");
+ fprintf(f, " }\n");
}
}
@@ -1141,14 +1149,14 @@ static char *rna_def_property_set_func(
if (dp->dnaarraylength == 1) {
if (prop->type == PROP_BOOLEAN && dp->booleanbit) {
fprintf(f,
- " if (%svalues[i]) data->%s |= (",
+ " if (%svalues[i]) { data->%s |= (",
(dp->booleannegative) ? "!" : "",
dp->dnaname);
rna_int_print(f, dp->booleanbit);
- fprintf(f, " << i);\n");
- fprintf(f, " else data->%s &= ~(", dp->dnaname);
+ fprintf(f, " << i); }\n");
+ fprintf(f, " else { data->%s &= ~(", dp->dnaname);
rna_int_print(f, dp->booleanbit);
- fprintf(f, " << i);\n");
+ fprintf(f, " << i); }\n");
}
else {
fprintf(
@@ -1159,14 +1167,14 @@ static char *rna_def_property_set_func(
else {
if (prop->type == PROP_BOOLEAN && dp->booleanbit) {
fprintf(f,
- " if (%svalues[i]) data->%s[i] |= ",
+ " if (%svalues[i]) { data->%s[i] |= ",
(dp->booleannegative) ? "!" : "",
dp->dnaname);
rna_int_print(f, dp->booleanbit);
- fprintf(f, ";\n");
- fprintf(f, " else data->%s[i] &= ~", dp->dnaname);
+ fprintf(f, "; }\n");
+ fprintf(f, " else { data->%s[i] &= ~", dp->dnaname);
rna_int_print(f, dp->booleanbit);
- fprintf(f, ";\n");
+ fprintf(f, "; }\n");
}
else if (rna_color_quantize(prop, dp)) {
fprintf(
@@ -1215,13 +1223,15 @@ static char *rna_def_property_set_func(
else {
rna_print_data_get(f, dp);
if (prop->type == PROP_BOOLEAN && dp->booleanbit) {
- fprintf(
- f, " if (%svalue) data->%s |= ", (dp->booleannegative) ? "!" : "", dp->dnaname);
+ fprintf(f,
+ " if (%svalue) { data->%s |= ",
+ (dp->booleannegative) ? "!" : "",
+ dp->dnaname);
rna_int_print(f, dp->booleanbit);
- fprintf(f, ";\n");
- fprintf(f, " else data->%s &= ~", dp->dnaname);
+ fprintf(f, "; }\n");
+ fprintf(f, " else { data->%s &= ~", dp->dnaname);
rna_int_print(f, dp->booleanbit);
- fprintf(f, ";\n");
+ fprintf(f, "; }\n");
}
else if (prop->type == PROP_ENUM && dp->enumbitflags) {
fprintf(f, " data->%s &= ~", dp->dnaname);
@@ -1278,7 +1288,7 @@ static char *rna_def_property_length_func(
else {
rna_print_data_get(f, dp);
if (!(prop->flag & PROP_NEVER_NULL)) {
- fprintf(f, " if (data->%s == NULL) return 0;\n", dp->dnaname);
+ fprintf(f, " if (data->%s == NULL) { return 0; }\n", dp->dnaname);
}
fprintf(f, " return strlen(data->%s);\n", dp->dnaname);
}
@@ -1393,8 +1403,9 @@ static char *rna_def_property_begin_func(
getfunc = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "get");
- fprintf(f, "\n if (iter->valid)\n");
- fprintf(f, " iter->ptr = %s(iter);\n", getfunc);
+ fprintf(f, "\n if (iter->valid) {\n");
+ fprintf(f, " iter->ptr = %s(iter);", getfunc);
+ fprintf(f, "\n }\n");
fprintf(f, "}\n\n");
@@ -1479,14 +1490,15 @@ static char *rna_def_property_lookup_int_func(FILE *f,
fprintf(f, " found = (index == -1 && iter.valid);\n");
fprintf(f, " }\n");
fprintf(f, " else {\n");
- fprintf(f, " while (index-- > 0 && internal->link)\n");
+ fprintf(f, " while (index-- > 0 && internal->link) {\n");
fprintf(f, " internal->link = internal->link->next;\n");
+ fprintf(f, " }\n");
fprintf(f, " found = (index == -1 && internal->link);\n");
fprintf(f, " }\n");
}
fprintf(f,
- " if (found) *r_ptr = %s_%s_get(&iter);\n",
+ " if (found) { *r_ptr = %s_%s_get(&iter); }\n",
srna->identifier,
rna_safe_id(prop->identifier));
fprintf(f, " }\n\n");
@@ -1672,8 +1684,9 @@ static char *rna_def_property_next_func(FILE *f,
getfunc = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "get");
- fprintf(f, "\n if (iter->valid)\n");
- fprintf(f, " iter->ptr = %s(iter);\n", getfunc);
+ fprintf(f, "\n if (iter->valid) {\n");
+ fprintf(f, " iter->ptr = %s(iter);", getfunc);
+ fprintf(f, "\n }\n");
fprintf(f, "}\n\n");
@@ -3770,7 +3783,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 +3795,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;
@@ -4269,9 +4284,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},
@@ -4294,9 +4307,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},
@@ -4306,9 +4317,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},
@@ -5135,7 +5144,10 @@ static void mem_error_cb(const char *errorStr)
int main(int argc, char **argv)
{
- int totblock, return_status = 0;
+ int return_status = 0;
+
+ MEM_initialize_memleak_detection();
+ MEM_set_error_callback(mem_error_cb);
CLG_init();
@@ -5157,12 +5169,5 @@ int main(int argc, char **argv)
CLG_exit();
- totblock = MEM_get_memory_blocks_in_use();
- if (totblock != 0) {
- fprintf(stderr, "Error Totblock: %d\n", totblock);
- MEM_set_error_callback(mem_error_cb);
- MEM_printmemlist();
- }
-
return return_status;
}
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index e0d862ee92a..e9ca0d577ce 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", ""},
@@ -100,6 +96,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
# include "BKE_font.h"
# include "BKE_global.h" /* XXX, remove me */
# include "BKE_idprop.h"
+# include "BKE_idtype.h"
# include "BKE_lib_override.h"
# include "BKE_lib_query.h"
# include "BKE_lib_remap.h"
@@ -254,11 +251,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;
}
@@ -292,11 +287,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;
}
@@ -306,11 +299,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;
}
@@ -364,11 +355,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:
@@ -402,11 +389,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:
@@ -414,11 +397,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:
@@ -540,7 +519,7 @@ static ID *rna_ID_copy(ID *id, Main *bmain)
static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
{
- if (!BKE_lib_override_library_is_enabled() || !ID_IS_OVERRIDABLE_LIBRARY(id)) {
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
return NULL;
}
@@ -1421,7 +1400,6 @@ static void rna_def_ID_override_library_property(BlenderRNA *brna)
static void rna_def_ID_override_library(BlenderRNA *brna)
{
StructRNA *srna;
- PropertyRNA *prop;
srna = RNA_def_struct(brna, "IDOverrideLibrary", NULL);
RNA_def_struct_ui_text(
@@ -1430,14 +1408,6 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
RNA_def_pointer(
srna, "reference", "ID", "Reference ID", "Linked ID used as reference by this override");
- prop = RNA_def_boolean(
- srna,
- "auto_generate",
- true,
- "Auto Generate Override",
- "Automatically generate overriding operations by detecting changes in properties");
- RNA_def_property_boolean_sdna(prop, NULL, "flag", OVERRIDE_LIBRARY_AUTO);
-
RNA_def_collection(srna,
"properties",
"IDOverrideLibraryProperty",
@@ -1554,6 +1524,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);
/* functions */
@@ -1665,7 +1636,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..793552c5c34 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);
@@ -558,9 +558,15 @@ static PropertyRNA *arraytypemap[IDP_NUMTYPES] = {
(PropertyRNA *)&rna_PropertyGroupItem_double_array,
};
-static void *rna_idproperty_check_ex(PropertyRNA **prop,
- PointerRNA *ptr,
- const bool return_rnaprop)
+/* This function initializes a PropertyRNAOrID with all required info, from a given PropertyRNA
+ * and PointerRNA data. It deals properly with the three cases (static RNA, runtime RNA, and
+ * IDProperty).
+ * WARNING: given `ptr` PointerRNA is assumed to be a valid data one here, calling code is
+ * responsible to ensure that.
+ */
+void rna_property_rna_or_id_get(PropertyRNA *prop,
+ PointerRNA *ptr,
+ PropertyRNAOrID *r_prop_rna_or_id)
{
/* This is quite a hack, but avoids some complexity in the API. we
* pass IDProperty structs as PropertyRNA pointers to the outside.
@@ -568,36 +574,64 @@ static void *rna_idproperty_check_ex(PropertyRNA **prop,
* distinguish it from IDProperty structs. If it is an ID property,
* we look up an IDP PropertyRNA based on the type, and set the data
* pointer to the IDProperty. */
+ memset(r_prop_rna_or_id, 0, sizeof(*r_prop_rna_or_id));
+
+ r_prop_rna_or_id->ptr = *ptr;
+ r_prop_rna_or_id->rawprop = prop;
- if ((*prop)->magic == RNA_MAGIC) {
- if ((*prop)->flag & PROP_IDPROPERTY) {
- IDProperty *idprop = rna_idproperty_find(ptr, (*prop)->identifier);
+ if (prop->magic == RNA_MAGIC) {
+ r_prop_rna_or_id->rnaprop = prop;
+ r_prop_rna_or_id->identifier = prop->identifier;
- if (idprop && !rna_idproperty_verify_valid(ptr, *prop, idprop)) {
+ r_prop_rna_or_id->is_array = prop->getlength || prop->totarraylength;
+ if (r_prop_rna_or_id->is_array) {
+ int arraylen[RNA_MAX_ARRAY_DIMENSION];
+ r_prop_rna_or_id->array_len = (prop->getlength && ptr->data) ?
+ (uint)prop->getlength(ptr, arraylen) :
+ prop->totarraylength;
+ }
+
+ if (prop->flag & PROP_IDPROPERTY) {
+ IDProperty *idprop = rna_idproperty_find(ptr, prop->identifier);
+
+ if (idprop != NULL && !rna_idproperty_verify_valid(ptr, prop, idprop)) {
IDProperty *group = RNA_struct_idprops(ptr, 0);
IDP_FreeFromGroup(group, idprop);
- return NULL;
+ idprop = NULL;
}
- return idprop;
+ r_prop_rna_or_id->idprop = idprop;
+ r_prop_rna_or_id->is_set = idprop != NULL && (idprop->flag & IDP_FLAG_GHOST) == 0;
}
else {
- return return_rnaprop ? *prop : NULL;
+ /* Full static RNA properties are always set. */
+ r_prop_rna_or_id->is_set = true;
}
}
+ else {
+ IDProperty *idprop = (IDProperty *)prop;
+ /* Given prop may come from the custom properties of another data, ensure we get the one from
+ * given data ptr. */
+ IDProperty *idprop_evaluated = rna_idproperty_find(ptr, idprop->name);
+ if (idprop_evaluated != NULL && idprop->type != idprop_evaluated->type) {
+ idprop_evaluated = NULL;
+ }
- {
- IDProperty *idprop = (IDProperty *)(*prop);
+ r_prop_rna_or_id->idprop = idprop_evaluated;
+ r_prop_rna_or_id->is_idprop = true;
+ /* Full IDProperties are always set, if it exists. */
+ r_prop_rna_or_id->is_set = (idprop_evaluated != NULL);
+ r_prop_rna_or_id->identifier = idprop->name;
if (idprop->type == IDP_ARRAY) {
- *prop = arraytypemap[(int)(idprop->subtype)];
+ r_prop_rna_or_id->rnaprop = arraytypemap[(int)(idprop->subtype)];
+ r_prop_rna_or_id->is_array = true;
+ r_prop_rna_or_id->array_len = idprop_evaluated != NULL ? (uint)idprop_evaluated->len : 0;
}
else {
- *prop = typemap[(int)(idprop->type)];
+ r_prop_rna_or_id->rnaprop = typemap[(int)(idprop->type)];
}
-
- return idprop;
}
}
@@ -605,14 +639,26 @@ static void *rna_idproperty_check_ex(PropertyRNA **prop,
* or NULL (in case IDProp could not be found, or prop is a real RNA property). */
IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr)
{
- return rna_idproperty_check_ex(prop, ptr, false);
+ PropertyRNAOrID prop_rna_or_id;
+
+ rna_property_rna_or_id_get(*prop, ptr, &prop_rna_or_id);
+
+ *prop = prop_rna_or_id.rnaprop;
+ return prop_rna_or_id.idprop;
}
/* This function always return the valid, real data pointer, be it a regular RNA property one,
* or an IDProperty one. */
PropertyRNA *rna_ensure_property_realdata(PropertyRNA **prop, PointerRNA *ptr)
{
- return rna_idproperty_check_ex(prop, ptr, true);
+ PropertyRNAOrID prop_rna_or_id;
+
+ rna_property_rna_or_id_get(*prop, ptr, &prop_rna_or_id);
+
+ *prop = prop_rna_or_id.rnaprop;
+ return (prop_rna_or_id.is_idprop || prop_rna_or_id.idprop != NULL) ?
+ (PropertyRNA *)prop_rna_or_id.idprop :
+ prop_rna_or_id.rnaprop;
}
PropertyRNA *rna_ensure_property(PropertyRNA *prop)
@@ -645,7 +691,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 +700,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 +1186,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 +1358,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 +1399,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 +1449,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 +1494,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 +2071,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 +2110,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,10 +2147,8 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char *
}
return false;
}
- if (id->override_library != NULL) {
- /* 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)) {
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ if (!RNA_property_overridable_get(ptr, prop)) {
if (!(*r_info)[0]) {
*r_info = N_("Can't edit this property from an override data-block");
}
@@ -2847,7 +2891,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 +2927,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 +3264,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 +3300,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 +3517,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 +3569,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 +5458,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 +5484,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..0a739dcfc5a 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"
@@ -58,7 +61,7 @@ bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop)
/* Special handling for insertions of constraints or modifiers... */
/* TODO Note We may want to add a more generic system to RNA
* (like a special property in struct of items)
- * if we get more override-able collections,
+ * if we get more overrideable collections,
* for now we can live with those special-cases handling I think. */
if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
bConstraint *con = ptr->data;
@@ -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;
}
@@ -174,15 +177,13 @@ bool RNA_property_copy(
}
static int rna_property_override_diff(Main *bmain,
- PointerRNA *ptr_a,
- PointerRNA *ptr_b,
- PropertyRNA *prop,
- PropertyRNA *prop_a,
- PropertyRNA *prop_b,
+ PropertyRNAOrID *prop_a,
+ PropertyRNAOrID *prop_b,
const char *rna_path,
+ const size_t rna_path_len,
eRNACompareMode mode,
IDOverrideLibrary *override,
- const int flags,
+ const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags);
bool RNA_property_equals(
@@ -190,8 +191,12 @@ 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);
+ PropertyRNAOrID prop_a, prop_b;
+
+ rna_property_rna_or_id_get(prop, ptr_a, &prop_a);
+ rna_property_rna_or_id_get(prop, ptr_b, &prop_b);
+
+ return (rna_property_override_diff(bmain, &prop_a, &prop_b, NULL, 0, mode, NULL, 0, NULL) == 0);
}
bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode)
@@ -243,58 +248,42 @@ bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACo
* but we cannot determine an order (greater than/lesser than), we return 1.
*/
static int rna_property_override_diff(Main *bmain,
- PointerRNA *ptr_a,
- PointerRNA *ptr_b,
- PropertyRNA *prop,
- PropertyRNA *prop_a,
- PropertyRNA *prop_b,
+ PropertyRNAOrID *prop_a,
+ PropertyRNAOrID *prop_b,
const char *rna_path,
+ const size_t rna_path_len,
eRNACompareMode mode,
IDOverrideLibrary *override,
- const int flags,
+ const eRNAOverrideMatch flags,
eRNAOverrideMatchResult *r_report_flags)
{
- if (prop != NULL) {
- BLI_assert(prop_a == NULL && prop_b == NULL);
- prop_a = prop;
- prop_b = prop;
- }
+ BLI_assert(!ELEM(NULL, prop_a, prop_b));
- if (ELEM(NULL, prop_a, prop_b)) {
- return (prop_a == prop_b) ? 0 : 1;
- }
-
- if (!RNA_property_comparable(ptr_a, prop_a) || !RNA_property_comparable(ptr_b, prop_b)) {
+ if (prop_a->rnaprop->flag_override & PROPOVERRIDE_NO_COMPARISON ||
+ prop_b->rnaprop->flag_override & PROPOVERRIDE_NO_COMPARISON) {
return 0;
}
if (mode == RNA_EQ_UNSET_MATCH_ANY) {
- /* uninitialized properties are assumed to match anything */
- if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) {
+ /* Unset properties are assumed to match anything. */
+ if (!prop_a->is_set || !prop_b->is_set) {
return 0;
}
}
else if (mode == RNA_EQ_UNSET_MATCH_NONE) {
- /* unset properties never match set properties */
- if (RNA_property_is_set(ptr_a, prop_a) != RNA_property_is_set(ptr_b, prop_b)) {
+ /* Unset properties never match set properties. */
+ if (prop_a->is_set != prop_b->is_set) {
return 1;
}
}
- if (prop != NULL) {
- /* Ensure we get real property data, be it an actual RNA property,
- * or an IDProperty in disguise. */
- prop_a = rna_ensure_property_realdata(&prop_a, ptr_a);
- prop_b = rna_ensure_property_realdata(&prop_b, ptr_b);
-
- if (ELEM(NULL, prop_a, prop_b)) {
- return (prop_a == prop_b) ? 0 : 1;
- }
+ if (prop_a->is_idprop && ELEM(NULL, prop_a->idprop, prop_b->idprop)) {
+ return (prop_a->idprop == prop_b->idprop) ? 0 : 1;
}
/* Check if we are working with arrays. */
- const bool is_array_a = RNA_property_array_check(prop_a);
- const bool is_array_b = RNA_property_array_check(prop_b);
+ const bool is_array_a = prop_a->is_array;
+ const bool is_array_b = prop_b->is_array;
if (is_array_a != is_array_b) {
/* Should probably never happen actually... */
@@ -303,8 +292,8 @@ static int rna_property_override_diff(Main *bmain,
}
/* Get the length of the array to work with. */
- const int len_a = RNA_property_array_length(ptr_a, prop_a);
- const int len_b = RNA_property_array_length(ptr_b, prop_b);
+ const uint len_a = prop_a->array_len;
+ const uint len_b = prop_b->array_len;
if (len_a != len_b) {
/* Do not handle override in that case,
@@ -319,50 +308,48 @@ static int rna_property_override_diff(Main *bmain,
RNAPropOverrideDiff override_diff = NULL;
/* Special case for IDProps, we use default callback then. */
- if (prop_a->magic != RNA_MAGIC) {
+ if (prop_a->is_idprop) {
override_diff = rna_property_override_diff_default;
- if (prop_b->magic == RNA_MAGIC && prop_b->override_diff != override_diff) {
+ if (!prop_b->is_idprop && prop_b->rnaprop->override_diff != override_diff) {
override_diff = NULL;
}
}
- else if (prop_b->magic != RNA_MAGIC) {
+ else if (prop_b->is_idprop) {
override_diff = rna_property_override_diff_default;
- if (prop_a->override_diff != override_diff) {
+ if (prop_a->rnaprop->override_diff != override_diff) {
override_diff = NULL;
}
}
- else if (prop_a->override_diff == prop_b->override_diff) {
- override_diff = prop_a->override_diff;
+ else if (prop_a->rnaprop->override_diff == prop_b->rnaprop->override_diff) {
+ override_diff = prop_a->rnaprop->override_diff;
+ if (override_diff == NULL) {
+ override_diff = rna_property_override_diff_default;
+ }
}
if (override_diff == NULL) {
#ifndef NDEBUG
printf("'%s' gives unmatching or NULL RNA diff callbacks, should not happen (%d vs. %d).\n",
- rna_path ?
- rna_path :
- (prop_a->magic != RNA_MAGIC ? ((IDProperty *)prop_a)->name : prop_a->identifier),
- prop_a->magic == RNA_MAGIC,
- prop_b->magic == RNA_MAGIC);
+ rna_path ? rna_path : prop_a->identifier,
+ !prop_a->is_idprop,
+ !prop_b->is_idprop);
#endif
BLI_assert(0);
return 1;
}
bool override_changed = false;
- int diff_flags = flags;
- if (!RNA_property_overridable_get(ptr_a, prop_a)) {
+ eRNAOverrideMatch diff_flags = flags;
+ if (!RNA_property_overridable_get(&prop_a->ptr, prop_a->rawprop)) {
diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE;
}
const int diff = override_diff(bmain,
- ptr_a,
- ptr_b,
prop_a,
prop_b,
- len_a,
- len_b,
mode,
override,
rna_path,
+ rna_path_len,
diff_flags,
&override_changed);
if (override_changed && r_report_flags) {
@@ -420,10 +407,13 @@ static bool rna_property_override_operation_store(Main *bmain,
}
else if (prop_local->override_store == prop_reference->override_store) {
override_store = prop_local->override_store;
+ if (override_store == NULL) {
+ override_store = rna_property_override_store_default;
+ }
}
if (ptr_storage != NULL && prop_storage->magic == RNA_MAGIC &&
- prop_storage->override_store != override_store) {
+ !ELEM(prop_storage->override_store, NULL, override_store)) {
override_store = NULL;
}
@@ -506,10 +496,13 @@ static bool rna_property_override_operation_apply(Main *bmain,
}
else if (prop_dst->override_apply == prop_src->override_apply) {
override_apply = prop_dst->override_apply;
+ if (override_apply == NULL) {
+ override_apply = rna_property_override_apply_default;
+ }
}
if (ptr_storage && prop_storage->magic == RNA_MAGIC &&
- prop_storage->override_apply != override_apply) {
+ !ELEM(prop_storage->override_apply, NULL, override_apply)) {
override_apply = NULL;
}
@@ -568,6 +561,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)
@@ -605,38 +599,29 @@ bool RNA_struct_override_matches(Main *bmain,
for (RNA_property_collection_begin(ptr_local, iterprop, &iter); iter.valid;
RNA_property_collection_next(&iter)) {
- PropertyRNA *prop_local = iter.ptr.data;
- PropertyRNA *prop_reference = iter.ptr.data;
-
- /* Ensure we get real property data, be it an actual RNA property,
- * or an IDProperty in disguise. */
- prop_local = rna_ensure_property_realdata(&prop_local, ptr_local);
- prop_reference = rna_ensure_property_realdata(&prop_reference, ptr_reference);
-
- /* IDProps (custom properties) are even more of a PITA here, we cannot use
- * `rna_ensure_property_realdata()` to deal with them, we have to use the path generated from
- * `prop_local` (which is valid) to access to the actual reference counterpart... */
- if (prop_local != NULL && prop_local->magic != RNA_MAGIC && prop_local == prop_reference) {
- /* We could also use (lower in this code, after rna_path has been computed):
- * RNA_path_resolve_property(ptr_reference, rna_path, &some_rna_ptr, &prop_reference);
- * But that would be much more costly, and would also fail when ptr_reference
- * is not an ID pointer itself, so we'd need to rebuild it from its owner_id, then check that
- * generated some_rna_ptr and ptr_reference do point to the same data, etc.
- * For now, let's try that simple access, it won't cover all cases but should handle fine
- * most basic custom properties situations. */
- prop_reference = (PropertyRNA *)rna_idproperty_find(ptr_reference,
- ((IDProperty *)prop_local)->name);
- }
+ PropertyRNA *rawprop = iter.ptr.data;
+
+ PropertyRNAOrID prop_local;
+ PropertyRNAOrID prop_reference;
+
+ rna_property_rna_or_id_get(rawprop, ptr_local, &prop_local);
+ rna_property_rna_or_id_get(rawprop, ptr_reference, &prop_reference);
+
+ BLI_assert(prop_local.rnaprop != NULL);
+ BLI_assert(prop_local.rnaprop == prop_reference.rnaprop);
+ BLI_assert(prop_local.is_idprop == prop_reference.is_idprop);
- if (ELEM(NULL, prop_local, prop_reference)) {
+ if ((prop_local.is_idprop && prop_local.idprop == NULL) ||
+ (prop_reference.is_idprop && prop_reference.idprop == NULL)) {
continue;
}
- if (ignore_non_overridable && !RNA_property_overridable_get(ptr_local, prop_local)) {
+ if (ignore_non_overridable && !RNA_property_overridable_get(&prop_local.ptr, rawprop)) {
continue;
}
- if (RNA_property_override_flag(prop_local) & PROPOVERRIDE_IGNORE) {
+ if (!prop_local.is_idprop &&
+ RNA_property_override_flag(prop_local.rnaprop) & PROPOVERRIDE_IGNORE) {
continue;
}
@@ -649,31 +634,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 = prop_local.identifier;
+ 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));
+ if (!prop_local.is_idprop) {
+ 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);
+ rna_path = RNA_path_from_ID_to_property(ptr_local, rawprop);
+ if (rna_path != NULL) {
+ rna_path_len = strlen(rna_path);
+ }
}
if (rna_path == NULL) {
continue;
@@ -684,7 +689,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;
}
@@ -696,12 +704,10 @@ bool RNA_struct_override_matches(Main *bmain,
eRNAOverrideMatchResult report_flags = 0;
const int diff = rna_property_override_diff(bmain,
- ptr_local,
- ptr_reference,
- NULL,
- prop_local,
- prop_reference,
+ &prop_local,
+ &prop_reference,
rna_path,
+ rna_path_len,
RNA_EQ_STRICT,
override,
flags,
@@ -733,7 +739,7 @@ bool RNA_struct_override_matches(Main *bmain,
/* We are allowed to restore to reference's values. */
if (ELEM(NULL, op, opop) || opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
/* We should restore that property to its reference value */
- if (RNA_property_editable(ptr_local, prop_local)) {
+ if (RNA_property_editable(ptr_local, rawprop)) {
IDOverrideLibraryPropertyOperation opop_tmp = {
.operation = IDOVERRIDE_LIBRARY_OP_REPLACE,
.subitem_reference_index = -1,
@@ -743,8 +749,8 @@ bool RNA_struct_override_matches(Main *bmain,
ptr_local,
ptr_reference,
NULL,
- prop_local,
- prop_reference,
+ rawprop,
+ rawprop,
NULL,
NULL,
NULL,
@@ -769,17 +775,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,36 +1052,87 @@ 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(Main *bmain,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ ID **r_id)
{
ID *id = ptr->owner_id;
+ ID *owner_id = id;
+ const char *rna_path_prefix = NULL;
- if (!id || !id->override_library) {
+ *r_id = NULL;
+
+ if (id == NULL) {
+ return NULL;
+ }
+
+ 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:
+ case ID_NT:
+ /* Master collections, Root node trees. */
+ owner_id = RNA_find_real_ID_and_path(bmain, id, &rna_path_prefix);
+ 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(Main *bmain,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ ID **r_owner_id)
+{
+ char *rna_path;
+
+ if ((rna_path = rna_property_override_property_real_id_owner(bmain, 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;
}
return NULL;
}
-IDOverrideLibraryProperty *RNA_property_override_property_get(PointerRNA *ptr,
+IDOverrideLibraryProperty *RNA_property_override_property_get(Main *bmain,
+ PointerRNA *ptr,
PropertyRNA *prop,
bool *r_created)
{
- ID *id = ptr->owner_id;
+ ID *id;
+ char *rna_path;
- if (!id || !id->override_library) {
- return NULL;
- }
-
- char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
- if (rna_path) {
+ if ((rna_path = rna_property_override_property_real_id_owner(bmain, ptr, prop, &id)) != NULL) {
IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
id->override_library, rna_path, r_created);
MEM_freeN(rna_path);
@@ -1084,9 +1142,15 @@ 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)
+ Main *bmain,
+ 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(bmain, ptr, prop, &owner_id);
if (!op) {
return NULL;
@@ -1097,6 +1161,7 @@ IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_fin
}
IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get(
+ Main *bmain,
PointerRNA *ptr,
PropertyRNA *prop,
const short operation,
@@ -1105,7 +1170,7 @@ IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get
bool *r_strict,
bool *r_created)
{
- IDOverrideLibraryProperty *op = RNA_property_override_property_get(ptr, prop, NULL);
+ IDOverrideLibraryProperty *op = RNA_property_override_property_get(bmain, ptr, prop, NULL);
if (!op) {
return NULL;
@@ -1115,17 +1180,14 @@ IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get
op, operation, NULL, NULL, index, index, strict, r_strict, r_created);
}
-eRNAOverrideStatus RNA_property_override_library_status(PointerRNA *ptr,
+eRNAOverrideStatus RNA_property_override_library_status(Main *bmain,
+ PointerRNA *ptr,
PropertyRNA *prop,
const int index)
{
- int override_status = 0;
-
- if (!BKE_lib_override_library_is_enabled()) {
- return override_status;
- }
+ uint override_status = 0;
- 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;
}
@@ -1134,7 +1196,7 @@ eRNAOverrideStatus RNA_property_override_library_status(PointerRNA *ptr,
}
IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_find(
- ptr, prop, index, false, NULL);
+ bmain, ptr, prop, index, false, NULL);
if (opop != NULL) {
override_status |= RNA_OVERRIDE_STATUS_OVERRIDDEN;
if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_MANDATORY) {
diff --git a/source/blender/makesrna/intern/rna_access_internal.h b/source/blender/makesrna/intern/rna_access_internal.h
index c7995746d08..a5b554ec7a6 100644
--- a/source/blender/makesrna/intern/rna_access_internal.h
+++ b/source/blender/makesrna/intern/rna_access_internal.h
@@ -26,8 +26,11 @@
#include "rna_internal_types.h"
struct IDProperty;
+struct PropertyRNAOrID;
-PropertyRNA *rna_ensure_property(PropertyRNA *prop);
+void rna_property_rna_or_id_get(PropertyRNA *prop,
+ PointerRNA *ptr,
+ PropertyRNAOrID *r_prop_rna_or_id);
void rna_idproperty_touch(struct IDProperty *idprop);
struct IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name);
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..00ce5488636 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},
};
@@ -241,6 +243,12 @@ static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = {
{GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Edit Lines", "Use edit lines as fill boundary limits"},
{0, NULL, 0, NULL, NULL}};
+static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = {
+ {GP_BRUSH_MODE_ACTIVE, "ACTIVE", 0, "Active", "Use current mode"},
+ {GP_BRUSH_MODE_MATERIAL, "MATERIAL", 0, "Material", "Use always material mode"},
+ {GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"},
+ {0, NULL, 0, NULL, NULL}};
+
static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = {
{GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""},
{GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""},
@@ -314,7 +322,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)
@@ -356,7 +365,7 @@ static bool rna_BrushCapabilities_has_overlay_get(PointerRNA *ptr)
static bool rna_BrushCapabilitiesSculpt_has_persistence_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
- return br->sculpt_tool == SCULPT_TOOL_LAYER;
+ return ELEM(br->sculpt_tool, SCULPT_TOOL_LAYER, SCULPT_TOOL_CLOTH);
}
static bool rna_BrushCapabilitiesSculpt_has_pinch_factor_get(PointerRNA *ptr)
@@ -409,6 +418,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 +1068,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");
@@ -1630,6 +1646,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Mode", "Mode to draw boundary limits");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ prop = RNA_def_property(srna, "brush_draw_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "brush_draw_mode");
+ RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_modes_items);
+ RNA_def_property_ui_text(prop, "Mode", "Preselected mode when using this brush");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
prop = RNA_def_property(srna, "trim", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_TRIM_STROKE);
RNA_def_property_boolean_default(prop, false);
@@ -1987,6 +2009,20 @@ static void rna_def_brush(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem brush_smear_deform_type_items[] = {
+ {BRUSH_SMEAR_DEFORM_DRAG, "DRAG", 0, "Drag", ""},
+ {BRUSH_SMEAR_DEFORM_PINCH, "PINCH", 0, "Pinch", ""},
+ {BRUSH_SMEAR_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem brush_slide_deform_type_items[] = {
+ {BRUSH_SLIDE_DEFORM_DRAG, "DRAG", 0, "Drag", ""},
+ {BRUSH_SLIDE_DEFORM_PINCH, "PINCH", 0, "Pinch", ""},
+ {BRUSH_SLIDE_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "Brush", "ID");
RNA_def_struct_ui_text(
srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting");
@@ -2107,6 +2143,16 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "smear_deform_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, brush_smear_deform_type_items);
+ RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "slide_deform_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, brush_slide_deform_type_items);
+ RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "pose_deform_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_pose_deform_type_items);
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
@@ -2221,6 +2267,124 @@ 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, "use_hardness_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_HARDNESS_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure for Hardness", "Use pressure to modulate hardness");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "invert_hardness_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_HARDNESS_PRESSURE_INVERT);
+ RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
+ RNA_def_property_ui_text(
+ prop, "Invert Pressure for Hardness", "Invert the modulation of pressure in hardness");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "use_flow_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_FLOW_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure for Flow", "Use pressure to modulate flow");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "invert_flow_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_FLOW_PRESSURE_INVERT);
+ RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
+ RNA_def_property_ui_text(
+ prop, "Invert Pressure for Flow", "Invert the modulation of pressure in flow");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "use_wet_mix_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_WET_MIX_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure for Wet Mix", "Use pressure to modulate wet mix");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "invert_wet_mix_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_WET_MIX_PRESSURE_INVERT);
+ RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
+ RNA_def_property_ui_text(
+ prop, "Invert Pressure for Wet Mix", "Invert the modulation of pressure in wet mix");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "use_wet_persistence_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_WET_PERSISTENCE_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(
+ prop, "Use Pressure for Wet Persistence", "Use pressure to modulate wet persistence");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "invert_wet_persistence_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "paint_flags", BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT);
+ RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
+ RNA_def_property_ui_text(prop,
+ "Invert Pressure for Wet Persistence",
+ "Invert the modulation of pressure in wet persistence");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "use_density_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_DENSITY_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure for Density", "Use pressure to modulate density");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "invert_density_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "paint_flags", BRUSH_PAINT_DENSITY_PRESSURE_INVERT);
+ RNA_def_property_ui_icon(prop, ICON_ARROW_LEFTRIGHT, 0);
+ RNA_def_property_ui_text(
+ prop, "Invert Pressure for Density", "Invert the modulation of pressure in density");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ 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 +2476,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);
@@ -2396,6 +2568,15 @@ static void rna_def_brush(BlenderRNA *brna)
"Area to apply deformation falloff to the effects of the simulation");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "cloth_constraint_softbody_strength", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "cloth_constraint_softbody_strength");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(
+ prop,
+ "Soft Body Influence",
+ "How much the simulation preserves the original shape, acting as a soft body");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "hardness", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "hardness");
RNA_def_property_range(prop, 0.0f, 1.0f);
@@ -2607,6 +2788,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_camera.c b/source/blender/makesrna/intern/rna_camera.c
index 79ee9619e36..31a71a80bbc 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -264,7 +264,7 @@ static void rna_def_camera_background_image(BlenderRNA *brna)
prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "alpha");
RNA_def_property_ui_text(
- prop, "Alpha", "Image opacity to blend the image against the background color");
+ prop, "Opacity", "Image opacity to blend the image against the background color");
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c
index 70f219259ef..e99bd531c65 100644
--- a/source/blender/makesrna/intern/rna_cloth.c
+++ b/source/blender/makesrna/intern/rna_cloth.c
@@ -34,7 +34,7 @@
#include "BKE_cloth.h"
#include "BKE_modifier.h"
-#include "BPH_mass_spring.h"
+#include "SIM_mass_spring.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -482,18 +482,18 @@ static void rna_def_cloth_solver_result(BlenderRNA *brna)
PropertyRNA *prop;
static const EnumPropertyItem status_items[] = {
- {BPH_SOLVER_SUCCESS, "SUCCESS", 0, "Success", "Computation was successful"},
- {BPH_SOLVER_NUMERICAL_ISSUE,
+ {SIM_SOLVER_SUCCESS, "SUCCESS", 0, "Success", "Computation was successful"},
+ {SIM_SOLVER_NUMERICAL_ISSUE,
"NUMERICAL_ISSUE",
0,
"Numerical Issue",
"The provided data did not satisfy the prerequisites"},
- {BPH_SOLVER_NO_CONVERGENCE,
+ {SIM_SOLVER_NO_CONVERGENCE,
"NO_CONVERGENCE",
0,
"No Convergence",
"Iterative procedure did not converge"},
- {BPH_SOLVER_INVALID_INPUT,
+ {SIM_SOLVER_INVALID_INPUT,
"INVALID_INPUT",
0,
"Invalid Input",
@@ -975,8 +975,10 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_pressure_volume", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", CLOTH_SIMSETTINGS_FLAG_PRESSURE_VOL);
- RNA_def_property_ui_text(
- prop, "Use Custom Volume", "Use the Volume parameter as the initial volume");
+ RNA_def_property_ui_text(prop,
+ "Use Custom Volume",
+ "Use the Target Volume parameter as the initial volume, instead "
+ "of calculating it from the mesh itself");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_cloth_update");
@@ -984,10 +986,10 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "uniform_pressure_force");
RNA_def_property_range(prop, -10000.0f, 10000.0f);
RNA_def_property_float_default(prop, 0.0f);
- RNA_def_property_ui_text(
- prop,
- "Pressure",
- "The uniform pressure that is constantly applied to the mesh. Can be negative");
+ RNA_def_property_ui_text(prop,
+ "Pressure",
+ "The uniform pressure that is constantly applied to the mesh, in units "
+ "of Pressure Scale. Can be negative");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "target_volume", PROP_FLOAT, PROP_NONE);
@@ -997,14 +999,28 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Target Volume",
"The mesh volume where the inner/outer pressure will be the same. If "
- "set to zero the volume will not contribute to the total pressure");
+ "set to zero the change in volume will not affect pressure");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "pressure_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "pressure_factor");
RNA_def_property_range(prop, 0.0f, 10000.0f);
RNA_def_property_float_default(prop, 1.0f);
- RNA_def_property_ui_text(prop, "Pressure Scale", "Air pressure scaling factor");
+ RNA_def_property_ui_text(prop,
+ "Pressure Scale",
+ "Ambient pressure (kPa) that balances out between the inside and "
+ "outside of the object when it has the target volume");
+ RNA_def_property_update(prop, 0, "rna_cloth_update");
+
+ prop = RNA_def_property(srna, "fluid_density", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fluid_density");
+ RNA_def_property_ui_range(prop, -2.0f, 2.0f, 0.05f, 4);
+ RNA_def_property_ui_text(
+ prop,
+ "Fluid Density",
+ "Density (kg/l) of the fluid contained inside the object, used to create "
+ "a hydrostatic pressure gradient simulating the weight of the internal fluid, "
+ "or buoyancy from the surrounding fluid if negative");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "vertex_group_pressure", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c
index fbc2b871026..9c6c95f2819 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -86,7 +86,7 @@ static void rna_Collection_objects_link(Collection *collection,
if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
BKE_reportf(reports,
RPT_ERROR,
- "Could not link the object '%s' because the collection '%s' is overridden.",
+ "Could not link the object '%s' because the collection '%s' is overridden",
object->id.name + 2,
collection->id.name + 2);
return;
@@ -94,7 +94,7 @@ static void rna_Collection_objects_link(Collection *collection,
if (ID_IS_LINKED(&collection->id)) {
BKE_reportf(reports,
RPT_ERROR,
- "Could not link the object '%s' because the collection '%s' is linked.",
+ "Could not link the object '%s' because the collection '%s' is linked",
object->id.name + 2,
collection->id.name + 2);
return;
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 5405cb4e24a..d9dd35c4280 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -1228,17 +1228,17 @@ static void rna_def_constraint_kinematic(BlenderRNA *brna)
prop = RNA_def_property(srna, "lock_rotation_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_IK_NO_ROT_X);
- RNA_def_property_ui_text(prop, "Lock X Rot", "Constraint rotation along X axis");
+ RNA_def_property_ui_text(prop, "Lock X Rotation", "Constraint rotation along X axis");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Constraint_dependency_update");
prop = RNA_def_property(srna, "lock_rotation_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_IK_NO_ROT_Y);
- RNA_def_property_ui_text(prop, "Lock Y Rot", "Constraint rotation along Y axis");
+ RNA_def_property_ui_text(prop, "Lock Y Rotation", "Constraint rotation along Y axis");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Constraint_dependency_update");
prop = RNA_def_property(srna, "lock_rotation_z", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_IK_NO_ROT_Z);
- RNA_def_property_ui_text(prop, "Lock Z Rot", "Constraint rotation along Z axis");
+ RNA_def_property_ui_text(prop, "Lock Z Rotation", "Constraint rotation along Z axis");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Constraint_dependency_update");
prop = RNA_def_property(srna, "use_stretch", PROP_BOOLEAN, PROP_NONE);
@@ -2063,8 +2063,8 @@ static void rna_def_constraint_transform(BlenderRNA *brna)
PropertyRNA *prop;
static const EnumPropertyItem transform_items[] = {
- {TRANS_LOCATION, "LOCATION", 0, "Loc", ""},
- {TRANS_ROTATION, "ROTATION", 0, "Rot", ""},
+ {TRANS_LOCATION, "LOCATION", 0, "Location", ""},
+ {TRANS_ROTATION, "ROTATION", 0, "Rotation", ""},
{TRANS_SCALE, "SCALE", 0, "Scale", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -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);
@@ -3036,32 +3036,32 @@ static void rna_def_constraint_pivot(BlenderRNA *brna)
{PIVOTCON_AXIS_X_NEG,
"NX",
0,
- "-X Rot",
+ "-X Rotation",
"Use the pivot point in the negative rotation range around the X-axis"},
{PIVOTCON_AXIS_Y_NEG,
"NY",
0,
- "-Y Rot",
+ "-Y Rotation",
"Use the pivot point in the negative rotation range around the Y-axis"},
{PIVOTCON_AXIS_Z_NEG,
"NZ",
0,
- "-Z Rot",
+ "-Z Rotation",
"Use the pivot point in the negative rotation range around the Z-axis"},
{PIVOTCON_AXIS_X,
"X",
0,
- "X Rot",
+ "X Rotation",
"Use the pivot point in the positive rotation range around the X-axis"},
{PIVOTCON_AXIS_Y,
"Y",
0,
- "Y Rot",
+ "Y Rotation",
"Use the pivot point in the positive rotation range around the Y-axis"},
{PIVOTCON_AXIS_Z,
"Z",
0,
- "Z Rot",
+ "Z Rotation",
"Use the pivot point in the positive rotation range around the Z-axis"},
{0, NULL, 0, NULL, NULL},
};
@@ -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..8a3186ea7fe 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -570,7 +570,7 @@ static void rna_Curve_body_set(PointerRNA *ptr, const char *value)
Curve *cu = (Curve *)ptr->owner_id;
- cu->len_wchar = len_chars;
+ cu->len_char32 = len_chars;
cu->len = len_bytes;
cu->pos = len_chars;
@@ -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) {
@@ -1205,7 +1206,7 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna)
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
prop = RNA_def_property(srna, "body_format", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_wchar");
+ RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_char32");
RNA_def_property_struct_type(prop, "TextCharacterFormat");
RNA_def_property_ui_text(prop, "Character Info", "Stores the style of each character");
@@ -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_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c
index ca34f69ab1e..da1ed166eb2 100644
--- a/source/blender/makesrna/intern/rna_depsgraph.c
+++ b/source/blender/makesrna/intern/rna_depsgraph.c
@@ -551,7 +551,7 @@ static void rna_def_depsgraph_instance(BlenderRNA *brna)
prop,
"Persistent ID",
"Persistent identifier for inter-frame matching of objects with motion blur");
- RNA_def_property_array(prop, 2 * MAX_DUPLI_RECUR);
+ RNA_def_property_array(prop, MAX_DUPLI_RECUR);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_DepsgraphObjectInstance_persistent_id_get", NULL, NULL);
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..ab8f97ae3c2 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -77,7 +77,7 @@ static void rna_Fluid_datacache_reset(Main *UNUSED(bmain), Scene *UNUSED(scene),
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- if (settings->mmd && settings->mmd->domain) {
+ if (settings->fmd && settings->fmd->domain) {
Object *ob = (Object *)ptr->owner_id;
int cache_map = (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE |
FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES);
@@ -90,7 +90,7 @@ static void rna_Fluid_noisecache_reset(Main *UNUSED(bmain), Scene *UNUSED(scene)
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- if (settings->mmd && settings->mmd->domain) {
+ if (settings->fmd && settings->fmd->domain) {
Object *ob = (Object *)ptr->owner_id;
int cache_map = FLUID_DOMAIN_OUTDATED_NOISE;
BKE_fluid_cache_free(settings, ob, cache_map);
@@ -102,7 +102,7 @@ static void rna_Fluid_meshcache_reset(Main *UNUSED(bmain), Scene *UNUSED(scene),
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- if (settings->mmd && settings->mmd->domain) {
+ if (settings->fmd && settings->fmd->domain) {
Object *ob = (Object *)ptr->owner_id;
int cache_map = FLUID_DOMAIN_OUTDATED_MESH;
BKE_fluid_cache_free(settings, ob, cache_map);
@@ -116,7 +116,7 @@ static void rna_Fluid_particlescache_reset(Main *UNUSED(bmain),
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- if (settings->mmd && settings->mmd->domain) {
+ if (settings->fmd && settings->fmd->domain) {
Object *ob = (Object *)ptr->owner_id;
int cache_map = FLUID_DOMAIN_OUTDATED_PARTICLES;
BKE_fluid_cache_free(settings, ob, cache_map);
@@ -130,7 +130,7 @@ static void rna_Fluid_guidingcache_reset(Main *UNUSED(bmain),
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- if (settings->mmd && settings->mmd->domain) {
+ if (settings->fmd && settings->fmd->domain) {
Object *ob = (Object *)ptr->owner_id;
int cache_map = (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE |
FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES |
@@ -161,22 +161,55 @@ static void rna_Fluid_flow_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
rna_Fluid_update(bmain, scene, ptr);
}
-static void rna_Fluid_domain_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
+static void rna_Fluid_domain_data_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- BKE_fluid_modifier_reset(settings->mmd);
+ BKE_fluid_modifier_reset(settings->fmd);
# endif
rna_Fluid_datacache_reset(bmain, scene, ptr);
rna_Fluid_update(bmain, scene, ptr);
}
+static void rna_Fluid_domain_noise_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+# ifdef WITH_FLUID
+ FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
+ BKE_fluid_modifier_reset(settings->fmd);
+# endif
+
+ rna_Fluid_noisecache_reset(bmain, scene, ptr);
+ rna_Fluid_update(bmain, scene, ptr);
+}
+
+static void rna_Fluid_domain_mesh_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+# ifdef WITH_FLUID
+ FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
+ BKE_fluid_modifier_reset(settings->fmd);
+# endif
+
+ rna_Fluid_meshcache_reset(bmain, scene, ptr);
+ rna_Fluid_update(bmain, scene, ptr);
+}
+
+static void rna_Fluid_domain_particles_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+# ifdef WITH_FLUID
+ FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
+ BKE_fluid_modifier_reset(settings->fmd);
+# endif
+
+ rna_Fluid_particlescache_reset(bmain, scene, ptr);
+ rna_Fluid_update(bmain, scene, ptr);
+}
+
static void rna_Fluid_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr)
{
# ifdef WITH_FLUID
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- BKE_fluid_modifier_reset(settings->mmd);
+ BKE_fluid_modifier_reset(settings->fmd);
# endif
rna_Fluid_dependency_update(bmain, scene, ptr);
@@ -223,27 +256,27 @@ static bool rna_Fluid_parts_exists(PointerRNA *ptr, int ptype)
static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- FluidModifierData *mmd;
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd;
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_FLIP);
/* Only create a particle system in liquid domain mode.
* Remove any remaining data from a liquid sim when switching to gas. */
- if (mmd->domain->type != FLUID_DOMAIN_TYPE_LIQUID) {
+ if (fmd->domain->type != FLUID_DOMAIN_TYPE_LIQUID) {
rna_Fluid_parts_delete(ptr, PART_FLUID_FLIP);
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
- rna_Fluid_domain_reset(bmain, scene, ptr);
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
+ rna_Fluid_domain_data_reset(bmain, scene, ptr);
return;
}
if (ob->type == OB_MESH && !exists) {
rna_Fluid_parts_create(
bmain, ptr, "LiquidParticleSettings", "Liquid", "Liquid Particle System", PART_FLUID_FLIP);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FLIP;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FLIP;
}
else {
rna_Fluid_parts_delete(ptr, PART_FLUID_FLIP);
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
}
rna_Fluid_update(bmain, scene, ptr);
}
@@ -251,26 +284,26 @@ static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *p
static void rna_Fluid_spray_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- FluidModifierData *mmd;
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd;
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY);
if (ob->type == OB_MESH && !exists) {
rna_Fluid_parts_create(
bmain, ptr, "SprayParticleSettings", "Spray", "Spray Particle System", PART_FLUID_SPRAY);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
}
else {
rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY;
}
}
static void rna_Fluid_bubble_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- FluidModifierData *mmd;
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd;
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_BUBBLE);
if (ob->type == OB_MESH && !exists) {
@@ -280,37 +313,37 @@ static void rna_Fluid_bubble_parts_update(Main *bmain, Scene *UNUSED(scene), Poi
"Bubbles",
"Bubble Particle System",
PART_FLUID_BUBBLE);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
}
else {
rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE;
}
}
static void rna_Fluid_foam_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- FluidModifierData *mmd;
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd;
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM);
if (ob->type == OB_MESH && !exists) {
rna_Fluid_parts_create(
bmain, ptr, "FoamParticleSettings", "Foam", "Foam Particle System", PART_FLUID_FOAM);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
}
else {
rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM;
}
}
static void rna_Fluid_tracer_parts_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- FluidModifierData *mmd;
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd;
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
bool exists = rna_Fluid_parts_exists(ptr, PART_FLUID_TRACER);
if (ob->type == OB_MESH && !exists) {
@@ -320,21 +353,21 @@ static void rna_Fluid_tracer_parts_update(Main *bmain, Scene *UNUSED(scene), Poi
"Tracers",
"Tracer Particle System",
PART_FLUID_TRACER);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_TRACER;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_TRACER;
}
else {
rna_Fluid_parts_delete(ptr, PART_FLUID_TRACER);
- mmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER;
+ fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER;
}
}
static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- FluidModifierData *mmd;
- mmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
+ FluidModifierData *fmd;
+ fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
- if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_OFF) {
+ if (fmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_OFF) {
rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM);
rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE);
rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE);
@@ -345,17 +378,17 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
bool exists_bubble = rna_Fluid_parts_exists(ptr, PART_FLUID_BUBBLE);
/* Re-add each particle type if enabled and no particle system exists for them anymore. */
- if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && !exists_spray) {
+ if ((fmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && !exists_spray) {
rna_Fluid_spray_parts_update(bmain, scene, ptr);
}
- if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && !exists_foam) {
+ if ((fmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && !exists_foam) {
rna_Fluid_foam_parts_update(bmain, scene, ptr);
}
- if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && !exists_bubble) {
+ if ((fmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && !exists_bubble) {
rna_Fluid_bubble_parts_update(bmain, scene, ptr);
}
}
- else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_FOAM) {
+ else if (fmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_FOAM) {
if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAYFOAM)) {
rna_Fluid_parts_create(bmain,
@@ -365,8 +398,8 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
"Spray + Foam Particle System",
PART_FLUID_SPRAYFOAM);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
@@ -376,12 +409,12 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
/* Re-add spray if enabled and no particle system exists for it anymore. */
bool exists_bubble = rna_Fluid_parts_exists(ptr, PART_FLUID_BUBBLE);
- if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && !exists_bubble) {
+ if ((fmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && !exists_bubble) {
rna_Fluid_bubble_parts_update(bmain, scene, ptr);
}
}
}
- else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_BUBBLE) {
+ else if (fmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_SPRAY_BUBBLE) {
if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAYBUBBLE)) {
rna_Fluid_parts_create(bmain,
@@ -391,8 +424,8 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
"Spray + Bubble Particle System",
PART_FLUID_SPRAYBUBBLE);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
@@ -402,12 +435,12 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
/* Re-add foam if enabled and no particle system exists for it anymore. */
bool exists_foam = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM);
- if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && !exists_foam) {
+ if ((fmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && !exists_foam) {
rna_Fluid_foam_parts_update(bmain, scene, ptr);
}
}
}
- else if (mmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_FOAM_BUBBLE) {
+ else if (fmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_FOAM_BUBBLE) {
if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_FOAMBUBBLE)) {
rna_Fluid_parts_create(bmain,
@@ -417,8 +450,8 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
"Foam + Bubble Particle System",
PART_FLUID_FOAMBUBBLE);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
@@ -428,12 +461,12 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
/* Re-add foam if enabled and no particle system exists for it anymore. */
bool exists_spray = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY);
- if ((mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && !exists_spray) {
+ if ((fmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && !exists_spray) {
rna_Fluid_spray_parts_update(bmain, scene, ptr);
}
}
}
- else if (mmd->domain->sndparticle_combined_export ==
+ else if (fmd->domain->sndparticle_combined_export ==
SNDPARTICLE_COMBINED_EXPORT_SPRAY_FOAM_BUBBLE) {
if (ob->type == OB_MESH && !rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAYFOAMBUBBLE)) {
@@ -444,9 +477,9 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
"Spray + Foam + Bubble Particle System",
PART_FLUID_SPRAYFOAMBUBBLE);
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
- mmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
+ fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
@@ -512,20 +545,20 @@ static void rna_Fluid_guide_parent_set(struct PointerRNA *ptr,
struct PointerRNA value,
struct ReportList *UNUSED(reports))
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
Object *par = (Object *)value.data;
- FluidModifierData *mmd_par = NULL;
+ FluidModifierData *fmd_par = NULL;
if (par != NULL) {
- mmd_par = (FluidModifierData *)BKE_modifiers_findby_type(par, eModifierType_Fluid);
- if (mmd_par && mmd_par->domain) {
- mds->guide_parent = value.data;
- copy_v3_v3_int(mds->guide_res, mmd_par->domain->res);
+ fmd_par = (FluidModifierData *)BKE_modifiers_findby_type(par, eModifierType_Fluid);
+ if (fmd_par && fmd_par->domain) {
+ fds->guide_parent = value.data;
+ copy_v3_v3_int(fds->guide_res, fmd_par->domain->res);
}
}
else {
- mds->guide_parent = NULL;
+ fds->guide_parent = NULL;
}
}
@@ -557,7 +590,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 +612,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 *fds = (FluidDomainSettings *)ptr->data;
+ if (fds->cache_data_format == FLUID_DOMAIN_FILE_RAW ||
+ fds->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;
@@ -637,7 +675,7 @@ static void rna_Fluid_domaintype_set(struct PointerRNA *ptr, int value)
static char *rna_FluidDomainSettings_path(PointerRNA *ptr)
{
FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data;
- ModifierData *md = (ModifierData *)settings->mmd;
+ ModifierData *md = (ModifierData *)settings->fmd;
char name_esc[sizeof(md->name) * 2];
BLI_strescape(name_esc, md->name, sizeof(name_esc));
@@ -647,7 +685,7 @@ static char *rna_FluidDomainSettings_path(PointerRNA *ptr)
static char *rna_FluidFlowSettings_path(PointerRNA *ptr)
{
FluidFlowSettings *settings = (FluidFlowSettings *)ptr->data;
- ModifierData *md = (ModifierData *)settings->mmd;
+ ModifierData *md = (ModifierData *)settings->fmd;
char name_esc[sizeof(md->name) * 2];
BLI_strescape(name_esc, md->name, sizeof(name_esc));
@@ -657,7 +695,7 @@ static char *rna_FluidFlowSettings_path(PointerRNA *ptr)
static char *rna_FluidEffectorSettings_path(PointerRNA *ptr)
{
FluidEffectorSettings *settings = (FluidEffectorSettings *)ptr->data;
- ModifierData *md = (ModifierData *)settings->mmd;
+ ModifierData *md = (ModifierData *)settings->fmd;
char name_esc[sizeof(md->name) * 2];
BLI_strescape(name_esc, md->name, sizeof(name_esc));
@@ -672,23 +710,23 @@ static char *rna_FluidEffectorSettings_path(PointerRNA *ptr)
static int rna_FluidModifier_grid_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
float *density = NULL;
int size = 0;
- if (mds->flags & FLUID_DOMAIN_USE_NOISE && mds->fluid) {
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
/* high resolution smoke */
int res[3];
- manta_smoke_turbulence_get_res(mds->fluid, res);
+ manta_noise_get_res(fds->fluid, res);
size = res[0] * res[1] * res[2];
- density = manta_smoke_turbulence_get_density(mds->fluid);
+ density = manta_noise_get_density(fds->fluid);
}
- else if (mds->fluid) {
+ else if (fds->fluid) {
/* regular resolution */
- size = mds->res[0] * mds->res[1] * mds->res[2];
- density = manta_smoke_get_density(mds->fluid);
+ size = fds->res[0] * fds->res[1] * fds->res[2];
+ density = manta_smoke_get_density(fds->fluid);
}
length[0] = (density) ? size : 0;
@@ -707,18 +745,18 @@ static int rna_FluidModifier_color_grid_get_length(PointerRNA *ptr,
static int rna_FluidModifier_velocity_grid_get_length(PointerRNA *ptr,
int length[RNA_MAX_ARRAY_DIMENSION])
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
float *vx = NULL;
float *vy = NULL;
float *vz = NULL;
int size = 0;
/* Velocity data is always low-resolution. */
- if (mds->fluid) {
- size = 3 * mds->res[0] * mds->res[1] * mds->res[2];
- vx = manta_get_velocity_x(mds->fluid);
- vy = manta_get_velocity_y(mds->fluid);
- vz = manta_get_velocity_z(mds->fluid);
+ if (fds->fluid) {
+ size = 3 * fds->res[0] * fds->res[1] * fds->res[2];
+ vx = manta_get_velocity_x(fds->fluid);
+ vy = manta_get_velocity_y(fds->fluid);
+ vz = manta_get_velocity_z(fds->fluid);
}
length[0] = (vx && vy && vz) ? size : 0;
@@ -728,14 +766,14 @@ static int rna_FluidModifier_velocity_grid_get_length(PointerRNA *ptr,
static int rna_FluidModifier_heat_grid_get_length(PointerRNA *ptr,
int length[RNA_MAX_ARRAY_DIMENSION])
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
float *heat = NULL;
int size = 0;
/* Heat data is always low-resolution. */
- if (mds->fluid) {
- size = mds->res[0] * mds->res[1] * mds->res[2];
- heat = manta_smoke_get_heat(mds->fluid);
+ if (fds->fluid) {
+ size = fds->res[0] * fds->res[1] * fds->res[2];
+ heat = manta_smoke_get_heat(fds->fluid);
}
length[0] = (heat) ? size : 0;
@@ -744,38 +782,38 @@ static int rna_FluidModifier_heat_grid_get_length(PointerRNA *ptr,
static void rna_FluidModifier_density_grid_get(PointerRNA *ptr, float *values)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
int length[RNA_MAX_ARRAY_DIMENSION];
int size = rna_FluidModifier_grid_get_length(ptr, length);
float *density;
- BLI_rw_mutex_lock(mds->fluid_mutex, THREAD_LOCK_READ);
+ BLI_rw_mutex_lock(fds->fluid_mutex, THREAD_LOCK_READ);
- if (mds->flags & FLUID_DOMAIN_USE_NOISE && mds->fluid) {
- density = manta_smoke_turbulence_get_density(mds->fluid);
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
+ density = manta_noise_get_density(fds->fluid);
}
else {
- density = manta_smoke_get_density(mds->fluid);
+ density = manta_smoke_get_density(fds->fluid);
}
memcpy(values, density, size * sizeof(float));
- BLI_rw_mutex_unlock(mds->fluid_mutex);
+ BLI_rw_mutex_unlock(fds->fluid_mutex);
}
static void rna_FluidModifier_velocity_grid_get(PointerRNA *ptr, float *values)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
int length[RNA_MAX_ARRAY_DIMENSION];
int size = rna_FluidModifier_velocity_grid_get_length(ptr, length);
float *vx, *vy, *vz;
int i;
- BLI_rw_mutex_lock(mds->fluid_mutex, THREAD_LOCK_READ);
+ BLI_rw_mutex_lock(fds->fluid_mutex, THREAD_LOCK_READ);
- vx = manta_get_velocity_x(mds->fluid);
- vy = manta_get_velocity_y(mds->fluid);
- vz = manta_get_velocity_z(mds->fluid);
+ vx = manta_get_velocity_x(fds->fluid);
+ vy = manta_get_velocity_y(fds->fluid);
+ vz = manta_get_velocity_z(fds->fluid);
for (i = 0; i < size; i += 3) {
*(values++) = *(vx++);
@@ -783,56 +821,56 @@ static void rna_FluidModifier_velocity_grid_get(PointerRNA *ptr, float *values)
*(values++) = *(vz++);
}
- BLI_rw_mutex_unlock(mds->fluid_mutex);
+ BLI_rw_mutex_unlock(fds->fluid_mutex);
}
static void rna_FluidModifier_color_grid_get(PointerRNA *ptr, float *values)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
int length[RNA_MAX_ARRAY_DIMENSION];
int size = rna_FluidModifier_grid_get_length(ptr, length);
- BLI_rw_mutex_lock(mds->fluid_mutex, THREAD_LOCK_READ);
+ BLI_rw_mutex_lock(fds->fluid_mutex, THREAD_LOCK_READ);
- if (!mds->fluid) {
+ if (!fds->fluid) {
memset(values, 0, size * sizeof(float));
}
else {
- if (mds->flags & FLUID_DOMAIN_USE_NOISE) {
- if (manta_smoke_turbulence_has_colors(mds->fluid)) {
- manta_smoke_turbulence_get_rgba(mds->fluid, values, 0);
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE) {
+ if (manta_noise_has_colors(fds->fluid)) {
+ manta_noise_get_rgba(fds->fluid, values, 0);
}
else {
- manta_smoke_turbulence_get_rgba_fixed_color(mds->fluid, mds->active_color, values, 0);
+ manta_noise_get_rgba_fixed_color(fds->fluid, fds->active_color, values, 0);
}
}
else {
- if (manta_smoke_has_colors(mds->fluid)) {
- manta_smoke_get_rgba(mds->fluid, values, 0);
+ if (manta_smoke_has_colors(fds->fluid)) {
+ manta_smoke_get_rgba(fds->fluid, values, 0);
}
else {
- manta_smoke_get_rgba_fixed_color(mds->fluid, mds->active_color, values, 0);
+ manta_smoke_get_rgba_fixed_color(fds->fluid, fds->active_color, values, 0);
}
}
}
- BLI_rw_mutex_unlock(mds->fluid_mutex);
+ BLI_rw_mutex_unlock(fds->fluid_mutex);
}
static void rna_FluidModifier_flame_grid_get(PointerRNA *ptr, float *values)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
int length[RNA_MAX_ARRAY_DIMENSION];
int size = rna_FluidModifier_grid_get_length(ptr, length);
float *flame;
- BLI_rw_mutex_lock(mds->fluid_mutex, THREAD_LOCK_READ);
+ BLI_rw_mutex_lock(fds->fluid_mutex, THREAD_LOCK_READ);
- if (mds->flags & FLUID_DOMAIN_USE_NOISE && mds->fluid) {
- flame = manta_smoke_turbulence_get_flame(mds->fluid);
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
+ flame = manta_noise_get_flame(fds->fluid);
}
else {
- flame = manta_smoke_get_flame(mds->fluid);
+ flame = manta_smoke_get_flame(fds->fluid);
}
if (flame) {
@@ -842,19 +880,19 @@ static void rna_FluidModifier_flame_grid_get(PointerRNA *ptr, float *values)
memset(values, 0, size * sizeof(float));
}
- BLI_rw_mutex_unlock(mds->fluid_mutex);
+ BLI_rw_mutex_unlock(fds->fluid_mutex);
}
static void rna_FluidModifier_heat_grid_get(PointerRNA *ptr, float *values)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
int length[RNA_MAX_ARRAY_DIMENSION];
int size = rna_FluidModifier_heat_grid_get_length(ptr, length);
float *heat;
- BLI_rw_mutex_lock(mds->fluid_mutex, THREAD_LOCK_READ);
+ BLI_rw_mutex_lock(fds->fluid_mutex, THREAD_LOCK_READ);
- heat = manta_smoke_get_heat(mds->fluid);
+ heat = manta_smoke_get_heat(fds->fluid);
if (heat != NULL) {
/* scale heat values from -2.0-2.0 to -1.0-1.0. */
@@ -866,29 +904,29 @@ static void rna_FluidModifier_heat_grid_get(PointerRNA *ptr, float *values)
memset(values, 0, size * sizeof(float));
}
- BLI_rw_mutex_unlock(mds->fluid_mutex);
+ BLI_rw_mutex_unlock(fds->fluid_mutex);
}
static void rna_FluidModifier_temperature_grid_get(PointerRNA *ptr, float *values)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
int length[RNA_MAX_ARRAY_DIMENSION];
int size = rna_FluidModifier_grid_get_length(ptr, length);
float *flame;
- BLI_rw_mutex_lock(mds->fluid_mutex, THREAD_LOCK_READ);
+ BLI_rw_mutex_lock(fds->fluid_mutex, THREAD_LOCK_READ);
- if (mds->flags & FLUID_DOMAIN_USE_NOISE && mds->fluid) {
- flame = manta_smoke_turbulence_get_flame(mds->fluid);
+ if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
+ flame = manta_noise_get_flame(fds->fluid);
}
else {
- flame = manta_smoke_get_flame(mds->fluid);
+ flame = manta_smoke_get_flame(fds->fluid);
}
if (flame) {
/* Output is such that 0..1 maps to 0..1000K */
- float offset = mds->flame_ignition;
- float scale = mds->flame_max_temp - mds->flame_ignition;
+ float offset = fds->flame_ignition;
+ float scale = fds->flame_max_temp - fds->flame_ignition;
for (int i = 0; i < size; i++) {
values[i] = (flame[i] > 0.01f) ? offset + flame[i] * scale : 0.0f;
@@ -898,7 +936,7 @@ static void rna_FluidModifier_temperature_grid_get(PointerRNA *ptr, float *value
memset(values, 0, size * sizeof(float));
}
- BLI_rw_mutex_unlock(mds->fluid_mutex);
+ BLI_rw_mutex_unlock(fds->fluid_mutex);
}
# endif /* WITH_FLUID */
@@ -930,12 +968,12 @@ static void rna_FluidFlow_uvlayer_set(struct PointerRNA *ptr, const char *value)
static void rna_Fluid_use_color_ramp_set(struct PointerRNA *ptr, bool value)
{
- FluidDomainSettings *mds = (FluidDomainSettings *)ptr->data;
+ FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data;
- mds->use_coba = value;
+ fds->use_coba = value;
- if (value && mds->coba == NULL) {
- mds->coba = BKE_colorband_add(false);
+ if (value && fds->coba == NULL) {
+ fds->coba = BKE_colorband_add(false);
}
}
@@ -1058,27 +1096,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},
};
@@ -1354,7 +1383,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Adaptive Domain", "Adapt simulation resolution and size to fluid");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset");
/* fluid domain options */
@@ -1368,7 +1397,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
"Resolution used for the fluid domain. Value corresponds to the longest domain side "
"(resolution for other domain sides is calculated automatically)");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset");
prop = RNA_def_property(srna, "use_collision_border_front", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "border_collisions", FLUID_DOMAIN_BORDER_FRONT);
@@ -1551,7 +1580,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
"The noise simulation is scaled up by this factor (compared to the "
"base resolution of the domain)");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_noise_reset");
prop = RNA_def_property(srna, "noise_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "noise_type");
@@ -1559,7 +1588,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Noise Method", "Noise method which is used during the high-res simulation");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_noise_reset");
prop = RNA_def_property(srna, "use_noise", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_NOISE);
@@ -1573,7 +1602,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "simulation_method");
RNA_def_property_enum_items(prop, simulation_methods);
RNA_def_property_ui_text(prop, "Simulation Method", "Change the underlying simulation method");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset");
prop = RNA_def_property(srna, "flip_ratio", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 1.0);
@@ -1654,6 +1683,15 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
"and reduce the boundary smoothening effect)");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset");
+ prop = RNA_def_property(srna, "sys_particle_maximum", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "sys_particle_maximum");
+ RNA_def_property_range(prop, 0, INT_MAX);
+ RNA_def_property_ui_text(
+ prop,
+ "System Maximum",
+ "Maximum number of fluid particles that are allowed in this simulation");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset");
+
/* diffusion options */
prop = RNA_def_property(srna, "use_diffusion", PROP_BOOLEAN, PROP_NONE);
@@ -1661,7 +1699,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Use Diffusion", "Enable fluid diffusion settings (e.g. viscosity, surface tension)");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset");
prop = RNA_def_property(srna, "surface_tension", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 100.0);
@@ -1728,7 +1766,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
"resolution of the domain). For best meshing, it is recommended to "
"adjust the mesh particle radius alongside this value");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_meshcache_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_mesh_reset");
prop = RNA_def_property(srna, "mesh_generator", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mesh_generator");
@@ -1928,7 +1966,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
"The particle simulation is scaled up by this factor (compared to the "
"base resolution of the domain)");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_particles_reset");
prop = RNA_def_property(srna, "use_spray_particles", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_SPRAY);
@@ -2006,13 +2044,28 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "cache_frame_start");
RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
RNA_def_property_int_funcs(prop, NULL, "rna_Fluid_cache_startframe_set", NULL);
- RNA_def_property_ui_text(prop, "Start", "Frame on which the simulation starts");
+ RNA_def_property_ui_text(
+ prop,
+ "Start",
+ "Frame on which the simulation starts. This is the first frame that will be baked");
prop = RNA_def_property(srna, "cache_frame_end", PROP_INT, PROP_TIME);
RNA_def_property_int_sdna(prop, NULL, "cache_frame_end");
RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
RNA_def_property_int_funcs(prop, NULL, "rna_Fluid_cache_endframe_set", NULL);
- RNA_def_property_ui_text(prop, "End", "Frame on which the simulation stops");
+ RNA_def_property_ui_text(
+ prop,
+ "End",
+ "Frame on which the simulation stops. This is the last frame that will be baked");
+
+ prop = RNA_def_property(srna, "cache_frame_offset", PROP_INT, PROP_TIME);
+ RNA_def_property_int_sdna(prop, NULL, "cache_frame_offset");
+ RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
+ RNA_def_property_ui_text(
+ prop,
+ "Offset",
+ "Frame offset that is used when loading the simulation from the cache. It is not considered "
+ "when baking the simulation, only when loading it");
prop = RNA_def_property(srna, "cache_frame_pause_data", PROP_INT, PROP_TIME);
RNA_def_property_int_sdna(prop, NULL, "cache_frame_pause_data");
@@ -2070,7 +2123,17 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_enum_items(prop, cache_types);
RNA_def_property_enum_funcs(prop, NULL, "rna_Fluid_cachetype_set", NULL);
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");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_domain_data_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);
@@ -2137,7 +2200,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
"only needed if you plan to analyze the cache (e.g. view grids, velocity vectors, "
"particles) in Mantaflow directly (outside of Blender) after baking the simulation");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset");
/* time options */
@@ -2320,13 +2383,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) "
@@ -2634,7 +2697,7 @@ static void rna_def_fluid_effector_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_plane_init", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_EFFECTOR_USE_PLANE_INIT);
RNA_def_property_ui_text(prop, "Is Planar", "Treat this object as a planar, unclosed mesh");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset");
prop = RNA_def_property(srna, "velocity_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "vel_multi");
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index a52811a9a9a..c8d16ab65cc 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -76,6 +76,35 @@ static EnumPropertyItem rna_enum_gpencil_onion_modes_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_enum_keyframe_type_items[] = {
+ {BEZT_KEYTYPE_KEYFRAME,
+ "KEYFRAME",
+ ICON_KEYTYPE_KEYFRAME_VEC,
+ "Keyframe",
+ "Normal keyframe - e.g. for key poses"},
+ {BEZT_KEYTYPE_BREAKDOWN,
+ "BREAKDOWN",
+ ICON_KEYTYPE_BREAKDOWN_VEC,
+ "Breakdown",
+ "A breakdown pose - e.g. for transitions between key poses"},
+ {BEZT_KEYTYPE_MOVEHOLD,
+ "MOVING_HOLD",
+ ICON_KEYTYPE_MOVING_HOLD_VEC,
+ "Moving Hold",
+ "A keyframe that is part of a moving hold"},
+ {BEZT_KEYTYPE_EXTREME,
+ "EXTREME",
+ ICON_KEYTYPE_EXTREME_VEC,
+ "Extreme",
+ "An 'extreme' pose, or some other purpose as needed"},
+ {BEZT_KEYTYPE_JITTER,
+ "JITTER",
+ ICON_KEYTYPE_JITTER_VEC,
+ "Jitter",
+ "A filler or baked keyframe for keying on ones, or some other purpose as needed"},
+ {0, NULL, 0, NULL, NULL},
+};
+
static const EnumPropertyItem rna_enum_onion_keyframe_type_items[] = {
{-1, "ALL", ICON_ACTION, "All Types", "Include all Keyframe types"},
{BEZT_KEYTYPE_KEYFRAME,
@@ -982,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");
@@ -1324,6 +1359,13 @@ static void rna_def_gpencil_frame(BlenderRNA *brna)
RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
RNA_def_property_ui_text(prop, "Frame Number", "The frame on which this sketch appears");
+ prop = RNA_def_property(srna, "keyframe_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "key_type");
+ RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+ RNA_def_property_enum_items(prop, rna_enum_keyframe_type_items);
+ RNA_def_property_ui_text(prop, "Keyframe Type", "Type of keyframe");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
/* Flags */
prop = RNA_def_property(srna, "is_edited", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 3a8ded1a275..9519e3e1433 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,
@@ -1817,8 +1817,7 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
PropertyRNA *prop;
srna = RNA_def_struct(brna, "MirrorGpencilModifier", "GpencilModifier");
- RNA_def_struct_ui_text(
- srna, "Mirror Modifier", "Change stroke using lattice to deform modifier");
+ RNA_def_struct_ui_text(srna, "Mirror Modifier", "Create mirroring strokes");
RNA_def_struct_sdna(srna, "MirrorGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_MIRROR);
@@ -2127,8 +2126,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 +2181,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 +2267,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 +2358,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..1c43815d3a2 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -67,7 +67,6 @@ static const EnumPropertyItem image_source_items[] = {
# include "BKE_global.h"
-# include "GPU_draw.h"
# include "GPU_texture.h"
# include "IMB_imbuf.h"
@@ -200,7 +199,7 @@ static void rna_Image_gpu_texture_update(Main *UNUSED(bmain),
Image *ima = (Image *)ptr->owner_id;
if (!G.background) {
- GPU_free_image(ima);
+ BKE_image_free_gputextures(ima);
}
WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id);
@@ -398,7 +397,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values)
static int rna_Image_bindcode_get(PointerRNA *ptr)
{
Image *ima = (Image *)ptr->data;
- GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0];
+ GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0];
return (tex) ? GPU_texture_opengl_bindcode(tex) : 0;
}
@@ -516,7 +515,7 @@ static void rna_Image_pixels_set(PointerRNA *ptr, const float *values)
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID | IB_MIPMAP_INVALID;
BKE_image_mark_dirty(ima, ibuf);
if (!G.background) {
- GPU_free_image(ima);
+ BKE_image_free_gputextures(ima);
}
WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id);
}
@@ -915,13 +914,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..6f876923e52 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -50,8 +50,6 @@
# include "DNA_image_types.h"
# include "DNA_scene_types.h"
-# include "GPU_glew.h"
-
# include "MEM_guardedalloc.h"
static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, ReportList *reports)
@@ -116,7 +114,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 +136,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 {
@@ -222,23 +220,24 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame)
BKE_imageuser_default(&iuser);
iuser.framenr = frame;
- GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, GL_TEXTURE_2D);
+ GPUTexture *tex = BKE_image_get_gpu_texture(image, &iuser, NULL);
if (tex == NULL) {
BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2);
- return (int)GL_INVALID_OPERATION;
+ /* TODO(fclem) this error code makes no sense for vulkan. */
+ return 0x0502; /* GL_INVALID_OPERATION */
}
- return GL_NO_ERROR;
+ return 0; /* GL_NO_ERROR */
}
static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame)
{
- int error = GL_NO_ERROR;
+ int error = 0; /* GL_NO_ERROR */
BKE_image_tag_time(image);
- if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL) {
+ if (image->gputexture[TEXTARGET_2D][0] == NULL) {
error = rna_Image_gl_load(image, reports, frame);
}
@@ -247,7 +246,7 @@ static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame)
static void rna_Image_gl_free(Image *image)
{
- GPU_free_image(image);
+ BKE_image_free_gputextures(image);
/* remove the nocollect flag, image is available for garbage collection again */
image->flag &= ~IMA_NOCOLLECT;
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 44f118a8744..a8085c00cb3 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -23,6 +23,8 @@
#include "BLI_utildefines.h"
+#include "BLI_compiler_attrs.h"
+
#include "rna_internal_types.h"
#include "UI_resources.h"
@@ -478,9 +480,11 @@ extern StructRNA RNA_PropertyGroupItem;
extern StructRNA RNA_PropertyGroup;
#endif
-struct IDProperty *rna_idproperty_check(struct PropertyRNA **prop, struct PointerRNA *ptr);
+struct IDProperty *rna_idproperty_check(struct PropertyRNA **prop,
+ struct PointerRNA *ptr) ATTR_WARN_UNUSED_RESULT;
struct PropertyRNA *rna_ensure_property_realdata(struct PropertyRNA **prop,
- struct PointerRNA *ptr);
+ struct PointerRNA *ptr) ATTR_WARN_UNUSED_RESULT;
+struct PropertyRNA *rna_ensure_property(struct PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT;
/* Override default callbacks. */
/* Default override callbacks for all types. */
@@ -489,15 +493,12 @@ struct PropertyRNA *rna_ensure_property_realdata(struct PropertyRNA **prop,
* Not obvious though, those are fairly more complicated than basic SDNA access.
*/
int rna_property_override_diff_default(struct Main *bmain,
- struct PointerRNA *ptr_a,
- struct PointerRNA *ptr_b,
- struct PropertyRNA *prop_a,
- struct PropertyRNA *prop_b,
- const int len_a,
- const int len_b,
+ struct PropertyRNAOrID *prop_a,
+ struct PropertyRNAOrID *prop_b,
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..20c8743f768 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -41,6 +41,8 @@ struct Scene;
struct StructRNA;
struct bContext;
+typedef struct IDProperty IDProperty;
+
/* store local properties here */
#define RNA_IDP_UI "_RNA_UI"
@@ -155,27 +157,59 @@ typedef void (*PropEnumSetFuncEx)(struct PointerRNA *ptr, struct PropertyRNA *pr
/* Handling override operations, and also comparison. */
+/** Structure storing all needed data to process all three kinds of RNA properties. */
+typedef struct PropertyRNAOrID {
+ PointerRNA ptr;
+
+ /** The PropertyRNA passed as parameter, used to generate that structure's content:
+ * - Static RNA: The RNA property (same as `rnaprop`), never NULL.
+ * - Runtime RNA: The RNA property (same as `rnaprop`), never NULL.
+ * - IDProperty: The IDProperty, never NULL.
+ */
+ PropertyRNA *rawprop;
+ /** The real RNA property of this property, never NULL:
+ * - Static RNA: The rna property, also gives direct access to the data (from any matching
+ * PointerRNA).
+ * - Runtime RNA: The rna property, does not directly gives access to the data.
+ * - IDProperty: The generic PropertyRNA matching its type.
+ */
+ PropertyRNA *rnaprop;
+ /** The IDProperty storing the data of this property, may be NULL:
+ * - Static RNA: Always NULL.
+ * - Runtime RNA: The IDProperty storing the data of that property, may be NULL if never set yet.
+ * - IDProperty: The IDProperty, never NULL.
+ */
+ IDProperty *idprop;
+ /** The name of the property. */
+ const char *identifier;
+
+ /** Whether this property is a 'pure' IDProperty or not. */
+ bool is_idprop;
+ /** For runtime RNA properties, whether it is set, defined, or not.
+ * WARNING: This DOES take into account the `IDP_FLAG_GHOST` flag, i.e. it matches result of
+ * `RNA_property_is_set`. */
+ bool is_set;
+
+ bool is_array;
+ uint array_len;
+} PropertyRNAOrID;
+
/**
- * If \a override is NULL, merely do comparison between prop_a from ptr_a and prop_b from ptr_b,
+ * If \a override is NULL, merely do comparison between prop_a and prop_b,
* following comparison mode given.
* If \a override and \a rna_path are not NULL, it will add a new override operation for
* overridable properties that differ and have not yet been overridden
* (and set accordingly \a r_override_changed if given).
*
- * \note Given PropertyRNA are final (in case of IDProps...).
- * \note In non-array cases, \a len values are 0.
* \note \a override, \a rna_path and \a r_override_changed may be NULL pointers.
*/
typedef int (*RNAPropOverrideDiff)(struct Main *bmain,
- struct PointerRNA *ptr_a,
- struct PointerRNA *ptr_b,
- struct PropertyRNA *prop_a,
- struct PropertyRNA *prop_b,
- const int len_a,
- const int len_b,
+ struct PropertyRNAOrID *prop_a,
+ struct PropertyRNAOrID *prop_b,
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 +362,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..e0005766c48 100644
--- a/source/blender/makesrna/intern/rna_key.c
+++ b/source/blender/makesrna/intern/rna_key.c
@@ -708,9 +708,8 @@ static KeyBlock *rna_ShapeKeyData_find_keyblock(Key *key, float *point)
return NULL;
}
- /* we'll need to manually search through the keyblocks and check
- * if the point is somewhere in the middle of each block's data
- */
+ /* We'll need to manually search through the key-blocks and check
+ * if the point is somewhere in the middle of each block's data. */
for (kb = key->block.first; kb; kb = kb->next) {
if (kb->data) {
float *start = (float *)kb->data;
@@ -918,6 +917,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 +944,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 +967,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 +1030,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 +1054,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_layer.c b/source/blender/makesrna/intern/rna_layer.c
index b99457056fe..e7a898b97ae 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -458,7 +458,7 @@ static void rna_def_layer_objects(BlenderRNA *brna, PropertyRNA *cprop)
NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
RNA_def_property_ui_text(prop, "Active Object", "Active object for this layer");
- /* Could call: ED_object_base_activate(C, rl->basact);
+ /* Could call: `ED_object_base_activate(C, view_layer->basact);`
* but would be a bad level call and it seems the notifier is enough */
RNA_def_property_update(prop, NC_SCENE | ND_OB_ACTIVE, NULL);
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..97702b06b6f 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},
};
@@ -420,7 +410,7 @@ void RNA_def_main(BlenderRNA *brna)
srna = RNA_def_struct(brna, "BlendData", NULL);
RNA_def_struct_ui_text(srna,
- "Blendfile Data",
+ "Blend-file Data",
"Main data structure representing a .blend file and all its data-blocks");
RNA_def_struct_ui_icon(srna, ICON_BLENDER);
@@ -446,7 +436,7 @@ void RNA_def_main(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_autopack", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_Main_use_autopack_get", "rna_Main_use_autopack_set");
RNA_def_property_ui_text(
- prop, "Use Autopack", "Automatically pack all external data into .blend file");
+ prop, "Use Auto-pack", "Automatically pack all external data into .blend file");
prop = RNA_def_int_vector(srna,
"version",
@@ -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 c5781175d65..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
@@ -1174,6 +1166,22 @@ void RNA_def_main_libraries(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "tag", "rna_Main_libraries_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove a camera from the current blendfile");
+ parm = RNA_def_pointer(func, "library", "Library", "", "Library to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_boolean(
+ func, "do_unlink", true, "", "Unlink all usages of this library before deleting it");
+ RNA_def_boolean(func,
+ "do_id_user",
+ true,
+ "",
+ "Decrement user counter of all datablocks used by this object");
+ RNA_def_boolean(
+ func, "do_ui_user", true, "", "Make sure interface does not reference this object");
}
void RNA_def_main_screens(BlenderRNA *brna, PropertyRNA *cprop)
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..bb5ec0d6835 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 */
@@ -3147,7 +3376,7 @@ static void rna_def_mesh(BlenderRNA *brna)
/* readonly editmesh info - use for extrude menu */
prop = RNA_def_property(srna, "total_vert_sel", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_funcs(prop, "rna_Mesh_tot_vert_get", NULL, NULL);
- RNA_def_property_ui_text(prop, "Selected Vert Total", "Selected vertex count in editmode");
+ RNA_def_property_ui_text(prop, "Selected Vertex Total", "Selected vertex count in editmode");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "total_edge_sel", PROP_INT, PROP_UNSIGNED);
@@ -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 a7faef520bf..86f05c350f3 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},
};
@@ -1042,20 +1040,20 @@ static void rna_UVProjector_object_set(PointerRNA *ptr,
static void rna_fluid_set_type(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- FluidModifierData *mmd = (FluidModifierData *)ptr->data;
+ FluidModifierData *fmd = (FluidModifierData *)ptr->data;
Object *ob = (Object *)ptr->owner_id;
/* nothing changed */
- if ((mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
+ if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
return;
}
# ifdef WITH_FLUID
- BKE_fluid_modifier_free(mmd); /* XXX TODO: completely free all 3 pointers */
- BKE_fluid_modifier_create_type_data(mmd); /* create regarding of selected type */
+ BKE_fluid_modifier_free(fmd); /* XXX TODO: completely free all 3 pointers */
+ BKE_fluid_modifier_create_type_data(fmd); /* create regarding of selected type */
# endif
- switch (mmd->type) {
+ switch (fmd->type) {
case MOD_FLUID_TYPE_DOMAIN:
ob->dt = OB_WIRE;
break;
@@ -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,44 @@ static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene,
}
rna_Modifier_dependency_update(bmain, scene, ptr);
}
-# endif
+
+static void rna_SimulationModifier_data_path_get(PointerRNA *ptr, char *value)
+{
+ SimulationModifierData *smd = ptr->data;
+
+ if (smd->data_path) {
+ strcpy(value, smd->data_path);
+ }
+ else {
+ value[0] = '\0';
+ }
+}
+
+static int rna_SimulationModifier_data_path_length(PointerRNA *ptr)
+{
+ SimulationModifierData *smd = ptr->data;
+ return smd->data_path ? strlen(smd->data_path) : 0;
+}
+
+static void rna_SimulationModifier_data_path_set(PointerRNA *ptr, const char *value)
+{
+ SimulationModifierData *smd = ptr->data;
+
+ if (smd->data_path) {
+ MEM_freeN(smd->data_path);
+ }
+
+ if (value[0]) {
+ smd->data_path = BLI_strdup(value);
+ }
+ else {
+ smd->data_path = NULL;
+ }
+}
/**
* 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)
{
@@ -1658,6 +1688,17 @@ static void rna_Modifier_show_expanded_set(PointerRNA *ptr, bool value)
}
}
+/**
+ * Only check the first bit of the expansion flag for the main panel's expansion,
+ * maintaining compatibility with older versions where there was only one expansion
+ * value.
+ */
+static bool rna_Modifier_show_expanded_get(PointerRNA *ptr)
+{
+ ModifierData *md = ptr->data;
+ return md->ui_expand_flag & (1 << 0);
+}
+
#else
/* NOTE: *MUST* return subdivision_type property. */
@@ -1742,7 +1783,7 @@ static void rna_def_modifier_subsurf(BlenderRNA *brna)
PropertyRNA *prop;
srna = RNA_def_struct(brna, "SubsurfModifier", "Modifier");
- RNA_def_struct_ui_text(srna, "Subsurf Modifier", "Subdivision surface modifier");
+ RNA_def_struct_ui_text(srna, "Subdivision Surface Modifier", "Subdivision surface modifier");
RNA_def_struct_sdna(srna, "SubsurfModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SUBSURF);
@@ -1777,6 +1818,12 @@ static void rna_def_modifier_subsurf(BlenderRNA *brna)
prop, "Use Creases", "Use mesh edge crease information to sharpen edges");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "use_custom_normals", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", eSubsurfModifierFlag_UseCustomNormals);
+ RNA_def_property_ui_text(
+ prop, "Use Custom Normals", "Interpolates existing custom normals to resulting mesh");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
RNA_define_lib_overridable(false);
}
@@ -1978,6 +2025,12 @@ static void rna_def_modifier_multires(BlenderRNA *brna)
prop, "Use Creases", "Use mesh edge crease information to sharpen edges");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "use_custom_normals", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", eMultiresModifierFlag_UseCustomNormals);
+ RNA_def_property_ui_text(
+ prop, "Use Custom Normals", "Interpolates existing custom normals to resulting mesh");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
RNA_define_lib_overridable(false);
}
@@ -2090,7 +2143,7 @@ static void rna_def_modifier_build(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_TIME);
RNA_def_property_float_sdna(prop, NULL, "start");
RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF);
- RNA_def_property_ui_text(prop, "Start", "Start frame of the effect");
+ RNA_def_property_ui_text(prop, "Start Frame", "Start frame of the effect");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "frame_duration", PROP_FLOAT, PROP_TIME);
@@ -3204,12 +3257,12 @@ static void rna_def_modifier_correctivesmooth(BlenderRNA *brna)
"ORCO",
0,
"Original Coords",
- "Use base mesh vert coords as the rest position"},
+ "Use base mesh vertex coords as the rest position"},
{MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND,
"BIND",
0,
"Bind Coords",
- "Use bind vert coords for rest position"},
+ "Use bind vertex coords for rest position"},
{0, NULL, 0, NULL, NULL},
};
@@ -3419,7 +3472,7 @@ static void rna_def_modifier_cast(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_radius_as_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_CAST_SIZE_FROM_RADIUS);
RNA_def_property_ui_text(
- prop, "From Radius", "Use radius as size of projection shape (0 = auto)");
+ prop, "Size from Radius", "Use radius as size of projection shape (0 = auto)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_transform", PROP_BOOLEAN, PROP_NONE);
@@ -3948,6 +4001,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},
};
@@ -3987,8 +4059,8 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
};
static const EnumPropertyItem prop_affect_items[] = {
- {0, "EDGES", 0, "Edges", "Affect only edges"},
- {MOD_BEVEL_VERT, "VERTICES", 0, "Vertices", "Affect only vertices"},
+ {MOD_BEVEL_AFFECT_VERTICES, "VERTICES", 0, "Vertices", "Affect only vertices"},
+ {MOD_BEVEL_AFFECT_EDGES, "EDGES", 0, "Edges", "Affect only edges"},
{0, NULL, 0, NULL, NULL},
};
@@ -4021,7 +4093,7 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_BevelModifier_update_segments");
prop = RNA_def_property(srna, "affect", PROP_ENUM, PROP_NONE); /* as an enum */
- RNA_def_property_enum_bitflag_sdna(prop, NULL, "flags");
+ RNA_def_property_enum_sdna(prop, NULL, "affect_type");
RNA_def_property_enum_items(prop, prop_affect_items);
RNA_def_property_ui_text(prop, "Affect", "Affect edges or vertices");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -4061,6 +4133,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 +4199,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");
@@ -4784,7 +4857,7 @@ static void rna_def_modifier_screw(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_normal_calculate", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SCREW_NORMAL_CALC);
RNA_def_property_ui_text(
- prop, "Calc Order", "Calculate the order of edges (needed for meshes, but not curves)");
+ prop, "Calculate Order", "Calculate the order of edges (needed for meshes, but not curves)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_object_screw_offset", PROP_BOOLEAN, PROP_NONE);
@@ -5021,7 +5094,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 +5345,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 +5511,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",
@@ -5570,6 +5644,24 @@ static void rna_def_modifier_ocean(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Generate Foam", "Generate foam mask as a vertex color channel");
RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update");
+ prop = RNA_def_property(srna, "use_spray", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_OCEAN_GENERATE_SPRAY);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(
+ prop, "Generate Spray Map", "Generate map of spray direction as a vertex color channel");
+ RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update");
+
+ prop = RNA_def_property(srna, "invert_spray", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_OCEAN_INVERT_SPRAY);
+ RNA_def_property_ui_text(prop, "Invert Spray", "Invert the spray direction map");
+ RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update");
+
+ prop = RNA_def_property(srna, "spray_layer_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "spraylayername");
+ RNA_def_property_ui_text(
+ prop, "Spray Map", "Name of the vertex color layer used for the spray direction map");
+ RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update");
+
prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "resolution");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -5609,7 +5701,7 @@ static void rna_def_modifier_ocean(BlenderRNA *brna)
prop = RNA_def_property(srna, "wave_alignment", PROP_FLOAT, PROP_UNSIGNED);
RNA_def_property_float_sdna(prop, NULL, "wave_alignment");
- RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_ui_text(prop, "Wave Alignment", "How much the waves are aligned to each other");
RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update");
@@ -5686,7 +5778,7 @@ static void rna_def_modifier_ocean(BlenderRNA *brna)
prop = RNA_def_property(srna, "sharpen_peak_jonswap", PROP_FLOAT, PROP_UNSIGNED);
RNA_def_property_float_sdna(prop, NULL, "sharpen_peak_jonswap");
- RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_ui_text(prop, "Sharpen peak", "Peak sharpening for 'JONSWAP' and 'TMA' models");
RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update");
@@ -6158,7 +6250,8 @@ static void rna_def_modifier_wireframe(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_crease", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_WIREFRAME_CREASE);
- RNA_def_property_ui_text(prop, "Offset Relative", "Crease hub edges for improved subsurf");
+ RNA_def_property_ui_text(
+ prop, "Offset Relative", "Crease hub edges for improved subdivision surface");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "crease_weight", PROP_FLOAT, PROP_NONE);
@@ -6817,14 +6910,16 @@ 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_string_funcs(prop,
+ "rna_SimulationModifier_data_path_get",
+ "rna_SimulationModifier_data_path_length",
+ "rna_SimulationModifier_data_path_set");
RNA_def_property_ui_text(
prop, "Data Path", "Identifier of the simulation component that should be accessed");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -6887,7 +6982,8 @@ void RNA_def_modifier(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_funcs(prop, NULL, "rna_Modifier_show_expanded_set");
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Modifier_show_expanded_get", "rna_Modifier_show_expanded_set");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c
index f55d44e7bad..b94221ae936 100644
--- a/source/blender/makesrna/intern/rna_movieclip.c
+++ b/source/blender/makesrna/intern/rna_movieclip.c
@@ -218,7 +218,7 @@ static void rna_def_movieclip_proxy(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "100%", "Build proxy resolution 100% of the original undistorted footage dimension");
- /* build timecodes */
+ /* Build time-codes. */
prop = RNA_def_property(srna, "build_record_run", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "build_tc_flag", IMB_TC_RECORD_RUN);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -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..0359b2ed959 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -38,6 +38,7 @@
#include "BKE_animsys.h"
#include "BKE_image.h"
#include "BKE_node.h"
+#include "BKE_simulation.h"
#include "BKE_texture.h"
#include "RNA_access.h"
@@ -2848,6 +2849,14 @@ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA *
}
}
+static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr)
+{
+ rna_NodeSocketStandard_value_update(C, ptr);
+ bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
+ Main *bmain = CTX_data_main(C);
+ ntreeUpdateTree(bmain, ntree);
+}
+
/* ******** Node Types ******** */
static void rna_NodeInternalSocketTemplate_name_get(PointerRNA *ptr, char *value)
@@ -3629,8 +3638,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 +3663,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 +4414,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,8 +4429,8 @@ 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_update(prop, 0, "rna_Node_update");
+ RNA_def_property_ui_text(prop, "Sky Type", "Which sky model should be used");
+ RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "sun_direction", PROP_FLOAT, PROP_DIRECTION);
RNA_def_property_ui_text(prop, "Sun Direction", "Direction from where the sun is shining");
@@ -4439,6 +4449,59 @@ 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, 0, "rna_ShaderNode_socket_update");
+
+ prop = RNA_def_property(srna, "sun_size", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_ui_text(prop, "Sun Size", "Size of sun disc");
+ 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_intensity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Sun Intensity", "Strength of sun");
+ RNA_def_property_range(prop, 0.0f, 1000.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, "sun_elevation", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_ui_text(prop, "Sun Elevation", "Sun angle from 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_float_default(prop, 0.0f);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "altitude", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Altitude", "Height from sea level");
+ RNA_def_property_range(prop, 0.0f, 60.0f);
+ 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, "air_density", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_ui_text(prop, "Air", "Density of air molecules");
+ 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 molecules and water droplets");
+ 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");
+ 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[] = {
@@ -8312,6 +8375,8 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, "rna_NodeSocket_node_get", NULL, NULL, NULL);
RNA_def_property_struct_type(prop, "Node");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "Node", "Node owning this socket");
/* NB: the type property is used by standard sockets.
@@ -8806,7 +8871,8 @@ static void rna_def_node_socket_object(BlenderRNA *brna,
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
/* socket interface */
@@ -8840,7 +8906,8 @@ static void rna_def_node_socket_image(BlenderRNA *brna,
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "Image");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
/* socket interface */
@@ -8971,7 +9038,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);
@@ -9267,6 +9334,8 @@ static void rna_def_node(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "parent");
RNA_def_property_pointer_funcs(prop, NULL, "rna_Node_parent_set", NULL, "rna_Node_parent_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_struct_type(prop, "Node");
RNA_def_property_ui_text(prop, "Parent", "Parent this node is attached to");
@@ -9481,29 +9550,39 @@ static void rna_def_node_link(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "fromnode");
RNA_def_property_struct_type(prop, "Node");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "From node", "");
prop = RNA_def_property(srna, "to_node", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "tonode");
RNA_def_property_struct_type(prop, "Node");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "To node", "");
prop = RNA_def_property(srna, "from_socket", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "fromsock");
RNA_def_property_struct_type(prop, "NodeSocket");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "From socket", "");
prop = RNA_def_property(srna, "to_socket", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "tosock");
RNA_def_property_struct_type(prop, "NodeSocket");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "To socket", "");
prop = RNA_def_property(srna, "is_hidden", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_NodeLink_is_hidden_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "Is Hidden", "Link is hidden due to invisible sockets");
}
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 79a9e0be051..84b83bee089 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -174,7 +174,7 @@ static const EnumPropertyItem parent_type_items[] = {
#define INSTANCE_ITEMS_SHARED \
{0, "NONE", 0, "None", ""}, \
- {OB_DUPLIVERTS, "VERTS", 0, "Verts", "Instantiate child objects on all vertices"}, \
+ {OB_DUPLIVERTS, "VERTS", 0, "Vertices", "Instantiate child objects on all vertices"}, \
{ \
OB_DUPLIFACES, "FACES", 0, "Faces", "Instantiate child objects on all faces" \
}
@@ -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,
@@ -1572,7 +1579,8 @@ static void rna_Object_modifier_remove(Object *object,
PointerRNA *md_ptr)
{
ModifierData *md = md_ptr->data;
- if (ED_object_modifier_remove(reports, CTX_data_main(C), object, md) == false) {
+ if (ED_object_modifier_remove(reports, CTX_data_main(C), CTX_data_scene(C), object, md) ==
+ false) {
/* error is already set */
return;
}
@@ -1584,7 +1592,7 @@ static void rna_Object_modifier_remove(Object *object,
static void rna_Object_modifier_clear(Object *object, bContext *C)
{
- ED_object_modifier_clear(CTX_data_main(C), object);
+ ED_object_modifier_clear(CTX_data_main(C), CTX_data_scene(C), object);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object);
}
@@ -2134,6 +2142,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 +2151,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 +2158,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 +2171,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 +2233,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 +2548,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 +2603,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 +2617,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 +2631,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 +2643,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 +2695,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 +2709,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 +2723,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 +2730,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 +2741,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 +2748,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 +2760,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 +2767,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 +2779,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 +2788,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 +2888,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 +2911,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 +2925,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 +2935,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 +2946,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 +2954,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 +2975,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 +2983,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 +3118,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 +3125,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 +3147,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 +3176,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 +3183,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);
@@ -3215,7 +3242,7 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
prop = RNA_def_property(srna, "show_in_front", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAWXRAY);
+ RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAW_IN_FRONT);
RNA_def_property_ui_text(prop, "In Front", "Make the object draw in front of others");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_GPencil_update");
@@ -3229,7 +3256,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 +3275,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 +3301,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 +3318,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..fa837df682a 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -805,11 +805,11 @@ static char *rna_EffectorWeight_path(PointerRNA *ptr)
/* check smoke modifier */
md = (ModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
if (md) {
- FluidModifierData *mmd = (FluidModifierData *)md;
- if (mmd->domain->effector_weights == ew) {
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ if (fmd->domain->effector_weights == ew) {
char name_esc[sizeof(md->name) * 2];
BLI_strescape(name_esc, md->name, sizeof(name_esc));
- return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
+ return BLI_sprintfN("modifiers[\"%s\"].domain_settings.effector_weights", name_esc);
}
}
@@ -851,7 +851,7 @@ static void rna_CollisionSettings_dependency_update(Main *bmain, Scene *scene, P
ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Collision);
}
else if (!ob->pd->deflect && md) {
- ED_object_modifier_remove(NULL, bmain, ob, md);
+ ED_object_modifier_remove(NULL, bmain, scene, ob, md);
}
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
@@ -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_particle.c b/source/blender/makesrna/intern/rna_particle.c
index 73b3515030e..af1f1847fc6 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -52,7 +52,7 @@
#ifdef RNA_RUNTIME
static const EnumPropertyItem part_from_items[] = {
- {PART_FROM_VERT, "VERT", 0, "Verts", ""},
+ {PART_FROM_VERT, "VERT", 0, "Vertices", ""},
{PART_FROM_FACE, "FACE", 0, "Faces", ""},
{PART_FROM_VOLUME, "VOLUME", 0, "Volume", ""},
{0, NULL, 0, NULL, NULL},
@@ -61,7 +61,7 @@ static const EnumPropertyItem part_from_items[] = {
#ifndef RNA_RUNTIME
static const EnumPropertyItem part_reactor_from_items[] = {
- {PART_FROM_VERT, "VERT", 0, "Verts", ""},
+ {PART_FROM_VERT, "VERT", 0, "Vertices", ""},
{PART_FROM_FACE, "FACE", 0, "Faces", ""},
{PART_FROM_VOLUME, "VOLUME", 0, "Volume", ""},
{0, NULL, 0, NULL, NULL},
@@ -2957,7 +2957,7 @@ static void rna_def_particle_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "tangent_phase", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "tanphase");
RNA_def_property_range(prop, -1.0f, 1.0f);
- RNA_def_property_ui_text(prop, "Rot", "Rotate the surface tangent");
+ RNA_def_property_ui_text(prop, "Rotation", "Rotate the surface tangent");
RNA_def_property_update(prop, 0, "rna_Particle_reset");
prop = RNA_def_property(srna, "reactor_factor", PROP_FLOAT, PROP_NONE);
@@ -3360,7 +3360,7 @@ static void rna_def_particle_settings(BlenderRNA *brna)
prop,
"Use Modifier Stack",
"Emit particles from mesh with modifiers applied "
- "(must use same subsurf level for viewport and render for correct results)");
+ "(must use same subdivision surface level for viewport and render for correct results)");
RNA_def_property_update(prop, 0, "rna_Particle_change_type");
/* draw objects & collections */
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..5b77632be79 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -27,6 +27,8 @@
#include "BLI_path_util.h"
#include "BLI_utildefines.h"
+#include "BPY_extern.h"
+
#include "DEG_depsgraph.h"
#include "BKE_image.h"
@@ -299,7 +301,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,
@@ -408,6 +410,19 @@ static PointerRNA rna_RenderEngine_camera_override_get(PointerRNA *ptr)
}
}
+static void rna_RenderEngine_engine_frame_set(RenderEngine *engine, int frame, float subframe)
+{
+# ifdef WITH_PYTHON
+ BPy_BEGIN_ALLOW_THREADS;
+# endif
+
+ RE_engine_frame_set(engine, frame, subframe);
+
+# ifdef WITH_PYTHON
+ BPy_END_ALLOW_THREADS;
+# endif
+}
+
static void rna_RenderResult_views_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
RenderResult *rr = (RenderResult *)ptr->data;
@@ -673,7 +688,7 @@ static void rna_def_render_engine(BlenderRNA *brna)
parm = RNA_def_string(func, "info", NULL, 0, "Info", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
- func = RNA_def_function(srna, "frame_set", "RE_engine_frame_set");
+ func = RNA_def_function(srna, "frame_set", "rna_RenderEngine_engine_frame_set");
RNA_def_function_ui_description(func, "Evaluate scene at a different frame (for motion blur)");
parm = RNA_def_int(func, "frame", 0, INT_MIN, INT_MAX, "Frame", "", INT_MIN, INT_MAX);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
@@ -830,6 +845,14 @@ static void rna_def_render_engine(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Use Eevee Viewport", "Uses Eevee for viewport shading in LookDev shading mode");
+ prop = RNA_def_property(srna, "bl_use_gpu_context", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_GPU_CONTEXT);
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
+ RNA_def_property_ui_text(
+ prop,
+ "Use GPU Context",
+ "Enable OpenGL context for the render method, for engines that render using OpenGL");
+
prop = RNA_def_property(srna, "bl_use_shading_nodes_custom", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_SHADING_NODES_CUSTOM);
RNA_def_property_boolean_default(prop, true);
diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c
index 325c4e3caa9..450d148d8a3 100644
--- a/source/blender/makesrna/intern/rna_rigidbody.c
+++ b/source/blender/makesrna/intern/rna_rigidbody.c
@@ -75,6 +75,11 @@ const EnumPropertyItem rna_enum_rigidbody_object_shape_items[] = {
"Mesh",
"Mesh consisting of triangles only, allowing for more detailed interactions than convex "
"hulls"},
+ {RB_SHAPE_COMPOUND,
+ "COMPOUND",
+ ICON_MESH_DATA,
+ "Compound Parent",
+ "Combines all of its direct rigid body children into one rigid object."},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index d7e93975d31..9f5440be9f8 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -479,7 +479,7 @@ static StructRNA *rna_Property_refine(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr); /* XXX ptr? */
+ prop = rna_ensure_property(prop);
switch (prop->type) {
case PROP_BOOLEAN:
@@ -504,90 +504,90 @@ static StructRNA *rna_Property_refine(PointerRNA *ptr)
static void rna_Property_identifier_get(PointerRNA *ptr, char *value)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
strcpy(value, ((PropertyRNA *)prop)->identifier);
}
static int rna_Property_identifier_length(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return strlen(prop->identifier);
}
static void rna_Property_name_get(PointerRNA *ptr, char *value)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
strcpy(value, prop->name ? prop->name : "");
}
static int rna_Property_name_length(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return prop->name ? strlen(prop->name) : 0;
}
static void rna_Property_description_get(PointerRNA *ptr, char *value)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
strcpy(value, prop->description ? prop->description : "");
}
static int rna_Property_description_length(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return prop->description ? strlen(prop->description) : 0;
}
static void rna_Property_translation_context_get(PointerRNA *ptr, char *value)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
strcpy(value, prop->translation_context);
}
static int rna_Property_translation_context_length(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return strlen(prop->translation_context);
}
static int rna_Property_type_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return prop->type;
}
static int rna_Property_subtype_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return prop->subtype;
}
static PointerRNA rna_Property_srna_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return rna_pointer_inherit_refine(ptr, &RNA_Struct, prop->srna);
}
static int rna_Property_unit_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return RNA_SUBTYPE_UNIT(prop->subtype);
}
static int rna_Property_icon_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return prop->icon;
}
@@ -698,7 +698,7 @@ static const EnumPropertyItem *rna_Property_tags_itemf(bContext *UNUSED(C),
static int rna_Property_array_length_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return prop->totarraylength;
}
@@ -706,7 +706,7 @@ static void rna_Property_array_dimensions_get(PointerRNA *ptr,
int dimensions[RNA_MAX_ARRAY_DIMENSION])
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
if (prop->arraydimension > 1) {
for (int i = RNA_MAX_ARRAY_DIMENSION; i--;) {
@@ -740,14 +740,14 @@ static bool rna_Property_is_runtime_get(PointerRNA *ptr)
static bool rna_BoolProperty_default_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((BoolPropertyRNA *)prop)->defaultvalue;
}
static int rna_IntProperty_default_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((IntPropertyRNA *)prop)->defaultvalue;
}
/* int/float/bool */
@@ -755,7 +755,7 @@ static int rna_NumberProperty_default_array_get_length(PointerRNA *ptr,
int length[RNA_MAX_ARRAY_DIMENSION])
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
length[0] = prop->totarraylength;
@@ -771,7 +771,7 @@ static bool rna_NumberProperty_is_array_get(PointerRNA *ptr)
static void rna_IntProperty_default_array_get(PointerRNA *ptr, int *values)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
if (prop->totarraylength > 0) {
PointerRNA null_ptr = PointerRNA_NULL;
RNA_property_int_get_default_array(&null_ptr, prop, values);
@@ -781,7 +781,7 @@ static void rna_IntProperty_default_array_get(PointerRNA *ptr, int *values)
static void rna_BoolProperty_default_array_get(PointerRNA *ptr, bool *values)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
if (prop->totarraylength > 0) {
PointerRNA null_ptr = PointerRNA_NULL;
RNA_property_boolean_get_default_array(&null_ptr, prop, values);
@@ -791,7 +791,7 @@ static void rna_BoolProperty_default_array_get(PointerRNA *ptr, bool *values)
static void rna_FloatProperty_default_array_get(PointerRNA *ptr, float *values)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
if (prop->totarraylength > 0) {
PointerRNA null_ptr = PointerRNA_NULL;
RNA_property_float_get_default_array(&null_ptr, prop, values);
@@ -801,103 +801,103 @@ static void rna_FloatProperty_default_array_get(PointerRNA *ptr, float *values)
static int rna_IntProperty_hard_min_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((IntPropertyRNA *)prop)->hardmin;
}
static int rna_IntProperty_hard_max_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((IntPropertyRNA *)prop)->hardmax;
}
static int rna_IntProperty_soft_min_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((IntPropertyRNA *)prop)->softmin;
}
static int rna_IntProperty_soft_max_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((IntPropertyRNA *)prop)->softmax;
}
static int rna_IntProperty_step_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((IntPropertyRNA *)prop)->step;
}
static float rna_FloatProperty_default_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->defaultvalue;
}
static float rna_FloatProperty_hard_min_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->hardmin;
}
static float rna_FloatProperty_hard_max_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->hardmax;
}
static float rna_FloatProperty_soft_min_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->softmin;
}
static float rna_FloatProperty_soft_max_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->softmax;
}
static float rna_FloatProperty_step_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->step;
}
static int rna_FloatProperty_precision_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((FloatPropertyRNA *)prop)->precision;
}
static void rna_StringProperty_default_get(PointerRNA *ptr, char *value)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
strcpy(value, ((StringPropertyRNA *)prop)->defaultvalue);
}
static int rna_StringProperty_default_length(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return strlen(((StringPropertyRNA *)prop)->defaultvalue);
}
static int rna_StringProperty_max_length_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((StringPropertyRNA *)prop)->maxlength;
}
@@ -909,7 +909,7 @@ static const EnumPropertyItem *rna_EnumProperty_default_itemf(bContext *C,
PropertyRNA *prop = (PropertyRNA *)ptr->data;
EnumPropertyRNA *eprop;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
eprop = (EnumPropertyRNA *)prop;
/* incompatible default attributes */
@@ -931,7 +931,7 @@ static const EnumPropertyItem *rna_EnumProperty_default_itemf(bContext *C,
static int rna_EnumProperty_default_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return ((EnumPropertyRNA *)prop)->defaultvalue;
}
@@ -950,7 +950,7 @@ static void rna_EnumProperty_items_begin(CollectionPropertyIterator *iter, Point
int totitem;
bool free;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
/* eprop = (EnumPropertyRNA *)prop; */
RNA_property_enum_items_ex(
@@ -1016,14 +1016,14 @@ static int rna_EnumPropertyItem_icon_get(PointerRNA *ptr)
static PointerRNA rna_PointerProperty_fixed_type_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return rna_pointer_inherit_refine(ptr, &RNA_Struct, ((PointerPropertyRNA *)prop)->type);
}
static PointerRNA rna_CollectionProperty_fixed_type_get(PointerRNA *ptr)
{
PropertyRNA *prop = (PropertyRNA *)ptr->data;
- rna_idproperty_check(&prop, ptr);
+ prop = rna_ensure_property(prop);
return rna_pointer_inherit_refine(ptr, &RNA_Struct, ((CollectionPropertyRNA *)prop)->item_type);
}
@@ -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,8 @@ 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 uint property_type,
const char *rna_itemname_a,
const char *rna_itemname_b,
const int rna_itemindex_a,
@@ -1255,6 +1258,8 @@ static int rna_property_override_diff_propptr(Main *bmain,
const int flags,
bool *r_override_changed)
{
+ BLI_assert(ELEM(property_type, PROP_POINTER, PROP_COLLECTION));
+
const bool do_create = override != NULL && (flags & RNA_OVERRIDE_COMPARE_CREATE) != 0 &&
rna_path != NULL;
@@ -1265,6 +1270,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 +1284,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) {
@@ -1297,6 +1303,13 @@ static int rna_property_override_diff_propptr(Main *bmain,
if (op != NULL) {
BKE_lib_override_library_operations_tag(op, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
+ if (created || op->rna_prop_type == 0) {
+ op->rna_prop_type = property_type;
+ }
+ else {
+ BLI_assert(op->rna_prop_type == property_type);
+ }
+
if (created || rna_itemname_a != NULL || rna_itemname_b != NULL ||
rna_itemindex_a != -1 || rna_itemindex_b != -1) {
BKE_lib_override_library_property_operation_get(op,
@@ -1324,16 +1337,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 +1346,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;
}
@@ -1386,18 +1441,22 @@ static int rna_property_override_diff_propptr(Main *bmain,
RNA_property_##_typename##_set((_ptr), (_prop), (_value)))
int rna_property_override_diff_default(Main *bmain,
- PointerRNA *ptr_a,
- PointerRNA *ptr_b,
- PropertyRNA *prop_a,
- PropertyRNA *prop_b,
- const int len_a,
- const int len_b,
+ PropertyRNAOrID *prop_a,
+ PropertyRNAOrID *prop_b,
const int mode,
IDOverrideLibrary *override,
const char *rna_path,
+ const size_t rna_path_len,
const int flags,
bool *r_override_changed)
{
+ PointerRNA *ptr_a = &prop_a->ptr;
+ PointerRNA *ptr_b = &prop_b->ptr;
+ PropertyRNA *rawprop_a = prop_a->rawprop;
+ PropertyRNA *rawprop_b = prop_b->rawprop;
+ const uint len_a = prop_a->array_len;
+ const uint len_b = prop_b->array_len;
+
BLI_assert(len_a == len_b);
/* Note: at this point, we are sure that when len_a is zero,
@@ -1406,7 +1465,20 @@ int rna_property_override_diff_default(Main *bmain,
const bool do_create = override != NULL && (flags & RNA_OVERRIDE_COMPARE_CREATE) != 0 &&
rna_path != NULL;
- switch (RNA_property_type(prop_a)) {
+ const bool no_ownership = (prop_a->rnaprop->flag & PROP_PTR_NO_OWNERSHIP) != 0;
+ const bool no_prop_name = (prop_a->rnaprop->flag_override & PROPOVERRIDE_NO_PROP_NAME) != 0;
+
+ /* Note: we assume we only insert in ptr_a (i.e. we can only get new items in ptr_a),
+ * and that we never remove anything. */
+ const bool use_collection_insertion = (prop_a->rnaprop->flag_override &
+ PROPOVERRIDE_LIBRARY_INSERTION) &&
+ do_create;
+
+ const uint rna_prop_type = RNA_property_type(prop_a->rnaprop);
+ bool created = false;
+ IDOverrideLibraryProperty *op = NULL;
+
+ switch (rna_prop_type) {
case PROP_BOOLEAN: {
if (len_a) {
bool array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY];
@@ -1417,16 +1489,14 @@ int rna_property_override_diff_default(Main *bmain,
array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(bool) * len_b, "RNA equals") :
array_stack_b;
- RNA_property_boolean_get_array(ptr_a, prop_a, array_a);
- RNA_property_boolean_get_array(ptr_b, prop_b, array_b);
+ RNA_property_boolean_get_array(ptr_a, rawprop_a, array_a);
+ RNA_property_boolean_get_array(ptr_b, rawprop_b, array_b);
const int comp = memcmp(array_a, array_b, sizeof(bool) * len_a);
if (do_create && comp != 0) {
/* XXX TODO this will have to be refined to handle array items */
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) {
BKE_lib_override_library_property_operation_get(
@@ -1450,14 +1520,12 @@ int rna_property_override_diff_default(Main *bmain,
return comp;
}
else {
- const bool value_a = RNA_property_boolean_get(ptr_a, prop_a);
- const bool value_b = RNA_property_boolean_get(ptr_b, prop_b);
+ const bool value_a = RNA_property_boolean_get(ptr_a, rawprop_a);
+ const bool value_b = RNA_property_boolean_get(ptr_b, rawprop_b);
const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0;
if (do_create && comp != 0) {
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) { /* If not yet overridden... */
BKE_lib_override_library_property_operation_get(
@@ -1482,16 +1550,14 @@ int rna_property_override_diff_default(Main *bmain,
array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(int) * len_b, "RNA equals") :
array_stack_b;
- RNA_property_int_get_array(ptr_a, prop_a, array_a);
- RNA_property_int_get_array(ptr_b, prop_b, array_b);
+ RNA_property_int_get_array(ptr_a, rawprop_a, array_a);
+ RNA_property_int_get_array(ptr_b, rawprop_b, array_b);
const int comp = memcmp(array_a, array_b, sizeof(int) * len_a);
if (do_create && comp != 0) {
/* XXX TODO this will have to be refined to handle array items */
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) {
BKE_lib_override_library_property_operation_get(
@@ -1515,14 +1581,12 @@ int rna_property_override_diff_default(Main *bmain,
return comp;
}
else {
- const int value_a = RNA_property_int_get(ptr_a, prop_a);
- const int value_b = RNA_property_int_get(ptr_b, prop_b);
+ const int value_a = RNA_property_int_get(ptr_a, rawprop_a);
+ const int value_b = RNA_property_int_get(ptr_b, rawprop_b);
const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0;
if (do_create && comp != 0) {
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) { /* If not yet overridden... */
BKE_lib_override_library_property_operation_get(
@@ -1547,16 +1611,14 @@ int rna_property_override_diff_default(Main *bmain,
array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(float) * len_b, "RNA equals") :
array_stack_b;
- RNA_property_float_get_array(ptr_a, prop_a, array_a);
- RNA_property_float_get_array(ptr_b, prop_b, array_b);
+ RNA_property_float_get_array(ptr_a, rawprop_a, array_a);
+ RNA_property_float_get_array(ptr_b, rawprop_b, array_b);
const int comp = memcmp(array_a, array_b, sizeof(float) * len_a);
if (do_create && comp != 0) {
/* XXX TODO this will have to be refined to handle array items */
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) {
BKE_lib_override_library_property_operation_get(
@@ -1580,14 +1642,12 @@ int rna_property_override_diff_default(Main *bmain,
return comp;
}
else {
- const float value_a = RNA_property_float_get(ptr_a, prop_a);
- const float value_b = RNA_property_float_get(ptr_b, prop_b);
+ const float value_a = RNA_property_float_get(ptr_a, rawprop_a);
+ const float value_b = RNA_property_float_get(ptr_b, rawprop_b);
const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0;
if (do_create && comp != 0) {
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) { /* If not yet overridden... */
BKE_lib_override_library_property_operation_get(
@@ -1603,14 +1663,12 @@ int rna_property_override_diff_default(Main *bmain,
}
case PROP_ENUM: {
- const int value_a = RNA_property_enum_get(ptr_a, prop_a);
- const int value_b = RNA_property_enum_get(ptr_b, prop_b);
+ const int value_a = RNA_property_enum_get(ptr_a, rawprop_a);
+ const int value_b = RNA_property_enum_get(ptr_b, rawprop_b);
const int comp = value_a != value_b;
if (do_create && comp != 0) {
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) { /* If not yet overridden... */
BKE_lib_override_library_property_operation_get(
@@ -1628,9 +1686,9 @@ int rna_property_override_diff_default(Main *bmain,
char fixed_a[4096], fixed_b[4096];
int len_str_a, len_str_b;
char *value_a = RNA_property_string_get_alloc(
- ptr_a, prop_a, fixed_a, sizeof(fixed_a), &len_str_a);
+ ptr_a, rawprop_a, fixed_a, sizeof(fixed_a), &len_str_a);
char *value_b = RNA_property_string_get_alloc(
- ptr_b, prop_b, fixed_b, sizeof(fixed_b), &len_str_b);
+ ptr_b, rawprop_b, fixed_b, sizeof(fixed_b), &len_str_b);
/* TODO we could do a check on length too,
* but then we would not have a 'real' string comparison...
* Maybe behind a eRNAOverrideMatch flag? */
@@ -1642,9 +1700,7 @@ int rna_property_override_diff_default(Main *bmain,
const int comp = strcmp(value_a, value_b);
if (do_create && comp != 0) {
- bool created = false;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (op != NULL && created) { /* If not yet overridden... */
BKE_lib_override_library_property_operation_get(
@@ -1666,16 +1722,13 @@ int rna_property_override_diff_default(Main *bmain,
}
case PROP_POINTER: {
- if (STREQ(RNA_property_identifier(prop_a), "rna_type")) {
+ if (STREQ(prop_a->identifier, "rna_type")) {
/* Dummy 'pass' answer, this is a meta-data and must be ignored... */
return 0;
}
else {
- PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, prop_a);
- PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, prop_b);
- 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;
+ PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, rawprop_a);
+ PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, rawprop_b);
return rna_property_override_diff_propptr(bmain,
&propptr_a,
&propptr_b,
@@ -1684,6 +1737,8 @@ int rna_property_override_diff_default(Main *bmain,
no_prop_name,
override,
rna_path,
+ rna_path_len,
+ PROP_POINTER,
NULL,
NULL,
-1,
@@ -1695,13 +1750,6 @@ int rna_property_override_diff_default(Main *bmain,
}
case PROP_COLLECTION: {
- /* Note: we assume we only insert in ptr_a (i.e. we can only get new items in ptr_a),
- * and that we never remove anything. */
- const bool use_insertion = (RNA_property_override_flag(prop_a) &
- PROPOVERRIDE_LIBRARY_INSERTION) &&
- do_create;
- const bool no_prop_name = (RNA_property_override_flag(prop_a) & PROPOVERRIDE_NO_PROP_NAME) !=
- 0;
bool equals = true;
bool abort = false;
bool is_first_insert = true;
@@ -1709,8 +1757,8 @@ int rna_property_override_diff_default(Main *bmain,
int idx_b = 0;
CollectionPropertyIterator iter_a, iter_b;
- RNA_property_collection_begin(ptr_a, prop_a, &iter_a);
- RNA_property_collection_begin(ptr_b, prop_b, &iter_b);
+ RNA_property_collection_begin(ptr_a, rawprop_a, &iter_a);
+ RNA_property_collection_begin(ptr_b, rawprop_b, &iter_b);
char buff_a[4096];
char buff_prev_a[4096] = {0};
@@ -1725,7 +1773,7 @@ int rna_property_override_diff_default(Main *bmain,
do {
bool is_id = false, is_null = false, is_type_diff = false;
- is_valid_for_insertion = use_insertion;
+ is_valid_for_insertion = use_collection_insertion;
/* If false, it means that the whole data itself is different,
* so no point in going inside of it at all! */
@@ -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,
@@ -1796,10 +1846,8 @@ int rna_property_override_diff_default(Main *bmain,
/* Collections do not support replacement of their data (except for collections of ID
* pointers), since they do not support removing, only in *some* cases, insertion. We
* also assume then that _a data is the one where things are inserted. */
- if (is_valid_for_insertion && use_insertion) {
- bool created;
- IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get(
- override, rna_path, &created);
+ if (is_valid_for_insertion && use_collection_insertion) {
+ op = BKE_lib_override_library_property_get(override, rna_path, &created);
if (is_first_insert) {
/* We need to clean up all possible existing insertion operations,
@@ -1832,10 +1880,10 @@ int rna_property_override_diff_default(Main *bmain,
prev_propname_a,
idx_a - 1);
# endif
+ op = NULL;
}
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 +1892,8 @@ int rna_property_override_diff_default(Main *bmain,
no_prop_name,
override,
rna_path,
+ rna_path_len,
+ PROP_COLLECTION,
propname_a,
propname_b,
idx_a,
@@ -1880,7 +1930,7 @@ int rna_property_override_diff_default(Main *bmain,
break;
}
- if (!(use_insertion && !(is_id || is_valid_for_diffing))) {
+ if (!(use_collection_insertion && !(is_id || is_valid_for_diffing))) {
break;
}
@@ -1912,6 +1962,15 @@ int rna_property_override_diff_default(Main *bmain,
break;
}
+ if (op != NULL) {
+ if (created || op->rna_prop_type == 0) {
+ op->rna_prop_type = rna_prop_type;
+ }
+ else {
+ BLI_assert(op->rna_prop_type == rna_prop_type);
+ }
+ }
+
return 0;
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 7e9753b090a..66698d60423 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -924,13 +924,6 @@ static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), P
DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_VOLUME | ID_RECALC_SEQUENCER_STRIPS);
}
-static const char *rna_Scene_statistics_string_get(Scene *UNUSED(scene),
- Main *UNUSED(bmain),
- ViewLayer *view_layer)
-{
- return ED_info_footer_string(view_layer);
-}
-
static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images;
@@ -1662,7 +1655,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 +3083,22 @@ 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_transform_correct_keep_connected", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "uvcalc_flag", UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED);
+ RNA_def_property_ui_text(
+ prop,
+ "Keep Connected",
+ "During the Face Attributes correction, merge attributes connected to the same vertex");
+ 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(
@@ -3449,7 +3458,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_edge_path_live_unwrap", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "edge_mode_live_unwrap", 1);
- RNA_def_property_ui_text(prop, "Live Unwrap", "Changing edges seam re-calculates UV unwrap");
+ RNA_def_property_ui_text(prop, "Live Unwrap", "Changing edges seam recalculates UV unwrap");
prop = RNA_def_property(srna, "normal_vector", PROP_FLOAT, PROP_XYZ);
RNA_def_property_ui_text(prop, "Normal Vector", "Normal Vector used to copy, add or multiply");
@@ -4807,7 +4816,7 @@ void rna_def_freestyle_settings(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"View Map Cache",
- "Keep the computed view map and avoid re-calculating it if mesh geometry is unchanged");
+ "Keep the computed view map and avoid recalculating it if mesh geometry is unchanged");
RNA_def_property_update(
prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_use_view_map_cache_update");
@@ -6456,7 +6465,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "simplify_gpencil_shader_fx", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_FX);
- RNA_def_property_ui_text(prop, "ShadersFX", "Display Shader FX");
+ RNA_def_property_ui_text(prop, "Shaders Effects", "Display Shader Effects");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "simplify_gpencil_tint", PROP_BOOLEAN, PROP_NONE);
@@ -7150,12 +7159,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 +7166,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);
@@ -7236,7 +7266,7 @@ static void rna_def_scene_gpencil(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3);
RNA_def_property_ui_text(prop,
"Anti-Aliasing Threshold",
- "Threshold for edge detection algorithm (higher values might overblur "
+ "Threshold for edge detection algorithm (higher values might over-blur "
"some part of the image)");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
@@ -7247,9 +7277,6 @@ void RNA_def_scene(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- FunctionRNA *func;
- PropertyRNA *parm;
-
static const EnumPropertyItem audio_distance_model_items[] = {
{0, "NONE", 0, "None", "No distance attenuation"},
{1, "INVERSE", 0, "Inverse", "Inverse distance model"},
@@ -7469,6 +7496,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);
@@ -7640,14 +7668,6 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE, NULL);
RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update");
- /* Statistics */
- func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get");
- RNA_def_function_flag(func, FUNC_USE_MAIN);
- parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "", "Active layer");
- RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
- parm = RNA_def_string(func, "statistics", NULL, 0, "Statistics", "");
- RNA_def_function_return(func, parm);
-
/* Grease Pencil */
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
@@ -7699,6 +7719,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_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 1d03b16d5bb..a66e20258d2 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -41,13 +41,6 @@
# include "ABC_alembic.h"
#endif
-const EnumPropertyItem rna_enum_abc_compression_items[] = {
-#ifdef WITH_ALEMBIC
- {ABC_ARCHIVE_OGAWA, "OGAWA", 0, "Ogawa", ""},
- {ABC_ARCHIVE_HDF5, "HDF5", 0, "HDF5", ""},
-#endif
- {0, NULL, 0, NULL, NULL}};
-
#ifdef RNA_RUNTIME
# include "BKE_editmesh.h"
@@ -105,13 +98,13 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf
}
}
-static void rna_Scene_uvedit_aspect(Scene *scene, Object *ob, float *aspect)
+static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float *aspect)
{
if ((ob->type == OB_MESH) && (ob->mode == OB_MODE_EDIT)) {
BMEditMesh *em;
em = BKE_editmesh_from_object(ob);
if (EDBM_uv_check(em)) {
- ED_uvedit_get_aspect(scene, ob, em->bm, aspect, aspect + 1);
+ ED_uvedit_get_aspect(ob, aspect, aspect + 1);
return;
}
}
@@ -222,7 +215,6 @@ static void rna_Scene_alembic_export(Scene *scene,
bool use_subdiv_schema,
bool export_hair,
bool export_particles,
- int compression_type,
bool packuv,
float scale,
bool triangulate,
@@ -257,7 +249,6 @@ static void rna_Scene_alembic_export(Scene *scene,
.use_subdiv_schema = use_subdiv_schema,
.export_hair = export_hair,
.export_particles = export_particles,
- .compression_type = compression_type,
.packuv = packuv,
.triangulate = triangulate,
.quad_method = quad_method,
@@ -410,7 +401,6 @@ void RNA_api_scene(StructRNA *srna)
func, "export_hair", 1, "Export Hair", "Exports hair particle systems as animated curves");
RNA_def_boolean(
func, "export_particles", 1, "Export Particles", "Exports non-hair particle systems");
- RNA_def_enum(func, "compression_type", rna_enum_abc_compression_items, 0, "Compression", "");
RNA_def_boolean(
func, "packuv", 0, "Export with packed UV islands", "Export with packed UV islands");
RNA_def_float(
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index 20ae69dc031..24c4818694f 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -30,6 +30,8 @@
#include "DNA_screen_types.h"
#include "DNA_workspace_types.h"
+#include "ED_info.h"
+
const EnumPropertyItem rna_enum_region_type_items[] = {
{RGN_TYPE_WINDOW, "WINDOW", 0, "Window", ""},
{RGN_TYPE_HEADER, "HEADER", 0, "Header", ""},
@@ -90,6 +92,12 @@ static bool rna_Screen_is_animation_playing_get(PointerRNA *UNUSED(ptr))
return wm ? (ED_screen_animation_playing(wm) != NULL) : 0;
}
+static bool rna_Screen_is_scrubbing_get(PointerRNA *ptr)
+{
+ bScreen *screen = (bScreen *)ptr->data;
+ return screen->scrubbing;
+}
+
static int rna_region_alignment_get(PointerRNA *ptr)
{
ARegion *region = ptr->data;
@@ -280,6 +288,11 @@ static void rna_View2D_view_to_region(
}
}
+static const char *rna_Screen_statusbar_info_get(struct bScreen *screen, Main *bmain, bContext *C)
+{
+ return ED_info_statusbar_string(bmain, screen, C);
+}
+
#else
/* Area.spaces */
@@ -354,7 +367,7 @@ static void rna_def_area(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Area_type_update");
prop = RNA_def_property(srna, "ui_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, DummyRNA_NULL_items); /* infact dummy */
+ RNA_def_property_enum_items(prop, DummyRNA_NULL_items); /* in fact dummy */
RNA_def_property_enum_default(prop, SPACE_VIEW3D << 16);
RNA_def_property_enum_funcs(
prop, "rna_Area_ui_type_get", "rna_Area_ui_type_set", "rna_Area_ui_type_itemf");
@@ -530,6 +543,9 @@ static void rna_def_screen(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
srna = RNA_def_struct(brna, "Screen", "ID");
RNA_def_struct_sdna(srna, "Screen"); /* it is actually bScreen but for 2.5 the dna is patched! */
RNA_def_struct_ui_text(
@@ -548,6 +564,12 @@ static void rna_def_screen(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, "rna_Screen_is_animation_playing_get", NULL);
RNA_def_property_ui_text(prop, "Animation Playing", "Animation playback is active");
+ prop = RNA_def_property(srna, "is_scrubbing", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_Screen_is_scrubbing_get", NULL);
+ RNA_def_property_ui_text(
+ prop, "User is Scrubbing", "True when the user is scrubbing through time");
+
prop = RNA_def_property(srna, "is_temporary", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_sdna(prop, NULL, "temp", 1);
@@ -558,11 +580,18 @@ static void rna_def_screen(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, "rna_Screen_fullscreen_get", NULL);
RNA_def_property_ui_text(prop, "Maximize", "An area is maximized, filling this screen");
+ /* Status Bar. */
+
prop = RNA_def_property(srna, "show_statusbar", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SCREEN_COLLAPSE_STATUSBAR);
- RNA_def_property_ui_text(prop, "Show Status Bar", "Show status bar");
+ RNA_def_property_ui_text(prop, "Show Status Bar", "Show Status Bar");
RNA_def_property_update(prop, 0, "rna_Screen_bar_update");
+ func = RNA_def_function(srna, "statusbar_info", "rna_Screen_statusbar_info_get");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_CONTEXT);
+ parm = RNA_def_string(func, "statusbar_info", NULL, 0, "Status Bar Info", "");
+ RNA_def_function_return(func, parm);
+
/* Define Anim Playback Areas */
prop = RNA_def_property(srna, "use_play_top_left_3d_editor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "redraws_flag", TIME_REGION);
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..d360e37588c 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -749,6 +749,25 @@ static IDProperty *rna_Sequence_idprops(PointerRNA *ptr, bool create)
return seq->prop;
}
+static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main *bmain)
+{
+ Scene *scene = (Scene *)scene_id;
+ bool has_reloaded;
+ bool can_produce_frames;
+
+ BKE_sequence_movie_reload_if_needed(bmain, scene, seq, &has_reloaded, &can_produce_frames);
+
+ if (has_reloaded && can_produce_frames) {
+ BKE_sequence_calc(scene, seq);
+ BKE_sequence_invalidate_cache_raw(scene, seq);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
+ }
+
+ return can_produce_frames;
+}
+
static PointerRNA rna_MovieSequence_metadata_get(Sequence *seq)
{
if (seq == NULL || seq->anims.first == NULL) {
@@ -843,6 +862,43 @@ static int rna_Sequence_input_count_get(PointerRNA *ptr)
return BKE_sequence_effect_get_num_inputs(seq->type);
}
+static void rna_Sequence_input_set(PointerRNA *ptr,
+ PointerRNA ptr_value,
+ struct ReportList *reports,
+ int input_num)
+{
+
+ Sequence *seq = ptr->data;
+ Sequence *input = ptr_value.data;
+
+ if (BKE_sequencer_render_loop_check(input, seq)) {
+ BKE_report(reports, RPT_ERROR, "Cannot reassign inputs: recursion detected.");
+ return;
+ }
+
+ switch (input_num) {
+ case 1:
+ seq->seq1 = input;
+ break;
+ case 2:
+ seq->seq2 = input;
+ break;
+ }
+}
+
+static void rna_Sequence_input_1_set(PointerRNA *ptr,
+ PointerRNA ptr_value,
+ struct ReportList *reports)
+{
+ rna_Sequence_input_set(ptr, ptr_value, reports, 1);
+}
+
+static void rna_Sequence_input_2_set(PointerRNA *ptr,
+ PointerRNA ptr_value,
+ struct ReportList *reports)
+{
+ rna_Sequence_input_set(ptr, ptr_value, reports, 2);
+}
# if 0
static void rna_SoundSequence_filename_set(PointerRNA *ptr, const char *value)
{
@@ -1253,6 +1309,24 @@ static void rna_Sequence_modifier_clear(Sequence *seq, bContext *C)
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
}
+static void rna_SequenceModifier_strip_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SequenceModifierData *smd = ptr->data;
+ Scene *scene = (Scene *)ptr->owner_id;
+ Editing *ed = BKE_sequencer_editing_get(scene, false);
+ Sequence *seq = sequence_get_by_modifier(ed, smd);
+ Sequence *target = (Sequence *)value.data;
+
+ if (target != NULL && BKE_sequencer_render_loop_check(target, seq)) {
+ BKE_report(reports, RPT_ERROR, "Recursion detected, can not use this strip");
+ return;
+ }
+
+ smd->mask_sequence = target;
+}
+
static float rna_Sequence_fps_get(PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
@@ -2126,7 +2200,7 @@ static void rna_def_proxy(StructRNA *srna)
prop = RNA_def_property(srna, "use_proxy", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXY);
RNA_def_property_ui_text(
- prop, "Use Proxy / Timecode", "Use a preview proxy and/or timecode index for this strip");
+ prop, "Use Proxy / Timecode", "Use a preview proxy and/or time-code index for this strip");
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_proxy_set");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
@@ -2174,6 +2248,7 @@ static void rna_def_effect_inputs(StructRNA *srna, int count)
prop = RNA_def_property(srna, "input_1", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "seq1");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_Sequence_input_1_set", NULL, NULL);
RNA_def_property_ui_text(prop, "Input 1", "First input for the effect strip");
}
@@ -2181,6 +2256,7 @@ static void rna_def_effect_inputs(StructRNA *srna, int count)
prop = RNA_def_property(srna, "input_2", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "seq2");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_Sequence_input_2_set", NULL, NULL);
RNA_def_property_ui_text(prop, "Input 2", "Second input for the effect strip");
}
@@ -2385,6 +2461,13 @@ static void rna_def_movie(BlenderRNA *brna)
"rna_Sequence_filepath_set");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update");
+ func = RNA_def_function(srna, "reload_if_needed", "rna_MovieSequence_reload_if_needed");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
+ /* return type */
+ parm = RNA_def_boolean(
+ func, "can_produce_frames", 0, "True if the strip can produce frames, False otherwise", "");
+ RNA_def_function_return(func, parm);
+
/* metadata */
func = RNA_def_function(srna, "metadata", "rna_MovieSequence_metadata_get");
RNA_def_function_ui_description(func, "Retrieve metadata of the movie file");
@@ -2565,29 +2648,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 +2679,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 +2734,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 +2803,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 +2832,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 +2866,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 +2963,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[] = {
@@ -3063,8 +3110,11 @@ static void rna_def_modifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "input_mask_strip", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "mask_sequence");
- RNA_def_property_pointer_funcs(
- prop, NULL, NULL, NULL, "rna_SequenceModifier_otherSequence_poll");
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SequenceModifier_strip_set",
+ NULL,
+ "rna_SequenceModifier_otherSequence_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Mask Strip", "Strip used as mask input for the modifier");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index 6c8f51f97a1..59cedf8fcb8 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -79,7 +79,6 @@ static Sequence *alloc_generic_sequence(
Editing *ed, const char *name, int frame_start, int channel, int type, const char *file)
{
Sequence *seq;
- Strip *strip;
StripElem *se;
seq = BKE_sequence_alloc(ed->seqbasep, frame_start, channel, type);
@@ -87,8 +86,7 @@ static Sequence *alloc_generic_sequence(
BLI_strncpy(seq->name + 2, name, sizeof(seq->name) - 2);
BKE_sequence_base_unique_name_recursive(&ed->seqbase, seq);
- seq->strip = strip = MEM_callocN(sizeof(Strip), "strip");
- seq->strip->us = 1;
+ Strip *strip = seq->strip;
if (file) {
strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
@@ -114,7 +112,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);
@@ -207,33 +205,28 @@ static Sequence *rna_Sequences_new_image(ID *id,
return seq;
}
-static Sequence *rna_Sequences_new_movie(ID *id,
- Editing *ed,
- ReportList *reports,
- const char *name,
- const char *file,
- int channel,
- int frame_start)
+static Sequence *rna_Sequences_new_movie(
+ ID *id, Editing *ed, const char *name, const char *file, int channel, int frame_start)
{
Scene *scene = (Scene *)id;
Sequence *seq;
StripAnim *sanim;
- struct anim *an = openanim(file, IB_rect, 0, NULL);
+ seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file);
+ struct anim *an = openanim(file, IB_rect, 0, NULL);
if (an == NULL) {
- BKE_report(reports, RPT_ERROR, "Sequences.new_movie: unable to open movie file");
- return NULL;
+ /* Without anim, the strip gets duration 0, which makes it impossible to select in the UI. */
+ seq->len = 1;
}
+ else {
+ sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
+ BLI_addtail(&seq->anims, sanim);
+ sanim->anim = an;
- seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file);
-
- sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
- BLI_addtail(&seq->anims, sanim);
- sanim->anim = an;
-
- seq->anim_preseek = IMB_anim_get_preseek(an);
- seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
+ seq->anim_preseek = IMB_anim_get_preseek(an);
+ seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
+ }
BKE_sequence_calc_disp(scene, seq);
BKE_sequence_invalidate_cache_composite(scene, seq);
@@ -265,7 +258,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);
@@ -376,13 +370,14 @@ static void rna_Sequences_remove(
Sequence *seq = seq_ptr->data;
Scene *scene = (Scene *)id;
- if (BLI_remlink_safe(&ed->seqbase, seq) == false) {
+ if (BLI_findindex(&ed->seqbase, seq) == -1) {
BKE_reportf(
reports, RPT_ERROR, "Sequence '%s' not in scene '%s'", seq->name + 2, scene->id.name + 2);
return;
}
- BKE_sequence_free(scene, seq, true);
+ BKE_sequencer_flag_for_removal(scene, &ed->seqbase, seq);
+ BKE_sequencer_remove_flagged_sequences(scene, &ed->seqbase);
RNA_POINTER_INVALIDATE(seq_ptr);
DEG_relations_tag_update(bmain);
@@ -447,6 +442,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 +464,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 +496,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 dependent 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)
@@ -639,7 +663,7 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "new_movie", "rna_Sequences_new_movie");
- RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Add a new movie sequence");
parm = RNA_def_string(func, "name", "Name", 0, "", "Name for the new sequence");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c
index 71f767fa93b..e1970a57a91 100644
--- a/source/blender/makesrna/intern/rna_shader_fx.c
+++ b/source/blender/makesrna/intern/rna_shader_fx.c
@@ -168,7 +168,7 @@ static char *rna_ShaderFx_path(PointerRNA *ptr)
static void rna_ShaderFx_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->owner_id);
+ WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ptr->owner_id);
}
static void rna_ShaderFx_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -220,7 +220,7 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "radius");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(prop, "Size", "Factor of Blur");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "samples");
@@ -228,18 +228,18 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 32, 2, -1);
RNA_def_property_int_default(prop, 4);
RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "rotation");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Rotation", "Rotation of the effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "use_dof_mode", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_BLUR_DOF_MODE);
RNA_def_property_ui_text(prop, "Use as Depth Of Field", "Blur using camera depth of field");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_colorize(BlenderRNA *brna)
@@ -256,27 +256,27 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "factor");
RNA_def_property_range(prop, 0, 1.0);
RNA_def_property_ui_text(prop, "Factor", "Mix factor");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "low_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "low_color");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Low Color", "First color used for effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "high_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "high_color");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "High Color", "Second color used for effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, rna_enum_shaderfx_colorize_modes_items);
RNA_def_property_ui_text(prop, "Mode", "Effect mode");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_wave(BlenderRNA *brna)
@@ -298,25 +298,25 @@ static void rna_def_shader_fx_wave(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "orientation");
RNA_def_property_enum_items(prop, prop_shaderfx_wave_type_items);
RNA_def_property_ui_text(prop, "Orientation", "Direction of the wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "amplitude");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_text(prop, "Amplitude", "Amplitude of Wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "period");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_text(prop, "Period", "Period of Wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "phase");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_pixel(BlenderRNA *brna)
@@ -334,14 +334,12 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna)
RNA_def_property_range(prop, 1, SHRT_MAX);
RNA_def_property_array(prop, 2);
RNA_def_property_ui_text(prop, "Size", "Pixel size");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "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");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ 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_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_rim(BlenderRNA *brna)
@@ -358,34 +356,34 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "offset");
RNA_def_property_range(prop, SHRT_MIN, SHRT_MAX);
RNA_def_property_ui_text(prop, "Offset", "Offset of the rim");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "rim_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "rim_rgb");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Rim Color", "Color used for Rim");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "mask_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "mask_rgb");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Mask Color", "Color that must be kept");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, rna_enum_shaderfx_rim_modes_items);
RNA_def_property_ui_text(prop, "Mode", "Blend mode");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "blur", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "blur");
RNA_def_property_range(prop, 0, SHRT_MAX);
RNA_def_property_ui_text(
prop, "Blur", "Number of pixels for blurring rim (set to 0 to disable)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "samples");
@@ -393,7 +391,7 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 32, 2, -1);
RNA_def_property_int_default(prop, 4);
RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_shadow(BlenderRNA *brna)
@@ -422,58 +420,58 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "offset");
RNA_def_property_range(prop, SHRT_MIN, SHRT_MAX);
RNA_def_property_ui_text(prop, "Offset", "Offset of the shadow");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "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");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "shadow_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "shadow_rgba");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Shadow Color", "Color used for Shadow");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "orientation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "orientation");
RNA_def_property_enum_items(prop, prop_shaderfx_shadow_type_items);
RNA_def_property_ui_text(prop, "Orientation", "Direction of the wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "amplitude");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_text(prop, "Amplitude", "Amplitude of Wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "period");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_text(prop, "Period", "Period of Wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "phase");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "rotation");
RNA_def_property_range(prop, DEG2RAD(-360), DEG2RAD(360));
RNA_def_property_ui_range(prop, DEG2RAD(-360), DEG2RAD(360), 5, 2);
RNA_def_property_ui_text(prop, "Rotation", "Rotation around center or object");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "blur", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "blur");
RNA_def_property_range(prop, 0, SHRT_MAX);
RNA_def_property_ui_text(
prop, "Blur", "Number of pixels for blurring shadow (set to 0 to disable)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "samples");
@@ -481,17 +479,17 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 32, 2, -1);
RNA_def_property_int_default(prop, 4);
RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "use_object", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SHADOW_USE_OBJECT);
RNA_def_property_ui_text(prop, "Use Object", "Use object as center of rotation");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "use_wave", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SHADOW_USE_WAVE);
RNA_def_property_ui_text(prop, "Wave", "Use wave effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_glow(BlenderRNA *brna)
@@ -509,41 +507,41 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "glow_color");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Glow Color", "Color used for generated glow");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "glow_color[3]");
RNA_def_property_range(prop, 0.0, 1.0f);
RNA_def_property_ui_text(prop, "Opacity", "Effect Opacity");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "select_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "select_color");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Select Color", "Color selected to apply glow");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, rna_enum_shaderfx_glow_modes_items);
RNA_def_property_ui_text(prop, "Mode", "Glow mode");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "threshold");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3);
RNA_def_property_ui_text(prop, "Threshold", "Limit to select color for glow effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
/* Use blur fields to make compatible with blur filter */
prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "blur");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(prop, "Size", "Size of the effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "samples");
@@ -551,26 +549,26 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 1, 32, 2, -1);
RNA_def_property_int_default(prop, 4);
RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "use_glow_under", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_GLOW_USE_ALPHA);
RNA_def_property_ui_text(
prop, "Glow Under", "Glow only areas with alpha (not supported with Regular blend mode)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "rotation");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Rotation", "Rotation of the effect");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
/* blend mode */
prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "blend_mode");
RNA_def_property_enum_items(prop, rna_enum_glow_blend_modes_items);
RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
static void rna_def_shader_fx_swirl(BlenderRNA *brna)
@@ -587,19 +585,19 @@ static void rna_def_shader_fx_swirl(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "radius");
RNA_def_property_range(prop, 0, SHRT_MAX);
RNA_def_property_ui_text(prop, "Radius", "Radius to apply");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "angle");
RNA_def_property_range(prop, DEG2RAD(-5 * 360), DEG2RAD(5 * 360));
RNA_def_property_ui_range(prop, DEG2RAD(-5 * 360), DEG2RAD(5 * 360), 5, 2);
RNA_def_property_ui_text(prop, "Angle", "Angle of rotation");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "use_transparent", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SWIRL_MAKE_TRANSPARENT);
RNA_def_property_ui_text(prop, "Transparent", "Make image transparent outside of radius");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Object to determine center location");
@@ -622,12 +620,12 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna)
prop = RNA_def_property(srna, "flip_horizontal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_HORIZONTAL);
RNA_def_property_ui_text(prop, "Horizontal", "Flip image horizontally");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
prop = RNA_def_property(srna, "flip_vertical", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_VERTICAL);
RNA_def_property_ui_text(prop, "Vertical", "Flip image vertically");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
}
void RNA_def_shader_fx(BlenderRNA *brna)
@@ -646,7 +644,7 @@ void RNA_def_shader_fx(BlenderRNA *brna)
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_ShaderFx_name_set");
RNA_def_property_ui_text(prop, "Name", "Effect name");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL);
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX | NA_RENAME, NULL);
RNA_def_struct_name_property(srna, prop);
/* enums */
@@ -663,7 +661,7 @@ void RNA_def_shader_fx(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Realtime", "Display effect in viewport");
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_ON, 1);
prop = RNA_def_property(srna, "show_render", PROP_BOOLEAN, PROP_NONE);
@@ -671,19 +669,19 @@ void RNA_def_shader_fx(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Render", "Use effect during render");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_ON, 1);
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, NULL);
prop = RNA_def_property(srna, "show_in_editmode", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Editmode);
RNA_def_property_ui_text(prop, "Edit Mode", "Display effect in Edit mode");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
RNA_def_property_ui_icon(prop, ICON_EDITMODE_HLT, 0);
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 df0d514fabf..155f5ab3043 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -1776,87 +1776,28 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSE
{
SpaceProperties *sbuts = (SpaceProperties *)(ptr->data);
EnumPropertyItem *item = NULL;
- int totitem = 0;
-
- if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TOOL);
- }
-
- if (totitem) {
- RNA_enum_item_add_separator(&item, &totitem);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_RENDER);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OUTPUT);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_VIEW_LAYER);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SCENE);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_WORLD);
- }
-
- if (totitem) {
- RNA_enum_item_add_separator(&item, &totitem);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OBJECT);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MODIFIER);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SHADERFX);
- }
- if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_CONSTRAINT);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_DATA)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_DATA);
- (item + totitem - 1)->icon = sbuts->dataicon;
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_BONE)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE_CONSTRAINT);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MATERIAL);
- }
+ /* We use 32 tabs maximum here so a flag for each can fit into a 32 bit integer flag.
+ * A theoretical maximum would be BCONTEXT_TOT * 2, with every tab displayed and a spacer
+ * in every other item. But this size is currently limited by the size of integer
+ * supported by RNA enums. */
+ int context_tabs_array[32];
+ int totitem = ED_buttons_tabs_list(sbuts, context_tabs_array);
+ BLI_assert(totitem <= ARRAY_SIZE(context_tabs_array));
+
+ int totitem_added = 0;
+ for (int i = 0; i < totitem; i++) {
+ if (context_tabs_array[i] == -1) {
+ RNA_enum_item_add_separator(&item, &totitem_added);
+ continue;
+ }
- if (totitem) {
- RNA_enum_item_add_separator(&item, &totitem);
- }
+ RNA_enum_items_add_value(&item, &totitem_added, buttons_context_items, context_tabs_array[i]);
- if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE);
+ /* Add the object data icon dynamically for the data tab. */
+ if (context_tabs_array[i] == BCONTEXT_DATA) {
+ (item + totitem_added - 1)->icon = sbuts->dataicon;
+ }
}
RNA_enum_item_end(&item, &totitem);
@@ -2181,7 +2122,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;
@@ -2213,7 +2153,6 @@ static void rna_SpaceNodeEditor_simulation_set(PointerRNA *ptr,
}
snode->id = &sim->id;
}
-# endif
static int rna_SpaceNodeEditor_tree_type_get(PointerRNA *ptr)
{
@@ -4317,12 +4256,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),
@@ -5534,9 +5471,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,
@@ -5580,21 +5515,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"},
@@ -6276,7 +6207,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");
@@ -6287,7 +6217,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);
@@ -6613,7 +6542,7 @@ static void rna_def_space_clip(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_graph_only_selected", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SC_SHOW_GRAPH_SEL_ONLY);
RNA_def_property_ui_text(
- prop, "Only Selected", "Only include channels relating to selected objects and data");
+ prop, "Only Show Selected", "Only include channels relating to selected objects and data");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, 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_tracking.c b/source/blender/makesrna/intern/rna_tracking.c
index 507d06482df..27297a4d19a 100644
--- a/source/blender/makesrna/intern/rna_tracking.c
+++ b/source/blender/makesrna/intern/rna_tracking.c
@@ -834,22 +834,22 @@ static const EnumPropertyItem tracker_motion_model[] = {
{TRACK_MOTION_MODEL_TRANSLATION_ROTATION_SCALE,
"LocRotScale",
0,
- "LocRotScale",
+ "Location, Rotation & Scale",
"Search for markers that are translated, rotated, and scaled between frames"},
{TRACK_MOTION_MODEL_TRANSLATION_SCALE,
"LocScale",
0,
- "LocScale",
+ "Location & Scale",
"Search for markers that are translated and scaled between frames"},
{TRACK_MOTION_MODEL_TRANSLATION_ROTATION,
"LocRot",
0,
- "LocRot",
+ "Location & Rotation",
"Search for markers that are translated and rotated between frames"},
{TRACK_MOTION_MODEL_TRANSLATION,
"Loc",
0,
- "Loc",
+ "Location",
"Search for markers that are translated between frames"},
{0, NULL, 0, NULL, NULL},
};
@@ -1036,7 +1036,6 @@ static void rna_def_trackingSettings(BlenderRNA *brna)
/* default_tracking_motion_model */
prop = RNA_def_property(srna, "default_motion_model", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_enum_items(prop, tracker_motion_model);
RNA_def_property_ui_text(prop, "Motion Model", "Default motion model to use for tracking");
@@ -1070,7 +1069,6 @@ static void rna_def_trackingSettings(BlenderRNA *brna)
/* default minimal correlation */
prop = RNA_def_property(srna, "default_correlation_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_float_sdna(prop, NULL, "default_minimum_correlation");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05, 3);
@@ -1203,7 +1201,6 @@ static void rna_def_trackingCamera(BlenderRNA *brna)
prop = RNA_def_property(srna, "units", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "units");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_enum_items(prop, camera_units_items);
RNA_def_property_ui_text(prop, "Units", "Units used for camera focal length");
@@ -2443,7 +2440,7 @@ static void rna_def_trackingDopesheet(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_only_selected", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_DOPE_SELECTED_ONLY);
RNA_def_property_ui_text(
- prop, "Only Selected", "Only include channels relating to selected objects and data");
+ prop, "Only Show Selected", "Only include channels relating to selected objects and data");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, 0);
RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_trackingDopesheet_tagUpdate");
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index 1449c410d18..5228f00d0de 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -227,6 +227,9 @@ static void rna_Panel_unregister(Main *bmain, StructRNA *type)
}
}
}
+ /* The unregistered panel might have had a template that added instanced panels,
+ * so remove them just in case. They can be re-added on redraw anyway. */
+ UI_panels_free_instanced(NULL, region);
}
}
}
@@ -1290,7 +1293,7 @@ static void rna_def_panel(BlenderRNA *brna)
0,
"Instanced Panel",
"Multiple panels with this type can be used as part of a list depending on data external "
- "to the UI. Used to create panels for the modifiers and other stacks."},
+ "to the UI. Used to create panels for the modifiers and other stacks"},
{PNL_LAYOUT_HEADER_EXPAND,
"HEADER_LAYOUT_EXPAND",
0,
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..6ce6098d0c3 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -179,6 +179,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "BKE_blender.h"
# include "BKE_global.h"
# include "BKE_idprop.h"
+# include "BKE_image.h"
# include "BKE_main.h"
# include "BKE_mesh_runtime.h"
# include "BKE_paint.h"
@@ -187,8 +188,9 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "DEG_depsgraph.h"
-# include "GPU_draw.h"
+# include "GPU_extensions.h"
# include "GPU_select.h"
+# include "GPU_texture.h"
# include "BLF_api.h"
@@ -328,6 +330,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),
@@ -360,13 +364,14 @@ static void rna_userdef_load_ui_update(Main *UNUSED(bmain), Scene *UNUSED(scene)
static void rna_userdef_anisotropic_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- GPU_set_anisotropic(U.anisotropic_filter);
+ GPU_samplers_free();
+ GPU_samplers_init();
rna_userdef_update(bmain, scene, ptr);
}
static void rna_userdef_gl_texture_limit_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- GPU_free_images(bmain);
+ BKE_image_free_all_gputextures(bmain);
rna_userdef_update(bmain, scene, ptr);
}
@@ -437,13 +442,12 @@ static void rna_userdef_timecode_style_set(PointerRNA *ptr, int value)
UserDef *userdef = (UserDef *)ptr->data;
int required_size = userdef->v2d_min_gridsize;
- /* set the timecode style */
+ /* Set the time-code style. */
userdef->timecode_style = value;
- /* adjust the v2d gridsize if needed so that timecodes don't overlap
+ /* Adjust the v2d grid-size if needed so that time-codes don't overlap
* NOTE: most of these have been hand-picked to avoid overlaps while still keeping
- * things from getting too blown out
- */
+ * things from getting too blown out. */
switch (value) {
case USER_TIMECODE_MINIMAL:
case USER_TIMECODE_SECONDS_ONLY:
@@ -1064,6 +1068,11 @@ static void rna_UserDef_studiolight_light_ambient_get(PointerRNA *ptr, float *va
copy_v3_v3(values, sl->light_ambient);
}
+int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char **UNUSED(r_info))
+{
+ return GPU_mem_stats_supported() ? PROP_EDITABLE : 0;
+}
+
#else
# define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \
@@ -2270,7 +2279,7 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna)
prop = RNA_def_property(srna, "editmesh_active", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
- RNA_def_property_ui_text(prop, "Active Vert/Edge/Face", "");
+ RNA_def_property_ui_text(prop, "Active Vertex/Edge/Face", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_COLOR_GAMMA);
@@ -2969,7 +2978,7 @@ static void rna_def_userdef_theme_space_image(BlenderRNA *brna)
prop = RNA_def_property(srna, "editmesh_active", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
- RNA_def_property_ui_text(prop, "Active Vert/Edge/Face", "");
+ RNA_def_property_ui_text(prop, "Active Vertex/Edge/Face", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "wire_edit", PROP_FLOAT, PROP_COLOR_GAMMA);
@@ -3971,12 +3980,13 @@ static void rna_def_userdef_studiolights(BlenderRNA *brna)
func = RNA_def_function(srna, "new", "rna_StudioLights_new");
RNA_def_function_ui_description(func, "Create studiolight from default lighting");
- parm = RNA_def_string(func,
- "path",
- NULL,
- 0,
- "Path",
- "Path to the file that will contain the lighing info (without extension)");
+ parm = RNA_def_string(
+ func,
+ "path",
+ NULL,
+ 0,
+ "Path",
+ "Path to the file that will contain the lighting info (without extension)");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(func, "studio_light", "StudioLight", "", "Newly created StudioLight");
RNA_def_function_return(func, parm);
@@ -4768,6 +4778,29 @@ static void rna_def_userdef_view(BlenderRNA *brna)
"Translate New Names",
"Translate the names of new data-blocks (objects, materials...)");
RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+ /* Statusbar. */
+
+ prop = RNA_def_property(srna, "show_statusbar_memory", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_MEMORY);
+ RNA_def_property_ui_text(prop, "Show Memory", "Show Blender memory usage");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "show_statusbar_vram", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_VRAM);
+ RNA_def_property_ui_text(prop, "Show VRAM", "Show GPU video memory usage");
+ RNA_def_property_editable_func(prop, "rna_show_statusbar_vram_editable");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "show_statusbar_version", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_VERSION);
+ RNA_def_property_ui_text(prop, "Show Version", "Show Blender version string");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "show_statusbar_stats", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "statusbar_flag", STATUSBAR_SHOW_STATS);
+ RNA_def_property_ui_text(prop, "Show Statistics", "Show scene statistics");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_INFO, "rna_userdef_update");
}
static void rna_def_userdef_edit(BlenderRNA *brna)
@@ -4957,7 +4990,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
/* grease pencil */
prop = RNA_def_property(srna, "grease_pencil_manhattan_distance", PROP_INT, PROP_PIXEL);
- RNA_def_property_int_sdna(prop, NULL, "gp_manhattendist");
+ RNA_def_property_int_sdna(prop, NULL, "gp_manhattandist");
RNA_def_property_range(prop, 0, 100);
RNA_def_property_ui_text(prop,
"Grease Pencil Manhattan Distance",
@@ -5044,7 +5077,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 +5094,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 +5103,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 +5918,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 +6101,24 @@ 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");
+
+ prop = RNA_def_property(srna, "use_sculpt_vertex_colors", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_vertex_colors", 1);
+ RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "Use the new Vertex Painting system");
}
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 6749aa9495a..bfd99c01551 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 78db7de2ac2..cf87e3598b6 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -23,8 +23,9 @@ set(INC
intern
../blenfont
../blenkernel
- ../blentranslation
../blenlib
+ ../blenloader
+ ../blentranslation
../bmesh
../depsgraph
../editors/include
@@ -34,6 +35,9 @@ set(INC
../windowmanager
../../../intern/eigen
../../../intern/guardedalloc
+
+ # dna_type_offsets.h in BLO_read_write.h
+ ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
)
set(INC_SYS
@@ -171,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 ad8d6ac8b8a..b62c03d1b03 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -34,9 +34,9 @@
#include "DNA_screen_types.h"
#include "BKE_action.h"
+#include "BKE_armature.h"
#include "BKE_context.h"
#include "BKE_editmesh.h"
-#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
@@ -49,6 +49,8 @@
#include "RNA_access.h"
+#include "BLO_read_write.h"
+
#include "DEG_depsgraph_query.h"
#include "bmesh.h"
@@ -74,7 +76,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
ArmatureModifierData *tamd = (ArmatureModifierData *)target;
BKE_modifier_copydata_generic(md, target, flag);
- tamd->prevCos = NULL;
+ tamd->vert_coords_prev = NULL;
}
static void requiredDataMask(Object *UNUSED(ob),
@@ -146,88 +148,64 @@ static void deformVerts(ModifierData *md,
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
- armature_deform_verts(amd->object,
- ctx->object,
- mesh,
- vertexCos,
- NULL,
- numVerts,
- amd->deformflag,
- (float(*)[3])amd->prevCos,
- amd->defgrp_name,
- NULL);
+ BKE_armature_deform_coords_with_mesh(amd->object,
+ ctx->object,
+ vertexCos,
+ NULL,
+ numVerts,
+ amd->deformflag,
+ amd->vert_coords_prev,
+ amd->defgrp_name,
+ mesh);
/* free cache */
- if (amd->prevCos) {
- MEM_freeN(amd->prevCos);
- amd->prevCos = NULL;
- }
+ MEM_SAFE_FREE(amd->vert_coords_prev);
}
static void deformVertsEM(ModifierData *md,
const ModifierEvalContext *ctx,
struct BMEditMesh *em,
- Mesh *mesh,
+ Mesh *UNUSED(mesh),
float (*vertexCos)[3],
int numVerts)
{
ArmatureModifierData *amd = (ArmatureModifierData *)md;
- Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, numVerts, false, false);
-
- /* TODO(Campbell): use edit-mode data only (remove this line). */
- if (mesh_src != NULL) {
- BKE_mesh_wrapper_ensure_mdata(mesh_src);
- }
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
- armature_deform_verts(amd->object,
- ctx->object,
- mesh_src,
- vertexCos,
- NULL,
- numVerts,
- amd->deformflag,
- (float(*)[3])amd->prevCos,
- amd->defgrp_name,
- NULL);
+ BKE_armature_deform_coords_with_editmesh(amd->object,
+ ctx->object,
+ vertexCos,
+ NULL,
+ numVerts,
+ amd->deformflag,
+ amd->vert_coords_prev,
+ amd->defgrp_name,
+ em);
/* free cache */
- if (amd->prevCos) {
- MEM_freeN(amd->prevCos);
- amd->prevCos = NULL;
- }
-
- if (mesh_src != mesh) {
- BKE_id_free(NULL, mesh_src);
- }
+ MEM_SAFE_FREE(amd->vert_coords_prev);
}
static void deformMatricesEM(ModifierData *md,
const ModifierEvalContext *ctx,
struct BMEditMesh *em,
- Mesh *mesh,
+ Mesh *UNUSED(mesh),
float (*vertexCos)[3],
float (*defMats)[3][3],
int numVerts)
{
ArmatureModifierData *amd = (ArmatureModifierData *)md;
- Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, numVerts, false, false);
-
- armature_deform_verts(amd->object,
- ctx->object,
- mesh_src,
- vertexCos,
- defMats,
- numVerts,
- amd->deformflag,
- NULL,
- amd->defgrp_name,
- NULL);
-
- if (mesh_src != mesh) {
- BKE_id_free(NULL, mesh_src);
- }
+
+ BKE_armature_deform_coords_with_editmesh(amd->object,
+ ctx->object,
+ vertexCos,
+ defMats,
+ numVerts,
+ amd->deformflag,
+ NULL,
+ amd->defgrp_name,
+ em);
}
static void deformMatrices(ModifierData *md,
@@ -240,16 +218,15 @@ static void deformMatrices(ModifierData *md,
ArmatureModifierData *amd = (ArmatureModifierData *)md;
Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, numVerts, false, false);
- armature_deform_verts(amd->object,
- ctx->object,
- mesh_src,
- vertexCos,
- defMats,
- numVerts,
- amd->deformflag,
- NULL,
- amd->defgrp_name,
- NULL);
+ BKE_armature_deform_coords_with_mesh(amd->object,
+ ctx->object,
+ vertexCos,
+ defMats,
+ numVerts,
+ amd->deformflag,
+ NULL,
+ amd->defgrp_name,
+ mesh_src);
if (!ELEM(mesh_src, NULL, mesh)) {
BKE_id_free(NULL, mesh_src);
@@ -274,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);
@@ -286,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",
@@ -317,4 +301,6 @@ ModifierTypeInfo modifierType_Armature = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index 68e12a8bbef..48d69f8e02c 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -76,6 +76,9 @@ static void initData(ModifierData *md)
amd->fit_type = MOD_ARR_FIXEDCOUNT;
amd->offset_type = MOD_ARR_OFF_RELATIVE;
amd->flags = 0;
+
+ /* Open the first subpanel by default, it corresspnds to Relative offset which is enabled too. */
+ md->ui_expand_flag = (1 << 0) | (1 << 1);
}
static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData)
@@ -835,7 +838,6 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
static void panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -857,12 +859,6 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "curve", 0, NULL, ICON_NONE);
}
- uiItemS(layout);
-
- col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "start_cap", 0, IFACE_("Cap Start"), ICON_NONE);
- uiItemR(col, &ptr, "end_cap", 0, IFACE_("End"), ICON_NONE);
-
modifier_panel_end(layout, &ptr);
}
@@ -984,6 +980,21 @@ static void uv_panel_draw(const bContext *C, Panel *panel)
uiItemR(col, &ptr, "offset_v", UI_ITEM_R_EXPAND, IFACE_("V"), ICON_NONE);
}
+static void caps_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "start_cap", 0, IFACE_("Cap Start"), ICON_NONE);
+ uiItemR(col, &ptr, "end_cap", 0, IFACE_("End"), ICON_NONE);
+}
+
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Array, panel_draw);
@@ -1004,6 +1015,7 @@ static void panelRegister(ARegionType *region_type)
modifier_subpanel_register(
region_type, "merge", "", symmetry_panel_header_draw, symmetry_panel_draw, panel_type);
modifier_subpanel_register(region_type, "uv", "UVs", NULL, uv_panel_draw, panel_type);
+ modifier_subpanel_register(region_type, "caps", "Caps", NULL, caps_panel_draw, panel_type);
}
ModifierTypeInfo modifierType_Array = {
@@ -1038,4 +1050,6 @@ ModifierTypeInfo modifierType_Array = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index 76b8985975e..c0428785009 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"
@@ -71,6 +73,7 @@ static void initData(ModifierData *md)
bmd->face_str_mode = MOD_BEVEL_FACE_STRENGTH_NONE;
bmd->miter_inner = MOD_BEVEL_MITER_SHARP;
bmd->miter_outer = MOD_BEVEL_MITER_SHARP;
+ bmd->affect_type = MOD_BEVEL_AFFECT_EDGES;
bmd->spread = 0.1f;
bmd->mat = -1;
bmd->profile = 0.5f;
@@ -115,9 +118,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MDeformVert *dvert = NULL;
BevelModifierData *bmd = (BevelModifierData *)md;
const float threshold = cosf(bmd->bevel_angle + 0.000000175f);
- 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,8 +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;
bm = BKE_mesh_to_bmesh_ex(mesh,
@@ -150,7 +151,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MOD_get_vgroup(ctx->object, mesh, bmd->defgrp_name, &dvert, &vgroup);
}
- if (vertex_only) {
+ if (bmd->affect_type == MOD_BEVEL_AFFECT_VERTICES) {
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (bmd->lim_flags & MOD_BEVEL_WEIGHT) {
weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT);
@@ -225,9 +226,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BM_mesh_bevel(bm,
value,
offset_type,
+ profile_type,
bmd->res,
bmd->profile,
- vertex_only,
+ bmd->affect_type,
bmd->lim_flags & MOD_BEVEL_WEIGHT,
do_clamp,
dvert,
@@ -242,9 +244,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
miter_inner,
spread,
mesh->smoothresh,
- use_custom_profile,
bmd->custom_profile,
- vmesh_method);
+ bmd->vmesh_method);
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
@@ -277,48 +278,38 @@ static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED
static void panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *col;
+ uiLayout *col, *sub;
uiLayout *layout = panel->layout;
PointerRNA ptr;
PointerRNA ob_ptr;
modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+ bool edge_bevel = RNA_enum_get(&ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
+
+ uiItemR(layout, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
uiLayoutSetPropSep(layout, true);
col = uiLayoutColumn(layout, false);
- const char *offset_name = "";
+ uiItemR(col, &ptr, "offset_type", 0, NULL, ICON_NONE);
if (RNA_enum_get(&ptr, "offset_type") == BEVEL_AMT_PERCENT) {
uiItemR(col, &ptr, "width_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;
- }
- uiItemR(col, &ptr, "width", 0, IFACE_(offset_name), ICON_NONE);
+ uiItemR(col, &ptr, "width", 0, IFACE_("Amount"), ICON_NONE);
}
- uiItemR(col, &ptr, "offset_type", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
-
- uiItemS(layout);
-
col = uiLayoutColumn(layout, false);
uiItemR(col, &ptr, "limit_method", 0, NULL, ICON_NONE);
int limit_method = RNA_enum_get(&ptr, "limit_method");
if (limit_method == MOD_BEVEL_ANGLE) {
+ sub = uiLayoutColumn(col, false);
+ uiLayoutSetActive(sub, edge_bevel);
uiItemR(col, &ptr, "angle_limit", 0, NULL, ICON_NONE);
}
else if (limit_method == MOD_BEVEL_VGROUP) {
@@ -328,25 +319,78 @@ static void panel_draw(const bContext *C, Panel *panel)
modifier_panel_end(layout, &ptr);
}
+static void profile_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ int profile_type = RNA_enum_get(&ptr, "profile_type");
+ int miter_inner = RNA_enum_get(&ptr, "miter_inner");
+ int miter_outer = RNA_enum_get(&ptr, "miter_outer");
+ bool edge_bevel = RNA_enum_get(&ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
+
+ uiItemR(layout, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ uiLayoutSetPropSep(layout, true);
+
+ if (ELEM(profile_type, MOD_BEVEL_PROFILE_SUPERELLIPSE, MOD_BEVEL_PROFILE_CUSTOM)) {
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(
+ row,
+ profile_type == MOD_BEVEL_PROFILE_SUPERELLIPSE ||
+ (profile_type == MOD_BEVEL_PROFILE_CUSTOM && edge_bevel &&
+ !((miter_inner == MOD_BEVEL_MITER_SHARP) && (miter_outer == MOD_BEVEL_MITER_SHARP))));
+ uiItemR(row,
+ &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 geometry_panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *row;
uiLayout *layout = panel->layout;
PointerRNA ptr;
modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+ bool edge_bevel = RNA_enum_get(&ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
+
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "miter_inner", 0, IFACE_("Miter Inner"), ICON_NONE);
- uiItemR(layout, &ptr, "miter_outer", 0, IFACE_("Outer"), ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, edge_bevel);
+ uiItemR(row, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, edge_bevel);
+ uiItemR(row, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) {
- uiItemR(layout, &ptr, "spread", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, edge_bevel);
+ uiItemR(row, &ptr, "spread", 0, NULL, ICON_NONE);
}
uiItemS(layout);
- uiItemR(layout, &ptr, "vmesh_method", 0, IFACE_("Intersections"), ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, edge_bevel);
+ uiItemR(row, &ptr, "vmesh_method", 0, IFACE_("Intersections"), ICON_NONE);
uiItemR(layout, &ptr, "use_clamp_overlap", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "loop_slide", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, edge_bevel);
+ uiItemR(row, &ptr, "loop_slide", 0, NULL, ICON_NONE);
}
static void shading_panel_draw(const bContext *C, Panel *panel)
@@ -357,11 +401,14 @@ static void shading_panel_draw(const bContext *C, Panel *panel)
PointerRNA ptr;
modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+ bool edge_bevel = RNA_enum_get(&ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
+
uiLayoutSetPropSep(layout, true);
uiItemR(layout, &ptr, "harden_normals", 0, NULL, ICON_NONE);
col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark"));
+ uiLayoutSetActive(col, edge_bevel);
uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seam"), ICON_NONE);
uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
@@ -369,53 +416,34 @@ static void shading_panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "face_strength_mode", 0, NULL, ICON_NONE);
}
-static void profile_panel_draw(const bContext *C, Panel *panel)
+static void panelRegister(ARegionType *region_type)
{
- 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);
+ PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Bevel, panel_draw);
+ modifier_subpanel_register(
+ region_type, "profile", "Profile", NULL, profile_panel_draw, panel_type);
+ 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 custom_profile_panel_draw_header(const bContext *C, Panel *panel)
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
{
- uiLayout *layout = panel->layout;
+ const BevelModifierData *bmd = (const BevelModifierData *)md;
- PointerRNA ptr;
- modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
-
- uiItemR(layout, &ptr, "use_custom_profile", 0, NULL, ICON_NONE);
+ if (bmd->custom_profile) {
+ BKE_curveprofile_blend_write(writer, bmd->custom_profile);
+ }
}
-static void custom_profile_panel_draw(const bContext *C, Panel *panel)
+static void blendRead(BlendDataReader *reader, ModifierData *md)
{
- PointerRNA ptr;
- modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
- uiLayout *layout = panel->layout;
-
- uiLayoutSetActive(layout, RNA_boolean_get(&ptr, "use_custom_profile"));
-
- uiTemplateCurveProfile(layout, &ptr, "custom_profile");
-}
+ BevelModifierData *bmd = (BevelModifierData *)md;
-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(
- 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);
+ BLO_read_data_address(reader, &bmd->custom_profile);
+ if (bmd->custom_profile) {
+ BKE_curveprofile_blend_read(reader, bmd->custom_profile);
+ }
}
ModifierTypeInfo modifierType_Bevel = {
@@ -446,4 +474,6 @@ ModifierTypeInfo modifierType_Bevel = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* uiPanel */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
index c2412b295ff..1d39aa786a5 100644
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ b/source/blender/modifiers/intern/MOD_boolean.c
@@ -364,9 +364,10 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA ptr;
modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+ uiItemR(layout, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "operation", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE);
@@ -413,4 +414,6 @@ ModifierTypeInfo modifierType_Boolean = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 93f97c7c5b8..be0ca6af032 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -360,4 +360,6 @@ ModifierTypeInfo modifierType_Build = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index e2653701970..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);
@@ -607,4 +607,6 @@ ModifierTypeInfo modifierType_Cast = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index c7551b1be00..f3ee14e0206 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -312,4 +312,6 @@ ModifierTypeInfo modifierType_Cloth = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index c58ba3b62dc..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",
@@ -299,4 +329,6 @@ ModifierTypeInfo modifierType_Collision = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index f277e8bc12c..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,4 +871,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index a2348539779..760e4717b6d 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -33,8 +33,9 @@
#include "DNA_screen_types.h"
#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_deform.h"
#include "BKE_editmesh.h"
-#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
@@ -129,16 +130,17 @@ static void deformVerts(ModifierData *md,
int defgrp_index = -1;
MOD_get_vgroup(ctx->object, mesh_src, cmd->name, &dvert, &defgrp_index);
- /* silly that defaxis and curve_deform_verts are off by 1
+ /* Silly that defaxis and BKE_curve_deform_coords are off by 1
* but leave for now to save having to call do_versions */
- curve_deform_verts(cmd->object,
- ctx->object,
- vertexCos,
- numVerts,
- dvert,
- defgrp_index,
- cmd->flag,
- cmd->defaxis - 1);
+
+ BKE_curve_deform_coords(cmd->object,
+ ctx->object,
+ vertexCos,
+ numVerts,
+ dvert,
+ defgrp_index,
+ cmd->flag,
+ cmd->defaxis - 1);
if (!ELEM(mesh_src, NULL, mesh)) {
BKE_id_free(NULL, mesh_src);
@@ -147,22 +149,41 @@ static void deformVerts(ModifierData *md,
static void deformVertsEM(ModifierData *md,
const ModifierEvalContext *ctx,
- struct BMEditMesh *em,
- Mesh *mesh,
+ BMEditMesh *em,
+ Mesh *UNUSED(mesh),
float (*vertexCos)[3],
int numVerts)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, numVerts, false, false);
+ CurveModifierData *cmd = (CurveModifierData *)md;
+ bool use_dverts = false;
+ int defgrp_index = -1;
- /* TODO(Campbell): use edit-mode data only (remove this line). */
- if (mesh_src != NULL) {
- BKE_mesh_wrapper_ensure_mdata(mesh_src);
+ if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') {
+ defgrp_index = BKE_object_defgroup_name_index(ctx->object, cmd->name);
+ if (defgrp_index != -1) {
+ use_dverts = true;
+ }
}
- deformVerts(md, ctx, mesh_src, vertexCos, numVerts);
-
- if (!ELEM(mesh_src, NULL, mesh)) {
- BKE_id_free(NULL, mesh_src);
+ if (use_dverts) {
+ BKE_curve_deform_coords_with_editmesh(cmd->object,
+ ctx->object,
+ vertexCos,
+ numVerts,
+ defgrp_index,
+ cmd->flag,
+ cmd->defaxis - 1,
+ em);
+ }
+ else {
+ BKE_curve_deform_coords(cmd->object,
+ ctx->object,
+ vertexCos,
+ numVerts,
+ NULL,
+ defgrp_index,
+ cmd->flag,
+ cmd->defaxis - 1);
}
}
@@ -220,4 +241,6 @@ ModifierTypeInfo modifierType_Curve = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index e9436147649..0b205ec4fc5 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -506,4 +506,6 @@ ModifierTypeInfo modifierType_DataTransfer = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 5b494baea1a..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);
@@ -307,4 +307,6 @@ ModifierTypeInfo modifierType_Decimate = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index 7c119f4b128..9b9dd0a079c 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");
@@ -452,12 +454,12 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetActive(col, has_texture);
uiItemR(col, &ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE);
if (texture_coords == MOD_DISP_MAP_OBJECT) {
- uiItemR(col, &ptr, "texture_coords_object", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "texture_coords_object", 0, IFACE_("Object"), ICON_NONE);
PointerRNA texture_coords_obj_ptr = RNA_pointer_get(&ptr, "texture_coords_object");
if (!RNA_pointer_is_null(&texture_coords_obj_ptr) &&
(RNA_enum_get(&texture_coords_obj_ptr, "type") == OB_ARMATURE)) {
PointerRNA texture_coords_obj_data_ptr = RNA_pointer_get(&texture_coords_obj_ptr, "data");
- uiItemPointerR(layout,
+ uiItemPointerR(col,
&ptr,
"texture_coords_bone",
&texture_coords_obj_data_ptr,
@@ -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);
@@ -528,4 +530,6 @@ ModifierTypeInfo modifierType_Displace = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index f8a100f1f0a..0706d4259bd 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -232,4 +232,6 @@ ModifierTypeInfo modifierType_DynamicPaint = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ freeRuntimeData,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index 0134b5c9d64..b166a140ddc 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -196,4 +196,6 @@ ModifierTypeInfo modifierType_EdgeSplit = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index 0c1eedd429a..4e46e135b72 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"
@@ -252,7 +254,7 @@ static void remap_faces_3_6_9_12(Mesh *mesh,
Mesh *split,
MFace *mf,
int *facepa,
- int *vertpa,
+ const int *vertpa,
int i,
EdgeHash *eh,
int cur,
@@ -320,7 +322,7 @@ static void remap_faces_5_10(Mesh *mesh,
Mesh *split,
MFace *mf,
int *facepa,
- int *vertpa,
+ const int *vertpa,
int i,
EdgeHash *eh,
int cur,
@@ -376,7 +378,7 @@ static void remap_faces_15(Mesh *mesh,
Mesh *split,
MFace *mf,
int *facepa,
- int *vertpa,
+ const int *vertpa,
int i,
EdgeHash *eh,
int cur,
@@ -460,7 +462,7 @@ static void remap_faces_7_11_13_14(Mesh *mesh,
Mesh *split,
MFace *mf,
int *facepa,
- int *vertpa,
+ const int *vertpa,
int i,
EdgeHash *eh,
int cur,
@@ -529,7 +531,7 @@ static void remap_faces_19_21_22(Mesh *mesh,
Mesh *split,
MFace *mf,
int *facepa,
- int *vertpa,
+ const int *vertpa,
int i,
EdgeHash *eh,
int cur,
@@ -583,7 +585,7 @@ static void remap_faces_23(Mesh *mesh,
Mesh *split,
MFace *mf,
int *facepa,
- int *vertpa,
+ const int *vertpa,
int i,
EdgeHash *eh,
int cur,
@@ -1115,7 +1117,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
explode->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
if (psmd->psys->lattice_deform_data) {
- end_latt_deform(psmd->psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psmd->psys->lattice_deform_data);
psmd->psys->lattice_deform_data = NULL;
}
@@ -1187,7 +1189,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
static void panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *row;
+ uiLayout *row, *col;
uiLayout *layout = panel->layout;
int toggles_flag = UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE;
@@ -1209,8 +1211,9 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "use_edge_cut", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_size", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "use_edge_cut", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "use_size", 0, NULL, ICON_NONE);
modifier_vgroup_ui(layout, &ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL);
@@ -1228,6 +1231,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",
@@ -1257,4 +1267,6 @@ ModifierTypeInfo modifierType_Explode = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 0ef062d72d5..e994b80ddea 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -59,13 +59,13 @@
static void initData(ModifierData *md)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- mmd->domain = NULL;
- mmd->flow = NULL;
- mmd->effector = NULL;
- mmd->type = 0;
- mmd->time = -1;
+ fmd->domain = NULL;
+ fmd->flow = NULL;
+ fmd->effector = NULL;
+ fmd->type = 0;
+ fmd->time = -1;
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
@@ -73,11 +73,11 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
#ifndef WITH_FLUID
UNUSED_VARS(md, target, flag);
#else
- const FluidModifierData *mmd = (const FluidModifierData *)md;
- FluidModifierData *tmmd = (FluidModifierData *)target;
+ const FluidModifierData *fmd = (const FluidModifierData *)md;
+ FluidModifierData *tfmd = (FluidModifierData *)target;
- BKE_fluid_modifier_free(tmmd);
- BKE_fluid_modifier_copy(mmd, tmmd, flag);
+ BKE_fluid_modifier_free(tfmd);
+ BKE_fluid_modifier_copy(fmd, tfmd, flag);
#endif /* WITH_FLUID */
}
@@ -86,9 +86,9 @@ static void freeData(ModifierData *md)
#ifndef WITH_FLUID
UNUSED_VARS(md);
#else
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- BKE_fluid_modifier_free(mmd);
+ BKE_fluid_modifier_free(fmd);
#endif /* WITH_FLUID */
}
@@ -96,16 +96,16 @@ static void requiredDataMask(Object *UNUSED(ob),
ModifierData *md,
CustomData_MeshMasks *r_cddata_masks)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if (mmd && (mmd->type & MOD_FLUID_TYPE_FLOW) && mmd->flow) {
- if (mmd->flow->source == FLUID_FLOW_SOURCE_MESH) {
+ if (fmd && (fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow) {
+ if (fmd->flow->source == FLUID_FLOW_SOURCE_MESH) {
/* vertex groups */
- if (mmd->flow->vgroup_density) {
+ if (fmd->flow->vgroup_density) {
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
/* uv layer */
- if (mmd->flow->texture_type == FLUID_FLOW_TEXTURE_MAP_UV) {
+ if (fmd->flow->texture_type == FLUID_FLOW_TEXTURE_MAP_UV) {
r_cddata_masks->fmask |= CD_MASK_MTFACE;
}
}
@@ -118,7 +118,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
UNUSED_VARS(md, ctx);
return me;
#else
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
Mesh *result = NULL;
if (ctx->flag & MOD_APPLY_ORCO) {
@@ -127,7 +127,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
- result = BKE_fluid_modifier_do(mmd, ctx->depsgraph, scene, ctx->object, me);
+ result = BKE_fluid_modifier_do(fmd, ctx->depsgraph, scene, ctx->object, me);
return result ? result : me;
#endif /* WITH_FLUID */
}
@@ -139,69 +139,69 @@ static bool dependsOnTime(ModifierData *UNUSED(md))
static bool is_flow_cb(Object *UNUSED(ob), ModifierData *md)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
- return (mmd->type & MOD_FLUID_TYPE_FLOW) && mmd->flow;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ return (fmd->type & MOD_FLUID_TYPE_FLOW) && fmd->flow;
}
static bool is_coll_cb(Object *UNUSED(ob), ModifierData *md)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
- return (mmd->type & MOD_FLUID_TYPE_EFFEC) && mmd->effector;
+ FluidModifierData *fmd = (FluidModifierData *)md;
+ return (fmd->type & MOD_FLUID_TYPE_EFFEC) && fmd->effector;
}
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if (mmd && (mmd->type & MOD_FLUID_TYPE_DOMAIN) && mmd->domain) {
+ if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
DEG_add_collision_relations(ctx->node,
ctx->object,
- mmd->domain->fluid_group,
+ fmd->domain->fluid_group,
eModifierType_Fluid,
is_flow_cb,
"Fluid Flow");
DEG_add_collision_relations(ctx->node,
ctx->object,
- mmd->domain->effector_group,
+ fmd->domain->effector_group,
eModifierType_Fluid,
is_coll_cb,
"Fluid Effector");
DEG_add_forcefield_relations(ctx->node,
ctx->object,
- mmd->domain->effector_weights,
+ fmd->domain->effector_weights,
true,
PFIELD_FLUIDFLOW,
"Fluid Force Field");
- if (mmd->domain->guide_parent != NULL) {
+ if (fmd->domain->guide_parent != NULL) {
DEG_add_object_relation(
- ctx->node, mmd->domain->guide_parent, DEG_OB_COMP_TRANSFORM, "Fluid Guiding Object");
+ ctx->node, fmd->domain->guide_parent, DEG_OB_COMP_TRANSFORM, "Fluid Guiding Object");
DEG_add_object_relation(
- ctx->node, mmd->domain->guide_parent, DEG_OB_COMP_GEOMETRY, "Fluid Guiding Object");
+ ctx->node, fmd->domain->guide_parent, DEG_OB_COMP_GEOMETRY, "Fluid Guiding Object");
}
}
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
{
- FluidModifierData *mmd = (FluidModifierData *)md;
+ FluidModifierData *fmd = (FluidModifierData *)md;
- if (mmd->type == MOD_FLUID_TYPE_DOMAIN && mmd->domain) {
- walk(userData, ob, (ID **)&mmd->domain->effector_group, IDWALK_CB_NOP);
- walk(userData, ob, (ID **)&mmd->domain->fluid_group, IDWALK_CB_NOP);
- walk(userData, ob, (ID **)&mmd->domain->force_group, IDWALK_CB_NOP);
+ if (fmd->type == MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
+ walk(userData, ob, (ID **)&fmd->domain->effector_group, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&fmd->domain->fluid_group, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&fmd->domain->force_group, IDWALK_CB_NOP);
- if (mmd->domain->guide_parent) {
- walk(userData, ob, (ID **)&mmd->domain->guide_parent, IDWALK_CB_NOP);
+ if (fmd->domain->guide_parent) {
+ walk(userData, ob, (ID **)&fmd->domain->guide_parent, IDWALK_CB_NOP);
}
- if (mmd->domain->effector_weights) {
- walk(userData, ob, (ID **)&mmd->domain->effector_weights->group, IDWALK_CB_NOP);
+ if (fmd->domain->effector_weights) {
+ walk(userData, ob, (ID **)&fmd->domain->effector_weights->group, IDWALK_CB_NOP);
}
}
- if (mmd->type == MOD_FLUID_TYPE_FLOW && mmd->flow) {
- walk(userData, ob, (ID **)&mmd->flow->noise_texture, IDWALK_CB_USER);
+ if (fmd->type == MOD_FLUID_TYPE_FLOW && fmd->flow) {
+ walk(userData, ob, (ID **)&fmd->flow->noise_texture, IDWALK_CB_USER);
}
}
@@ -252,4 +252,6 @@ ModifierTypeInfo modifierType_Fluid = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 2eff026c040..861d5ea7435 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -40,12 +40,15 @@
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "DEG_depsgraph_query.h"
@@ -392,6 +395,11 @@ static void deformVertsEM(struct ModifierData *md,
Mesh *mesh_src = MOD_deform_mesh_eval_get(
ctx->object, editData, mesh, NULL, numVerts, false, false);
+ /* TODO(Campbell): use edit-mode data only (remove this line). */
+ if (mesh_src != NULL) {
+ BKE_mesh_wrapper_ensure_mdata(mesh_src);
+ }
+
deformVerts_do(hmd, ctx, ctx->object, mesh_src, vertexCos, numVerts);
if (!ELEM(mesh_src, NULL, mesh)) {
@@ -468,6 +476,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,4 +529,6 @@ ModifierTypeInfo modifierType_Hook = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index c6b36212857..e2646e77af9 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -49,6 +49,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BLO_read_write.h"
+
#include "RNA_access.h"
#include "MOD_ui_common.h"
@@ -855,6 +857,21 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_LaplacianDeform, panel_draw);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md;
+
+ BLO_write_float3_array(writer, lmd->total_verts, lmd->vertexco);
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md;
+
+ BLO_read_float3_array(reader, lmd->total_verts, &lmd->vertexco);
+ lmd->cache_system = NULL;
+}
+
ModifierTypeInfo modifierType_LaplacianDeform = {
/* name */ "LaplacianDeform",
/* structName */ "LaplacianDeformModifierData",
@@ -884,4 +901,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index bcbc21c557b..a7c74664cd9 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -649,4 +649,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index 4d96638fc83..b1a9258ec51 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -113,14 +113,14 @@ static void deformVerts(ModifierData *md,
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
- lattice_deform_verts(lmd->object,
- ctx->object,
- mesh_src,
- vertexCos,
- numVerts,
- lmd->flag,
- lmd->name,
- lmd->strength);
+ BKE_lattice_deform_coords_with_mesh(lmd->object,
+ ctx->object,
+ vertexCos,
+ numVerts,
+ lmd->flag,
+ lmd->name,
+ lmd->strength,
+ mesh_src);
if (!ELEM(mesh_src, NULL, mesh)) {
BKE_id_free(NULL, mesh_src);
@@ -130,23 +130,16 @@ static void deformVerts(ModifierData *md,
static void deformVertsEM(ModifierData *md,
const ModifierEvalContext *ctx,
struct BMEditMesh *em,
- struct Mesh *mesh,
+ struct Mesh *UNUSED(mesh),
float (*vertexCos)[3],
int numVerts)
{
- struct Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, em, mesh, NULL, numVerts, false, false);
-
- /* TODO(Campbell): use edit-mode data only (remove this line). */
- if (mesh_src != NULL) {
- BKE_mesh_wrapper_ensure_mdata(mesh_src);
- }
+ LatticeModifierData *lmd = (LatticeModifierData *)md;
- deformVerts(md, ctx, mesh_src, vertexCos, numVerts);
+ MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
- if (!ELEM(mesh_src, NULL, mesh)) {
- BKE_id_free(NULL, mesh_src);
- }
+ BKE_lattice_deform_coords_with_editmesh(
+ lmd->object, ctx->object, vertexCos, numVerts, lmd->flag, lmd->name, lmd->strength, em);
}
static void panel_draw(const bContext *C, Panel *panel)
@@ -204,4 +197,6 @@ ModifierTypeInfo modifierType_Lattice = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 46b88142223..93fb7749392 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -471,4 +471,6 @@ ModifierTypeInfo modifierType_Mask = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 2d104e1e0c2..2041b7a25db 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -410,4 +410,6 @@ ModifierTypeInfo modifierType_MeshCache = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index 0d4ee8633c1..ae031bffb04 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"
@@ -407,11 +409,12 @@ static void meshdeformModifier_do(ModifierData *md,
totcagevert = cagemesh->totvert;
if (mmd->totvert != totvert) {
- BKE_modifier_set_error(md, "Verts changed from %d to %d", mmd->totvert, totvert);
+ BKE_modifier_set_error(md, "Vertices changed from %d to %d", mmd->totvert, totvert);
goto finally;
}
else if (mmd->totcagevert != totcagevert) {
- BKE_modifier_set_error(md, "Cage verts changed from %d to %d", mmd->totcagevert, totcagevert);
+ BKE_modifier_set_error(
+ md, "Cage vertices changed from %d to %d", mmd->totcagevert, totcagevert);
goto finally;
}
else if (mmd->bindcagecos == NULL) {
@@ -607,6 +610,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,4 +670,6 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index 801badc382c..8f6676dd0b2 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"
@@ -198,7 +200,6 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
static void panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *box;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -208,11 +209,10 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA cache_file_ptr = RNA_pointer_get(&ptr, "cache_file");
bool has_cache_file = !RNA_pointer_is_null(&cache_file_ptr);
- box = uiLayoutBox(layout);
- uiTemplateCacheFile(box, C, &ptr, "cache_file");
-
uiLayoutSetPropSep(layout, true);
+ uiTemplateCacheFile(layout, C, &ptr, "cache_file");
+
if (has_cache_file) {
uiItemPointerR(layout, &ptr, "object_path", &cache_file_ptr, "object_paths", NULL, ICON_NONE);
}
@@ -229,6 +229,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",
@@ -259,4 +266,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index 949b115a25f..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);
}
@@ -245,4 +248,6 @@ ModifierTypeInfo modifierType_Mirror = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index f1cba313583..f091385ff8e 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -75,6 +75,29 @@ static void initData(ModifierData *md)
mmd->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS;
mmd->quality = 4;
mmd->flags |= (eMultiresModifierFlag_UseCrease | eMultiresModifierFlag_ControlEdges);
+
+ /* Open subdivision panels by default. */
+ md->ui_expand_flag = (1 << 0) | (1 << 1);
+}
+
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *md,
+ CustomData_MeshMasks *r_cddata_masks)
+{
+ MultiresModifierData *mmd = (MultiresModifierData *)md;
+ if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) {
+ r_cddata_masks->lmask |= CD_MASK_NORMAL;
+ r_cddata_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL;
+ }
+}
+
+static bool dependsOnNormals(ModifierData *md)
+{
+ MultiresModifierData *mmd = (MultiresModifierData *)md;
+ if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) {
+ return true;
+ }
+ return false;
}
static void copyData(const ModifierData *md_src, ModifierData *md_dst, const int flag)
@@ -205,6 +228,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Happens on bad topology, ut also on empty input mesh. */
return result;
}
+ const bool use_clnors = mmd->flags & eMultiresModifierFlag_UseCustomNormals &&
+ mesh->flag & ME_AUTOSMOOTH &&
+ CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
/* NOTE: Orco needs final coordinates on CPU side, which are expected to be
* accessible via MVert. For this reason we do not evaluate multires to
* grids when orco is requested. */
@@ -240,7 +266,22 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
// BKE_subdiv_stats_print(&subdiv->stats);
}
else {
+ if (use_clnors) {
+ /* If custom normals are present and the option is turned on calculate the split
+ * normals and clear flag so the normals get interpolated to the result mesh. */
+ BKE_mesh_calc_normals_split(mesh);
+ CustomData_clear_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ }
+
result = multires_as_mesh(mmd, ctx, mesh, subdiv);
+
+ if (use_clnors) {
+ float(*lnors)[3] = CustomData_get_layer(&result->ldata, CD_NORMAL);
+ BLI_assert(lnors != NULL);
+ BKE_mesh_set_custom_normals(result, lnors);
+ CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ CustomData_set_layer_flag(&result->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ }
// BKE_subdiv_stats_print(&subdiv->stats);
if (subdiv != runtime_data->subdiv) {
BKE_subdiv_free(subdiv);
@@ -287,50 +328,50 @@ static void deformMatrices(ModifierData *md,
static void panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *row, *col, *split, *col2;
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
- PointerRNA ob_ptr;
- modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
-
- /**
- * 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
- * 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.
- */
- bool has_displacement = RNA_int_get(&ptr, "total_levels") != 0;
- MultiresModifierData *mmd = (MultiresModifierData *)ptr.data;
+ modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
uiLayoutSetPropSep(layout, true);
- col = uiLayoutColumn(layout, false);
- uiLayoutSetEnabled(col, !has_displacement);
- uiItemR(col, &ptr, "subdivision_type", 0, NULL, ICON_NONE);
-
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "sculpt_levels", 0, IFACE_("Levels Sculpt"), ICON_NONE);
- uiItemR(col, &ptr, "levels", 0, IFACE_("Viewport"), ICON_NONE);
+ uiItemR(col, &ptr, "levels", 0, IFACE_("Level Viewport"), ICON_NONE);
+ uiItemR(col, &ptr, "sculpt_levels", 0, IFACE_("Sculpt"), ICON_NONE);
uiItemR(col, &ptr, "render_levels", 0, IFACE_("Render"), ICON_NONE);
+
uiItemR(layout, &ptr, "show_only_control_edges", 0, NULL, ICON_NONE);
- uiItemS(layout);
+ modifier_panel_end(layout, &ptr);
+}
+
+static void subdivisions_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ uiLayoutSetEnabled(layout, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT);
- split = uiLayoutSplit(layout, 0.5f, false);
- uiLayoutSetEnabled(split, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT);
- col = uiLayoutColumn(split, false);
- col2 = uiLayoutColumn(split, false);
+ MultiresModifierData *mmd = (MultiresModifierData *)ptr.data;
- uiItemO(col, IFACE_("Unsubdivide"), ICON_NONE, "OBJECT_OT_multires_unsubdivide");
+ /**
+ * Changing some of the properties can not be done once there is an
+ * 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.
+ */
- row = uiLayoutRow(col2, true);
PointerRNA op_ptr;
- uiItemFullO(row,
+ uiItemFullO(layout,
"OBJECT_OT_multires_subdivide",
IFACE_("Subdivide"),
ICON_NONE,
@@ -340,10 +381,12 @@ static void panel_draw(const bContext *C, Panel *panel)
&op_ptr);
RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_CATMULL_CLARK);
RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name);
+
+ row = uiLayoutRow(layout, false);
uiItemFullO(row,
"OBJECT_OT_multires_subdivide",
IFACE_("Simple"),
- ICON_NONE, /* TODO: Needs icon, remove text */
+ ICON_NONE,
NULL,
WM_OP_EXEC_DEFAULT,
0,
@@ -353,7 +396,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemFullO(row,
"OBJECT_OT_multires_subdivide",
IFACE_("Linear"),
- ICON_NONE, /* TODO: Needs icon, remove text */
+ ICON_NONE,
NULL,
WM_OP_EXEC_DEFAULT,
0,
@@ -361,13 +404,41 @@ static void panel_draw(const bContext *C, Panel *panel)
RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_LINEAR);
RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name);
- uiItemL(col, "", ICON_NONE);
- uiItemO(col2, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete");
+ uiItemS(layout);
- uiItemO(col, IFACE_("Reshape"), ICON_NONE, "OBJECT_OT_multires_reshape");
- uiItemO(col2, IFACE_("Apply Base"), ICON_NONE, "OBJECT_OT_multires_base_apply");
+ uiItemO(layout, IFACE_("Unsubdivide"), ICON_NONE, "OBJECT_OT_multires_unsubdivide");
- uiItemS(layout);
+ row = uiLayoutRow(layout, false);
+ uiItemL(row, "", ICON_NONE);
+ uiItemO(row, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete");
+}
+
+static void shape_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ PointerRNA ob_ptr;
+ modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+
+ uiLayoutSetEnabled(layout, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT);
+
+ row = uiLayoutRow(layout, false);
+ uiItemO(row, IFACE_("Reshape"), ICON_NONE, "OBJECT_OT_multires_reshape");
+ uiItemO(row, IFACE_("Apply Base"), ICON_NONE, "OBJECT_OT_multires_base_apply");
+}
+
+static void generate_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col, *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+ MultiresModifierData *mmd = (MultiresModifierData *)ptr.data;
+
+ bool is_external = RNA_boolean_get(&ptr, "is_external");
if (mmd->totlvl == 0) {
uiItemO(
@@ -376,16 +447,15 @@ static void panel_draw(const bContext *C, Panel *panel)
col = uiLayoutColumn(layout, false);
row = uiLayoutRow(col, false);
- if (RNA_boolean_get(&ptr, "is_external")) {
+ if (is_external) {
uiItemO(row, IFACE_("Pack External"), ICON_NONE, "OBJECT_OT_multires_external_pack");
+ uiLayoutSetPropSep(col, true);
row = uiLayoutRow(col, false);
- uiItemR(row, &ptr, "filepath", 0, "", ICON_NONE);
+ uiItemR(row, &ptr, "filepath", 0, NULL, ICON_NONE);
}
else {
uiItemO(col, IFACE_("Save External..."), ICON_NONE, "OBJECT_OT_multires_external_save");
}
-
- modifier_panel_end(layout, &ptr);
}
static void advanced_panel_draw(const bContext *C, Panel *panel)
@@ -400,21 +470,28 @@ static void advanced_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- col = uiLayoutColumn(layout, false);
- uiLayoutSetEnabled(col, !has_displacement);
- uiItemR(col, &ptr, "quality", 0, NULL, ICON_NONE);
+ uiLayoutSetEnabled(layout, !has_displacement);
- uiItemR(layout, &ptr, "uv_smooth", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "subdivision_type", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "quality", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiLayoutSetEnabled(col, !has_displacement);
- uiItemR(col, &ptr, "use_creases", 0, NULL, ICON_NONE);
+ uiLayoutSetEnabled(col, true);
+ uiItemR(col, &ptr, "uv_smooth", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, &ptr, "use_creases", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "use_custom_normals", 0, NULL, ICON_NONE);
}
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Multires, panel_draw);
modifier_subpanel_register(
+ region_type, "subdivide", "Subdivions", NULL, subdivisions_panel_draw, panel_type);
+ modifier_subpanel_register(region_type, "shape", "Shape", NULL, shape_panel_draw, panel_type);
+ modifier_subpanel_register(
+ region_type, "generate", "Generate", NULL, generate_panel_draw, panel_type);
+ modifier_subpanel_register(
region_type, "advanced", "Advanced", NULL, advanced_panel_draw, panel_type);
}
@@ -438,15 +515,17 @@ ModifierTypeInfo modifierType_Multires = {
/* modifyVolume */ NULL,
/* initData */ initData,
- /* requiredDataMask */ NULL,
+ /* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
/* isDisabled */ NULL,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
- /* dependsOnNormals */ NULL,
+ /* dependsOnNormals */ dependsOnNormals,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
/* freeRuntimeData */ freeRuntimeData,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c
index 84c9fc1d726..a84a10f4b6c 100644
--- a/source/blender/modifiers/intern/MOD_none.c
+++ b/source/blender/modifiers/intern/MOD_none.c
@@ -67,4 +67,7 @@ ModifierTypeInfo modifierType_None = {
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
+ /* panelRegister */ NULL,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index c1901a97cda..7039b24cfc6 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -709,11 +709,12 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA ob_ptr;
modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
- uiLayoutSetPropSep(layout, true);
-
int mode = RNA_enum_get(&ptr, "mode");
uiItemR(layout, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ uiLayoutSetPropSep(layout, true);
+
uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, false);
@@ -809,4 +810,6 @@ ModifierTypeInfo modifierType_NormalEdit = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index e16a23f54c6..6374f081581 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"
@@ -120,7 +122,8 @@ static void initData(ModifierData *md)
omd->bakeend = 250;
omd->oceancache = NULL;
omd->foam_fade = 0.98;
- omd->foamlayername[0] = '\0'; /* layer name empty by default */
+ omd->foamlayername[0] = '\0'; /* layer name empty by default */
+ omd->spraylayername[0] = '\0'; /* layer name empty by default */
omd->ocean = BKE_ocean_add();
BKE_ocean_init_from_modifier(omd->ocean, omd);
@@ -418,6 +421,12 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes
MLoopCol *mloopcols = CustomData_add_layer_named(
&result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, num_loops, omd->foamlayername);
+ MLoopCol *mloopcols_spray = NULL;
+ if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
+ mloopcols_spray = CustomData_add_layer_named(
+ &result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, num_loops, omd->spraylayername);
+ }
+
if (mloopcols) { /* unlikely to fail */
MPoly *mpolys = result->mpoly;
MPoly *mp;
@@ -426,6 +435,11 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes
MLoop *ml = &mloops[mp->loopstart];
MLoopCol *mlcol = &mloopcols[mp->loopstart];
+ MLoopCol *mlcolspray = NULL;
+ if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
+ mlcolspray = &mloopcols_spray[mp->loopstart];
+ }
+
for (j = mp->totloop; j--; ml++, mlcol++) {
const float *vco = mverts[ml->v].co;
const float u = OCEAN_CO(size_co_inv, vco[0]);
@@ -445,6 +459,23 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes
mlcol->r = mlcol->g = mlcol->b = (char)(foam * 255);
/* This needs to be set (render engine uses) */
mlcol->a = 255;
+
+ if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
+ if (omd->flag & MOD_OCEAN_INVERT_SPRAY) {
+ mlcolspray->r = ocr.Eminus[0] * 255;
+ }
+ else {
+ mlcolspray->r = ocr.Eplus[0] * 255;
+ }
+ mlcolspray->g = 0;
+ if (omd->flag & MOD_OCEAN_INVERT_SPRAY) {
+ mlcolspray->b = ocr.Eminus[2] * 255;
+ }
+ else {
+ mlcolspray->b = ocr.Eplus[2] * 255;
+ }
+ mlcolspray->a = 255;
+ }
}
}
}
@@ -512,7 +543,7 @@ static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
#ifdef WITH_OCEANSIM
- uiLayout *col;
+ uiLayout *col, *sub;
PointerRNA ptr;
PointerRNA ob_ptr;
@@ -520,22 +551,24 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "geometry_mode", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "geometry_mode", 0, NULL, ICON_NONE);
if (RNA_enum_get(&ptr, "geometry_mode") == MOD_OCEAN_GEOM_GENERATE) {
- col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "repeat_x", 0, IFACE_("Repeat X"), ICON_NONE);
- uiItemR(col, &ptr, "repeat_y", 0, IFACE_("Y"), ICON_NONE);
+ sub = uiLayoutColumn(col, true);
+ uiItemR(sub, &ptr, "repeat_x", 0, IFACE_("Repeat X"), ICON_NONE);
+ uiItemR(sub, &ptr, "repeat_y", 0, IFACE_("Y"), ICON_NONE);
}
- uiItemR(layout, &ptr, "random_seed", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "resolution", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "resolution", 0, NULL, ICON_NONE);
+
+ uiItemR(col, &ptr, "time", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "time", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "depth", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "depth", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "size", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "spatial_size", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "size", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "spatial_size", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "random_seed", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_normals", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "use_normals", 0, NULL, ICON_NONE);
modifier_panel_end(layout, &ptr);
@@ -548,7 +581,7 @@ static void panel_draw(const bContext *C, Panel *panel)
#ifdef WITH_OCEANSIM
static void waves_panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *col;
+ uiLayout *col, *sub;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -556,16 +589,20 @@ static void waves_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "wave_scale", 0, IFACE_("Scale"), ICON_NONE);
- uiItemR(layout, &ptr, "wave_scale_min", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "choppiness", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "wind_velocity", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "wave_scale", 0, IFACE_("Scale"), ICON_NONE);
+ uiItemR(col, &ptr, "wave_scale_min", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "choppiness", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "wind_velocity", 0, NULL, ICON_NONE);
+
+ uiItemS(layout);
- uiItemR(layout, &ptr, "wave_alignment", 0, IFACE_("Alignment"), ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiLayoutSetActive(col, RNA_float_get(&ptr, "wave_alignment") > 0.0f);
- uiItemR(col, &ptr, "wave_direction", 0, IFACE_("Direction"), ICON_NONE);
- uiItemR(col, &ptr, "damping", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "wave_alignment", UI_ITEM_R_SLIDER, IFACE_("Alignment"), ICON_NONE);
+ sub = uiLayoutColumn(col, false);
+ uiLayoutSetActive(sub, RNA_float_get(&ptr, "wave_alignment") > 0.0f);
+ uiItemR(sub, &ptr, "wave_direction", 0, IFACE_("Direction"), ICON_NONE);
+ uiItemR(sub, &ptr, "damping", 0, NULL, ICON_NONE);
}
static void foam_panel_draw_header(const bContext *C, Panel *panel)
@@ -592,28 +629,61 @@ static void foam_panel_draw(const bContext *C, Panel *panel)
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, use_foam);
+ uiItemR(col, &ptr, "foam_layer_name", 0, IFACE_("Data Layer"), ICON_NONE);
uiItemR(col, &ptr, "foam_coverage", 0, IFACE_("Coverage"), ICON_NONE);
+}
- col = uiLayoutColumn(layout, true);
- uiLayoutSetActive(col, use_foam);
- uiItemR(col, &ptr, "foam_layer_name", 0, IFACE_("Data Layer"), ICON_NONE);
+static void spray_panel_draw_header(const bContext *C, Panel *panel)
+{
+ uiLayout *row;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
+ bool use_foam = RNA_boolean_get(&ptr, "use_foam");
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, use_foam);
+ uiItemR(row, &ptr, "use_spray", 0, IFACE_("Spray"), ICON_NONE);
}
-static void spectrum_panel_draw(const bContext *C, Panel *panel)
+static void spray_panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+ bool use_foam = RNA_boolean_get(&ptr, "use_foam");
+ bool use_spray = RNA_boolean_get(&ptr, "use_spray");
+
uiLayoutSetPropSep(layout, true);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, use_foam && use_spray);
+ uiItemR(col, &ptr, "spray_layer_name", 0, IFACE_("Data Layer"), ICON_NONE);
+ uiItemR(col, &ptr, "invert_spray", 0, IFACE_("Invert"), ICON_NONE);
+}
+
+static void spectrum_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *col;
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ptr;
+ modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
+
int spectrum = RNA_enum_get(&ptr, "spectrum");
- uiItemR(layout, &ptr, "spectrum", 0, NULL, ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "spectrum", 0, NULL, ICON_NONE);
if (ELEM(spectrum, MOD_OCEAN_SPECTRUM_TEXEL_MARSEN_ARSLOE, MOD_OCEAN_SPECTRUM_JONSWAP)) {
- uiItemR(layout, &ptr, "sharpen_peak_jonswap", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "fetch_jonswap", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "sharpen_peak_jonswap", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "fetch_jonswap", 0, NULL, ICON_NONE);
}
}
@@ -650,7 +720,7 @@ static void bake_panel_draw(const bContext *C, Panel *panel)
col = uiLayoutColumn(layout, true);
uiLayoutSetEnabled(col, !is_cached);
- uiItemR(col, &ptr, "frame_start", 0, IFACE_("Start"), ICON_NONE);
+ uiItemR(col, &ptr, "frame_start", 0, IFACE_("Frame Start"), ICON_NONE);
uiItemR(col, &ptr, "frame_end", 0, IFACE_("End"), ICON_NONE);
col = uiLayoutColumn(layout, false);
@@ -664,9 +734,11 @@ static void panelRegister(ARegionType *region_type)
PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Ocean, panel_draw);
#ifdef WITH_OCEANSIM
modifier_subpanel_register(region_type, "waves", "Waves", NULL, waves_panel_draw, panel_type);
- modifier_subpanel_register(
+ PanelType *foam_panel = modifier_subpanel_register(
region_type, "foam", "", foam_panel_draw_header, foam_panel_draw, panel_type);
modifier_subpanel_register(
+ region_type, "spray", "", spray_panel_draw_header, spray_panel_draw, foam_panel);
+ modifier_subpanel_register(
region_type, "spectrum", "Spectrum", NULL, spectrum_panel_draw, panel_type);
modifier_subpanel_register(region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type);
#else
@@ -674,6 +746,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",
@@ -705,4 +784,6 @@ ModifierTypeInfo modifierType_Ocean = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index 0a9fb964281..dd881f1ac74 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -542,7 +542,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
@@ -645,6 +645,7 @@ static void path_panel_draw(const bContext *C, Panel *panel)
static void layers_panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -655,20 +656,11 @@ static void layers_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemPointerR(layout,
- &ptr,
- "index_layer_name",
- &obj_data_ptr,
- "vertex_colors",
- IFACE_("Index"),
- ICON_NONE);
- uiItemPointerR(layout,
- &ptr,
- "value_layer_name",
- &obj_data_ptr,
- "vertex_colors",
- IFACE_("Value"),
- ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemPointerR(
+ col, &ptr, "index_layer_name", &obj_data_ptr, "vertex_colors", IFACE_("Index"), ICON_NONE);
+ uiItemPointerR(
+ col, &ptr, "value_layer_name", &obj_data_ptr, "vertex_colors", IFACE_("Value"), ICON_NONE);
}
static void panelRegister(ARegionType *region_type)
@@ -711,4 +703,6 @@ ModifierTypeInfo modifierType_ParticleInstance = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index b63fb18de69..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",
@@ -329,4 +343,6 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index a76b3acb783..197d759c352 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,
@@ -241,7 +241,7 @@ static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
#ifdef WITH_MOD_REMESH
- uiLayout *row;
+ uiLayout *row, *col;
PointerRNA ptr;
PointerRNA ob_ptr;
@@ -253,16 +253,17 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
+ col = uiLayoutColumn(layout, false);
if (mode == MOD_REMESH_VOXEL) {
- uiItemR(layout, &ptr, "voxel_size", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "adaptivity", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "voxel_size", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "adaptivity", 0, NULL, ICON_NONE);
}
else {
- uiItemR(layout, &ptr, "octree_depth", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "scale", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "octree_depth", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "scale", 0, NULL, ICON_NONE);
if (mode == MOD_REMESH_SHARP_FEATURES) {
- uiItemR(layout, &ptr, "sharpness", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "sharpness", 0, NULL, ICON_NONE);
}
uiItemR(layout, &ptr, "use_remove_disconnected", 0, NULL, ICON_NONE);
@@ -316,4 +317,6 @@ ModifierTypeInfo modifierType_Remesh = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index b1c49ba2fec..b9bb7add811 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -1277,4 +1277,6 @@ ModifierTypeInfo modifierType_Screw = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index bd3301b543c..801995d6dbc 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -148,4 +148,6 @@ ModifierTypeInfo modifierType_ShapeKey = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ NULL,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index 765773c9e2b..a5f6be04a08 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -242,13 +242,14 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "project_limit", 0, IFACE_("Limit"), ICON_NONE);
uiItemR(layout, &ptr, "subsurf_levels", 0, NULL, ICON_NONE);
- row = uiLayoutRowWithHeading(layout, true, IFACE_("Axis"));
+ col = uiLayoutColumn(layout, false);
+ row = uiLayoutRowWithHeading(col, true, IFACE_("Axis"));
uiItemR(row, &ptr, "use_project_x", toggles_flag, NULL, ICON_NONE);
uiItemR(row, &ptr, "use_project_y", toggles_flag, NULL, ICON_NONE);
uiItemR(row, &ptr, "use_project_z", toggles_flag, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_negative_direction", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_positive_direction", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "use_negative_direction", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "use_positive_direction", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "cull_face", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
col = uiLayoutColumn(layout, false);
@@ -306,4 +307,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index 668b83db41e..811524fc474 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -571,4 +571,6 @@ ModifierTypeInfo modifierType_SimpleDeform = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc
index d55900dc7a9..edfcc16af85 100644
--- a/source/blender/modifiers/intern/MOD_simulation.cc
+++ b/source/blender/modifiers/intern/MOD_simulation.cc
@@ -29,6 +29,7 @@
#include "BLI_float3.hh"
#include "BLI_listbase.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_mesh_types.h"
@@ -47,6 +48,8 @@
#include "BKE_pointcloud.h"
#include "BKE_simulation.h"
+#include "BLO_read_write.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
@@ -90,15 +93,8 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
static const ParticleSimulationState *find_particle_state(SimulationModifierData *smd)
{
- if (smd->simulation == nullptr) {
- return nullptr;
- }
- LISTBASE_FOREACH (const SimulationState *, state, &smd->simulation->states) {
- if (state->type == SIM_STATE_TYPE_PARTICLES) {
- return (ParticleSimulationState *)state;
- }
- }
- return nullptr;
+ return (const ParticleSimulationState *)BKE_simulation_state_try_find_by_name_and_type(
+ smd->simulation, smd->data_path, SIM_TYPE_NAME_PARTICLE_SIMULATION);
}
static PointCloud *modifyPointCloud(ModifierData *md,
@@ -117,11 +113,13 @@ static PointCloud *modifyPointCloud(ModifierData *md,
}
const float3 *positions = (const float3 *)CustomData_get_layer_named(
- &state->attributes, CD_LOCATION, "Position");
+ &state->attributes, CD_PROP_FLOAT3, "Position");
+ const float *radii = (const float *)CustomData_get_layer_named(
+ &state->attributes, CD_PROP_FLOAT, "Radius");
memcpy(pointcloud->co, positions, sizeof(float3) * state->tot_particles);
for (int i = 0; i < state->tot_particles; i++) {
- pointcloud->radius[i] = 0.05f;
+ pointcloud->radius[i] = radii[i];
}
return pointcloud;
@@ -135,6 +133,9 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA ob_ptr;
modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
uiItemR(layout, &ptr, "simulation", 0, NULL, ICON_NONE);
uiItemR(layout, &ptr, "data_path", 0, NULL, ICON_NONE);
@@ -146,6 +147,37 @@ static void panelRegister(ARegionType *region_type)
modifier_panel_register(region_type, eModifierType_Simulation, panel_draw);
}
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const SimulationModifierData *smd = (const SimulationModifierData *)md;
+ BLO_write_string(writer, smd->data_path);
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ SimulationModifierData *smd = (SimulationModifierData *)md;
+ BLO_read_data_address(reader, &smd->data_path);
+}
+
+static void copyData(const ModifierData *md, ModifierData *target, const int flag)
+{
+ const SimulationModifierData *smd = (const SimulationModifierData *)md;
+ SimulationModifierData *tsmd = (SimulationModifierData *)target;
+
+ BKE_modifier_copydata_generic(md, target, flag);
+ if (smd->data_path != nullptr) {
+ tsmd->data_path = BLI_strdup(smd->data_path);
+ }
+}
+
+static void freeData(ModifierData *md)
+{
+ SimulationModifierData *smd = (SimulationModifierData *)md;
+ if (smd->data_path) {
+ MEM_freeN(smd->data_path);
+ }
+}
+
ModifierTypeInfo modifierType_Simulation = {
/* name */ "Simulation",
/* structName */ "SimulationModifierData",
@@ -153,7 +185,7 @@ ModifierTypeInfo modifierType_Simulation = {
/* type */ eModifierTypeType_None,
/* flags */ (ModifierTypeFlag)0,
- /* copyData */ BKE_modifier_copydata_generic,
+ /* copyData */ copyData,
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
@@ -166,7 +198,7 @@ ModifierTypeInfo modifierType_Simulation = {
/* initData */ NULL,
/* requiredDataMask */ NULL,
- /* freeData */ NULL,
+ /* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
@@ -176,4 +208,6 @@ ModifierTypeInfo modifierType_Simulation = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 38d7b31a335..8f0174fe6d9 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -1974,7 +1974,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemO(row, IFACE_("Create Armature"), ICON_NONE, "OBJECT_OT_skin_armature_create");
uiItemO(row, NULL, ICON_NONE, "MESH_OT_customdata_skin_add");
- row = uiLayoutRow(layout, true);
+ row = uiLayoutRow(layout, false);
uiItemFullO(row,
"OBJECT_OT_skin_loose_mark_clear",
IFACE_("Mark Loose"),
@@ -2035,4 +2035,6 @@ ModifierTypeInfo modifierType_Skin = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index f25bb52f2c7..deece2b999b 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -147,7 +147,7 @@ static void smoothModifier_do(
MDeformVert *dv = dvert;
for (int i = 0; i < numVerts; i++, dv++) {
float *vco_orig = vertexCos[i];
- if (num_accumulated_vecs[0] > 0) {
+ if (num_accumulated_vecs[i] > 0) {
mul_v3_fl(accumulated_vecs[i], 1.0f / (float)num_accumulated_vecs[i]);
}
float *vco_new = accumulated_vecs[i];
@@ -174,7 +174,7 @@ static void smoothModifier_do(
else { /* no vertex group */
for (int i = 0; i < numVerts; i++) {
float *vco_orig = vertexCos[i];
- if (num_accumulated_vecs[0] > 0) {
+ if (num_accumulated_vecs[i] > 0) {
mul_v3_fl(accumulated_vecs[i], 1.0f / (float)num_accumulated_vecs[i]);
}
float *vco_new = accumulated_vecs[i];
@@ -300,4 +300,6 @@ ModifierTypeInfo modifierType_Smooth = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 515303b7271..965d270829e 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -133,4 +133,6 @@ ModifierTypeInfo modifierType_Softbody = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 1471d088af9..df9cdee1893 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -124,8 +124,11 @@ static void panel_draw(const bContext *C, Panel *panel)
if (solidify_mode == MOD_SOLIDIFY_MODE_NONMANIFOLD) {
uiItemR(layout, &ptr, "nonmanifold_merge_threshold", 0, NULL, ICON_NONE);
}
+ else {
+ 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"));
@@ -149,6 +152,7 @@ static void panel_draw(const bContext *C, Panel *panel)
static void normals_panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -159,10 +163,10 @@ static void normals_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "use_flip_normals", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "use_flip_normals", 0, IFACE_("Flip"), ICON_NONE);
if (solidify_mode == MOD_SOLIDIFY_MODE_EXTRUDE) {
- uiItemR(layout, &ptr, "use_quality_normals", 0, IFACE_("High Quality"), ICON_NONE);
- uiItemR(layout, &ptr, "use_even_offset", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "use_quality_normals", 0, IFACE_("High Quality"), ICON_NONE);
}
}
@@ -196,16 +200,18 @@ static void edge_data_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
if (solidify_mode == MOD_SOLIDIFY_MODE_EXTRUDE) {
- uiItemR(layout, &ptr, "edge_crease_inner", 0, IFACE_("Inner"), ICON_NONE);
- uiItemR(layout, &ptr, "edge_crease_outer", 0, IFACE_("Outer"), ICON_NONE);
- uiItemR(layout, &ptr, "edge_crease_rim", 0, IFACE_("Rim"), ICON_NONE);
+ uiLayout *col;
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, &ptr, "edge_crease_inner", 0, IFACE_("Crease Inner"), ICON_NONE);
+ uiItemR(col, &ptr, "edge_crease_outer", 0, IFACE_("Outer"), ICON_NONE);
+ uiItemR(col, &ptr, "edge_crease_rim", 0, IFACE_("Rim"), ICON_NONE);
}
uiItemR(layout, &ptr, "bevel_convex", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
static void clamp_panel_draw(const bContext *C, Panel *panel)
{
- uiLayout *row;
+ uiLayout *row, *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -214,8 +220,9 @@ static void clamp_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "thickness_clamp", 0, NULL, ICON_NONE);
- row = uiLayoutRow(layout, false);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "thickness_clamp", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(col, false);
uiLayoutSetActive(row, RNA_float_get(&ptr, "thickness_clamp") > 0.0f);
uiItemR(row, &ptr, "use_thickness_angle_clamp", 0, NULL, ICON_NONE);
}
@@ -288,4 +295,6 @@ ModifierTypeInfo modifierType_Solidify = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 75d2be5292e..7e5e4ecd9d3 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.
*/
@@ -284,7 +284,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
new_edge_arr = MEM_malloc_arrayN(((numEdges * 2) + numVerts), sizeof(*new_edge_arr), __func__);
edge_users = MEM_malloc_arrayN(numEdges, sizeof(*edge_users), "solid_mod edges");
- edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod eorder");
+ edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod order");
/* save doing 2 loops here... */
#if 0
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 2b2f6893a2e..8424b549f85 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -36,6 +36,7 @@
#include "DNA_screen_types.h"
#include "BKE_context.h"
+#include "BKE_mesh.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_subdiv.h"
@@ -57,6 +58,8 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
+#include "BLO_read_write.h"
+
#include "intern/CCGSubSurf.h"
typedef struct SubsurfRuntimeData {
@@ -75,6 +78,26 @@ static void initData(ModifierData *md)
smd->flags |= (eSubsurfModifierFlag_UseCrease | eSubsurfModifierFlag_ControlEdges);
}
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *md,
+ CustomData_MeshMasks *r_cddata_masks)
+{
+ SubsurfModifierData *smd = (SubsurfModifierData *)md;
+ if (smd->flags & eSubsurfModifierFlag_UseCustomNormals) {
+ r_cddata_masks->lmask |= CD_MASK_NORMAL;
+ r_cddata_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL;
+ }
+}
+
+static bool dependsOnNormals(ModifierData *md)
+{
+ SubsurfModifierData *smd = (SubsurfModifierData *)md;
+ if (smd->flags & eSubsurfModifierFlag_UseCustomNormals) {
+ return true;
+ }
+ return false;
+}
+
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
#if 0
@@ -240,6 +263,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Happens on bad topology, but also on empty input mesh. */
return result;
}
+ const bool use_clnors = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) &&
+ (mesh->flag & ME_AUTOSMOOTH) &&
+ CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
+ if (use_clnors) {
+ /* If custom normals are present and the option is turned on calculate the split
+ * normals and clear flag so the normals get interpolated to the result mesh. */
+ BKE_mesh_calc_normals_split(mesh);
+ CustomData_clear_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ }
/* TODO(sergey): Decide whether we ever want to use CCG for subsurf,
* maybe when it is a last modifier in the stack? */
if (true) {
@@ -248,6 +280,14 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
else {
result = subdiv_as_ccg(smd, ctx, mesh, subdiv);
}
+
+ if (use_clnors) {
+ float(*lnors)[3] = CustomData_get_layer(&result->ldata, CD_NORMAL);
+ BLI_assert(lnors != NULL);
+ BKE_mesh_set_custom_normals(result, lnors);
+ CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ CustomData_set_layer_flag(&result->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ }
// BKE_subdiv_stats_print(&subdiv->stats);
if (subdiv != runtime_data->subdiv) {
BKE_subdiv_free(subdiv);
@@ -348,9 +388,9 @@ static void panel_draw(const bContext *C, Panel *panel)
}
#endif
- uiLayoutSetPropSep(layout, true);
+ uiItemR(layout, &ptr, "subdivision_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "subdivision_type", 0, IFACE_("Type"), ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
if (show_adaptive_options) {
uiItemR(layout,
@@ -414,8 +454,7 @@ 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);
+ uiItemR(layout, &ptr, "use_custom_normals", 0, NULL, ICON_NONE);
}
static void panelRegister(ARegionType *region_type)
@@ -425,6 +464,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",
@@ -446,15 +492,17 @@ ModifierTypeInfo modifierType_Subsurf = {
/* modifyVolume */ NULL,
/* initData */ initData,
- /* requiredDataMask */ NULL,
+ /* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
- /* dependsOnNormals */ NULL,
+ /* dependsOnNormals */ dependsOnNormals,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
/* freeRuntimeData */ freeRuntimeData,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 252022a5439..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",
@@ -241,4 +254,6 @@ ModifierTypeInfo modifierType_Surface = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index fbd8d7d27ec..6a0e82a686b 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"
@@ -1128,7 +1130,7 @@ static bool surfacedeformBind(SurfaceDeformModifierData *smd_orig,
freeData((ModifierData *)smd_orig);
}
else if (data.success == MOD_SDEF_BIND_RESULT_OVERLAP_ERR) {
- BKE_modifier_set_error((ModifierData *)smd_eval, "Target contains overlapping verts");
+ BKE_modifier_set_error((ModifierData *)smd_eval, "Target contains overlapping vertices");
freeData((ModifierData *)smd_orig);
}
else if (data.success == MOD_SDEF_BIND_RESULT_GENERIC_ERR) {
@@ -1278,7 +1280,7 @@ static void surfacedeformModifier_do(ModifierData *md,
/* Poly count checks */
if (smd->numverts != numverts) {
- BKE_modifier_set_error(md, "Verts changed from %u to %u", smd->numverts, numverts);
+ BKE_modifier_set_error(md, "Vertices changed from %u to %u", smd->numverts, numverts);
return;
}
else if (smd->numpoly != tnumpoly) {
@@ -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,4 +1528,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 351a1016a35..65c972b5fb4 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -181,4 +181,6 @@ ModifierTypeInfo modifierType_Triangulate = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index cc0d3d8ccee..01b9e972086 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -33,7 +33,6 @@
#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
-#include "DNA_space_types.h"
#include "ED_object.h"
@@ -50,24 +49,13 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h" /* Self include */
-static Object *get_modifier_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 don't show for other object types with modifiers (only
* grease pencil currently).
*/
static bool modifier_ui_poll(const bContext *C, PanelType *UNUSED(pt))
{
- Object *ob = get_modifier_object(C);
+ Object *ob = ED_object_active_context(C);
return (ob != NULL) && (ob->type != OB_GPENCIL);
}
@@ -81,7 +69,7 @@ static bool modifier_ui_poll(const bContext *C, PanelType *UNUSED(pt))
*/
static void modifier_reorder(bContext *C, Panel *panel, int new_index)
{
- Object *ob = get_modifier_object(C);
+ Object *ob = ED_object_active_context(C);
ModifierData *md = BLI_findlink(&ob->modifiers, panel->runtime.list_index);
PointerRNA props_ptr;
@@ -95,14 +83,14 @@ static void modifier_reorder(bContext *C, Panel *panel, int new_index)
static short get_modifier_expand_flag(const bContext *C, Panel *panel)
{
- Object *ob = get_modifier_object(C);
+ Object *ob = ED_object_active_context(C);
ModifierData *md = BLI_findlink(&ob->modifiers, panel->runtime.list_index);
return md->ui_expand_flag;
}
static void set_modifier_expand_flag(const bContext *C, Panel *panel, short expand_flag)
{
- Object *ob = get_modifier_object(C);
+ Object *ob = ED_object_active_context(C);
ModifierData *md = BLI_findlink(&ob->modifiers, panel->runtime.list_index);
md->ui_expand_flag = expand_flag;
}
@@ -127,7 +115,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,
@@ -135,7 +123,7 @@ void modifier_panel_get_property_pointers(const bContext *C,
PointerRNA *r_ob_ptr,
PointerRNA *r_md_ptr)
{
- Object *ob = get_modifier_object(C);
+ Object *ob = ED_object_active_context(C);
ModifierData *md = BLI_findlink(&ob->modifiers, panel->runtime.list_index);
@@ -146,8 +134,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,28 +209,44 @@ 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 = ED_object_active_context(C);
+ RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
+ uiLayoutSetContextPointer(layout, "modifier", &ptr);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
- uiItemEnumO(layout,
- "OBJECT_OT_modifier_apply",
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
- 0,
- "apply_as",
- MODIFIER_APPLY_DATA);
+ uiLayoutSetUnitsX(layout, 4.0f);
+ /* Apply. */
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
+ ICON_CHECKMARK,
+ "OBJECT_OT_modifier_apply");
+
+ /* Apply as shapekey. */
if (BKE_modifier_is_same_topology(md) && !BKE_modifier_is_non_geometrical(md)) {
- uiItemEnumO(layout,
- "OBJECT_OT_modifier_apply",
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply As Shapekey"),
- ICON_SHAPEKEY_DATA,
- "apply_as",
- MODIFIER_APPLY_SHAPE);
+ uiItemBooleanO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply As Shapekey"),
+ ICON_SHAPEKEY_DATA,
+ "OBJECT_OT_modifier_apply_as_shapekey",
+ "keep_modifier",
+ false);
+
+ uiItemBooleanO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Save As Shapekey"),
+ ICON_SHAPEKEY_DATA,
+ "OBJECT_OT_modifier_apply_as_shapekey",
+ "keep_modifier",
+ true);
}
+ /* Duplicate. */
if (!ELEM(md->type,
eModifierType_Fluidsim,
eModifierType_Softbody,
@@ -256,21 +259,46 @@ 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;
- Object *ob = get_modifier_object(C);
+ Object *ob = ED_object_active_context(C);
/* Don't use #modifier_panel_get_property_pointers, we don't want to lock the header. */
ModifierData *md = BLI_findlink(&ob->modifiers, panel->runtime.list_index);
@@ -280,32 +308,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 +333,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 +350,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 +419,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 +431,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_util.c b/source/blender/modifiers/intern/MOD_util.c
index 1aee545aa43..c6dff375109 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -169,12 +169,12 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd,
}
}
-void MOD_previous_vcos_store(ModifierData *md, float (*vertexCos)[3])
+void MOD_previous_vcos_store(ModifierData *md, const float (*vert_coords)[3])
{
while ((md = md->next) && md->type == eModifierType_Armature) {
ArmatureModifierData *amd = (ArmatureModifierData *)md;
- if (amd->multi && amd->prevCos == NULL) {
- amd->prevCos = MEM_dupallocN(vertexCos);
+ if (amd->multi && amd->vert_coords_prev == NULL) {
+ amd->vert_coords_prev = MEM_dupallocN(vert_coords);
}
else {
break;
@@ -187,7 +187,7 @@ void MOD_previous_vcos_store(ModifierData *md, float (*vertexCos)[3])
Mesh *MOD_deform_mesh_eval_get(Object *ob,
struct BMEditMesh *em,
Mesh *mesh,
- float (*vertexCos)[3],
+ const float (*vertexCos)[3],
const int num_verts,
const bool use_normals,
const bool use_orco)
diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h
index 38e2083082d..a05e25d204c 100644
--- a/source/blender/modifiers/intern/MOD_util.h
+++ b/source/blender/modifiers/intern/MOD_util.h
@@ -40,12 +40,12 @@ void MOD_get_texture_coords(struct MappingInfoModifierData *dmd,
float (*cos)[3],
float (*r_texco)[3]);
-void MOD_previous_vcos_store(struct ModifierData *md, float (*vertexCos)[3]);
+void MOD_previous_vcos_store(struct ModifierData *md, const float (*vertexCos)[3]);
struct Mesh *MOD_deform_mesh_eval_get(struct Object *ob,
struct BMEditMesh *em,
struct Mesh *mesh,
- float (*vertexCos)[3],
+ const float (*vertexCos)[3],
const int num_verts,
const bool use_normals,
const bool use_orco);
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index 07a57806f87..6f261f9f67a 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -392,4 +392,6 @@ ModifierTypeInfo modifierType_UVProject = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index a8321cdc26a..4aca3c28ed8 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -351,4 +351,6 @@ ModifierTypeInfo modifierType_UVWarp = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 339566354db..cbe774e91da 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"
@@ -466,6 +468,7 @@ static void falloff_panel_draw(const bContext *C, Panel *panel)
static void texture_panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -478,15 +481,15 @@ static void texture_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE);
-
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE);
if (texture_coords == MOD_DISP_MAP_OBJECT) {
- uiItemR(layout, &ptr, "texture_coords_object", 0, "Object", ICON_NONE);
+ uiItemR(col, &ptr, "texture_coords_object", 0, IFACE_("Object"), ICON_NONE);
PointerRNA texture_coords_obj_ptr = RNA_pointer_get(&ptr, "texture_coords_object");
if (!RNA_pointer_is_null(&texture_coords_obj_ptr) &&
(RNA_enum_get(&texture_coords_obj_ptr, "type") == OB_ARMATURE)) {
PointerRNA texture_coords_obj_data_ptr = RNA_pointer_get(&texture_coords_obj_ptr, "data");
- uiItemPointerR(layout,
+ uiItemPointerR(col,
&ptr,
"texture_coords_bone",
&texture_coords_obj_data_ptr,
@@ -497,7 +500,7 @@ static void texture_panel_draw(const bContext *C, Panel *panel)
}
else if (texture_coords == MOD_DISP_MAP_UV && RNA_enum_get(&ob_ptr, "type") == OB_MESH) {
PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
- uiItemPointerR(layout, &ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE);
+ uiItemPointerR(col, &ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE);
}
}
@@ -510,6 +513,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,4 +562,6 @@ ModifierTypeInfo modifierType_Warp = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 6a818629979..26f1bf2d69a 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);
@@ -449,6 +449,7 @@ static void time_panel_draw(const bContext *C, Panel *panel)
static void texture_panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -461,14 +462,15 @@ static void texture_panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "texture_coords", 0, IFACE_("Coordinates"), ICON_NONE);
if (texture_coords == MOD_DISP_MAP_OBJECT) {
- uiItemR(layout, &ptr, "texture_coords_object", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "texture_coords_object", 0, IFACE_("Object"), ICON_NONE);
PointerRNA texture_coords_obj_ptr = RNA_pointer_get(&ptr, "texture_coords_object");
if (!RNA_pointer_is_null(&texture_coords_obj_ptr) &&
(RNA_enum_get(&texture_coords_obj_ptr, "type") == OB_ARMATURE)) {
PointerRNA texture_coords_obj_data_ptr = RNA_pointer_get(&texture_coords_obj_ptr, "data");
- uiItemPointerR(layout,
+ uiItemPointerR(col,
&ptr,
"texture_coords_bone",
&texture_coords_obj_data_ptr,
@@ -479,7 +481,7 @@ static void texture_panel_draw(const bContext *C, Panel *panel)
}
else if (texture_coords == MOD_DISP_MAP_UV && RNA_enum_get(&ob_ptr, "type") == OB_MESH) {
PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
- uiItemPointerR(layout, &ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE);
+ uiItemPointerR(col, &ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE);
}
}
@@ -524,4 +526,6 @@ ModifierTypeInfo modifierType_Wave = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index 1ed892f5464..f174b6d5aa4 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -713,6 +713,7 @@ static bool dependsOnNormals(ModifierData *UNUSED(md))
static void panel_draw(const bContext *C, Panel *panel)
{
+ uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA ptr;
@@ -726,8 +727,9 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(layout, &ptr, "weight", 0, IFACE_("Weight"), ICON_NONE);
uiItemR(layout, &ptr, "thresh", 0, IFACE_("Threshold"), ICON_NONE);
- uiItemR(layout, &ptr, "keep_sharp", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "face_influence", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &ptr, "keep_sharp", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "face_influence", 0, NULL, ICON_NONE);
modifier_vgroup_ui(layout, &ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL);
@@ -770,4 +772,6 @@ ModifierTypeInfo modifierType_WeightedNormal = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 9a9ab55a835..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,4 +451,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
};
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index e089720b3e3..d1c618df68b 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -539,4 +539,6 @@ ModifierTypeInfo modifierType_WeightVGMix = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index d8bf8b7bf03..0668a7a086f 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -741,4 +741,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
/* foreachTexLink */ foreachTexLink,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c
index 76ca85163d9..cf92da1b0e6 100644
--- a/source/blender/modifiers/intern/MOD_weld.c
+++ b/source/blender/modifiers/intern/MOD_weld.c
@@ -2004,6 +2004,8 @@ ModifierTypeInfo modifierType_Weld = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
/** \} */
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 66a03ee5d71..ef3d16c1b32 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -208,4 +208,6 @@ ModifierTypeInfo modifierType_Wireframe = {
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
+ /* blendWrite */ NULL,
+ /* blendRead */ NULL,
};
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 80bf0f7c5e2..c53e01ac80f 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -30,6 +30,7 @@ set(INC
../blenlib
../blentranslation
../depsgraph
+ ../functions
../gpu
../imbuf
../makesdna
@@ -37,6 +38,7 @@ set(INC
../render/extern/include
../../../intern/glew-mx
../../../intern/guardedalloc
+ ../../../intern/sky/include
)
set(INC_SYS
@@ -133,6 +135,7 @@ set(SRC
function/nodes/node_fn_combine_strings.cc
function/nodes/node_fn_float_compare.cc
function/nodes/node_fn_group_instance_id.cc
+ function/nodes/node_fn_object_transforms.cc
function/nodes/node_fn_switch.cc
function/node_function_util.cc
@@ -157,7 +160,7 @@ set(SRC
shader/nodes/node_shader_bsdf_velvet.c
shader/nodes/node_shader_bump.c
shader/nodes/node_shader_camera.c
- shader/nodes/node_shader_clamp.c
+ shader/nodes/node_shader_clamp.cc
shader/nodes/node_shader_common.c
shader/nodes/node_shader_curves.c
shader/nodes/node_shader_displacement.c
@@ -174,9 +177,9 @@ set(SRC
shader/nodes/node_shader_layer_weight.c
shader/nodes/node_shader_light_falloff.c
shader/nodes/node_shader_light_path.c
- shader/nodes/node_shader_map_range.c
+ shader/nodes/node_shader_map_range.cc
shader/nodes/node_shader_mapping.c
- shader/nodes/node_shader_math.c
+ shader/nodes/node_shader_math.cc
shader/nodes/node_shader_mixRgb.c
shader/nodes/node_shader_mix_shader.c
shader/nodes/node_shader_normal.c
@@ -191,8 +194,8 @@ set(SRC
shader/nodes/node_shader_rgb.c
shader/nodes/node_shader_script.c
shader/nodes/node_shader_sepcombHSV.c
- shader/nodes/node_shader_sepcombRGB.c
- shader/nodes/node_shader_sepcombXYZ.c
+ shader/nodes/node_shader_sepcombRGB.cc
+ shader/nodes/node_shader_sepcombXYZ.cc
shader/nodes/node_shader_shaderToRgb.c
shader/nodes/node_shader_squeeze.c
shader/nodes/node_shader_subsurface_scattering.c
@@ -213,11 +216,11 @@ set(SRC
shader/nodes/node_shader_tex_white_noise.c
shader/nodes/node_shader_uvAlongStroke.c
shader/nodes/node_shader_uvmap.c
- shader/nodes/node_shader_valToRgb.c
- shader/nodes/node_shader_value.c
+ shader/nodes/node_shader_valToRgb.cc
+ shader/nodes/node_shader_value.cc
shader/nodes/node_shader_vectTransform.c
shader/nodes/node_shader_vector_displacement.c
- shader/nodes/node_shader_vector_math.c
+ shader/nodes/node_shader_vector_math.cc
shader/nodes/node_shader_vector_rotate.c
shader/nodes/node_shader_vertex_color.c
shader/nodes/node_shader_volume_absorption.c
@@ -271,20 +274,28 @@ set(SRC
texture/node_texture_tree.c
texture/node_texture_util.c
+ intern/derived_node_tree.cc
intern/node_common.c
intern/node_exec.c
- intern/node_socket.c
+ intern/node_socket.cc
+ intern/node_tree_dependencies.cc
+ intern/node_tree_multi_function.cc
+ intern/node_tree_ref.cc
intern/node_util.c
composite/node_composite_util.h
- function/node_function_util.h
+ function/node_function_util.hh
shader/node_shader_util.h
simulation/node_simulation_util.h
texture/node_texture_util.h
NOD_common.h
NOD_composite.h
+ NOD_derived_node_tree.hh
NOD_function.h
+ NOD_node_tree_dependencies.hh
+ NOD_node_tree_multi_function.hh
+ NOD_node_tree_ref.hh
NOD_shader.h
NOD_simulation.h
NOD_socket.h
@@ -296,6 +307,8 @@ set(SRC
)
set(LIB
+ bf_functions
+ bf_intern_sky
)
if(WITH_PYTHON)
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/blenkernel/BKE_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index 009d9b747f9..b39c9fd1a23 100644
--- a/source/blender/blenkernel/BKE_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __BKE_DERIVED_NODE_TREE_HH__
-#define __BKE_DERIVED_NODE_TREE_HH__
+#ifndef __NOD_DERIVED_NODE_TREE_HH__
+#define __NOD_DERIVED_NODE_TREE_HH__
/** \file
- * \ingroup bke
+ * \ingroup nodes
*
* DerivedNodeTree provides a flattened view on a bNodeTree, i.e. node groups are inlined. It
* builds on top of NodeTreeRef and supports similar queries efficiently.
@@ -30,9 +30,9 @@
* There is a dot graph exporter for debugging purposes.
*/
-#include "BKE_node_tree_ref.hh"
+#include "NOD_node_tree_ref.hh"
-namespace BKE {
+namespace blender::nodes {
class DSocket;
class DInputSocket;
@@ -42,19 +42,19 @@ 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;
- uint m_id;
+ DNode *node_;
+ const SocketRef *socket_ref_;
+ int id_;
friend DerivedNodeTree;
public:
const DNode &node() const;
- uint id() const;
- uint index() const;
+ int id() const;
+ int index() const;
bool is_input() const;
bool is_output() const;
@@ -66,12 +66,17 @@ 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 {
private:
- Vector<DOutputSocket *> m_linked_sockets;
- Vector<DGroupInput *> m_linked_group_inputs;
+ Vector<DOutputSocket *> linked_sockets_;
+ Vector<DGroupInput *> linked_group_inputs_;
friend DerivedNodeTree;
@@ -86,7 +91,7 @@ class DInputSocket : public DSocket {
class DOutputSocket : public DSocket {
private:
- Vector<DInputSocket *> m_linked_sockets;
+ Vector<DInputSocket *> linked_sockets_;
friend DerivedNodeTree;
@@ -95,32 +100,33 @@ 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;
- Vector<DInputSocket *> m_linked_sockets;
- uint m_id;
+ const InputSocketRef *socket_ref_;
+ DParentNode *parent_;
+ Vector<DInputSocket *> linked_sockets_;
+ int id_;
friend DerivedNodeTree;
public:
const InputSocketRef &socket_ref() const;
+ bNodeSocket *bsocket() const;
const DParentNode *parent() const;
Span<const DInputSocket *> linked_sockets() const;
- uint id() const;
+ int id() const;
StringRefNull name() const;
};
-class DNode : blender::NonCopyable, blender::NonMovable {
+class DNode : NonCopyable, NonMovable {
private:
- const NodeRef *m_node_ref;
- DParentNode *m_parent;
+ const NodeRef *node_ref_;
+ DParentNode *parent_;
- Span<DInputSocket *> m_inputs;
- Span<DOutputSocket *> m_outputs;
+ Span<DInputSocket *> inputs_;
+ Span<DOutputSocket *> outputs_;
- uint m_id;
+ int id_;
friend DerivedNodeTree;
@@ -131,10 +137,13 @@ class DNode : blender::NonCopyable, blender::NonMovable {
Span<const DInputSocket *> inputs() const;
Span<const DOutputSocket *> outputs() const;
- const DInputSocket &input(uint index) const;
- const DOutputSocket &output(uint index) const;
+ const DInputSocket &input(int index) const;
+ const DOutputSocket &output(int index) const;
- uint id() const;
+ const DInputSocket &input(int index, StringRef expected_name) const;
+ const DOutputSocket &output(int index, StringRef expected_name) const;
+
+ int id() const;
PointerRNA *rna() const;
StringRefNull idname() const;
@@ -144,47 +153,50 @@ 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;
- uint m_id;
+ const NodeRef *node_ref_;
+ DParentNode *parent_;
+ int id_;
friend DerivedNodeTree;
public:
const DParentNode *parent() const;
const NodeRef &node_ref() const;
- uint id() const;
+ int id() const;
};
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;
- Vector<DNode *> m_nodes_by_id;
- Vector<DGroupInput *> m_group_inputs;
- Vector<DParentNode *> m_parent_nodes;
+ LinearAllocator<> allocator_;
+ bNodeTree *btree_;
+ Vector<DNode *> nodes_by_id_;
+ Vector<DGroupInput *> group_inputs_;
+ Vector<DParentNode *> parent_nodes_;
- Vector<DSocket *> m_sockets_by_id;
- Vector<DInputSocket *> m_input_sockets;
- Vector<DOutputSocket *> m_output_sockets;
+ Vector<DSocket *> sockets_by_id_;
+ Vector<DInputSocket *> input_sockets_;
+ Vector<DOutputSocket *> output_sockets_;
- Map<std::string, Vector<DNode *>> m_nodes_by_idname;
+ MultiValueMap<const bNodeType *, DNode *> 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:
@@ -225,27 +237,27 @@ class DerivedNodeTree : blender::NonCopyable, blender::NonMovable {
inline const DNode &DSocket::node() const
{
- return *m_node;
+ return *node_;
}
-inline uint DSocket::id() const
+inline int DSocket::id() const
{
- return m_id;
+ return id_;
}
-inline uint DSocket::index() const
+inline int DSocket::index() const
{
- return m_socket_ref->index();
+ return socket_ref_->index();
}
inline bool DSocket::is_input() const
{
- return m_socket_ref->is_input();
+ return socket_ref_->is_input();
}
inline bool DSocket::is_output() const
{
- return m_socket_ref->is_output();
+ return socket_ref_->is_output();
}
inline const DSocket &DSocket::as_base() const
@@ -265,17 +277,32 @@ inline const DOutputSocket &DSocket::as_output() const
inline PointerRNA *DSocket::rna() const
{
- return m_socket_ref->rna();
+ return socket_ref_->rna();
}
inline StringRefNull DSocket::idname() const
{
- return m_socket_ref->idname();
+ return socket_ref_->idname();
}
inline StringRefNull DSocket::name() const
{
- return m_socket_ref->name();
+ return socket_ref_->name();
+}
+
+inline const SocketRef &DSocket::socket_ref() const
+{
+ return *socket_ref_;
+}
+
+inline bNodeSocket *DSocket::bsocket() const
+{
+ return socket_ref_->bsocket();
+}
+
+inline bool DSocket::is_available() const
+{
+ return (socket_ref_->bsocket()->flag & SOCK_UNAVAIL) == 0;
}
/* --------------------------------------------------------------------
@@ -284,22 +311,22 @@ inline StringRefNull DSocket::name() const
inline const InputSocketRef &DInputSocket::socket_ref() const
{
- return m_socket_ref->as_input();
+ return socket_ref_->as_input();
}
inline Span<const DOutputSocket *> DInputSocket::linked_sockets() const
{
- return m_linked_sockets.as_span();
+ return linked_sockets_;
}
inline Span<const DGroupInput *> DInputSocket::linked_group_inputs() const
{
- return m_linked_group_inputs.as_span();
+ return linked_group_inputs_;
}
inline bool DInputSocket::is_linked() const
{
- return m_linked_sockets.size() > 0 || m_linked_group_inputs.size() > 0;
+ return linked_sockets_.size() > 0 || linked_group_inputs_.size() > 0;
}
/* --------------------------------------------------------------------
@@ -308,12 +335,12 @@ inline bool DInputSocket::is_linked() const
inline const OutputSocketRef &DOutputSocket::socket_ref() const
{
- return m_socket_ref->as_output();
+ return socket_ref_->as_output();
}
inline Span<const DInputSocket *> DOutputSocket::linked_sockets() const
{
- return m_linked_sockets.as_span();
+ return linked_sockets_;
}
/* --------------------------------------------------------------------
@@ -322,27 +349,32 @@ inline Span<const DInputSocket *> DOutputSocket::linked_sockets() const
inline const InputSocketRef &DGroupInput::socket_ref() const
{
- return *m_socket_ref;
+ return *socket_ref_;
+}
+
+inline bNodeSocket *DGroupInput::bsocket() const
+{
+ return socket_ref_->bsocket();
}
inline const DParentNode *DGroupInput::parent() const
{
- return m_parent;
+ return parent_;
}
inline Span<const DInputSocket *> DGroupInput::linked_sockets() const
{
- return m_linked_sockets.as_span();
+ return linked_sockets_;
}
-inline uint DGroupInput::id() const
+inline int DGroupInput::id() const
{
- return m_id;
+ return id_;
}
inline StringRefNull DGroupInput::name() const
{
- return m_socket_ref->name();
+ return socket_ref_->name();
}
/* --------------------------------------------------------------------
@@ -351,52 +383,68 @@ inline StringRefNull DGroupInput::name() const
inline const NodeRef &DNode::node_ref() const
{
- return *m_node_ref;
+ return *node_ref_;
}
inline const DParentNode *DNode::parent() const
{
- return m_parent;
+ return parent_;
}
inline Span<const DInputSocket *> DNode::inputs() const
{
- return m_inputs;
+ return inputs_;
}
inline Span<const DOutputSocket *> DNode::outputs() const
{
- return m_outputs;
+ return outputs_;
}
-inline const DInputSocket &DNode::input(uint index) const
+inline const DInputSocket &DNode::input(int index) const
{
- return *m_inputs[index];
+ return *inputs_[index];
}
-inline const DOutputSocket &DNode::output(uint index) const
+inline const DOutputSocket &DNode::output(int index) const
{
- return *m_outputs[index];
+ return *outputs_[index];
}
-inline uint DNode::id() const
+inline const DInputSocket &DNode::input(int index, StringRef expected_name) const
{
- return m_id;
+ const DInputSocket &socket = *inputs_[index];
+ BLI_assert(socket.name() == expected_name);
+ UNUSED_VARS_NDEBUG(expected_name);
+ return socket;
+}
+
+inline const DOutputSocket &DNode::output(int index, StringRef expected_name) const
+{
+ const DOutputSocket &socket = *outputs_[index];
+ BLI_assert(socket.name() == expected_name);
+ UNUSED_VARS_NDEBUG(expected_name);
+ return socket;
+}
+
+inline int DNode::id() const
+{
+ return id_;
}
inline PointerRNA *DNode::rna() const
{
- return m_node_ref->rna();
+ return node_ref_->rna();
}
inline StringRefNull DNode::idname() const
{
- return m_node_ref->idname();
+ return node_ref_->idname();
}
inline StringRefNull DNode::name() const
{
- return m_node_ref->name();
+ return node_ref_->name();
}
/* --------------------------------------------------------------------
@@ -405,17 +453,17 @@ inline StringRefNull DNode::name() const
inline const DParentNode *DParentNode::parent() const
{
- return m_parent;
+ return parent_;
}
inline const NodeRef &DParentNode::node_ref() const
{
- return *m_node_ref;
+ return *node_ref_;
}
-inline uint DParentNode::id() const
+inline int DParentNode::id() const
{
- return m_id;
+ return id_;
}
/* --------------------------------------------------------------------
@@ -424,30 +472,40 @@ inline uint DParentNode::id() const
inline Span<const DNode *> DerivedNodeTree::nodes() const
{
- return m_nodes_by_id.as_span();
+ return nodes_by_id_;
}
-inline Span<const DNode *> DerivedNodeTree::nodes_with_idname(StringRef idname) const
+inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) const
{
- const Vector<DNode *> *nodes = m_nodes_by_idname.lookup_ptr(idname);
- if (nodes == nullptr) {
- return {};
- }
- else {
- return nodes->as_span();
- }
+ const bNodeType *nodetype = nodeTypeFind(idname.c_str());
+ return this->nodes_by_type(nodetype);
+}
+
+inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const
+{
+ return nodes_by_type_.lookup(nodetype);
+}
+
+inline Span<const DSocket *> DerivedNodeTree::sockets() const
+{
+ return sockets_by_id_;
}
inline Span<const DInputSocket *> DerivedNodeTree::input_sockets() const
{
- return m_input_sockets.as_span();
+ return input_sockets_;
}
inline Span<const DOutputSocket *> DerivedNodeTree::output_sockets() const
{
- return m_output_sockets.as_span();
+ return output_sockets_;
+}
+
+inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
+{
+ return group_inputs_;
}
-} // namespace BKE
+} // namespace blender::nodes
-#endif /* __BKE_DERIVED_NODE_TREE_HH__ */
+#endif /* __NOD_DERIVED_NODE_TREE_HH__ */
diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h
index 377ae8bfb84..4c05da694f7 100644
--- a/source/blender/nodes/NOD_function.h
+++ b/source/blender/nodes/NOD_function.h
@@ -26,6 +26,7 @@ void register_node_type_fn_float_compare(void);
void register_node_type_fn_switch(void);
void register_node_type_fn_group_instance_id(void);
void register_node_type_fn_combine_strings(void);
+void register_node_type_fn_object_transforms(void);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/NOD_node_tree_dependencies.hh b/source/blender/nodes/NOD_node_tree_dependencies.hh
new file mode 100644
index 00000000000..ca7059caa5f
--- /dev/null
+++ b/source/blender/nodes/NOD_node_tree_dependencies.hh
@@ -0,0 +1,79 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __NOD_NODE_TREE_DEPENDENCIES_H__
+#define __NOD_NODE_TREE_DEPENDENCIES_H__
+
+#include "BLI_vector_set.hh"
+
+#include "DNA_ID.h"
+#include "DNA_object_types.h"
+
+struct bNodeTree;
+
+namespace blender::nodes {
+
+class NodeTreeDependencies {
+ private:
+ VectorSet<Object *> transform_deps_;
+ VectorSet<Object *> geometry_deps_;
+ VectorSet<ID *> id_deps_;
+
+ public:
+ void add_transform_dependency(Object *object)
+ {
+ if (object == nullptr) {
+ return;
+ }
+ transform_deps_.add(object);
+ id_deps_.add(&object->id);
+ }
+
+ void add_geometry_dependency(Object *object)
+ {
+ if (object == nullptr) {
+ return;
+ }
+ geometry_deps_.add(object);
+ id_deps_.add(&object->id);
+ }
+
+ bool depends_on(ID *id) const
+ {
+ return id_deps_.contains(id);
+ }
+
+ Span<Object *> transform_dependencies()
+ {
+ return transform_deps_;
+ }
+
+ Span<Object *> geometry_dependencies()
+ {
+ return geometry_deps_;
+ }
+
+ Span<ID *> id_dependencies()
+ {
+ return id_deps_;
+ }
+};
+
+NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree);
+
+} // namespace blender::nodes
+
+#endif /* __NOD_NODE_TREE_DEPENDENCIES_H__ */
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
new file mode 100644
index 00000000000..81b467eca3a
--- /dev/null
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -0,0 +1,393 @@
+/*
+ * 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 __NOD_NODE_TREE_FUNCTION_HH__
+#define __NOD_NODE_TREE_FUNCTION_HH__
+
+/** \file
+ * \ingroup nodes
+ *
+ * This file allows you to generate a multi-function network from a user-generated node tree.
+ */
+
+#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_network.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "BLI_resource_collector.hh"
+
+namespace blender::nodes {
+
+/* Maybe this should be moved to BKE_node.h. */
+inline bool is_multi_function_data_socket(const bNodeSocket *bsocket)
+{
+ if (bsocket->typeinfo->get_mf_data_type != nullptr) {
+ BLI_assert(bsocket->typeinfo->expand_in_mf_network != nullptr);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * A MFNetworkTreeMap maps various components of a DerivedNodeTree to components of a
+ * fn::MFNetwork. This is necessary for further processing of a multi-function network that has
+ * been generated from a node tree.
+ */
+class MFNetworkTreeMap {
+ private:
+ /**
+ * Store by id instead of using a hash table to avoid unnecessary hash table lookups.
+ *
+ * Input sockets in a node tree can have multiple corresponding sockets in the generated
+ * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes.
+ */
+ const DerivedNodeTree &tree_;
+ fn::MFNetwork &network_;
+ Array<Vector<fn::MFSocket *, 1>> sockets_by_dsocket_id_;
+ Array<fn::MFOutputSocket *> socket_by_group_input_id_;
+
+ public:
+ MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
+ : tree_(tree),
+ network_(network),
+ sockets_by_dsocket_id_(tree.sockets().size()),
+ socket_by_group_input_id_(tree.group_inputs().size(), nullptr)
+ {
+ }
+
+ const DerivedNodeTree &tree() const
+ {
+ return tree_;
+ }
+
+ const fn::MFNetwork &network() const
+ {
+ return network_;
+ }
+
+ fn::MFNetwork &network()
+ {
+ return network_;
+ }
+
+ void add(const DSocket &dsocket, fn::MFSocket &socket)
+ {
+ BLI_assert(dsocket.is_input() == socket.is_input());
+ BLI_assert(dsocket.is_input() || sockets_by_dsocket_id_[dsocket.id()].size() == 0);
+ sockets_by_dsocket_id_[dsocket.id()].append(&socket);
+ }
+
+ void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
+ {
+ sockets_by_dsocket_id_[dsocket.id()].append(&socket);
+ }
+
+ void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
+ {
+ /* There can be at most one matching output socket. */
+ BLI_assert(sockets_by_dsocket_id_[dsocket.id()].size() == 0);
+ sockets_by_dsocket_id_[dsocket.id()].append(&socket);
+ }
+
+ void add(Span<const DInputSocket *> dsockets, Span<fn::MFInputSocket *> sockets)
+ {
+ assert_same_size(dsockets, sockets);
+ for (int i : dsockets.index_range()) {
+ this->add(*dsockets[i], *sockets[i]);
+ }
+ }
+
+ void add(Span<const DOutputSocket *> dsockets, Span<fn::MFOutputSocket *> sockets)
+ {
+ assert_same_size(dsockets, sockets);
+ for (int i : dsockets.index_range()) {
+ this->add(*dsockets[i], *sockets[i]);
+ }
+ }
+
+ void add(const DGroupInput &group_input, fn::MFOutputSocket &socket)
+ {
+ BLI_assert(socket_by_group_input_id_[group_input.id()] == nullptr);
+ socket_by_group_input_id_[group_input.id()] = &socket;
+ }
+
+ void add_try_match(const DNode &dnode, fn::MFNode &node)
+ {
+ this->add_try_match(dnode.inputs(), node.inputs());
+ this->add_try_match(dnode.outputs(), node.outputs());
+ }
+
+ void add_try_match(Span<const DSocket *> dsockets, Span<fn::MFSocket *> sockets)
+ {
+ int used_sockets = 0;
+ for (const DSocket *dsocket : dsockets) {
+ if (!dsocket->is_available()) {
+ continue;
+ }
+ if (!is_multi_function_data_socket(dsocket->bsocket())) {
+ continue;
+ }
+ fn::MFSocket *socket = sockets[used_sockets];
+ this->add(*dsocket, *socket);
+ used_sockets++;
+ }
+ }
+
+ fn::MFOutputSocket &lookup(const DGroupInput &group_input)
+ {
+ fn::MFOutputSocket *socket = socket_by_group_input_id_[group_input.id()];
+ BLI_assert(socket != nullptr);
+ return *socket;
+ }
+
+ fn::MFOutputSocket &lookup(const DOutputSocket &dsocket)
+ {
+ auto &sockets = sockets_by_dsocket_id_[dsocket.id()];
+ BLI_assert(sockets.size() == 1);
+ return sockets[0]->as_output();
+ }
+
+ Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
+ {
+ return sockets_by_dsocket_id_[dsocket.id()].as_span().cast<fn::MFInputSocket *>();
+ }
+
+ fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket)
+ {
+ Span<fn::MFInputSocket *> sockets = this->lookup(dsocket);
+ BLI_assert(sockets.size() == 1);
+ fn::MFInputSocket &socket = *sockets[0];
+ BLI_assert(socket.node().is_dummy());
+ return socket;
+ }
+
+ fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket)
+ {
+ fn::MFOutputSocket &socket = this->lookup(dsocket);
+ BLI_assert(socket.node().is_dummy());
+ return socket;
+ }
+
+ bool is_mapped(const DSocket &dsocket) const
+ {
+ return sockets_by_dsocket_id_[dsocket.id()].size() >= 1;
+ }
+};
+
+/**
+ * This data is necessary throughout the generation of a MFNetwork from a node tree.
+ */
+struct CommonMFNetworkBuilderData {
+ ResourceCollector &resources;
+ fn::MFNetwork &network;
+ MFNetworkTreeMap &network_map;
+ const DerivedNodeTree &tree;
+};
+
+class MFNetworkBuilderBase {
+ protected:
+ CommonMFNetworkBuilderData &common_;
+
+ public:
+ MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common)
+ {
+ }
+
+ /**
+ * Returns the network that is currently being built.
+ */
+ fn::MFNetwork &network()
+ {
+ return common_.network;
+ }
+
+ /**
+ * Returns the map between the node tree and the multi-function network that is being built.
+ */
+ MFNetworkTreeMap &network_map()
+ {
+ return common_.network_map;
+ }
+
+ /**
+ * Returns a resource collector that will only be destructed after the multi-function network is
+ * destructed.
+ */
+ ResourceCollector &resources()
+ {
+ return common_.resources;
+ }
+
+ /**
+ * Constructs a new function that will live at least as long as the MFNetwork.
+ */
+ template<typename T, typename... Args> T &construct_fn(Args &&... args)
+ {
+ BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
+ void *buffer = common_.resources.linear_allocator().allocate(sizeof(T), alignof(T));
+ T *fn = new (buffer) T(std::forward<Args>(args)...);
+ common_.resources.add(destruct_ptr<T>(fn), fn->name().c_str());
+ return *fn;
+ }
+};
+
+/**
+ * This class is used by socket implementations to define how an unlinked input socket is handled
+ * in a multi-function network.
+ */
+class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
+ private:
+ const DSocket *dsocket_ = nullptr;
+ const DGroupInput *group_input_ = nullptr;
+ bNodeSocket *bsocket_;
+ fn::MFOutputSocket *built_socket_ = nullptr;
+
+ public:
+ SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket)
+ : MFNetworkBuilderBase(common), dsocket_(&dsocket), bsocket_(dsocket.bsocket())
+ {
+ }
+
+ SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DGroupInput &group_input)
+ : MFNetworkBuilderBase(common), group_input_(&group_input), bsocket_(group_input.bsocket())
+ {
+ }
+
+ /**
+ * Returns the socket that is currently being built.
+ */
+ bNodeSocket &bsocket()
+ {
+ return *bsocket_;
+ }
+
+ /**
+ * Utility method that returns bsocket->default_value for the current socket.
+ */
+ template<typename T> T *socket_default_value()
+ {
+ return (T *)bsocket_->default_value;
+ }
+
+ /**
+ * Builds a function node for that socket that outputs the given constant value.
+ */
+ template<typename T> void set_constant_value(T value)
+ {
+ this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value));
+ }
+
+ template<typename T, typename... Args> void construct_generator_fn(Args &&... args)
+ {
+ const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...);
+ this->set_generator_fn(fn);
+ }
+
+ /**
+ * Uses the first output of the given multi-function as value of the socket.
+ */
+ void set_generator_fn(const fn::MultiFunction &fn)
+ {
+ fn::MFFunctionNode &node = common_.network.add_function(fn);
+ this->set_socket(node.output(0));
+ }
+
+ /**
+ * Define a multi-function socket that outputs the value of the bsocket.
+ */
+ void set_socket(fn::MFOutputSocket &socket)
+ {
+ built_socket_ = &socket;
+ }
+
+ fn::MFOutputSocket *built_socket()
+ {
+ return built_socket_;
+ }
+};
+
+/**
+ * This class is used by node implementations to define how a user-level node expands into
+ * multi-function nodes internally.
+ */
+class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
+ private:
+ const DNode &dnode_;
+
+ public:
+ NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DNode &dnode)
+ : MFNetworkBuilderBase(common), dnode_(dnode)
+ {
+ }
+
+ /**
+ * Tells the builder to build a function that corresponds to the node that is being built. It
+ * will try to match up sockets.
+ */
+ template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&... args)
+ {
+ T &function = this->construct_fn<T>(std::forward<Args>(args)...);
+ this->set_matching_fn(function);
+ return function;
+ }
+
+ const fn::MultiFunction &get_not_implemented_fn()
+ {
+ return this->get_default_fn("Not Implemented (" + dnode_.name() + ")");
+ }
+
+ const fn::MultiFunction &get_default_fn(StringRef name);
+
+ const void set_not_implemented()
+ {
+ this->set_matching_fn(this->get_not_implemented_fn());
+ }
+
+ /**
+ * Tells the builder that the given function corresponds to the node that is being built. It will
+ * try to match up sockets. For that it skips unavailable and non-data sockets.
+ */
+ void set_matching_fn(const fn::MultiFunction &function)
+ {
+ fn::MFFunctionNode &node = common_.network.add_function(function);
+ common_.network_map.add_try_match(dnode_, node);
+ }
+
+ /**
+ * Returns the node that is currently being built.
+ */
+ bNode &bnode()
+ {
+ return *dnode_.node_ref().bnode();
+ }
+
+ /**
+ * Returns the node that is currently being built.
+ */
+ const DNode &dnode() const
+ {
+ return dnode_;
+ }
+};
+
+MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
+ const DerivedNodeTree &tree,
+ ResourceCollector &resources);
+
+} // namespace blender::nodes
+
+#endif /* __NOD_NODE_TREE_FUNCTION_HH__ */
diff --git a/source/blender/blenkernel/BKE_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 043ef957af7..13656c2e940 100644
--- a/source/blender/blenkernel/BKE_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __BKE_NODE_TREE_REF_HH__
-#define __BKE_NODE_TREE_REF_HH__
+#ifndef __NOD_NODE_TREE_REF_HH__
+#define __NOD_NODE_TREE_REF_HH__
/** \file
- * \ingroup bke
+ * \ingroup nodes
*
* NodeTreeRef makes querying information about a bNodeTree more efficient. It is an immutable data
* structure. It should not be used after anymore, after the underlying node tree changed.
@@ -47,6 +47,7 @@
#include "BLI_array.hh"
#include "BLI_linear_allocator.hh"
#include "BLI_map.hh"
+#include "BLI_multi_value_map.hh"
#include "BLI_string_ref.hh"
#include "BLI_timeit.hh"
#include "BLI_utility_mixins.hh"
@@ -58,17 +59,7 @@
#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::nodes {
class SocketRef;
class InputSocketRef;
@@ -76,16 +67,16 @@ class OutputSocketRef;
class NodeRef;
class NodeTreeRef;
-class SocketRef : blender::NonCopyable, blender::NonMovable {
+class SocketRef : NonCopyable, NonMovable {
protected:
- NodeRef *m_node;
- bNodeSocket *m_bsocket;
- bool m_is_input;
- uint m_id;
- uint m_index;
- PointerRNA m_rna;
- Vector<SocketRef *> m_linked_sockets;
- Vector<SocketRef *> m_directly_linked_sockets;
+ NodeRef *node_;
+ bNodeSocket *bsocket_;
+ bool is_input_;
+ int id_;
+ int index_;
+ PointerRNA rna_;
+ Vector<SocketRef *> linked_sockets_;
+ Vector<SocketRef *> directly_linked_sockets_;
friend NodeTreeRef;
@@ -97,8 +88,8 @@ class SocketRef : blender::NonCopyable, blender::NonMovable {
const NodeRef &node() const;
const NodeTreeRef &tree() const;
- uint id() const;
- uint index() const;
+ int id() const;
+ int index() const;
bool is_input() const;
bool is_output() const;
@@ -129,14 +120,14 @@ 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;
- PointerRNA m_rna;
- uint m_id;
- Vector<InputSocketRef *> m_inputs;
- Vector<OutputSocketRef *> m_outputs;
+ NodeTreeRef *tree_;
+ bNode *bnode_;
+ PointerRNA rna_;
+ int id_;
+ Vector<InputSocketRef *> inputs_;
+ Vector<OutputSocketRef *> outputs_;
friend NodeTreeRef;
@@ -146,8 +137,8 @@ class NodeRef : blender::NonCopyable, blender::NonMovable {
Span<const InputSocketRef *> inputs() const;
Span<const OutputSocketRef *> outputs() const;
- const InputSocketRef &input(uint index) const;
- const OutputSocketRef &output(uint index) const;
+ const InputSocketRef &input(int index) const;
+ const OutputSocketRef &output(int index) const;
bNode *bnode() const;
bNodeTree *btree() const;
@@ -156,7 +147,7 @@ class NodeRef : blender::NonCopyable, blender::NonMovable {
StringRefNull idname() const;
StringRefNull name() const;
- uint id() const;
+ int id() const;
bool is_reroute_node() const;
bool is_group_node() const;
@@ -164,22 +155,23 @@ 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;
- Vector<NodeRef *> m_nodes_by_id;
- 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;
+ LinearAllocator<> allocator_;
+ bNodeTree *btree_;
+ Vector<NodeRef *> nodes_by_id_;
+ Vector<SocketRef *> sockets_by_id_;
+ Vector<InputSocketRef *> input_sockets_;
+ Vector<OutputSocketRef *> output_sockets_;
+ MultiValueMap<const bNodeType *, NodeRef *> 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;
@@ -206,47 +198,47 @@ class NodeTreeRef : blender::NonCopyable, blender::NonMovable {
inline Span<const SocketRef *> SocketRef::linked_sockets() const
{
- return m_linked_sockets.as_span();
+ return linked_sockets_;
}
inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const
{
- return m_directly_linked_sockets.as_span();
+ return directly_linked_sockets_;
}
inline bool SocketRef::is_linked() const
{
- return m_linked_sockets.size() > 0;
+ return linked_sockets_.size() > 0;
}
inline const NodeRef &SocketRef::node() const
{
- return *m_node;
+ return *node_;
}
inline const NodeTreeRef &SocketRef::tree() const
{
- return m_node->tree();
+ return node_->tree();
}
-inline uint SocketRef::id() const
+inline int SocketRef::id() const
{
- return m_id;
+ return id_;
}
-inline uint SocketRef::index() const
+inline int SocketRef::index() const
{
- return m_index;
+ return index_;
}
inline bool SocketRef::is_input() const
{
- return m_is_input;
+ return is_input_;
}
inline bool SocketRef::is_output() const
{
- return !m_is_input;
+ return !is_input_;
}
inline const SocketRef &SocketRef::as_base() const
@@ -268,32 +260,32 @@ inline const OutputSocketRef &SocketRef::as_output() const
inline PointerRNA *SocketRef::rna() const
{
- return const_cast<PointerRNA *>(&m_rna);
+ return const_cast<PointerRNA *>(&rna_);
}
inline StringRefNull SocketRef::idname() const
{
- return m_bsocket->idname;
+ return bsocket_->idname;
}
inline StringRefNull SocketRef::name() const
{
- return m_bsocket->name;
+ return bsocket_->name;
}
inline bNodeSocket *SocketRef::bsocket() const
{
- return m_bsocket;
+ return bsocket_;
}
inline bNode *SocketRef::bnode() const
{
- return m_node->bnode();
+ return node_->bnode();
}
inline bNodeTree *SocketRef::btree() const
{
- return m_node->btree();
+ return node_->btree();
}
/* --------------------------------------------------------------------
@@ -302,12 +294,12 @@ inline bNodeTree *SocketRef::btree() const
inline Span<const OutputSocketRef *> InputSocketRef::linked_sockets() const
{
- return m_linked_sockets.as_span().cast<const OutputSocketRef *>();
+ return linked_sockets_.as_span().cast<const OutputSocketRef *>();
}
inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() const
{
- return m_directly_linked_sockets.as_span().cast<const OutputSocketRef *>();
+ return directly_linked_sockets_.as_span().cast<const OutputSocketRef *>();
}
/* --------------------------------------------------------------------
@@ -316,12 +308,12 @@ inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() c
inline Span<const InputSocketRef *> OutputSocketRef::linked_sockets() const
{
- return m_linked_sockets.as_span().cast<const InputSocketRef *>();
+ return linked_sockets_.as_span().cast<const InputSocketRef *>();
}
inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() const
{
- return m_directly_linked_sockets.as_span().cast<const InputSocketRef *>();
+ return directly_linked_sockets_.as_span().cast<const InputSocketRef *>();
}
/* --------------------------------------------------------------------
@@ -330,77 +322,77 @@ inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() c
inline const NodeTreeRef &NodeRef::tree() const
{
- return *m_tree;
+ return *tree_;
}
inline Span<const InputSocketRef *> NodeRef::inputs() const
{
- return m_inputs.as_span();
+ return inputs_;
}
inline Span<const OutputSocketRef *> NodeRef::outputs() const
{
- return m_outputs.as_span();
+ return outputs_;
}
-inline const InputSocketRef &NodeRef::input(uint index) const
+inline const InputSocketRef &NodeRef::input(int index) const
{
- return *m_inputs[index];
+ return *inputs_[index];
}
-inline const OutputSocketRef &NodeRef::output(uint index) const
+inline const OutputSocketRef &NodeRef::output(int index) const
{
- return *m_outputs[index];
+ return *outputs_[index];
}
inline bNode *NodeRef::bnode() const
{
- return m_bnode;
+ return bnode_;
}
inline bNodeTree *NodeRef::btree() const
{
- return m_tree->btree();
+ return tree_->btree();
}
inline PointerRNA *NodeRef::rna() const
{
- return const_cast<PointerRNA *>(&m_rna);
+ return const_cast<PointerRNA *>(&rna_);
}
inline StringRefNull NodeRef::idname() const
{
- return m_bnode->idname;
+ return bnode_->idname;
}
inline StringRefNull NodeRef::name() const
{
- return m_bnode->name;
+ return bnode_->name;
}
-inline uint NodeRef::id() const
+inline int NodeRef::id() const
{
- return m_id;
+ return id_;
}
inline bool NodeRef::is_reroute_node() const
{
- return m_bnode->type == NODE_REROUTE;
+ return bnode_->type == NODE_REROUTE;
}
inline bool NodeRef::is_group_node() const
{
- return m_bnode->type == NODE_GROUP;
+ return bnode_->type == NODE_GROUP;
}
inline bool NodeRef::is_group_input_node() const
{
- return m_bnode->type == NODE_GROUP_INPUT;
+ return bnode_->type == NODE_GROUP_INPUT;
}
inline bool NodeRef::is_group_output_node() const
{
- return m_bnode->type == NODE_GROUP_OUTPUT;
+ return bnode_->type == NODE_GROUP_OUTPUT;
}
/* --------------------------------------------------------------------
@@ -409,40 +401,40 @@ inline bool NodeRef::is_group_output_node() const
inline Span<const NodeRef *> NodeTreeRef::nodes() const
{
- return m_nodes_by_id.as_span();
+ return nodes_by_id_;
+}
+
+inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const
+{
+ const bNodeType *nodetype = nodeTypeFind(idname.c_str());
+ return this->nodes_by_type(nodetype);
}
-inline Span<const NodeRef *> NodeTreeRef::nodes_with_idname(StringRef idname) const
+inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const
{
- const Vector<NodeRef *> *nodes = m_nodes_by_idname.lookup_ptr(idname);
- if (nodes == nullptr) {
- return {};
- }
- else {
- return nodes->as_span();
- }
+ return nodes_by_type_.lookup(nodetype);
}
inline Span<const SocketRef *> NodeTreeRef::sockets() const
{
- return m_sockets_by_id.as_span();
+ return sockets_by_id_;
}
inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const
{
- return m_input_sockets.as_span();
+ return input_sockets_;
}
inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const
{
- return m_output_sockets.as_span();
+ return output_sockets_;
}
inline bNodeTree *NodeTreeRef::btree() const
{
- return m_btree;
+ return btree_;
}
-} // namespace BKE
+} // namespace blender::nodes
-#endif /* __BKE_NODE_TREE_REF_HH__ */
+#endif /* __NOD_NODE_TREE_REF_HH__ */
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_static_types.h b/source/blender/nodes/NOD_static_types.h
index 91aa11566d3..31ce3f81450 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -276,6 +276,8 @@ DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE",
DefNode(FunctionNode, FN_NODE_SWITCH, def_fn_switch, "SWITCH", Switch, "Switch", "")
DefNode(FunctionNode, FN_NODE_GROUP_INSTANCE_ID, 0, "GROUP_INSTANCE_ID", GroupInstanceID, "Group Instance ID", "")
DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS", CombineStrings, "Combine Strings", "")
+DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "")
+
/* undefine macros */
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/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc
index 0927ba335fe..342c330a8fa 100644
--- a/source/blender/nodes/function/node_function_util.cc
+++ b/source/blender/nodes/function/node_function_util.cc
@@ -14,7 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_function_util.h"
+#include "node_function_util.hh"
#include "node_util.h"
bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
diff --git a/source/blender/nodes/function/node_function_util.h b/source/blender/nodes/function/node_function_util.hh
index 85e252f9bdd..8e09ab0f24f 100644
--- a/source/blender/nodes/function/node_function_util.h
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -19,6 +19,7 @@
#include <string.h>
+#include "BLI_float3.hh"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -30,9 +31,12 @@
#include "BLT_translation.h"
#include "NOD_function.h"
+#include "NOD_node_tree_multi_function.hh"
#include "node_util.h"
+#include "FN_multi_function_builder.hh"
+
void fn_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
bool fn_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index 615ad4c6733..231771abbfa 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -19,7 +19,7 @@
#include "RNA_enum_types.h"
-#include "node_function_util.h"
+#include "node_function_util.hh"
static bNodeSocketTemplate fn_node_boolean_math_in[] = {
{SOCK_BOOLEAN, N_("Boolean")},
@@ -50,6 +50,33 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char
BLI_strncpy(label, IFACE_(name), maxlen);
}
+static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
+{
+ static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
+ "And", [](bool a, bool b) { return a && b; }};
+ static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{
+ "Or", [](bool a, bool b) { return a || b; }};
+ static blender::fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }};
+
+ switch (bnode.custom1) {
+ case NODE_BOOLEAN_MATH_AND:
+ return and_fn;
+ case NODE_BOOLEAN_MATH_OR:
+ return or_fn;
+ case NODE_BOOLEAN_MATH_NOT:
+ return not_fn;
+ }
+
+ BLI_assert(false);
+ return blender::fn::dummy_multi_function;
+}
+
+static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ builder.set_matching_fn(fn);
+}
+
void register_node_type_fn_boolean_math()
{
static bNodeType ntype;
@@ -58,5 +85,6 @@ void register_node_type_fn_boolean_math()
node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out);
node_type_label(&ntype, node_boolean_math_label);
node_type_update(&ntype, node_boolean_math_update);
+ ntype.expand_in_mf_network = node_boolean_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_combine_strings.cc b/source/blender/nodes/function/nodes/node_fn_combine_strings.cc
index 1b6091451d9..a545c4f0749 100644
--- a/source/blender/nodes/function/nodes/node_fn_combine_strings.cc
+++ b/source/blender/nodes/function/nodes/node_fn_combine_strings.cc
@@ -1,4 +1,20 @@
-#include "node_function_util.h"
+/*
+ * 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 "node_function_util.hh"
static bNodeSocketTemplate fn_node_combine_strings_in[] = {
{SOCK_STRING, N_("A")},
@@ -11,11 +27,20 @@ static bNodeSocketTemplate fn_node_combine_strings_out[] = {
{-1, ""},
};
+static void fn_node_combine_strings_expand_in_mf_network(
+ blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static blender::fn::CustomMF_SI_SI_SO<std::string, std::string, std::string> combine_fn{
+ "Combine Strings", [](const std::string &a, const std::string &b) { return a + b; }};
+ builder.set_matching_fn(combine_fn);
+}
+
void register_node_type_fn_combine_strings()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_COMBINE_STRINGS, "Combine Strings", 0, 0);
node_type_socket_templates(&ntype, fn_node_combine_strings_in, fn_node_combine_strings_out);
+ ntype.expand_in_mf_network = fn_node_combine_strings_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index 9788402850b..f8bd9a30940 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -14,12 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <cmath>
+
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "RNA_enum_types.h"
-#include "node_function_util.h"
+#include "node_function_util.hh"
static bNodeSocketTemplate fn_node_float_compare_in[] = {
{SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
@@ -54,6 +56,46 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
+static const blender::fn::MultiFunction &get_multi_function(bNode &node)
+{
+ static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{
+ "Less Than", [](float a, float b) { return a < b; }};
+ static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_equal_fn{
+ "Less Equal", [](float a, float b) { return a <= b; }};
+ static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_than_fn{
+ "Greater Than", [](float a, float b) { return a > b; }};
+ static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_equal_fn{
+ "Greater Equal", [](float a, float b) { return a >= b; }};
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> equal_fn{
+ "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }};
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> not_equal_fn{
+ "Not Equal", [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }};
+
+ switch (node.custom1) {
+ case NODE_FLOAT_COMPARE_LESS_THAN:
+ return less_than_fn;
+ case NODE_FLOAT_COMPARE_LESS_EQUAL:
+ return less_equal_fn;
+ case NODE_FLOAT_COMPARE_GREATER_THAN:
+ return greater_than_fn;
+ case NODE_FLOAT_COMPARE_GREATER_EQUAL:
+ return greater_equal_fn;
+ case NODE_FLOAT_COMPARE_EQUAL:
+ return equal_fn;
+ case NODE_FLOAT_COMPARE_NOT_EQUAL:
+ return not_equal_fn;
+ }
+
+ BLI_assert(false);
+ return blender::fn::dummy_multi_function;
+}
+
+static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ builder.set_matching_fn(fn);
+}
+
void register_node_type_fn_float_compare()
{
static bNodeType ntype;
@@ -62,5 +104,6 @@ void register_node_type_fn_float_compare()
node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out);
node_type_label(&ntype, node_float_compare_label);
node_type_update(&ntype, node_float_compare_update);
+ ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc b/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc
index 2ac86ee2407..1e22cde721d 100644
--- a/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc
+++ b/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc
@@ -1,15 +1,45 @@
-#include "node_function_util.h"
+/*
+ * 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 "node_function_util.hh"
static bNodeSocketTemplate fn_node_group_instance_id_out[] = {
{SOCK_STRING, N_("Identifier")},
{-1, ""},
};
+static void fn_node_group_instance_id_expand_in_mf_network(
+ blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const blender::nodes::DNode &node = builder.dnode();
+ std::string id = "/";
+ for (const blender::nodes::DParentNode *parent = node.parent(); parent;
+ parent = parent->parent()) {
+ id = "/" + parent->node_ref().name() + id;
+ }
+ builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(
+ std::move(id));
+}
+
void register_node_type_fn_group_instance_id()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_GROUP_INSTANCE_ID, "Group Instance ID", 0, 0);
node_type_socket_templates(&ntype, nullptr, fn_node_group_instance_id_out);
+ ntype.expand_in_mf_network = fn_node_group_instance_id_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_object_transforms.cc b/source/blender/nodes/function/nodes/node_fn_object_transforms.cc
new file mode 100644
index 00000000000..26b25406590
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_object_transforms.cc
@@ -0,0 +1,88 @@
+/*
+ * 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 "node_function_util.hh"
+
+#include "BKE_persistent_data_handle.hh"
+
+static bNodeSocketTemplate fn_node_object_transforms_in[] = {
+ {SOCK_OBJECT, N_("Object")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate fn_node_object_transforms_out[] = {
+ {SOCK_VECTOR, N_("Location")},
+ {-1, ""},
+};
+
+class ObjectTransformsFunction : public blender::fn::MultiFunction {
+ public:
+ ObjectTransformsFunction()
+ {
+ blender::fn::MFSignatureBuilder signature = this->get_builder("Object Transforms");
+ signature.depends_on_context();
+ signature.single_input<blender::bke::PersistentObjectHandle>("Object");
+ signature.single_output<blender::float3>("Location");
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext context) const override
+ {
+ blender::fn::VSpan handles =
+ params.readonly_single_input<blender::bke::PersistentObjectHandle>(0, "Object");
+ blender::MutableSpan locations = params.uninitialized_single_output<blender::float3>(
+ 1, "Location");
+
+ const blender::bke::PersistentDataHandleMap *handle_map =
+ context.get_global_context<blender::bke::PersistentDataHandleMap>(
+ "PersistentDataHandleMap");
+ if (handle_map == nullptr) {
+ locations.fill_indices(mask, {0, 0, 0});
+ return;
+ }
+
+ for (int64_t i : mask) {
+ blender::bke::PersistentObjectHandle handle = handles[i];
+ const Object *object = handle_map->lookup(handle);
+ blender::float3 location;
+ if (object == nullptr) {
+ location = {0, 0, 0};
+ }
+ else {
+ location = object->loc;
+ }
+ locations[i] = location;
+ }
+ }
+};
+
+static void fn_node_object_transforms_expand_in_mf_network(
+ blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static ObjectTransformsFunction fn;
+ builder.set_matching_fn(fn);
+}
+
+void register_node_type_fn_object_transforms()
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(&ntype, FN_NODE_OBJECT_TRANSFORMS, "Object Transforms", 0, 0);
+ node_type_socket_templates(&ntype, fn_node_object_transforms_in, fn_node_object_transforms_out);
+ ntype.expand_in_mf_network = fn_node_object_transforms_expand_in_mf_network;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/function/nodes/node_fn_switch.cc b/source/blender/nodes/function/nodes/node_fn_switch.cc
index cb721058875..281ddb05c76 100644
--- a/source/blender/nodes/function/nodes/node_fn_switch.cc
+++ b/source/blender/nodes/function/nodes/node_fn_switch.cc
@@ -15,7 +15,7 @@
*/
#include "BLI_listbase.h"
-#include "node_function_util.h"
+#include "node_function_util.hh"
static bNodeSocketTemplate fn_node_switch_in[] = {
{SOCK_BOOLEAN, N_("Switch")},
diff --git a/source/blender/blenkernel/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index 961fef958b7..5414ea9208a 100644
--- a/source/blender/blenkernel/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -14,21 +14,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_derived_node_tree.hh"
+#include "NOD_derived_node_tree.hh"
#include "BLI_dot_export.hh"
#define UNINITIALIZED_ID UINT32_MAX
-namespace BKE {
+namespace blender::nodes {
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)
+DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
{
const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree);
@@ -62,8 +62,8 @@ BLI_NOINLINE void DerivedNodeTree::insert_nodes_and_links_in_id_order(const Node
DInputSocket *to_socket = (DInputSocket *)sockets_map[to_socket_ref->id()];
for (const OutputSocketRef *from_socket_ref : to_socket_ref->linked_sockets()) {
DOutputSocket *from_socket = (DOutputSocket *)sockets_map[from_socket_ref->id()];
- to_socket->m_linked_sockets.append(from_socket);
- from_socket->m_linked_sockets.append(to_socket);
+ to_socket->linked_sockets_.append(from_socket);
+ from_socket->linked_sockets_.append(to_socket);
}
}
}
@@ -73,34 +73,34 @@ DNode &DerivedNodeTree::create_node(const NodeRef &node_ref,
DParentNode *parent,
MutableSpan<DSocket *> r_sockets_map)
{
- DNode &node = *m_allocator.construct<DNode>();
- node.m_node_ref = &node_ref;
- node.m_parent = parent;
- node.m_id = UNINITIALIZED_ID;
+ DNode &node = *allocator_.construct<DNode>();
+ node.node_ref_ = &node_ref;
+ node.parent_ = parent;
+ node.id_ = UNINITIALIZED_ID;
- node.m_inputs = m_allocator.construct_elements_and_pointer_array<DInputSocket>(
+ node.inputs_ = allocator_.construct_elements_and_pointer_array<DInputSocket>(
node_ref.inputs().size());
- node.m_outputs = m_allocator.construct_elements_and_pointer_array<DOutputSocket>(
+ node.outputs_ = allocator_.construct_elements_and_pointer_array<DOutputSocket>(
node_ref.outputs().size());
- for (uint i : node.m_inputs.index_range()) {
+ for (int i : node.inputs_.index_range()) {
const InputSocketRef &socket_ref = node_ref.input(i);
- DInputSocket &socket = *node.m_inputs[i];
+ DInputSocket &socket = *node.inputs_[i];
- socket.m_id = UNINITIALIZED_ID;
- socket.m_node = &node;
- socket.m_socket_ref = &socket_ref;
+ socket.id_ = UNINITIALIZED_ID;
+ socket.node_ = &node;
+ socket.socket_ref_ = &socket_ref;
r_sockets_map[socket_ref.id()] = &socket;
}
- for (uint i : node.m_outputs.index_range()) {
+ for (int i : node.outputs_.index_range()) {
const OutputSocketRef &socket_ref = node_ref.output(i);
- DOutputSocket &socket = *node.m_outputs[i];
+ DOutputSocket &socket = *node.outputs_[i];
- socket.m_id = UNINITIALIZED_ID;
- socket.m_node = &node;
- socket.m_socket_ref = &socket_ref;
+ socket.id_ = UNINITIALIZED_ID;
+ socket.node_ = &node;
+ socket.socket_ref_ = &socket_ref;
r_sockets_map[socket_ref.id()] = &socket;
}
@@ -113,9 +113,9 @@ BLI_NOINLINE void DerivedNodeTree::expand_groups(Vector<DNode *> &all_nodes,
Vector<DParentNode *> &all_parent_nodes,
NodeTreeRefMap &node_tree_refs)
{
- for (uint i = 0; i < all_nodes.size(); i++) {
+ for (int i = 0; i < all_nodes.size(); i++) {
DNode &node = *all_nodes[i];
- if (node.m_node_ref->is_group_node()) {
+ if (node.node_ref_->is_group_node()) {
this->expand_group_node(node, all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
}
}
@@ -127,7 +127,7 @@ BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
Vector<DParentNode *> &all_parent_nodes,
NodeTreeRefMap &node_tree_refs)
{
- const NodeRef &group_node_ref = *group_node.m_node_ref;
+ const NodeRef &group_node_ref = *group_node.node_ref_;
BLI_assert(group_node_ref.is_group_node());
bNodeTree *btree = (bNodeTree *)group_node_ref.bnode()->id;
@@ -137,10 +137,10 @@ BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
const NodeTreeRef &group_ref = get_tree_ref(node_tree_refs, btree);
- DParentNode &parent = *m_allocator.construct<DParentNode>();
- parent.m_id = all_parent_nodes.append_and_get_index(&parent);
- parent.m_parent = group_node.m_parent;
- parent.m_node_ref = &group_node_ref;
+ DParentNode &parent = *allocator_.construct<DParentNode>();
+ parent.id_ = all_parent_nodes.append_and_get_index(&parent);
+ parent.parent_ = group_node.parent_;
+ parent.node_ref_ = &group_node_ref;
this->insert_nodes_and_links_in_id_order(group_ref, &parent, all_nodes);
Span<DNode *> new_nodes_by_id = all_nodes.as_span().take_back(group_ref.nodes().size());
@@ -153,18 +153,18 @@ BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
BLI_NOINLINE void DerivedNodeTree::create_group_inputs_for_unlinked_inputs(
DNode &node, Vector<DGroupInput *> &all_group_inputs)
{
- for (DInputSocket *input_socket : node.m_inputs) {
+ for (DInputSocket *input_socket : node.inputs_) {
if (input_socket->is_linked()) {
continue;
}
- DGroupInput &group_input = *m_allocator.construct<DGroupInput>();
- group_input.m_id = UNINITIALIZED_ID;
- group_input.m_socket_ref = &input_socket->socket_ref();
- group_input.m_parent = node.m_parent;
+ DGroupInput &group_input = *allocator_.construct<DGroupInput>();
+ group_input.id_ = UNINITIALIZED_ID;
+ group_input.socket_ref_ = &input_socket->socket_ref();
+ group_input.parent_ = node.parent_;
- group_input.m_linked_sockets.append(input_socket);
- input_socket->m_linked_group_inputs.append(&group_input);
+ group_input.linked_sockets_.append(input_socket);
+ input_socket->linked_group_inputs_.append(&group_input);
all_group_inputs.append(&group_input);
}
}
@@ -173,7 +173,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;
}
@@ -181,38 +181,38 @@ BLI_NOINLINE void DerivedNodeTree::relink_group_inputs(const NodeTreeRef &group_
const NodeRef &input_node_ref = *node_refs[0];
DNode &input_node = *nodes_by_id[input_node_ref.id()];
- uint input_amount = group_node.inputs().size();
+ int input_amount = group_node.inputs().size();
BLI_assert(input_amount == input_node_ref.outputs().size() - 1);
- for (uint input_index : IndexRange(input_amount)) {
- DInputSocket *outside_group = group_node.m_inputs[input_index];
- DOutputSocket *inside_group = input_node.m_outputs[input_index];
+ for (int input_index : IndexRange(input_amount)) {
+ DInputSocket *outside_group = group_node.inputs_[input_index];
+ DOutputSocket *inside_group = input_node.outputs_[input_index];
- for (DOutputSocket *outside_connected : outside_group->m_linked_sockets) {
- outside_connected->m_linked_sockets.remove_first_occurrence_and_reorder(outside_group);
+ for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
+ outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
}
- for (DGroupInput *outside_connected : outside_group->m_linked_group_inputs) {
- outside_connected->m_linked_sockets.remove_first_occurrence_and_reorder(outside_group);
+ for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
+ outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
}
- for (DInputSocket *inside_connected : inside_group->m_linked_sockets) {
- inside_connected->m_linked_sockets.remove_first_occurrence_and_reorder(inside_group);
+ for (DInputSocket *inside_connected : inside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
- for (DOutputSocket *outside_connected : outside_group->m_linked_sockets) {
- inside_connected->m_linked_sockets.append(outside_connected);
- outside_connected->m_linked_sockets.append(inside_connected);
+ for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.append(outside_connected);
+ outside_connected->linked_sockets_.append(inside_connected);
}
- for (DGroupInput *outside_connected : outside_group->m_linked_group_inputs) {
- inside_connected->m_linked_group_inputs.append(outside_connected);
- outside_connected->m_linked_sockets.append(inside_connected);
+ for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
+ inside_connected->linked_group_inputs_.append(outside_connected);
+ outside_connected->linked_sockets_.append(inside_connected);
}
}
- inside_group->m_linked_sockets.clear();
- outside_group->m_linked_sockets.clear();
- outside_group->m_linked_group_inputs.clear();
+ inside_group->linked_sockets_.clear();
+ outside_group->linked_sockets_.clear();
+ outside_group->linked_group_inputs_.clear();
}
}
@@ -220,7 +220,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;
}
@@ -228,37 +228,37 @@ BLI_NOINLINE void DerivedNodeTree::relink_group_outputs(const NodeTreeRef &group
const NodeRef &output_node_ref = *node_refs[0];
DNode &output_node = *nodes_by_id[output_node_ref.id()];
- uint output_amount = group_node.outputs().size();
+ int output_amount = group_node.outputs().size();
BLI_assert(output_amount == output_node_ref.inputs().size() - 1);
- for (uint output_index : IndexRange(output_amount)) {
- DOutputSocket *outside_group = group_node.m_outputs[output_index];
- DInputSocket *inside_group = output_node.m_inputs[output_index];
+ for (int output_index : IndexRange(output_amount)) {
+ DOutputSocket *outside_group = group_node.outputs_[output_index];
+ DInputSocket *inside_group = output_node.inputs_[output_index];
- for (DInputSocket *outside_connected : outside_group->m_linked_sockets) {
- outside_connected->m_linked_sockets.remove_first_occurrence_and_reorder(outside_group);
+ for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
+ outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
}
- for (DOutputSocket *inside_connected : inside_group->m_linked_sockets) {
- inside_connected->m_linked_sockets.remove_first_occurrence_and_reorder(inside_group);
+ for (DOutputSocket *inside_connected : inside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
- for (DInputSocket *outside_connected : outside_group->m_linked_sockets) {
- inside_connected->m_linked_sockets.append(outside_connected);
- outside_connected->m_linked_sockets.append(inside_connected);
+ for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.append(outside_connected);
+ outside_connected->linked_sockets_.append(inside_connected);
}
}
- for (DGroupInput *inside_connected : inside_group->m_linked_group_inputs) {
- inside_connected->m_linked_sockets.remove_first_occurrence_and_reorder(inside_group);
+ for (DGroupInput *inside_connected : inside_group->linked_group_inputs_) {
+ inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
- for (DInputSocket *outside_connected : outside_group->m_linked_sockets) {
- inside_connected->m_linked_sockets.append(outside_connected);
- outside_connected->m_linked_group_inputs.append(inside_connected);
+ for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.append(outside_connected);
+ outside_connected->linked_group_inputs_.append(inside_connected);
}
}
- outside_group->m_linked_sockets.clear();
- inside_group->m_linked_sockets.clear();
+ outside_group->linked_sockets_.clear();
+ inside_group->linked_sockets_.clear();
}
}
@@ -267,9 +267,9 @@ BLI_NOINLINE void DerivedNodeTree::remove_expanded_group_interfaces(Vector<DNode
int index = 0;
while (index < all_nodes.size()) {
DNode &node = *all_nodes[index];
- const NodeRef &node_ref = *node.m_node_ref;
+ const NodeRef &node_ref = *node.node_ref_;
if (node_ref.is_group_node() ||
- (node.m_parent != nullptr &&
+ (node.parent_ != nullptr &&
(node_ref.is_group_input_node() || node_ref.is_group_output_node()))) {
all_nodes.remove_and_reorder(index);
node.destruct_with_sockets();
@@ -286,7 +286,7 @@ BLI_NOINLINE void DerivedNodeTree::remove_unused_group_inputs(
int index = 0;
while (index < all_group_inputs.size()) {
DGroupInput &group_input = *all_group_inputs[index];
- if (group_input.m_linked_sockets.is_empty()) {
+ if (group_input.linked_sockets_.is_empty()) {
all_group_inputs.remove_and_reorder(index);
group_input.~DGroupInput();
}
@@ -298,10 +298,10 @@ BLI_NOINLINE void DerivedNodeTree::remove_unused_group_inputs(
void DNode::destruct_with_sockets()
{
- for (DInputSocket *socket : m_inputs) {
+ for (DInputSocket *socket : inputs_) {
socket->~DInputSocket();
}
- for (DOutputSocket *socket : m_outputs) {
+ for (DOutputSocket *socket : outputs_) {
socket->~DOutputSocket();
}
this->~DNode();
@@ -312,63 +312,62 @@ BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids(
Vector<DGroupInput *> &&all_group_inputs,
Vector<DParentNode *> &&all_parent_nodes)
{
- m_nodes_by_id = std::move(all_nodes);
- m_group_inputs = std::move(all_group_inputs);
- m_parent_nodes = std::move(all_parent_nodes);
+ nodes_by_id_ = std::move(all_nodes);
+ group_inputs_ = std::move(all_group_inputs);
+ parent_nodes_ = std::move(all_parent_nodes);
- for (uint node_index : m_nodes_by_id.index_range()) {
- DNode *node = m_nodes_by_id[node_index];
- node->m_id = node_index;
+ for (int node_index : nodes_by_id_.index_range()) {
+ DNode *node = nodes_by_id_[node_index];
+ node->id_ = node_index;
- m_nodes_by_idname.lookup_or_add_default(node->idname()).append(node);
+ const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo;
+ nodes_by_type_.add(nodetype, node);
- for (DInputSocket *socket : node->m_inputs) {
- socket->m_id = m_sockets_by_id.append_and_get_index(socket);
- m_input_sockets.append(socket);
+ for (DInputSocket *socket : node->inputs_) {
+ socket->id_ = sockets_by_id_.append_and_get_index(socket);
+ input_sockets_.append(socket);
}
- for (DOutputSocket *socket : node->m_outputs) {
- socket->m_id = m_sockets_by_id.append_and_get_index(socket);
- m_output_sockets.append(socket);
+ for (DOutputSocket *socket : node->outputs_) {
+ socket->id_ = sockets_by_id_.append_and_get_index(socket);
+ output_sockets_.append(socket);
}
}
- for (uint i : m_group_inputs.index_range()) {
- m_group_inputs[i]->m_id = i;
+ for (int i : group_inputs_.index_range()) {
+ group_inputs_[i]->id_ = i;
}
}
DerivedNodeTree::~DerivedNodeTree()
{
- for (DInputSocket *socket : m_input_sockets) {
+ for (DInputSocket *socket : input_sockets_) {
socket->~DInputSocket();
}
- for (DOutputSocket *socket : m_output_sockets) {
+ for (DOutputSocket *socket : output_sockets_) {
socket->~DOutputSocket();
}
- for (DNode *node : m_nodes_by_id) {
+ for (DNode *node : nodes_by_id_) {
node->~DNode();
}
- for (DGroupInput *group_input : m_group_inputs) {
+ for (DGroupInput *group_input : group_inputs_) {
group_input->~DGroupInput();
}
- for (DParentNode *parent : m_parent_nodes) {
+ for (DParentNode *parent : parent_nodes_) {
parent->~DParentNode();
}
}
-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 +376,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("");
+ for (const DNode *node : nodes_by_id_) {
+ dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
Vector<std::string> input_names;
@@ -398,37 +397,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("");
+ for (const DGroupInput *group_input : group_inputs_) {
+ 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);
+ for (const DNode *to_node : nodes_by_id_) {
+ 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 +438,4 @@ std::string DerivedNodeTree::to_dot() const
return digraph.to_dot_string();
}
-} // namespace BKE
+} // namespace blender::nodes
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..57c8d51975e 100644
--- a/source/blender/nodes/intern/node_socket.c
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -25,6 +25,8 @@
#include "DNA_node_types.h"
+#include "BLI_color.hh"
+#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
@@ -32,12 +34,14 @@
#include "BKE_lib_id.h"
#include "BKE_node.h"
+#include "BKE_persistent_data_handle.hh"
#include "RNA_access.h"
#include "RNA_types.h"
#include "MEM_guardedalloc.h"
+#include "NOD_node_tree_multi_function.hh"
#include "NOD_socket.h"
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
@@ -53,26 +57,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 +85,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 +102,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 +157,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 +204,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 +215,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 +226,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 +235,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 +247,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 +264,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 +302,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 +393,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 +426,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 +470,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));
@@ -511,39 +514,162 @@ static bNodeSocketType *make_socket_type_control_flow(int type)
return stype;
}
+static bNodeSocketType *make_socket_type_bool()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
+ socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<bool>(); };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value;
+ builder.set_constant_value(value);
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
+ socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<float>(); };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ float value = builder.socket_default_value<bNodeSocketValueFloat>()->value;
+ builder.set_constant_value(value);
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
+ socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<int>(); };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ int value = builder.socket_default_value<bNodeSocketValueInt>()->value;
+ builder.set_constant_value(value);
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
+ socktype->get_mf_data_type = []() {
+ return blender::fn::MFDataType::ForSingle<blender::float3>();
+ };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value;
+ builder.set_constant_value(value);
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_rgba()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
+ socktype->get_mf_data_type = []() {
+ return blender::fn::MFDataType::ForSingle<blender::Color4f>();
+ };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value;
+ builder.set_constant_value(value);
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_string()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
+ socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<std::string>(); };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ std::string value = builder.socket_default_value<bNodeSocketValueString>()->value;
+ builder.set_constant_value(value);
+ };
+ return socktype;
+}
+
+class ObjectSocketMultiFunction : public blender::fn::MultiFunction {
+ private:
+ Object *object_;
+
+ public:
+ ObjectSocketMultiFunction(Object *object) : object_(object)
+ {
+ blender::fn::MFSignatureBuilder signature = this->get_builder("Object Socket");
+ signature.depends_on_context();
+ signature.single_output<blender::bke::PersistentObjectHandle>("Object");
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext context) const override
+ {
+ blender::MutableSpan output =
+ params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object");
+
+ /* Try to get a handle map, so that the object can be converted to a handle. */
+ const blender::bke::PersistentDataHandleMap *handle_map =
+ context.get_global_context<blender::bke::PersistentDataHandleMap>(
+ "PersistentDataHandleMap");
+
+ if (handle_map == nullptr) {
+ /* Return empty handles when there is no handle map. */
+ output.fill_indices(mask, blender::bke::PersistentObjectHandle());
+ return;
+ }
+
+ blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_);
+ for (int64_t i : mask) {
+ output[i] = handle;
+ }
+ }
+};
+
+MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
+
+static bNodeSocketType *make_socket_type_object()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
+ socktype->get_mf_data_type = []() {
+ /* Objects are not passed along as raw pointers, but as handles. */
+ return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>();
+ };
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
+ Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
+ builder.construct_generator_fn<ObjectSocketMultiFunction>(object);
+ };
+ return socktype;
+}
+
void register_standard_node_socket_types(void)
{
/* draw callbacks are set in drawnode.c to avoid bad-level calls */
- nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_NONE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_UNSIGNED));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_PERCENTAGE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_FACTOR));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_ANGLE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_TIME));
+ nodeRegisterSocketType(make_socket_type_float(PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED));
+ nodeRegisterSocketType(make_socket_type_float(PROP_PERCENTAGE));
+ nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR));
+ nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE));
+ nodeRegisterSocketType(make_socket_type_float(PROP_TIME));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_NONE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_UNSIGNED));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_PERCENTAGE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_FACTOR));
+ nodeRegisterSocketType(make_socket_type_int(PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_int(PROP_UNSIGNED));
+ nodeRegisterSocketType(make_socket_type_int(PROP_PERCENTAGE));
+ nodeRegisterSocketType(make_socket_type_int(PROP_FACTOR));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_bool());
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_NONE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_TRANSLATION));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_DIRECTION));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_VELOCITY));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_ACCELERATION));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_EULER));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_XYZ));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_TRANSLATION));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_DIRECTION));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_VELOCITY));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_ACCELERATION));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_EULER));
+ nodeRegisterSocketType(make_socket_type_vector(PROP_XYZ));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_RGBA, PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_rgba());
- nodeRegisterSocketType(make_standard_socket_type(SOCK_STRING, PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_string());
nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE));
- nodeRegisterSocketType(make_standard_socket_type(SOCK_OBJECT, PROP_NONE));
+ nodeRegisterSocketType(make_socket_type_object());
nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE));
diff --git a/source/blender/nodes/intern/node_tree_dependencies.cc b/source/blender/nodes/intern/node_tree_dependencies.cc
new file mode 100644
index 00000000000..efe75a10f7e
--- /dev/null
+++ b/source/blender/nodes/intern/node_tree_dependencies.cc
@@ -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.
+ */
+
+#include "NOD_node_tree_dependencies.hh"
+
+#include "DNA_node_types.h"
+
+#include "BKE_node.h"
+
+namespace blender::nodes {
+
+static void add_dependencies_of_node_tree(bNodeTree &ntree, NodeTreeDependencies &r_dependencies)
+{
+ /* TODO: Do a bit more sophisticated parsing to see which dependencies are really required. */
+ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->type == SOCK_OBJECT) {
+ Object *object = ((bNodeSocketValueObject *)socket->default_value)->value;
+ if (object != nullptr) {
+ r_dependencies.add_transform_dependency(object);
+ if (object->type == OB_MESH) {
+ r_dependencies.add_geometry_dependency(object);
+ }
+ }
+ }
+ }
+
+ if (node->type == NODE_GROUP) {
+ bNodeTree *group = (bNodeTree *)node->id;
+ if (group != nullptr) {
+ add_dependencies_of_node_tree(*group, r_dependencies);
+ }
+ }
+ }
+}
+
+NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree)
+{
+ NodeTreeDependencies dependencies;
+ add_dependencies_of_node_tree(ntree, dependencies);
+ return dependencies;
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
new file mode 100644
index 00000000000..82842c4ef32
--- /dev/null
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -0,0 +1,344 @@
+/*
+ * 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 "NOD_node_tree_multi_function.hh"
+
+#include "BLI_color.hh"
+#include "BLI_float3.hh"
+
+namespace blender::nodes {
+
+/* Maybe this should be moved to BKE_node.h. */
+static std::optional<fn::MFDataType> try_get_multi_function_data_type_of_socket(
+ const bNodeSocket *bsocket)
+{
+ if (bsocket->typeinfo->get_mf_data_type == nullptr) {
+ return {};
+ }
+ return bsocket->typeinfo->get_mf_data_type();
+}
+
+const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
+{
+ Vector<fn::MFDataType, 10> input_types;
+ Vector<fn::MFDataType, 10> output_types;
+
+ for (const DInputSocket *dsocket : dnode_.inputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ input_types.append(*data_type);
+ }
+ }
+ }
+ for (const DOutputSocket *dsocket : dnode_.outputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ output_types.append(*data_type);
+ }
+ }
+ }
+
+ const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>(
+ name, input_types, output_types);
+ return fn;
+}
+
+static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode)
+{
+ constexpr int stack_capacity = 10;
+
+ Vector<fn::MFDataType, stack_capacity> input_types;
+ Vector<StringRef, stack_capacity> input_names;
+ Vector<const DInputSocket *, stack_capacity> input_dsockets;
+
+ for (const DInputSocket *dsocket : dnode.inputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ input_types.append(*data_type);
+ input_names.append(dsocket->name());
+ input_dsockets.append(dsocket);
+ }
+ }
+ }
+
+ Vector<fn::MFDataType, stack_capacity> output_types;
+ Vector<StringRef, stack_capacity> output_names;
+ Vector<const DOutputSocket *, stack_capacity> output_dsockets;
+
+ for (const DOutputSocket *dsocket : dnode.outputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ output_types.append(*data_type);
+ output_names.append(dsocket->name());
+ output_dsockets.append(dsocket);
+ }
+ }
+ }
+
+ fn::MFDummyNode &dummy_node = common.network.add_dummy(
+ dnode.name(), input_types, output_types, input_names, output_names);
+
+ common.network_map.add(input_dsockets, dummy_node.inputs());
+ common.network_map.add(output_dsockets, dummy_node.outputs());
+}
+
+static bool has_data_sockets(const DNode &dnode)
+{
+ for (const DInputSocket *socket : dnode.inputs()) {
+ if (is_multi_function_data_socket(socket->bsocket())) {
+ return true;
+ }
+ }
+ for (const DOutputSocket *socket : dnode.outputs()) {
+ if (is_multi_function_data_socket(socket->bsocket())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Expands all function nodes in the multi-function network. Nodes that don't have an expand
+ * function, but do have data sockets, will get corresponding dummy nodes.
+ */
+static void insert_nodes(CommonMFNetworkBuilderData &common)
+{
+ for (const DNode *dnode : common.tree.nodes()) {
+ const bNodeType *node_type = dnode->node_ref().bnode()->typeinfo;
+ if (node_type->expand_in_mf_network != nullptr) {
+ NodeMFNetworkBuilder builder{common, *dnode};
+ node_type->expand_in_mf_network(builder);
+ }
+ else if (has_data_sockets(*dnode)) {
+ insert_dummy_node(common, *dnode);
+ }
+ }
+}
+
+static void insert_group_inputs(CommonMFNetworkBuilderData &common)
+{
+ for (const DGroupInput *group_input : common.tree.group_inputs()) {
+ bNodeSocket *bsocket = group_input->bsocket();
+ if (is_multi_function_data_socket(bsocket)) {
+ bNodeSocketType *socktype = bsocket->typeinfo;
+ BLI_assert(socktype->expand_in_mf_network != nullptr);
+
+ SocketMFNetworkBuilder builder{common, *group_input};
+ socktype->expand_in_mf_network(builder);
+
+ fn::MFOutputSocket *from_socket = builder.built_socket();
+ BLI_assert(from_socket != nullptr);
+ common.network_map.add(*group_input, *from_socket);
+ }
+ }
+}
+
+static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
+ const DInputSocket &to_dsocket)
+{
+ Span<const DOutputSocket *> from_dsockets = to_dsocket.linked_sockets();
+ Span<const DGroupInput *> from_group_inputs = to_dsocket.linked_group_inputs();
+ int total_linked_amount = from_dsockets.size() + from_group_inputs.size();
+ BLI_assert(total_linked_amount <= 1);
+
+ if (total_linked_amount == 0) {
+ return nullptr;
+ }
+
+ if (from_dsockets.size() == 1) {
+ const DOutputSocket &from_dsocket = *from_dsockets[0];
+ if (!from_dsocket.is_available()) {
+ return nullptr;
+ }
+ if (is_multi_function_data_socket(from_dsocket.bsocket())) {
+ return &common.network_map.lookup(from_dsocket);
+ }
+ return nullptr;
+ }
+ else {
+ const DGroupInput &from_group_input = *from_group_inputs[0];
+ if (is_multi_function_data_socket(from_group_input.bsocket())) {
+ return &common.network_map.lookup(from_group_input);
+ }
+ return nullptr;
+ }
+}
+
+using ImplicitConversionsMap =
+ Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *>;
+
+template<typename From, typename To>
+static void add_implicit_conversion(ImplicitConversionsMap &map)
+{
+ static fn::CustomMF_Convert<From, To> function;
+ map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
+}
+
+template<typename From, typename To, typename ConversionF>
+static void add_implicit_conversion(ImplicitConversionsMap &map,
+ StringRef name,
+ ConversionF conversion)
+{
+ static fn::CustomMF_SI_SO<From, To> function{name, conversion};
+ map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
+}
+
+static ImplicitConversionsMap get_implicit_conversions()
+{
+ ImplicitConversionsMap conversions;
+ add_implicit_conversion<float, int32_t>(conversions);
+ add_implicit_conversion<float, float3>(conversions);
+ add_implicit_conversion<int32_t, float>(conversions);
+ add_implicit_conversion<float3, float>(
+ conversions, "Vector Length", [](float3 a) { return a.length(); });
+ add_implicit_conversion<int32_t, float3>(
+ conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
+ add_implicit_conversion<float3, Color4f>(
+ conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
+ add_implicit_conversion<Color4f, float3>(
+ conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
+ return conversions;
+}
+
+static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to)
+{
+ static const ImplicitConversionsMap conversions = get_implicit_conversions();
+ const fn::MultiFunction *function = conversions.lookup_default({from, to}, nullptr);
+ return function;
+}
+
+static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
+ fn::MFDataType type)
+{
+ const fn::MultiFunction *default_fn;
+ if (type.is_single()) {
+ default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>(
+ AT, type.single_type(), type.single_type().default_value());
+ }
+ else {
+ default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>(
+ AT, fn::GSpan(type.vector_base_type()));
+ }
+
+ fn::MFNode &node = common.network.add_function(*default_fn);
+ return node.output(0);
+}
+
+static void insert_links(CommonMFNetworkBuilderData &common)
+{
+ for (const DInputSocket *to_dsocket : common.tree.input_sockets()) {
+ if (!to_dsocket->is_available()) {
+ continue;
+ }
+ if (!to_dsocket->is_linked()) {
+ continue;
+ }
+ if (!is_multi_function_data_socket(to_dsocket->bsocket())) {
+ continue;
+ }
+
+ Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(*to_dsocket);
+ BLI_assert(to_sockets.size() >= 1);
+ fn::MFDataType to_type = to_sockets[0]->data_type();
+
+ fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket);
+ if (from_socket == nullptr) {
+ from_socket = &insert_default_value_for_type(common, to_type);
+ }
+
+ fn::MFDataType from_type = from_socket->data_type();
+
+ if (from_type != to_type) {
+ const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type);
+ if (conversion_fn != nullptr) {
+ fn::MFNode &node = common.network.add_function(*conversion_fn);
+ common.network.add_link(*from_socket, node.input(0));
+ from_socket = &node.output(0);
+ }
+ else {
+ from_socket = &insert_default_value_for_type(common, to_type);
+ }
+ }
+
+ for (fn::MFInputSocket *to_socket : to_sockets) {
+ common.network.add_link(*from_socket, *to_socket);
+ }
+ }
+}
+
+static void insert_unlinked_input(CommonMFNetworkBuilderData &common, const DInputSocket &dsocket)
+{
+ bNodeSocket *bsocket = dsocket.bsocket();
+ bNodeSocketType *socktype = bsocket->typeinfo;
+ BLI_assert(socktype->expand_in_mf_network != nullptr);
+
+ SocketMFNetworkBuilder builder{common, dsocket};
+ socktype->expand_in_mf_network(builder);
+
+ fn::MFOutputSocket *from_socket = builder.built_socket();
+ BLI_assert(from_socket != nullptr);
+
+ for (fn::MFInputSocket *to_socket : common.network_map.lookup(dsocket)) {
+ common.network.add_link(*from_socket, *to_socket);
+ }
+}
+
+static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common)
+{
+ Vector<const DInputSocket *> unlinked_data_inputs;
+ for (const DInputSocket *dsocket : common.tree.input_sockets()) {
+ if (dsocket->is_available()) {
+ if (is_multi_function_data_socket(dsocket->bsocket())) {
+ if (!dsocket->is_linked()) {
+ insert_unlinked_input(common, *dsocket);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Expands all function nodes contained in the given node tree within the given multi-function
+ * network.
+ *
+ * Returns a mapping between the original node tree and the generated nodes/sockets for further
+ * processing.
+ */
+MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
+ const DerivedNodeTree &tree,
+ ResourceCollector &resources)
+{
+ MFNetworkTreeMap network_map{tree, network};
+
+ CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+
+ insert_nodes(common);
+ insert_group_inputs(common);
+ insert_links(common);
+ insert_unlinked_inputs(common);
+
+ return network_map;
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/blenkernel/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 517c1b85aff..47669bc5ca2 100644
--- a/source/blender/blenkernel/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -14,46 +14,46 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_node_tree_ref.hh"
+#include "NOD_node_tree_ref.hh"
#include "BLI_dot_export.hh"
-namespace BKE {
+namespace blender::nodes {
-NodeTreeRef::NodeTreeRef(bNodeTree *btree) : m_btree(btree)
+NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
{
Map<bNode *, NodeRef *> node_mapping;
LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) {
- NodeRef &node = *m_allocator.construct<NodeRef>();
+ NodeRef &node = *allocator_.construct<NodeRef>();
- node.m_tree = this;
- node.m_bnode = bnode;
- node.m_id = m_nodes_by_id.append_and_get_index(&node);
- RNA_pointer_create(&btree->id, &RNA_Node, bnode, &node.m_rna);
+ node.tree_ = this;
+ node.bnode_ = bnode;
+ node.id_ = nodes_by_id_.append_and_get_index(&node);
+ RNA_pointer_create(&btree->id, &RNA_Node, bnode, &node.rna_);
LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
- InputSocketRef &socket = *m_allocator.construct<InputSocketRef>();
- socket.m_node = &node;
- socket.m_index = node.m_inputs.append_and_get_index(&socket);
- socket.m_is_input = true;
- socket.m_bsocket = bsocket;
- socket.m_id = m_sockets_by_id.append_and_get_index(&socket);
- RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.m_rna);
+ InputSocketRef &socket = *allocator_.construct<InputSocketRef>();
+ socket.node_ = &node;
+ socket.index_ = node.inputs_.append_and_get_index(&socket);
+ socket.is_input_ = true;
+ socket.bsocket_ = bsocket;
+ socket.id_ = sockets_by_id_.append_and_get_index(&socket);
+ RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_);
}
LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
- OutputSocketRef &socket = *m_allocator.construct<OutputSocketRef>();
- socket.m_node = &node;
- socket.m_index = node.m_outputs.append_and_get_index(&socket);
- socket.m_is_input = false;
- socket.m_bsocket = bsocket;
- socket.m_id = m_sockets_by_id.append_and_get_index(&socket);
- RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.m_rna);
+ OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>();
+ socket.node_ = &node;
+ socket.index_ = node.outputs_.append_and_get_index(&socket);
+ socket.is_input_ = false;
+ socket.bsocket_ = bsocket;
+ socket.id_ = sockets_by_id_.append_and_get_index(&socket);
+ RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_);
}
- m_input_sockets.extend(node.m_inputs);
- m_output_sockets.extend(node.m_outputs);
+ input_sockets_.extend(node.inputs_.as_span());
+ output_sockets_.extend(node.outputs_.as_span());
node_mapping.add_new(bnode, &node);
}
@@ -64,33 +64,34 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : m_btree(btree)
InputSocketRef &to_socket = this->find_input_socket(
node_mapping, blink->tonode, blink->tosock);
- from_socket.m_directly_linked_sockets.append(&to_socket);
- to_socket.m_directly_linked_sockets.append(&from_socket);
+ from_socket.directly_linked_sockets_.append(&to_socket);
+ to_socket.directly_linked_sockets_.append(&from_socket);
}
- for (OutputSocketRef *socket : m_output_sockets) {
- if (!socket->m_node->is_reroute_node()) {
- this->find_targets_skipping_reroutes(*socket, socket->m_linked_sockets);
- for (SocketRef *target : socket->m_linked_sockets) {
- target->m_linked_sockets.append(socket);
+ for (OutputSocketRef *socket : output_sockets_) {
+ if (!socket->node_->is_reroute_node()) {
+ this->find_targets_skipping_reroutes(*socket, socket->linked_sockets_);
+ for (SocketRef *target : socket->linked_sockets_) {
+ target->linked_sockets_.append(socket);
}
}
}
- for (NodeRef *node : m_nodes_by_id) {
- m_nodes_by_idname.lookup_or_add_default(node->idname()).append(node);
+ for (NodeRef *node : nodes_by_id_) {
+ const bNodeType *nodetype = node->bnode_->typeinfo;
+ nodes_by_type_.add(nodetype, node);
}
}
NodeTreeRef::~NodeTreeRef()
{
- for (NodeRef *node : m_nodes_by_id) {
+ for (NodeRef *node : nodes_by_id_) {
node->~NodeRef();
}
- for (InputSocketRef *socket : m_input_sockets) {
+ for (InputSocketRef *socket : input_sockets_) {
socket->~InputSocketRef();
}
- for (OutputSocketRef *socket : m_output_sockets) {
+ for (OutputSocketRef *socket : output_sockets_) {
socket->~OutputSocketRef();
}
}
@@ -100,13 +101,13 @@ InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_map
bNodeSocket *bsocket)
{
NodeRef *node = node_mapping.lookup(bnode);
- for (SocketRef *socket : node->m_inputs) {
- if (socket->m_bsocket == bsocket) {
+ for (SocketRef *socket : node->inputs_) {
+ if (socket->bsocket_ == bsocket) {
return *(InputSocketRef *)socket;
}
}
BLI_assert(false);
- return *node->m_inputs[0];
+ return *node->inputs_[0];
}
OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
@@ -114,21 +115,21 @@ OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_m
bNodeSocket *bsocket)
{
NodeRef *node = node_mapping.lookup(bnode);
- for (SocketRef *socket : node->m_outputs) {
- if (socket->m_bsocket == bsocket) {
+ for (SocketRef *socket : node->outputs_) {
+ if (socket->bsocket_ == bsocket) {
return *(OutputSocketRef *)socket;
}
}
BLI_assert(false);
- return *node->m_outputs[0];
+ return *node->outputs_[0];
}
void NodeTreeRef::find_targets_skipping_reroutes(OutputSocketRef &socket,
Vector<SocketRef *> &r_targets)
{
- for (SocketRef *direct_target : socket.m_directly_linked_sockets) {
- if (direct_target->m_node->is_reroute_node()) {
- this->find_targets_skipping_reroutes(*direct_target->m_node->m_outputs[0], r_targets);
+ for (SocketRef *direct_target : socket.directly_linked_sockets_) {
+ if (direct_target->node_->is_reroute_node()) {
+ this->find_targets_skipping_reroutes(*direct_target->node_->outputs_[0], r_targets);
}
else {
r_targets.append_non_duplicates(direct_target);
@@ -138,15 +139,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("");
+ for (const NodeRef *node : nodes_by_id_) {
+ dot::Node &dot_node = digraph.new_node("");
dot_node.set_background_color("white");
Vector<std::string> input_names;
@@ -159,13 +158,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 OutputSocketRef *from_socket : 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 +174,4 @@ std::string NodeTreeRef::to_dot() const
return digraph.to_dot_string();
}
-} // namespace BKE
+} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 8834de0633e..b0ba1ea194f 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -42,6 +42,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "BLI_math_base_safe.h"
#include "BLI_rand.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
@@ -69,6 +70,17 @@
#include "GPU_texture.h"
#include "GPU_uniformbuffer.h"
+#ifdef __cplusplus
+# include "FN_multi_function_builder.hh"
+
+# include "NOD_node_tree_multi_function.hh"
+
+# include "BLI_color.hh"
+# include "BLI_float3.hh"
+
+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 +113,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_clamp.c b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
index 808f9686f0a..1077f616a62 100644
--- a/source/blender/nodes/shader/nodes/node_shader_clamp.c
+++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
@@ -50,6 +50,30 @@ static int gpu_shader_clamp(GPUMaterial *mat,
GPU_stack_link(mat, node, "clamp_range", in, out);
}
+static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{
+ "Clamp (Min Max)",
+ [](float value, float min, float max) { return std::min(std::max(value, min), max); }};
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> range_fn{
+ "Clamp (Range)", [](float value, float a, float b) {
+ if (a < b) {
+ return clamp_f(value, a, b);
+ }
+ else {
+ return clamp_f(value, b, a);
+ }
+ }};
+
+ int clamp_type = builder.bnode().custom1;
+ if (clamp_type == NODE_CLAMP_MINMAX) {
+ builder.set_matching_fn(minmax_fn);
+ }
+ else {
+ builder.set_matching_fn(range_fn);
+ }
+}
+
void register_node_type_sh_clamp(void)
{
static bNodeType ntype;
@@ -58,6 +82,7 @@ void register_node_type_sh_clamp(void)
node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out);
node_type_init(&ntype, node_shader_init_clamp);
node_type_gpu(&ntype, gpu_shader_clamp);
+ ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.c b/source/blender/nodes/shader/nodes/node_shader_geometry.c
index deb0ee9037c..30e376f8e09 100644
--- a/source/blender/nodes/shader/nodes/node_shader_geometry.c
+++ b/source/blender/nodes/shader/nodes/node_shader_geometry.c
@@ -45,6 +45,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) :
GPU_builtin(GPU_BARYCENTRIC_TEXCO);
+ if (out[5].hasoutput) {
+ GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
+ }
/* Opti: don't request orco if not needed. */
GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) :
GPU_attribute(mat, CD_ORCO, "");
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.c b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index 5db7983e752..4a6c9ec9a4d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.c
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -50,22 +50,33 @@ static void node_shader_init_map_range(bNodeTree *UNUSED(ntree), bNode *node)
node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */
}
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case NODE_MAP_RANGE_LINEAR:
+ return "map_range_linear";
+ case NODE_MAP_RANGE_STEPPED:
+ return "map_range_stepped";
+ case NODE_MAP_RANGE_SMOOTHSTEP:
+ return "map_range_smoothstep";
+ case NODE_MAP_RANGE_SMOOTHERSTEP:
+ return "map_range_smootherstep";
+ }
+
+ return nullptr;
+}
+
static int gpu_shader_map_range(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
- static const char *names[] = {
- [NODE_MAP_RANGE_LINEAR] = "map_range_linear",
- [NODE_MAP_RANGE_STEPPED] = "map_range_stepped",
- [NODE_MAP_RANGE_SMOOTHSTEP] = "map_range_smoothstep",
- [NODE_MAP_RANGE_SMOOTHERSTEP] = "map_range_smootherstep",
- };
+ const char *name = gpu_shader_get_name(node->custom2);
int ret = 0;
- if (node->custom2 < ARRAY_SIZE(names) && names[node->custom2]) {
- ret = GPU_stack_link(mat, node, names[node->custom2], in, out);
+ if (name != nullptr) {
+ ret = GPU_stack_link(mat, node, name, in, out);
}
else {
ret = GPU_stack_link(mat, node, "map_range_linear", in, out);
@@ -77,6 +88,68 @@ static int gpu_shader_map_range(GPUMaterial *mat,
return ret;
}
+class MapRangeFunction : public blender::fn::MultiFunction {
+ private:
+ bool clamp_;
+
+ public:
+ MapRangeFunction(bool clamp) : clamp_(clamp)
+ {
+ blender::fn::MFSignatureBuilder signature = this->get_builder("Map Range");
+ signature.single_input<float>("Value");
+ signature.single_input<float>("From Min");
+ signature.single_input<float>("From Max");
+ signature.single_input<float>("To Min");
+ signature.single_input<float>("To Max");
+ signature.single_output<float>("Result");
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ blender::fn::VSpan<float> values = params.readonly_single_input<float>(0, "Value");
+ blender::fn::VSpan<float> from_min = params.readonly_single_input<float>(1, "From Min");
+ blender::fn::VSpan<float> from_max = params.readonly_single_input<float>(2, "From Max");
+ blender::fn::VSpan<float> to_min = params.readonly_single_input<float>(3, "To Min");
+ blender::fn::VSpan<float> to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+
+ if (clamp_) {
+ for (int64_t i : mask) {
+ CLAMP(results[i], 0.0f, 1.0f);
+ }
+ }
+ }
+};
+
+static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ bool clamp = bnode.custom1 != 0;
+ int interpolation_type = bnode.custom2;
+
+ if (interpolation_type == NODE_MAP_RANGE_LINEAR) {
+ static MapRangeFunction fn_with_clamp{true};
+ static MapRangeFunction fn_without_clamp{false};
+
+ if (clamp) {
+ builder.set_matching_fn(fn_with_clamp);
+ }
+ else {
+ builder.set_matching_fn(fn_without_clamp);
+ }
+ }
+ else {
+ builder.set_not_implemented();
+ }
+}
+
void register_node_type_sh_map_range(void)
{
static bNodeType ntype;
@@ -86,6 +159,7 @@ void register_node_type_sh_map_range(void)
node_type_init(&ntype, node_shader_init_map_range);
node_type_update(&ntype, node_shader_update_map_range);
node_type_gpu(&ntype, gpu_shader_map_range);
+ ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.c b/source/blender/nodes/shader/nodes/node_shader_math.c
deleted file mode 100644
index 8abebbf5081..00000000000
--- a/source/blender/nodes/shader/nodes/node_shader_math.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup shdnodes
- */
-
-#include "node_shader_util.h"
-
-/* **************** SCALAR MATH ******************** */
-static bNodeSocketTemplate sh_node_math_in[] = {
- {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {-1, ""}};
-
-static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}};
-
-static int gpu_shader_math(GPUMaterial *mat,
- bNode *node,
- bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
- GPUNodeStack *out)
-{
- static const char *names[] = {
- [NODE_MATH_ADD] = "math_add",
- [NODE_MATH_SUBTRACT] = "math_subtract",
- [NODE_MATH_MULTIPLY] = "math_multiply",
- [NODE_MATH_DIVIDE] = "math_divide",
- [NODE_MATH_MULTIPLY_ADD] = "math_multiply_add",
-
- [NODE_MATH_POWER] = "math_power",
- [NODE_MATH_LOGARITHM] = "math_logarithm",
- [NODE_MATH_EXPONENT] = "math_exponent",
- [NODE_MATH_SQRT] = "math_sqrt",
- [NODE_MATH_INV_SQRT] = "math_inversesqrt",
- [NODE_MATH_ABSOLUTE] = "math_absolute",
- [NODE_MATH_RADIANS] = "math_radians",
- [NODE_MATH_DEGREES] = "math_degrees",
-
- [NODE_MATH_MINIMUM] = "math_minimum",
- [NODE_MATH_MAXIMUM] = "math_maximum",
- [NODE_MATH_LESS_THAN] = "math_less_than",
- [NODE_MATH_GREATER_THAN] = "math_greater_than",
- [NODE_MATH_SIGN] = "math_sign",
- [NODE_MATH_COMPARE] = "math_compare",
- [NODE_MATH_SMOOTH_MIN] = "math_smoothmin",
- [NODE_MATH_SMOOTH_MAX] = "math_smoothmax",
-
- [NODE_MATH_ROUND] = "math_round",
- [NODE_MATH_FLOOR] = "math_floor",
- [NODE_MATH_CEIL] = "math_ceil",
- [NODE_MATH_FRACTION] = "math_fraction",
- [NODE_MATH_MODULO] = "math_modulo",
- [NODE_MATH_TRUNC] = "math_trunc",
- [NODE_MATH_SNAP] = "math_snap",
- [NODE_MATH_WRAP] = "math_wrap",
- [NODE_MATH_PINGPONG] = "math_pingpong",
-
- [NODE_MATH_SINE] = "math_sine",
- [NODE_MATH_COSINE] = "math_cosine",
- [NODE_MATH_TANGENT] = "math_tangent",
- [NODE_MATH_SINH] = "math_sinh",
- [NODE_MATH_COSH] = "math_cosh",
- [NODE_MATH_TANH] = "math_tanh",
- [NODE_MATH_ARCSINE] = "math_arcsine",
- [NODE_MATH_ARCCOSINE] = "math_arccosine",
- [NODE_MATH_ARCTANGENT] = "math_arctangent",
- [NODE_MATH_ARCTAN2] = "math_arctan2",
- };
-
- if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) {
- int ret = GPU_stack_link(mat, node, names[node->custom1], in, out);
-
- if (ret && node->custom2 & SHD_MATH_CLAMP) {
- float min[3] = {0.0f, 0.0f, 0.0f};
- float max[3] = {1.0f, 1.0f, 1.0f};
- GPU_link(
- mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
- }
- return ret;
- }
- else {
- return 0;
- }
-}
-
-void register_node_type_sh_math(void)
-{
- static bNodeType ntype;
-
- sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0);
- node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out);
- node_type_label(&ntype, node_math_label);
- node_type_gpu(&ntype, gpu_shader_math);
- node_type_update(&ntype, node_math_update);
-
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
new file mode 100644
index 00000000000..c7035da8c70
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -0,0 +1,387 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup shdnodes
+ */
+
+#include "node_shader_util.h"
+
+/* **************** SCALAR MATH ******************** */
+static bNodeSocketTemplate sh_node_math_in[] = {
+ {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {-1, ""}};
+
+static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}};
+
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case NODE_MATH_ADD:
+ return "math_add";
+ case NODE_MATH_SUBTRACT:
+ return "math_subtract";
+ case NODE_MATH_MULTIPLY:
+ return "math_multiply";
+ case NODE_MATH_DIVIDE:
+ return "math_divide";
+ case NODE_MATH_MULTIPLY_ADD:
+ return "math_multiply_add";
+
+ case NODE_MATH_POWER:
+ return "math_power";
+ case NODE_MATH_LOGARITHM:
+ return "math_logarithm";
+ case NODE_MATH_EXPONENT:
+ return "math_exponent";
+ case NODE_MATH_SQRT:
+ return "math_sqrt";
+ case NODE_MATH_INV_SQRT:
+ return "math_inversesqrt";
+ case NODE_MATH_ABSOLUTE:
+ return "math_absolute";
+ case NODE_MATH_RADIANS:
+ return "math_radians";
+ case NODE_MATH_DEGREES:
+ return "math_degrees";
+
+ case NODE_MATH_MINIMUM:
+ return "math_minimum";
+ case NODE_MATH_MAXIMUM:
+ return "math_maximum";
+ case NODE_MATH_LESS_THAN:
+ return "math_less_than";
+ case NODE_MATH_GREATER_THAN:
+ return "math_greater_than";
+ case NODE_MATH_SIGN:
+ return "math_sign";
+ case NODE_MATH_COMPARE:
+ return "math_compare";
+ case NODE_MATH_SMOOTH_MIN:
+ return "math_smoothmin";
+ case NODE_MATH_SMOOTH_MAX:
+ return "math_smoothmax";
+
+ case NODE_MATH_ROUND:
+ return "math_round";
+ case NODE_MATH_FLOOR:
+ return "math_floor";
+ case NODE_MATH_CEIL:
+ return "math_ceil";
+ case NODE_MATH_FRACTION:
+ return "math_fraction";
+ case NODE_MATH_MODULO:
+ return "math_modulo";
+ case NODE_MATH_TRUNC:
+ return "math_trunc";
+ case NODE_MATH_SNAP:
+ return "math_snap";
+ case NODE_MATH_WRAP:
+ return "math_wrap";
+ case NODE_MATH_PINGPONG:
+ return "math_pingpong";
+
+ case NODE_MATH_SINE:
+ return "math_sine";
+ case NODE_MATH_COSINE:
+ return "math_cosine";
+ case NODE_MATH_TANGENT:
+ return "math_tangent";
+ case NODE_MATH_SINH:
+ return "math_sinh";
+ case NODE_MATH_COSH:
+ return "math_cosh";
+ case NODE_MATH_TANH:
+ return "math_tanh";
+ case NODE_MATH_ARCSINE:
+ return "math_arcsine";
+ case NODE_MATH_ARCCOSINE:
+ return "math_arccosine";
+ case NODE_MATH_ARCTANGENT:
+ return "math_arctangent";
+ case NODE_MATH_ARCTAN2:
+ return "math_arctan2";
+ }
+ return nullptr;
+}
+
+static int gpu_shader_math(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const char *name = gpu_shader_get_name(node->custom1);
+ if (name != nullptr) {
+ int ret = GPU_stack_link(mat, node, name, in, out);
+
+ if (ret && node->custom2 & SHD_MATH_CLAMP) {
+ float min[3] = {0.0f, 0.0f, 0.0f};
+ float max[3] = {1.0f, 1.0f, 1.0f};
+ GPU_link(
+ mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
+ }
+ return ret;
+ }
+ else {
+ return 0;
+ }
+}
+
+static const blender::fn::MultiFunction &get_base_multi_function(
+ blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const int mode = builder.bnode().custom1;
+ switch (mode) {
+ case NODE_MATH_ADD: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Add", [](float a, float b) { return a + b; }};
+ return fn;
+ }
+ case NODE_MATH_SUBTRACT: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Subtract", [](float a, float b) { return a - b; }};
+ return fn;
+ }
+ case NODE_MATH_MULTIPLY: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Multiply", [](float a, float b) { return a * b; }};
+ return fn;
+ }
+ case NODE_MATH_DIVIDE: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Divide", safe_divide};
+ return fn;
+ }
+ case NODE_MATH_MULTIPLY_ADD: {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Multiply Add", [](float a, float b, float c) { return a * b + c; }};
+ return fn;
+ }
+
+ case NODE_MATH_POWER: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Power", safe_powf};
+ return fn;
+ }
+ case NODE_MATH_LOGARITHM: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{"Logarithm", safe_logf};
+ return fn;
+ }
+ case NODE_MATH_EXPONENT: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Exponent", expf};
+ return fn;
+ }
+ case NODE_MATH_SQRT: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Sqrt", safe_sqrtf};
+ return fn;
+ }
+ case NODE_MATH_INV_SQRT: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Inverse Sqrt", safe_inverse_sqrtf};
+ return fn;
+ };
+ case NODE_MATH_ABSOLUTE: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Absolute",
+ [](float a) { return fabs(a); }};
+ return fn;
+ }
+ case NODE_MATH_RADIANS: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Radians",
+ [](float a) { return DEG2RAD(a); }};
+ return fn;
+ }
+ case NODE_MATH_DEGREES: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Degrees",
+ [](float a) { return RAD2DEG(a); }};
+ return fn;
+ }
+
+ case NODE_MATH_MINIMUM: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Minimum", [](float a, float b) { return std::min(a, b); }};
+ return fn;
+ }
+ case NODE_MATH_MAXIMUM: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Maximum", [](float a, float b) { return std::max(a, b); }};
+ return fn;
+ }
+ case NODE_MATH_LESS_THAN: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Less Than", [](float a, float b) { return (float)(a < b); }};
+ return fn;
+ }
+ case NODE_MATH_GREATER_THAN: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Greater Than", [](float a, float b) { return (float)(a > b); }};
+ return fn;
+ }
+ case NODE_MATH_SIGN: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{
+ "Sign", [](float a) { return compatible_signf(a); }};
+ return fn;
+ }
+ case NODE_MATH_COMPARE: {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Compare", [](float a, float b, float c) -> float {
+ return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
+ }};
+ return fn;
+ }
+ case NODE_MATH_SMOOTH_MIN: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_MATH_SMOOTH_MAX: {
+ return builder.get_not_implemented_fn();
+ }
+
+ case NODE_MATH_ROUND: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{
+ "Round", [](float a) { return floorf(a + 0.5f); }};
+ return fn;
+ }
+ case NODE_MATH_FLOOR: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Floor",
+ [](float a) { return floorf(a); }};
+ return fn;
+ }
+ case NODE_MATH_CEIL: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Ceil",
+ [](float a) { return ceilf(a); }};
+ return fn;
+ }
+ case NODE_MATH_FRACTION: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Fraction",
+ [](float a) { return a - floorf(a); }};
+ return fn;
+ }
+ case NODE_MATH_MODULO: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Modulo", [](float a, float b) { return safe_modf(a, b); }};
+ return fn;
+ }
+ case NODE_MATH_TRUNC: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{
+ "Trunc", [](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); }};
+ return fn;
+ }
+ case NODE_MATH_SNAP: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Snap", [](float a, float b) { return floorf(safe_divide(a, b)) * b; }};
+ return fn;
+ }
+ case NODE_MATH_WRAP: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_MATH_PINGPONG: {
+ return builder.get_not_implemented_fn();
+ }
+
+ case NODE_MATH_SINE: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Sine", [](float a) { return sinf(a); }};
+ return fn;
+ }
+ case NODE_MATH_COSINE: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Cosine",
+ [](float a) { return cosf(a); }};
+ return fn;
+ }
+ case NODE_MATH_TANGENT: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Tangent",
+ [](float a) { return tanf(a); }};
+ return fn;
+ }
+ case NODE_MATH_SINH: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Sine",
+ [](float a) { return sinhf(a); }};
+ return fn;
+ }
+ case NODE_MATH_COSH: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Cosine",
+ [](float a) { return coshf(a); }};
+ return fn;
+ }
+ case NODE_MATH_TANH: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Hyperbolic Tangent",
+ [](float a) { return tanhf(a); }};
+ return fn;
+ }
+ case NODE_MATH_ARCSINE: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Sine", safe_asinf};
+ return fn;
+ }
+ case NODE_MATH_ARCCOSINE: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Cosine", safe_acosf};
+ return fn;
+ }
+ case NODE_MATH_ARCTANGENT: {
+ static blender::fn::CustomMF_SI_SO<float, float> fn{"Arc Tangent",
+ [](float a) { return atanf(a); }};
+ return fn;
+ }
+ case NODE_MATH_ARCTAN2: {
+ static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{
+ "Arc Tangent 2", [](float a, float b) { return atan2f(a, b); }};
+ return fn;
+ }
+
+ default:
+ BLI_assert(false);
+ return builder.get_not_implemented_fn();
+ }
+}
+
+static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const blender::fn::MultiFunction &base_function = get_base_multi_function(builder);
+
+ const blender::nodes::DNode &dnode = builder.dnode();
+ blender::fn::MFNetwork &network = builder.network();
+ blender::fn::MFFunctionNode &base_node = network.add_function(base_function);
+
+ builder.network_map().add_try_match(dnode.inputs(), base_node.inputs());
+
+ const bool clamp_output = builder.bnode().custom2 != 0;
+ if (clamp_output) {
+ static blender::fn::CustomMF_SI_SO<float, float> clamp_fn{"Clamp", [](float value) {
+ CLAMP(value, 0.0f, 1.0f);
+ return value;
+ }};
+ blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn);
+ network.add_link(base_node.output(0), clamp_node.input(0));
+ builder.network_map().add(dnode.output(0), clamp_node.output(0));
+ }
+ else {
+ builder.network_map().add(dnode.output(0), base_node.output(0));
+ }
+}
+
+void register_node_type_sh_math(void)
+{
+ static bNodeType ntype;
+
+ sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0);
+ node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out);
+ node_type_label(&ntype, node_math_label);
+ node_type_gpu(&ntype, gpu_shader_math);
+ node_type_update(&ntype, node_math_update);
+ ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index d0dc45dcedd..c1543f791f1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.c
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -59,6 +59,42 @@ static int gpu_shader_seprgb(GPUMaterial *mat,
return GPU_stack_link(mat, node, "separate_rgb", in, out);
}
+class SeparateRGBFunction : public blender::fn::MultiFunction {
+ public:
+ SeparateRGBFunction()
+ {
+ blender::fn::MFSignatureBuilder signature = this->get_builder("Separate RGB");
+ signature.single_input<blender::Color4f>("Color");
+ signature.single_output<float>("R");
+ signature.single_output<float>("G");
+ signature.single_output<float>("B");
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ blender::fn::VSpan<blender::Color4f> colors = params.readonly_single_input<blender::Color4f>(
+ 0, "Color");
+ blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R");
+ blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G");
+ blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B");
+
+ for (int64_t i : mask) {
+ blender::Color4f color = colors[i];
+ rs[i] = color.r;
+ gs[i] = color.g;
+ bs[i] = color.b;
+ }
+ }
+};
+
+static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static SeparateRGBFunction fn;
+ builder.set_matching_fn(fn);
+}
+
void register_node_type_sh_seprgb(void)
{
static bNodeType ntype;
@@ -67,6 +103,7 @@ void register_node_type_sh_seprgb(void)
node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_seprgb);
node_type_gpu(&ntype, gpu_shader_seprgb);
+ ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network;
nodeRegisterType(&ntype);
}
@@ -109,6 +146,13 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_rgb", in, out);
}
+static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::Color4f> fn{
+ "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }};
+ builder.set_matching_fn(fn);
+}
+
void register_node_type_sh_combrgb(void)
{
static bNodeType ntype;
@@ -117,6 +161,7 @@ void register_node_type_sh_combrgb(void)
node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_combrgb);
node_type_gpu(&ntype, gpu_shader_combrgb);
+ ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
index 429b1a3e818..8b23327460f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
@@ -44,6 +44,42 @@ static int gpu_shader_sepxyz(GPUMaterial *mat,
return GPU_stack_link(mat, node, "separate_xyz", in, out);
}
+class MF_SeparateXYZ : public blender::fn::MultiFunction {
+ public:
+ MF_SeparateXYZ()
+ {
+ blender::fn::MFSignatureBuilder signature = this->get_builder("Separate XYZ");
+ signature.single_input<blender::float3>("XYZ");
+ signature.single_output<float>("X");
+ signature.single_output<float>("Y");
+ signature.single_output<float>("Z");
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ blender::fn::VSpan<blender::float3> vectors = params.readonly_single_input<blender::float3>(
+ 0, "XYZ");
+ blender::MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X");
+ blender::MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y");
+ blender::MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z");
+
+ for (int64_t i : mask) {
+ blender::float3 xyz = vectors[i];
+ xs[i] = xyz.x;
+ ys[i] = xyz.y;
+ zs[i] = xyz.z;
+ }
+ }
+};
+
+static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static MF_SeparateXYZ separate_fn;
+ builder.set_matching_fn(separate_fn);
+}
+
void register_node_type_sh_sepxyz(void)
{
static bNodeType ntype;
@@ -51,6 +87,7 @@ void register_node_type_sh_sepxyz(void)
sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out);
node_type_gpu(&ntype, gpu_shader_sepxyz);
+ ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network;
nodeRegisterType(&ntype);
}
@@ -76,6 +113,13 @@ static int gpu_shader_combxyz(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_xyz", in, out);
}
+static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{
+ "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }};
+ builder.set_matching_fn(fn);
+}
+
void register_node_type_sh_combxyz(void)
{
static bNodeType ntype;
@@ -83,6 +127,7 @@ void register_node_type_sh_combxyz(void)
sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out);
node_type_gpu(&ntype, gpu_shader_combxyz);
+ ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
index 0cf4b51f307..38e79ebe94d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
@@ -19,8 +19,6 @@
#include "../node_shader_util.h"
-#include "GPU_draw.h"
-
/* **************** OUTPUT ******************** */
static bNodeSocketTemplate sh_node_tex_environment_in[] = {
@@ -59,7 +57,8 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
NodeTexImage *tex_original = node_original->storage;
ImageUser *iuser = &tex_original->iuser;
eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER;
- if (GPU_get_mipmap()) {
+ /* TODO(fclem) For now assume mipmap is always enabled. */
+ if (true) {
sampler |= GPU_SAMPLER_MIPMAP;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c
index cbda72cd228..1a78d2f5bf2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c
@@ -19,8 +19,6 @@
#include "../node_shader_util.h"
-#include "GPU_draw.h"
-
/* **************** OUTPUT ******************** */
static bNodeSocketTemplate sh_node_tex_image_in[] = {
@@ -95,7 +93,8 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
if (tex->interpolation != SHD_INTERP_CLOSEST) {
sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER;
- sampler_state |= GPU_get_mipmap() ? GPU_SAMPLER_MIPMAP : 0;
+ /* TODO(fclem) For now assume mipmap is always enabled. */
+ sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0;
}
const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART);
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..94ffbbe0c55 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
@@ -18,6 +18,7 @@
*/
#include "../node_shader_util.h"
+#include "sky_model.h"
/* **************** OUTPUT ******************** */
@@ -41,24 +42,185 @@ 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.545f);
+ tex->sun_intensity = 1.0f;
+ tex->sun_elevation = DEG2RADF(15.0f);
+ tex->sun_rotation = 0.0f;
+ tex->altitude = 0.0f;
+ tex->air_density = 1.0f;
+ tex->dust_density = 1.0f;
+ tex->ozone_density = 1.0f;
+ tex->sky_model = SHD_SKY_NISHITA;
node->storage = tex;
}
+typedef struct SkyModelPreetham {
+ float config_Y[5], config_x[5], config_y[5]; /* named after xyY color space */
+ float radiance[3];
+} SkyModelPreetham;
+
+typedef struct XYZ_to_RGB /* transposed imbuf_xyz_to_rgb, passed as 3x vec3 */
+{
+ float r[3], g[3], b[3];
+} XYZ_to_RGB;
+
+static float sky_perez_function(const float *lam, float theta, float gamma)
+{
+ float ctheta = cosf(theta);
+ float cgamma = cosf(gamma);
+
+ return (1.0 + lam[0] * expf(lam[1] / ctheta)) *
+ (1.0 + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma);
+}
+
+static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[], float turbidity)
+{
+ float theta = sun_angles[0];
+ float theta2 = theta * theta;
+ float theta3 = theta2 * theta;
+ float T = turbidity;
+ float T2 = T * T;
+ float chi = (4.0f / 9.0f - T / 120.0f) * (M_PI - 2.0f * theta);
+
+ sunsky->radiance[0] = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
+ sunsky->radiance[0] *= 0.06f;
+
+ sunsky->radiance[1] = (0.00166f * theta3 - 0.00375f * theta2 + 0.00209f * theta) * T2 +
+ (-0.02903f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) *
+ T +
+ (0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25886f);
+
+ sunsky->radiance[2] = (0.00275f * theta3 - 0.00610f * theta2 + 0.00317f * theta) * T2 +
+ (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00516f) *
+ T +
+ (0.15346f * theta3 - 0.26756f * theta2 + 0.06670f * theta + 0.26688f);
+
+ sunsky->config_Y[0] = (0.1787f * T - 1.4630f);
+ sunsky->config_Y[1] = (-0.3554f * T + 0.4275f);
+ sunsky->config_Y[2] = (-0.0227f * T + 5.3251f);
+ sunsky->config_Y[3] = (0.1206f * T - 2.5771f);
+ sunsky->config_Y[4] = (-0.0670f * T + 0.3703f);
+
+ sunsky->config_x[0] = (-0.0193f * T - 0.2592f);
+ sunsky->config_x[1] = (-0.0665f * T + 0.0008f);
+ sunsky->config_x[2] = (-0.0004f * T + 0.2125f);
+ sunsky->config_x[3] = (-0.0641f * T - 0.8989f);
+ sunsky->config_x[4] = (-0.0033f * T + 0.0452f);
+
+ sunsky->config_y[0] = (-0.0167f * T - 0.2608f);
+ sunsky->config_y[1] = (-0.0950f * T + 0.0092f);
+ sunsky->config_y[2] = (-0.0079f * T + 0.2102f);
+ sunsky->config_y[3] = (-0.0441f * T - 1.6537f);
+ sunsky->config_y[4] = (-0.0109f * T + 0.0529f);
+
+ sunsky->radiance[0] /= sky_perez_function(sunsky->config_Y, 0, theta);
+ sunsky->radiance[1] /= sky_perez_function(sunsky->config_x, 0, theta);
+ sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta);
+}
+
+static void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
+{
+ const float *xyz_to_rgb = IMB_colormangement_get_xyz_to_rgb();
+ data->r[0] = xyz_to_rgb[0];
+ data->r[1] = xyz_to_rgb[3];
+ data->r[2] = xyz_to_rgb[6];
+ data->g[0] = xyz_to_rgb[1];
+ data->g[1] = xyz_to_rgb[4];
+ data->g[2] = xyz_to_rgb[7];
+ data->b[0] = xyz_to_rgb[2];
+ data->b[1] = xyz_to_rgb[5];
+ data->b[2] = xyz_to_rgb[8];
+}
+
static int node_shader_gpu_tex_sky(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
- if (!in[0].link) {
- in[0].link = GPU_attribute(mat, CD_ORCO, "");
+ node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
+ node_shader_gpu_tex_mapping(mat, node, in, out);
+ NodeTexSky *tex = (NodeTexSky *)node->storage;
+ float sun_angles[2]; /* [0]=theta=zenith angle [1]=phi=azimuth */
+ sun_angles[0] = acosf(tex->sun_direction[2]);
+ sun_angles[1] = atan2f(tex->sun_direction[0], tex->sun_direction[1]);
+
+ if (tex->sky_model == 0) {
+ /* Preetham */
+ SkyModelPreetham sunsky;
+ sky_precompute_old(&sunsky, sun_angles, tex->turbidity);
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
+ return GPU_stack_link(mat,
+ node,
+ "node_tex_sky_preetham",
+ in,
+ out,
+ /* Pass config_Y/x/y as 3x(vec4+float) */
+ GPU_uniform(&sunsky.config_Y[0]),
+ GPU_uniform(&sunsky.config_Y[4]),
+ GPU_uniform(&sunsky.config_x[0]),
+ GPU_uniform(&sunsky.config_x[4]),
+ GPU_uniform(&sunsky.config_y[0]),
+ GPU_uniform(&sunsky.config_y[4]),
+ GPU_uniform(sun_angles),
+ GPU_uniform(sunsky.radiance),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b));
+ }
+ else if (tex->sky_model == 1) {
+ /* Hosek / Wilkie */
+ sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */
+ SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init(
+ tex->turbidity, tex->ground_albedo, fmax(0.0, M_PI_2 - sun_angles[0]));
+ /* Pass sky_state->configs[3][9] as 3*(vec4+vec4)+vec3 */
+ float config_x07[8], config_y07[8], config_z07[8], config_xyz8[3];
+ for (int i = 0; i < 8; ++i) {
+ config_x07[i] = (float)sky_state->configs[0][i];
+ config_y07[i] = (float)sky_state->configs[1][i];
+ config_z07[i] = (float)sky_state->configs[2][i];
+ }
+ for (int i = 0; i < 3; ++i) {
+ config_xyz8[i] = (float)sky_state->configs[i][8];
+ }
+ float radiance[3];
+ for (int i = 0; i < 3; i++) {
+ radiance[i] = sky_state->radiances[i] * (2 * M_PI / 683);
+ }
+ SKY_arhosekskymodelstate_free(sky_state);
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
+ return GPU_stack_link(mat,
+ node,
+ "node_tex_sky_hosekwilkie",
+ in,
+ out,
+ GPU_uniform(&config_x07[0]),
+ GPU_uniform(&config_x07[4]),
+ GPU_uniform(&config_y07[0]),
+ GPU_uniform(&config_y07[4]),
+ GPU_uniform(&config_z07[0]),
+ GPU_uniform(&config_z07[4]),
+ GPU_uniform(config_xyz8),
+ GPU_uniform(sun_angles),
+ GPU_uniform(radiance),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b));
+ }
+ else {
+ return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out);
}
+}
- node_shader_gpu_tex_mapping(mat, node, in, out);
+static void node_shader_update_sky(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector");
- return GPU_stack_link(mat, node, "node_tex_sky", in, out);
+ NodeTexSky *tex = (NodeTexSky *)node->storage;
+ nodeSetSocketAvailability(sockVector, !(tex->sky_model == 2 && tex->sun_disc == 1));
}
/* node type definition */
@@ -72,6 +234,8 @@ void register_node_type_sh_tex_sky(void)
node_type_init(&ntype, node_shader_init_tex_sky);
node_type_storage(&ntype, "NodeTexSky", node_free_standard_storage, node_copy_standard_storage);
node_type_gpu(&ntype, node_shader_gpu_tex_sky);
+ /* remove Vector input for Nishita */
+ node_type_update(&ntype, node_shader_update_sky);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.c b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 20f280d00c3..7f712b0db40 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -22,6 +22,11 @@
*/
#include "IMB_colormanagement.h"
+
+#include "DNA_texture_types.h"
+
+#include "BLI_color.hh"
+
#include "node_shader_util.h"
/* **************** VALTORGB ******************** */
@@ -49,7 +54,7 @@ static void node_shader_exec_valtorgb(void *UNUSED(data),
float fac;
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
- BKE_colorband_evaluate(node->storage, fac, out[0]->vec);
+ BKE_colorband_evaluate((ColorBand *)node->storage, fac, out[0]->vec);
out[1]->vec[0] = out[0]->vec[3];
}
}
@@ -65,7 +70,7 @@ static int gpu_shader_valtorgb(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- struct ColorBand *coba = node->storage;
+ struct ColorBand *coba = (ColorBand *)node->storage;
float *array, layer;
int size;
@@ -121,6 +126,44 @@ static int gpu_shader_valtorgb(GPUMaterial *mat,
}
}
+class ColorBandFunction : public blender::fn::MultiFunction {
+ private:
+ const ColorBand &color_band_;
+
+ public:
+ ColorBandFunction(const ColorBand &color_band) : color_band_(color_band)
+ {
+ blender::fn::MFSignatureBuilder signature = this->get_builder("Color Band");
+ signature.single_input<float>("Value");
+ signature.single_output<blender::Color4f>("Color");
+ signature.single_output<float>("Alpha");
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ blender::fn::VSpan<float> values = params.readonly_single_input<float>(0, "Value");
+ blender::MutableSpan<blender::Color4f> colors =
+ params.uninitialized_single_output<blender::Color4f>(1, "Color");
+ blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha");
+
+ for (int64_t i : mask) {
+ blender::Color4f color;
+ BKE_colorband_evaluate(&color_band_, values[i], color);
+ colors[i] = color;
+ alphas[i] = color.a;
+ }
+ }
+};
+
+static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ const ColorBand *color_band = (const ColorBand *)bnode.storage;
+ builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
+}
+
void register_node_type_sh_valtorgb(void)
{
static bNodeType ntype;
@@ -132,6 +175,7 @@ void register_node_type_sh_valtorgb(void)
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
node_type_exec(&ntype, NULL, NULL, node_shader_exec_valtorgb);
node_type_gpu(&ntype, gpu_shader_valtorgb);
+ ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.c b/source/blender/nodes/shader/nodes/node_shader_value.cc
index c32e9e1d581..1d7c3f47233 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.c
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -39,13 +39,21 @@ static int gpu_shader_value(GPUMaterial *mat,
return GPU_stack_link(mat, node, "set_value", in, out, link);
}
+static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const bNodeSocket *bsocket = builder.dnode().output(0).bsocket();
+ const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value;
+ builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value);
+}
+
void register_node_type_sh_value(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, NULL, sh_node_value_out);
node_type_gpu(&ntype, gpu_shader_value);
+ ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.c b/source/blender/nodes/shader/nodes/node_shader_vector_math.c
deleted file mode 100644
index b719fe03d9b..00000000000
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup shdnodes
- */
-
-#include "node_shader_util.h"
-
-/* **************** VECTOR MATH ******************** */
-static bNodeSocketTemplate sh_node_vector_math_in[] = {
- {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {SOCK_FLOAT, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
- {-1, ""}};
-
-static bNodeSocketTemplate sh_node_vector_math_out[] = {
- {SOCK_VECTOR, N_("Vector")}, {SOCK_FLOAT, N_("Value")}, {-1, ""}};
-
-static int gpu_shader_vector_math(GPUMaterial *mat,
- bNode *node,
- bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
- GPUNodeStack *out)
-{
- static const char *names[] = {
- [NODE_VECTOR_MATH_ADD] = "vector_math_add",
- [NODE_VECTOR_MATH_SUBTRACT] = "vector_math_subtract",
- [NODE_VECTOR_MATH_MULTIPLY] = "vector_math_multiply",
- [NODE_VECTOR_MATH_DIVIDE] = "vector_math_divide",
-
- [NODE_VECTOR_MATH_CROSS_PRODUCT] = "vector_math_cross",
- [NODE_VECTOR_MATH_PROJECT] = "vector_math_project",
- [NODE_VECTOR_MATH_REFLECT] = "vector_math_reflect",
- [NODE_VECTOR_MATH_DOT_PRODUCT] = "vector_math_dot",
-
- [NODE_VECTOR_MATH_DISTANCE] = "vector_math_distance",
- [NODE_VECTOR_MATH_LENGTH] = "vector_math_length",
- [NODE_VECTOR_MATH_SCALE] = "vector_math_scale",
- [NODE_VECTOR_MATH_NORMALIZE] = "vector_math_normalize",
-
- [NODE_VECTOR_MATH_SNAP] = "vector_math_snap",
- [NODE_VECTOR_MATH_FLOOR] = "vector_math_floor",
- [NODE_VECTOR_MATH_CEIL] = "vector_math_ceil",
- [NODE_VECTOR_MATH_MODULO] = "vector_math_modulo",
- [NODE_VECTOR_MATH_FRACTION] = "vector_math_fraction",
- [NODE_VECTOR_MATH_ABSOLUTE] = "vector_math_absolute",
- [NODE_VECTOR_MATH_MINIMUM] = "vector_math_minimum",
- [NODE_VECTOR_MATH_MAXIMUM] = "vector_math_maximum",
- [NODE_VECTOR_MATH_WRAP] = "vector_math_wrap",
- [NODE_VECTOR_MATH_SINE] = "vector_math_sine",
- [NODE_VECTOR_MATH_COSINE] = "vector_math_cosine",
- [NODE_VECTOR_MATH_TANGENT] = "vector_math_tangent",
- };
-
- if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) {
- return GPU_stack_link(mat, node, names[node->custom1], in, out);
- }
- else {
- return 0;
- }
-}
-
-static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node)
-{
- bNodeSocket *sockB = BLI_findlink(&node->inputs, 1);
- bNodeSocket *sockC = BLI_findlink(&node->inputs, 2);
- bNodeSocket *sockScale = nodeFindSocket(node, SOCK_IN, "Scale");
-
- bNodeSocket *sockVector = nodeFindSocket(node, SOCK_OUT, "Vector");
- bNodeSocket *sockValue = nodeFindSocket(node, SOCK_OUT, "Value");
-
- nodeSetSocketAvailability(sockB,
- !ELEM(node->custom1,
- NODE_VECTOR_MATH_SINE,
- NODE_VECTOR_MATH_COSINE,
- NODE_VECTOR_MATH_TANGENT,
- NODE_VECTOR_MATH_CEIL,
- NODE_VECTOR_MATH_SCALE,
- NODE_VECTOR_MATH_FLOOR,
- NODE_VECTOR_MATH_LENGTH,
- NODE_VECTOR_MATH_ABSOLUTE,
- NODE_VECTOR_MATH_FRACTION,
- NODE_VECTOR_MATH_NORMALIZE));
- nodeSetSocketAvailability(sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP));
- nodeSetSocketAvailability(sockScale, node->custom1 == NODE_VECTOR_MATH_SCALE);
- nodeSetSocketAvailability(sockVector,
- !ELEM(node->custom1,
- NODE_VECTOR_MATH_LENGTH,
- NODE_VECTOR_MATH_DISTANCE,
- NODE_VECTOR_MATH_DOT_PRODUCT));
- nodeSetSocketAvailability(sockValue,
- ELEM(node->custom1,
- NODE_VECTOR_MATH_LENGTH,
- NODE_VECTOR_MATH_DISTANCE,
- NODE_VECTOR_MATH_DOT_PRODUCT));
-
- /* Labels */
- if (sockB->label[0] != '\0') {
- sockB->label[0] = '\0';
- }
- if (sockC->label[0] != '\0') {
- sockC->label[0] = '\0';
- }
- switch (node->custom1) {
- case NODE_VECTOR_MATH_WRAP:
- node_sock_label(sockB, "Max");
- node_sock_label(sockC, "Min");
- break;
- case NODE_VECTOR_MATH_SNAP:
- node_sock_label(sockB, "Increment");
- break;
- }
-}
-
-void register_node_type_sh_vect_math(void)
-{
- static bNodeType ntype;
-
- sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_MATH, "Vector Math", NODE_CLASS_OP_VECTOR, 0);
- node_type_socket_templates(&ntype, sh_node_vector_math_in, sh_node_vector_math_out);
- node_type_label(&ntype, node_vector_math_label);
- node_type_gpu(&ntype, gpu_shader_vector_math);
- node_type_update(&ntype, node_shader_update_vector_math);
-
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
new file mode 100644
index 00000000000..c18ad8bb244
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -0,0 +1,292 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup shdnodes
+ */
+
+#include "node_shader_util.h"
+
+/* **************** VECTOR MATH ******************** */
+static bNodeSocketTemplate sh_node_vector_math_in[] = {
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
+ {-1, ""}};
+
+static bNodeSocketTemplate sh_node_vector_math_out[] = {
+ {SOCK_VECTOR, N_("Vector")}, {SOCK_FLOAT, N_("Value")}, {-1, ""}};
+
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case NODE_VECTOR_MATH_ADD:
+ return "vector_math_add";
+ case NODE_VECTOR_MATH_SUBTRACT:
+ return "vector_math_subtract";
+ case NODE_VECTOR_MATH_MULTIPLY:
+ return "vector_math_multiply";
+ case NODE_VECTOR_MATH_DIVIDE:
+ return "vector_math_divide";
+
+ case NODE_VECTOR_MATH_CROSS_PRODUCT:
+ return "vector_math_cross";
+ case NODE_VECTOR_MATH_PROJECT:
+ return "vector_math_project";
+ case NODE_VECTOR_MATH_REFLECT:
+ return "vector_math_reflect";
+ case NODE_VECTOR_MATH_DOT_PRODUCT:
+ return "vector_math_dot";
+
+ case NODE_VECTOR_MATH_DISTANCE:
+ return "vector_math_distance";
+ case NODE_VECTOR_MATH_LENGTH:
+ return "vector_math_length";
+ case NODE_VECTOR_MATH_SCALE:
+ return "vector_math_scale";
+ case NODE_VECTOR_MATH_NORMALIZE:
+ return "vector_math_normalize";
+
+ case NODE_VECTOR_MATH_SNAP:
+ return "vector_math_snap";
+ case NODE_VECTOR_MATH_FLOOR:
+ return "vector_math_floor";
+ case NODE_VECTOR_MATH_CEIL:
+ return "vector_math_ceil";
+ case NODE_VECTOR_MATH_MODULO:
+ return "vector_math_modulo";
+ case NODE_VECTOR_MATH_FRACTION:
+ return "vector_math_fraction";
+ case NODE_VECTOR_MATH_ABSOLUTE:
+ return "vector_math_absolute";
+ case NODE_VECTOR_MATH_MINIMUM:
+ return "vector_math_minimum";
+ case NODE_VECTOR_MATH_MAXIMUM:
+ return "vector_math_maximum";
+ case NODE_VECTOR_MATH_WRAP:
+ return "vector_math_wrap";
+ case NODE_VECTOR_MATH_SINE:
+ return "vector_math_sine";
+ case NODE_VECTOR_MATH_COSINE:
+ return "vector_math_cosine";
+ case NODE_VECTOR_MATH_TANGENT:
+ return "vector_math_tangent";
+ }
+
+ return nullptr;
+}
+
+static int gpu_shader_vector_math(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const char *name = gpu_shader_get_name(node->custom1);
+ if (name != nullptr) {
+ return GPU_stack_link(mat, node, name, in, out);
+ }
+ else {
+ return 0;
+ }
+}
+
+static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sockC = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
+ bNodeSocket *sockScale = nodeFindSocket(node, SOCK_IN, "Scale");
+
+ bNodeSocket *sockVector = nodeFindSocket(node, SOCK_OUT, "Vector");
+ bNodeSocket *sockValue = nodeFindSocket(node, SOCK_OUT, "Value");
+
+ nodeSetSocketAvailability(sockB,
+ !ELEM(node->custom1,
+ NODE_VECTOR_MATH_SINE,
+ NODE_VECTOR_MATH_COSINE,
+ NODE_VECTOR_MATH_TANGENT,
+ NODE_VECTOR_MATH_CEIL,
+ NODE_VECTOR_MATH_SCALE,
+ NODE_VECTOR_MATH_FLOOR,
+ NODE_VECTOR_MATH_LENGTH,
+ NODE_VECTOR_MATH_ABSOLUTE,
+ NODE_VECTOR_MATH_FRACTION,
+ NODE_VECTOR_MATH_NORMALIZE));
+ nodeSetSocketAvailability(sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP));
+ nodeSetSocketAvailability(sockScale, node->custom1 == NODE_VECTOR_MATH_SCALE);
+ nodeSetSocketAvailability(sockVector,
+ !ELEM(node->custom1,
+ NODE_VECTOR_MATH_LENGTH,
+ NODE_VECTOR_MATH_DISTANCE,
+ NODE_VECTOR_MATH_DOT_PRODUCT));
+ nodeSetSocketAvailability(sockValue,
+ ELEM(node->custom1,
+ NODE_VECTOR_MATH_LENGTH,
+ NODE_VECTOR_MATH_DISTANCE,
+ NODE_VECTOR_MATH_DOT_PRODUCT));
+
+ /* Labels */
+ if (sockB->label[0] != '\0') {
+ sockB->label[0] = '\0';
+ }
+ if (sockC->label[0] != '\0') {
+ sockC->label[0] = '\0';
+ }
+ switch (node->custom1) {
+ case NODE_VECTOR_MATH_WRAP:
+ node_sock_label(sockB, "Max");
+ node_sock_label(sockC, "Min");
+ break;
+ case NODE_VECTOR_MATH_SNAP:
+ node_sock_label(sockB, "Increment");
+ break;
+ }
+}
+
+static const blender::fn::MultiFunction &get_multi_function(
+ blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ using blender::float3;
+
+ const int mode = builder.bnode().custom1;
+ switch (mode) {
+ case NODE_VECTOR_MATH_ADD: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
+ "Add", [](float3 a, float3 b) { return a + b; }};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_SUBTRACT: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
+ "Subtract", [](float3 a, float3 b) { return a - b; }};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_MULTIPLY: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
+ "Multiply", [](float3 a, float3 b) { return a * b; }};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_DIVIDE: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
+ "Divide", [](float3 a, float3 b) { return float3::safe_divide(a, b); }};
+ return fn;
+ }
+
+ case NODE_VECTOR_MATH_CROSS_PRODUCT: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
+ "Cross Product", float3::cross_high_precision};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_PROJECT: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{"Project", float3::project};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_REFLECT: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
+ "Reflect", [](float3 a, float3 b) { return a.reflected(b); }};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_DOT_PRODUCT: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Dot Product", float3::dot};
+ return fn;
+ }
+
+ case NODE_VECTOR_MATH_DISTANCE: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Distance",
+ float3::distance};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_LENGTH: {
+ static blender::fn::CustomMF_SI_SO<float3, float> fn{"Length",
+ [](float3 a) { return a.length(); }};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_SCALE: {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{
+ "Scale", [](float3 a, float factor) { return a * factor; }};
+ return fn;
+ }
+ case NODE_VECTOR_MATH_NORMALIZE: {
+ static blender::fn::CustomMF_SI_SO<float3, float3> fn{
+ "Normalize", [](float3 a) { return a.normalized(); }};
+ return fn;
+ }
+
+ case NODE_VECTOR_MATH_SNAP: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_FLOOR: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_CEIL: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_MODULO: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_FRACTION: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_ABSOLUTE: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_MINIMUM: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_MAXIMUM: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_WRAP: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_SINE: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_COSINE: {
+ return builder.get_not_implemented_fn();
+ }
+ case NODE_VECTOR_MATH_TANGENT: {
+ return builder.get_not_implemented_fn();
+ }
+
+ default:
+ BLI_assert(false);
+ return builder.get_not_implemented_fn();
+ };
+}
+
+static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ const blender::fn::MultiFunction &fn = get_multi_function(builder);
+ builder.set_matching_fn(fn);
+}
+
+void register_node_type_sh_vect_math(void)
+{
+ static bNodeType ntype;
+
+ sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_MATH, "Vector Math", NODE_CLASS_OP_VECTOR, 0);
+ node_type_socket_templates(&ntype, sh_node_vector_math_in, sh_node_vector_math_out);
+ node_type_label(&ntype, node_vector_math_label);
+ node_type_gpu(&ntype, gpu_shader_vector_math);
+ node_type_update(&ntype, node_shader_update_vector_math);
+ ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.c b/source/blender/nodes/shader/nodes/node_shader_vertex_color.c
index 70c924a7f85..40576b68dd5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.c
+++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.c
@@ -39,6 +39,10 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat,
GPUNodeStack *out)
{
NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage;
+ if (U.experimental.use_sculpt_vertex_colors) {
+ GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_PROP_COLOR, vertexColor->layer_name);
+ return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink);
+ }
GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name);
return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.c b/source/blender/nodes/shader/nodes/node_shader_wireframe.c
index e1da1cd34e4..d1ed13e1ffd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wireframe.c
+++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.c
@@ -36,6 +36,7 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
+ GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
/* node->custom1 is use_pixel_size */
if (node->custom1) {
return GPU_stack_link(
diff --git a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc
index 2de7be2d3eb..859ad81656b 100644
--- a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc
+++ b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc
@@ -20,7 +20,7 @@
static bNodeSocketTemplate sim_node_particle_mesh_emitter_in[] = {
{SOCK_OBJECT, N_("Object")},
- {SOCK_FLOAT, N_("Rate"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
+ {SOCK_FLOAT, N_("Rate"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
diff --git a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc
index 8696dbe340c..8f5c6818cb4 100644
--- a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc
+++ b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc
@@ -18,6 +18,7 @@
#include "node_simulation_util.h"
static bNodeSocketTemplate sim_node_set_particle_attribute_in[] = {
+ {SOCK_CONTROL_FLOW, N_("Execute")},
{SOCK_STRING, N_("Name")},
{SOCK_FLOAT, N_("Float"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{SOCK_INT, N_("Int"), 0, 0, 0, 0, -10000, 10000},
@@ -38,7 +39,7 @@ static void sim_node_set_particle_attribute_update(bNodeTree *UNUSED(ntree), bNo
{
int index = 0;
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- if (index >= 1) {
+ if (index >= 2) {
nodeSetSocketAvailability(sock, sock->type == node->custom1);
}
index++;
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/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index 18d4c7da534..049b919b4a4 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -21,6 +21,7 @@
#ifndef __BPY_EXTERN_H__
#define __BPY_EXTERN_H__
+struct AnimationEvalContext;
struct ChannelDriver; /* DNA_anim_types.h */
struct ID; /* DNA_ID.h */
struct ListBase; /* DNA_listBase.h */
@@ -81,23 +82,23 @@ bool BPY_execute_text(struct bContext *C,
bool BPY_execute_string_as_number(struct bContext *C,
const char *imports[],
const char *expr,
- const bool verbose,
+ const char *report_prefix,
double *r_value);
bool BPY_execute_string_as_intptr(struct bContext *C,
const char *imports[],
const char *expr,
- const bool verbose,
+ const char *report_prefix,
intptr_t *r_value);
bool BPY_execute_string_as_string_and_size(struct bContext *C,
const char *imports[],
const char *expr,
- const bool verbose,
+ const char *report_prefix,
char **r_value,
size_t *r_value_size);
bool BPY_execute_string_as_string(struct bContext *C,
const char *imports[],
const char *expr,
- const bool verbose,
+ const char *report_prefix,
char **r_value);
bool BPY_execute_string_ex(struct bContext *C,
@@ -117,7 +118,7 @@ void BPY_driver_reset(void);
float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
struct ChannelDriver *driver,
struct ChannelDriver *driver_orig,
- const float evaltime);
+ const struct AnimationEvalContext *anim_eval_context);
void BPY_DECREF(void *pyob_ptr); /* Py_DECREF() */
void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr);
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 652b6ecf894..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 subsurf - 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/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt
index 822f05bad90..785c1d66407 100644
--- a/source/blender/python/generic/CMakeLists.txt
+++ b/source/blender/python/generic/CMakeLists.txt
@@ -36,12 +36,14 @@ set(SRC
bpy_threads.c
idprop_py_api.c
imbuf_py_api.c
+ bl_math_py_api.c
py_capi_utils.c
bgl.h
blf_py_api.h
idprop_py_api.h
imbuf_py_api.h
+ bl_math_py_api.h
py_capi_utils.h
# header-only
diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c
index 5471fc25f37..2ad2794c76f 100644
--- a/source/blender/python/generic/bgl.c
+++ b/source/blender/python/generic/bgl.c
@@ -487,7 +487,7 @@ static int gl_buffer_type_from_py_buffer(Py_buffer *pybuffer)
return -1; /* UNKNOWN */
}
-static bool compare_dimensions(int ndim, int *dim1, Py_ssize_t *dim2)
+static bool compare_dimensions(int ndim, const int *dim1, const Py_ssize_t *dim2)
{
for (int i = 0; i < ndim; i++) {
if (dim1[i] != dim2[i]) {
diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c
new file mode 100644
index 00000000000..f17aaa4ca5d
--- /dev/null
+++ b/source/blender/python/generic/bl_math_py_api.c
@@ -0,0 +1,162 @@
+/*
+ * 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 pygen
+ *
+ * This file defines the 'bl_math' module, a module for math utilities.
+ */
+
+#include <Python.h>
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "py_capi_utils.h"
+
+#include "bl_math_py_api.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Module Doc String
+ * \{ */
+
+PyDoc_STRVAR(M_bl_math_doc, "Miscellaneous math utilities module");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Python Functions
+ * \{ */
+
+PyDoc_STRVAR(py_bl_math_clamp_doc,
+ ".. function:: clamp(value, min=0, max=1)\n"
+ "\n"
+ " Clamps the float value between minimum and maximum. To avoid\n"
+ " confusion, any call must use either one or all three arguments.\n"
+ "\n"
+ " :arg value: The value to clamp.\n"
+ " :type value: float\n"
+ " :arg min: The minimum value, defaults to 0.\n"
+ " :type min: float\n"
+ " :arg max: The maximum value, defaults to 1.\n"
+ " :type max: float\n"
+ " :return: The clamped value.\n"
+ " :rtype: float\n");
+static PyObject *py_bl_math_clamp(PyObject *UNUSED(self), PyObject *args)
+{
+ double x, minv = 0.0, maxv = 1.0;
+
+ if (PyTuple_Size(args) <= 1) {
+ if (!PyArg_ParseTuple(args, "d:clamp", &x)) {
+ return NULL;
+ }
+ }
+ else {
+ if (!PyArg_ParseTuple(args, "ddd:clamp", &x, &minv, &maxv)) {
+ return NULL;
+ }
+ }
+
+ CLAMP(x, minv, maxv);
+
+ return PyFloat_FromDouble(x);
+}
+
+PyDoc_STRVAR(py_bl_math_lerp_doc,
+ ".. function:: lerp(from, to, factor)\n"
+ "\n"
+ " Linearly interpolate between two float values based on factor.\n"
+ "\n"
+ " :arg from: The value to return when factor is 0.\n"
+ " :type from: float\n"
+ " :arg to: The value to return when factor is 1.\n"
+ " :type to: float\n"
+ " :arg factor: The interpolation value, normally in [0.0, 1.0].\n"
+ " :type factor: float\n"
+ " :return: The interpolated value.\n"
+ " :rtype: float\n");
+static PyObject *py_bl_math_lerp(PyObject *UNUSED(self), PyObject *args)
+{
+ double a, b, x;
+ if (!PyArg_ParseTuple(args, "ddd:lerp", &a, &b, &x)) {
+ return NULL;
+ }
+
+ return PyFloat_FromDouble(a * (1.0 - x) + b * x);
+}
+
+PyDoc_STRVAR(
+ py_bl_math_smoothstep_doc,
+ ".. function:: smoothstep(from, to, value)\n"
+ "\n"
+ " Performs smooth interpolation between 0 and 1 as value changes between from and to.\n"
+ " Outside the range the function returns the same value as the nearest edge.\n"
+ "\n"
+ " :arg from: The edge value where the result is 0.\n"
+ " :type from: float\n"
+ " :arg to: The edge value where the result is 1.\n"
+ " :type to: float\n"
+ " :arg factor: The interpolation value.\n"
+ " :type factor: float\n"
+ " :return: The interpolated value in [0.0, 1.0].\n"
+ " :rtype: float\n");
+static PyObject *py_bl_math_smoothstep(PyObject *UNUSED(self), PyObject *args)
+{
+ double a, b, x;
+ if (!PyArg_ParseTuple(args, "ddd:smoothstep", &a, &b, &x)) {
+ return NULL;
+ }
+
+ double t = (x - a) / (b - a);
+
+ CLAMP(t, 0.0, 1.0);
+
+ return PyFloat_FromDouble(t * t * (3.0 - 2.0 * t));
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module Definition
+ * \{ */
+
+static PyMethodDef M_bl_math_methods[] = {
+ {"clamp", (PyCFunction)py_bl_math_clamp, METH_VARARGS, py_bl_math_clamp_doc},
+ {"lerp", (PyCFunction)py_bl_math_lerp, METH_VARARGS, py_bl_math_lerp_doc},
+ {"smoothstep", (PyCFunction)py_bl_math_smoothstep, METH_VARARGS, py_bl_math_smoothstep_doc},
+ {NULL, NULL, 0, NULL},
+};
+
+static struct PyModuleDef M_bl_math_module_def = {
+ PyModuleDef_HEAD_INIT,
+ "bl_math", /* m_name */
+ M_bl_math_doc, /* m_doc */
+ 0, /* m_size */
+ M_bl_math_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyMODINIT_FUNC BPyInit_bl_math(void)
+{
+ PyObject *submodule = PyModule_Create(&M_bl_math_module_def);
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/editors/include/ED_logic.h b/source/blender/python/generic/bl_math_py_api.h
index ae2d5038321..9183573abfc 100644
--- a/source/blender/editors/include/ED_logic.h
+++ b/source/blender/python/generic/bl_math_py_api.h
@@ -12,27 +12,16 @@
* 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) 2001-2002 by NaN Holding BV.
- * All rights reserved.
*/
-/** \file
- * \ingroup editors
+/**
+ * \file
+ * \ingroup pygen
*/
-#ifndef __ED_LOGIC_H__
-#define __ED_LOGIC_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* logic_ops.c */
-void ED_operatortypes_logic(void);
+#ifndef __BL_MATH_PY_API_H__
+#define __BL_MATH_PY_API_H__
-#ifdef __cplusplus
-}
-#endif
+PyMODINIT_FUNC BPyInit_bl_math(void);
-#endif /* __ED_LOGIC_H__ */
+#endif /* __BL_MATH_PY_API_H__ */
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 9c84a4bb824..406dbdafe22 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -634,7 +634,7 @@ error_cleanup:
PyObject *PyC_ExceptionBuffer_Simple(void)
{
- PyObject *string_io_buf;
+ PyObject *string_io_buf = NULL;
PyObject *error_type, *error_value, *error_traceback;
@@ -648,7 +648,19 @@ PyObject *PyC_ExceptionBuffer_Simple(void)
return NULL;
}
- string_io_buf = PyObject_Str(error_value);
+ if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) {
+ /* Special exception for syntax errors,
+ * in these cases the full error is verbose and not very useful,
+ * just use the initial text so we know what the error is. */
+ if (PyTuple_CheckExact(error_value) && PyTuple_GET_SIZE(error_value) >= 1) {
+ string_io_buf = PyObject_Str(PyTuple_GET_ITEM(error_value, 0));
+ }
+ }
+
+ if (string_io_buf == NULL) {
+ string_io_buf = PyObject_Str(error_value);
+ }
+
/* Python does this too */
if (UNLIKELY(string_io_buf == NULL)) {
string_io_buf = PyUnicode_FromFormat("<unprintable %s object>", Py_TYPE(error_value)->tp_name);
@@ -804,8 +816,12 @@ void PyC_MainModule_Restore(PyObject *main_mod)
Py_XDECREF(main_mod);
}
-/* Must be called before Py_Initialize,
- * expects output of BKE_appdir_folder_id(BLENDER_PYTHON, NULL). */
+/**
+ * - Must be called before #Py_Initialize.
+ * - Expects output of `BKE_appdir_folder_id(BLENDER_PYTHON, NULL)`.
+ * - Note that the `PYTHONPATH` environment variable isn't reliable, see T31506.
+ Use #Py_SetPythonHome instead.
+ */
void PyC_SetHomePath(const char *py_path_bundle)
{
if (py_path_bundle == NULL) {
@@ -824,24 +840,14 @@ void PyC_SetHomePath(const char *py_path_bundle)
/* OSX allow file/directory names to contain : character (represented as / in the Finder)
* but current Python lib (release 3.1.1) doesn't handle these correctly */
if (strchr(py_path_bundle, ':')) {
- printf(
- "Warning : Blender application is located in a path containing : or / chars\
- \nThis may make python import function fail\n");
- }
-# endif
-
-# if 0 /* disable for now [#31506] - campbell */
-# ifdef _WIN32
- /* cmake/MSVC debug build crashes without this, why only
- * in this case is unknown.. */
- {
- /*BLI_setenv("PYTHONPATH", py_path_bundle)*/;
+ fprintf(stderr,
+ "Warning! Blender application is located in a path containing ':' or '/' chars\n"
+ "This may make python import function fail\n");
}
-# endif
# endif
{
- static wchar_t py_path_bundle_wchar[1024];
+ wchar_t py_path_bundle_wchar[1024];
/* Can't use this, on linux gives bug: #23018,
* TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 2008 */
diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c
index b3df991cf12..f9fe1654db0 100644
--- a/source/blender/python/gpu/gpu_py_batch.c
+++ b/source/blender/python/gpu/gpu_py_batch.c
@@ -184,8 +184,7 @@ static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_sh
}
GPUShader *shader = py_shader->shader;
- GPU_batch_program_set(
- self->batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader));
+ GPU_batch_set_shader(self->batch, shader);
#ifdef USE_GPU_PY_REFERENCES
/* Remove existing user (if any), hold new user. */
@@ -229,9 +228,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args)
}
}
else if (self->batch->program != GPU_shader_get_program(py_program->shader)) {
- GPU_batch_program_set(self->batch,
- GPU_shader_get_program(py_program->shader),
- GPU_shader_get_interface(py_program->shader));
+ GPU_batch_set_shader(self->batch, py_program->shader);
}
GPU_batch_draw(self->batch);
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 311cf2b8c73..e56f87e6221 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -86,17 +86,17 @@ static PyObject *bpygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args
BPYGPU_IS_INIT_OR_ERROR_OBJ;
GPUOffScreen *ofs = NULL;
- int width, height, samples = 0;
+ int width, height;
char err_out[256];
- static const char *_keywords[] = {"width", "height", "samples", NULL};
+ static const char *_keywords[] = {"width", "height", NULL};
static _PyArg_Parser _parser = {"ii|i:GPUOffScreen.__new__", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &width, &height, &samples)) {
+ if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &width, &height)) {
return NULL;
}
if (GPU_context_active_get()) {
- ofs = GPU_offscreen_create(width, height, samples, true, false, err_out);
+ ofs = GPU_offscreen_create(width, height, true, false, err_out);
}
else {
strncpy(err_out, "No active GPU context found", 256);
@@ -345,16 +345,14 @@ static struct PyMethodDef bpygpu_offscreen_methods[] = {
};
PyDoc_STRVAR(bpygpu_offscreen_doc,
- ".. class:: GPUOffScreen(width, height, samples=0)\n"
+ ".. class:: GPUOffScreen(width, height)\n"
"\n"
" This object gives access to off screen buffers.\n"
"\n"
" :arg width: Horizontal dimension of the buffer.\n"
" :type width: `int`\n"
" :arg height: Vertical dimension of the buffer.\n"
- " :type height: `int`\n"
- " :arg samples: OpenGL samples to use for MSAA or zero to disable.\n"
- " :type samples: `int`\n");
+ " :type height: `int`\n");
PyTypeObject BPyGPUOffScreen_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUOffScreen",
.tp_basicsize = sizeof(BPyGPUOffScreen),
diff --git a/source/blender/python/intern/bpy.h b/source/blender/python/intern/bpy.h
index 88d1db6f8bc..8f91c4e1208 100644
--- a/source/blender/python/intern/bpy.h
+++ b/source/blender/python/intern/bpy.h
@@ -21,6 +21,10 @@
#ifndef __BPY_H__
#define __BPY_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void BPy_init_modules(void);
extern PyObject *bpy_package_py;
@@ -31,4 +35,8 @@ void BPY_atexit_unregister(void);
extern struct CLG_LogRef *BPY_LOG_CONTEXT;
extern struct CLG_LogRef *BPY_LOG_RNA;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_H__ */
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 957d49eb04e..4ee936aff91 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -50,7 +50,6 @@
#include "BKE_appdir.h"
#include "BKE_blender_version.h"
#include "BKE_global.h"
-#include "BKE_lib_override.h"
#include "DNA_ID.h"
@@ -392,29 +391,6 @@ static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void
return PyC_UnicodeFromByte(G.autoexec_fail);
}
-PyDoc_STRVAR(bpy_app_use_override_library_doc,
- "Boolean, whether library override is exposed in UI or not.");
-static PyObject *bpy_app_use_override_library_get(PyObject *UNUSED(self), void *UNUSED(closure))
-{
- return PyBool_FromLong((long)BKE_lib_override_library_is_enabled());
-}
-
-static int bpy_app_use_override_library_set(PyObject *UNUSED(self),
- PyObject *value,
- void *UNUSED(closure))
-{
- const int param = PyC_Long_AsBool(value);
-
- if (param == -1 && PyErr_Occurred()) {
- PyErr_SetString(PyExc_TypeError, "bpy.app.use_override_library must be a boolean");
- return -1;
- }
-
- BKE_lib_override_library_enable((const bool)param);
-
- return 0;
-}
-
static PyGetSetDef bpy_app_getsets[] = {
{"debug", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG},
{"debug_ffmpeg",
@@ -485,11 +461,6 @@ static PyGetSetDef bpy_app_getsets[] = {
(void *)G_DEBUG_GPU_MEM},
{"debug_io", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG_IO},
- {"use_override_library",
- bpy_app_use_override_library_get,
- bpy_app_use_override_library_set,
- bpy_app_use_override_library_doc,
- NULL},
{"use_event_simulate",
bpy_app_global_flag_get,
bpy_app_global_flag_set__only_disable,
diff --git a/source/blender/python/intern/bpy_app.h b/source/blender/python/intern/bpy_app.h
index b46de025599..880686f3536 100644
--- a/source/blender/python/intern/bpy_app.h
+++ b/source/blender/python/intern/bpy_app.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_H__
#define __BPY_APP_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_H__ */
diff --git a/source/blender/python/intern/bpy_app_alembic.h b/source/blender/python/intern/bpy_app_alembic.h
index 4cc890ec64d..fcd93fb2b13 100644
--- a/source/blender/python/intern/bpy_app_alembic.h
+++ b/source/blender/python/intern/bpy_app_alembic.h
@@ -24,6 +24,14 @@
#ifndef __BPY_APP_ALEMBIC_H__
#define __BPY_APP_ALEMBIC_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_alembic_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_ALEMBIC_H__ */
diff --git a/source/blender/python/intern/bpy_app_build_options.h b/source/blender/python/intern/bpy_app_build_options.h
index 52c2e57c12a..a10f42d53be 100644
--- a/source/blender/python/intern/bpy_app_build_options.h
+++ b/source/blender/python/intern/bpy_app_build_options.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_BUILD_OPTIONS_H__
#define __BPY_APP_BUILD_OPTIONS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_build_options_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_BUILD_OPTIONS_H__ */
diff --git a/source/blender/python/intern/bpy_app_ffmpeg.h b/source/blender/python/intern/bpy_app_ffmpeg.h
index 63fff2b1b4b..4809288662b 100644
--- a/source/blender/python/intern/bpy_app_ffmpeg.h
+++ b/source/blender/python/intern/bpy_app_ffmpeg.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_FFMPEG_H__
#define __BPY_APP_FFMPEG_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_ffmpeg_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_FFMPEG_H__ */
diff --git a/source/blender/python/intern/bpy_app_handlers.h b/source/blender/python/intern/bpy_app_handlers.h
index 426fe376b55..9c9dd75dd18 100644
--- a/source/blender/python/intern/bpy_app_handlers.h
+++ b/source/blender/python/intern/bpy_app_handlers.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_HANDLERS_H__
#define __BPY_APP_HANDLERS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_handlers_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_HANDLERS_H__ */
diff --git a/source/blender/python/intern/bpy_app_icons.h b/source/blender/python/intern/bpy_app_icons.h
index 2d7cb96aae7..2904dc06136 100644
--- a/source/blender/python/intern/bpy_app_icons.h
+++ b/source/blender/python/intern/bpy_app_icons.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_ICONS_H__
#define __BPY_APP_ICONS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_icons_module(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_ICONS_H__ */
diff --git a/source/blender/python/intern/bpy_app_ocio.h b/source/blender/python/intern/bpy_app_ocio.h
index 66fc03657c1..145cea1a9be 100644
--- a/source/blender/python/intern/bpy_app_ocio.h
+++ b/source/blender/python/intern/bpy_app_ocio.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_OCIO_H__
#define __BPY_APP_OCIO_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_ocio_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_OCIO_H__ */
diff --git a/source/blender/python/intern/bpy_app_oiio.h b/source/blender/python/intern/bpy_app_oiio.h
index 4b08652dff5..e106a9a055a 100644
--- a/source/blender/python/intern/bpy_app_oiio.h
+++ b/source/blender/python/intern/bpy_app_oiio.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_OIIO_H__
#define __BPY_APP_OIIO_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_oiio_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_OIIO_H__ */
diff --git a/source/blender/python/intern/bpy_app_opensubdiv.h b/source/blender/python/intern/bpy_app_opensubdiv.h
index f04fa6fb44d..f659e58c071 100644
--- a/source/blender/python/intern/bpy_app_opensubdiv.h
+++ b/source/blender/python/intern/bpy_app_opensubdiv.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_OPENSUBDIV_H__
#define __BPY_APP_OPENSUBDIV_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_opensubdiv_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_OPENSUBDIV_H__ */
diff --git a/source/blender/python/intern/bpy_app_openvdb.h b/source/blender/python/intern/bpy_app_openvdb.h
index 333d79142f7..60c2bbaa922 100644
--- a/source/blender/python/intern/bpy_app_openvdb.h
+++ b/source/blender/python/intern/bpy_app_openvdb.h
@@ -24,6 +24,14 @@
#ifndef __BPY_APP_OPENVDB_H__
#define __BPY_APP_OPENVDB_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_openvdb_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_OPENVDB_H__ */
diff --git a/source/blender/python/intern/bpy_app_sdl.h b/source/blender/python/intern/bpy_app_sdl.h
index 453c1c0c1a3..dd21ece89c7 100644
--- a/source/blender/python/intern/bpy_app_sdl.h
+++ b/source/blender/python/intern/bpy_app_sdl.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_SDL_H__
#define __BPY_APP_SDL_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_sdl_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_SDL_H__ */
diff --git a/source/blender/python/intern/bpy_app_timers.h b/source/blender/python/intern/bpy_app_timers.h
index bc70e85ae12..6da92eb8b31 100644
--- a/source/blender/python/intern/bpy_app_timers.h
+++ b/source/blender/python/intern/bpy_app_timers.h
@@ -21,6 +21,14 @@
#ifndef __BPY_APP_TIMERS_H__
#define __BPY_APP_TIMERS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_timers_module(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_TIMERS_H__ */
diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c
index 190d4eb1855..c152c920453 100644
--- a/source/blender/python/intern/bpy_app_translations.c
+++ b/source/blender/python/intern/bpy_app_translations.c
@@ -566,7 +566,7 @@ static PyObject *_py_pgettext(PyObject *args,
PyDoc_STRVAR(
app_translations_pgettext_doc,
- ".. method:: pgettext(msgid, msgctxt)\n"
+ ".. method:: pgettext(msgid, msgctxt=None)\n"
"\n"
" Try to translate the given msgid (with optional msgctxt).\n"
"\n"
@@ -600,7 +600,7 @@ static PyObject *app_translations_pgettext(BlenderAppTranslations *UNUSED(self),
}
PyDoc_STRVAR(app_translations_pgettext_iface_doc,
- ".. method:: pgettext_iface(msgid, msgctxt)\n"
+ ".. method:: pgettext_iface(msgid, msgctxt=None)\n"
"\n"
" Try to translate the given msgid (with optional msgctxt), if labels' translation "
"is enabled.\n"
@@ -622,7 +622,7 @@ static PyObject *app_translations_pgettext_iface(BlenderAppTranslations *UNUSED(
}
PyDoc_STRVAR(app_translations_pgettext_tip_doc,
- ".. method:: pgettext_tip(msgid, msgctxt)\n"
+ ".. method:: pgettext_tip(msgid, msgctxt=None)\n"
"\n"
" Try to translate the given msgid (with optional msgctxt), if tooltips' "
"translation is enabled.\n"
@@ -644,7 +644,7 @@ static PyObject *app_translations_pgettext_tip(BlenderAppTranslations *UNUSED(se
}
PyDoc_STRVAR(app_translations_pgettext_data_doc,
- ".. method:: pgettext_data(msgid, msgctxt)\n"
+ ".. method:: pgettext_data(msgid, msgctxt=None)\n"
"\n"
" Try to translate the given msgid (with optional msgctxt), if new data name's "
"translation is enabled.\n"
diff --git a/source/blender/python/intern/bpy_app_translations.h b/source/blender/python/intern/bpy_app_translations.h
index e1e82480b49..1e98c7d9aaa 100644
--- a/source/blender/python/intern/bpy_app_translations.h
+++ b/source/blender/python/intern/bpy_app_translations.h
@@ -21,7 +21,15 @@
#ifndef __BPY_APP_TRANSLATIONS_H__
#define __BPY_APP_TRANSLATIONS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_translations_struct(void);
void BPY_app_translations_end(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_TRANSLATIONS_H__ */
diff --git a/source/blender/python/intern/bpy_app_usd.h b/source/blender/python/intern/bpy_app_usd.h
index e3e1d72b366..0482b10974d 100644
--- a/source/blender/python/intern/bpy_app_usd.h
+++ b/source/blender/python/intern/bpy_app_usd.h
@@ -24,6 +24,14 @@
#ifndef __BPY_APP_USD_H__
#define __BPY_APP_USD_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_app_usd_struct(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_APP_USD_H__ */
diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c
index 89ef2f40a30..27eea80f1f6 100644
--- a/source/blender/python/intern/bpy_capi_utils.c
+++ b/source/blender/python/intern/bpy_capi_utils.c
@@ -97,7 +97,10 @@ void BPy_reports_write_stdout(const ReportList *reports, const char *header)
}
}
-bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const bool use_location)
+bool BPy_errors_to_report_ex(ReportList *reports,
+ const char *error_prefix,
+ const bool use_full,
+ const bool use_location)
{
PyObject *pystring;
@@ -124,40 +127,38 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo
return 0;
}
+ if (error_prefix == NULL) {
+ /* Not very helpful, better than nothing. */
+ error_prefix = "Python";
+ }
+
if (use_location) {
const char *filename;
int lineno;
- PyObject *pystring_format; /* workaround, see below */
- const char *cstring;
-
PyC_FileAndNum(&filename, &lineno);
if (filename == NULL) {
filename = "<unknown location>";
}
-#if 0 /* ARG!. workaround for a bug in blenders use of vsnprintf */
BKE_reportf(reports,
RPT_ERROR,
- "%s\nlocation: %s:%d\n",
+ TIP_("%s: %s\nlocation: %s:%d\n"),
+ error_prefix,
_PyUnicode_AsString(pystring),
filename,
lineno);
-#else
- pystring_format = PyUnicode_FromFormat(
- TIP_("%s\nlocation: %s:%d\n"), _PyUnicode_AsString(pystring), filename, lineno);
-
- cstring = _PyUnicode_AsString(pystring_format);
- BKE_report(reports, RPT_ERROR, cstring);
-
- /* not exactly needed. just for testing */
- fprintf(stderr, TIP_("%s\nlocation: %s:%d\n"), cstring, filename, lineno);
- Py_DECREF(pystring_format); /* workaround */
-#endif
+ /* Not exactly needed. Useful for developers tracking down issues. */
+ fprintf(stderr,
+ TIP_("%s: %s\nlocation: %s:%d\n"),
+ error_prefix,
+ _PyUnicode_AsString(pystring),
+ filename,
+ lineno);
}
else {
- BKE_report(reports, RPT_ERROR, _PyUnicode_AsString(pystring));
+ BKE_reportf(reports, RPT_ERROR, "%s: %s", error_prefix, _PyUnicode_AsString(pystring));
}
Py_DECREF(pystring);
@@ -166,5 +167,5 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo
bool BPy_errors_to_report(ReportList *reports)
{
- return BPy_errors_to_report_ex(reports, true, true);
+ return BPy_errors_to_report_ex(reports, NULL, true, true);
}
diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h
index fe086b61097..11409397028 100644
--- a/source/blender/python/intern/bpy_capi_utils.h
+++ b/source/blender/python/intern/bpy_capi_utils.h
@@ -25,6 +25,10 @@
# error "Python 3.7 or greater is required, you'll need to update your Python."
#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct EnumPropertyItem;
struct ReportList;
@@ -39,8 +43,10 @@ char *BPy_enum_as_string(const struct EnumPropertyItem *item);
short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear);
void BPy_reports_write_stdout(const struct ReportList *reports, const char *header);
bool BPy_errors_to_report_ex(struct ReportList *reports,
+ const char *error_prefix,
const bool use_full,
const bool use_location);
+bool BPy_errors_to_report_brief_with_prefix(struct ReportList *reports, const char *error_prefix);
bool BPy_errors_to_report(struct ReportList *reports);
/* TODO - find a better solution! */
@@ -48,6 +54,10 @@ struct bContext *BPy_GetContext(void);
void BPy_SetContext(struct bContext *C);
extern void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate);
-extern void bpy_context_clear(struct bContext *C, PyGILState_STATE *gilstate);
+extern void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* __BPY_CAPI_UTILS_H__ */
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
index 50ae05694eb..d48ad2b197c 100644
--- a/source/blender/python/intern/bpy_driver.c
+++ b/source/blender/python/intern/bpy_driver.c
@@ -33,19 +33,22 @@
#include "BLI_math_base.h"
#include "BLI_string.h"
+#include "BKE_animsys.h"
#include "BKE_fcurve_driver.h"
#include "BKE_global.h"
+#include "RNA_access.h"
+#include "RNA_types.h"
+
#include "bpy_rna_driver.h" /* for pyrna_driver_get_variable_value */
#include "bpy_intern_string.h"
#include "bpy_driver.h"
+#include "bpy_rna.h"
#include "BPY_extern.h"
-extern void BPY_update_rna_module(void);
-
#define USE_RNA_AS_PYOBJECT
#define USE_BYTECODE_WHITELIST
@@ -111,6 +114,19 @@ int bpy_pydriver_create_dict(void)
Py_DECREF(mod);
}
+ /* Add math utility functions. */
+ mod = PyImport_ImportModuleLevel("bl_math", NULL, NULL, NULL, 0);
+ if (mod) {
+ static const char *names[] = {"clamp", "lerp", "smoothstep", NULL};
+
+ for (const char **pname = names; *pname; ++pname) {
+ PyObject *func = PyDict_GetItemString(PyModule_GetDict(mod), *pname);
+ PyDict_SetItemString(bpy_pydriver_Dict, *pname, func);
+ }
+
+ Py_DECREF(mod);
+ }
+
#ifdef USE_BYTECODE_WHITELIST
/* setup the whitelist */
{
@@ -130,6 +146,10 @@ int bpy_pydriver_create_dict(void)
"bool",
"float",
"int",
+ /* bl_math */
+ "clamp",
+ "lerp",
+ "smoothstep",
NULL,
};
@@ -237,8 +257,6 @@ void BPY_driver_reset(void)
if (use_gil) {
PyGILState_Release(gilstate);
}
-
- return;
}
/* error return function for BPY_eval_pydriver */
@@ -379,17 +397,51 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d
#endif /* USE_BYTECODE_WHITELIST */
-/* This evals py driver expressions, 'expr' is a Python expression that
- * should evaluate to a float number, which is returned.
+static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph)
+{
+ /* This should never happen, but it's probably better to have None in Python
+ * than a NULL-wrapping Depsgraph py struct. */
+ BLI_assert(depsgraph != NULL);
+ if (depsgraph == NULL) {
+ Py_RETURN_NONE;
+ }
+
+ struct PointerRNA depsgraph_ptr;
+ RNA_pointer_create(NULL, &RNA_Depsgraph, depsgraph, &depsgraph_ptr);
+ return pyrna_struct_CreatePyObject(&depsgraph_ptr);
+}
+
+/**
+ * Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated
+ * data-blocks, and the current view layer and scene. See T75553.
+ */
+static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars,
+ struct Depsgraph *depsgraph)
+{
+ PyObject *py_depsgraph = bpy_pydriver_depsgraph_as_pyobject(depsgraph);
+ const char *depsgraph_variable_name = "depsgraph";
+
+ if (PyDict_SetItemString(driver_vars, depsgraph_variable_name, py_depsgraph) == -1) {
+ fprintf(stderr,
+ "\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n",
+ depsgraph_variable_name);
+ PyErr_Print();
+ PyErr_Clear();
+ }
+}
+
+/**
+ * This evaluates Python driver expressions, `driver_orig->expression`
+ * is a Python expression that should evaluate to a float number, which is returned.
*
* (old)note: PyGILState_Ensure() isn't always called because python can call
* the bake operator which intern starts a thread which calls scene update
- * which does a driver update. to avoid a deadlock check PyC_IsInterpreterActive()
- * if PyGILState_Ensure() is needed - see [#27683]
+ * which does a driver update. to avoid a deadlock check #PyC_IsInterpreterActive()
+ * if #PyGILState_Ensure() is needed, see T27683.
*
- * (new)note: checking if python is running is not threadsafe [#28114]
+ * (new)note: checking if python is running is not thread-safe T28114
* now release the GIL on python operator execution instead, using
- * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender.
+ * #PyEval_SaveThread() / #PyEval_RestoreThread() so we don't lock up blender.
*
* For copy-on-write we always cache expressions and write errors in the
* original driver, otherwise these would get freed while editing. Due to
@@ -398,7 +450,7 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d
float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
ChannelDriver *driver,
ChannelDriver *driver_orig,
- const float evaltime)
+ const AnimationEvalContext *anim_eval_context)
{
PyObject *driver_vars = NULL;
PyObject *retval = NULL;
@@ -458,7 +510,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
}
/* update global namespace */
- bpy_pydriver_namespace_update_frame(evaltime);
+ bpy_pydriver_namespace_update_frame(anim_eval_context->eval_time);
if (driver_orig->flag & DRIVER_FLAG_USE_SELF) {
bpy_pydriver_namespace_update_self(anim_rna);
@@ -591,6 +643,8 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
}
#endif /* USE_BYTECODE_WHITELIST */
+ bpy_pydriver_namespace_add_depsgraph(driver_vars, anim_eval_context->depsgraph);
+
#if 0 /* slow, with this can avoid all Py_CompileString above. */
/* execute expression to get a value */
retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars);
diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h
index c77815c7e0e..84f3cbdbbb3 100644
--- a/source/blender/python/intern/bpy_driver.h
+++ b/source/blender/python/intern/bpy_driver.h
@@ -21,7 +21,15 @@
#ifndef __BPY_DRIVER_H__
#define __BPY_DRIVER_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int bpy_pydriver_create_dict(void);
extern PyObject *bpy_pydriver_Dict;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_DRIVER_H__ */
diff --git a/source/blender/python/intern/bpy_gizmo_wrap.h b/source/blender/python/intern/bpy_gizmo_wrap.h
index d9031282c40..3a46dd5d78b 100644
--- a/source/blender/python/intern/bpy_gizmo_wrap.h
+++ b/source/blender/python/intern/bpy_gizmo_wrap.h
@@ -24,8 +24,16 @@
struct wmGizmoGroupType;
struct wmGizmoType;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* exposed to rna/wm api */
void BPY_RNA_gizmo_wrapper(struct wmGizmoType *gzt, void *userdata);
void BPY_RNA_gizmogroup_wrapper(struct wmGizmoGroupType *gzgt, void *userdata);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_GIZMO_WRAP_H__ */
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 6da1715b02d..f91afa414ce 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -68,6 +68,7 @@
/* inittab initialization functions */
#include "../bmesh/bmesh_py_api.h"
#include "../generic/bgl.h"
+#include "../generic/bl_math_py_api.h"
#include "../generic/blf_py_api.h"
#include "../generic/idprop_py_api.h"
#include "../generic/imbuf_py_api.h"
@@ -136,7 +137,7 @@ void bpy_context_set(bContext *C, PyGILState_STATE *gilstate)
}
/* context should be used but not now because it causes some bugs */
-void bpy_context_clear(bContext *UNUSED(C), PyGILState_STATE *gilstate)
+void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
{
py_call_level--;
@@ -228,6 +229,7 @@ static struct _inittab bpy_internal_modules[] = {
{"_bpy_path", BPyInit__bpy_path},
{"bgl", BPyInit_bgl},
{"blf", BPyInit_blf},
+ {"bl_math", BPyInit_bl_math},
{"imbuf", BPyInit_imbuf},
{"bmesh", BPyInit_bmesh},
#if 0
@@ -256,11 +258,13 @@ void BPY_python_start(int argc, const char **argv)
PyThreadState *py_tstate = NULL;
const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL);
- /* not essential but nice to set our name */
- static wchar_t program_path_wchar[FILE_MAX]; /* python holds a reference */
- BLI_strncpy_wchar_from_utf8(
- program_path_wchar, BKE_appdir_program_path(), ARRAY_SIZE(program_path_wchar));
- Py_SetProgramName(program_path_wchar);
+ /* Not essential but nice to set our name. */
+ {
+ const char *program_path = BKE_appdir_program_path();
+ wchar_t program_path_wchar[FILE_MAX];
+ BLI_strncpy_wchar_from_utf8(program_path_wchar, program_path, ARRAY_SIZE(program_path_wchar));
+ Py_SetProgramName(program_path_wchar);
+ }
/* must run before python initializes */
PyImport_ExtendInittab(bpy_internal_modules);
@@ -268,11 +272,11 @@ void BPY_python_start(int argc, const char **argv)
/* allow to use our own included python */
PyC_SetHomePath(py_path_bundle);
- /* without this the sys.stdout may be set to 'ascii'
+ /* Without this the `sys.stdout` may be set to 'ascii'
* (it is on my system at least), where printing unicode values will raise
- * an error, this is highly annoying, another stumbling block for devs,
+ * an error, this is highly annoying, another stumbling block for developers,
* so use a more relaxed error handler and enforce utf-8 since the rest of
- * blender is utf-8 too - campbell */
+ * Blender is utf-8 too - campbell */
Py_SetStandardStreamEncoding("utf-8", "surrogateescape");
/* Suppress error messages when calculating the module search path.
@@ -613,8 +617,11 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr)
/**
* \return success
*/
-bool BPY_execute_string_as_number(
- bContext *C, const char *imports[], const char *expr, const bool verbose, double *r_value)
+bool BPY_execute_string_as_number(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ double *r_value)
{
PyGILState_STATE gilstate;
bool ok = true;
@@ -633,8 +640,8 @@ bool BPY_execute_string_as_number(
ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
if (ok == false) {
- if (verbose) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), false, false);
+ if (report_prefix != NULL) {
+ BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
}
else {
PyErr_Clear();
@@ -652,7 +659,7 @@ bool BPY_execute_string_as_number(
bool BPY_execute_string_as_string_and_size(bContext *C,
const char *imports[],
const char *expr,
- const bool verbose,
+ const char *report_prefix,
char **r_value,
size_t *r_value_size)
{
@@ -670,8 +677,8 @@ bool BPY_execute_string_as_string_and_size(bContext *C,
ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size);
if (ok == false) {
- if (verbose) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), false, false);
+ if (report_prefix != NULL) {
+ BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix);
}
else {
PyErr_Clear();
@@ -683,12 +690,15 @@ bool BPY_execute_string_as_string_and_size(bContext *C,
return ok;
}
-bool BPY_execute_string_as_string(
- bContext *C, const char *imports[], const char *expr, const bool verbose, char **r_value)
+bool BPY_execute_string_as_string(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ char **r_value)
{
size_t value_dummy_size;
return BPY_execute_string_as_string_and_size(
- C, imports, expr, verbose, r_value, &value_dummy_size);
+ C, imports, expr, report_prefix, r_value, &value_dummy_size);
}
/**
@@ -696,8 +706,11 @@ bool BPY_execute_string_as_string(
*
* \return success
*/
-bool BPY_execute_string_as_intptr(
- bContext *C, const char *imports[], const char *expr, const bool verbose, intptr_t *r_value)
+bool BPY_execute_string_as_intptr(bContext *C,
+ const char *imports[],
+ const char *expr,
+ const char *report_prefix,
+ intptr_t *r_value)
{
BLI_assert(r_value && expr);
PyGILState_STATE gilstate;
@@ -713,8 +726,8 @@ bool BPY_execute_string_as_intptr(
ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
if (ok == false) {
- if (verbose) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), false, false);
+ if (report_prefix != NULL) {
+ BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
}
else {
PyErr_Clear();
diff --git a/source/blender/python/intern/bpy_intern_string.h b/source/blender/python/intern/bpy_intern_string.h
index 14f9607f3b4..f87a43e6bf6 100644
--- a/source/blender/python/intern/bpy_intern_string.h
+++ b/source/blender/python/intern/bpy_intern_string.h
@@ -21,6 +21,10 @@
* \ingroup pythonintern
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void bpy_intern_string_init(void);
void bpy_intern_string_exit(void);
@@ -41,4 +45,8 @@ extern PyObject *bpy_intern_str_register;
extern PyObject *bpy_intern_str_self;
extern PyObject *bpy_intern_str_unregister;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_INTERN_STRING_H__ */
diff --git a/source/blender/python/intern/bpy_library.h b/source/blender/python/intern/bpy_library.h
index 6840807d2b0..aca4ae94d6e 100644
--- a/source/blender/python/intern/bpy_library.h
+++ b/source/blender/python/intern/bpy_library.h
@@ -21,9 +21,17 @@
#ifndef __BPY_LIBRARY_H__
#define __BPY_LIBRARY_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int BPY_library_load_type_ready(void);
extern PyMethodDef BPY_library_load_method_def;
extern PyMethodDef BPY_library_write_method_def;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_LIBRARY_H__ */
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_msgbus.h b/source/blender/python/intern/bpy_msgbus.h
index 2b3cc4cfaf4..9a797218832 100644
--- a/source/blender/python/intern/bpy_msgbus.h
+++ b/source/blender/python/intern/bpy_msgbus.h
@@ -21,6 +21,14 @@
#ifndef __BPY_MSGBUS_H__
#define __BPY_MSGBUS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_msgbus_module(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_MSGBUS_H__ */
diff --git a/source/blender/python/intern/bpy_operator.h b/source/blender/python/intern/bpy_operator.h
index 37f6b90fbdb..37a6acfe822 100644
--- a/source/blender/python/intern/bpy_operator.h
+++ b/source/blender/python/intern/bpy_operator.h
@@ -21,6 +21,10 @@
#ifndef __BPY_OPERATOR_H__
#define __BPY_OPERATOR_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern PyTypeObject pyop_base_Type;
#define BPy_OperatorBase_Check(v) (PyObject_TypeCheck(v, &pyop_base_Type))
@@ -31,4 +35,8 @@ typedef struct {
PyObject *BPY_operator_module(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/python/intern/bpy_operator_wrap.h b/source/blender/python/intern/bpy_operator_wrap.h
index 5bb087540cc..942f9dbea39 100644
--- a/source/blender/python/intern/bpy_operator_wrap.h
+++ b/source/blender/python/intern/bpy_operator_wrap.h
@@ -23,6 +23,10 @@
struct wmOperatorType;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* these are used for operator methods, used by bpy_operator.c */
PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args);
@@ -30,4 +34,8 @@ PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args);
void BPY_RNA_operator_wrapper(struct wmOperatorType *ot, void *userdata);
void BPY_RNA_operator_macro_wrapper(struct wmOperatorType *ot, void *userdata);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/python/intern/bpy_path.h b/source/blender/python/intern/bpy_path.h
index 3f102ae2bb9..0da9e610b53 100644
--- a/source/blender/python/intern/bpy_path.h
+++ b/source/blender/python/intern/bpy_path.h
@@ -21,6 +21,14 @@
#ifndef __BPY_PATH_H__
#define __BPY_PATH_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPyInit__bpy_path(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index ade02780210..a1f9d4afc51 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -88,6 +88,37 @@ static const EnumPropertyItem property_flag_enum_items[] = {
"'LIBRARY_EDITABLE'].\n" \
" :type options: set\n"
+static const EnumPropertyItem property_flag_override_items[] = {
+ {PROPOVERRIDE_OVERRIDABLE_LIBRARY,
+ "LIBRARY_OVERRIDABLE",
+ 0,
+ "Library Overridable",
+ "Allow that property to be overridable from library linked data-blocks"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+#define BPY_PROPDEF_OPTIONS_OVERRIDE_DOC \
+ " :arg options: Enumerator in ['LIBRARY_OVERRIDE'].\n" \
+ " :type options: set\n"
+
+static const EnumPropertyItem property_flag_override_collection_items[] = {
+ {PROPOVERRIDE_OVERRIDABLE_LIBRARY,
+ "LIBRARY_OVERRIDABLE",
+ 0,
+ "Library Overridable",
+ "Make that property editable in library overrides of linked data-blocks"},
+ {PROPOVERRIDE_NO_PROP_NAME,
+ "NO_PROPERTY_NAME",
+ 0,
+ "No Name",
+ "Do not use the names of the items, only their indices in the collection"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+#define BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC \
+ " :arg options: Enumerator in ['LIBRARY_OVERRIDE', 'NO_PROPERTY_NAME'].\n" \
+ " :type options: set\n"
+
/* subtypes */
/* XXX Keep in sync with rna_rna.c's rna_enum_property_subtype_items ???
* Currently it is not...
@@ -202,6 +233,11 @@ static void bpy_prop_assign_flag(PropertyRNA *prop, const int flag)
}
}
+static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_override)
+{
+ RNA_def_property_override_flag(prop, flag_override);
+}
+
/* operators and classes use this so it can store the args given but defer
* running it until the operator runs where these values are used to setup
* the default args for that operator instance */
@@ -1366,14 +1402,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 +1458,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 +1478,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 +1513,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 +1532,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 +1579,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;
}
}
@@ -1946,7 +1995,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
/* terse macros for error checks shared between all funcs cant use function
* calls because of static strings passed to pyrna_set_to_enum_bitfield */
-#define BPY_PROPDEF_CHECK(_func, _property_flag_items) \
+#define BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items) \
if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) { \
PyErr_Format(PyExc_TypeError, \
#_func "(): '%.200s' too long, max length is %d", \
@@ -1962,6 +2011,12 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
_property_flag_items, pyopts, &opts, #_func "(options={ ...}):"))) { \
return NULL; \
} \
+ if (UNLIKELY(pyopts_override && pyrna_set_to_enum_bitfield(_property_flag_override_items, \
+ pyopts_override, \
+ &opts_override, \
+ #_func "(override={ ...}):"))) { \
+ return NULL; \
+ } \
{ \
const EnumPropertyItem *tag_defines = RNA_struct_property_tag_defines(srna); \
if (py_tags && !tag_defines) { \
@@ -1977,8 +2032,9 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
} \
(void)0
-#define BPY_PROPDEF_SUBTYPE_CHECK(_func, _property_flag_items, _subtype) \
- BPY_PROPDEF_CHECK(_func, _property_flag_items); \
+#define BPY_PROPDEF_SUBTYPE_CHECK( \
+ _func, _property_flag_items, _property_flag_override_items, _subtype) \
+ BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items); \
if (UNLIKELY(pysubtype && RNA_enum_value_from_id(_subtype, pysubtype, &subtype) == 0)) { \
const char *enum_str = BPy_enum_as_string(_subtype); \
PyErr_Format(PyExc_TypeError, \
@@ -2086,7 +2142,8 @@ PyDoc_STRVAR(BPy_BoolProperty_doc,
"description=\"\", "
"default=False, "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "override=set(), "
+ "tags=set(), "
"subtype='NONE', "
"update=None, "
"get=None, "
@@ -2094,8 +2151,9 @@ PyDoc_STRVAR(BPy_BoolProperty_doc,
"\n"
" Returns a new boolean property definition.\n"
"\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC
- BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC
- BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
+ BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC
+ BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC
+ BPY_PROPDEF_SET_DOC);
static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2108,7 +2166,9 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
bool def = false;
PropertyRNA *prop;
PyObject *pyopts = NULL;
+ PyObject *pyopts_override = NULL;
int opts = 0;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2123,6 +2183,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
"description",
"default",
"options",
+ "override",
"tags",
"subtype",
"update",
@@ -2130,7 +2191,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssO&O!O!sOOO:BoolProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2143,6 +2204,8 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&update_cb,
@@ -2151,7 +2214,10 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, property_flag_items, property_subtype_number_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_number_items);
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
@@ -2173,6 +2239,9 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_boolean(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2181,24 +2250,26 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(BPy_BoolVectorProperty_doc,
- ".. function:: BoolVectorProperty(name=\"\", "
- "description=\"\", "
- "default=(False, False, False), "
- "options={'ANIMATABLE'}, "
- "tags={}, "
- "subtype='NONE', "
- "size=3, "
- "update=None, "
- "get=None, "
- "set=None)\n"
- "\n"
- " Returns a new vector boolean property definition.\n"
- "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
- " :arg default: sequence of booleans the length of *size*.\n"
- " :type default: sequence\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC
- BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC
- BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
+PyDoc_STRVAR(
+ BPy_BoolVectorProperty_doc,
+ ".. function:: BoolVectorProperty(name=\"\", "
+ "description=\"\", "
+ "default=(False, False, False), "
+ "options={'ANIMATABLE'}, "
+ "override=set(), "
+ "tags=set(), "
+ "subtype='NONE', "
+ "size=3, "
+ "update=None, "
+ "get=None, "
+ "set=None)\n"
+ "\n"
+ " Returns a new vector boolean property definition.\n"
+ "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
+ " :arg default: sequence of booleans the length of *size*.\n"
+ " :type default: sequence\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
+ BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC
+ BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2213,7 +2284,9 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
PropertyRNA *prop;
PyObject *pydef = NULL;
PyObject *pyopts = NULL;
+ PyObject *pyopts_override = NULL;
int opts = 0;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2228,6 +2301,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
"description",
"default",
"options",
+ "override",
"tags",
"subtype",
"size",
@@ -2236,7 +2310,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOO!O!siOOO:BoolVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2248,6 +2322,8 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&size,
@@ -2257,8 +2333,10 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(
- BoolVectorProperty, property_flag_items, property_subtype_array_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_array_items);
if (size < 1 || size > PYRNA_STACK_ARRAY) {
PyErr_Format(
@@ -2301,6 +2379,9 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_boolean_array(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2309,28 +2390,29 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
Py_RETURN_NONE;
}
-PyDoc_STRVAR(BPy_IntProperty_doc,
- ".. function:: IntProperty(name=\"\", "
- "description=\"\", "
- "default=0, "
- "min=-2**31, max=2**31-1, "
- "soft_min=-2**31, soft_max=2**31-1, "
- "step=1, "
- "options={'ANIMATABLE'}, "
- "tags={}, "
- "subtype='NONE', "
- "update=None, "
- "get=None, "
- "set=None)\n"
- "\n"
- " Returns a new int property definition.\n"
- "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_NUM_MIN_DOC
- " :type min: int\n" BPY_PROPDEF_NUM_MAX_DOC
- " :type max: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC
- " :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMIN_DOC
- " :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC
- BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC
- BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
+PyDoc_STRVAR(
+ BPy_IntProperty_doc,
+ ".. function:: IntProperty(name=\"\", "
+ "description=\"\", "
+ "default=0, "
+ "min=-2**31, max=2**31-1, "
+ "soft_min=-2**31, soft_max=2**31-1, "
+ "step=1, "
+ "options={'ANIMATABLE'}, "
+ "override=set(), "
+ "tags=set(), "
+ "subtype='NONE', "
+ "update=None, "
+ "get=None, "
+ "set=None)\n"
+ "\n"
+ " Returns a new int property definition.\n"
+ "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_NUM_MIN_DOC
+ " :type min: int\n" BPY_PROPDEF_NUM_MAX_DOC " :type max: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC
+ " :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMIN_DOC
+ " :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC
+ BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC
+ BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2344,6 +2426,8 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
PropertyRNA *prop;
PyObject *pyopts = NULL;
int opts = 0;
+ PyObject *pyopts_override = NULL;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2363,6 +2447,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
"soft_max",
"step",
"options",
+ "override",
"tags",
"subtype",
"update",
@@ -2370,7 +2455,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssiiiiiiO!O!sOOO:IntProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2387,6 +2472,8 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&update_cb,
@@ -2395,7 +2482,10 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, property_flag_items, property_subtype_number_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(IntProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_number_items);
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
@@ -2419,6 +2509,9 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_int(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2434,7 +2527,8 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc,
"soft_max=2**31-1, "
"step=1, "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "override=set(), "
+ "tags=set(), "
"subtype='NONE', "
"size=3, "
"update=None, "
@@ -2449,8 +2543,9 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc,
" :type max: int\n" BPY_PROPDEF_NUM_SOFTMIN_DOC
" :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC
" :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC
- BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC
- BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
+ BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC
+ BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC
+ BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2467,6 +2562,8 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
PyObject *pydef = NULL;
PyObject *pyopts = NULL;
int opts = 0;
+ PyObject *pyopts_override = NULL;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2486,6 +2583,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
"soft_max",
"step",
"options",
+ "override",
"tags",
"subtype",
"size",
@@ -2494,7 +2592,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOiiiiiO!O!siOOO:IntVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2511,6 +2609,8 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&size,
@@ -2520,8 +2620,10 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(
- IntVectorProperty, property_flag_items, property_subtype_array_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(IntVectorProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_array_items);
if (size < 1 || size > PYRNA_STACK_ARRAY) {
PyErr_Format(
@@ -2562,6 +2664,9 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_int_array(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2578,7 +2683,8 @@ PyDoc_STRVAR(BPy_FloatProperty_doc,
"step=3, "
"precision=2, "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "override=set(), "
+ "tags=set(), "
"subtype='NONE', "
"unit='NONE', "
"update=None, "
@@ -2591,9 +2697,9 @@ PyDoc_STRVAR(BPy_FloatProperty_doc,
" :type max: float\n" BPY_PROPDEF_NUM_SOFTMIN_DOC
" :type soft_min: float\n" BPY_PROPDEF_NUM_SOFTMAX_DOC
" :type soft_max: float\n" BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC
- BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC
- BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC
- BPY_PROPDEF_SET_DOC);
+ BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC
+ BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_UPDATE_DOC
+ BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2609,6 +2715,8 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
PropertyRNA *prop;
PyObject *pyopts = NULL;
int opts = 0;
+ PyObject *pyopts_override = NULL;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2620,26 +2728,11 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
PyObject *py_tags = NULL;
static const char *_keywords[] = {
- "attr",
- "name",
- "description",
- "default",
- "min",
- "max",
- "soft_min",
- "soft_max",
- "step",
- "precision",
- "options",
- "tags",
- "subtype",
- "unit",
- "update",
- "get",
- "set",
- NULL,
+ "attr", "name", "description", "default", "min", "max", "soft_min",
+ "soft_max", "step", "precision", "options", "override", "tags", "subtype",
+ "unit", "update", "get", "set", NULL,
};
- static _PyArg_Parser _parser = {"s#|ssffffffiO!O!ssOOO:FloatProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2657,6 +2750,8 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&pyunit,
@@ -2666,7 +2761,10 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, property_flag_items, property_subtype_number_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_number_items);
if (pyunit && RNA_enum_value_from_id(rna_enum_property_unit_items, pyunit, &unit) == 0) {
PyErr_Format(PyExc_TypeError, "FloatProperty(unit='%s'): invalid unit", pyunit);
@@ -2695,6 +2793,9 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_float(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2711,7 +2812,8 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc,
"step=3, "
"precision=2, "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "override=set(), "
+ "tags=set(), "
"subtype='NONE', "
"unit='NONE', "
"size=3, "
@@ -2726,8 +2828,8 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc,
" :type min: float\n" BPY_PROPDEF_NUM_MAX_DOC
" :type max: float\n" BPY_PROPDEF_NUM_SOFTMIN_DOC
" :type soft_min: float\n" BPY_PROPDEF_NUM_SOFTMAX_DOC
- " :type soft_max: float\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC
- BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC
+ " :type soft_max: float\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
+ BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC
BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_VECSIZE_DOC
BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
@@ -2746,6 +2848,8 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
PyObject *pydef = NULL;
PyObject *pyopts = NULL;
int opts = 0;
+ PyObject *pyopts_override = NULL;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2757,11 +2861,11 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
PyObject *py_tags = NULL;
static const char *_keywords[] = {
- "attr", "name", "description", "default", "min", "max", "soft_min",
- "soft_max", "step", "precision", "options", "tags", "subtype", "unit",
- "size", "update", "get", "set", NULL,
+ "attr", "name", "description", "default", "min", "max", "soft_min",
+ "soft_max", "step", "precision", "options", "override", "tags", "subtype",
+ "unit", "size", "update", "get", "set", NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOfffffiO!O!ssiOOO:FloatVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2779,6 +2883,8 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&pyunit,
@@ -2789,8 +2895,10 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(
- FloatVectorProperty, property_flag_items, property_subtype_array_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_array_items);
if (pyunit && RNA_enum_value_from_id(rna_enum_property_unit_items, pyunit, &unit) == 0) {
PyErr_Format(PyExc_TypeError, "FloatVectorProperty(unit='%s'): invalid unit", pyunit);
@@ -2837,6 +2945,9 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_float_array(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2850,7 +2961,8 @@ PyDoc_STRVAR(BPy_StringProperty_doc,
"default=\"\", "
"maxlen=0, "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "options=set(), "
+ "tags=set(), "
"subtype='NONE', "
"update=None, "
"get=None, "
@@ -2861,9 +2973,9 @@ PyDoc_STRVAR(BPy_StringProperty_doc,
" :arg default: initializer string.\n"
" :type default: string\n"
" :arg maxlen: maximum length of the string.\n"
- " :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC
- BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC
- BPY_PROPDEF_SET_DOC);
+ " :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC
+ BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC
+ BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2877,6 +2989,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
PropertyRNA *prop;
PyObject *pyopts = NULL;
int opts = 0;
+ PyObject *pyopts_override = NULL;
+ int opts_override = 0;
int prop_tags = 0;
const char *pysubtype = NULL;
int subtype = PROP_NONE;
@@ -2892,6 +3006,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
"default",
"maxlen",
"options",
+ "override",
"tags",
"subtype",
"update",
@@ -2899,7 +3014,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|sssiO!O!sOOO:StringProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|sssiO!O!O!sOOO:StringProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2912,6 +3027,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&pysubtype,
&update_cb,
@@ -2920,7 +3037,10 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
return NULL;
}
- BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, property_flag_items, property_subtype_string_items);
+ BPY_PROPDEF_SUBTYPE_CHECK(StringProperty,
+ property_flag_items,
+ property_flag_override_items,
+ property_subtype_string_items);
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
@@ -2948,6 +3068,9 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_string(prop, get_cb, set_cb);
RNA_def_property_duplicate_pointers(srna, prop);
@@ -2962,7 +3085,8 @@ PyDoc_STRVAR(
"description=\"\", "
"default=None, "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "override=set(), "
+ "tags=set(), "
"update=None, "
"get=None, "
"set=None)\n"
@@ -3001,14 +3125,14 @@ 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
- BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC);
+ " :type default: string, integer or set\n" BPY_PROPDEF_OPTIONS_ENUM_DOC
+ BPY_PROPDEF_OPTIONS_OVERRIDE_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)
{
StructRNA *srna;
@@ -3025,6 +3149,8 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
PropertyRNA *prop;
PyObject *pyopts = NULL;
int opts = 0;
+ PyObject *pyopts_override = NULL;
+ int opts_override = 0;
int prop_tags = 0;
bool is_itemf = false;
PyObject *update_cb = NULL;
@@ -3039,13 +3165,14 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
"description",
"default",
"options",
+ "override",
"tags",
"update",
"get",
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssOO!O!OOO:EnumProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|ssOO!O!O!OOO:EnumProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3058,6 +3185,8 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&update_cb,
&get_cb,
@@ -3065,7 +3194,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
return NULL;
}
- BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items);
+ BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items, property_flag_override_items);
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
@@ -3095,10 +3224,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;
@@ -3133,6 +3266,9 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
bpy_prop_callback_assign_update(prop, update_cb);
bpy_prop_callback_assign_enum(prop, get_cb, set_cb, (is_itemf ? items : NULL));
RNA_def_property_duplicate_pointers(srna, prop);
@@ -3178,14 +3314,15 @@ PyDoc_STRVAR(BPy_PointerProperty_doc,
"name=\"\", "
"description=\"\", "
"options={'ANIMATABLE'}, "
- "tags={}, "
+ "override=set(), "
+ "tags=set(), "
"poll=None, "
"update=None)\n"
"\n"
" Returns a new pointer property definition.\n"
"\n" BPY_PROPDEF_TYPE_DOC BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
- BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_POLL_DOC
- BPY_PROPDEF_UPDATE_DOC);
+ BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC
+ BPY_PROPDEF_POLL_DOC BPY_PROPDEF_UPDATE_DOC);
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -3199,8 +3336,10 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
StructRNA *ptype;
PyObject *type = Py_None;
PyObject *pyopts = NULL;
+ PyObject *pyopts_override = NULL;
PyObject *py_tags = NULL;
int opts = 0;
+ int opts_override = 0;
int prop_tags = 0;
PyObject *update_cb = NULL, *poll_cb = NULL;
@@ -3210,12 +3349,13 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
"name",
"description",
"options",
+ "override",
"tags",
"poll",
"update",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssO!O!OO:PointerProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|ssO!O!O!OO:PointerProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3227,13 +3367,15 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags,
&poll_cb,
&update_cb)) {
return NULL;
}
- BPY_PROPDEF_CHECK(PointerProperty, property_flag_items);
+ BPY_PROPDEF_CHECK(PointerProperty, property_flag_items, property_flag_override_items);
ptype = pointer_type_from_py(type, "PointerProperty(...)");
if (!ptype) {
@@ -3259,6 +3401,9 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
if (RNA_struct_idprops_contains_datablock(ptype)) {
if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
@@ -3277,11 +3422,13 @@ PyDoc_STRVAR(BPy_CollectionProperty_doc,
"name=\"\", "
"description=\"\", "
"options={'ANIMATABLE'}, "
- "tags={})\n"
+ "override=set(), "
+ "tags=set())\n"
"\n"
" Returns a new collection property definition.\n"
"\n" BPY_PROPDEF_TYPE_DOC BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC
- BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC);
+ BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC
+ BPY_PROPDEF_TAGS_DOC);
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -3295,8 +3442,10 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
StructRNA *ptype;
PyObject *type = Py_None;
PyObject *pyopts = NULL;
+ PyObject *pyopts_override = NULL;
PyObject *py_tags = NULL;
int opts = 0;
+ int opts_override = 0;
int prop_tags = 0;
static const char *_keywords[] = {
@@ -3305,10 +3454,11 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
"name",
"description",
"options",
+ "override",
"tags",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssO!O!:CollectionProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|ssO!O!O!:CollectionProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3320,11 +3470,14 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
&PySet_Type,
&pyopts,
&PySet_Type,
+ &pyopts_override,
+ &PySet_Type,
&py_tags)) {
return NULL;
}
- BPY_PROPDEF_CHECK(CollectionProperty, property_flag_items);
+ BPY_PROPDEF_CHECK(
+ CollectionProperty, property_flag_items, property_flag_override_collection_items);
ptype = pointer_type_from_py(type, "CollectionProperty(...):");
if (!ptype) {
@@ -3346,6 +3499,9 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+ if (pyopts_override) {
+ bpy_prop_assign_flag_override(prop, opts_override);
+ }
if (RNA_struct_idprops_contains_datablock(ptype)) {
if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h
index d559a3493b4..9bcfe8e263a 100644
--- a/source/blender/python/intern/bpy_props.h
+++ b/source/blender/python/intern/bpy_props.h
@@ -21,6 +21,10 @@
#ifndef __BPY_PROPS_H__
#define __BPY_PROPS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_rna_props(void);
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
@@ -29,4 +33,8 @@ StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
#define PYRNA_STACK_ARRAY RNA_STACK_ARRAY
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 179daad2f60..893832b61b6 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,
@@ -7660,8 +7660,8 @@ void BPY_update_rna_module(void)
}
#if 0
-/* This is a way we can access docstrings for RNA types
- * without having the datatypes in blender */
+/* This is a way we can access doc-strings for RNA types
+ * without having the data-types in Blender. */
PyObject *BPY_rna_doc(void)
{
PointerRNA ptr;
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 652d65fe64c..fadda9a8002 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -67,6 +67,10 @@
struct ID;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern PyTypeObject pyrna_struct_meta_idprop_Type;
extern PyTypeObject pyrna_struct_Type;
extern PyTypeObject pyrna_prop_Type;
@@ -265,4 +269,8 @@ extern PyMethodDef meth_bpy_owner_id_get;
extern BPy_StructRNA *bpy_context_module;
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index d792b2032b5..8aba2ae8598 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -36,6 +36,7 @@
#include "ED_keyframing.h"
#include "BKE_anim_data.h"
+#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
@@ -332,7 +333,20 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
&options) == -1) {
return NULL;
}
- else if (self->ptr.type == &RNA_NlaStrip) {
+
+ /* This assumes that keyframes are only added on original data & using the active depsgraph. If
+ * it turns out to be necessary for some reason to insert keyframes on evaluated objects, we can
+ * revisit this and add an explicit `depsgraph` keyword argument to the function call.
+ *
+ * It is unlikely that driver code (which is the reason this depsgraph pointer is obtained) will
+ * be executed from this function call, as this only happens when `options` has
+ * `INSERTKEY_DRIVER`, which is not exposed to Python. */
+ bContext *C = BPy_GetContext();
+ struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
+ cfra);
+
+ if (self->ptr.type == &RNA_NlaStrip) {
/* Handle special properties for NLA Strips, whose F-Curves are stored on the
* strips themselves. These are stored separately or else the properties will
* not have any effect.
@@ -355,8 +369,8 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
if (prop) {
NlaStrip *strip = ptr.data;
FCurve *fcu = BKE_fcurve_find(&strip->fcurves, RNA_property_identifier(prop), index);
-
- result = insert_keyframe_direct(&reports, ptr, prop, fcu, cfra, keytype, NULL, options);
+ result = insert_keyframe_direct(
+ &reports, ptr, prop, fcu, &anim_eval_context, keytype, NULL, options);
}
else {
BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full);
@@ -384,7 +398,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
group_name,
path_full,
index,
- cfra,
+ &anim_eval_context,
keytype,
NULL,
options) != 0);
@@ -415,7 +429,7 @@ char pyrna_struct_keyframe_delete_doc[] =
" :arg group: The name of the group the F-Curve should be added to if it doesn't exist "
"yet.\n"
" :type group: str\n"
- " :return: Success of keyframe deleation.\n"
+ " :return: Success of keyframe deletion.\n"
" :rtype: boolean\n";
PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyObject *kw)
{
diff --git a/source/blender/python/intern/bpy_rna_anim.h b/source/blender/python/intern/bpy_rna_anim.h
index 7630c268bbe..88537778dde 100644
--- a/source/blender/python/intern/bpy_rna_anim.h
+++ b/source/blender/python/intern/bpy_rna_anim.h
@@ -21,6 +21,10 @@
* \ingroup pythonintern
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern char pyrna_struct_keyframe_insert_doc[];
extern char pyrna_struct_keyframe_delete_doc[];
extern char pyrna_struct_driver_add_doc[];
@@ -31,4 +35,8 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb
PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args);
PyObject *pyrna_struct_driver_remove(BPy_StructRNA *self, PyObject *args);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_RNA_ANIM_H__ */
diff --git a/source/blender/python/intern/bpy_rna_callback.h b/source/blender/python/intern/bpy_rna_callback.h
index 71e1f71f8af..67392b60995 100644
--- a/source/blender/python/intern/bpy_rna_callback.h
+++ b/source/blender/python/intern/bpy_rna_callback.h
@@ -24,6 +24,10 @@
struct BPy_StructRNA;
struct PyObject;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#if 0
PyObject *pyrna_callback_add(BPy_StructRNA *self, PyObject *args);
PyObject *pyrna_callback_remove(BPy_StructRNA *self, PyObject *args);
@@ -32,4 +36,8 @@ PyObject *pyrna_callback_remove(BPy_StructRNA *self, PyObject *args);
PyObject *pyrna_callback_classmethod_add(PyObject *cls, PyObject *args);
PyObject *pyrna_callback_classmethod_remove(PyObject *cls, PyObject *args);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_RNA_CALLBACK_H__ */
diff --git a/source/blender/python/intern/bpy_rna_driver.h b/source/blender/python/intern/bpy_rna_driver.h
index 36160d7c867..a534f770732 100644
--- a/source/blender/python/intern/bpy_rna_driver.h
+++ b/source/blender/python/intern/bpy_rna_driver.h
@@ -25,10 +25,18 @@ struct ChannelDriver;
struct DriverTarget;
struct PathResolvedRNA;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar);
PyObject *pyrna_driver_self_from_anim_rna(struct PathResolvedRNA *anim_rna);
bool pyrna_driver_is_equal_anim_rna(const struct PathResolvedRNA *anim_rna,
const PyObject *py_anim_rna);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_RNA_DRIVER_H__ */
diff --git a/source/blender/python/intern/bpy_rna_gizmo.h b/source/blender/python/intern/bpy_rna_gizmo.h
index 293ab38a6ab..b209dbf4a26 100644
--- a/source/blender/python/intern/bpy_rna_gizmo.h
+++ b/source/blender/python/intern/bpy_rna_gizmo.h
@@ -21,6 +21,14 @@
#ifndef __BPY_RNA_GIZMO_H__
#define __BPY_RNA_GIZMO_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int BPY_rna_gizmo_module(PyObject *);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_RNA_GIZMO_H__ */
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index b607f1635e6..a4df8c3aef1 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -132,7 +132,7 @@ static int foreach_libblock_id_user_map_callback(LibraryIDLinkCallbackData *cb_d
PyDoc_STRVAR(bpy_user_map_doc,
".. method:: user_map([subset=(id1, id2, ...)], key_types={..}, value_types={..})\n"
"\n"
- " Returns a mapping of all ID datablocks in current ``bpy.data`` to a set of all "
+ " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all "
"datablocks using them.\n"
"\n"
" For list of valid set members for key_types & value_types, see: "
diff --git a/source/blender/python/intern/bpy_rna_id_collection.h b/source/blender/python/intern/bpy_rna_id_collection.h
index ee8f4c666a8..223ae126323 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.h
+++ b/source/blender/python/intern/bpy_rna_id_collection.h
@@ -21,8 +21,16 @@
#ifndef __BPY_RNA_ID_COLLECTION_H__
#define __BPY_RNA_ID_COLLECTION_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern PyMethodDef BPY_rna_id_collection_user_map_method_def;
extern PyMethodDef BPY_rna_id_collection_batch_remove_method_def;
extern PyMethodDef BPY_rna_id_collection_orphans_purge_method_def;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_RNA_ID_COLLECTION_H__ */
diff --git a/source/blender/python/intern/bpy_rna_types_capi.h b/source/blender/python/intern/bpy_rna_types_capi.h
index 1049122e468..402dc2c7936 100644
--- a/source/blender/python/intern/bpy_rna_types_capi.h
+++ b/source/blender/python/intern/bpy_rna_types_capi.h
@@ -21,6 +21,14 @@
#ifndef __BPY_RNA_TYPES_CAPI_H__
#define __BPY_RNA_TYPES_CAPI_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void BPY_rna_types_extend_capi(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_RNA_TYPES_CAPI_H__ */
diff --git a/source/blender/python/intern/bpy_traceback.c b/source/blender/python/intern/bpy_traceback.c
index e92a0b788ea..e2c894e90f8 100644
--- a/source/blender/python/intern/bpy_traceback.c
+++ b/source/blender/python/intern/bpy_traceback.c
@@ -34,8 +34,8 @@
static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
{
- return PyBytes_AS_STRING(
- (*coerce = PyUnicode_EncodeFSDefault(tb->tb_frame->f_code->co_filename)));
+ *coerce = PyUnicode_EncodeFSDefault(tb->tb_frame->f_code->co_filename);
+ return PyBytes_AS_STRING(*coerce);
}
/* copied from pythonrun.c, 3.4.0 */
@@ -140,8 +140,8 @@ void python_script_error_jump(const char *filepath, int *lineno, int *offset)
PyErr_Fetch(&exception, &value, (PyObject **)&tb);
if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) {
- /* no traceback available when SyntaxError.
- * python has no api's to this. reference parse_syntax_error() from pythonrun.c */
+ /* no trace-back available when `SyntaxError`.
+ * python has no API's to this. reference #parse_syntax_error() from pythonrun.c */
PyErr_NormalizeException(&exception, &value, (PyObject **)&tb);
if (value) { /* should always be true */
diff --git a/source/blender/python/intern/bpy_traceback.h b/source/blender/python/intern/bpy_traceback.h
index 11295706240..cb23dd23c9b 100644
--- a/source/blender/python/intern/bpy_traceback.h
+++ b/source/blender/python/intern/bpy_traceback.h
@@ -21,6 +21,14 @@
#ifndef __BPY_TRACEBACK_H__
#define __BPY_TRACEBACK_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void python_script_error_jump(const char *filepath, int *lineno, int *offset);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_TRACEBACK_H__ */
diff --git a/source/blender/python/intern/bpy_utils_previews.h b/source/blender/python/intern/bpy_utils_previews.h
index e9de8876068..23fd0216668 100644
--- a/source/blender/python/intern/bpy_utils_previews.h
+++ b/source/blender/python/intern/bpy_utils_previews.h
@@ -21,6 +21,14 @@
#ifndef __BPY_UTILS_PREVIEWS_H__
#define __BPY_UTILS_PREVIEWS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_utils_previews_module(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_UTILS_PREVIEWS_H__ */
diff --git a/source/blender/python/intern/bpy_utils_units.h b/source/blender/python/intern/bpy_utils_units.h
index 5a30c1c3dae..add766d0d93 100644
--- a/source/blender/python/intern/bpy_utils_units.h
+++ b/source/blender/python/intern/bpy_utils_units.h
@@ -21,6 +21,14 @@
#ifndef __BPY_UTILS_UNITS_H__
#define __BPY_UTILS_UNITS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
PyObject *BPY_utils_units(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __BPY_UTILS_UNITS_H__ */
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 7a3a92d8a10..3e30c81c8c6 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);
@@ -1527,7 +1529,7 @@ PyDoc_STRVAR(
" (instead of raising a :exc:`ValueError` exception).\n"
" :type fallback: :class:`Matrix`\n"
"\n"
- " .. seealso:: `Inverse matrix <https://en.wikipedia.org/wiki/Inverse_matrix>` on "
+ " .. seealso:: `Inverse matrix <https://en.wikipedia.org/wiki/Inverse_matrix>`__ on "
"Wikipedia.\n");
static PyObject *Matrix_invert(MatrixObject *self, PyObject *args)
{
@@ -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) {
@@ -1696,7 +1699,7 @@ PyDoc_STRVAR(
"\n"
" .. note:: When the matrix cannot be adjugated a :exc:`ValueError` exception is raised.\n"
"\n"
- " .. seealso:: `Adjugate matrix <https://en.wikipedia.org/wiki/Adjugate_matrix>` on "
+ " .. seealso:: `Adjugate matrix <https://en.wikipedia.org/wiki/Adjugate_matrix>`__ on "
"Wikipedia.\n");
static PyObject *Matrix_adjugate(MatrixObject *self)
{
@@ -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(
@@ -1883,7 +1886,7 @@ PyDoc_STRVAR(
" :return: Return the determinant of a matrix.\n"
" :rtype: float\n"
"\n"
- " .. seealso:: `Determinant <https://en.wikipedia.org/wiki/Determinant>` on Wikipedia.\n");
+ " .. seealso:: `Determinant <https://en.wikipedia.org/wiki/Determinant>`__ on Wikipedia.\n");
static PyObject *Matrix_determinant(MatrixObject *self)
{
if (BaseMath_ReadCallback(self) == -1) {
@@ -1906,7 +1909,7 @@ PyDoc_STRVAR(
"\n"
" Set the matrix to its transpose.\n"
"\n"
- " .. seealso:: `Transpose <https://en.wikipedia.org/wiki/Transpose>` on Wikipedia.\n");
+ " .. seealso:: `Transpose <https://en.wikipedia.org/wiki/Transpose>`__ on Wikipedia.\n");
static PyObject *Matrix_transpose(MatrixObject *self)
{
if (BaseMath_ReadCallback_ForWrite(self) == -1) {
@@ -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() -----------------------*/
@@ -2039,7 +2042,7 @@ PyDoc_STRVAR(Matrix_identity_doc,
" .. note:: An object with a location and rotation of zero, and a scale of one\n"
" will have an identity matrix.\n"
"\n"
- " .. seealso:: `Identity matrix <https://en.wikipedia.org/wiki/Identity_matrix>` "
+ " .. seealso:: `Identity matrix <https://en.wikipedia.org/wiki/Identity_matrix>`__ "
"on Wikipedia.\n");
static PyObject *Matrix_identity(MatrixObject *self)
{
@@ -2523,7 +2526,6 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
}
if (mat1 && mat2) {
-#ifdef USE_MATHUTILS_ELEM_MUL
/* MATRIX * MATRIX */
float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
@@ -2537,7 +2539,6 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
mul_vn_vnvn(mat, mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row);
return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1));
-#endif
}
else if (mat2) {
/*FLOAT/INT * MATRIX */
@@ -2581,7 +2582,6 @@ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2)
}
if (mat1 && mat2) {
-#ifdef USE_MATHUTILS_ELEM_MUL
/* MATRIX *= MATRIX */
if ((mat1->num_row != mat2->num_row) || (mat1->num_col != mat2->num_col)) {
PyErr_SetString(PyExc_ValueError,
@@ -2591,14 +2591,6 @@ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2)
}
mul_vn_vn(mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row);
-#else
- PyErr_Format(PyExc_TypeError,
- "In place element-wise multiplication: "
- "not supported between '%.200s' and '%.200s' types",
- Py_TYPE(m1)->tp_name,
- Py_TYPE(m2)->tp_name);
- return NULL;
-#endif
}
else if (mat1 && (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0)) {
/* MATRIX *= FLOAT/INT */
diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c
index 39d84c1ac96..2b7761b7678 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,
@@ -961,11 +962,9 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
}
if (quat1 && quat2) { /* QUAT * QUAT (element-wise product) */
-#ifdef USE_MATHUTILS_ELEM_MUL
float quat[QUAT_SIZE];
mul_vn_vnvn(quat, quat1->quat, quat2->quat, QUAT_SIZE);
return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
-#endif
}
/* the only case this can happen (for a supported type is "FLOAT * QUAT") */
else if (quat2) { /* FLOAT * QUAT */
@@ -1006,17 +1005,8 @@ static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2)
}
}
- if (quat1 && quat2) { /* QUAT *= QUAT (inplace element-wise product) */
-#ifdef USE_MATHUTILS_ELEM_MUL
+ if (quat1 && quat2) { /* QUAT *= QUAT (in-place element-wise product). */
mul_vn_vn(quat1->quat, quat2->quat, QUAT_SIZE);
-#else
- PyErr_Format(PyExc_TypeError,
- "In place element-wise multiplication: "
- "not supported between '%.200s' and '%.200s' types",
- Py_TYPE(q1)->tp_name,
- Py_TYPE(q2)->tp_name);
- return NULL;
-#endif
}
else if (quat1 && (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
/* QUAT *= FLOAT */
@@ -1385,10 +1375,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..4b47440a530 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,
@@ -1738,7 +1738,7 @@ static PyObject *vector_mul_float(VectorObject *vec, const float scalar)
mul_vn_vn_fl(tvec, vec->vec, vec->size, scalar);
return Vector_CreatePyObject_alloc(tvec, vec->size, Py_TYPE(vec));
}
-#ifdef USE_MATHUTILS_ELEM_MUL
+
static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2)
{
float *tvec = PyMem_Malloc(vec1->size * sizeof(float));
@@ -1752,7 +1752,7 @@ static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2)
mul_vn_vnvn(tvec, vec1->vec, vec2->vec, vec1->size);
return Vector_CreatePyObject_alloc(tvec, vec1->size, Py_TYPE(vec1));
}
-#endif
+
static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
{
VectorObject *vec1 = NULL, *vec2 = NULL;
@@ -1775,7 +1775,6 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
/* make sure v1 is always the vector */
if (vec1 && vec2) {
-#ifdef USE_MATHUTILS_ELEM_MUL
if (vec1->size != vec2->size) {
PyErr_SetString(PyExc_ValueError,
"Vector multiplication: "
@@ -1785,7 +1784,6 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
/* element-wise product */
return vector_mul_vec(vec1, vec2);
-#endif
}
else if (vec1) {
if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC * FLOAT */
@@ -1832,7 +1830,6 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2)
/* Intentionally don't support (Quaternion, Matrix) here, uses reverse order instead. */
if (vec1 && vec2) {
-#ifdef USE_MATHUTILS_ELEM_MUL
if (vec1->size != vec2->size) {
PyErr_SetString(PyExc_ValueError,
"Vector multiplication: "
@@ -1840,16 +1837,8 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2)
return NULL;
}
- /* element-wise product inplace */
+ /* Element-wise product in-place. */
mul_vn_vn(vec1->vec, vec2->vec, vec1->size);
-#else
- PyErr_Format(PyExc_TypeError,
- "In place element-wise multiplication: "
- "not supported between '%.200s' and '%.200s' types",
- Py_TYPE(v1)->tp_name,
- Py_TYPE(v2)->tp_name);
- return NULL;
-#endif
}
else if (vec1 && (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) ==
0)) { /* VEC *= FLOAT */
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 59c0021e0f3..93dbac32c19 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -1537,9 +1537,9 @@ static PyObject *M_Geometry_convex_hull_2d(PyObject *UNUSED(self), PyObject *poi
* to fill values, with start_table and len_table giving the start index
* and length of the toplevel_len sub-lists.
*/
-static PyObject *list_of_lists_from_arrays(int *array,
- int *start_table,
- int *len_table,
+static PyObject *list_of_lists_from_arrays(const int *array,
+ const int *start_table,
+ const int *len_table,
int toplevel_len)
{
PyObject *ret, *sublist;
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/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index f49c68a258d..e3c3cf712f9 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -32,7 +32,7 @@ set(INC
../makesdna
../makesrna
../nodes
- ../physics
+ ../simulation
../../../intern/atomic
../../../intern/guardedalloc
../../../intern/mikktspace
diff --git a/source/blender/render/extern/include/RE_bake.h b/source/blender/render/extern/include/RE_bake.h
index 3bab9179f84..6bb241b83ed 100644
--- a/source/blender/render/extern/include/RE_bake.h
+++ b/source/blender/render/extern/include/RE_bake.h
@@ -29,6 +29,10 @@ struct ImBuf;
struct Mesh;
struct Render;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct BakeImage {
struct Image *image;
int width;
@@ -120,4 +124,8 @@ void RE_bake_normal_world_to_world(const BakePixel pixel_array[],
void RE_bake_ibuf_clear(struct Image *image, const bool is_tangent);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __RE_BAKE_H__ */
diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h
index 77a60854616..49d312923e7 100644
--- a/source/blender/render/extern/include/RE_engine.h
+++ b/source/blender/render/extern/include/RE_engine.h
@@ -48,6 +48,10 @@ struct ViewLayer;
struct bNode;
struct bNodeTree;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* External Engine */
/* RenderEngineType.flag */
@@ -60,6 +64,7 @@ struct bNodeTree;
#define RE_USE_SHADING_NODES_CUSTOM 64
#define RE_USE_SPHERICAL_STEREO 128
#define RE_USE_STEREO_VIEWPORT 256
+#define RE_USE_GPU_CONTEXT 512
/* RenderEngine.flag */
#define RE_ENGINE_ANIMATION 1
@@ -236,4 +241,8 @@ void RE_bake_engine_set_engine_parameters(struct Render *re,
void RE_engine_free_blender_memory(struct RenderEngine *engine);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __RE_ENGINE_H__ */
diff --git a/source/blender/render/extern/include/RE_multires_bake.h b/source/blender/render/extern/include/RE_multires_bake.h
index 6abd9be6608..7698ff7d59c 100644
--- a/source/blender/render/extern/include/RE_multires_bake.h
+++ b/source/blender/render/extern/include/RE_multires_bake.h
@@ -27,6 +27,10 @@
struct MultiresBakeRender;
struct Scene;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct MultiresBakeRender {
Scene *scene;
DerivedMesh *lores_dm, *hires_dm;
@@ -63,4 +67,8 @@ typedef struct MultiresBakeRender {
void RE_multires_bake_images(struct MultiresBakeRender *bkr);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/render/extern/include/RE_render_ext.h b/source/blender/render/extern/include/RE_render_ext.h
index bdf81354b8d..7dfba8f668f 100644
--- a/source/blender/render/extern/include/RE_render_ext.h
+++ b/source/blender/render/extern/include/RE_render_ext.h
@@ -32,6 +32,10 @@ struct Depsgraph;
struct ImagePool;
struct MTex;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* render_texture.c */
bool RE_texture_evaluate(const struct MTex *mtex,
const float vec[3],
@@ -72,4 +76,8 @@ void RE_point_density_free(struct PointDensity *pd);
void RE_point_density_fix_linking(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __RE_RENDER_EXT_H__ */
diff --git a/source/blender/render/intern/include/initrender.h b/source/blender/render/intern/include/initrender.h
index 99282a8e703..ae78ac1dc1b 100644
--- a/source/blender/render/intern/include/initrender.h
+++ b/source/blender/render/intern/include/initrender.h
@@ -24,10 +24,18 @@
#ifndef __INITRENDER_H__
#define __INITRENDER_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Functions */
void RE_parts_init(Render *re);
void RE_parts_free(Render *re);
void RE_parts_clamp(Render *re);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __INITRENDER_H__ */
diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h
index 0ed8871b224..70b358ec5b4 100644
--- a/source/blender/render/intern/include/render_result.h
+++ b/source/blender/render/intern/include/render_result.h
@@ -44,6 +44,10 @@ struct RenderResult;
struct Scene;
struct rcti;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* New */
struct RenderResult *render_result_new(struct Render *re,
@@ -148,4 +152,8 @@ bool render_result_has_views(struct RenderResult *rr);
} \
((void)0)
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __RENDER_RESULT_H__ */
diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h
index 3ae4b9c0b90..14893c9bd1a 100644
--- a/source/blender/render/intern/include/render_types.h
+++ b/source/blender/render/intern/include/render_types.h
@@ -43,6 +43,10 @@ struct Object;
struct RenderEngine;
struct ReportList;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* this is handed over to threaded hiding/passes/shading engine */
typedef struct RenderPart {
struct RenderPart *next, *prev;
@@ -160,4 +164,8 @@ struct Render {
/* R.flag */
#define R_ANIMATION 1
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __RENDER_TYPES_H__ */
diff --git a/source/blender/render/intern/include/renderpipeline.h b/source/blender/render/intern/include/renderpipeline.h
index 12b231ef55f..3e8864fe56d 100644
--- a/source/blender/render/intern/include/renderpipeline.h
+++ b/source/blender/render/intern/include/renderpipeline.h
@@ -30,10 +30,18 @@ struct RenderData;
struct RenderLayer;
struct RenderResult;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct RenderLayer *render_get_active_layer(struct Render *re, struct RenderResult *rr);
void render_update_anim_renderdata(struct Render *re,
struct RenderData *rd,
struct ListBase *render_layers);
void render_copy_renderdata(struct RenderData *to, struct RenderData *from);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __RENDERPIPELINE_H__ */
diff --git a/source/blender/render/intern/include/texture.h b/source/blender/render/intern/include/texture.h
index f051d3ed318..1c66cb3f04d 100644
--- a/source/blender/render/intern/include/texture.h
+++ b/source/blender/render/intern/include/texture.h
@@ -24,6 +24,10 @@
#ifndef __TEXTURE_H__
#define __TEXTURE_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define BRICONT \
texres->tin = (texres->tin - 0.5f) * tex->contrast + tex->bright - 0.5f; \
if (!(tex->flag & TEX_NO_CLAMP)) { \
@@ -95,4 +99,8 @@ void image_sample(struct Image *ima,
float result[4],
struct ImagePool *pool);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __TEXTURE_H__ */
diff --git a/source/blender/render/intern/include/zbuf.h b/source/blender/render/intern/include/zbuf.h
index bc6addd6c5f..d03f997c8f2 100644
--- a/source/blender/render/intern/include/zbuf.h
+++ b/source/blender/render/intern/include/zbuf.h
@@ -21,6 +21,10 @@
#ifndef __ZBUF_H__
#define __ZBUF_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* span fill in method, is also used to localize data for zbuffering */
typedef struct ZSpan {
int rectx, recty; /* range for clipping */
@@ -40,4 +44,8 @@ void zspan_scanconvert(struct ZSpan *zpan,
float *v3,
void (*func)(void *, int, int, float, float));
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c
index 06f77854595..6e336604b59 100644
--- a/source/blender/render/intern/source/bake_api.c
+++ b/source/blender/render/intern/source/bake_api.c
@@ -694,14 +694,7 @@ void RE_bake_pixels_populate(Mesh *me,
const BakeImages *bake_images,
const char *uv_layer)
{
- BakeDataZSpan bd;
- size_t i;
- int a, p_id;
-
const MLoopUV *mloopuv;
- const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
- MLoopTri *looptri;
-
if ((uv_layer == NULL) || (uv_layer[0] == '\0')) {
mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
}
@@ -714,25 +707,26 @@ void RE_bake_pixels_populate(Mesh *me,
return;
}
+ BakeDataZSpan bd;
bd.pixel_array = pixel_array;
bd.zspan = MEM_callocN(sizeof(ZSpan) * bake_images->size, "bake zspan");
/* initialize all pixel arrays so we know which ones are 'blank' */
- for (i = 0; i < num_pixels; i++) {
+ for (int i = 0; i < num_pixels; i++) {
pixel_array[i].primitive_id = -1;
pixel_array[i].object_id = 0;
}
- for (i = 0; i < bake_images->size; i++) {
+ for (int i = 0; i < bake_images->size; i++) {
zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height);
}
- looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
+ const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
+ MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
- p_id = -1;
- for (i = 0; i < tottri; i++) {
+ for (int i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
const MPoly *mp = &me->mpoly[lt->poly];
float vec[3][2];
@@ -744,9 +738,9 @@ void RE_bake_pixels_populate(Mesh *me,
}
bd.bk_image = &bake_images->data[image_id];
- bd.primitive_id = ++p_id;
+ bd.primitive_id = i;
- for (a = 0; a < 3; a++) {
+ for (int a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;
/* Note, workaround for pixel aligned UVs which are common and can screw up our
@@ -761,7 +755,7 @@ void RE_bake_pixels_populate(Mesh *me,
zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel);
}
- for (i = 0; i < bake_images->size; i++) {
+ for (int i = 0; i < bake_images->size; i++) {
zbuf_free_span(&bd.zspan[i]);
}
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index af3a6fdd384..633b9324d9f 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -624,10 +624,6 @@ void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
return;
}
-#ifdef WITH_PYTHON
- BPy_BEGIN_ALLOW_THREADS;
-#endif
-
Render *re = engine->re;
double cfra = (double)frame + (double)subframe;
@@ -636,10 +632,6 @@ void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
BKE_scene_graph_update_for_newframe(engine->depsgraph, re->main);
BKE_scene_camera_switch_update(re->scene);
-
-#ifdef WITH_PYTHON
- BPy_END_ALLOW_THREADS;
-#endif
}
/* Bake */
@@ -838,9 +830,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);
@@ -869,8 +863,16 @@ int RE_engine_render(Render *re, int do_all)
re->draw_lock(re->dlh, 0);
}
+ if (engine->type->flag & RE_USE_GPU_CONTEXT) {
+ DRW_render_context_enable(engine->re);
+ }
+
type->render(engine, engine->depsgraph);
+ if (engine->type->flag & RE_USE_GPU_CONTEXT) {
+ DRW_render_context_disable(engine->re);
+ }
+
/* Grease pencil render over previous render result.
*
* NOTE: External engine might have been requested to free its
diff --git a/source/blender/render/intern/source/multires_bake.c b/source/blender/render/intern/source/multires_bake.c
index 7c301f7d591..b30821a1b73 100644
--- a/source/blender/render/intern/source/multires_bake.c
+++ b/source/blender/render/intern/source/multires_bake.c
@@ -1319,8 +1319,11 @@ static void bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
}
}
-static void bake_ibuf_normalize_displacement(
- ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max)
+static void bake_ibuf_normalize_displacement(ImBuf *ibuf,
+ const float *displacement,
+ const char *mask,
+ float displacement_min,
+ float displacement_max)
{
int i;
const float *current_displacement = displacement;
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index c66c43ec467..ade80898131 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;
}
}
@@ -2493,7 +2493,9 @@ void RE_RenderAnim(Render *re,
{
float ctime = BKE_scene_frame_get(scene);
AnimData *adt = BKE_animdata_from_id(&scene->id);
- BKE_animsys_evaluate_animdata(&scene->id, adt, ctime, ADT_RECALC_ALL, false);
+ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
+ re->pipeline_depsgraph, ctime);
+ BKE_animsys_evaluate_animdata(&scene->id, adt, &anim_eval_context, ADT_RECALC_ALL, false);
}
render_update_depsgraph(re);
diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c
index edade21fd27..8daad33b477 100644
--- a/source/blender/render/intern/source/pointdensity.c
+++ b/source/blender/render/intern/source/pointdensity.c
@@ -274,7 +274,7 @@ static void pointdensity_cache_psys(
BLI_bvhtree_balance(pd->point_tree);
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
}
@@ -823,7 +823,7 @@ static void particle_system_minmax(Depsgraph *depsgraph,
}
if (psys->lattice_deform_data) {
- end_latt_deform(psys->lattice_deform_data);
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
psys->lattice_deform_data = NULL;
}
}
diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c
index ee484924bf9..b37eeed3681 100644
--- a/source/blender/render/intern/source/render_texture.c
+++ b/source/blender/render/intern/source/render_texture.c
@@ -902,7 +902,7 @@ static void do_2d_mapping(
float fx, fy, fac1, area[8];
int ok, proj, areaflag = 0, wrap;
- /* mtex variables localized, only cubemap doesn't cooperate yet... */
+ /* #MTex variables localized, only cube-map doesn't cooperate yet. */
wrap = mtex->mapping;
tex = mtex->tex;
@@ -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..a12f36f9713 100644
--- a/source/blender/shader_fx/CMakeLists.txt
+++ b/source/blender/shader_fx/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/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..82e1d151cb6 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, Y 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_("Y"), 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 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_("Y"), 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..952a6df560b
--- /dev/null
+++ b/source/blender/shader_fx/intern/FX_ui_common.c
@@ -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.
+ */
+
+/** \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 "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 */
+
+/* -------------------------------------------------------------------- */
+/** \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 = ED_object_active_context(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);
+ WM_operator_properties_free(&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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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 = ED_object_active_context(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/physics/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt
index 10520a18513..cbc6ee65303 100644
--- a/source/blender/physics/CMakeLists.txt
+++ b/source/blender/simulation/CMakeLists.txt
@@ -24,8 +24,11 @@ set(INC
../blenkernel
../blenlib
../depsgraph
+ ../functions
../imbuf
../makesdna
+ ../makesrna
+ ../nodes
../../../intern/guardedalloc
)
@@ -34,18 +37,36 @@ set(INC_SYS
)
set(SRC
- intern/BPH_mass_spring.cpp
- intern/ConstrainedConjugateGradient.h
- intern/eigen_utils.h
+ intern/SIM_mass_spring.cpp
intern/hair_volume.cpp
- intern/implicit.h
intern/implicit_blender.c
intern/implicit_eigen.cpp
+ intern/particle_allocator.cc
+ intern/particle_function.cc
+ intern/particle_mesh_emitter.cc
+ intern/simulation_collect_influences.cc
+ intern/simulation_solver_influences.cc
+ intern/simulation_solver.cc
+ intern/simulation_update.cc
+
+ intern/ConstrainedConjugateGradient.h
+ intern/eigen_utils.h
+ intern/implicit.h
+ intern/particle_allocator.hh
+ intern/particle_function.hh
+ intern/particle_mesh_emitter.hh
+ intern/simulation_collect_influences.hh
+ intern/simulation_solver_influences.hh
+ intern/simulation_solver.hh
+ intern/time_interval.hh
- BPH_mass_spring.h
+ SIM_mass_spring.h
+ SIM_simulation_update.hh
)
set(LIB
+ bf_blenkernel
+ bf_nodes
)
if(WITH_OPENMP_STATIC)
@@ -54,4 +75,4 @@ if(WITH_OPENMP_STATIC)
)
endif()
-blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+blender_add_lib(bf_simulation "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/physics/BPH_mass_spring.h b/source/blender/simulation/SIM_mass_spring.h
index 5a8c78812a4..d30a7160fd8 100644
--- a/source/blender/physics/BPH_mass_spring.h
+++ b/source/blender/simulation/SIM_mass_spring.h
@@ -21,8 +21,8 @@
* \ingroup bph
*/
-#ifndef __BPH_MASS_SPRING_H__
-#define __BPH_MASS_SPRING_H__
+#ifndef __SIM_MASS_SPRING_H__
+#define __SIM_MASS_SPRING_H__
#ifdef __cplusplus
extern "C" {
@@ -35,25 +35,25 @@ struct ListBase;
struct Object;
typedef enum eMassSpringSolverStatus {
- BPH_SOLVER_SUCCESS = (1 << 0),
- BPH_SOLVER_NUMERICAL_ISSUE = (1 << 1),
- BPH_SOLVER_NO_CONVERGENCE = (1 << 2),
- BPH_SOLVER_INVALID_INPUT = (1 << 3),
+ SIM_SOLVER_SUCCESS = (1 << 0),
+ SIM_SOLVER_NUMERICAL_ISSUE = (1 << 1),
+ SIM_SOLVER_NO_CONVERGENCE = (1 << 2),
+ SIM_SOLVER_INVALID_INPUT = (1 << 3),
} eMassSpringSolverStatus;
-struct Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings);
-void BPH_mass_spring_solver_free(struct Implicit_Data *id);
-int BPH_mass_spring_solver_numvert(struct Implicit_Data *id);
+struct Implicit_Data *SIM_mass_spring_solver_create(int numverts, int numsprings);
+void SIM_mass_spring_solver_free(struct Implicit_Data *id);
+int SIM_mass_spring_solver_numvert(struct Implicit_Data *id);
-int BPH_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd);
-void BPH_cloth_solver_free(struct ClothModifierData *clmd);
-int BPH_cloth_solve(struct Depsgraph *depsgraph,
+int SIM_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd);
+void SIM_cloth_solver_free(struct ClothModifierData *clmd);
+int SIM_cloth_solve(struct Depsgraph *depsgraph,
struct Object *ob,
float frame,
struct ClothModifierData *clmd,
struct ListBase *effectors);
-void BKE_cloth_solver_set_positions(struct ClothModifierData *clmd);
-void BKE_cloth_solver_set_volume(ClothModifierData *clmd);
+void SIM_cloth_solver_set_positions(struct ClothModifierData *clmd);
+void SIM_cloth_solver_set_volume(ClothModifierData *clmd);
#ifdef __cplusplus
}
diff --git a/source/blender/functions/FN_cpp_types.hh b/source/blender/simulation/SIM_simulation_update.hh
index eceb43beb9d..efdfde8a4de 100644
--- a/source/blender/functions/FN_cpp_types.hh
+++ b/source/blender/simulation/SIM_simulation_update.hh
@@ -14,35 +14,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __FN_CPP_TYPES_HH__
-#define __FN_CPP_TYPES_HH__
+#ifndef __SIM_SIMULATION_UPDATE_HH__
+#define __SIM_SIMULATION_UPDATE_HH__
-/** \file
- * \ingroup functions
- *
- * This header provides convenient access to CPPType instances for some core types like integer
- * types.
- */
-
-#include "FN_cpp_type.hh"
-
-namespace FN {
-
-extern const CPPType &CPPType_bool;
-
-extern const CPPType &CPPType_float;
-extern const CPPType &CPPType_float3;
-extern const CPPType &CPPType_float4x4;
+struct Depsgraph;
+struct Scene;
+struct Simulation;
-extern const CPPType &CPPType_int32;
-extern const CPPType &CPPType_uint32;
-extern const CPPType &CPPType_uint8;
+namespace blender::sim {
-extern const CPPType &CPPType_Color4f;
-extern const CPPType &CPPType_Color4b;
+void update_simulation_in_depsgraph(Depsgraph *depsgraph,
+ Scene *scene_cow,
+ Simulation *simulation_cow);
-extern const CPPType &CPPType_string;
+bool update_simulation_dependencies(Simulation *simulation);
-} // namespace FN
+} // namespace blender::sim
-#endif /* __FN_CPP_TYPES_HH__ */
+#endif /* __SIM_SIMULATION_UPDATE_HH__ */
diff --git a/source/blender/physics/intern/ConstrainedConjugateGradient.h b/source/blender/simulation/intern/ConstrainedConjugateGradient.h
index c924490f97d..c924490f97d 100644
--- a/source/blender/physics/intern/ConstrainedConjugateGradient.h
+++ b/source/blender/simulation/intern/ConstrainedConjugateGradient.h
diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/simulation/intern/SIM_mass_spring.cpp
index 6a951519730..4834d6d351c 100644
--- a/source/blender/physics/intern/BPH_mass_spring.cpp
+++ b/source/blender/simulation/intern/SIM_mass_spring.cpp
@@ -38,7 +38,7 @@
#include "BKE_collision.h"
#include "BKE_effect.h"
-#include "BPH_mass_spring.h"
+#include "SIM_mass_spring.h"
#include "implicit.h"
#include "DEG_depsgraph.h"
@@ -72,12 +72,50 @@ static int cloth_count_nondiag_blocks(Cloth *cloth)
return nondiag;
}
+static bool cloth_get_pressure_weights(ClothModifierData *clmd,
+ const MVertTri *vt,
+ float *r_weights)
+{
+ /* We have custom vertex weights for pressure. */
+ if (clmd->sim_parms->vgroup_pressure > 0) {
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *verts = cloth->verts;
+
+ for (unsigned int j = 0; j < 3; j++) {
+ r_weights[j] = verts[vt->tri[j]].pressure_factor;
+
+ /* Skip the entire triangle if it has a zero weight. */
+ if (r_weights[j] == 0.0f) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static void cloth_calc_pressure_gradient(ClothModifierData *clmd,
+ const float gradient_vector[3],
+ float *r_vertex_pressure)
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ unsigned int mvert_num = cloth->mvert_num;
+ float pt[3];
+
+ for (unsigned int i = 0; i < mvert_num; i++) {
+ SIM_mass_spring_get_position(data, i, pt);
+ r_vertex_pressure[i] = dot_v3v3(pt, gradient_vector);
+ }
+}
+
static float cloth_calc_volume(ClothModifierData *clmd)
{
/* Calculate the (closed) cloth volume. */
Cloth *cloth = clmd->clothObject;
const MVertTri *tri = cloth->tri;
Implicit_Data *data = cloth->implicit;
+ float weights[3] = {1.0f, 1.0f, 1.0f};
float vol = 0;
/* Early exit for hair, as it never has volume. */
@@ -85,39 +123,75 @@ static float cloth_calc_volume(ClothModifierData *clmd)
return 0.0f;
}
- if (clmd->sim_parms->vgroup_pressure > 0) {
- for (unsigned int i = 0; i < cloth->primitive_num; i++) {
- bool skip_face = false;
- /* We have custom vertex weights for pressure. */
- const MVertTri *vt = &tri[i];
- for (unsigned int j = 0; j < 3; j++) {
- /* If any weight is zero, don't take this face into account for volume calculation. */
- ClothVertex *verts = clmd->clothObject->verts;
-
- if (verts[vt->tri[j]].pressure_factor == 0.0f) {
- skip_face = true;
- }
- }
- if (skip_face) {
- continue;
- }
+ for (unsigned int i = 0; i < cloth->primitive_num; i++) {
+ const MVertTri *vt = &tri[i];
- vol += BPH_tri_tetra_volume_signed_6x(data, vt->tri[0], vt->tri[1], vt->tri[2]);
+ if (cloth_get_pressure_weights(clmd, vt, weights)) {
+ vol += SIM_tri_tetra_volume_signed_6x(data, vt->tri[0], vt->tri[1], vt->tri[2]);
}
}
- else {
- for (unsigned int i = 0; i < cloth->primitive_num; i++) {
- const MVertTri *vt = &tri[i];
- vol += BPH_tri_tetra_volume_signed_6x(data, vt->tri[0], vt->tri[1], vt->tri[2]);
+
+ /* We need to divide by 6 to get the actual volume. */
+ vol = vol / 6.0f;
+
+ 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;
}
-int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
+static float cloth_calc_average_pressure(ClothModifierData *clmd, const float *vertex_pressure)
+{
+ Cloth *cloth = clmd->clothObject;
+ const MVertTri *tri = cloth->tri;
+ Implicit_Data *data = cloth->implicit;
+ float weights[3] = {1.0f, 1.0f, 1.0f};
+ float total_force = 0;
+ float total_area = 0;
+
+ for (unsigned int i = 0; i < cloth->primitive_num; i++) {
+ const MVertTri *vt = &tri[i];
+
+ if (cloth_get_pressure_weights(clmd, vt, weights)) {
+ float area = SIM_tri_area(data, vt->tri[0], vt->tri[1], vt->tri[2]);
+
+ total_force += (vertex_pressure[vt->tri[0]] + vertex_pressure[vt->tri[1]] +
+ vertex_pressure[vt->tri[2]]) *
+ area / 3.0f;
+ total_area += area;
+ }
+ }
+
+ return total_force / total_area;
+}
+
+int SIM_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
ClothVertex *verts = cloth->verts;
@@ -126,30 +200,30 @@ int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
unsigned int i, nondiag;
nondiag = cloth_count_nondiag_blocks(cloth);
- cloth->implicit = id = BPH_mass_spring_solver_create(cloth->mvert_num, nondiag);
+ cloth->implicit = id = SIM_mass_spring_solver_create(cloth->mvert_num, nondiag);
for (i = 0; i < cloth->mvert_num; i++) {
- BPH_mass_spring_set_vertex_mass(id, i, verts[i].mass);
+ SIM_mass_spring_set_vertex_mass(id, i, verts[i].mass);
}
for (i = 0; i < cloth->mvert_num; i++) {
- BPH_mass_spring_set_motion_state(id, i, verts[i].x, ZERO);
+ SIM_mass_spring_set_motion_state(id, i, verts[i].x, ZERO);
}
return 1;
}
-void BPH_cloth_solver_free(ClothModifierData *clmd)
+void SIM_cloth_solver_free(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
if (cloth->implicit) {
- BPH_mass_spring_solver_free(cloth->implicit);
+ SIM_mass_spring_solver_free(cloth->implicit);
cloth->implicit = NULL;
}
}
-void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
+void SIM_cloth_solver_set_positions(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
ClothVertex *verts = cloth->verts;
@@ -160,21 +234,21 @@ void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
for (i = 0; i < mvert_num; i++) {
if (cloth_hairdata) {
ClothHairData *root = &cloth_hairdata[i];
- BPH_mass_spring_set_rest_transform(id, i, root->rot);
+ SIM_mass_spring_set_rest_transform(id, i, root->rot);
}
else {
- BPH_mass_spring_set_rest_transform(id, i, I3);
+ SIM_mass_spring_set_rest_transform(id, i, I3);
}
- BPH_mass_spring_set_motion_state(id, i, verts[i].x, verts[i].v);
+ SIM_mass_spring_set_motion_state(id, i, verts[i].x, verts[i].v);
}
}
-void BKE_cloth_solver_set_volume(ClothModifierData *clmd)
+void SIM_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
@@ -191,12 +265,12 @@ static void cloth_setup_constraints(ClothModifierData *clmd)
const float ZERO[3] = {0.0f, 0.0f, 0.0f};
- BPH_mass_spring_clear_constraints(data);
+ SIM_mass_spring_clear_constraints(data);
for (v = 0; v < mvert_num; v++) {
if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) {
/* pinned vertex constraints */
- BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
+ SIM_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
}
verts[v].impulse_count = 0;
@@ -314,7 +388,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
k = scaling * s->restlen *
0.1f; /* Multiplying by 0.1, just to scale the forces to more reasonable values. */
- BPH_mass_spring_force_spring_angular(
+ SIM_mass_spring_force_spring_angular(
data, s->ij, s->kl, s->pa, s->pb, s->la, s->lb, s->restang, k, parms->bending_damping);
#endif
}
@@ -335,7 +409,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
/* TODO: verify, half verified (couldn't see error)
* sewing springs usually have a large distance at first so clamp the force so we don't get
* tunneling through collision objects. */
- BPH_mass_spring_force_spring_linear(data,
+ SIM_mass_spring_force_spring_linear(data,
s->ij,
s->kl,
s->restlen,
@@ -353,7 +427,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
s->lin_stiffness * fabsf(parms->max_compression - parms->compression);
k_compression = scaling_compression / (parms->avg_spring_len + FLT_EPSILON);
- BPH_mass_spring_force_spring_linear(data,
+ SIM_mass_spring_force_spring_linear(data,
s->ij,
s->kl,
s->restlen,
@@ -391,7 +465,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
k_compression_damp = 0.0f;
}
- BPH_mass_spring_force_spring_linear(data,
+ SIM_mass_spring_force_spring_linear(data,
s->ij,
s->kl,
s->restlen,
@@ -414,7 +488,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
scaling = parms->shear + s->lin_stiffness * fabsf(parms->max_shear - parms->shear);
k = scaling / (parms->avg_spring_len + FLT_EPSILON);
- BPH_mass_spring_force_spring_linear(data,
+ SIM_mass_spring_force_spring_linear(data,
s->ij,
s->kl,
s->restlen,
@@ -439,7 +513,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
// Fix for [#45084] for cloth stiffness must have cb proportional to kb
cb = kb * parms->bending_damping;
- BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb);
+ SIM_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb);
#endif
}
else if (s->type & CLOTH_SPRING_TYPE_BENDING_HAIR) {
@@ -460,14 +534,14 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
/* XXX assuming same restlen for ij and jk segments here,
* this can be done correctly for hair later. */
- BPH_mass_spring_force_spring_bending_hair(data, s->ij, s->kl, s->mn, s->target, kb, cb);
+ SIM_mass_spring_force_spring_bending_hair(data, s->ij, s->kl, s->mn, s->target, kb, cb);
# if 0
{
float x_kl[3], x_mn[3], v[3], d[3];
- BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
- BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
+ SIM_mass_spring_get_motion_state(data, s->kl, x_kl, v);
+ SIM_mass_spring_get_motion_state(data, s->mn, x_mn, v);
BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
BKE_sim_debug_data_add_line(
@@ -495,7 +569,7 @@ static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax
INIT_MINMAX(gmin, gmax);
for (i = 0; i < mvert_num; i++) {
float x[3];
- BPH_mass_spring_get_motion_state(data, i, x, NULL);
+ SIM_mass_spring_get_motion_state(data, i, x, NULL);
DO_MINMAX(x, gmin, gmax);
}
}
@@ -525,7 +599,7 @@ static void cloth_calc_force(
vert = cloth->verts;
for (i = 0; i < cloth->mvert_num; i++, vert++) {
- BPH_mass_spring_force_gravity(data, i, vert->mass, gravity);
+ SIM_mass_spring_force_gravity(data, i, vert->mass, gravity);
/* Vertex goal springs */
if ((!(vert->flags & CLOTH_VERT_FLAG_PINNED)) && (vert->goal > FLT_EPSILON)) {
@@ -539,7 +613,7 @@ static void cloth_calc_force(
k = vert->goal * clmd->sim_parms->goalspring /
(clmd->sim_parms->avg_spring_len + FLT_EPSILON);
- BPH_mass_spring_force_spring_goal(
+ SIM_mass_spring_force_spring_goal(
data, i, goal_x, goal_v, k, clmd->sim_parms->goalfrict * 0.01f);
}
}
@@ -548,12 +622,13 @@ static void cloth_calc_force(
/* cloth_calc_volume_force(clmd); */
#ifdef CLOTH_FORCE_DRAG
- BPH_mass_spring_force_drag(data, drag);
+ SIM_mass_spring_force_drag(data, drag);
#endif
/* handle pressure forces (making sure that this never gets computed for hair). */
if ((parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE) && (clmd->hairdata == NULL)) {
/* The difference in pressure between the inside and outside of the mesh.*/
float pressure_difference = 0.0f;
+ float volume_factor = 1.0f;
float init_vol;
if (parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE_VOL) {
@@ -568,80 +643,111 @@ static void cloth_calc_force(
float f;
float vol = cloth_calc_volume(clmd);
+ /* If the volume is the same don't apply any pressure. */
+ volume_factor = init_vol / vol;
+ pressure_difference = volume_factor - 1;
+
/* Calculate an artificial maximum value for cloth pressure. */
f = fabs(clmd->sim_parms->uniform_pressure_force) + 200.0f;
/* Clamp the cloth pressure to the calculated maximum value. */
- if (vol * f < init_vol) {
- pressure_difference = f;
- }
- else {
- /* If the volume is the same don't apply any pressure. */
- pressure_difference = (init_vol / vol) - 1;
- }
+ CLAMP_MAX(pressure_difference, f);
}
- pressure_difference += clmd->sim_parms->uniform_pressure_force;
+ pressure_difference += clmd->sim_parms->uniform_pressure_force;
pressure_difference *= clmd->sim_parms->pressure_factor;
- for (i = 0; i < cloth->primitive_num; i++) {
- const MVertTri *vt = &tri[i];
- if (fabs(pressure_difference) > 1E-6f) {
- if (clmd->sim_parms->vgroup_pressure > 0) {
- /* We have custom vertex weights for pressure. */
- ClothVertex *verts = clmd->clothObject->verts;
- int v1, v2, v3;
- v1 = vt->tri[0];
- v2 = vt->tri[1];
- v3 = vt->tri[2];
-
- float weights[3];
- bool skip_face = false;
-
- weights[0] = verts[v1].pressure_factor;
- weights[1] = verts[v2].pressure_factor;
- weights[2] = verts[v3].pressure_factor;
- for (unsigned int j = 0; j < 3; j++) {
- if (weights[j] == 0.0f) {
- /* Exclude faces which has a zero weight vert. */
- skip_face = true;
- break;
- }
- }
- if (skip_face) {
- continue;
- }
+ /* Compute the hydrostatic pressure gradient if enabled. */
+ float fluid_density = clmd->sim_parms->fluid_density * 1000; /* kg/l -> kg/m3 */
+ float *hydrostatic_pressure = NULL;
- BPH_mass_spring_force_pressure(data, v1, v2, v3, pressure_difference, weights);
- }
- else {
- float weights[3] = {1.0f, 1.0f, 1.0f};
- BPH_mass_spring_force_pressure(
- data, vt->tri[0], vt->tri[1], vt->tri[2], pressure_difference, weights);
+ if (fabs(fluid_density) > 1e-6f) {
+ float hydrostatic_vector[3];
+ copy_v3_v3(hydrostatic_vector, gravity);
+
+ /* When the fluid is inside the object, factor in the acceleration of
+ * the object into the pressure field, as gravity is indistinguishable
+ * from acceleration from the inside. */
+ if (fluid_density > 0) {
+ sub_v3_v3(hydrostatic_vector, cloth->average_acceleration);
+
+ /* Preserve the total mass by scaling density to match the change in volume. */
+ fluid_density *= volume_factor;
+ }
+
+ mul_v3_fl(hydrostatic_vector, fluid_density);
+
+ /* Compute an array of per-vertex hydrostatic pressure, and subtract the average. */
+ hydrostatic_pressure = (float *)MEM_mallocN(sizeof(float) * mvert_num,
+ "hydrostatic pressure gradient");
+
+ cloth_calc_pressure_gradient(clmd, hydrostatic_vector, hydrostatic_pressure);
+
+ pressure_difference -= cloth_calc_average_pressure(clmd, hydrostatic_pressure);
+ }
+
+ /* Apply pressure. */
+ if (hydrostatic_pressure || fabs(pressure_difference) > 1E-6f) {
+ float weights[3] = {1.0f, 1.0f, 1.0f};
+
+ for (i = 0; i < cloth->primitive_num; i++) {
+ const MVertTri *vt = &tri[i];
+
+ if (cloth_get_pressure_weights(clmd, vt, weights)) {
+ SIM_mass_spring_force_pressure(data,
+ vt->tri[0],
+ vt->tri[1],
+ vt->tri[2],
+ pressure_difference,
+ hydrostatic_pressure,
+ weights);
}
}
}
+
+ if (hydrostatic_pressure) {
+ MEM_freeN(hydrostatic_pressure);
+ }
}
/* 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);
+ SIM_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) {
+ SIM_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
+ }
+ if (has_force) {
+ SIM_mass_spring_force_face_extern(data, vt->tri[0], vt->tri[1], vt->tri[2], forcevec);
+ }
}
}
else {
@@ -655,11 +761,11 @@ static void cloth_calc_force(
if (hairdata) {
hair_ij = &hairdata[spring->ij];
hair_kl = &hairdata[spring->kl];
- BPH_mass_spring_force_edge_wind(
+ SIM_mass_spring_force_edge_wind(
data, spring->ij, spring->kl, hair_ij->radius, hair_kl->radius, winvec);
}
else {
- BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, 1.0f, 1.0f, winvec);
+ SIM_mass_spring_force_edge_wind(data, spring->ij, spring->kl, 1.0f, 1.0f, winvec);
}
}
}
@@ -670,10 +776,10 @@ static void cloth_calc_force(
for (i = 0; i < cloth->mvert_num; i++, vert++) {
if (hairdata) {
ClothHairData *hair = &hairdata[i];
- BPH_mass_spring_force_vertex_wind(data, i, hair->radius, winvec);
+ SIM_mass_spring_force_vertex_wind(data, i, hair->radius, winvec);
}
else {
- BPH_mass_spring_force_vertex_wind(data, i, 1.0f, winvec);
+ SIM_mass_spring_force_vertex_wind(data, i, 1.0f, winvec);
}
}
#endif
@@ -700,8 +806,8 @@ BLI_INLINE void cloth_get_grid_location(Implicit_Data *data,
float x[3],
float v[3])
{
- BPH_mass_spring_get_position(data, index, x);
- BPH_mass_spring_get_new_velocity(data, index, v);
+ SIM_mass_spring_get_position(data, index, x);
+ SIM_mass_spring_get_new_velocity(data, index, v);
mul_v3_fl(x, cell_scale);
add_v3_v3(x, cell_offset);
@@ -796,7 +902,7 @@ static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid,
zero_v3(dir3);
}
- BPH_hair_volume_add_segment(
+ SIM_hair_volume_add_segment(
grid, x1, v1, x2, v2, x3, v3, x4, v4, spring1 ? dir1 : NULL, dir2, spring3 ? dir3 : NULL);
}
@@ -815,7 +921,7 @@ static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
float x[3], v[3];
cloth_get_vertex_motion_state(data, vert, x, v);
- BPH_hair_volume_add_vertex(grid, x, v);
+ SIM_hair_volume_add_vertex(grid, x, v);
}
#else
LinkNode *link;
@@ -824,7 +930,7 @@ static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
/* scale and offset for transforming vertex locations into grid space
* (cell size is 0..1, gmin becomes origin)
*/
- BPH_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL);
+ SIM_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL);
cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f;
mul_v3_v3fl(cell_offset, gmin, cell_scale);
negate_v3(cell_offset);
@@ -840,7 +946,7 @@ static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
}
}
#endif
- BPH_hair_volume_normalize_vertex_grid(grid);
+ SIM_hair_volume_normalize_vertex_grid(grid);
}
static void cloth_continuum_step(ClothModifierData *clmd, float dt)
@@ -871,31 +977,31 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
/* gather velocities & density */
if (smoothfac > 0.0f || density_strength > 0.0f) {
- HairGrid *grid = BPH_hair_volume_create_vertex_grid(
+ HairGrid *grid = SIM_hair_volume_create_vertex_grid(
clmd->sim_parms->voxel_cell_size, gmin, gmax);
cloth_continuum_fill_grid(grid, cloth);
/* main hair continuum solver */
- BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
+ SIM_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
for (i = 0, vert = cloth->verts; i < mvert_num; i++, vert++) {
float x[3], v[3], nv[3];
/* calculate volumetric velocity influence */
- BPH_mass_spring_get_position(data, i, x);
- BPH_mass_spring_get_new_velocity(data, i, v);
+ SIM_mass_spring_get_position(data, i, x);
+ SIM_mass_spring_get_new_velocity(data, i, v);
- BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
+ SIM_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
interp_v3_v3v3(nv, v, nv, smoothfac);
/* apply on hair data */
- BPH_mass_spring_set_new_velocity(data, i, nv);
+ SIM_mass_spring_set_new_velocity(data, i, nv);
}
/* store basic grid info in the modifier data */
- BPH_hair_volume_grid_geometry(grid,
+ SIM_hair_volume_grid_geometry(grid,
&clmd->hair_grid_cellsize,
clmd->hair_grid_res,
clmd->hair_grid_min,
@@ -928,7 +1034,7 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
madd_v3_v3fl(x, b, (float)j / (float)(size - 1));
zero_v3(v);
- BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
+ SIM_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
// BKE_sim_debug_data_add_circle(
// clmd->debug_data, x, gdensity, 0.7, 0.3, 1,
@@ -968,7 +1074,7 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt)
}
#endif
- BPH_hair_volume_free_vertex_grid(grid);
+ SIM_hair_volume_free_vertex_grid(grid);
}
}
@@ -993,7 +1099,7 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
/* gather velocities & density */
if (smoothfac > 0.0f || pressfac > 0.0f) {
- HairVertexGrid *vertex_grid = BPH_hair_volume_create_vertex_grid(
+ HairVertexGrid *vertex_grid = SIM_hair_volume_create_vertex_grid(
clmd->sim_parms->voxel_res, gmin, gmax);
vert = cloth->verts;
@@ -1005,11 +1111,11 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
copy_v3_v3(v, vert->v);
}
else {
- BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
+ SIM_mass_spring_get_motion_state(data, vert->solver_index, x, v);
}
- BPH_hair_volume_add_vertex(vertex_grid, x, v);
+ SIM_hair_volume_add_vertex(vertex_grid, x, v);
}
- BPH_hair_volume_normalize_vertex_grid(vertex_grid);
+ SIM_hair_volume_normalize_vertex_grid(vertex_grid);
vert = cloth->verts;
for (i = 0; i < mvert_num; i++, vert++) {
@@ -1020,18 +1126,42 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
}
/* calculate volumetric forces */
- BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
- BPH_hair_volume_vertex_grid_forces(
+ SIM_mass_spring_get_motion_state(data, vert->solver_index, x, v);
+ SIM_hair_volume_vertex_grid_forces(
vertex_grid, x, v, smoothfac, pressfac, minpress, f, dfdx, dfdv);
/* apply on hair data */
- BPH_mass_spring_force_extern(data, vert->solver_index, f, dfdx, dfdv);
+ SIM_mass_spring_force_extern(data, vert->solver_index, f, dfdx, dfdv);
}
- BPH_hair_volume_free_vertex_grid(vertex_grid);
+ SIM_hair_volume_free_vertex_grid(vertex_grid);
}
}
#endif
+static void cloth_calc_average_acceleration(ClothModifierData *clmd, float dt)
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ int i, mvert_num = cloth->mvert_num;
+ float total[3] = {0.0f, 0.0f, 0.0f};
+
+ for (i = 0; i < mvert_num; i++) {
+ float v[3], nv[3];
+
+ SIM_mass_spring_get_velocity(data, i, v);
+ SIM_mass_spring_get_new_velocity(data, i, nv);
+
+ sub_v3_v3(nv, v);
+ add_v3_v3(total, nv);
+ }
+
+ mul_v3_fl(total, 1.0f / dt / mvert_num);
+
+ /* Smooth the data using a running average to prevent instability.
+ * This is effectively an abstraction of the wave propagation speed in fluid. */
+ interp_v3_v3v3(cloth->average_acceleration, total, cloth->average_acceleration, powf(0.25f, dt));
+}
+
static void cloth_solve_collisions(
Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt)
{
@@ -1051,11 +1181,11 @@ static void cloth_solve_collisions(
return;
}
- BPH_mass_spring_solve_positions(id, dt);
+ SIM_mass_spring_solve_positions(id, dt);
/* Update verts to current positions. */
for (i = 0; i < mvert_num; i++) {
- BPH_mass_spring_get_new_position(id, i, verts[i].tx);
+ SIM_mass_spring_get_new_position(id, i, verts[i].tx);
sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
zero_v3(verts[i].dcvel);
@@ -1071,9 +1201,9 @@ static void cloth_solve_collisions(
continue;
}
- BPH_mass_spring_get_new_velocity(id, i, verts[i].tv);
+ SIM_mass_spring_get_new_velocity(id, i, verts[i].tv);
madd_v3_v3fl(verts[i].tv, verts[i].dcvel, time_multiplier);
- BPH_mass_spring_set_new_velocity(id, i, verts[i].tv);
+ SIM_mass_spring_set_new_velocity(id, i, verts[i].tv);
}
}
}
@@ -1094,7 +1224,7 @@ static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *r
if (sres->status) { /* already initialized ? */
/* error only makes sense for successful iterations */
- if (result->status == BPH_SOLVER_SUCCESS) {
+ if (result->status == SIM_SOLVER_SUCCESS) {
sres->min_error = min_ff(sres->min_error, result->error);
sres->max_error = max_ff(sres->max_error, result->error);
sres->avg_error += result->error * dt;
@@ -1106,7 +1236,7 @@ static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *r
}
else {
/* error only makes sense for successful iterations */
- if (result->status == BPH_SOLVER_SUCCESS) {
+ if (result->status == SIM_SOLVER_SUCCESS) {
sres->min_error = sres->max_error = result->error;
sres->avg_error += result->error * dt;
}
@@ -1118,7 +1248,7 @@ static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *r
sres->status |= result->status;
}
-int BPH_cloth_solve(
+int SIM_cloth_solve(
Depsgraph *depsgraph, Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
{
/* Hair currently is a cloth sim in disguise ...
@@ -1136,6 +1266,10 @@ int BPH_cloth_solve(
float dt = clmd->sim_parms->dt * clmd->sim_parms->timescale;
Implicit_Data *id = cloth->implicit;
+ /* Hydrostatic pressure gradient of the fluid inside the object is affected by acceleration. */
+ bool use_acceleration = (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_PRESSURE) &&
+ (clmd->sim_parms->fluid_density > 0);
+
BKE_sim_debug_data_clear_category("collision");
if (!clmd->solver_result) {
@@ -1153,11 +1287,15 @@ int BPH_cloth_solve(
// mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
/* divide by time_scale to prevent constrained velocities from being multiplied */
mul_v3_fl(v, 1.0f / clmd->sim_parms->time_scale);
- BPH_mass_spring_set_velocity(id, i, v);
+ SIM_mass_spring_set_velocity(id, i, v);
}
}
}
+ if (!use_acceleration) {
+ zero_v3(cloth->average_acceleration);
+ }
+
while (step < tf) {
ImplicitSolverResult result;
@@ -1165,13 +1303,13 @@ int BPH_cloth_solve(
cloth_setup_constraints(clmd);
/* initialize forces to zero */
- BPH_mass_spring_clear_forces(id);
+ SIM_mass_spring_clear_forces(id);
// calculate forces
cloth_calc_force(scene, clmd, frame, effectors, step);
// calculate new velocity and position
- BPH_mass_spring_solve_velocities(id, dt, &result);
+ SIM_mass_spring_solve_velocities(id, dt, &result);
cloth_record_result(clmd, &result, dt);
/* Calculate collision impulses. */
@@ -1181,8 +1319,12 @@ int BPH_cloth_solve(
cloth_continuum_step(clmd, dt);
}
- BPH_mass_spring_solve_positions(id, dt);
- BPH_mass_spring_apply_result(id);
+ if (use_acceleration) {
+ cloth_calc_average_acceleration(clmd, dt);
+ }
+
+ SIM_mass_spring_solve_positions(id, dt);
+ SIM_mass_spring_apply_result(id);
/* move pinned verts to correct position */
for (i = 0; i < mvert_num; i++) {
@@ -1193,11 +1335,11 @@ int BPH_cloth_solve(
* delta locations from being multiplied */
interp_v3_v3v3(
x, verts[i].xold, verts[i].xconst, (step + dt) / clmd->sim_parms->time_scale);
- BPH_mass_spring_set_position(id, i, x);
+ SIM_mass_spring_set_position(id, i, x);
}
}
- BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
+ SIM_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
}
step += dt;
@@ -1205,7 +1347,7 @@ int BPH_cloth_solve(
/* copy results back to cloth data */
for (i = 0; i < mvert_num; i++) {
- BPH_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
+ SIM_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
copy_v3_v3(verts[i].txold, verts[i].x);
}
diff --git a/source/blender/physics/intern/eigen_utils.h b/source/blender/simulation/intern/eigen_utils.h
index c186cf567df..c186cf567df 100644
--- a/source/blender/physics/intern/eigen_utils.h
+++ b/source/blender/simulation/intern/eigen_utils.h
diff --git a/source/blender/physics/intern/hair_volume.cpp b/source/blender/simulation/intern/hair_volume.cpp
index 6246bf54f75..c80ae69ce73 100644
--- a/source/blender/physics/intern/hair_volume.cpp
+++ b/source/blender/simulation/intern/hair_volume.cpp
@@ -205,7 +205,7 @@ BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid,
}
}
-void BPH_hair_volume_vertex_grid_forces(HairGrid *grid,
+void SIM_hair_volume_vertex_grid_forces(HairGrid *grid,
const float x[3],
const float v[3],
float smoothfac,
@@ -244,7 +244,7 @@ void BPH_hair_volume_vertex_grid_forces(HairGrid *grid,
mul_m3_fl(dfdv, smoothfac);
}
-void BPH_hair_volume_grid_interpolate(HairGrid *grid,
+void SIM_hair_volume_grid_interpolate(HairGrid *grid,
const float x[3],
float *density,
float velocity[3],
@@ -264,7 +264,7 @@ void BPH_hair_volume_grid_interpolate(HairGrid *grid,
velocity_gradient);
}
-void BPH_hair_volume_grid_velocity(
+void SIM_hair_volume_grid_velocity(
HairGrid *grid, const float x[3], const float v[3], float fluid_factor, float r_v[3])
{
float gdensity, gvelocity[3], gvel_smooth[3], ggrad[3], gvelgrad[3][3];
@@ -291,7 +291,7 @@ void BPH_hair_volume_grid_velocity(
interp_v3_v3v3(r_v, v_pic, v_flip, fluid_factor);
}
-void BPH_hair_volume_grid_clear(HairGrid *grid)
+void SIM_hair_volume_grid_clear(HairGrid *grid)
{
const int size = hair_grid_size(grid->res);
int i;
@@ -303,7 +303,7 @@ void BPH_hair_volume_grid_clear(HairGrid *grid)
}
}
-BLI_INLINE bool hair_grid_point_valid(const float vec[3], float gmin[3], float gmax[3])
+BLI_INLINE bool hair_grid_point_valid(const float vec[3], const float gmin[3], const float gmax[3])
{
return !(vec[0] < gmin[0] || vec[1] < gmin[1] || vec[2] < gmin[2] || vec[0] > gmax[0] ||
vec[1] > gmax[1] || vec[2] > gmax[2]);
@@ -362,7 +362,7 @@ BLI_INLINE void grid_to_world(HairGrid *grid, float vecw[3], const float vec[3])
add_v3_v3(vecw, grid->gmin);
}
-void BPH_hair_volume_add_vertex(HairGrid *grid, const float x[3], const float v[3])
+void SIM_hair_volume_add_vertex(HairGrid *grid, const float x[3], const float v[3])
{
const int res[3] = {grid->res[0], grid->res[1], grid->res[2]};
float weights[8];
@@ -503,7 +503,7 @@ BLI_INLINE void hair_volume_add_segment_2D(HairGrid *grid,
* The radius of influence around a segment is assumed to be at most 2*cellsize,
* i.e. only cells containing the segment and their direct neighbors are examined.
*/
-void BPH_hair_volume_add_segment(HairGrid *grid,
+void SIM_hair_volume_add_segment(HairGrid *grid,
const float x1[3],
const float v1[3],
const float x2[3],
@@ -633,7 +633,7 @@ BLI_INLINE void hair_volume_eval_grid_vertex_sample(HairGridVert *vert,
/* XXX simplified test implementation using a series of discrete sample along the segment,
* instead of finding the closest point for all affected grid vertices.
*/
-void BPH_hair_volume_add_segment(HairGrid *grid,
+void SIM_hair_volume_add_segment(HairGrid *grid,
const float UNUSED(x1[3]),
const float UNUSED(v1[3]),
const float x2[3],
@@ -684,7 +684,7 @@ void BPH_hair_volume_add_segment(HairGrid *grid,
}
#endif
-void BPH_hair_volume_normalize_vertex_grid(HairGrid *grid)
+void SIM_hair_volume_normalize_vertex_grid(HairGrid *grid)
{
int i, size = hair_grid_size(grid->res);
/* divide velocity with density */
@@ -715,7 +715,7 @@ BLI_INLINE float hair_volume_density_divergence(float density,
}
}
-bool BPH_hair_volume_solve_divergence(HairGrid *grid,
+bool SIM_hair_volume_solve_divergence(HairGrid *grid,
float /*dt*/,
float target_density,
float target_strength)
@@ -1078,7 +1078,7 @@ BLI_INLINE void hair_volume_filter_box_convolute(
}
}
-void BPH_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_size)
+void SIM_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_size)
{
int size = hair_grid_size(grid->res);
int kernel_sizev[3] = {kernel_size, kernel_size, kernel_size};
@@ -1113,7 +1113,7 @@ void BPH_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_siz
}
#endif
-HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize,
+HairGrid *SIM_hair_volume_create_vertex_grid(float cellsize,
const float gmin[3],
const float gmax[3])
{
@@ -1170,7 +1170,7 @@ HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize,
return grid;
}
-void BPH_hair_volume_free_vertex_grid(HairGrid *grid)
+void SIM_hair_volume_free_vertex_grid(HairGrid *grid)
{
if (grid) {
if (grid->verts) {
@@ -1180,7 +1180,7 @@ void BPH_hair_volume_free_vertex_grid(HairGrid *grid)
}
}
-void BPH_hair_volume_grid_geometry(
+void SIM_hair_volume_grid_geometry(
HairGrid *grid, float *cellsize, int res[3], float gmin[3], float gmax[3])
{
if (cellsize) {
diff --git a/source/blender/physics/intern/implicit.h b/source/blender/simulation/intern/implicit.h
index 490e727b5f2..17dfb8b88b9 100644
--- a/source/blender/physics/intern/implicit.h
+++ b/source/blender/simulation/intern/implicit.h
@@ -65,83 +65,87 @@ BLI_INLINE void implicit_print_matrix_elem(float v)
printf("%-8.3f", v);
}
-void BPH_mass_spring_set_vertex_mass(struct Implicit_Data *data, int index, float mass);
-void BPH_mass_spring_set_rest_transform(struct Implicit_Data *data, int index, float rot[3][3]);
+void SIM_mass_spring_set_vertex_mass(struct Implicit_Data *data, int index, float mass);
+void SIM_mass_spring_set_rest_transform(struct Implicit_Data *data, int index, float rot[3][3]);
-void BPH_mass_spring_set_motion_state(struct Implicit_Data *data,
+void SIM_mass_spring_set_motion_state(struct Implicit_Data *data,
int index,
const float x[3],
const float v[3]);
-void BPH_mass_spring_set_position(struct Implicit_Data *data, int index, const float x[3]);
-void BPH_mass_spring_set_velocity(struct Implicit_Data *data, int index, const float v[3]);
-void BPH_mass_spring_get_motion_state(struct Implicit_Data *data,
+void SIM_mass_spring_set_position(struct Implicit_Data *data, int index, const float x[3]);
+void SIM_mass_spring_set_velocity(struct Implicit_Data *data, int index, const float v[3]);
+void SIM_mass_spring_get_motion_state(struct Implicit_Data *data,
int index,
float x[3],
float v[3]);
-void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]);
+void SIM_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]);
+void SIM_mass_spring_get_velocity(struct Implicit_Data *data, int index, float v[3]);
/* access to modified motion state during solver step */
-void BPH_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3]);
-void BPH_mass_spring_set_new_position(struct Implicit_Data *data, int index, const float x[3]);
-void BPH_mass_spring_get_new_velocity(struct Implicit_Data *data, int index, float v[3]);
-void BPH_mass_spring_set_new_velocity(struct Implicit_Data *data, int index, const float v[3]);
+void SIM_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3]);
+void SIM_mass_spring_set_new_position(struct Implicit_Data *data, int index, const float x[3]);
+void SIM_mass_spring_get_new_velocity(struct Implicit_Data *data, int index, float v[3]);
+void SIM_mass_spring_set_new_velocity(struct Implicit_Data *data, int index, const float v[3]);
-void BPH_mass_spring_clear_constraints(struct Implicit_Data *data);
-void BPH_mass_spring_add_constraint_ndof0(struct Implicit_Data *data,
+void SIM_mass_spring_clear_constraints(struct Implicit_Data *data);
+void SIM_mass_spring_add_constraint_ndof0(struct Implicit_Data *data,
int index,
const float dV[3]);
-void BPH_mass_spring_add_constraint_ndof1(struct Implicit_Data *data,
+void SIM_mass_spring_add_constraint_ndof1(struct Implicit_Data *data,
int index,
const float c1[3],
const float c2[3],
const float dV[3]);
-void BPH_mass_spring_add_constraint_ndof2(struct Implicit_Data *data,
+void SIM_mass_spring_add_constraint_ndof2(struct Implicit_Data *data,
int index,
const float c1[3],
const float dV[3]);
-bool BPH_mass_spring_solve_velocities(struct Implicit_Data *data,
+bool SIM_mass_spring_solve_velocities(struct Implicit_Data *data,
float dt,
struct ImplicitSolverResult *result);
-bool BPH_mass_spring_solve_positions(struct Implicit_Data *data, float dt);
-void BPH_mass_spring_apply_result(struct Implicit_Data *data);
+bool SIM_mass_spring_solve_positions(struct Implicit_Data *data, float dt);
+void SIM_mass_spring_apply_result(struct Implicit_Data *data);
/* Clear the force vector at the beginning of the time step */
-void BPH_mass_spring_clear_forces(struct Implicit_Data *data);
+void SIM_mass_spring_clear_forces(struct Implicit_Data *data);
/* Fictitious forces introduced by moving coordinate systems */
-void BPH_mass_spring_force_reference_frame(struct Implicit_Data *data,
+void SIM_mass_spring_force_reference_frame(struct Implicit_Data *data,
int index,
const float acceleration[3],
const float omega[3],
const float domega_dt[3],
float mass);
/* Simple uniform gravity force */
-void BPH_mass_spring_force_gravity(struct Implicit_Data *data,
+void SIM_mass_spring_force_gravity(struct Implicit_Data *data,
int index,
float mass,
const float g[3]);
/* Global drag force (velocity damping) */
-void BPH_mass_spring_force_drag(struct Implicit_Data *data, float drag);
+void SIM_mass_spring_force_drag(struct Implicit_Data *data, float drag);
/* Custom external force */
-void BPH_mass_spring_force_extern(
+void SIM_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 */
-void BPH_mass_spring_force_face_wind(
+/* Wind force, acting on a face (only generates pressure from the normal component) */
+void SIM_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 SIM_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,
+void SIM_mass_spring_force_edge_wind(struct Implicit_Data *data,
int v1,
int v2,
float radius1,
float radius2,
const float (*winvec)[3]);
/* Wind force, acting on a vertex */
-void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data,
+void SIM_mass_spring_force_vertex_wind(struct Implicit_Data *data,
int v,
float radius,
const float (*winvec)[3]);
/* Linear spring force between two points */
-bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data,
+bool SIM_mass_spring_force_spring_linear(struct Implicit_Data *data,
int i,
int j,
float restlen,
@@ -153,7 +157,7 @@ bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data,
bool new_compress,
float clamp_force);
/* Angular spring force between two polygons */
-bool BPH_mass_spring_force_spring_angular(struct Implicit_Data *data,
+bool SIM_mass_spring_force_spring_angular(struct Implicit_Data *data,
int i,
int j,
int *i_a,
@@ -164,10 +168,10 @@ bool BPH_mass_spring_force_spring_angular(struct Implicit_Data *data,
float stiffness,
float damping);
/* Bending force, forming a triangle at the base of two structural springs */
-bool BPH_mass_spring_force_spring_bending(
+bool SIM_mass_spring_force_spring_bending(
struct Implicit_Data *data, int i, int j, float restlen, float kb, float cb);
/* Angular bending force based on local target vectors */
-bool BPH_mass_spring_force_spring_bending_hair(struct Implicit_Data *data,
+bool SIM_mass_spring_force_spring_bending_hair(struct Implicit_Data *data,
int i,
int j,
int k,
@@ -175,21 +179,23 @@ bool BPH_mass_spring_force_spring_bending_hair(struct Implicit_Data *data,
float stiffness,
float damping);
/* Global goal spring */
-bool BPH_mass_spring_force_spring_goal(struct Implicit_Data *data,
+bool SIM_mass_spring_force_spring_goal(struct Implicit_Data *data,
int i,
const float goal_x[3],
const float goal_v[3],
float stiffness,
float damping);
-float BPH_tri_tetra_volume_signed_6x(struct Implicit_Data *data, int v1, int v2, int v3);
+float SIM_tri_tetra_volume_signed_6x(struct Implicit_Data *data, int v1, int v2, int v3);
+float SIM_tri_area(struct Implicit_Data *data, int v1, int v2, int v3);
-void BPH_mass_spring_force_pressure(struct Implicit_Data *data,
+void SIM_mass_spring_force_pressure(struct Implicit_Data *data,
int v1,
int v2,
int v3,
- float pressure_difference,
- float weights[3]);
+ float common_pressure,
+ const float *vertex_pressure,
+ const float weights[3]);
/* ======== Hair Volumetric Forces ======== */
@@ -197,16 +203,16 @@ struct HairGrid;
#define MAX_HAIR_GRID_RES 256
-struct HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize,
+struct HairGrid *SIM_hair_volume_create_vertex_grid(float cellsize,
const float gmin[3],
const float gmax[3]);
-void BPH_hair_volume_free_vertex_grid(struct HairGrid *grid);
-void BPH_hair_volume_grid_geometry(
+void SIM_hair_volume_free_vertex_grid(struct HairGrid *grid);
+void SIM_hair_volume_grid_geometry(
struct HairGrid *grid, float *cellsize, int res[3], float gmin[3], float gmax[3]);
-void BPH_hair_volume_grid_clear(struct HairGrid *grid);
-void BPH_hair_volume_add_vertex(struct HairGrid *grid, const float x[3], const float v[3]);
-void BPH_hair_volume_add_segment(struct HairGrid *grid,
+void SIM_hair_volume_grid_clear(struct HairGrid *grid);
+void SIM_hair_volume_add_vertex(struct HairGrid *grid, const float x[3], const float v[3]);
+void SIM_hair_volume_add_segment(struct HairGrid *grid,
const float x1[3],
const float v1[3],
const float x2[3],
@@ -219,17 +225,17 @@ void BPH_hair_volume_add_segment(struct HairGrid *grid,
const float dir2[3],
const float dir3[3]);
-void BPH_hair_volume_normalize_vertex_grid(struct HairGrid *grid);
+void SIM_hair_volume_normalize_vertex_grid(struct HairGrid *grid);
-bool BPH_hair_volume_solve_divergence(struct HairGrid *grid,
+bool SIM_hair_volume_solve_divergence(struct HairGrid *grid,
float dt,
float target_density,
float target_strength);
#if 0 /* XXX weighting is incorrect, disabled for now */
-void BPH_hair_volume_vertex_grid_filter_box(struct HairVertexGrid *grid, int kernel_size);
+void SIM_hair_volume_vertex_grid_filter_box(struct HairVertexGrid *grid, int kernel_size);
#endif
-void BPH_hair_volume_grid_interpolate(struct HairGrid *grid,
+void SIM_hair_volume_grid_interpolate(struct HairGrid *grid,
const float x[3],
float *density,
float velocity[3],
@@ -241,7 +247,7 @@ void BPH_hair_volume_grid_interpolate(struct HairGrid *grid,
* fluid_factor controls blending between PIC (Particle-in-Cell)
* and FLIP (Fluid-Implicit-Particle) methods (0 = only PIC, 1 = only FLIP)
*/
-void BPH_hair_volume_grid_velocity(
+void SIM_hair_volume_grid_velocity(
struct HairGrid *grid, const float x[3], const float v[3], float fluid_factor, float r_v[3]);
/* XXX Warning: expressing grid effects on velocity as a force is not very stable,
* due to discontinuities in interpolated values!
@@ -249,7 +255,7 @@ void BPH_hair_volume_grid_velocity(
* "Detail Preserving Continuum Simulation of Straight Hair"
* (McAdams, Selle 2009)
*/
-void BPH_hair_volume_vertex_grid_forces(struct HairGrid *grid,
+void SIM_hair_volume_vertex_grid_forces(struct HairGrid *grid,
const float x[3],
const float v[3],
float smoothfac,
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/simulation/intern/implicit_blender.c
index b1afaa52ef2..856572aa3f5 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/simulation/intern/implicit_blender.c
@@ -40,7 +40,7 @@
# include "BKE_collision.h"
# include "BKE_effect.h"
-# include "BPH_mass_spring.h"
+# include "SIM_mass_spring.h"
# ifdef __GNUC__
# pragma GCC diagnostic ignored "-Wtype-limits"
@@ -90,7 +90,7 @@ typedef struct fmatrix3x3 {
///////////////////////////
/* simple vector code */
/* STATUS: verified */
-DO_INLINE void mul_fvector_S(float to[3], float from[3], float scalar)
+DO_INLINE void mul_fvector_S(float to[3], const float from[3], float scalar)
{
to[0] = from[0] * scalar;
to[1] = from[1] * scalar;
@@ -429,7 +429,7 @@ DO_INLINE void mul_fmatrix_S(float matrix[3][3], float scalar)
/* a vector multiplied by a 3x3 matrix */
/* STATUS: verified */
-DO_INLINE void mul_fvector_fmatrix(float *to, float *from, float matrix[3][3])
+DO_INLINE void mul_fvector_fmatrix(float *to, const float *from, float matrix[3][3])
{
to[0] = matrix[0][0] * from[0] + matrix[1][0] * from[1] + matrix[2][0] * from[2];
to[1] = matrix[0][1] * from[0] + matrix[1][1] * from[1] + matrix[2][1] * from[2];
@@ -478,7 +478,7 @@ DO_INLINE void muladd_fmatrix_fvector(float to[3], float matrix[3][3], float fro
to[2] += dot_v3v3(matrix[2], from);
}
-DO_INLINE void muladd_fmatrixT_fvector(float to[3], float matrix[3][3], float from[3])
+DO_INLINE void muladd_fmatrixT_fvector(float to[3], float matrix[3][3], const float from[3])
{
to[0] += matrix[0][0] * from[0] + matrix[1][0] * from[1] + matrix[2][0] * from[2];
to[1] += matrix[0][1] * from[0] + matrix[1][1] * from[1] + matrix[2][1] * from[2];
@@ -679,7 +679,7 @@ typedef struct Implicit_Data {
fmatrix3x3 *P, *Pinv; /* pre-conditioning matrix */
} Implicit_Data;
-Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings)
+Implicit_Data *SIM_mass_spring_solver_create(int numverts, int numsprings)
{
Implicit_Data *id = (Implicit_Data *)MEM_callocN(sizeof(Implicit_Data), "implicit vecmat");
@@ -707,7 +707,7 @@ Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings)
return id;
}
-void BPH_mass_spring_solver_free(Implicit_Data *id)
+void SIM_mass_spring_solver_free(Implicit_Data *id)
{
del_bfmatrix(id->tfm);
del_bfmatrix(id->A);
@@ -924,8 +924,8 @@ static int cg_filtered(lfVector *ldV,
del_lfvector(s);
// printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount);
- result->status = conjgrad_loopcount < conjgrad_looplimit ? BPH_SOLVER_SUCCESS :
- BPH_SOLVER_NO_CONVERGENCE;
+ result->status = conjgrad_loopcount < conjgrad_looplimit ? SIM_SOLVER_SUCCESS :
+ SIM_SOLVER_NO_CONVERGENCE;
result->iterations = conjgrad_loopcount;
result->error = bnorm2 > 0.0f ? sqrtf(delta_new / bnorm2) : 0.0f;
@@ -1139,7 +1139,7 @@ static int cg_filtered_pre(lfVector *dv,
}
# endif
-bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSolverResult *result)
+bool SIM_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSolverResult *result)
{
unsigned int numverts = data->dFdV[0].vcount;
@@ -1173,10 +1173,10 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
del_lfvector(dFdXmV);
- return result->status == BPH_SOLVER_SUCCESS;
+ return result->status == SIM_SOLVER_SUCCESS;
}
-bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt)
+bool SIM_mass_spring_solve_positions(Implicit_Data *data, float dt)
{
int numverts = data->M[0].vcount;
@@ -1186,20 +1186,20 @@ bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt)
return true;
}
-void BPH_mass_spring_apply_result(Implicit_Data *data)
+void SIM_mass_spring_apply_result(Implicit_Data *data)
{
int numverts = data->M[0].vcount;
cp_lfvector(data->X, data->Xnew, numverts);
cp_lfvector(data->V, data->Vnew, numverts);
}
-void BPH_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass)
+void SIM_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass)
{
unit_m3(data->M[index].m);
mul_m3_fl(data->M[index].m, mass);
}
-void BPH_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tfm[3][3])
+void SIM_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tfm[3][3])
{
# ifdef CLOTH_ROOT_FRAME
copy_m3_m3(data->tfm[index].m, tfm);
@@ -1209,7 +1209,7 @@ void BPH_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tf
# endif
}
-void BPH_mass_spring_set_motion_state(Implicit_Data *data,
+void SIM_mass_spring_set_motion_state(Implicit_Data *data,
int index,
const float x[3],
const float v[3])
@@ -1218,17 +1218,17 @@ void BPH_mass_spring_set_motion_state(Implicit_Data *data,
world_to_root_v3(data, index, data->V[index], v);
}
-void BPH_mass_spring_set_position(Implicit_Data *data, int index, const float x[3])
+void SIM_mass_spring_set_position(Implicit_Data *data, int index, const float x[3])
{
world_to_root_v3(data, index, data->X[index], x);
}
-void BPH_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3])
+void SIM_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3])
{
world_to_root_v3(data, index, data->V[index], v);
}
-void BPH_mass_spring_get_motion_state(struct Implicit_Data *data,
+void SIM_mass_spring_get_motion_state(struct Implicit_Data *data,
int index,
float x[3],
float v[3])
@@ -1241,34 +1241,39 @@ void BPH_mass_spring_get_motion_state(struct Implicit_Data *data,
}
}
-void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3])
+void SIM_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3])
{
root_to_world_v3(data, index, x, data->X[index]);
}
-void BPH_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3])
+void SIM_mass_spring_get_velocity(struct Implicit_Data *data, int index, float v[3])
+{
+ root_to_world_v3(data, index, v, data->V[index]);
+}
+
+void SIM_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3])
{
root_to_world_v3(data, index, x, data->Xnew[index]);
}
-void BPH_mass_spring_set_new_position(struct Implicit_Data *data, int index, const float x[3])
+void SIM_mass_spring_set_new_position(struct Implicit_Data *data, int index, const float x[3])
{
world_to_root_v3(data, index, data->Xnew[index], x);
}
-void BPH_mass_spring_get_new_velocity(struct Implicit_Data *data, int index, float v[3])
+void SIM_mass_spring_get_new_velocity(struct Implicit_Data *data, int index, float v[3])
{
root_to_world_v3(data, index, v, data->Vnew[index]);
}
-void BPH_mass_spring_set_new_velocity(struct Implicit_Data *data, int index, const float v[3])
+void SIM_mass_spring_set_new_velocity(struct Implicit_Data *data, int index, const float v[3])
{
world_to_root_v3(data, index, data->Vnew[index], v);
}
/* -------------------------------- */
-static int BPH_mass_spring_add_block(Implicit_Data *data, int v1, int v2)
+static int SIM_mass_spring_add_block(Implicit_Data *data, int v1, int v2)
{
int s = data->M[0].vcount + data->num_blocks; /* index from array start */
BLI_assert(s < data->M[0].vcount + data->M[0].scount);
@@ -1286,7 +1291,7 @@ static int BPH_mass_spring_add_block(Implicit_Data *data, int v1, int v2)
return s;
}
-void BPH_mass_spring_clear_constraints(Implicit_Data *data)
+void SIM_mass_spring_clear_constraints(Implicit_Data *data)
{
int i, numverts = data->S[0].vcount;
for (i = 0; i < numverts; i++) {
@@ -1295,14 +1300,14 @@ void BPH_mass_spring_clear_constraints(Implicit_Data *data)
}
}
-void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
+void SIM_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
{
zero_m3(data->S[index].m);
world_to_root_v3(data, index, data->z[index], dV);
}
-void BPH_mass_spring_add_constraint_ndof1(
+void SIM_mass_spring_add_constraint_ndof1(
Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3])
{
float m[3][3], p[3], q[3], u[3], cmat[3][3];
@@ -1323,7 +1328,7 @@ void BPH_mass_spring_add_constraint_ndof1(
add_v3_v3(data->z[index], u);
}
-void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data,
+void SIM_mass_spring_add_constraint_ndof2(Implicit_Data *data,
int index,
const float c1[3],
const float dV[3])
@@ -1341,7 +1346,7 @@ void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data,
add_v3_v3(data->z[index], u);
}
-void BPH_mass_spring_clear_forces(Implicit_Data *data)
+void SIM_mass_spring_clear_forces(Implicit_Data *data)
{
int numverts = data->M[0].vcount;
zero_lfvector(data->F, numverts);
@@ -1351,7 +1356,7 @@ void BPH_mass_spring_clear_forces(Implicit_Data *data)
data->num_blocks = 0;
}
-void BPH_mass_spring_force_reference_frame(Implicit_Data *data,
+void SIM_mass_spring_force_reference_frame(Implicit_Data *data,
int index,
const float acceleration[3],
const float omega[3],
@@ -1406,7 +1411,7 @@ void BPH_mass_spring_force_reference_frame(Implicit_Data *data,
# endif
}
-void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, const float g[3])
+void SIM_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, const float g[3])
{
/* force = mass * acceleration (in this case: gravity) */
float f[3];
@@ -1416,7 +1421,7 @@ void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, c
add_v3_v3(data->F[index], f);
}
-void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
+void SIM_mass_spring_force_drag(Implicit_Data *data, float drag)
{
int i, numverts = data->M[0].vcount;
for (i = 0; i < numverts; i++) {
@@ -1431,7 +1436,7 @@ void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
}
}
-void BPH_mass_spring_force_extern(
+void SIM_mass_spring_force_extern(
struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3])
{
float tf[3], tdfdx[3][3], tdfdv[3][3];
@@ -1460,49 +1465,131 @@ static float calc_nor_area_tri(float nor[3],
/* XXX does not support force jacobians yet, since the effector system does not provide them either
*/
-void BPH_mass_spring_force_face_wind(
+void SIM_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);
+ }
+
+ /* 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];
- world_to_root_v3(data, v3, win, winvec[v3]);
- madd_v3_v3fl(data->F[v3], nor, factor * dot_v3v3(win, nor));
+ /* 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]);
}
-float BPH_tri_tetra_volume_signed_6x(Implicit_Data *data, int v1, int v2, int v3)
+void SIM_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 SIM_tri_tetra_volume_signed_6x(Implicit_Data *data, int v1, int v2, int v3)
{
/* The result will be 6x the volume */
return volume_tri_tetrahedron_signed_v3_6x(data->X[v1], data->X[v2], data->X[v3]);
}
-void BPH_mass_spring_force_pressure(
- Implicit_Data *data, int v1, int v2, int v3, float pressure_difference, float weights[3])
+float SIM_tri_area(struct Implicit_Data *data, int v1, int v2, int v3)
+{
+ float nor[3];
+
+ return calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]);
+}
+
+void SIM_mass_spring_force_pressure(Implicit_Data *data,
+ int v1,
+ int v2,
+ int v3,
+ float common_pressure,
+ const float *vertex_pressure,
+ const float weights[3])
{
float 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 = pressure_difference * area / 3.0f;
+ factor = area / 3.0f;
+ base_force = common_pressure * factor;
+
+ /* 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.
+ */
+ if (vertex_pressure) {
+ copy_v3_fl3(force, vertex_pressure[v1], vertex_pressure[v2], vertex_pressure[v3]);
+ mul_v3_fl(force, factor / 4.0f);
+
+ base_force += force[0] + force[1] + force[2];
+ }
+ else {
+ zero_v3(force);
+ }
/* add pressure to each of the face verts */
- madd_v3_v3fl(data->F[v1], nor, factor * weights[0]);
- madd_v3_v3fl(data->F[v2], nor, factor * weights[1]);
- madd_v3_v3fl(data->F[v3], nor, factor * weights[2]);
+ madd_v3_v3fl(data->F[v1], nor, (base_force + force[0]) * weights[0]);
+ madd_v3_v3fl(data->F[v2], nor, (base_force + force[1]) * weights[1]);
+ madd_v3_v3fl(data->F[v3], nor, (base_force + force[2]) * weights[2]);
}
static void edge_wind_vertex(const float dir[3],
@@ -1530,7 +1617,7 @@ static void edge_wind_vertex(const float dir[3],
mul_v3_v3fl(f, wind, density * cross_section);
}
-void BPH_mass_spring_force_edge_wind(
+void SIM_mass_spring_force_edge_wind(
Implicit_Data *data, int v1, int v2, float radius1, float radius2, const float (*winvec)[3])
{
float win[3], dir[3], length;
@@ -1548,7 +1635,7 @@ void BPH_mass_spring_force_edge_wind(
add_v3_v3(data->F[v2], f);
}
-void BPH_mass_spring_force_vertex_wind(Implicit_Data *data,
+void SIM_mass_spring_force_vertex_wind(Implicit_Data *data,
int v,
float UNUSED(radius),
const float (*winvec)[3])
@@ -1679,7 +1766,7 @@ BLI_INLINE bool spring_length(Implicit_Data *data,
BLI_INLINE void apply_spring(
Implicit_Data *data, int i, int j, const float f[3], float dfdx[3][3], float dfdv[3][3])
{
- int block_ij = BPH_mass_spring_add_block(data, i, j);
+ int block_ij = SIM_mass_spring_add_block(data, i, j);
add_v3_v3(data->F[i], f);
sub_v3_v3(data->F[j], f);
@@ -1693,7 +1780,7 @@ BLI_INLINE void apply_spring(
sub_m3_m3m3(data->dFdV[block_ij].m, data->dFdV[block_ij].m, dfdv);
}
-bool BPH_mass_spring_force_spring_linear(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_linear(Implicit_Data *data,
int i,
int j,
float restlen,
@@ -1754,7 +1841,7 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data,
}
/* See "Stable but Responsive Cloth" (Choi, Ko 2005) */
-bool BPH_mass_spring_force_spring_bending(
+bool SIM_mass_spring_force_spring_bending(
Implicit_Data *data, int i, int j, float restlen, float kb, float cb)
{
float extent[3], length, dir[3], vel[3];
@@ -1782,7 +1869,7 @@ bool BPH_mass_spring_force_spring_bending(
}
}
-BLI_INLINE void poly_avg(lfVector *data, int *inds, int len, float r_avg[3])
+BLI_INLINE void poly_avg(lfVector *data, const int *inds, int len, float r_avg[3])
{
float fact = 1.0f / (float)len;
@@ -1861,7 +1948,7 @@ BLI_INLINE void spring_angle(Implicit_Data *data,
/* Angular springs roughly based on the bending model proposed by Baraff and Witkin in "Large Steps
* in Cloth Simulation". */
-bool BPH_mass_spring_force_spring_angular(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_angular(Implicit_Data *data,
int i,
int j,
int *i_a,
@@ -2082,7 +2169,7 @@ BLI_INLINE void spring_hairbend_estimate_dfdv(Implicit_Data *data,
/* Angular spring that pulls the vertex toward the local target
* See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a)
*/
-bool BPH_mass_spring_force_spring_bending_hair(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_bending_hair(Implicit_Data *data,
int i,
int j,
int k,
@@ -2097,9 +2184,9 @@ bool BPH_mass_spring_force_spring_bending_hair(Implicit_Data *data,
const float vecnull[3] = {0.0f, 0.0f, 0.0f};
- int block_ij = BPH_mass_spring_add_block(data, i, j);
- int block_jk = BPH_mass_spring_add_block(data, j, k);
- int block_ik = BPH_mass_spring_add_block(data, i, k);
+ int block_ij = SIM_mass_spring_add_block(data, i, j);
+ int block_jk = SIM_mass_spring_add_block(data, j, k);
+ int block_ik = SIM_mass_spring_add_block(data, i, k);
world_to_root_v3(data, j, goal, target);
@@ -2231,7 +2318,7 @@ bool BPH_mass_spring_force_spring_bending_hair(Implicit_Data *data,
return true;
}
-bool BPH_mass_spring_force_spring_goal(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_goal(Implicit_Data *data,
int i,
const float goal_x[3],
const float goal_v[3],
diff --git a/source/blender/physics/intern/implicit_eigen.cpp b/source/blender/simulation/intern/implicit_eigen.cpp
index 58538c13116..15b9e30e32a 100644
--- a/source/blender/physics/intern/implicit_eigen.cpp
+++ b/source/blender/simulation/intern/implicit_eigen.cpp
@@ -78,7 +78,7 @@ extern "C" {
# include "BKE_effect.h"
# include "BKE_global.h"
-# include "BPH_mass_spring.h"
+# include "SIM_mass_spring.h"
}
typedef float Scalar;
@@ -460,20 +460,20 @@ struct Implicit_Data {
lMatrixCtor iS; /* filtering matrix for constraints */
};
-Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings)
+Implicit_Data *SIM_mass_spring_solver_create(int numverts, int numsprings)
{
Implicit_Data *id = new Implicit_Data(numverts);
return id;
}
-void BPH_mass_spring_solver_free(Implicit_Data *id)
+void SIM_mass_spring_solver_free(Implicit_Data *id)
{
if (id) {
delete id;
}
}
-int BPH_mass_spring_solver_numvert(Implicit_Data *id)
+int SIM_mass_spring_solver_numvert(Implicit_Data *id)
{
if (id) {
return id->numverts;
@@ -511,7 +511,7 @@ BLI_INLINE void root_to_world_m3(Implicit_Data *data, int index, float r[3][3],
/* ================================ */
-bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSolverResult *result)
+bool SIM_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSolverResult *result)
{
# ifdef USE_EIGEN_CORE
typedef ConjugateGradient solver_t;
@@ -566,16 +566,16 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
switch (cg.info()) {
case Eigen::Success:
- result->status = BPH_SOLVER_SUCCESS;
+ result->status = SIM_SOLVER_SUCCESS;
break;
case Eigen::NoConvergence:
- result->status = BPH_SOLVER_NO_CONVERGENCE;
+ result->status = SIM_SOLVER_NO_CONVERGENCE;
break;
case Eigen::InvalidInput:
- result->status = BPH_SOLVER_INVALID_INPUT;
+ result->status = SIM_SOLVER_INVALID_INPUT;
break;
case Eigen::NumericalIssue:
- result->status = BPH_SOLVER_NUMERICAL_ISSUE;
+ result->status = SIM_SOLVER_NUMERICAL_ISSUE;
break;
}
@@ -585,7 +585,7 @@ bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSol
return cg.info() == Eigen::Success;
}
-bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt)
+bool SIM_mass_spring_solve_positions(Implicit_Data *data, float dt)
{
data->Xnew = data->X + data->Vnew * dt;
return true;
@@ -593,13 +593,13 @@ bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt)
/* ================================ */
-void BPH_mass_spring_apply_result(Implicit_Data *data)
+void SIM_mass_spring_apply_result(Implicit_Data *data)
{
data->X = data->Xnew;
data->V = data->Vnew;
}
-void BPH_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass)
+void SIM_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass)
{
float m[3][3];
copy_m3_m3(m, I);
@@ -607,7 +607,7 @@ void BPH_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass)
data->iM.add(index, index, m);
}
-void BPH_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tfm[3][3])
+void SIM_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tfm[3][3])
{
# ifdef CLOTH_ROOT_FRAME
copy_m3_m3(data->tfm[index], tfm);
@@ -617,7 +617,7 @@ void BPH_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tf
# endif
}
-void BPH_mass_spring_set_motion_state(Implicit_Data *data,
+void SIM_mass_spring_set_motion_state(Implicit_Data *data,
int index,
const float x[3],
const float v[3])
@@ -626,17 +626,17 @@ void BPH_mass_spring_set_motion_state(Implicit_Data *data,
world_to_root_v3(data, index, data->V.v3(index), v);
}
-void BPH_mass_spring_set_position(Implicit_Data *data, int index, const float x[3])
+void SIM_mass_spring_set_position(Implicit_Data *data, int index, const float x[3])
{
world_to_root_v3(data, index, data->X.v3(index), x);
}
-void BPH_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3])
+void SIM_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3])
{
world_to_root_v3(data, index, data->V.v3(index), v);
}
-void BPH_mass_spring_get_motion_state(struct Implicit_Data *data,
+void SIM_mass_spring_get_motion_state(struct Implicit_Data *data,
int index,
float x[3],
float v[3])
@@ -649,22 +649,22 @@ void BPH_mass_spring_get_motion_state(struct Implicit_Data *data,
}
}
-void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3])
+void SIM_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3])
{
root_to_world_v3(data, index, x, data->X.v3(index));
}
-void BPH_mass_spring_get_new_velocity(Implicit_Data *data, int index, float v[3])
+void SIM_mass_spring_get_new_velocity(Implicit_Data *data, int index, float v[3])
{
root_to_world_v3(data, index, v, data->V.v3(index));
}
-void BPH_mass_spring_set_new_velocity(Implicit_Data *data, int index, const float v[3])
+void SIM_mass_spring_set_new_velocity(Implicit_Data *data, int index, const float v[3])
{
world_to_root_v3(data, index, data->V.v3(index), v);
}
-void BPH_mass_spring_clear_constraints(Implicit_Data *data)
+void SIM_mass_spring_clear_constraints(Implicit_Data *data)
{
int numverts = data->numverts;
for (int i = 0; i < numverts; i++) {
@@ -673,14 +673,14 @@ void BPH_mass_spring_clear_constraints(Implicit_Data *data)
}
}
-void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
+void SIM_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3])
{
data->iS.sub(index, index, I);
world_to_root_v3(data, index, data->z.v3(index), dV);
}
-void BPH_mass_spring_add_constraint_ndof1(
+void SIM_mass_spring_add_constraint_ndof1(
Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3])
{
float m[3][3], p[3], q[3], u[3], cmat[3][3];
@@ -701,7 +701,7 @@ void BPH_mass_spring_add_constraint_ndof1(
add_v3_v3(data->z.v3(index), u);
}
-void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data,
+void SIM_mass_spring_add_constraint_ndof2(Implicit_Data *data,
int index,
const float c1[3],
const float dV[3])
@@ -719,14 +719,14 @@ void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data,
add_v3_v3(data->z.v3(index), u);
}
-void BPH_mass_spring_clear_forces(Implicit_Data *data)
+void SIM_mass_spring_clear_forces(Implicit_Data *data)
{
data->F.setZero();
data->dFdX.setZero();
data->dFdV.setZero();
}
-void BPH_mass_spring_force_reference_frame(Implicit_Data *data,
+void SIM_mass_spring_force_reference_frame(Implicit_Data *data,
int index,
const float acceleration[3],
const float omega[3],
@@ -781,7 +781,7 @@ void BPH_mass_spring_force_reference_frame(Implicit_Data *data,
# endif
}
-void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, const float g[3])
+void SIM_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, const float g[3])
{
/* force = mass * acceleration (in this case: gravity) */
float f[3];
@@ -791,7 +791,7 @@ void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, c
add_v3_v3(data->F.v3(index), f);
}
-void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
+void SIM_mass_spring_force_drag(Implicit_Data *data, float drag)
{
int numverts = data->numverts;
for (int i = 0; i < numverts; i++) {
@@ -806,7 +806,7 @@ void BPH_mass_spring_force_drag(Implicit_Data *data, float drag)
}
}
-void BPH_mass_spring_force_extern(
+void SIM_mass_spring_force_extern(
struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3])
{
float tf[3], tdfdx[3][3], tdfdv[3][3];
@@ -835,7 +835,7 @@ static float calc_nor_area_tri(float nor[3],
/* XXX does not support force jacobians yet,
* since the effector system does not provide them either. */
-void BPH_mass_spring_force_face_wind(
+void SIM_mass_spring_force_face_wind(
Implicit_Data *data, int v1, int v2, int v3, const float (*winvec)[3])
{
const float effector_scale = 0.02f;
@@ -856,7 +856,7 @@ void BPH_mass_spring_force_face_wind(
madd_v3_v3fl(data->F.v3(v3), nor, factor * dot_v3v3(win, nor));
}
-void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, const float (*winvec)[3])
+void SIM_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, const float (*winvec)[3])
{
const float effector_scale = 0.01;
float win[3], dir[3], nor[3], length;
@@ -1000,7 +1000,7 @@ BLI_INLINE void apply_spring(
data->idFdV.sub(j, i, dfdv);
}
-bool BPH_mass_spring_force_spring_linear(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_linear(Implicit_Data *data,
int i,
int j,
float restlen,
@@ -1063,7 +1063,7 @@ bool BPH_mass_spring_force_spring_linear(Implicit_Data *data,
}
/* See "Stable but Responsive Cloth" (Choi, Ko 2005) */
-bool BPH_mass_spring_force_spring_bending(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_bending(Implicit_Data *data,
int i,
int j,
float restlen,
@@ -1293,7 +1293,7 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data,
/* Angular spring that pulls the vertex toward the local target
* See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a)
*/
-bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_bending_angular(Implicit_Data *data,
int i,
int j,
int k,
@@ -1444,7 +1444,7 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data,
return true;
}
-bool BPH_mass_spring_force_spring_goal(Implicit_Data *data,
+bool SIM_mass_spring_force_spring_goal(Implicit_Data *data,
int i,
const float goal_x[3],
const float goal_v[3],
diff --git a/source/blender/simulation/intern/particle_allocator.cc b/source/blender/simulation/intern/particle_allocator.cc
new file mode 100644
index 00000000000..e47a6354d81
--- /dev/null
+++ b/source/blender/simulation/intern/particle_allocator.cc
@@ -0,0 +1,86 @@
+/*
+ * 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 "particle_allocator.hh"
+
+#include "BLI_rand.hh"
+
+namespace blender::sim {
+
+AttributesAllocator::~AttributesAllocator()
+{
+ for (std::unique_ptr<AttributesBlock> &block : allocated_blocks_) {
+ for (int i : attributes_info_.index_range()) {
+ const fn::CPPType &type = attributes_info_.type_of(i);
+ type.destruct_n(block->buffers[i], block->size);
+ MEM_freeN(block->buffers[i]);
+ }
+ }
+}
+
+fn::MutableAttributesRef AttributesAllocator::allocate_uninitialized(int size)
+{
+ std::unique_ptr<AttributesBlock> block = std::make_unique<AttributesBlock>();
+ block->buffers = Array<void *>(attributes_info_.size(), nullptr);
+ block->size = size;
+
+ for (int i : attributes_info_.index_range()) {
+ const fn::CPPType &type = attributes_info_.type_of(i);
+ void *buffer = MEM_mallocN_aligned(size * type.size(), type.alignment(), AT);
+ block->buffers[i] = buffer;
+ }
+
+ fn::MutableAttributesRef attributes{attributes_info_, block->buffers, size};
+
+ {
+ std::lock_guard lock{mutex_};
+ allocated_blocks_.append(std::move(block));
+ allocated_attributes_.append(attributes);
+ total_allocated_ += size;
+ }
+
+ return attributes;
+}
+
+fn::MutableAttributesRef ParticleAllocator::allocate(int size)
+{
+ const fn::AttributesInfo &info = attributes_allocator_.attributes_info();
+ fn::MutableAttributesRef attributes = attributes_allocator_.allocate_uninitialized(size);
+ for (int i : info.index_range()) {
+ const fn::CPPType &type = info.type_of(i);
+ StringRef name = info.name_of(i);
+ if (name == "ID") {
+ int start_id = next_id_.fetch_add(size);
+ MutableSpan<int> ids = attributes.get<int>("ID");
+ for (int pindex : IndexRange(size)) {
+ ids[pindex] = start_id + pindex;
+ }
+ }
+ else if (name == "Hash") {
+ MutableSpan<int> hashes = attributes.get<int>("Hash");
+ RandomNumberGenerator rng(hash_seed_ ^ (uint32_t)next_id_);
+ for (int pindex : IndexRange(size)) {
+ hashes[pindex] = (int)rng.get_uint32();
+ }
+ }
+ else {
+ type.fill_uninitialized(info.default_of(i), attributes.get(i).data(), size);
+ }
+ }
+ return attributes;
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh
new file mode 100644
index 00000000000..1c412508fe6
--- /dev/null
+++ b/source/blender/simulation/intern/particle_allocator.hh
@@ -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.
+ */
+
+#ifndef __SIM_PARTICLE_ALLOCATOR_HH__
+#define __SIM_PARTICLE_ALLOCATOR_HH__
+
+#include "BLI_array.hh"
+#include "BLI_vector.hh"
+
+#include "FN_attributes_ref.hh"
+
+#include <atomic>
+#include <mutex>
+
+namespace blender::sim {
+
+class AttributesAllocator : NonCopyable, NonMovable {
+ private:
+ struct AttributesBlock {
+ Array<void *> buffers;
+ int size;
+ };
+
+ const fn::AttributesInfo &attributes_info_;
+ Vector<std::unique_ptr<AttributesBlock>> allocated_blocks_;
+ Vector<fn::MutableAttributesRef> allocated_attributes_;
+ int total_allocated_ = 0;
+ std::mutex mutex_;
+
+ public:
+ AttributesAllocator(const fn::AttributesInfo &attributes_info)
+ : attributes_info_(attributes_info)
+ {
+ }
+
+ ~AttributesAllocator();
+
+ Span<fn::MutableAttributesRef> get_allocations() const
+ {
+ return allocated_attributes_;
+ }
+
+ int total_allocated() const
+ {
+ return total_allocated_;
+ }
+
+ const fn::AttributesInfo &attributes_info() const
+ {
+ return attributes_info_;
+ }
+
+ fn::MutableAttributesRef allocate_uninitialized(int size);
+};
+
+class ParticleAllocator : NonCopyable, NonMovable {
+ private:
+ AttributesAllocator attributes_allocator_;
+ std::atomic<int> next_id_;
+ uint32_t hash_seed_;
+
+ public:
+ ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id, uint32_t hash_seed)
+ : attributes_allocator_(attributes_info), next_id_(next_id), hash_seed_(hash_seed)
+ {
+ }
+
+ const fn::AttributesInfo &attributes_info() const
+ {
+ return attributes_allocator_.attributes_info();
+ }
+
+ Span<fn::MutableAttributesRef> get_allocations() const
+ {
+ return attributes_allocator_.get_allocations();
+ }
+
+ int total_allocated() const
+ {
+ return attributes_allocator_.total_allocated();
+ }
+
+ fn::MutableAttributesRef allocate(int size);
+};
+
+} // namespace blender::sim
+
+#endif /* __SIM_PARTICLE_ALLOCATOR_HH__ */
diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc
new file mode 100644
index 00000000000..035e6d50e21
--- /dev/null
+++ b/source/blender/simulation/intern/particle_function.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "particle_function.hh"
+
+namespace blender::sim {
+
+ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn,
+ const fn::MultiFunction *per_particle_fn,
+ Span<const ParticleFunctionInput *> global_inputs,
+ Span<const ParticleFunctionInput *> per_particle_inputs,
+ Span<bool> output_is_global)
+ : global_fn_(global_fn),
+ per_particle_fn_(per_particle_fn),
+ global_inputs_(global_inputs),
+ per_particle_inputs_(per_particle_inputs),
+ output_is_global_(output_is_global)
+{
+ for (int i : output_is_global_.index_range()) {
+ if (output_is_global_[i]) {
+ int param_index = global_inputs_.size() + global_output_indices_.size();
+ fn::MFParamType param_type = global_fn_->param_type(param_index);
+ BLI_assert(param_type.is_output());
+ output_types_.append(param_type.data_type());
+ output_names_.append(global_fn_->param_name(param_index));
+ global_output_indices_.append(i);
+ }
+ else {
+ int param_index = per_particle_inputs_.size() + per_particle_output_indices_.size();
+ fn::MFParamType param_type = per_particle_fn_->param_type(param_index);
+ BLI_assert(param_type.is_output());
+ output_types_.append(param_type.data_type());
+ output_names_.append(per_particle_fn_->param_name(param_index));
+ per_particle_output_indices_.append(i);
+ }
+ }
+}
+
+ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn,
+ const SimulationSolveContext &solve_context,
+ const ParticleChunkContext &particles)
+ : particle_fn_(particle_fn),
+ solve_context_(solve_context),
+ particles_(particles),
+ mask_(particles_.index_mask),
+ outputs_(particle_fn_.output_types_.size(), nullptr)
+{
+ global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map);
+ per_particle_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map);
+}
+
+ParticleFunctionEvaluator::~ParticleFunctionEvaluator()
+{
+ for (int output_index : outputs_.index_range()) {
+ void *buffer = outputs_[output_index];
+ fn::MFDataType data_type = particle_fn_.output_types_[output_index];
+ BLI_assert(data_type.is_single()); /* For now. */
+ const fn::CPPType &type = data_type.single_type();
+
+ if (particle_fn_.output_is_global_[output_index]) {
+ type.destruct(buffer);
+ }
+ else {
+ type.destruct_indices(outputs_[0], mask_);
+ }
+ }
+}
+
+void ParticleFunctionEvaluator::compute()
+{
+ BLI_assert(!is_computed_);
+ this->compute_globals();
+ this->compute_per_particle();
+ is_computed_ = true;
+}
+
+fn::GVSpan ParticleFunctionEvaluator::get(int output_index, StringRef expected_name) const
+{
+#ifdef DEBUG
+ if (expected_name != "") {
+ StringRef real_name = particle_fn_.output_names_[output_index];
+ BLI_assert(expected_name == real_name);
+ }
+ BLI_assert(is_computed_);
+#endif
+ UNUSED_VARS_NDEBUG(expected_name);
+ const void *buffer = outputs_[output_index];
+ const fn::CPPType &type = particle_fn_.output_types_[output_index].single_type();
+ if (particle_fn_.output_is_global_[output_index]) {
+ return fn::GVSpan::FromSingleWithMaxSize(type, buffer);
+ }
+ else {
+ return fn::GVSpan(fn::GSpan(type, buffer, mask_.min_array_size()));
+ }
+}
+
+void ParticleFunctionEvaluator::compute_globals()
+{
+ if (particle_fn_.global_fn_ == nullptr) {
+ return;
+ }
+
+ fn::MFParamsBuilder params(*particle_fn_.global_fn_, mask_.min_array_size());
+
+ /* Add input parameters. */
+ ParticleFunctionInputContext input_context{solve_context_, particles_};
+ for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) {
+ input->add_input(input_context, params, resources_);
+ }
+
+ /* Add output parameters. */
+ for (int output_index : particle_fn_.global_output_indices_) {
+ fn::MFDataType data_type = particle_fn_.output_types_[output_index];
+ BLI_assert(data_type.is_single()); /* For now. */
+
+ const fn::CPPType &type = data_type.single_type();
+ void *buffer = resources_.linear_allocator().allocate(type.size(), type.alignment());
+ params.add_uninitialized_single_output(fn::GMutableSpan(type, buffer, 1));
+ outputs_[output_index] = buffer;
+ }
+
+ particle_fn_.global_fn_->call({0}, params, global_context_);
+}
+
+void ParticleFunctionEvaluator::compute_per_particle()
+{
+ if (particle_fn_.per_particle_fn_ == nullptr) {
+ return;
+ }
+
+ fn::MFParamsBuilder params(*particle_fn_.per_particle_fn_, mask_.min_array_size());
+
+ /* Add input parameters. */
+ ParticleFunctionInputContext input_context{solve_context_, particles_};
+ for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) {
+ input->add_input(input_context, params, resources_);
+ }
+
+ /* Add output parameters. */
+ for (int output_index : particle_fn_.per_particle_output_indices_) {
+ fn::MFDataType data_type = particle_fn_.output_types_[output_index];
+ BLI_assert(data_type.is_single()); /* For now. */
+
+ const fn::CPPType &type = data_type.single_type();
+ void *buffer = resources_.linear_allocator().allocate(type.size() * mask_.min_array_size(),
+ type.alignment());
+ params.add_uninitialized_single_output(fn::GMutableSpan(type, buffer, mask_.min_array_size()));
+ outputs_[output_index] = buffer;
+ }
+
+ particle_fn_.per_particle_fn_->call(mask_, params, global_context_);
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/particle_function.hh b/source/blender/simulation/intern/particle_function.hh
new file mode 100644
index 00000000000..c9b35640b47
--- /dev/null
+++ b/source/blender/simulation/intern/particle_function.hh
@@ -0,0 +1,97 @@
+/*
+ * 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 __SIM_PARTICLE_FUNCTION_HH__
+#define __SIM_PARTICLE_FUNCTION_HH__
+
+#include "FN_attributes_ref.hh"
+#include "FN_multi_function.hh"
+
+#include "BLI_resource_collector.hh"
+
+#include "simulation_solver.hh"
+
+namespace blender::sim {
+
+struct ParticleFunctionInputContext {
+ const SimulationSolveContext &solve_context;
+ const ParticleChunkContext &particles;
+};
+
+class ParticleFunctionInput {
+ public:
+ virtual ~ParticleFunctionInput() = default;
+ virtual void add_input(ParticleFunctionInputContext &context,
+ fn::MFParamsBuilder &params,
+ ResourceCollector &resources) const = 0;
+};
+
+class ParticleFunction {
+ private:
+ const fn::MultiFunction *global_fn_;
+ const fn::MultiFunction *per_particle_fn_;
+ Array<const ParticleFunctionInput *> global_inputs_;
+ Array<const ParticleFunctionInput *> per_particle_inputs_;
+ Array<bool> output_is_global_;
+ Vector<int> global_output_indices_;
+ Vector<int> per_particle_output_indices_;
+ Vector<fn::MFDataType> output_types_;
+ Vector<StringRefNull> output_names_;
+
+ friend class ParticleFunctionEvaluator;
+
+ public:
+ ParticleFunction(const fn::MultiFunction *global_fn,
+ const fn::MultiFunction *per_particle_fn,
+ Span<const ParticleFunctionInput *> global_inputs,
+ Span<const ParticleFunctionInput *> per_particle_inputs,
+ Span<bool> output_is_global);
+};
+
+class ParticleFunctionEvaluator {
+ private:
+ ResourceCollector resources_;
+ const ParticleFunction &particle_fn_;
+ const SimulationSolveContext &solve_context_;
+ const ParticleChunkContext &particles_;
+ IndexMask mask_;
+ fn::MFContextBuilder global_context_;
+ fn::MFContextBuilder per_particle_context_;
+ Vector<void *> outputs_;
+ bool is_computed_ = false;
+
+ public:
+ ParticleFunctionEvaluator(const ParticleFunction &particle_fn,
+ const SimulationSolveContext &solve_context,
+ const ParticleChunkContext &particles);
+ ~ParticleFunctionEvaluator();
+
+ void compute();
+ fn::GVSpan get(int output_index, StringRef expected_name = "") const;
+
+ template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name = "") const
+ {
+ return this->get(output_index, expected_name).typed<T>();
+ }
+
+ private:
+ void compute_globals();
+ void compute_per_particle();
+};
+
+} // namespace blender::sim
+
+#endif /* __SIM_PARTICLE_FUNCTION_HH__ */
diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc
new file mode 100644
index 00000000000..c1482a29cb7
--- /dev/null
+++ b/source/blender/simulation/intern/particle_mesh_emitter.cc
@@ -0,0 +1,352 @@
+/*
+ * 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 "particle_mesh_emitter.hh"
+
+#include "BLI_float4x4.hh"
+#include "BLI_rand.hh"
+#include "BLI_vector_adaptor.hh"
+
+#include "BKE_mesh_runtime.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+namespace blender::sim {
+
+ParticleMeshEmitter::~ParticleMeshEmitter() = default;
+
+struct EmitterSettings {
+ Object *object;
+ float rate;
+};
+
+static BLI_NOINLINE void compute_birth_times(float rate,
+ TimeInterval emit_interval,
+ ParticleMeshEmitterSimulationState &state,
+ Vector<float> &r_birth_times)
+{
+ const float time_between_particles = 1.0f / rate;
+ int counter = 0;
+ while (true) {
+ counter++;
+ const float time_offset = counter * time_between_particles;
+ const float birth_time = state.last_birth_time + time_offset;
+ if (birth_time > emit_interval.stop()) {
+ break;
+ }
+ if (birth_time <= emit_interval.start()) {
+ continue;
+ }
+ r_birth_times.append(birth_time);
+ }
+}
+
+static BLI_NOINLINE Span<MLoopTri> get_mesh_triangles(Mesh &mesh)
+{
+ const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(&mesh);
+ int amount = BKE_mesh_runtime_looptri_len(&mesh);
+ return Span(triangles, amount);
+}
+
+static BLI_NOINLINE void compute_triangle_areas(Mesh &mesh,
+ Span<MLoopTri> triangles,
+ MutableSpan<float> r_areas)
+{
+ assert_same_size(triangles, r_areas);
+
+ for (int i : triangles.index_range()) {
+ const MLoopTri &tri = triangles[i];
+
+ const float3 v1 = mesh.mvert[mesh.mloop[tri.tri[0]].v].co;
+ const float3 v2 = mesh.mvert[mesh.mloop[tri.tri[1]].v].co;
+ const float3 v3 = mesh.mvert[mesh.mloop[tri.tri[2]].v].co;
+
+ const float area = area_tri_v3(v1, v2, v3);
+ r_areas[i] = area;
+ }
+}
+
+static BLI_NOINLINE void compute_triangle_weights(Mesh &mesh,
+ Span<MLoopTri> triangles,
+ MutableSpan<float> r_weights)
+{
+ assert_same_size(triangles, r_weights);
+ compute_triangle_areas(mesh, triangles, r_weights);
+}
+
+static BLI_NOINLINE void compute_cumulative_distribution(Span<float> weights,
+ MutableSpan<float> r_cumulative_weights)
+{
+ BLI_assert(weights.size() + 1 == r_cumulative_weights.size());
+
+ r_cumulative_weights[0] = 0;
+ for (int i : weights.index_range()) {
+ r_cumulative_weights[i + 1] = r_cumulative_weights[i] + weights[i];
+ }
+}
+
+static void sample_cumulative_distribution_recursive(RandomNumberGenerator &rng,
+ int amount,
+ int start,
+ int one_after_end,
+ Span<float> cumulative_weights,
+ VectorAdaptor<int> &r_sampled_indices)
+{
+ BLI_assert(start <= one_after_end);
+ const int size = one_after_end - start;
+ if (size == 0) {
+ BLI_assert(amount == 0);
+ }
+ else if (amount == 0) {
+ return;
+ }
+ else if (size == 1) {
+ r_sampled_indices.append_n_times(start, amount);
+ }
+ else {
+ const int middle = start + size / 2;
+ const float left_weight = cumulative_weights[middle] - cumulative_weights[start];
+ const float right_weight = cumulative_weights[one_after_end] - cumulative_weights[middle];
+ BLI_assert(left_weight >= 0.0f && right_weight >= 0.0f);
+ const float weight_sum = left_weight + right_weight;
+ BLI_assert(weight_sum > 0.0f);
+
+ const float left_factor = left_weight / weight_sum;
+ const float right_factor = right_weight / weight_sum;
+
+ int left_amount = amount * left_factor;
+ int right_amount = amount * right_factor;
+
+ if (left_amount + right_amount < amount) {
+ BLI_assert(left_amount + right_amount + 1 == amount);
+ const float weight_per_item = weight_sum / amount;
+ const float total_remaining_weight = weight_sum -
+ (left_amount + right_amount) * weight_per_item;
+ const float left_remaining_weight = left_weight - left_amount * weight_per_item;
+ const float left_remaining_factor = left_remaining_weight / total_remaining_weight;
+ if (rng.get_float() < left_remaining_factor) {
+ left_amount++;
+ }
+ else {
+ right_amount++;
+ }
+ }
+
+ sample_cumulative_distribution_recursive(
+ rng, left_amount, start, middle, cumulative_weights, r_sampled_indices);
+ sample_cumulative_distribution_recursive(
+ rng, right_amount, middle, one_after_end, cumulative_weights, r_sampled_indices);
+ }
+}
+
+static BLI_NOINLINE void sample_cumulative_distribution(RandomNumberGenerator &rng,
+ Span<float> cumulative_weights,
+ MutableSpan<int> r_samples)
+{
+ VectorAdaptor<int> sampled_indices(r_samples);
+ sample_cumulative_distribution_recursive(rng,
+ r_samples.size(),
+ 0,
+ cumulative_weights.size() - 1,
+ cumulative_weights,
+ sampled_indices);
+ BLI_assert(sampled_indices.is_full());
+}
+
+static BLI_NOINLINE bool sample_weighted_buckets(RandomNumberGenerator &rng,
+ Span<float> weights,
+ MutableSpan<int> r_samples)
+{
+ Array<float> cumulative_weights(weights.size() + 1);
+ compute_cumulative_distribution(weights, cumulative_weights);
+
+ if (r_samples.size() > 0 && cumulative_weights.as_span().last() == 0.0f) {
+ /* All weights are zero. */
+ return false;
+ }
+
+ sample_cumulative_distribution(rng, cumulative_weights, r_samples);
+ return true;
+}
+
+static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng,
+ Mesh &mesh,
+ Span<MLoopTri> triangles,
+ Span<int> triangles_to_sample,
+ MutableSpan<float3> r_sampled_positions,
+ MutableSpan<float3> r_sampled_normals)
+{
+ assert_same_size(triangles_to_sample, r_sampled_positions, r_sampled_normals);
+
+ MLoop *loops = mesh.mloop;
+ MVert *verts = mesh.mvert;
+
+ for (uint i : triangles_to_sample.index_range()) {
+ const uint triangle_index = triangles_to_sample[i];
+ const MLoopTri &triangle = triangles[triangle_index];
+
+ const float3 v1 = verts[loops[triangle.tri[0]].v].co;
+ const float3 v2 = verts[loops[triangle.tri[1]].v].co;
+ const float3 v3 = verts[loops[triangle.tri[2]].v].co;
+
+ const float3 bary_coords = rng.get_barycentric_coordinates();
+
+ float3 position;
+ interp_v3_v3v3v3(position, v1, v2, v3, bary_coords);
+
+ float3 normal;
+ normal_tri_v3(normal, v1, v2, v3);
+
+ r_sampled_positions[i] = position;
+ r_sampled_normals[i] = normal;
+ }
+}
+
+static BLI_NOINLINE bool compute_new_particle_attributes(ParticleEmitterContext &context,
+ EmitterSettings &settings,
+ ParticleMeshEmitterSimulationState &state,
+ Vector<float3> &r_positions,
+ Vector<float3> &r_velocities,
+ Vector<float> &r_birth_times)
+{
+ if (settings.object == nullptr) {
+ return false;
+ }
+ if (settings.rate <= 0.000001f) {
+ return false;
+ }
+ if (settings.object->type != OB_MESH) {
+ return false;
+ }
+ Mesh &mesh = *(Mesh *)settings.object->data;
+ if (mesh.totvert == 0) {
+ return false;
+ }
+
+ const float start_time = context.emit_interval.start();
+ const uint32_t seed = DefaultHash<StringRef>{}(state.head.name);
+ RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed};
+
+ compute_birth_times(settings.rate, context.emit_interval, state, r_birth_times);
+ const int particle_amount = r_birth_times.size();
+ if (particle_amount == 0) {
+ return false;
+ }
+
+ const float last_birth_time = r_birth_times.last();
+ rng.shuffle(r_birth_times.as_mutable_span());
+
+ Span<MLoopTri> triangles = get_mesh_triangles(mesh);
+ if (triangles.is_empty()) {
+ return false;
+ }
+
+ Array<float> triangle_weights(triangles.size());
+ compute_triangle_weights(mesh, triangles, triangle_weights);
+
+ Array<int> triangles_to_sample(particle_amount);
+ if (!sample_weighted_buckets(rng, triangle_weights, triangles_to_sample)) {
+ return false;
+ }
+
+ r_positions.resize(particle_amount);
+ r_velocities.resize(particle_amount);
+ sample_looptris(rng, mesh, triangles, triangles_to_sample, r_positions, r_velocities);
+
+ if (context.solve_context.dependency_animations.is_object_transform_changing(*settings.object)) {
+ Array<float4x4> local_to_world_matrices(particle_amount);
+ context.solve_context.dependency_animations.get_object_transforms(
+ *settings.object, r_birth_times, local_to_world_matrices);
+
+ for (int i : IndexRange(particle_amount)) {
+ const float4x4 &position_to_world = local_to_world_matrices[i];
+ const float4x4 normal_to_world = position_to_world.inverted_transposed_affine();
+ r_positions[i] = position_to_world * r_positions[i];
+ r_velocities[i] = normal_to_world * r_velocities[i];
+ }
+ }
+ else {
+ const float4x4 position_to_world = settings.object->obmat;
+ const float4x4 normal_to_world = position_to_world.inverted_transposed_affine();
+ for (int i : IndexRange(particle_amount)) {
+ r_positions[i] = position_to_world * r_positions[i];
+ r_velocities[i] = normal_to_world * r_velocities[i];
+ }
+ }
+
+ for (int i : IndexRange(particle_amount)) {
+ r_velocities[i].normalize();
+ }
+
+ state.last_birth_time = last_birth_time;
+ return true;
+}
+
+static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn,
+ ParticleEmitterContext &context)
+{
+ EmitterSettings parameters;
+
+ fn::MFContextBuilder mf_context;
+ mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context.handle_map);
+
+ fn::MFParamsBuilder mf_params{inputs_fn, 1};
+ bke::PersistentObjectHandle object_handle;
+ mf_params.add_uninitialized_single_output(&object_handle, "Object");
+ mf_params.add_uninitialized_single_output(&parameters.rate, "Rate");
+
+ inputs_fn.call(IndexRange(1), mf_params, mf_context);
+
+ parameters.object = context.solve_context.handle_map.lookup(object_handle);
+ return parameters;
+}
+
+void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const
+{
+ auto *state = context.lookup_state<ParticleMeshEmitterSimulationState>(own_state_name_);
+ if (state == nullptr) {
+ return;
+ }
+
+ EmitterSettings settings = compute_settings(inputs_fn_, context);
+
+ Vector<float3> new_positions;
+ Vector<float3> new_velocities;
+ Vector<float> new_birth_times;
+
+ if (!compute_new_particle_attributes(
+ context, settings, *state, new_positions, new_velocities, new_birth_times)) {
+ return;
+ }
+
+ for (StringRef name : particle_names_) {
+ ParticleAllocator *allocator = context.try_get_particle_allocator(name);
+ if (allocator == nullptr) {
+ continue;
+ }
+
+ int amount = new_positions.size();
+ fn::MutableAttributesRef attributes = allocator->allocate(amount);
+
+ attributes.get<float3>("Position").copy_from(new_positions);
+ attributes.get<float3>("Velocity").copy_from(new_velocities);
+ attributes.get<float>("Birth Time").copy_from(new_birth_times);
+ }
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/particle_mesh_emitter.hh b/source/blender/simulation/intern/particle_mesh_emitter.hh
new file mode 100644
index 00000000000..601697c9986
--- /dev/null
+++ b/source/blender/simulation/intern/particle_mesh_emitter.hh
@@ -0,0 +1,49 @@
+/*
+ * 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 __SIM_PARTICLE_MESH_EMITTER_HH__
+#define __SIM_PARTICLE_MESH_EMITTER_HH__
+
+#include "simulation_solver_influences.hh"
+
+#include "FN_multi_function.hh"
+
+namespace blender::sim {
+
+class ParticleMeshEmitter final : public ParticleEmitter {
+ private:
+ std::string own_state_name_;
+ Array<std::string> particle_names_;
+ const fn::MultiFunction &inputs_fn_;
+
+ public:
+ ParticleMeshEmitter(std::string own_state_name,
+ Array<std::string> particle_names,
+ const fn::MultiFunction &inputs_fn)
+ : own_state_name_(std::move(own_state_name)),
+ particle_names_(particle_names),
+ inputs_fn_(inputs_fn)
+ {
+ }
+
+ ~ParticleMeshEmitter();
+
+ void emit(ParticleEmitterContext &context) const override;
+};
+
+} // namespace blender::sim
+
+#endif /* __SIM_PARTICLE_MESH_EMITTER_HH__ */
diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc
new file mode 100644
index 00000000000..c744defb7d5
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_collect_influences.cc
@@ -0,0 +1,785 @@
+/*
+ * 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 "simulation_collect_influences.hh"
+#include "particle_function.hh"
+#include "particle_mesh_emitter.hh"
+
+#include "FN_attributes_ref.hh"
+#include "FN_multi_function_network_evaluation.hh"
+#include "FN_multi_function_network_optimization.hh"
+
+#include "NOD_node_tree_multi_function.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "BLI_hash.h"
+#include "BLI_rand.hh"
+#include "BLI_set.hh"
+
+namespace blender::sim {
+
+using fn::GVSpan;
+using fn::MFContextBuilder;
+using fn::MFDataType;
+using fn::MFDummyNode;
+using fn::MFFunctionNode;
+using fn::MFInputSocket;
+using fn::MFNetwork;
+using fn::MFNetworkEvaluator;
+using fn::MFNode;
+using fn::MFOutputSocket;
+using fn::MFParamsBuilder;
+using fn::MFParamType;
+using fn::MultiFunction;
+using fn::VSpan;
+using nodes::DerivedNodeTree;
+using nodes::DInputSocket;
+using nodes::DNode;
+using nodes::DOutputSocket;
+using nodes::DParentNode;
+using nodes::MFNetworkTreeMap;
+using nodes::NodeTreeRefMap;
+
+struct DummyDataSources {
+ Map<const MFOutputSocket *, std::string> particle_attributes;
+ Set<const MFOutputSocket *> simulation_time;
+ Set<const MFOutputSocket *> scene_time;
+};
+
+extern "C" {
+void WM_clipboard_text_set(const char *buf, bool selection);
+}
+
+static std::string dnode_to_path(const DNode &dnode)
+{
+ std::string path;
+ for (const DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) {
+ path = parent->node_ref().name() + "/" + path;
+ }
+ path = path + dnode.name();
+ return path;
+}
+
+struct CollectContext : NonCopyable, NonMovable {
+ SimulationInfluences &influences;
+ RequiredStates &required_states;
+ ResourceCollector &resources;
+ MFNetworkTreeMap &network_map;
+ MFNetwork &network;
+ const DerivedNodeTree &tree;
+
+ DummyDataSources data_sources;
+ Span<const DNode *> particle_simulation_nodes;
+ Map<const DNode *, std::string> node_paths;
+
+ CollectContext(SimulationInfluences &influences,
+ RequiredStates &required_states,
+ ResourceCollector &resources,
+ MFNetworkTreeMap &network_map)
+ : influences(influences),
+ required_states(required_states),
+ resources(resources),
+ network_map(network_map),
+ network(network_map.network()),
+ tree(network_map.tree())
+ {
+ particle_simulation_nodes = tree.nodes_by_type("SimulationNodeParticleSimulation");
+ }
+};
+
+static const ParticleAction *create_particle_action(CollectContext &context,
+ const DOutputSocket &dsocket,
+ Span<StringRefNull> particle_names);
+
+static const ParticleAction *create_particle_action(CollectContext &context,
+ const DInputSocket &dsocket,
+ Span<StringRefNull> particle_names)
+{
+ BLI_assert(dsocket.bsocket()->type == SOCK_CONTROL_FLOW);
+ if (dsocket.linked_sockets().size() != 1) {
+ return nullptr;
+ }
+ return create_particle_action(context, *dsocket.linked_sockets()[0], particle_names);
+}
+
+static StringRefNull get_identifier(CollectContext &context, const DNode &dnode)
+{
+ return context.node_paths.lookup_or_add_cb(&dnode, [&]() { return dnode_to_path(dnode); });
+}
+
+static Span<const DNode *> nodes_by_type(CollectContext &context, StringRefNull idname)
+{
+ return context.tree.nodes_by_type(idname);
+}
+
+static Array<StringRefNull> find_linked_particle_simulations(CollectContext &context,
+ const DOutputSocket &output_socket)
+{
+ VectorSet<StringRefNull> names;
+ for (const DInputSocket *target_socket : output_socket.linked_sockets()) {
+ if (target_socket->node().idname() == "SimulationNodeParticleSimulation") {
+ names.add(get_identifier(context, target_socket->node()));
+ }
+ }
+ return names.as_span();
+}
+
+/* Returns true on success. */
+static bool compute_global_inputs(MFNetworkTreeMap &network_map,
+ ResourceCollector &resources,
+ Span<const MFInputSocket *> sockets,
+ MutableSpan<GMutableSpan> r_results)
+{
+ int amount = sockets.size();
+ if (amount == 0) {
+ return true;
+ }
+
+ if (network_map.network().have_dummy_or_unlinked_dependencies(sockets)) {
+ return false;
+ }
+
+ MFNetworkEvaluator network_fn{{}, sockets};
+ MFParamsBuilder params{network_fn, 1};
+ for (int param_index : network_fn.param_indices()) {
+ MFParamType param_type = network_fn.param_type(param_index);
+ BLI_assert(param_type.category() == MFParamType::Category::SingleOutput); /* For now. */
+ const CPPType &type = param_type.data_type().single_type();
+ void *buffer = resources.linear_allocator().allocate(type.size(), type.alignment());
+ resources.add(buffer, type.destruct_cb(), AT);
+ GMutableSpan span{type, buffer, 1};
+ r_results[param_index] = span;
+ params.add_uninitialized_single_output(span);
+ }
+ MFContextBuilder context;
+ network_fn.call(IndexRange(1), params, context);
+ return true;
+}
+
+static std::optional<Array<std::string>> compute_global_string_inputs(
+ MFNetworkTreeMap &network_map, Span<const MFInputSocket *> sockets)
+{
+ ResourceCollector local_resources;
+ Array<GMutableSpan> computed_values(sockets.size(), NoInitialization());
+ if (!compute_global_inputs(network_map, local_resources, sockets, computed_values)) {
+ return {};
+ }
+
+ Array<std::string> strings(sockets.size());
+ for (int i : sockets.index_range()) {
+ strings[i] = std::move(computed_values[i].typed<std::string>()[0]);
+ }
+ return strings;
+}
+
+/**
+ * This will find all the particle attribute input nodes. Then it will compute the attribute names
+ * by evaluating the network (those names should not depend on per particle data). In the end,
+ * input nodes that access the same attribute are combined.
+ */
+static void prepare_particle_attribute_nodes(CollectContext &context)
+{
+ Span<const DNode *> attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute");
+
+ Vector<MFInputSocket *> name_sockets;
+ for (const DNode *dnode : attribute_dnodes) {
+ MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode->input(0));
+ name_sockets.append(&name_socket);
+ }
+
+ std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(
+ context.network_map, name_sockets);
+ if (!attribute_names.has_value()) {
+ return;
+ }
+
+ MultiValueMap<std::pair<std::string, MFDataType>, MFNode *> attribute_nodes_by_name_and_type;
+ for (int i : attribute_names->index_range()) {
+ attribute_nodes_by_name_and_type.add(
+ {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()},
+ &name_sockets[i]->node());
+ }
+
+ Map<const MFOutputSocket *, std::string> attribute_inputs;
+ for (auto item : attribute_nodes_by_name_and_type.items()) {
+ StringRef attribute_name = item.key.first;
+ MFDataType data_type = item.key.second;
+ Span<MFNode *> nodes = item.value;
+
+ MFOutputSocket &new_attribute_socket = context.network.add_input(
+ "Attribute '" + attribute_name + "'", data_type);
+ for (MFNode *node : nodes) {
+ context.network.relink(node->output(0), new_attribute_socket);
+ }
+ context.network.remove(nodes);
+
+ context.data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name);
+ }
+}
+
+static void prepare_time_input_nodes(CollectContext &context)
+{
+ Span<const DNode *> time_input_dnodes = nodes_by_type(context, "SimulationNodeTime");
+ Vector<const DNode *> simulation_time_inputs;
+ Vector<const DNode *> scene_time_inputs;
+ for (const DNode *dnode : time_input_dnodes) {
+ NodeSimInputTimeType type = (NodeSimInputTimeType)dnode->node_ref().bnode()->custom1;
+ switch (type) {
+ case NODE_SIM_INPUT_SIMULATION_TIME: {
+ simulation_time_inputs.append(dnode);
+ break;
+ }
+ case NODE_SIM_INPUT_SCENE_TIME: {
+ scene_time_inputs.append(dnode);
+ break;
+ }
+ }
+ }
+
+ if (simulation_time_inputs.size() > 0) {
+ MFOutputSocket &new_socket = context.network.add_input("Simulation Time",
+ MFDataType::ForSingle<float>());
+ for (const DNode *dnode : simulation_time_inputs) {
+ MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0));
+ context.network.relink(old_socket, new_socket);
+ context.network.remove(old_socket.node());
+ }
+ context.data_sources.simulation_time.add(&new_socket);
+ }
+ if (scene_time_inputs.size() > 0) {
+ MFOutputSocket &new_socket = context.network.add_input("Scene Time",
+ MFDataType::ForSingle<float>());
+ for (const DNode *dnode : scene_time_inputs) {
+ MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0));
+ context.network.relink(old_socket, new_socket);
+ context.network.remove(old_socket.node());
+ }
+ context.data_sources.scene_time.add(&new_socket);
+ }
+}
+
+class ParticleAttributeInput : public ParticleFunctionInput {
+ private:
+ std::string attribute_name_;
+ const CPPType &attribute_type_;
+
+ public:
+ ParticleAttributeInput(std::string attribute_name, const CPPType &attribute_type)
+ : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type)
+ {
+ }
+
+ void add_input(ParticleFunctionInputContext &context,
+ MFParamsBuilder &params,
+ ResourceCollector &UNUSED(resources)) const override
+ {
+ std::optional<GSpan> span = context.particles.attributes.try_get(attribute_name_,
+ attribute_type_);
+ if (span.has_value()) {
+ params.add_readonly_single_input(*span);
+ }
+ else {
+ params.add_readonly_single_input(GVSpan::FromDefault(attribute_type_));
+ }
+ }
+};
+
+class SceneTimeInput : public ParticleFunctionInput {
+ void add_input(ParticleFunctionInputContext &context,
+ MFParamsBuilder &params,
+ ResourceCollector &resources) const override
+ {
+ const float time = DEG_get_ctime(&context.solve_context.depsgraph);
+ float *time_ptr = &resources.construct<float>(AT, time);
+ params.add_readonly_single_input(time_ptr);
+ }
+};
+
+class SimulationTimeInput : public ParticleFunctionInput {
+ void add_input(ParticleFunctionInputContext &context,
+ MFParamsBuilder &params,
+ ResourceCollector &resources) const override
+ {
+ /* TODO: Vary this per particle. */
+ const float time = context.solve_context.solve_interval.stop();
+ float *time_ptr = &resources.construct<float>(AT, time);
+ params.add_readonly_single_input(time_ptr);
+ }
+};
+
+static const ParticleFunction *create_particle_function_for_inputs(
+ CollectContext &context, Span<const MFInputSocket *> sockets_to_compute)
+{
+ BLI_assert(sockets_to_compute.size() >= 1);
+ const MFNetwork &network = sockets_to_compute[0]->node().network();
+
+ VectorSet<const MFOutputSocket *> dummy_deps;
+ VectorSet<const MFInputSocket *> unlinked_input_deps;
+ network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps);
+ BLI_assert(unlinked_input_deps.size() == 0);
+
+ Vector<const ParticleFunctionInput *> per_particle_inputs;
+ for (const MFOutputSocket *socket : dummy_deps) {
+ if (context.data_sources.particle_attributes.contains(socket)) {
+ const std::string *attribute_name = context.data_sources.particle_attributes.lookup_ptr(
+ socket);
+ if (attribute_name == nullptr) {
+ return nullptr;
+ }
+ per_particle_inputs.append(&context.resources.construct<ParticleAttributeInput>(
+ AT, *attribute_name, socket->data_type().single_type()));
+ }
+ else if (context.data_sources.scene_time.contains(socket)) {
+ per_particle_inputs.append(&context.resources.construct<SceneTimeInput>(AT));
+ }
+ else if (context.data_sources.simulation_time.contains(socket)) {
+ per_particle_inputs.append(&context.resources.construct<SimulationTimeInput>(AT));
+ }
+ }
+
+ const MultiFunction &per_particle_fn = context.resources.construct<MFNetworkEvaluator>(
+ AT, dummy_deps.as_span(), sockets_to_compute);
+
+ Array<bool> output_is_global(sockets_to_compute.size(), false);
+
+ const ParticleFunction &particle_fn = context.resources.construct<ParticleFunction>(
+ AT,
+ nullptr,
+ &per_particle_fn,
+ Span<const ParticleFunctionInput *>(),
+ per_particle_inputs.as_span(),
+ output_is_global.as_span());
+
+ return &particle_fn;
+}
+
+class ParticleFunctionForce : public ParticleForce {
+ private:
+ const ParticleFunction &particle_fn_;
+
+ public:
+ ParticleFunctionForce(const ParticleFunction &particle_fn) : particle_fn_(particle_fn)
+ {
+ }
+
+ void add_force(ParticleForceContext &context) const override
+ {
+ IndexMask mask = context.particles.index_mask;
+ MutableSpan<float3> r_combined_force = context.force_dst;
+
+ ParticleFunctionEvaluator evaluator{particle_fn_, context.solve_context, context.particles};
+ evaluator.compute();
+ VSpan<float3> forces = evaluator.get<float3>(0, "Force");
+
+ for (int64_t i : mask) {
+ r_combined_force[i] += forces[i];
+ }
+ }
+};
+
+static void create_forces_for_particle_simulation(CollectContext &context,
+ const DNode &simulation_node)
+{
+ Vector<const ParticleForce *> forces;
+ for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) {
+ const DNode &origin_node = origin_socket->node();
+ if (origin_node.idname() != "SimulationNodeForce") {
+ continue;
+ }
+
+ const MFInputSocket &force_socket = context.network_map.lookup_dummy(
+ origin_node.input(0, "Force"));
+
+ const ParticleFunction *particle_fn = create_particle_function_for_inputs(context,
+ {&force_socket});
+
+ if (particle_fn == nullptr) {
+ continue;
+ }
+
+ const ParticleForce &force = context.resources.construct<ParticleFunctionForce>(AT,
+ *particle_fn);
+ forces.append(&force);
+ }
+
+ StringRef particle_name = get_identifier(context, simulation_node);
+ context.influences.particle_forces.add_multiple_as(particle_name, forces);
+}
+
+static void collect_forces(CollectContext &context)
+{
+ for (const DNode *dnode : context.particle_simulation_nodes) {
+ create_forces_for_particle_simulation(context, *dnode);
+ }
+}
+
+static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode)
+{
+ Array<StringRefNull> names = find_linked_particle_simulations(context, dnode.output(0));
+ if (names.size() == 0) {
+ return nullptr;
+ }
+
+ Array<const MFInputSocket *> input_sockets{dnode.inputs().size()};
+ for (int i : input_sockets.index_range()) {
+ input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i));
+ }
+
+ if (context.network.have_dummy_or_unlinked_dependencies(input_sockets)) {
+ return nullptr;
+ }
+
+ MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>(
+ AT, Span<const MFOutputSocket *>(), input_sockets.as_span());
+
+ StringRefNull own_state_name = get_identifier(context, dnode);
+ context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER);
+ ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>(
+ AT, own_state_name, names.as_span(), inputs_fn);
+ return &emitter;
+}
+
+static void collect_emitters(CollectContext &context)
+{
+ for (const DNode *dnode : nodes_by_type(context, "SimulationNodeParticleMeshEmitter")) {
+ ParticleEmitter *emitter = create_particle_emitter(context, *dnode);
+ if (emitter != nullptr) {
+ context.influences.particle_emitters.append(emitter);
+ }
+ }
+}
+
+static void collect_birth_events(CollectContext &context)
+{
+ for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleBirthEvent")) {
+ const DInputSocket &execute_input = event_dnode->input(0);
+ if (execute_input.linked_sockets().size() != 1) {
+ continue;
+ }
+
+ Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
+ event_dnode->output(0));
+
+ const DOutputSocket &execute_source = *execute_input.linked_sockets()[0];
+ const ParticleAction *action = create_particle_action(context, execute_source, particle_names);
+ if (action == nullptr) {
+ continue;
+ }
+
+ for (StringRefNull particle_name : particle_names) {
+ context.influences.particle_birth_actions.add_as(particle_name, action);
+ }
+ }
+}
+
+static void collect_time_step_events(CollectContext &context)
+{
+ for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) {
+ const DInputSocket &execute_input = event_dnode->input(0);
+ if (execute_input.linked_sockets().size() != 1) {
+ continue;
+ }
+
+ Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
+ event_dnode->output(0));
+
+ const DOutputSocket &execute_source = *execute_input.linked_sockets()[0];
+ const ParticleAction *action = create_particle_action(context, execute_source, particle_names);
+ if (action == nullptr) {
+ continue;
+ }
+
+ NodeSimParticleTimeStepEventType type =
+ (NodeSimParticleTimeStepEventType)event_dnode->node_ref().bnode()->custom1;
+ if (type == NODE_PARTICLE_TIME_STEP_EVENT_BEGIN) {
+ for (StringRefNull particle_name : particle_names) {
+ context.influences.particle_time_step_begin_actions.add_as(particle_name, action);
+ }
+ }
+ else {
+ for (StringRefNull particle_name : particle_names) {
+ context.influences.particle_time_step_end_actions.add_as(particle_name, action);
+ }
+ }
+ }
+}
+
+class SequenceParticleAction : public ParticleAction {
+ private:
+ Vector<const ParticleAction *> actions_;
+
+ public:
+ SequenceParticleAction(Span<const ParticleAction *> actions) : actions_(std::move(actions))
+ {
+ }
+
+ void execute(ParticleActionContext &context) const override
+ {
+ for (const ParticleAction *action : actions_) {
+ action->execute(context);
+ }
+ }
+};
+
+class SetParticleAttributeAction : public ParticleAction {
+ private:
+ std::string attribute_name_;
+ const CPPType &cpp_type_;
+ const ParticleFunction &inputs_fn_;
+
+ public:
+ SetParticleAttributeAction(std::string attribute_name,
+ const CPPType &cpp_type,
+ const ParticleFunction &inputs_fn)
+ : attribute_name_(std::move(attribute_name)), cpp_type_(cpp_type), inputs_fn_(inputs_fn)
+ {
+ }
+
+ void execute(ParticleActionContext &context) const override
+ {
+ std::optional<GMutableSpan> attribute_array = context.particles.attributes.try_get(
+ attribute_name_, cpp_type_);
+ if (!attribute_array.has_value()) {
+ return;
+ }
+
+ ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
+ evaluator.compute();
+ GVSpan values = evaluator.get(0);
+
+ if (values.is_single_element()) {
+ cpp_type_.fill_initialized_indices(
+ values.as_single_element(), attribute_array->data(), context.particles.index_mask);
+ }
+ else {
+ GSpan value_array = values.as_full_array();
+ cpp_type_.copy_to_initialized_indices(
+ value_array.data(), attribute_array->data(), context.particles.index_mask);
+ }
+ }
+};
+
+static const ParticleAction *concatenate_actions(CollectContext &context,
+ Span<const ParticleAction *> actions)
+{
+ Vector<const ParticleAction *> non_null_actions;
+ for (const ParticleAction *action : actions) {
+ if (action != nullptr) {
+ non_null_actions.append(action);
+ }
+ }
+ if (non_null_actions.size() == 0) {
+ return nullptr;
+ }
+ if (non_null_actions.size() == 1) {
+ return non_null_actions[0];
+ }
+ return &context.resources.construct<SequenceParticleAction>(AT, std::move(non_null_actions));
+}
+
+static const ParticleAction *create_set_particle_attribute_action(
+ CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names)
+{
+ const DNode &dnode = dsocket.node();
+ MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1));
+ MFInputSocket &value_socket = name_socket.node().input(1);
+ std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map,
+ {&name_socket});
+ if (!names.has_value()) {
+ return nullptr;
+ }
+
+ std::string attribute_name = (*names)[0];
+ const CPPType &attribute_type = value_socket.data_type().single_type();
+
+ const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
+ {&value_socket});
+ if (inputs_fn == nullptr) {
+ return nullptr;
+ }
+
+ for (StringRef particle_name : particle_names) {
+ context.influences.particle_attributes_builder.lookup_as(particle_name)
+ ->add(attribute_name, attribute_type);
+ }
+
+ ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>(
+ AT, attribute_name, attribute_type, *inputs_fn);
+
+ const ParticleAction *previous_action = create_particle_action(
+ context, dnode.input(0), particle_names);
+
+ return concatenate_actions(context, {previous_action, &this_action});
+}
+
+class ParticleConditionAction : public ParticleAction {
+ private:
+ const ParticleFunction &inputs_fn_;
+ const ParticleAction *action_true_;
+ const ParticleAction *action_false_;
+
+ public:
+ ParticleConditionAction(const ParticleFunction &inputs_fn,
+ const ParticleAction *action_true,
+ const ParticleAction *action_false)
+ : inputs_fn_(inputs_fn), action_true_(action_true), action_false_(action_false)
+ {
+ }
+
+ void execute(ParticleActionContext &context) const override
+ {
+ ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
+ evaluator.compute();
+ VSpan<bool> conditions = evaluator.get<bool>(0, "Condition");
+
+ if (conditions.is_single_element()) {
+ const bool condition = conditions.as_single_element();
+ if (condition) {
+ if (action_true_ != nullptr) {
+ action_true_->execute(context);
+ }
+ }
+ else {
+ if (action_false_ != nullptr) {
+ action_false_->execute(context);
+ }
+ }
+ }
+ else {
+ Span<bool> conditions_array = conditions.as_full_array();
+
+ Vector<int64_t> true_indices;
+ Vector<int64_t> false_indices;
+ for (int i : context.particles.index_mask) {
+ if (conditions_array[i]) {
+ true_indices.append(i);
+ }
+ else {
+ false_indices.append(i);
+ }
+ }
+
+ if (action_true_ != nullptr) {
+ ParticleChunkContext chunk_context{true_indices.as_span(), context.particles.attributes};
+ ParticleActionContext action_context{context.solve_context, chunk_context};
+ action_true_->execute(action_context);
+ }
+ if (action_false_ != nullptr) {
+ ParticleChunkContext chunk_context{false_indices.as_span(), context.particles.attributes};
+ ParticleActionContext action_context{context.solve_context, chunk_context};
+ action_false_->execute(action_context);
+ }
+ }
+ }
+};
+
+static const ParticleAction *create_particle_condition_action(CollectContext &context,
+ const DOutputSocket &dsocket,
+ Span<StringRefNull> particle_names)
+{
+ const DNode &dnode = dsocket.node();
+ MFInputSocket &condition_socket = context.network_map.lookup_dummy(dnode.input(0));
+
+ const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
+ {&condition_socket});
+ if (inputs_fn == nullptr) {
+ return nullptr;
+ }
+
+ const ParticleAction *true_action = create_particle_action(
+ context, dnode.input(1), particle_names);
+ const ParticleAction *false_action = create_particle_action(
+ context, dnode.input(2), particle_names);
+
+ if (true_action == nullptr && false_action == nullptr) {
+ return nullptr;
+ }
+ return &context.resources.construct<ParticleConditionAction>(
+ AT, *inputs_fn, true_action, false_action);
+}
+
+static const ParticleAction *create_particle_action(CollectContext &context,
+ const DOutputSocket &dsocket,
+ Span<StringRefNull> particle_names)
+{
+ const DNode &dnode = dsocket.node();
+ if (dnode.idname() == "SimulationNodeSetParticleAttribute") {
+ return create_set_particle_attribute_action(context, dsocket, particle_names);
+ }
+ if (dnode.idname() == "SimulationNodeExecuteCondition") {
+ return create_particle_condition_action(context, dsocket, particle_names);
+ }
+ return nullptr;
+}
+
+static void initialize_particle_attribute_builders(CollectContext &context)
+{
+ for (const DNode *dnode : context.particle_simulation_nodes) {
+ StringRef name = get_identifier(context, *dnode);
+ AttributesInfoBuilder &attributes_builder = context.resources.construct<AttributesInfoBuilder>(
+ AT);
+ attributes_builder.add<float3>("Position", {0, 0, 0});
+ attributes_builder.add<float3>("Velocity", {0, 0, 0});
+ attributes_builder.add<int>("ID", 0);
+ /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */
+ attributes_builder.add<int>("Dead", 0);
+ /* TODO: Use uint32_t, but we don't have a corresponding custom property type. */
+ attributes_builder.add<int>("Hash", 0);
+ attributes_builder.add<float>("Birth Time", 0.0f);
+ attributes_builder.add<float>("Radius", 0.02f);
+ context.influences.particle_attributes_builder.add_new(name, &attributes_builder);
+ }
+}
+
+static void optimize_function_network(CollectContext &context)
+{
+ fn::mf_network_optimization::constant_folding(context.network, context.resources);
+ fn::mf_network_optimization::common_subnetwork_elimination(context.network);
+ fn::mf_network_optimization::dead_node_removal(context.network);
+ // WM_clipboard_text_set(network.to_dot().c_str(), false);
+}
+
+void collect_simulation_influences(Simulation &simulation,
+ ResourceCollector &resources,
+ SimulationInfluences &r_influences,
+ RequiredStates &r_required_states)
+{
+ NodeTreeRefMap tree_refs;
+ const DerivedNodeTree tree{simulation.nodetree, tree_refs};
+
+ MFNetwork &network = resources.construct<MFNetwork>(AT);
+ MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources);
+
+ CollectContext context{r_influences, r_required_states, resources, network_map};
+ initialize_particle_attribute_builders(context);
+
+ prepare_particle_attribute_nodes(context);
+ prepare_time_input_nodes(context);
+
+ collect_forces(context);
+ collect_emitters(context);
+ collect_birth_events(context);
+ collect_time_step_events(context);
+
+ optimize_function_network(context);
+
+ for (const DNode *dnode : context.particle_simulation_nodes) {
+ r_required_states.add(get_identifier(context, *dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION);
+ }
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/simulation_collect_influences.hh b/source/blender/simulation/intern/simulation_collect_influences.hh
new file mode 100644
index 00000000000..caf5a8c4ffa
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_collect_influences.hh
@@ -0,0 +1,68 @@
+/*
+ * 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 __SIM_SIMULATION_COLLECT_INFLUENCES_HH__
+#define __SIM_SIMULATION_COLLECT_INFLUENCES_HH__
+
+#include "NOD_derived_node_tree.hh"
+
+#include "BLI_resource_collector.hh"
+
+#include "simulation_solver_influences.hh"
+
+namespace blender::sim {
+
+class RequiredStates {
+ private:
+ Map<std::string, const char *> state_type_by_state_name_;
+
+ public:
+ void add(std::string state_name, const char *state_type)
+ {
+ BLI_assert(state_type != nullptr);
+ const char *type_name = state_type_by_state_name_.lookup_default(state_name, nullptr);
+ if (type_name != nullptr) {
+ if (!STREQ(state_type, type_name)) {
+ std::cout << "Warning: Tried to have two different states with the same name.\n";
+ std::cout << " Name: " << state_name << "\n";
+ std::cout << " Type 1: " << state_type << "\n";
+ std::cout << " Type 2: " << type_name << "\n";
+ }
+ return;
+ }
+
+ state_type_by_state_name_.add(std::move(state_name), state_type);
+ }
+
+ const Map<std::string, const char *> &states() const
+ {
+ return state_type_by_state_name_;
+ }
+
+ bool is_required(StringRef state_name, StringRef state_type) const
+ {
+ return state_type_by_state_name_.lookup_default_as(state_name, "") == state_type;
+ }
+};
+
+void collect_simulation_influences(Simulation &simulation,
+ ResourceCollector &resources,
+ SimulationInfluences &r_influences,
+ RequiredStates &r_required_states);
+
+} // namespace blender::sim
+
+#endif /* __SIM_SIMULATION_COLLECT_INFLUENCES_HH__ */
diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc
new file mode 100644
index 00000000000..3c159eb1c58
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_solver.cc
@@ -0,0 +1,355 @@
+/*
+ * 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 "simulation_solver.hh"
+
+#include "BKE_customdata.h"
+#include "BKE_persistent_data_handle.hh"
+
+#include "BLI_rand.hh"
+#include "BLI_set.hh"
+
+#include "DEG_depsgraph_query.h"
+
+namespace blender::sim {
+
+static CustomDataType cpp_to_custom_data_type(const CPPType &type)
+{
+ if (type.is<float3>()) {
+ return CD_PROP_FLOAT3;
+ }
+ if (type.is<float>()) {
+ return CD_PROP_FLOAT;
+ }
+ if (type.is<int32_t>()) {
+ return CD_PROP_INT32;
+ }
+ BLI_assert(false);
+ return CD_PROP_FLOAT;
+}
+
+static const CPPType &custom_to_cpp_data_type(CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_FLOAT3:
+ return CPPType::get<float3>();
+ case CD_PROP_FLOAT:
+ return CPPType::get<float>();
+ case CD_PROP_INT32:
+ return CPPType::get<int32_t>();
+ default:
+ BLI_assert(false);
+ return CPPType::get<float>();
+ }
+}
+
+class CustomDataAttributesRef {
+ private:
+ Array<void *> buffers_;
+ int64_t size_;
+ const AttributesInfo &info_;
+
+ public:
+ CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info)
+ : buffers_(info.size(), nullptr), size_(size), info_(info)
+ {
+ for (int attribute_index : info.index_range()) {
+ StringRefNull name = info.name_of(attribute_index);
+ const CPPType &cpp_type = info.type_of(attribute_index);
+ CustomDataType custom_type = cpp_to_custom_data_type(cpp_type);
+ void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str());
+ buffers_[attribute_index] = data;
+ }
+ }
+
+ operator MutableAttributesRef()
+ {
+ return MutableAttributesRef(info_, buffers_, size_);
+ }
+
+ operator AttributesRef() const
+ {
+ return AttributesRef(info_, buffers_, size_);
+ }
+};
+
+static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info)
+{
+ bool found_layer_to_remove;
+ do {
+ found_layer_to_remove = false;
+ for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) {
+ CustomDataLayer *layer = &state->attributes.layers[layer_index];
+ BLI_assert(layer->name != nullptr);
+ const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type);
+ StringRefNull name = layer->name;
+ if (!info.has_attribute(name, cpp_type)) {
+ found_layer_to_remove = true;
+ CustomData_free_layer(&state->attributes, layer->type, state->tot_particles, layer_index);
+ break;
+ }
+ }
+ } while (found_layer_to_remove);
+
+ for (int attribute_index : info.index_range()) {
+ StringRefNull attribute_name = info.name_of(attribute_index);
+ const CPPType &cpp_type = info.type_of(attribute_index);
+ CustomDataType custom_type = cpp_to_custom_data_type(cpp_type);
+ if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) ==
+ nullptr) {
+ void *data = CustomData_add_layer_named(&state->attributes,
+ custom_type,
+ CD_CALLOC,
+ nullptr,
+ state->tot_particles,
+ attribute_name.c_str());
+ cpp_type.fill_uninitialized(info.default_of(attribute_index), data, state->tot_particles);
+ }
+ }
+}
+
+BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context,
+ ParticleSimulationState &state,
+ MutableAttributesRef attributes,
+ MutableSpan<float> remaining_durations,
+ float end_time)
+{
+ int particle_amount = attributes.size();
+
+ Span<const ParticleAction *> begin_actions =
+ solve_context.influences.particle_time_step_begin_actions.lookup_as(state.head.name);
+ for (const ParticleAction *action : begin_actions) {
+ ParticleChunkContext particles{IndexMask(particle_amount), attributes};
+ ParticleActionContext action_context{solve_context, particles};
+ action->execute(action_context);
+ }
+
+ Array<float3> force_vectors{particle_amount, {0, 0, 0}};
+ Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as(
+ state.head.name);
+ for (const ParticleForce *force : forces) {
+ ParticleChunkContext particles{IndexMask(particle_amount), attributes};
+ ParticleForceContext particle_force_context{solve_context, particles, force_vectors};
+ force->add_force(particle_force_context);
+ }
+
+ MutableSpan<float3> positions = attributes.get<float3>("Position");
+ MutableSpan<float3> velocities = attributes.get<float3>("Velocity");
+ MutableSpan<float> birth_times = attributes.get<float>("Birth Time");
+ MutableSpan<int> dead_states = attributes.get<int>("Dead");
+
+ for (int i : IndexRange(particle_amount)) {
+ const float time_step = remaining_durations[i];
+ velocities[i] += force_vectors[i] * time_step;
+ positions[i] += velocities[i] * time_step;
+
+ if (end_time - birth_times[i] > 2) {
+ dead_states[i] = true;
+ }
+ }
+
+ Span<const ParticleAction *> end_actions =
+ solve_context.influences.particle_time_step_end_actions.lookup_as(state.head.name);
+ for (const ParticleAction *action : end_actions) {
+ ParticleChunkContext particles{IndexMask(particle_amount), attributes};
+ ParticleActionContext action_context{solve_context, particles};
+ action->execute(action_context);
+ }
+}
+
+BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context,
+ ParticleSimulationState &state,
+ const AttributesInfo &attributes_info)
+{
+ CustomDataAttributesRef custom_data_attributes{
+ state.attributes, state.tot_particles, attributes_info};
+ MutableAttributesRef attributes = custom_data_attributes;
+
+ Array<float> remaining_durations(state.tot_particles, solve_context.solve_interval.duration());
+ simulate_particle_chunk(
+ solve_context, state, attributes, remaining_durations, solve_context.solve_interval.stop());
+}
+
+BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context,
+ ParticleAllocators &particle_allocators)
+{
+ for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) {
+ ParticleEmitterContext emitter_context{
+ solve_context, particle_allocators, solve_context.solve_interval};
+ emitter->emit(emitter_context);
+ }
+}
+
+BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState &state,
+ ParticleAllocator &allocator)
+{
+ CustomDataAttributesRef custom_data_attributes{
+ state.attributes, state.tot_particles, allocator.attributes_info()};
+ MutableAttributesRef attributes = custom_data_attributes;
+ int new_particle_amount = attributes.get<int>("Dead").count(0);
+
+ for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) {
+ new_particle_amount += emitted_attributes.get<int>("Dead").count(0);
+ }
+
+ return new_particle_amount;
+}
+
+BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationState &state,
+ ParticleAllocator &allocator)
+{
+ const int new_particle_amount = count_particles_after_time_step(state, allocator);
+
+ CustomDataAttributesRef custom_data_attributes{
+ state.attributes, state.tot_particles, allocator.attributes_info()};
+
+ Vector<MutableAttributesRef> particle_sources;
+ particle_sources.append(custom_data_attributes);
+ particle_sources.extend(allocator.get_allocations());
+
+ CustomDataLayer *dead_layer = nullptr;
+
+ for (CustomDataLayer &layer : MutableSpan(state.attributes.layers, state.attributes.totlayer)) {
+ StringRefNull name = layer.name;
+ if (name == "Dead") {
+ dead_layer = &layer;
+ continue;
+ }
+ const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type);
+ GMutableSpan new_buffer{
+ cpp_type,
+ MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT),
+ new_particle_amount};
+
+ int current = 0;
+ for (MutableAttributesRef attributes : particle_sources) {
+ Span<int> dead_states = attributes.get<int>("Dead");
+ GSpan source_buffer = attributes.get(name);
+ BLI_assert(source_buffer.type() == cpp_type);
+ for (int i : attributes.index_range()) {
+ if (dead_states[i] == 0) {
+ cpp_type.copy_to_uninitialized(source_buffer[i], new_buffer[current]);
+ current++;
+ }
+ }
+ }
+
+ if (layer.data != nullptr) {
+ MEM_freeN(layer.data);
+ }
+ layer.data = new_buffer.data();
+ }
+
+ BLI_assert(dead_layer != nullptr);
+ if (dead_layer->data != nullptr) {
+ MEM_freeN(dead_layer->data);
+ }
+ dead_layer->data = MEM_callocN(sizeof(int) * new_particle_amount, AT);
+
+ state.tot_particles = new_particle_amount;
+ state.next_particle_id += allocator.total_allocated();
+}
+
+void initialize_simulation_states(Simulation &simulation,
+ Depsgraph &UNUSED(depsgraph),
+ const SimulationInfluences &UNUSED(influences),
+ const bke::PersistentDataHandleMap &UNUSED(handle_map))
+{
+ simulation.current_simulation_time = 0.0f;
+}
+
+void solve_simulation_time_step(Simulation &simulation,
+ Depsgraph &depsgraph,
+ const SimulationInfluences &influences,
+ const bke::PersistentDataHandleMap &handle_map,
+ const DependencyAnimations &dependency_animations,
+ float time_step)
+{
+ SimulationStateMap state_map;
+ LISTBASE_FOREACH (SimulationState *, state, &simulation.states) {
+ state_map.add(state);
+ }
+
+ SimulationSolveContext solve_context{simulation,
+ depsgraph,
+ influences,
+ TimeInterval(simulation.current_simulation_time, time_step),
+ state_map,
+ handle_map,
+ dependency_animations};
+
+ Span<ParticleSimulationState *> particle_simulation_states =
+ state_map.lookup<ParticleSimulationState>();
+
+ Map<std::string, std::unique_ptr<AttributesInfo>> attribute_infos;
+ Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map;
+ for (ParticleSimulationState *state : particle_simulation_states) {
+ const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as(
+ state->head.name);
+ auto info = std::make_unique<AttributesInfo>(builder);
+
+ ensure_attributes_exist(state, *info);
+
+ uint32_t hash_seed = DefaultHash<StringRef>{}(state->head.name);
+ particle_allocators_map.add_new(
+ state->head.name,
+ std::make_unique<ParticleAllocator>(*info, state->next_particle_id, hash_seed));
+ attribute_infos.add_new(state->head.name, std::move(info));
+ }
+
+ ParticleAllocators particle_allocators{particle_allocators_map};
+
+ for (ParticleSimulationState *state : particle_simulation_states) {
+ const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name);
+ simulate_existing_particles(solve_context, *state, attributes_info);
+ }
+
+ run_emitters(solve_context, particle_allocators);
+
+ for (ParticleSimulationState *state : particle_simulation_states) {
+ ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name);
+
+ for (MutableAttributesRef attributes : allocator.get_allocations()) {
+ Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as(
+ state->head.name);
+ for (const ParticleAction *action : actions) {
+ ParticleChunkContext chunk_context{IndexRange(attributes.size()), attributes};
+ ParticleActionContext action_context{solve_context, chunk_context};
+ action->execute(action_context);
+ }
+ }
+ }
+
+ for (ParticleSimulationState *state : particle_simulation_states) {
+ ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name);
+
+ for (MutableAttributesRef attributes : allocator.get_allocations()) {
+ Array<float> remaining_durations(attributes.size());
+ Span<float> birth_times = attributes.get<float>("Birth Time");
+ const float end_time = solve_context.solve_interval.stop();
+ for (int i : attributes.index_range()) {
+ remaining_durations[i] = end_time - birth_times[i];
+ }
+ simulate_particle_chunk(solve_context, *state, attributes, remaining_durations, end_time);
+ }
+
+ remove_dead_and_add_new_particles(*state, allocator);
+ }
+
+ simulation.current_simulation_time = solve_context.solve_interval.stop();
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh
new file mode 100644
index 00000000000..9d30ea87731
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_solver.hh
@@ -0,0 +1,40 @@
+/*
+ * 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 __SIM_SIMULATION_SOLVER_HH__
+#define __SIM_SIMULATION_SOLVER_HH__
+
+#include "simulation_collect_influences.hh"
+
+struct Depsgraph;
+
+namespace blender::sim {
+
+void initialize_simulation_states(Simulation &simulation,
+ Depsgraph &depsgraph,
+ const SimulationInfluences &influences,
+ const bke::PersistentDataHandleMap &handle_map);
+
+void solve_simulation_time_step(Simulation &simulation,
+ Depsgraph &depsgraph,
+ const SimulationInfluences &influences,
+ const bke::PersistentDataHandleMap &handle_map,
+ const DependencyAnimations &dependency_animations,
+ float time_step);
+
+} // namespace blender::sim
+
+#endif /* __SIM_SIMULATION_SOLVER_HH__ */
diff --git a/source/blender/simulation/intern/simulation_solver_influences.cc b/source/blender/simulation/intern/simulation_solver_influences.cc
new file mode 100644
index 00000000000..75c2c820651
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_solver_influences.cc
@@ -0,0 +1,53 @@
+/*
+ * 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 "simulation_solver_influences.hh"
+
+#include "DNA_object_types.h"
+
+namespace blender::sim {
+
+ParticleForce::~ParticleForce()
+{
+}
+
+ParticleEmitter::~ParticleEmitter()
+{
+}
+
+ParticleAction::~ParticleAction()
+{
+}
+
+DependencyAnimations::~DependencyAnimations()
+{
+}
+
+bool DependencyAnimations::is_object_transform_changing(Object &UNUSED(object)) const
+{
+ return false;
+}
+
+void DependencyAnimations::get_object_transforms(Object &object,
+ Span<float> simulation_times,
+ MutableSpan<float4x4> r_transforms) const
+{
+ assert_same_size(simulation_times, r_transforms);
+ float4x4 world_matrix = object.obmat;
+ r_transforms.fill(world_matrix);
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh
new file mode 100644
index 00000000000..d3c515bb6a0
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_solver_influences.hh
@@ -0,0 +1,194 @@
+/*
+ * 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 __SIM_SIMULATION_SOLVER_INFLUENCES_HH__
+#define __SIM_SIMULATION_SOLVER_INFLUENCES_HH__
+
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_multi_value_map.hh"
+#include "BLI_span.hh"
+
+#include "DNA_simulation_types.h"
+
+#include "FN_attributes_ref.hh"
+
+#include "BKE_persistent_data_handle.hh"
+#include "BKE_simulation.h"
+
+#include "particle_allocator.hh"
+#include "time_interval.hh"
+
+namespace blender::sim {
+
+using fn::AttributesInfo;
+using fn::AttributesInfoBuilder;
+using fn::AttributesRef;
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GSpan;
+using fn::MutableAttributesRef;
+
+struct ParticleEmitterContext;
+struct ParticleForceContext;
+struct ParticleActionContext;
+
+class ParticleEmitter {
+ public:
+ virtual ~ParticleEmitter();
+ virtual void emit(ParticleEmitterContext &context) const = 0;
+};
+
+class ParticleForce {
+ public:
+ virtual ~ParticleForce();
+ virtual void add_force(ParticleForceContext &context) const = 0;
+};
+
+class ParticleAction {
+ public:
+ virtual ~ParticleAction();
+ virtual void execute(ParticleActionContext &context) const = 0;
+};
+
+struct SimulationInfluences {
+ MultiValueMap<std::string, const ParticleForce *> particle_forces;
+ MultiValueMap<std::string, const ParticleAction *> particle_birth_actions;
+ MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions;
+ MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions;
+ Map<std::string, AttributesInfoBuilder *> particle_attributes_builder;
+ Vector<const ParticleEmitter *> particle_emitters;
+};
+
+class SimulationStateMap {
+ private:
+ Map<StringRefNull, SimulationState *> states_by_name_;
+ MultiValueMap<StringRefNull, SimulationState *> states_by_type_;
+
+ public:
+ void add(SimulationState *state)
+ {
+ states_by_name_.add_new(state->name, state);
+ states_by_type_.add(state->type, state);
+ }
+
+ template<typename StateType> StateType *lookup(StringRef name) const
+ {
+ const char *type = BKE_simulation_get_state_type_name<StateType>();
+ return (StateType *)this->lookup_name_type(name, type);
+ }
+
+ template<typename StateType> Span<StateType *> lookup() const
+ {
+ const char *type = BKE_simulation_get_state_type_name<StateType>();
+ return this->lookup_type(type).cast<StateType *>();
+ }
+
+ SimulationState *lookup_name_type(StringRef name, StringRef type) const
+ {
+ SimulationState *state = states_by_name_.lookup_default_as(name, nullptr);
+ if (state == nullptr) {
+ return nullptr;
+ }
+ if (state->type == type) {
+ return state;
+ }
+ return nullptr;
+ }
+
+ Span<SimulationState *> lookup_type(StringRef type) const
+ {
+ return states_by_type_.lookup_as(type);
+ }
+};
+
+class DependencyAnimations {
+ public:
+ ~DependencyAnimations();
+
+ virtual bool is_object_transform_changing(Object &object) const;
+ virtual void get_object_transforms(Object &object,
+ Span<float> simulation_times,
+ MutableSpan<float4x4> r_transforms) const;
+};
+
+struct SimulationSolveContext {
+ Simulation &simulation;
+ Depsgraph &depsgraph;
+ const SimulationInfluences &influences;
+ TimeInterval solve_interval;
+ const SimulationStateMap &state_map;
+ const bke::PersistentDataHandleMap &handle_map;
+ const DependencyAnimations &dependency_animations;
+};
+
+class ParticleAllocators {
+ private:
+ Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_;
+
+ public:
+ ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators)
+ : allocators_(allocators)
+ {
+ }
+
+ ParticleAllocator *try_get_allocator(StringRef particle_simulation_name)
+ {
+ auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name);
+ if (ptr != nullptr) {
+ return ptr->get();
+ }
+ else {
+ return nullptr;
+ }
+ }
+};
+
+struct ParticleChunkContext {
+ IndexMask index_mask;
+ MutableAttributesRef attributes;
+};
+
+struct ParticleEmitterContext {
+ SimulationSolveContext &solve_context;
+ ParticleAllocators &particle_allocators;
+ TimeInterval emit_interval;
+
+ template<typename StateType> StateType *lookup_state(StringRef name)
+ {
+ return solve_context.state_map.lookup<StateType>(name);
+ }
+
+ ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name)
+ {
+ return particle_allocators.try_get_allocator(particle_simulation_name);
+ }
+};
+
+struct ParticleForceContext {
+ SimulationSolveContext &solve_context;
+ ParticleChunkContext &particles;
+ MutableSpan<float3> force_dst;
+};
+
+struct ParticleActionContext {
+ SimulationSolveContext &solve_context;
+ ParticleChunkContext &particles;
+};
+
+} // namespace blender::sim
+
+#endif /* __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ */
diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc
new file mode 100644
index 00000000000..32b582977d0
--- /dev/null
+++ b/source/blender/simulation/intern/simulation_update.cc
@@ -0,0 +1,356 @@
+/*
+ * 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 "SIM_simulation_update.hh"
+
+#include "BKE_customdata.h"
+#include "BKE_lib_id.h"
+#include "BKE_object.h"
+#include "BKE_simulation.h"
+
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_simulation_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_rand.h"
+#include "BLI_set.hh"
+#include "BLI_vector.hh"
+
+#include "NOD_node_tree_dependencies.hh"
+
+#include "particle_function.hh"
+#include "simulation_collect_influences.hh"
+#include "simulation_solver.hh"
+
+namespace blender::sim {
+
+static void copy_states_to_cow(const Simulation *simulation_orig, Simulation *simulation_cow)
+{
+ BKE_simulation_state_remove_all(simulation_cow);
+ simulation_cow->current_frame = simulation_orig->current_frame;
+
+ LISTBASE_FOREACH (const SimulationState *, state_orig, &simulation_orig->states) {
+ SimulationState *state_cow = BKE_simulation_state_add(
+ simulation_cow, state_orig->type, state_orig->name);
+ BKE_simulation_state_copy_data(state_orig, state_cow);
+ }
+}
+
+static void remove_unused_states(Simulation *simulation, const RequiredStates &required_states)
+{
+ LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) {
+ if (!required_states.is_required(state->name, state->type)) {
+ BKE_simulation_state_remove(simulation, state);
+ }
+ }
+}
+
+static void add_missing_states(Simulation *simulation, const RequiredStates &required_states)
+{
+ for (auto &&item : required_states.states().items()) {
+ const char *name = item.key.c_str();
+ const char *type = item.value;
+
+ SimulationState *state = BKE_simulation_state_try_find_by_name_and_type(
+ simulation, name, type);
+
+ if (state == nullptr) {
+ BKE_simulation_state_add(simulation, type, name);
+ }
+ }
+}
+
+static void reinitialize_empty_simulation_states(Simulation *simulation,
+ const RequiredStates &required_states)
+{
+ remove_unused_states(simulation, required_states);
+ BKE_simulation_state_reset_all(simulation);
+ add_missing_states(simulation, required_states);
+}
+
+static void update_simulation_state_list(Simulation *simulation,
+ const RequiredStates &required_states)
+{
+ remove_unused_states(simulation, required_states);
+ add_missing_states(simulation, required_states);
+}
+
+class SampledDependencyAnimations : public DependencyAnimations {
+ private:
+ TimeInterval simulation_time_interval_;
+ MultiValueMap<Object *, float4x4> object_transforms_cache_;
+
+ public:
+ SampledDependencyAnimations(TimeInterval simulation_time_interval)
+ : simulation_time_interval_(simulation_time_interval)
+ {
+ }
+
+ void add_object_transforms(Object &object, Span<float4x4> transforms)
+ {
+ object_transforms_cache_.add_multiple(&object, transforms);
+ }
+
+ bool is_object_transform_changing(Object &object) const
+ {
+ return object_transforms_cache_.lookup(&object).size() >= 2;
+ }
+
+ void get_object_transforms(Object &object,
+ Span<float> simulation_times,
+ MutableSpan<float4x4> r_transforms) const
+ {
+ assert_same_size(simulation_times, r_transforms);
+ Span<float4x4> cached_transforms = object_transforms_cache_.lookup(&object);
+ if (cached_transforms.size() == 0) {
+ r_transforms.fill(object.obmat);
+ return;
+ }
+ if (cached_transforms.size() == 1) {
+ r_transforms.fill(cached_transforms[0]);
+ return;
+ }
+
+ for (int i : simulation_times.index_range()) {
+ const float simulation_time = simulation_times[i];
+ if (simulation_time <= simulation_time_interval_.start()) {
+ r_transforms[i] = cached_transforms.first();
+ continue;
+ }
+ if (simulation_time >= simulation_time_interval_.stop()) {
+ r_transforms[i] = cached_transforms.last();
+ continue;
+ }
+ const float factor = simulation_time_interval_.factor_at_time(simulation_time);
+ BLI_assert(factor > 0.0f && factor < 1.0f);
+ const float scaled_factor = factor * (cached_transforms.size() - 1);
+ const int lower_sample = (int)scaled_factor;
+ const int upper_sample = lower_sample + 1;
+ const float mix_factor = scaled_factor - lower_sample;
+ r_transforms[i] = float4x4::interpolate(
+ cached_transforms[lower_sample], cached_transforms[upper_sample], mix_factor);
+ }
+ }
+};
+
+static void sample_object_transforms(Object &object,
+ Depsgraph &depsgraph,
+ Scene &scene,
+ TimeInterval scene_frame_interval,
+ MutableSpan<float4x4> r_transforms)
+{
+ if (r_transforms.size() == 0) {
+ return;
+ }
+ if (r_transforms.size() == 1) {
+ r_transforms[0] = object.obmat;
+ return;
+ }
+
+ Array<float> frames(r_transforms.size());
+ scene_frame_interval.compute_uniform_samples(frames);
+
+ for (int i : frames.index_range()) {
+ float frame = frames[i];
+ const int recursion_depth = 5;
+ BKE_object_modifier_update_subframe(
+ &depsgraph, &scene, &object, false, recursion_depth, frame, eModifierType_None);
+ r_transforms[i] = object.obmat;
+ }
+}
+
+template<typename T> static bool all_values_equal(Span<T> values)
+{
+ if (values.size() == 0) {
+ return true;
+ }
+ for (const T &value : values.drop_front(1)) {
+ if (value != values[0]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void prepare_dependency_animations(Depsgraph &depsgraph,
+ Scene &scene,
+ Simulation &simulation,
+ TimeInterval scene_frame_interval,
+ SampledDependencyAnimations &r_dependency_animations)
+{
+ LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation.dependencies) {
+ ID *id_cow = DEG_get_evaluated_id(&depsgraph, dependency->id);
+ if (id_cow == nullptr) {
+ continue;
+ }
+ if (GS(id_cow->name) != ID_OB) {
+ continue;
+ }
+ Object &object_cow = *(Object *)id_cow;
+ constexpr int sample_count = 10;
+ Array<float4x4, sample_count> transforms(sample_count);
+ sample_object_transforms(object_cow, depsgraph, scene, scene_frame_interval, transforms);
+
+ /* If all samples are the same, only store one. */
+ Span<float4x4> transforms_to_use = (all_values_equal(transforms.as_span())) ?
+ transforms.as_span().take_front(1) :
+ transforms.as_span();
+
+ r_dependency_animations.add_object_transforms(object_cow, transforms_to_use);
+ }
+}
+
+void update_simulation_in_depsgraph(Depsgraph *depsgraph,
+ Scene *scene_cow,
+ Simulation *simulation_cow)
+{
+ int current_frame = scene_cow->r.cfra;
+ if (simulation_cow->current_frame == current_frame) {
+ return;
+ }
+
+ /* Below we modify the original state/cache. Only the active depsgraph is allowed to do that. */
+ if (!DEG_is_active(depsgraph)) {
+ return;
+ }
+
+ Simulation *simulation_orig = (Simulation *)DEG_get_original_id(&simulation_cow->id);
+
+ ResourceCollector resources;
+ SimulationInfluences influences;
+ RequiredStates required_states;
+
+ collect_simulation_influences(*simulation_cow, resources, influences, required_states);
+
+ bke::PersistentDataHandleMap handle_map;
+ LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation_orig->dependencies) {
+ ID *id_cow = DEG_get_evaluated_id(depsgraph, dependency->id);
+ if (id_cow != nullptr) {
+ handle_map.add(dependency->handle, *id_cow);
+ }
+ }
+
+ if (current_frame == 1) {
+ reinitialize_empty_simulation_states(simulation_orig, required_states);
+
+ initialize_simulation_states(*simulation_orig, *depsgraph, influences, handle_map);
+ simulation_orig->current_frame = 1;
+
+ copy_states_to_cow(simulation_orig, simulation_cow);
+ }
+ else if (current_frame == simulation_orig->current_frame + 1) {
+ update_simulation_state_list(simulation_orig, required_states);
+
+ const float fps = scene_cow->r.frs_sec / scene_cow->r.frs_sec_base;
+ const float time_step = 1.0f / fps;
+ TimeInterval scene_frame_interval(current_frame - 1, 1);
+ TimeInterval simulation_time_interval(simulation_orig->current_simulation_time, time_step);
+ SampledDependencyAnimations dependency_animations{simulation_time_interval};
+ prepare_dependency_animations(
+ *depsgraph, *scene_cow, *simulation_orig, scene_frame_interval, dependency_animations);
+
+ solve_simulation_time_step(
+ *simulation_orig, *depsgraph, influences, handle_map, dependency_animations, time_step);
+ simulation_orig->current_frame = current_frame;
+
+ copy_states_to_cow(simulation_orig, simulation_cow);
+ }
+}
+
+/* Returns true when dependencies have changed. */
+bool update_simulation_dependencies(Simulation *simulation)
+{
+ nodes::NodeTreeDependencies dependencies = nodes::find_node_tree_dependencies(
+ *simulation->nodetree);
+
+ ListBase *dependency_list = &simulation->dependencies;
+
+ bool dependencies_changed = false;
+
+ Map<ID *, SimulationDependency *> dependency_by_id;
+ Map<SimulationDependency *, int> old_flag_by_dependency;
+ Set<int> used_handles;
+
+ /* Remove unused handle items and clear flags that are reinitialized later. */
+ LISTBASE_FOREACH_MUTABLE (SimulationDependency *, dependency, dependency_list) {
+ if (dependencies.depends_on(dependency->id)) {
+ dependency_by_id.add_new(dependency->id, dependency);
+ used_handles.add_new(dependency->handle);
+ old_flag_by_dependency.add_new(dependency, dependency->flag);
+ dependency->flag &= ~(SIM_DEPENDS_ON_TRANSFORM | SIM_DEPENDS_ON_GEOMETRY);
+ }
+ else {
+ if (dependency->id != nullptr) {
+ id_us_min(dependency->id);
+ }
+ BLI_remlink(dependency_list, dependency);
+ MEM_freeN(dependency);
+ dependencies_changed = true;
+ }
+ }
+
+ /* Add handle items for new id dependencies. */
+ int next_handle = 0;
+ for (ID *id : dependencies.id_dependencies()) {
+ dependency_by_id.lookup_or_add_cb(id, [&]() {
+ while (used_handles.contains(next_handle)) {
+ next_handle++;
+ }
+ used_handles.add_new(next_handle);
+
+ SimulationDependency *dependency = (SimulationDependency *)MEM_callocN(sizeof(*dependency),
+ AT);
+ id_us_plus(id);
+ dependency->id = id;
+ dependency->handle = next_handle;
+ BLI_addtail(dependency_list, dependency);
+
+ return dependency;
+ });
+ }
+
+ /* Set appropriate dependency flags. */
+ for (Object *object : dependencies.transform_dependencies()) {
+ SimulationDependency *dependency = dependency_by_id.lookup(&object->id);
+ dependency->flag |= SIM_DEPENDS_ON_TRANSFORM;
+ }
+ for (Object *object : dependencies.geometry_dependencies()) {
+ SimulationDependency *dependency = dependency_by_id.lookup(&object->id);
+ dependency->flag |= SIM_DEPENDS_ON_GEOMETRY;
+ }
+
+ if (!dependencies_changed) {
+ /* Check if any flags have changed. */
+ LISTBASE_FOREACH (SimulationDependency *, dependency, dependency_list) {
+ uint32_t old_flag = old_flag_by_dependency.lookup_default(dependency, 0);
+ uint32_t new_flag = dependency->flag;
+ if (old_flag != new_flag) {
+ dependencies_changed = true;
+ break;
+ }
+ }
+ }
+
+ return dependencies_changed;
+}
+
+} // namespace blender::sim
diff --git a/source/blender/simulation/intern/time_interval.hh b/source/blender/simulation/intern/time_interval.hh
new file mode 100644
index 00000000000..5996dc5d25b
--- /dev/null
+++ b/source/blender/simulation/intern/time_interval.hh
@@ -0,0 +1,93 @@
+/*
+ * 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 __SIM_TIME_INTERVAL_HH__
+#define __SIM_TIME_INTERVAL_HH__
+
+#include "BLI_utildefines.h"
+
+namespace blender::sim {
+
+/**
+ * The start time is exclusive and the end time is inclusive. If the duration is zero, the interval
+ * describes a single point in time.
+ */
+class TimeInterval {
+ private:
+ float start_;
+ float duration_;
+
+ public:
+ TimeInterval(float start, float duration) : start_(start), duration_(duration)
+ {
+ BLI_assert(duration_ >= 0.0f);
+ }
+
+ float start() const
+ {
+ return start_;
+ }
+
+ float stop() const
+ {
+ return start_ + duration_;
+ }
+
+ float duration() const
+ {
+ return duration_;
+ }
+
+ float time_at_factor(float factor) const
+ {
+ return start_ + factor * duration_;
+ }
+
+ float factor_at_time(float time) const
+ {
+ BLI_assert(duration_ > 0.0f);
+ return (time - start_) / duration_;
+ }
+
+ float safe_factor_at_time(float time) const
+ {
+ if (duration_ > 0.0f) {
+ return this->factor_at_time(time);
+ }
+ return 0.0f;
+ }
+
+ void compute_uniform_samples(MutableSpan<float> r_samples) const
+ {
+ int64_t amount = r_samples.size();
+ if (amount == 0) {
+ return;
+ }
+ if (amount == 1) {
+ r_samples[0] = this->time_at_factor(0.5f);
+ return;
+ }
+
+ const float step = duration_ / (float)(amount - 1);
+ for (int64_t i : r_samples.index_range()) {
+ r_samples[i] = start_ + i * step;
+ }
+ }
+};
+
+} // namespace blender::sim
+
+#endif /* __SIM_TIME_INTERVAL_HH__ */
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 802780b37f1..3d4c84805f9 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -593,6 +593,7 @@ void WM_uilisttype_free(void);
/* wm_menu_type.c */
void WM_menutype_init(void);
struct MenuType *WM_menutype_find(const char *idname, bool quiet);
+void WM_menutype_iter(struct GHashIterator *ghi);
bool WM_menutype_add(struct MenuType *mt);
void WM_menutype_freelink(struct MenuType *mt);
void WM_menutype_free(void);
@@ -743,8 +744,13 @@ void *WM_jobs_customdata_get(struct wmJob *);
void WM_jobs_customdata_set(struct wmJob *, void *customdata, void (*free)(void *));
void WM_jobs_timer(struct wmJob *, double timestep, unsigned int note, unsigned int endnote);
void WM_jobs_delay_start(struct wmJob *, double delay_time);
+
+typedef void (*wm_jobs_start_callback)(void *custom_data,
+ short *stop,
+ short *do_update,
+ float *progress);
void WM_jobs_callbacks(struct wmJob *,
- void (*startjob)(void *, short *, short *, float *),
+ wm_jobs_start_callback startjob,
void (*initjob)(void *),
void (*update)(void *),
void (*endjob)(void *));
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 4acce793707..b4b3d0957af 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -361,6 +361,7 @@ typedef struct wmNotifier {
#define ND_LOD (30 << 16)
#define ND_DRAW_RENDER_VIEWPORT \
(31 << 16) /* for camera & sequencer viewport update, also /w NC_SCENE */
+#define ND_SHADERFX (32 << 16)
/* NC_MATERIAL Material */
#define ND_SHADING (30 << 16)
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
index 07a3f0445bb..1066f009214 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
@@ -52,6 +52,10 @@ struct wmWindowManager;
#include "wm_gizmo_fn.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* -------------------------------------------------------------------- */
/* wmGizmo */
@@ -396,4 +400,8 @@ void WM_gizmo_group_tag_remove(struct wmGizmoGroup *gzgroup);
bool WM_gizmo_group_type_poll(const struct bContext *C, const struct wmGizmoGroupType *gzgt);
void WM_gizmo_group_refresh(const struct bContext *C, struct wmGizmoGroup *gzgroup);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_GIZMO_API_H__ */
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
index 346ed131c59..5def7f8d2f9 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
@@ -40,6 +40,10 @@ struct wmKeyConfig;
#include "DNA_listBase.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* -------------------------------------------------------------------- */
/* Enum Typedef's */
@@ -371,7 +375,7 @@ typedef struct wmGizmoType {
/** Activate a gizmo state when the user clicks on it. */
wmGizmoFnInvoke invoke;
- /** Called when gizmo tweaking is done - used to free data and reset property when cancelling. */
+ /** Called when gizmo tweaking is done - used to free data and reset property when canceling. */
wmGizmoFnExit exit;
wmGizmoFnCursorGet cursor_get;
@@ -506,4 +510,8 @@ typedef enum eWM_GizmoFlagMapDrawStep {
} eWM_GizmoFlagMapDrawStep;
#define WM_GIZMOMAP_DRAWSTEP_MAX 2
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_GIZMO_TYPES_H__ */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
index df571e84d31..90d9ba45637 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
@@ -29,7 +29,6 @@
#include "BKE_context.h"
#include "GPU_batch.h"
-#include "GPU_glew.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -108,7 +107,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/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 6ed6c485e89..2038a82c9fd 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -38,7 +38,6 @@
#include "ED_select_utils.h"
#include "ED_view3d.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
@@ -548,7 +547,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
/* pass */
}
else {
- glDepthMask(!is_depth_skip);
+ GPU_depth_mask(!is_depth_skip);
is_depth_skip_prev = is_depth_skip;
}
@@ -565,7 +564,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
GPU_depth_test(false);
}
if (is_depth_skip_prev) {
- glDepthMask(true);
+ GPU_depth_mask(true);
}
}
@@ -582,7 +581,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
rcti rect;
/* Almost certainly overkill, but allow for many custom gizmos. */
- GLuint buffer[MAXPICKBUF];
+ uint buffer[MAXPICKBUF];
short hits;
BLI_rcti_init_pt_radius(&rect, co, hotspot);
@@ -625,7 +624,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
- GLuint *buf_iter = buffer;
+ uint *buf_iter = buffer;
int hit_found = -1;
float dot_best = FLT_MAX;
@@ -649,7 +648,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
return hit_found;
}
else {
- const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
+ const uint *hit_near = GPU_select_buffer_near(buffer, hits);
return hit_near ? hit_near[3] : -1;
}
}
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
index 58b58fa01d0..00a3f476bac 100644
--- a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
@@ -27,6 +27,10 @@
struct wmMsgBus;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* wmGizmoGroup */
typedef bool (*wmGizmoGroupFnPoll)(const struct bContext *,
struct wmGizmoGroupType *) ATTR_WARN_UNUSED_RESULT;
@@ -85,4 +89,8 @@ typedef struct wmGizmoPropertyFnParams {
void *user_data;
} wmGizmoPropertyFnParams;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_GIZMO_FN_H__ */
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h b/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h
index cc9ccc5f4bb..edded4c7620 100644
--- a/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h
@@ -36,6 +36,10 @@ struct wmEventHandler_Op;
struct wmGizmoMap;
struct wmOperatorType;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* -------------------------------------------------------------------- */
/* wmGizmo */
@@ -92,4 +96,8 @@ struct ListBase *wm_gizmomap_groups_get(wmGizmoMap *gzmap);
void wm_gizmomaptypes_free(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_GIZMO_WMAPI_H__ */
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_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index ad3fc7a1302..5f13adcb971 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -39,7 +39,6 @@
#include "BKE_context.h"
#include "BKE_idtype.h"
-#include "GPU_glew.h"
#include "GPU_shader.h"
#include "GPU_state.h"
#include "GPU_viewport.h"
@@ -424,9 +423,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
y,
drag->imb->x,
drag->imb->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
+ GPU_RGBA8,
+ false,
drag->imb->rect,
drag->scale,
drag->scale,
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index fa12d6cf974..4db6eb6af91 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -51,7 +51,6 @@
#include "ED_screen.h"
#include "ED_view3d.h"
-#include "GPU_draw.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -69,6 +68,8 @@
#include "wm_surface.h"
#include "wm_window.h"
+#include "UI_resources.h"
+
#ifdef WITH_OPENSUBDIV
# include "BKE_subsurf.h"
#endif
@@ -104,11 +105,11 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region)
if (pc->poll == NULL || pc->poll(C)) {
/* Prevent drawing outside region. */
- glEnable(GL_SCISSOR_TEST);
- glScissor(region->winrct.xmin,
- region->winrct.ymin,
- BLI_rcti_size_x(&region->winrct) + 1,
- BLI_rcti_size_y(&region->winrct) + 1);
+ GPU_scissor_test(true);
+ GPU_scissor(region->winrct.xmin,
+ region->winrct.ymin,
+ BLI_rcti_size_x(&region->winrct) + 1,
+ BLI_rcti_size_y(&region->winrct) + 1);
if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) {
int x = 0, y = 0;
@@ -119,7 +120,7 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region)
pc->draw(C, win->eventstate->x, win->eventstate->y, pc->customdata);
}
- glDisable(GL_SCISSOR_TEST);
+ GPU_scissor_test(false);
}
}
}
@@ -128,6 +129,21 @@ 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)
+{
+ wmWindow *win = CTX_wm_window(C);
+
+ wmViewport(&region->winrct);
+ UI_SetTheme(area->spacetype, region->regiontype);
+ region->type->draw_overlay(C, region);
+ wmWindowViewport(win);
+}
+
+/** \} */
+/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
@@ -379,14 +395,8 @@ static void wm_draw_offscreen_texture_parameters(GPUOffScreen *offscreen)
/* We don't support multisample textures here. */
BLI_assert(GPU_texture_target(texture) == GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
-
/* No mipmaps or filtering. */
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
- /* GL_TEXTURE_BASE_LEVEL = 0 by default */
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glBindTexture(GL_TEXTURE_2D, 0);
+ GPU_texture_mipmap_mode(texture, false, false);
}
static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_viewport)
@@ -419,7 +429,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_
* depth or multisample buffers. 3D view creates own buffers with
* the data it needs. */
GPUOffScreen *offscreen = GPU_offscreen_create(
- region->winx, region->winy, 0, false, false, NULL);
+ region->winx, region->winy, false, false, NULL);
if (!offscreen) {
return;
}
@@ -449,8 +459,8 @@ static void wm_draw_region_bind(ARegion *region, int view)
/* For now scissor is expected by region drawing, we could disable it
* and do the enable/disable in the specific cases that setup scissor. */
- glEnable(GL_SCISSOR_TEST);
- glScissor(0, 0, region->winx, region->winy);
+ GPU_scissor_test(true);
+ GPU_scissor(0, 0, region->winx, region->winy);
}
region->draw_buffer->bound_view = view;
@@ -468,7 +478,7 @@ static void wm_draw_region_unbind(ARegion *region)
GPU_viewport_unbind(region->draw_buffer->viewport);
}
else {
- glDisable(GL_SCISSOR_TEST);
+ GPU_scissor_test(false);
GPU_offscreen_unbind(region->draw_buffer->offscreen, false);
}
}
@@ -530,24 +540,10 @@ void wm_draw_region_blend(ARegion *region, int view, bool blend)
alpha = 1.0f;
}
- /* setup actual texture */
- GPUTexture *texture = wm_draw_region_texture(region, view);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
-
/* wmOrtho for the screen has this same offset */
const float halfx = GLA_PIXEL_OFS / (BLI_rcti_size_x(&region->winrct) + 1);
const float halfy = GLA_PIXEL_OFS / (BLI_rcti_size_y(&region->winrct) + 1);
- if (blend) {
- /* GL_ONE because regions drawn offscreen have premultiplied alpha. */
- GPU_blend(true);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
- GPU_shader_bind(shader);
-
rcti rect_geo = region->winrct;
rect_geo.xmax += 1;
rect_geo.ymax += 1;
@@ -574,26 +570,39 @@ void wm_draw_region_blend(ARegion *region, int view, bool blend)
alpha = 1.0f;
}
- glUniform1i(GPU_shader_get_uniform(shader, "image"), 0);
- glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"),
- rect_tex.xmin,
- rect_tex.ymin,
- rect_tex.xmax,
- rect_tex.ymax);
- glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"),
- rect_geo.xmin,
- rect_geo.ymin,
- rect_geo.xmax,
- rect_geo.ymax);
- glUniform4f(
- GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR), alpha, alpha, alpha, alpha);
+ /* Not the same layout as rectf/recti. */
+ float rectt[4] = {rect_tex.xmin, rect_tex.ymin, rect_tex.xmax, rect_tex.ymax};
+ float rectg[4] = {rect_geo.xmin, rect_geo.ymin, rect_geo.xmax, rect_geo.ymax};
+
+ if (blend) {
+ /* GL_ONE because regions drawn offscreen have premultiplied alpha. */
+ GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+ GPU_blend(true);
+ }
+
+ /* setup actual texture */
+ GPUTexture *texture = wm_draw_region_texture(region, view);
+
+ GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR);
+ GPU_shader_bind(shader);
+
+ int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR);
+ int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon");
+ int rect_geo_loc = GPU_shader_get_uniform(shader, "rect_geom");
+ int texture_bind_loc = GPU_shader_get_texture_binding(shader, "image");
+
+ GPU_texture_bind(texture, texture_bind_loc);
+
+ GPU_shader_uniform_vector(shader, rect_tex_loc, 4, 1, rectt);
+ GPU_shader_uniform_vector(shader, rect_geo_loc, 4, 1, rectg);
+ GPU_shader_uniform_vector(shader, color_loc, 4, 1, (const float[4]){1, 1, 1, 1});
GPU_draw_primitive(GPU_PRIM_TRI_STRIP, 4);
- glBindTexture(GL_TEXTURE_2D, 0);
+ GPU_texture_unbind(texture);
if (blend) {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
GPU_blend(false);
}
}
@@ -712,8 +721,8 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
wm_draw_region_buffer_create(region, false, false);
wm_draw_region_bind(region, 0);
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear_color(0, 0, 0, 0);
+ GPU_clear(GPU_COLOR_BIT);
ED_region_do_draw(C, region);
wm_draw_region_unbind(region);
@@ -735,8 +744,8 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
* Actually this is only a problem when resizing the window.
* If it becomes a problem we should clear only when window size changes. */
#if 0
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear_color(0, 0, 0, 0);
+ GPU_clear(GPU_COLOR_BIT);
#endif
/* Blit non-overlapping area regions. */
@@ -749,25 +758,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) {
@@ -815,11 +829,13 @@ static void wm_draw_window(bContext *C, wmWindow *win)
}
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
/* For pageflip we simply draw to both back buffers. */
- glDrawBuffer(GL_BACK_LEFT);
+ GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT);
wm_draw_window_onscreen(C, win, 0);
- glDrawBuffer(GL_BACK_RIGHT);
+
+ GPU_backbuffer_bind(GPU_BACKBUFFER_RIGHT);
wm_draw_window_onscreen(C, win, 1);
- glDrawBuffer(GL_BACK);
+
+ GPU_backbuffer_bind(GPU_BACKBUFFER);
}
else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) {
/* For anaglyph and interlace, we draw individual regions with
@@ -832,7 +848,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
* stereo methods, but it's less efficient than drawing directly. */
const int width = WM_window_pixels_x(win);
const int height = WM_window_pixels_y(win);
- GPUOffScreen *offscreen = GPU_offscreen_create(width, height, 0, false, false, NULL);
+ GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, false, NULL);
if (offscreen) {
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
@@ -845,8 +861,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
GPU_offscreen_unbind(offscreen, false);
/* Draw offscreen buffer to screen. */
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(texture));
+ GPU_texture_bind(texture, 0);
wmWindowViewport(win);
if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
@@ -856,7 +871,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
wm_stereo3d_draw_topbottom(win, view);
}
- glBindTexture(GL_TEXTURE_2D, 0);
+ GPU_texture_unbind(texture);
}
GPU_offscreen_free(offscreen);
@@ -902,9 +917,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 +998,7 @@ void wm_draw_update(bContext *C)
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win;
- GPU_free_unused_buffers(bmain);
+ BKE_image_free_unused_gpu_textures();
for (win = wm->windows.first; win; win = win->next) {
#ifdef WIN32
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 53d6df915d6..05ef4bfac30 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -591,7 +591,7 @@ static int wm_handler_ui_call(bContext *C,
return WM_HANDLER_CONTINUE;
}
- /* UI is quite aggressive with swallowing events, like scrollwheel */
+ /* UI is quite aggressive with swallowing events, like scroll-wheel. */
/* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
if (do_wheel_ui == false) {
if (is_wheel) {
@@ -608,7 +608,7 @@ static int wm_handler_ui_call(bContext *C,
return WM_UI_HANDLER_CONTINUE;
}
- /* we set context to where ui handler came from */
+ /* We set context to where UI handler came from. */
if (handler->context.area) {
CTX_wm_area_set(C, handler->context.area);
}
@@ -810,7 +810,7 @@ bool WM_operator_check_ui_empty(wmOperatorType *ot)
return true;
}
- /* Assume a ui callback will draw something. */
+ /* Assume a UI callback will draw something. */
if (ot->ui) {
return false;
}
@@ -2707,7 +2707,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
handler_base = handler_base_next) {
handler_base_next = handler_base->next;
- /* during this loop, ui handlers for nested menus can tag multiple handlers free */
+ /* During this loop, UI handlers for nested menus can tag multiple handlers free. */
if (handler_base->flag & WM_HANDLER_DO_FREE) {
/* pass */
}
@@ -2829,7 +2829,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
/* XXX code this for all modal ops, and ensure free only happens here */
- /* modal ui handler can be tagged to be freed */
+ /* Modal UI handler can be tagged to be freed. */
if (BLI_findindex(handlers, handler_base) !=
-1) { /* could be freed already by regular modal ops */
if (handler_base->flag & WM_HANDLER_DO_FREE) {
@@ -4834,7 +4834,7 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C,
const wmEvent *event)
{
LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
- /* during this loop, ui handlers for nested menus can tag multiple handlers free */
+ /* During this loop, UI handlers for nested menus can tag multiple handlers free. */
if (handler_base->flag & WM_HANDLER_DO_FREE) {
/* pass */
}
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index ed1b29d61ce..f431a6f431b 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;
@@ -745,7 +746,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();
}
}
@@ -777,7 +778,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);
@@ -905,6 +906,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. */
@@ -1019,13 +1027,6 @@ void wm_homefile_read(bContext *C,
},
NULL);
}
- if (BLI_listbase_is_empty(&U.themes)) {
- if (G.debug & G_DEBUG) {
- printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n",
- filepath_startup);
- }
- success = false;
- }
if (success) {
if (update_defaults) {
if (use_data) {
@@ -1109,18 +1110,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) {
@@ -1408,11 +1397,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;
}
@@ -1425,7 +1413,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;
@@ -1458,7 +1451,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;
}
@@ -1491,21 +1484,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 */
@@ -1515,7 +1516,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();
}
@@ -1603,16 +1604,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;
}
}
@@ -1630,12 +1629,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);
@@ -1753,9 +1752,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;
}
@@ -2670,7 +2675,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")) {
@@ -2682,17 +2695,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 da4e4160724..31d36603505 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -34,6 +34,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
+#include "DNA_key_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
@@ -50,8 +51,10 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_override.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_report.h"
@@ -681,10 +684,10 @@ 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;
}
- RNA_string_set(op->ptr, "filepath", lib->filepath);
+ RNA_string_set(op->ptr, "filepath", lib->filepath_abs);
WM_event_add_fileselect(C, op);
@@ -694,6 +697,92 @@ static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UN
return OPERATOR_CANCELLED;
}
+static void lib_relocate_do_remap(Main *bmain,
+ ID *old_id,
+ ID *new_id,
+ ReportList *reports,
+ const bool do_reload,
+ const short remap_flags)
+{
+ BLI_assert(old_id);
+ if (do_reload) {
+ /* Since we asked for placeholders in case of missing IDs,
+ * we expect to always get a valid one. */
+ BLI_assert(new_id);
+ }
+ if (new_id) {
+#ifdef PRINT_DEBUG
+ printf("before remap of %s, old_id users: %d, new_id users: %d\n",
+ old_id->name,
+ old_id->us,
+ new_id->us);
+#endif
+ BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags);
+
+ if (old_id->flag & LIB_FAKEUSER) {
+ id_fake_user_clear(old_id);
+ id_fake_user_set(new_id);
+ }
+
+#ifdef PRINT_DEBUG
+ printf("after remap of %s, old_id users: %d, new_id users: %d\n",
+ old_id->name,
+ old_id->us,
+ new_id->us);
+#endif
+
+ /* In some cases, new_id might become direct link, remove parent of library in this case. */
+ if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) {
+ if (do_reload) {
+ BLI_assert(0); /* Should not happen in 'pure' reload case... */
+ }
+ new_id->lib->parent = NULL;
+ }
+ }
+
+ if (old_id->us > 0 && new_id && old_id->lib == new_id->lib) {
+ /* Note that this *should* not happen - but better be safe than sorry in this area,
+ * at least until we are 100% sure this cannot ever happen.
+ * Also, we can safely assume names were unique so far,
+ * so just replacing '.' by '~' should work,
+ * but this does not totally rules out the possibility of name collision. */
+ size_t len = strlen(old_id->name);
+ size_t dot_pos;
+ bool has_num = false;
+
+ for (dot_pos = len; dot_pos--;) {
+ char c = old_id->name[dot_pos];
+ if (c == '.') {
+ break;
+ }
+ else if (c < '0' || c > '9') {
+ has_num = false;
+ break;
+ }
+ has_num = true;
+ }
+
+ if (has_num) {
+ old_id->name[dot_pos] = '~';
+ }
+ else {
+ len = MIN2(len, MAX_ID_NAME - 7);
+ BLI_strncpy(&old_id->name[len], "~000", 7);
+ }
+
+ id_sort_by_name(which_libbase(bmain, GS(old_id->name)), old_id, NULL);
+
+ BKE_reportf(
+ reports,
+ RPT_WARNING,
+ "Lib Reload: Replacing all references to old data-block '%s' by reloaded one failed, "
+ "old one (%d remaining users) had to be kept and was renamed to '%s'",
+ new_id->name,
+ old_id->us,
+ old_id->name);
+ }
+}
+
static void lib_relocate_do(Main *bmain,
Library *library,
WMLinkAppendData *lapp_data,
@@ -725,6 +814,12 @@ static void lib_relocate_do(Main *bmain,
/* We remove it from current Main, and add it to items to link... */
/* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */
BLI_remlink(lbarray[lba_idx], id);
+ /* Usual special code for ShapeKeys snowflakes... */
+ Key *old_key = BKE_key_from_id(id);
+ if (old_key != NULL) {
+ BLI_remlink(which_libbase(bmain, GS(old_key->id.name)), &old_key->id);
+ }
+
item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id);
BLI_bitmap_set_all(item->libraries, true, lapp_data->num_libraries);
@@ -757,8 +852,18 @@ static void lib_relocate_do(Main *bmain,
BLI_assert(old_id);
BLI_addtail(which_libbase(bmain, GS(old_id->name)), old_id);
+
+ /* Usual special code for ShapeKeys snowflakes... */
+ Key *old_key = BKE_key_from_id(old_id);
+ if (old_key != NULL) {
+ BLI_addtail(which_libbase(bmain, GS(old_key->id.name)), &old_key->id);
+ }
}
+ /* Since our (old) reloaded IDs were removed from main, the user count done for them in linking
+ * code is wrong, we need to redo it here after adding them back to main. */
+ BKE_main_id_refcount_recompute(bmain, false);
+
/* Note that in reload case, we also want to replace indirect usages. */
const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE |
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE |
@@ -769,82 +874,20 @@ static void lib_relocate_do(Main *bmain,
ID *old_id = item->customdata;
ID *new_id = item->new_id;
- BLI_assert(old_id);
- if (do_reload) {
- /* Since we asked for placeholders in case of missing IDs,
- * we expect to always get a valid one. */
- BLI_assert(new_id);
- }
- if (new_id) {
-#ifdef PRINT_DEBUG
- printf("before remap of %s, old_id users: %d, new_id users: %d\n",
- old_id->name,
- old_id->us,
- new_id->us);
-#endif
- BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags);
-
- if (old_id->flag & LIB_FAKEUSER) {
- id_fake_user_clear(old_id);
- id_fake_user_set(new_id);
- }
-
-#ifdef PRINT_DEBUG
- printf("after remap of %s, old_id users: %d, new_id users: %d\n",
- old_id->name,
- old_id->us,
- new_id->us);
-#endif
-
- /* In some cases, new_id might become direct link, remove parent of library in this case. */
- if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) {
- if (do_reload) {
- BLI_assert(0); /* Should not happen in 'pure' reload case... */
- }
- new_id->lib->parent = NULL;
- }
+ lib_relocate_do_remap(bmain, old_id, new_id, reports, do_reload, remap_flags);
+ /* Usual special code for ShapeKeys snowflakes... */
+ Key **old_key_p = BKE_key_from_id_p(old_id);
+ if (old_key_p == NULL) {
+ continue;
}
-
- if (old_id->us > 0 && new_id && old_id->lib == new_id->lib) {
- /* Note that this *should* not happen - but better be safe than sorry in this area,
- * at least until we are 100% sure this cannot ever happen.
- * Also, we can safely assume names were unique so far,
- * so just replacing '.' by '~' should work,
- * but this does not totally rules out the possibility of name collision. */
- size_t len = strlen(old_id->name);
- size_t dot_pos;
- bool has_num = false;
-
- for (dot_pos = len; dot_pos--;) {
- char c = old_id->name[dot_pos];
- if (c == '.') {
- break;
- }
- else if (c < '0' || c > '9') {
- has_num = false;
- break;
- }
- has_num = true;
- }
-
- if (has_num) {
- old_id->name[dot_pos] = '~';
- }
- else {
- len = MIN2(len, MAX_ID_NAME - 7);
- BLI_strncpy(&old_id->name[len], "~000", 7);
- }
-
- id_sort_by_name(which_libbase(bmain, GS(old_id->name)), old_id, NULL);
-
- BKE_reportf(
- reports,
- RPT_WARNING,
- "Lib Reload: Replacing all references to old data-block '%s' by reloaded one failed, "
- "old one (%d remaining users) had to be kept and was renamed to '%s'",
- new_id->name,
- old_id->us,
- old_id->name);
+ Key *old_key = *old_key_p;
+ Key *new_key = BKE_key_from_id(new_id);
+ if (old_key != NULL) {
+ *old_key_p = NULL;
+ id_us_min(&old_key->id);
+ lib_relocate_do_remap(bmain, &old_key->id, &new_key->id, reports, do_reload, remap_flags);
+ *old_key_p = old_key;
+ id_us_plus_no_lib(&old_key->id);
}
}
@@ -896,6 +939,23 @@ static void lib_relocate_do(Main *bmain,
}
}
+ /* Update overrides of reloaded linked data-blocks.
+ * Note that this will not necessarily fully update the override, it might need to be manually
+ * 're-generated' depending on changes in linked data. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id) ||
+ (id->tag & LIB_TAG_PRE_EXISTING) == 0) {
+ continue;
+ }
+ if (id->override_library->reference->lib == library) {
+ BKE_lib_override_library_update(bmain, id);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_collection_sync(bmain);
+
BKE_main_lib_objects_recalc_all(bmain);
IMB_colormanagement_check_file_config(bmain);
@@ -909,24 +969,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, true);
@@ -959,7 +1019,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;
}
@@ -982,7 +1042,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
@@ -1019,7 +1079,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.c b/source/blender/windowmanager/intern/wm_gesture.c
index 6b2a74138c9..5a66cbd10eb 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -209,7 +209,7 @@ static void wm_gesture_draw_line(wmGesture *gt)
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
- glGetFloatv(GL_VIEWPORT, viewport_size);
+ GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
@@ -252,7 +252,7 @@ static void wm_gesture_draw_rect(wmGesture *gt)
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
- glGetFloatv(GL_VIEWPORT, viewport_size);
+ GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
@@ -291,7 +291,7 @@ static void wm_gesture_draw_circle(wmGesture *gt)
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
- glGetFloatv(GL_VIEWPORT, viewport_size);
+ GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
@@ -355,39 +355,22 @@ static void draw_filled_lasso(wmGesture *gt)
/* Additive Blending */
GPU_blend(true);
- glBlendFunc(GL_ONE, GL_ONE);
-
- GLint unpack_alignment;
- glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ GPU_blend_set_func(GPU_ONE, GPU_ONE);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_bind(state.shader);
GPU_shader_uniform_vector(
state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
- immDrawPixelsTex(&state,
- rect.xmin,
- rect.ymin,
- w,
- h,
- GL_RED,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
- pixel_buf,
- 1.0f,
- 1.0f,
- NULL);
+ immDrawPixelsTex(
+ &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL);
GPU_shader_unbind();
- glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
-
MEM_freeN(pixel_buf);
GPU_blend(false);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
}
MEM_freeN(mcoords);
@@ -415,7 +398,7 @@ static void wm_gesture_draw_lasso(wmGesture *gt, bool filled)
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
- glGetFloatv(GL_VIEWPORT, viewport_size);
+ GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
@@ -449,7 +432,7 @@ static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
- glGetFloatv(GL_VIEWPORT, viewport_size);
+ GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 2); /* "advanced" mode */
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 001acc459c2..0390bc1e4ef 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -59,6 +59,7 @@
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_icons.h"
+#include "BKE_image.h"
#include "BKE_keyconfig.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
@@ -120,7 +121,6 @@
#include "UI_interface.h"
#include "UI_resources.h"
-#include "GPU_draw.h"
#include "GPU_init_exit.h"
#include "GPU_material.h"
@@ -171,7 +171,7 @@ void WM_init_state_start_with_console_set(bool value)
*/
static bool opengl_is_init = false;
-void WM_init_opengl(Main *bmain)
+void WM_init_opengl(Main *UNUSED(bmain))
{
/* must be called only once */
BLI_assert(opengl_is_init == false);
@@ -185,9 +185,6 @@ void WM_init_opengl(Main *bmain)
DRW_opengl_context_create();
GPU_init();
- GPU_set_mipmap(bmain, true);
- GPU_set_linear_mipmap(true);
- GPU_set_anisotropic(U.anisotropic_filter);
GPU_pass_cache_init();
@@ -351,7 +348,7 @@ void WM_init(bContext *C, int argc, const char **argv)
BKE_material_copybuf_clear();
ED_render_clear_mtex_copybuf();
- // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ // GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
wm_history_file_read();
@@ -494,13 +491,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);
}
@@ -576,7 +575,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_subdiv_exit();
if (opengl_is_init) {
- GPU_free_unused_buffers(G_MAIN);
+ BKE_image_free_unused_gpu_textures();
}
BKE_blender_free(); /* blender.c, does entire library and spacetypes */
@@ -654,13 +653,6 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_blender_atexit();
- if (MEM_get_memory_blocks_in_use() != 0) {
- size_t mem_in_use = MEM_get_memory_in_use() + MEM_get_memory_in_use();
- printf("Error: Not freed memory blocks: %u, total unfreed memory %f MB\n",
- MEM_get_memory_blocks_in_use(),
- (double)mem_in_use / 1024 / 1024);
- MEM_printmemlist();
- }
wm_autosave_delete();
BKE_tempdir_session_purge();
diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c
index c10f03f3dab..87a19d832c9 100644
--- a/source/blender/windowmanager/intern/wm_jobs.c
+++ b/source/blender/windowmanager/intern/wm_jobs.c
@@ -86,7 +86,7 @@ struct wmJob {
* This performs the actual parallel work.
* Executed in worker thread(s).
*/
- void (*startjob)(void *, short *stop, short *do_update, float *progress);
+ wm_jobs_start_callback startjob;
/**
* Called if thread defines so (see `do_update` flag), and max once per timer step.
* Executed in main thread.
@@ -378,7 +378,7 @@ void WM_jobs_delay_start(wmJob *wm_job, double delay_time)
}
void WM_jobs_callbacks(wmJob *wm_job,
- void (*startjob)(void *, short *, short *, float *),
+ wm_jobs_start_callback startjob,
void (*initjob)(void *),
void (*update)(void *),
void (*endjob)(void *))
diff --git a/source/blender/windowmanager/intern/wm_menu_type.c b/source/blender/windowmanager/intern/wm_menu_type.c
index c4491423d82..dc4efe79433 100644
--- a/source/blender/windowmanager/intern/wm_menu_type.c
+++ b/source/blender/windowmanager/intern/wm_menu_type.c
@@ -57,6 +57,11 @@ MenuType *WM_menutype_find(const char *idname, bool quiet)
return NULL;
}
+void WM_menutype_iter(GHashIterator *ghi)
+{
+ BLI_ghashIterator_init(ghi, menutypes_hash);
+}
+
bool WM_menutype_add(MenuType *mt)
{
BLI_assert((mt->description == NULL) || (mt->description[0]));
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 93cd757ea93..78d8fc9a20d 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -446,8 +446,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 f99f47bc3ad..d1f65b6271b 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);
@@ -2120,7 +2120,7 @@ typedef struct {
int slow_mouse[2];
bool slow_mode;
Dial *dial;
- unsigned int gltex;
+ GPUTexture *texture;
ListBase orig_paintcursors;
bool use_secondary_tex;
void *cursor;
@@ -2224,11 +2224,15 @@ static void radial_control_set_tex(RadialControl *rc)
rc->image_id_ptr.data,
rc->use_secondary_tex,
!ELEM(rc->subtype, PROP_NONE, PROP_PIXEL, PROP_DISTANCE)))) {
- glGenTextures(1, &rc->gltex);
- glBindTexture(GL_TEXTURE_2D, rc->gltex);
- glTexImage2D(
- GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0, GL_RED, GL_FLOAT, ibuf->rect_float);
- glBindTexture(GL_TEXTURE_2D, 0);
+
+ rc->texture = GPU_texture_create_nD(
+ ibuf->x, ibuf->y, 0, 2, ibuf->rect_float, GPU_R8, GPU_DATA_FLOAT, 0, false, NULL);
+ GPU_texture_filter_mode(rc->texture, true);
+
+ GPU_texture_bind(rc->texture, 0);
+ GPU_texture_swizzle_set(rc->texture, "111r");
+ GPU_texture_unbind(rc->texture);
+
MEM_freeN(ibuf->rect_float);
MEM_freeN(ibuf);
}
@@ -2264,19 +2268,9 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- if (rc->gltex) {
-
+ if (rc->texture) {
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, rc->gltex);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- GLint swizzleMask[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED};
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
-
/* set up rotation if available */
if (rc->rot_prop) {
rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
@@ -2284,10 +2278,10 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
GPU_matrix_rotate_2d(RAD2DEGF(rot));
}
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
immUniformColor3fvAlpha(col, alpha);
- immUniform1i("image", 0);
+ immBindTexture("image", rc->texture);
/* draw textured quad */
immBegin(GPU_PRIM_TRI_FAN, 4);
@@ -2306,6 +2300,8 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
immEnd();
+ GPU_texture_unbind(rc->texture);
+
/* undo rotation */
if (rc->rot_prop) {
GPU_matrix_pop();
@@ -2730,7 +2726,7 @@ static int radial_control_invoke(bContext *C, wmOperator *op, const wmEvent *eve
rc->num_input.idx_max = 0;
rc->num_input.val_flag[0] |= NUM_NO_NEGATIVE;
rc->num_input.unit_sys = USER_UNIT_NONE;
- rc->num_input.unit_type[0] = B_UNIT_LENGTH;
+ rc->num_input.unit_type[0] = RNA_SUBTYPE_UNIT_VALUE(RNA_property_unit(rc->prop));
/* get subtype of property */
rc->subtype = RNA_property_subtype(rc->prop);
@@ -2803,7 +2799,9 @@ static void radial_control_cancel(bContext *C, wmOperator *op)
* new value is displayed in sliders/numfields */
WM_event_add_notifier(C, NC_WINDOW, NULL);
- glDeleteTextures(1, &rc->gltex);
+ if (rc->texture != NULL) {
+ GPU_texture_free(rc->texture);
+ }
MEM_freeN(rc);
}
@@ -2829,7 +2827,6 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
applyNumInput(&rc->num_input, &numValue);
if (rc->subtype == PROP_ANGLE) {
- numValue = DEG2RADF(numValue);
numValue = fmod(numValue, 2.0f * (float)M_PI);
if (numValue < 0.0f) {
numValue += 2.0f * (float)M_PI;
@@ -2994,7 +2991,6 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
applyNumInput(&rc->num_input, &numValue);
if (rc->subtype == PROP_ANGLE) {
- numValue = DEG2RADF(numValue);
numValue = fmod(numValue, 2.0f * (float)M_PI);
if (numValue < 0.0f) {
numValue += 2.0f * (float)M_PI;
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 948e8d9fb74..d0a70596957 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -58,6 +58,7 @@
#include "BIF_glutil.h"
#include "GPU_context.h"
+#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_init_exit.h"
@@ -309,13 +310,14 @@ static void playanim_toscreen(
CLAMP(offs_x, 0.0f, 1.0f);
CLAMP(offs_y, 0.0f, 1.0f);
- glClearColor(0.1, 0.1, 0.1, 0.0);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear_color(0.1, 0.1, 0.1, 0.0);
+ GPU_clear(GPU_COLOR_BIT);
/* checkerboard for case alpha */
if (ibuf->planes == 32) {
GPU_blend(true);
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ GPU_blend_set_func_separate(
+ GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
imm_draw_box_checker_2d_ex(offs_x,
offs_y,
@@ -333,9 +335,8 @@ static void playanim_toscreen(
offs_y + (ps->draw_flip[1] ? span_y : 0.0f),
ibuf->x,
ibuf->y,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- GL_NEAREST,
+ GPU_RGBA8,
+ false,
ibuf->rect,
((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x),
((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y),
@@ -542,7 +543,6 @@ static void build_pict_list_ex(
totframes--;
}
}
- return;
}
static void build_pict_list(PlayState *ps, const char *first, int totframes, int fstep, int fontid)
@@ -1059,8 +1059,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
/* zoom always show entire image */
ps->zoom = MIN2(zoomx, zoomy);
- glViewport(0, 0, ps->win_x, ps->win_y);
- glScissor(0, 0, ps->win_x, ps->win_y);
+ GPU_viewport(0, 0, ps->win_x, ps->win_y);
+ GPU_scissor(0, 0, ps->win_x, ps->win_y);
playanim_gl_matrix();
@@ -1316,13 +1316,13 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
}
- glClearColor(0.1, 0.1, 0.1, 0.0);
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear_color(0.1, 0.1, 0.1, 0.0);
+ GPU_clear(GPU_COLOR_BIT);
int win_x, win_y;
playanim_window_get_size(&win_x, &win_y);
- glViewport(0, 0, win_x, win_y);
- glScissor(0, 0, win_x, win_y);
+ GPU_viewport(0, 0, win_x, win_y);
+ GPU_scissor(0, 0, win_x, win_y);
playanim_gl_matrix();
GHOST_SwapWindowBuffers(g_WS.ghost_window);
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index 8ae343d5eb5..245560d3795 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -40,6 +40,7 @@
#include "ED_screen.h"
+#include "GPU_extensions.h"
#include "GPU_immediate.h"
#include "GPU_texture.h"
#include "GPU_viewport.h"
@@ -147,13 +148,6 @@ void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
immUnbindProgram();
}
-static bool wm_stereo3d_quadbuffer_supported(void)
-{
- GLboolean stereo = GL_FALSE;
- glGetBooleanv(GL_STEREO, &stereo);
- return stereo == GL_TRUE;
-}
-
static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
{
return ELEM(stereo_display, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM);
@@ -325,7 +319,7 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
}
/* pageflip requires a new window to be created with the proper OS flags */
else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
- if (wm_stereo3d_quadbuffer_supported()) {
+ if (GPU_stereo_quadbuffer_support()) {
BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
}
else {
diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c
index 5483d79c075..0cb76404258 100644
--- a/source/blender/windowmanager/intern/wm_subwindow.c
+++ b/source/blender/windowmanager/intern/wm_subwindow.c
@@ -29,7 +29,6 @@
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
-#include "GPU_glew.h"
#include "GPU_matrix.h"
#include "GPU_viewport.h"
@@ -40,8 +39,8 @@ void wmViewport(const rcti *winrct)
int width = BLI_rcti_size_x(winrct) + 1;
int height = BLI_rcti_size_y(winrct) + 1;
- glViewport(winrct->xmin, winrct->ymin, width, height);
- glScissor(winrct->xmin, winrct->ymin, width, height);
+ GPU_viewport(winrct->xmin, winrct->ymin, width, height);
+ GPU_scissor(winrct->xmin, winrct->ymin, width, height);
wmOrtho2_pixelspace(width, height);
GPU_matrix_identity_set();
@@ -79,8 +78,8 @@ void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct
scissor_height += 1;
}
- glViewport(0, 0, width, height);
- glScissor(x, y, scissor_width, scissor_height);
+ GPU_viewport(0, 0, width, height);
+ GPU_scissor(x, y, scissor_width, scissor_height);
wmOrtho2_pixelspace(width, height);
GPU_matrix_identity_set();
@@ -91,8 +90,8 @@ void wmWindowViewport(wmWindow *win)
int width = WM_window_pixels_x(win);
int height = WM_window_pixels_y(win);
- glViewport(0, 0, width, height);
- glScissor(0, 0, width, height);
+ GPU_viewport(0, 0, width, height);
+ GPU_scissor(0, 0, width, height);
wmOrtho2_pixelspace(width, height);
GPU_matrix_identity_set();
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..e444c5b2109 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -89,6 +89,7 @@
#include "GPU_init_exit.h"
#include "GPU_platform.h"
#include "GPU_state.h"
+#include "GPU_texture.h"
#include "UI_resources.h"
@@ -463,6 +464,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);
}
}
@@ -647,10 +649,10 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
}
#endif
/* until screens get drawn, make it nice gray */
- glClearColor(0.55, 0.55, 0.55, 0.0);
+ GPU_clear_color(0.55, 0.55, 0.55, 1.0f);
/* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
- glClear(GL_COLOR_BUFFER_BIT);
+ GPU_clear(GPU_COLOR_BIT);
}
/* needed here, because it's used before it reads userdef */
@@ -2053,9 +2055,7 @@ void WM_window_pixel_sample_read(const wmWindowManager *wm,
GPU_context_active_set(win->gpuctx);
}
- glReadBuffer(GL_FRONT);
- glReadPixels(pos[0], pos[1], 1, 1, GL_RGB, GL_FLOAT, r_col);
- glReadBuffer(GL_BACK);
+ GPU_frontbuffer_read_pixels(pos[0], pos[1], 1, 1, 3, GPU_DATA_FLOAT, r_col);
if (setup_context) {
if (wm->windrawable) {
@@ -2088,10 +2088,7 @@ uint *WM_window_pixels_read(wmWindowManager *wm, wmWindow *win, int r_size[2])
const uint rect_len = r_size[0] * r_size[1];
uint *rect = MEM_mallocN(sizeof(*rect) * rect_len, __func__);
- glReadBuffer(GL_FRONT);
- glReadPixels(0, 0, r_size[0], r_size[1], GL_RGBA, GL_UNSIGNED_BYTE, rect);
- glFinish();
- glReadBuffer(GL_BACK);
+ GPU_frontbuffer_read_pixels(0, 0, r_size[0], r_size[1], 4, GPU_DATA_UNSIGNED_BYTE, rect);
if (setup_context) {
if (wm->windrawable) {
diff --git a/source/blender/windowmanager/message_bus/wm_message_bus.h b/source/blender/windowmanager/message_bus/wm_message_bus.h
index 8020be3017a..74472386a39 100644
--- a/source/blender/windowmanager/message_bus/wm_message_bus.h
+++ b/source/blender/windowmanager/message_bus/wm_message_bus.h
@@ -34,6 +34,10 @@ struct wmMsgSubscribeKey;
struct wmMsgSubscribeValue;
struct wmMsgSubscribeValueLink;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef void (*wmMsgNotifyFn)(struct bContext *C,
struct wmMsgSubscribeKey *msg_key,
struct wmMsgSubscribeValue *msg_val);
@@ -287,4 +291,8 @@ void WM_msg_publish_ID(struct wmMsgBus *mbus, struct ID *id);
} \
((void)0)
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_MESSAGE_BUS_H__ */
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 16aa5cb44db..2b59e047f23 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -30,6 +30,10 @@ struct wmWindow;
#include "gizmo/wm_gizmo_wmapi.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct wmPaintCursor {
struct wmPaintCursor *next, *prev;
@@ -97,4 +101,8 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/source/blender/windowmanager/wm_cursors.h b/source/blender/windowmanager/wm_cursors.h
index 7a28aeb3c70..cdd6b325063 100644
--- a/source/blender/windowmanager/wm_cursors.h
+++ b/source/blender/windowmanager/wm_cursors.h
@@ -27,6 +27,10 @@
struct wmEvent;
struct wmWindow;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef enum WMCursorType {
WM_CURSOR_DEFAULT = 1,
WM_CURSOR_TEXT_EDIT,
@@ -77,4 +81,8 @@ typedef enum WMCursorType {
void wm_init_cursor_data(void);
bool wm_cursor_arrow_move(struct wmWindow *win, const struct wmEvent *event);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_CURSORS_H__ */
diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h
index ff2fc25333a..9b6bd66c0e5 100644
--- a/source/blender/windowmanager/wm_draw.h
+++ b/source/blender/windowmanager/wm_draw.h
@@ -24,12 +24,14 @@
#ifndef __WM_DRAW_H__
#define __WM_DRAW_H__
-#include "GPU_glew.h"
-
struct GPUOffScreen;
struct GPUTexture;
struct GPUViewport;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct wmDrawBuffer {
struct GPUOffScreen *offscreen;
struct GPUViewport *viewport;
@@ -50,4 +52,8 @@ void wm_draw_region_test(struct bContext *C, struct ScrArea *area, struct ARegio
struct GPUTexture *wm_draw_region_texture(struct ARegion *region, int view);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_DRAW_H__ */
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index efcf40d03eb..c61ba61f55d 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -34,6 +34,10 @@ struct ARegion;
struct GHOST_TabletData;
struct ScrArea;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* wmKeyMap is in DNA_windowmanager.h, it's saveable */
/** Custom types for handlers, for signaling, freeing */
@@ -165,4 +169,8 @@ void wm_dropbox_free(void);
void wm_drags_check_ops(bContext *C, const wmEvent *event);
void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_EVENT_SYSTEM_H__ */
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index ffed86abfe7..16b872c3c9c 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -26,6 +26,10 @@
#ifndef __WM_EVENT_TYPES_H__
#define __WM_EVENT_TYPES_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* customdata type */
enum {
EVT_DATA_TIMER = 2,
@@ -489,4 +493,8 @@ enum {
GESTURE_MODAL_CIRCLE_SIZE = 11,
};
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_EVENT_TYPES_H__ */
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index 42eab35cdb9..d7f8c3fc583 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -28,6 +28,10 @@ struct Main;
struct wmGenericCallback;
struct wmOperatorType;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* wm_files.c */
void wm_history_file_read(void);
void wm_homefile_read(struct bContext *C,
@@ -68,4 +72,8 @@ void WM_OT_append(struct wmOperatorType *ot);
void WM_OT_lib_relocate(struct wmOperatorType *ot);
void WM_OT_lib_reload(struct wmOperatorType *ot);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_FILES_H__ */
diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h
index e1b00ae1ade..8ab6301e8c9 100644
--- a/source/blender/windowmanager/wm_surface.h
+++ b/source/blender/windowmanager/wm_surface.h
@@ -27,6 +27,10 @@
struct bContext;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct wmSurface {
struct wmSurface *next, *prev;
@@ -38,6 +42,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 */
@@ -54,4 +63,8 @@ void wm_surface_clear_drawable(void);
void wm_surface_set_drawable(wmSurface *surface, bool activate);
void wm_surface_reset_drawable(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_SURFACE_H__ */
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 5ca5711b4f2..e3e8abae531 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -26,6 +26,10 @@
struct wmOperator;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* *************** internal api ************** */
void wm_ghost_init(bContext *C);
void wm_ghost_exit(void);
@@ -85,4 +89,8 @@ int wm_window_new_main_exec(bContext *C, struct wmOperator *op);
void wm_test_autorun_warning(bContext *C);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __WM_WINDOW_H__ */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index e9ff38c5a92..c564f74b771 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -41,8 +41,8 @@
#include "wm_window.h"
#include "wm_xr_intern.h"
-wmSurface *g_xr_surface = NULL;
-CLG_LogRef LOG = {"wm.xr"};
+static wmSurface *g_xr_surface = NULL;
+static CLG_LogRef LOG = {"wm.xr"};
/* -------------------------------------------------------------------- */
@@ -159,6 +159,13 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data,
wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose);
}
+typedef enum wmXrSessionStateEvent {
+ SESSION_STATE_EVENT_NONE = 0,
+ SESSION_STATE_EVENT_START,
+ SESSION_STATE_EVENT_RESET_TO_BASE_POSE,
+ SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE,
+} wmXrSessionStateEvent;
+
static bool wm_xr_session_draw_data_needs_reset_to_base_pose(const wmXrSessionState *state,
const XrSessionSettings *settings)
{
@@ -170,36 +177,64 @@ static bool wm_xr_session_draw_data_needs_reset_to_base_pose(const wmXrSessionSt
(state->prev_base_pose_object != settings->base_pose_object));
}
+static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState *state,
+ const XrSessionSettings *settings)
+{
+ if (!state->is_view_data_set) {
+ return SESSION_STATE_EVENT_START;
+ }
+ else if (wm_xr_session_draw_data_needs_reset_to_base_pose(state, settings)) {
+ return SESSION_STATE_EVENT_RESET_TO_BASE_POSE;
+ }
+ else {
+ const bool position_tracking_toggled = ((state->prev_settings_flag &
+ XR_SESSION_USE_POSITION_TRACKING) !=
+ (settings->flag & XR_SESSION_USE_POSITION_TRACKING));
+ if (position_tracking_toggled) {
+ return SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE;
+ }
+ }
+
+ return SESSION_STATE_EVENT_NONE;
+}
+
void wm_xr_session_draw_data_update(const wmXrSessionState *state,
const XrSessionSettings *settings,
const GHOST_XrDrawViewInfo *draw_view,
wmXrDrawData *draw_data)
{
- const bool position_tracking_toggled = ((state->prev_settings_flag &
- XR_SESSION_USE_POSITION_TRACKING) !=
- (settings->flag & XR_SESSION_USE_POSITION_TRACKING));
- const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING;
+ const wmXrSessionStateEvent event = wm_xr_session_state_to_event(state, settings);
+ const bool use_position_tracking = (settings->flag & XR_SESSION_USE_POSITION_TRACKING);
- /* Set the eye position offset, it's used to offset the base pose when changing positional
- * tracking. */
- if (!state->is_view_data_set ||
- wm_xr_session_draw_data_needs_reset_to_base_pose(state, settings)) {
- /* Always use the exact base pose with no offset when starting the session. */
- copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
- }
- else if (position_tracking_toggled) {
- if (use_position_tracking) {
+ switch (event) {
+ case SESSION_STATE_EVENT_START:
+ /* Always use the exact base pose with no offset when starting the session. */
copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
- }
- else {
- /* Store the current local offset (local pose) so that we can apply that to the eyes. This
- * way the eyes stay exactly where they are when disabling positional tracking. */
- copy_v3_v3(draw_data->eye_position_ofs, draw_view->local_pose.position);
- }
- }
- else if (!use_position_tracking) {
- /* Keep previous offset when positional tracking is disabled. */
- copy_v3_v3(draw_data->eye_position_ofs, state->prev_eye_position_ofs);
+ break;
+ /* This should be triggered by the VR add-on if a landmark changes. */
+ case SESSION_STATE_EVENT_RESET_TO_BASE_POSE:
+ if (use_position_tracking) {
+ /* Switch exactly to base pose, so use eye offset to cancel out current position delta. */
+ copy_v3_v3(draw_data->eye_position_ofs, draw_view->local_pose.position);
+ }
+ else {
+ copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
+ }
+ break;
+ case SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE:
+ if (use_position_tracking) {
+ /* Keep the current position, and let the user move from there. */
+ copy_v3_v3(draw_data->eye_position_ofs, state->prev_eye_position_ofs);
+ }
+ else {
+ /* Back to the exact base-pose position. */
+ copy_v3_fl(draw_data->eye_position_ofs, 0.0f);
+ }
+ break;
+ case SESSION_STATE_EVENT_NONE:
+ /* Keep previous offset when positional tracking is disabled. */
+ copy_v3_v3(draw_data->eye_position_ofs, state->prev_eye_position_ofs);
+ break;
}
}
@@ -243,6 +278,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
state->is_view_data_set = true;
+ /* Assume this was already done through wm_xr_session_draw_data_update(). */
+ state->force_reset_to_base_pose = false;
}
wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr)
@@ -299,9 +336,9 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
/**
* \brief Call Ghost-XR to draw a frame
*
- * Draw callback for the XR-session surface. It's expected to be called on each main loop iteration
- * and tells Ghost-XR to submit a new frame by drawing its views. Note that for drawing each view,
- * #wm_xr_draw_view() will be called through Ghost-XR (see GHOST_XrDrawViewFunc()).
+ * Draw callback for the XR-session surface. It's expected to be called on each main loop
+ * iteration and tells Ghost-XR to submit a new frame by drawing its views. Note that for drawing
+ * each view, #wm_xr_draw_view() will be called through Ghost-XR (see GHOST_XrDrawViewFunc()).
*/
static void wm_xr_session_surface_draw(bContext *C)
{
@@ -315,12 +352,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,
@@ -343,7 +377,7 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
}
if (!(surface_data->offscreen = GPU_offscreen_create(
- draw_view->width, draw_view->height, 0, true, false, err_out))) {
+ draw_view->width, draw_view->height, true, false, err_out))) {
failure = true;
}
@@ -391,6 +425,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 ea64184c826..9268ed15923 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -125,7 +125,7 @@ void main_python_exit(void);
* patched USD (see usd.diff) to avoid that particular static constructor, and have an
* initialization function instead.
*
- * This function is implemented in the USD source code, pxr/base/lib/plug/initConfig.cpp.
+ * This function is implemented in the USD source code, `pxr/base/lib/plug/initConfig.cpp`.
*/
void usd_initialise_plugin_path(const char *datafiles_usd_path);
#endif
@@ -158,7 +158,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);
}
@@ -208,7 +208,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
@@ -246,20 +246,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);
@@ -291,6 +295,7 @@ int main(int argc,
break;
}
}
+ MEM_initialize_memleak_detection();
}
#ifdef BUILD_DATE
@@ -341,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];
@@ -415,7 +420,7 @@ int main(int argc,
RE_engines_init();
init_nodesystem();
psys_init_rng();
- /* end second init */
+ /* End second initialization. */
#if defined(WITH_PYTHON_MODULE) || defined(WITH_HEADLESS)
/* Python module mode ALWAYS runs in background-mode (for now). */
@@ -471,7 +476,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"
@@ -528,7 +533,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/source/creator/creator_args.c b/source/creator/creator_args.c
index a2389b02d8f..20a75ebe3b9 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -51,7 +51,6 @@
# include "BKE_global.h"
# include "BKE_image.h"
# include "BKE_lib_id.h"
-# include "BKE_lib_override.h"
# include "BKE_main.h"
# include "BKE_report.h"
# include "BKE_scene.h"
@@ -619,7 +618,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
printf("Misc Options:\n");
BLI_argsPrintArgDoc(ba, "--app-template");
BLI_argsPrintArgDoc(ba, "--factory-startup");
- BLI_argsPrintArgDoc(ba, "--disable-library-override");
BLI_argsPrintArgDoc(ba, "--enable-event-simulate");
printf("\n");
BLI_argsPrintArgDoc(ba, "--env-system-datafiles");
@@ -683,7 +681,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
# ifdef WITH_SDL
printf(" $SDL_AUDIODRIVER LibSDL audio driver - alsa, esd, dma.\n");
# endif
- printf(" $PYTHONHOME Path to the Python directory, eg. /usr/lib/python.\n\n");
exit(0);
@@ -1122,17 +1119,6 @@ static int arg_handle_factory_startup_set(int UNUSED(argc),
return 0;
}
-static const char arg_handle_disable_override_library_doc[] =
- "\n\t"
- "Disable Library Override features in the UI.";
-static int arg_handle_disable_override_library(int UNUSED(argc),
- const char **UNUSED(argv),
- void *UNUSED(data))
-{
- BKE_lib_override_library_enable(false);
- return 0;
-}
-
static const char arg_handle_enable_event_simulate_doc[] =
"\n\t"
"Enable event simulation testing feature 'bpy.types.Window.event_simulate'.";
@@ -1460,7 +1446,7 @@ static int arg_handle_image_type_set(int argc, const char **argv, void *data)
return 1;
}
else {
- printf("\nError: you must specify a format after '-F / --render-foramt'.\n");
+ printf("\nError: you must specify a format after '-F / --render-format'.\n");
return 0;
}
}
@@ -2226,8 +2212,6 @@ void main_args_setup(bContext *C, bArgs *ba)
BLI_argsAdd(ba, 1, NULL, "--app-template", CB(arg_handle_app_template), NULL);
BLI_argsAdd(ba, 1, NULL, "--factory-startup", CB(arg_handle_factory_startup_set), NULL);
- BLI_argsAdd(
- ba, 1, NULL, "--disable-library-override", CB(arg_handle_disable_override_library), NULL);
BLI_argsAdd(ba, 1, NULL, "--enable-event-simulate", CB(arg_handle_enable_event_simulate), NULL);
/* TODO, add user env vars? */
diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt
index 0dfe041974e..9a7509f81f3 100644
--- a/tests/gtests/CMakeLists.txt
+++ b/tests/gtests/CMakeLists.txt
@@ -1,30 +1,21 @@
-# GTest
if(WITH_GTESTS)
-
- include(GTestTesting)
-
- add_definitions(${GFLAGS_DEFINES})
- add_definitions(${GLOG_DEFINES})
- add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
-
# Otherwise we get warnings here that we cant fix in external projects
remove_strict_flags()
+ # Build common test runner
+ add_subdirectory(runner)
+
+ # Build tests not yet ported to the common runner
add_subdirectory(testing)
- add_subdirectory(blenkernel)
add_subdirectory(blenlib)
add_subdirectory(blenloader)
add_subdirectory(guardedalloc)
add_subdirectory(bmesh)
- add_subdirectory(functions)
if(WITH_CODEC_FFMPEG)
add_subdirectory(ffmpeg)
endif()
if(WITH_ALEMBIC)
add_subdirectory(alembic)
endif()
- if(WITH_USD)
- add_subdirectory(usd)
- endif()
endif()
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..5c2b505958e 100644
--- a/tests/gtests/alembic/abc_export_test.cc
+++ b/tests/gtests/alembic/abc_export_test.cc
@@ -1,150 +1,161 @@
#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"
-}
#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..b58e989b1a1 100644
--- a/tests/gtests/alembic/abc_matrix_test.cc
+++ b/tests/gtests/alembic/abc_matrix_test.cc
@@ -3,10 +3,12 @@
// Keep first since utildefines defines AT which conflicts with STL
#include "intern/abc_axis_conversion.h"
-extern "C" {
#include "BLI_math.h"
#include "BLI_utildefines.h"
-}
+
+namespace blender {
+namespace io {
+namespace alembic {
TEST(abc_matrix, CreateRotationMatrixY_YfromZ)
{
@@ -284,3 +286,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_store_test.cc b/tests/gtests/blenlib/BLI_array_store_test.cc
index 5e8548911a2..a1ec8ec7bb3 100644
--- a/tests/gtests/blenlib/BLI_array_store_test.cc
+++ b/tests/gtests/blenlib/BLI_array_store_test.cc
@@ -4,7 +4,6 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_array_store.h"
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
@@ -13,7 +12,6 @@ extern "C" {
#include "BLI_string.h"
#include "BLI_sys_types.h"
#include "BLI_utildefines.h"
-}
/* print memory savings */
// #define DEBUG_PRINT
diff --git a/tests/gtests/blenlib/BLI_array_utils_test.cc b/tests/gtests/blenlib/BLI_array_utils_test.cc
index d6da9787768..33b4cd35d52 100644
--- a/tests/gtests/blenlib/BLI_array_utils_test.cc
+++ b/tests/gtests/blenlib/BLI_array_utils_test.cc
@@ -2,11 +2,9 @@
#include "testing/testing.h"
-extern "C" {
#include "BLI_array_utils.h"
#include "BLI_utildefines.h"
#include "BLI_utildefines_stack.h"
-}
/* -------------------------------------------------------------------- */
/* tests */
diff --git a/tests/gtests/blenlib/BLI_delaunay_2d_test.cc b/tests/gtests/blenlib/BLI_delaunay_2d_test.cc
index 6065ccc4e57..8d62b111e12 100644
--- a/tests/gtests/blenlib/BLI_delaunay_2d_test.cc
+++ b/tests/gtests/blenlib/BLI_delaunay_2d_test.cc
@@ -4,13 +4,11 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_math.h"
#include "BLI_rand.h"
#include "PIL_time.h"
#include "BLI_delaunay_2d.h"
-}
#include <fstream>
#include <iostream>
diff --git a/tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc b/tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc
index 449577401d7..aad21ae4ad4 100644
--- a/tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc
+++ b/tests/gtests/blenlib/BLI_expr_pylike_eval_test.cc
@@ -4,10 +4,8 @@
#include <string.h>
-extern "C" {
#include "BLI_expr_pylike_eval.h"
#include "BLI_math.h"
-};
#define TRUE_VAL 1.0
#define FALSE_VAL 0.0
@@ -157,6 +155,32 @@ TEST_EVAL(FMod, "fmod(x, 2)", 3.5, 1.5)
TEST_CONST(Pow, "pow(4, 0.5)", 2.0)
TEST_EVAL(Pow, "pow(4, x)", 0.5, 2.0)
+TEST_CONST(Log2_1, "log(4, 2)", 2.0)
+
+TEST_CONST(Round1, "round(-0.5)", -1.0)
+TEST_CONST(Round2, "round(-0.4)", 0.0)
+TEST_CONST(Round3, "round(0.4)", 0.0)
+TEST_CONST(Round4, "round(0.5)", 1.0)
+
+TEST_CONST(Clamp1, "clamp(-0.1)", 0.0)
+TEST_CONST(Clamp2, "clamp(0.5)", 0.5)
+TEST_CONST(Clamp3, "clamp(1.5)", 1.0)
+TEST_CONST(Clamp4, "clamp(0.5, 0.2, 0.3)", 0.3)
+TEST_CONST(Clamp5, "clamp(0.0, 0.2, 0.3)", 0.2)
+
+TEST_CONST(Lerp1, "lerp(-10,10,-1)", -30.0)
+TEST_CONST(Lerp2, "lerp(-10,10,0.25)", -5.0)
+TEST_CONST(Lerp3, "lerp(-10,10,1)", 10.0)
+TEST_EVAL(Lerp1, "lerp(-10,10,x)", 0, -10.0)
+TEST_EVAL(Lerp2, "lerp(-10,10,x)", 0.75, 5.0)
+
+TEST_CONST(Smoothstep1, "smoothstep(-10,10,-20)", 0.0)
+TEST_CONST(Smoothstep2, "smoothstep(-10,10,-10)", 0.0)
+TEST_CONST(Smoothstep3, "smoothstep(-10,10,10)", 1.0)
+TEST_CONST(Smoothstep4, "smoothstep(-10,10,20)", 1.0)
+TEST_CONST(Smoothstep5, "smoothstep(-10,10,-5)", 0.15625)
+TEST_EVAL(Smoothstep1, "smoothstep(-10,10,x)", 5, 0.84375)
+
TEST_RESULT(Min1, "min(3,1,2)", 1.0)
TEST_RESULT(Max1, "max(3,1,2)", 3.0)
TEST_RESULT(Min2, "min(1,2,3)", 1.0)
diff --git a/tests/gtests/blenlib/BLI_ghash_performance_test.cc b/tests/gtests/blenlib/BLI_ghash_performance_test.cc
index 1002ff7d2df..afabbcaae80 100644
--- a/tests/gtests/blenlib/BLI_ghash_performance_test.cc
+++ b/tests/gtests/blenlib/BLI_ghash_performance_test.cc
@@ -7,13 +7,11 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_ghash.h"
#include "BLI_rand.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "PIL_time_utildefines.h"
-}
/* Using http://corpora.uni-leipzig.de/downloads/eng_wikipedia_2010_1M-text.tar.gz
* (1 million of words, about 122MB of text) from
@@ -535,42 +533,6 @@ static void int2_ghash_tests(GHash *ghash, const char *id, const unsigned int nb
printf("========== ENDED %s ==========\n\n", id);
}
-TEST(ghash, Int2GHash2000)
-{
- GHash *ghash = BLI_ghash_new(
- BLI_ghashutil_uinthash_v2_p, BLI_ghashutil_uinthash_v2_cmp, __func__);
-
- int2_ghash_tests(ghash, "Int2GHash - GHash - 2000", 2000);
-}
-
-#ifdef GHASH_RUN_BIG
-TEST(ghash, Int2GHash20000000)
-{
- GHash *ghash = BLI_ghash_new(
- BLI_ghashutil_uinthash_v2_p, BLI_ghashutil_uinthash_v2_cmp, __func__);
-
- int2_ghash_tests(ghash, "Int2GHash - GHash - 20000000", 20000000);
-}
-#endif
-
-TEST(ghash, Int2Murmur2a2000)
-{
- GHash *ghash = BLI_ghash_new(
- BLI_ghashutil_uinthash_v2_p_murmur, BLI_ghashutil_uinthash_v2_cmp, __func__);
-
- int2_ghash_tests(ghash, "Int2GHash - Murmur - 2000", 2000);
-}
-
-#ifdef GHASH_RUN_BIG
-TEST(ghash, Int2Murmur2a20000000)
-{
- GHash *ghash = BLI_ghash_new(
- BLI_ghashutil_uinthash_v2_p_murmur, BLI_ghashutil_uinthash_v2_cmp, __func__);
-
- int2_ghash_tests(ghash, "Int2GHash - Murmur - 20000000", 20000000);
-}
-#endif
-
/* MultiSmall: create and manipulate a lot of very small ghashes
* (90% < 10 items, 9% < 100 items, 1% < 1000 items). */
diff --git a/tests/gtests/blenlib/BLI_ghash_test.cc b/tests/gtests/blenlib/BLI_ghash_test.cc
index a4b727e82aa..fcc0512cb9e 100644
--- a/tests/gtests/blenlib/BLI_ghash_test.cc
+++ b/tests/gtests/blenlib/BLI_ghash_test.cc
@@ -4,11 +4,9 @@
#define GHASH_INTERNAL_API
-extern "C" {
#include "BLI_ghash.h"
#include "BLI_rand.h"
#include "BLI_utildefines.h"
-}
#define TESTCASE_SIZE 10000
diff --git a/tests/gtests/blenlib/BLI_hash_mm2a_test.cc b/tests/gtests/blenlib/BLI_hash_mm2a_test.cc
index 4f6570d93ad..c7bea8e15de 100644
--- a/tests/gtests/blenlib/BLI_hash_mm2a_test.cc
+++ b/tests/gtests/blenlib/BLI_hash_mm2a_test.cc
@@ -2,9 +2,7 @@
#include "testing/testing.h"
-extern "C" {
#include "BLI_hash_mm2a.h"
-}
/* Note: Reference results are taken from reference implementation
* (cpp code, CMurmurHash2A variant):
diff --git a/tests/gtests/blenlib/BLI_heap_simple_test.cc b/tests/gtests/blenlib/BLI_heap_simple_test.cc
index f4d614def9e..e717a6e2653 100644
--- a/tests/gtests/blenlib/BLI_heap_simple_test.cc
+++ b/tests/gtests/blenlib/BLI_heap_simple_test.cc
@@ -5,13 +5,11 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_compiler_attrs.h"
#include "BLI_heap_simple.h"
#include "BLI_rand.h"
#include "BLI_sys_types.h"
#include "BLI_utildefines.h"
-};
#define SIZE 1024
diff --git a/tests/gtests/blenlib/BLI_heap_test.cc b/tests/gtests/blenlib/BLI_heap_test.cc
index cda13e62bb8..87e68c175a2 100644
--- a/tests/gtests/blenlib/BLI_heap_test.cc
+++ b/tests/gtests/blenlib/BLI_heap_test.cc
@@ -5,12 +5,10 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_compiler_attrs.h"
#include "BLI_heap.h"
#include "BLI_rand.h"
#include "BLI_utildefines.h"
-};
#define SIZE 1024
diff --git a/tests/gtests/blenlib/BLI_kdopbvh_test.cc b/tests/gtests/blenlib/BLI_kdopbvh_test.cc
index 333f7ffbf44..f8a4fc1290a 100644
--- a/tests/gtests/blenlib/BLI_kdopbvh_test.cc
+++ b/tests/gtests/blenlib/BLI_kdopbvh_test.cc
@@ -6,12 +6,10 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_compiler_attrs.h"
#include "BLI_kdopbvh.h"
#include "BLI_math_vector.h"
#include "BLI_rand.h"
-}
#include "stubs/bf_intern_eigen_stubs.h"
diff --git a/tests/gtests/blenlib/BLI_listbase_test.cc b/tests/gtests/blenlib/BLI_listbase_test.cc
index 32dbd49c8fc..e5b504a0040 100644
--- a/tests/gtests/blenlib/BLI_listbase_test.cc
+++ b/tests/gtests/blenlib/BLI_listbase_test.cc
@@ -4,13 +4,11 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_ressource_strings.h"
#include "BLI_string.h"
-}
/* local validation function */
static bool listbase_is_valid(const ListBase *listbase)
diff --git a/tests/gtests/blenlib/BLI_math_bits_test.cc b/tests/gtests/blenlib/BLI_math_bits_test.cc
index 9baa471cf48..4fa4809beed 100644
--- a/tests/gtests/blenlib/BLI_math_bits_test.cc
+++ b/tests/gtests/blenlib/BLI_math_bits_test.cc
@@ -1,3 +1,5 @@
+/* Apache License, Version 2.0 */
+
#include "BLI_math_bits.h"
#include "testing/testing.h"
#include <iostream>
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_memiter_test.cc b/tests/gtests/blenlib/BLI_memiter_test.cc
index 05602dcc1c8..3cc86630005 100644
--- a/tests/gtests/blenlib/BLI_memiter_test.cc
+++ b/tests/gtests/blenlib/BLI_memiter_test.cc
@@ -4,13 +4,11 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_array_utils.h"
#include "BLI_memiter.h"
#include "BLI_ressource_strings.h"
#include "BLI_string.h"
-}
TEST(memiter, Nop)
{
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_path_util_test.cc b/tests/gtests/blenlib/BLI_path_util_test.cc
index 480d48d6080..c9e6f3357ff 100644
--- a/tests/gtests/blenlib/BLI_path_util_test.cc
+++ b/tests/gtests/blenlib/BLI_path_util_test.cc
@@ -2,7 +2,6 @@
#include "testing/testing.h"
-extern "C" {
#include "../../../source/blender/imbuf/IMB_imbuf.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
@@ -11,7 +10,6 @@ extern "C" {
#ifdef _WIN32
# include "../../../source/blender/blenkernel/BKE_global.h"
#endif
-}
/* -------------------------------------------------------------------- */
/* stubs */
@@ -408,6 +406,20 @@ TEST(path_util, Frame)
EXPECT_FALSE(ret);
EXPECT_STREQ("test_middle", path);
}
+
+ /* negative frame numbers */
+ {
+ char path[FILE_MAX] = "test_####";
+ ret = BLI_path_frame(path, -1, 4);
+ EXPECT_TRUE(ret);
+ EXPECT_STREQ("test_-0001", path);
+ }
+ {
+ char path[FILE_MAX] = "test_####";
+ ret = BLI_path_frame(path, -100, 4);
+ EXPECT_TRUE(ret);
+ EXPECT_STREQ("test_-0100", path);
+ }
}
/* BLI_split_dirfile */
diff --git a/tests/gtests/blenlib/BLI_polyfill_2d_test.cc b/tests/gtests/blenlib/BLI_polyfill_2d_test.cc
index 911137bb83b..527a23ac3a8 100644
--- a/tests/gtests/blenlib/BLI_polyfill_2d_test.cc
+++ b/tests/gtests/blenlib/BLI_polyfill_2d_test.cc
@@ -11,7 +11,6 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_array_utils.h"
#include "BLI_edgehash.h"
#include "BLI_math.h"
@@ -27,7 +26,6 @@ extern "C" {
# include "BLI_memarena.h"
# include "BLI_polyfill_2d_beautify.h"
#endif
-}
#include "stubs/bf_intern_eigen_stubs.h"
diff --git a/tests/gtests/blenlib/BLI_stack_test.cc b/tests/gtests/blenlib/BLI_stack_test.cc
index 359e61c6f47..211916e3193 100644
--- a/tests/gtests/blenlib/BLI_stack_test.cc
+++ b/tests/gtests/blenlib/BLI_stack_test.cc
@@ -3,11 +3,9 @@
#include "testing/testing.h"
#include <string.h>
-extern "C" {
#include "BLI_array.h"
#include "BLI_stack.h"
#include "BLI_utildefines.h"
-};
#define SIZE 1024
diff --git a/tests/gtests/blenlib/BLI_string_test.cc b/tests/gtests/blenlib/BLI_string_test.cc
index aae0c12c8b2..0358a1611f2 100644
--- a/tests/gtests/blenlib/BLI_string_test.cc
+++ b/tests/gtests/blenlib/BLI_string_test.cc
@@ -9,12 +9,10 @@
#include <utility>
#include <vector>
-extern "C" {
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
-}
using std::initializer_list;
using std::pair;
diff --git a/tests/gtests/blenlib/BLI_string_utf8_test.cc b/tests/gtests/blenlib/BLI_string_utf8_test.cc
index 3e2ade40019..c496f918dc0 100644
--- a/tests/gtests/blenlib/BLI_string_utf8_test.cc
+++ b/tests/gtests/blenlib/BLI_string_utf8_test.cc
@@ -2,11 +2,9 @@
#include "testing/testing.h"
-extern "C" {
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
-}
/* Note that 'common' utf-8 variants of string functions (like copy, etc.) are tested in
* BLI_string_test.cc However, tests below are specific utf-8 conformance ones, and since they eat
diff --git a/tests/gtests/blenlib/BLI_task_performance_test.cc b/tests/gtests/blenlib/BLI_task_performance_test.cc
index 06e832bdb5e..208f168b599 100644
--- a/tests/gtests/blenlib/BLI_task_performance_test.cc
+++ b/tests/gtests/blenlib/BLI_task_performance_test.cc
@@ -9,7 +9,6 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
@@ -17,7 +16,6 @@ extern "C" {
#include "BLI_task.h"
#include "PIL_time.h"
-}
#define NUM_RUN_AVERAGED 100
diff --git a/tests/gtests/blenlib/BLI_task_test.cc b/tests/gtests/blenlib/BLI_task_test.cc
index ed300b3f238..3abaf6a6c0b 100644
--- a/tests/gtests/blenlib/BLI_task_test.cc
+++ b/tests/gtests/blenlib/BLI_task_test.cc
@@ -7,13 +7,11 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
#include "BLI_mempool.h"
#include "BLI_task.h"
-};
#define NUM_ITEMS 10000
diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt
index 8ddb2702b83..0727d317eaa 100644
--- a/tests/gtests/blenlib/CMakeLists.txt
+++ b/tests/gtests/blenlib/CMakeLists.txt
@@ -39,43 +39,31 @@ else()
set(BLI_path_util_extra_libs "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}")
endif()
-BLENDER_TEST(BLI_array "bf_blenlib")
BLENDER_TEST(BLI_array_store "bf_blenlib")
BLENDER_TEST(BLI_array_utils "bf_blenlib")
BLENDER_TEST(BLI_delaunay_2d "bf_blenlib")
-BLENDER_TEST(BLI_edgehash "bf_blenlib")
BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib")
BLENDER_TEST(BLI_ghash "bf_blenlib")
BLENDER_TEST(BLI_hash_mm2a "bf_blenlib")
BLENDER_TEST(BLI_heap "bf_blenlib")
BLENDER_TEST(BLI_heap_simple "bf_blenlib")
-BLENDER_TEST(BLI_index_mask "bf_blenlib")
-BLENDER_TEST(BLI_index_range "bf_blenlib")
BLENDER_TEST(BLI_kdopbvh "bf_blenlib;bf_intern_numaapi")
-BLENDER_TEST(BLI_linear_allocator "bf_blenlib")
BLENDER_TEST(BLI_linklist_lockfree "bf_blenlib;bf_intern_numaapi")
BLENDER_TEST(BLI_listbase "bf_blenlib")
-BLENDER_TEST(BLI_map "bf_blenlib")
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")
-BLENDER_TEST(BLI_span "bf_blenlib")
BLENDER_TEST(BLI_stack "bf_blenlib")
-BLENDER_TEST(BLI_stack_cxx "bf_blenlib")
BLENDER_TEST(BLI_string "bf_blenlib")
-BLENDER_TEST(BLI_string_ref "bf_blenlib")
BLENDER_TEST(BLI_string_utf8 "bf_blenlib")
BLENDER_TEST(BLI_task "bf_blenlib;bf_intern_numaapi")
BLENDER_TEST(BLI_task_graph "bf_blenlib;bf_intern_numaapi")
-BLENDER_TEST(BLI_vector "bf_blenlib")
-BLENDER_TEST(BLI_vector_set "bf_blenlib")
BLENDER_TEST_PERFORMANCE(BLI_ghash_performance "bf_blenlib")
BLENDER_TEST_PERFORMANCE(BLI_task_performance "bf_blenlib")
diff --git a/tests/gtests/blenloader/blendfile_loading_base_test.cc b/tests/gtests/blenloader/blendfile_loading_base_test.cc
index 62befae90cd..d74bab4b31c 100644
--- a/tests/gtests/blenloader/blendfile_loading_base_test.cc
+++ b/tests/gtests/blenloader/blendfile_loading_base_test.cc
@@ -19,7 +19,6 @@
#include "MEM_guardedalloc.h"
-extern "C" {
#include "BKE_appdir.h"
#include "BKE_blender.h"
#include "BKE_context.h"
@@ -48,9 +47,6 @@ extern "C" {
#include "WM_api.h"
#include "wm.h"
-}
-
-DEFINE_string(test_assets_dir, "", "lib/tests directory from SVN containing the test assets.");
BlendfileLoadingBaseTest::~BlendfileLoadingBaseTest()
{
@@ -102,14 +98,6 @@ void BlendfileLoadingBaseTest::TearDownTestCase()
BKE_blender_atexit();
- if (MEM_get_memory_blocks_in_use() != 0) {
- size_t mem_in_use = MEM_get_memory_in_use() + MEM_get_memory_in_use();
- printf("Error: Not freed memory blocks: %u, total unfreed memory %f MB\n",
- MEM_get_memory_blocks_in_use(),
- (double)mem_in_use / 1024 / 1024);
- MEM_printmemlist();
- }
-
BKE_tempdir_session_purge();
testing::Test::TearDownTestCase();
@@ -125,19 +113,18 @@ void BlendfileLoadingBaseTest::TearDown()
bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath)
{
- if (FLAGS_test_assets_dir.empty()) {
- ADD_FAILURE()
- << "Pass the flag --test-assets-dir and point to the lib/tests directory from SVN.";
+ const std::string &test_assets_dir = blender::tests::flags_test_asset_dir();
+ if (test_assets_dir.empty()) {
return false;
}
char abspath[FILENAME_MAX];
- BLI_path_join(abspath, sizeof(abspath), FLAGS_test_assets_dir.c_str(), filepath, NULL);
+ BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath, NULL);
bfile = BLO_read_from_file(abspath, BLO_READ_SKIP_NONE, NULL /* reports */);
if (bfile == nullptr) {
ADD_FAILURE() << "Unable to load file '" << filepath << "' from test assets dir '"
- << FLAGS_test_assets_dir << "'";
+ << test_assets_dir << "'";
return false;
}
return true;
diff --git a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
index 4866ac44e3c..4c676c6cc76 100644
--- a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
+++ b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
@@ -2,9 +2,7 @@
#include "testing/testing.h"
-extern "C" {
#include "BLI_utildefines.h"
-}
#include "MEM_guardedalloc.h"
diff --git a/tests/gtests/runner/BlenderAddTests.cmake b/tests/gtests/runner/BlenderAddTests.cmake
new file mode 100644
index 00000000000..c4f5c8aba8a
--- /dev/null
+++ b/tests/gtests/runner/BlenderAddTests.cmake
@@ -0,0 +1,3 @@
+# Disable ASAN leak detection when trying to discover tests.
+set(ENV{ASAN_OPTIONS} "detect_leaks=0")
+include(GoogleTestAddTests)
diff --git a/tests/gtests/runner/CMakeLists.txt b/tests/gtests/runner/CMakeLists.txt
new file mode 100644
index 00000000000..954fcf914a9
--- /dev/null
+++ b/tests/gtests/runner/CMakeLists.txt
@@ -0,0 +1,87 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2020, Blender Foundation
+# All rights reserved.
+# ***** END GPL LICENSE BLOCK *****
+
+# Build the test runner. This runner takes care of running all GTests, i.e.
+# the code that was built using the blender_add_test_lib() CMake macro (see
+# macros.cmake).
+set(SRC
+ blender_test.cc
+)
+
+if(WITH_BUILDINFO)
+ list(APPEND SRC
+ "$<TARGET_OBJECTS:buildinfoobj>"
+ )
+endif()
+
+
+# Test libraries need to be linked "whole archive", because they're not
+# directly referenced from other code.
+get_property(_test_libs GLOBAL PROPERTY BLENDER_TEST_LIBS)
+if(WIN32)
+ # Win32 is set using target_link_options after target creation.
+elseif(APPLE)
+ list(APPEND TEST_LIBS "-Wl,-force_load" ${_test_libs})
+elseif(UNIX)
+ list(APPEND TEST_LIBS "-Wl,--whole-archive" ${_test_libs} "-Wl,--no-whole-archive")
+else()
+ message(FATAL_ERROR "Unknown how to link whole-archive with your compiler ${CMAKE_CXX_COMPILER_ID}")
+endif()
+
+# This builds `bin/tests/blender_test`, but does not add it as a single test.
+setup_libdirs()
+BLENDER_SRC_GTEST_EX(
+ NAME blender
+ SRC "${SRC}"
+ EXTRA_LIBS "${TEST_LIBS}"
+ SKIP_ADD_TEST
+)
+setup_liblinks(blender_test)
+
+if(WIN32)
+ foreach(_lib ${_test_libs})
+ # Both target_link_libraries and target_link_options are required here
+ # target_link_libraries will add any dependend libraries, while just setting
+ # the wholearchive flag in target link options will not.
+ target_link_libraries(blender_test ${_lib})
+ target_link_options(blender_test PRIVATE /wholearchive:$<TARGET_FILE:${_lib}>)
+ endforeach()
+endif()
+
+unset(_test_libs)
+
+# This runs the blender_test executable with `--gtest_list_tests`, then
+# exposes those tests individually to the ctest runner.
+# See https://cmake.org/cmake/help/v3.18/module/GoogleTest.html
+include(GoogleTest)
+
+set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
+ ${CMAKE_CURRENT_LIST_DIR}/BlenderAddTests.cmake
+)
+
+gtest_discover_tests(blender_test
+ WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
+# So that it will run after the install phase that will copy the required libraries
+ DISCOVERY_MODE PRE_TEST
+# So that unit tests know where to find files:
+ EXTRA_ARGS
+ --test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests"
+ --test-release-dir "${TEST_INSTALL_DIR}/${BLENDER_VERSION}"
+)
diff --git a/tests/gtests/runner/blender_test.cc b/tests/gtests/runner/blender_test.cc
new file mode 100644
index 00000000000..fbce7a4283f
--- /dev/null
+++ b/tests/gtests/runner/blender_test.cc
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/* This file is intentionally left blank. It is necessary for CMake to have a source file for each
+ * executable. However, the blender_tests test runner only comprises of statically linked library
+ * files, including its main function.
+ *
+ * See source/blender/blenkernel/CMakeLists.txt for an example of how to add unit tests to the test
+ * runner. */
diff --git a/tests/gtests/testing/CMakeLists.txt b/tests/gtests/testing/CMakeLists.txt
index e08ee486933..d557b27f272 100644
--- a/tests/gtests/testing/CMakeLists.txt
+++ b/tests/gtests/testing/CMakeLists.txt
@@ -18,12 +18,17 @@
# All rights reserved.
# ***** END GPL LICENSE BLOCK *****
+add_definitions(${GFLAGS_DEFINES})
+add_definitions(${GLOG_DEFINES})
+add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
+
set(INC
.
..
${GLOG_INCLUDE_DIRS}
${GFLAGS_INCLUDE_DIRS}
../../../extern/gtest/include
+ ../../../intern/guardedalloc
)
set(INC_SYS
@@ -36,6 +41,7 @@ set(SRC
)
set(LIB
+ bf_intern_guardedalloc
)
blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/tests/gtests/testing/testing.h b/tests/gtests/testing/testing.h
index 32cb6e7f18a..34928035b7d 100644
--- a/tests/gtests/testing/testing.h
+++ b/tests/gtests/testing/testing.h
@@ -7,6 +7,15 @@
#include "glog/logging.h"
#include "gtest/gtest.h"
+namespace blender::tests {
+
+/* These strings are passed on the CLI with the --test-asset-dir and --test-release-dir arguments.
+ * The arguments are added automatically when invoking tests via `ctest`. */
+const std::string &flags_test_asset_dir(); /* ../lib/tests in the SVN directory. */
+const std::string &flags_test_release_dir(); /* bin/{blender version} in the build directory. */
+
+} // namespace blender::tests
+
#define EXPECT_V3_NEAR(a, b, eps) \
{ \
EXPECT_NEAR(a[0], b[0], eps); \
diff --git a/tests/gtests/testing/testing_main.cc b/tests/gtests/testing/testing_main.cc
index 6b3a8e5515d..9816e71526b 100644
--- a/tests/gtests/testing/testing_main.cc
+++ b/tests/gtests/testing/testing_main.cc
@@ -19,8 +19,36 @@
#include "testing/testing.h"
+#include "MEM_guardedalloc.h"
+
+DEFINE_string(test_assets_dir, "", "lib/tests directory from SVN containing the test assets.");
+DEFINE_string(test_release_dir, "", "bin/{blender version} directory of the current build.");
+
+namespace blender::tests {
+
+const std::string &flags_test_asset_dir()
+{
+ if (FLAGS_test_assets_dir.empty()) {
+ ADD_FAILURE()
+ << "Pass the flag --test-assets-dir and point to the lib/tests directory from SVN.";
+ }
+ return FLAGS_test_assets_dir;
+}
+
+const std::string &flags_test_release_dir()
+{
+ if (FLAGS_test_release_dir.empty()) {
+ ADD_FAILURE()
+ << "Pass the flag --test-release-dir and point to the bin/{blender version} directory.";
+ }
+ return FLAGS_test_release_dir;
+}
+
+} // namespace blender::tests
+
int main(int argc, char **argv)
{
+ MEM_initialize_memleak_detection();
testing::InitGoogleTest(&argc, argv);
BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
diff --git a/tests/gtests/usd/CMakeLists.txt b/tests/gtests/usd/CMakeLists.txt
deleted file mode 100644
index 56759f4ccea..00000000000
--- a/tests/gtests/usd/CMakeLists.txt
+++ /dev/null
@@ -1,106 +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.
-#
-# The Original Code is Copyright (C) 2019, Blender Foundation
-# All rights reserved.
-# ***** END GPL LICENSE BLOCK *****
-
-# This suppresses the warning "This file includes at least one deprecated or antiquated
-# header which may be removed without further notice at a future date", which is caused
-# by the USD library including <ext/hash_set> on Linux. This has been reported at:
-# https://github.com/PixarAnimationStudios/USD/issues/1057.
-if(UNIX AND NOT APPLE)
- add_definitions(-D_GLIBCXX_PERMIT_BACKWARD_HASH)
-endif()
-if(WIN32)
- add_definitions(-DNOMINMAX)
-endif()
-add_definitions(-DPXR_STATIC)
-
-set(INC
- .
- ..
- ../../../source/blender/blenlib
- ../../../source/blender/blenkernel
- ../../../source/blender/io/usd
- ../../../source/blender/makesdna
- ../../../source/blender/depsgraph
- ${USD_INCLUDE_DIRS}
- ${BOOST_INCLUDE_DIR}
- ${TBB_INCLUDE_DIR}
-)
-
-set(LIB
- bf_blenloader_test
- bf_blenloader
-
- # Should not be needed but gives windows linker errors if the ocio libs are linked before this:
- bf_intern_opencolorio
- bf_gpu
-
- bf_usd
-
- ${BOOST_LIBRARIES}
- ${TBB_LIBRARIES}
-)
-
-include_directories(${INC})
-
-setup_libdirs()
-get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP)
-
-set(SRC
- abstract_hierarchy_iterator_test.cc
- hierarchy_context_order_test.cc
-)
-
-# TODO(Sybren): re-enable this unit test.
-# if(NOT APPLE)
-# # TODO(Sybren): This unit test has only been tested on Linux, and should possibly be
-# # restructured to support other platforms as well.
-# list(APPEND SRC usd_stage_creation_test.cc)
-# endif()
-
-
-if(WITH_BUILDINFO)
- list(APPEND SRC
- "$<TARGET_OBJECTS:buildinfoobj>"
- )
-endif()
-
-# get_cmake_property(_variableNames VARIABLES)
-# list(SORT _variableNames)
-# foreach(_variableName ${_variableNames})
-# message(STATUS "${_variableName}=${${_variableName}}")
-# endforeach()
-
-# Works on Linux, not on Windows:
-# set(_usd_DATAFILES_DIR "${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}/datafiles/usd")
-set(_usd_DATAFILES_DIR "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION}/datafiles/usd")
-
-BLENDER_SRC_GTEST_EX(
- NAME usd
- SRC "${SRC}"
- EXTRA_LIBS "${LIB}"
- COMMAND_ARGS
- --test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests"
-)
-# TODO(Sybren): add the below CLI argument to the test when the usd_stage_creation_test.cc
-# test is reenabled.
-# --test-usd-datafiles-dir "${_usd_DATAFILES_DIR}"
-unset(_usd_DATAFILES_DIR)
-
-setup_liblinks(usd_test)
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index a3df01fdbe2..1b78a938a04 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -337,7 +337,7 @@ add_blender_test(
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/io_tests/export_ply_vertices.ply'\)
--md5_source=${TEST_OUT_DIR}/io_tests/export_ply_vertices.ply
- --md5=37faba0aa2014451b27f951afa92f870 --md5_method=FILE
+ --md5=ee6ce2e69c1d9a7418ff0548f6338f70 --md5_method=FILE
)
diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py
index 2d477c5a6f0..66545dc85c7 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'],
@@ -243,6 +243,63 @@ class DupliGroupExportTest(AbstractAlembicTest):
2.0, 3.0, 0.0, 1.0]
)
+ @with_tempdir
+ def test_multiple_duplicated_hierarchies(self, tempdir: pathlib.Path):
+ abc = tempdir / "multiple-duplicated-hierarchies.abc"
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1)" % abc.as_posix()
+ self.run_blender('multiple-duplicated-hierarchies.blend', script)
+
+ # This is the expected hierarchy:
+ # ABC
+ # `--Triangle
+ # |--Triangle
+ # |--Empty-1
+ # | `--Pole-1-0
+ # | |--Pole
+ # | `--Block-1-1
+ # | `--Block
+ # |--Empty
+ # | `--Pole-0
+ # | |--Pole
+ # | `--Block-1
+ # | `--Block
+ # |--Empty-2
+ # | `--Pole-2-0
+ # | |--Pole
+ # | `--Block-2-1
+ # | `--Block
+ # `--Empty-0
+ # `--Pole-0-0
+ # |--Pole
+ # `--Block-0-1
+ # `--Block
+
+ # Now check the resulting Alembic file.
+ xform = self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/Block-1-1/.xform')
+ self.assertEqual(1, xform['.inherits'])
+ self.assertAlmostEqualFloatArray(
+ xform['.vals'],
+ [1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 2.0, 0.0, 1.0]
+ )
+
+ # If the property can be gotten, the hierarchy is okay. No need to actually check each xform.
+ self.abcprop(abc, '/Triangle/.xform')
+ self.abcprop(abc, '/Triangle/Empty-1/.xform')
+ self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/.xform')
+ self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/Block-1-1/.xform')
+ self.abcprop(abc, '/Triangle/Empty/.xform')
+ self.abcprop(abc, '/Triangle/Empty/Pole-0/.xform')
+ self.abcprop(abc, '/Triangle/Empty/Pole-0/Block-1/.xform')
+ self.abcprop(abc, '/Triangle/Empty-2/.xform')
+ self.abcprop(abc, '/Triangle/Empty-2/Pole-2-0/.xform')
+ self.abcprop(abc, '/Triangle/Empty-2/Pole-2-0/Block-2-1/.xform')
+ self.abcprop(abc, '/Triangle/Empty-0/.xform')
+ self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/.xform')
+ self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/Block-0-1/.xform')
+
class CurveExportTest(AbstractAlembicTest):
@with_tempdir
@@ -253,10 +310,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 +337,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 +399,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 +418,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 +522,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/bevel_operator.py b/tests/python/bevel_operator.py
index 884bd356b96..50f52b958f7 100644
--- a/tests/python/bevel_operator.py
+++ b/tests/python/bevel_operator.py
@@ -56,10 +56,10 @@ def main():
['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_5', 'bevel', {'offset': 0.2, 'segments': 3}],
['EDGE', {2, 3}, 'Pyr4_test', 'Pyr4_result_6', 'bevel', {'offset': 0.2, 'segments': 2}],
['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_7', 'bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.15}],
- ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_8', 'bevel', {'offset': 0.75, 'segments': 4, 'vertex_only': True}],
+ ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_8', 'bevel', {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}],
# 20
['VERT', {1}, 'Pyr4_test', 'Pyr4_result_9', 'bevel',
- {'offset': 0.75, 'segments': 3, 'vertex_only': True, 'profile': 0.25}],
+ {'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}],
['EDGE', {2, 3}, 'Pyr6_test', 'Pyr6_result_1', 'bevel', {'offset': 0.2}],
['EDGE', {8, 2, 3}, 'Pyr6_test', 'Pyr6_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
['EDGE', {0, 2, 3, 4, 6, 7, 9, 10, 11}, 'Pyr6_test', 'Pyr6_result_3', 'bevel',
@@ -68,7 +68,7 @@ def main():
# 25
['EDGE', {8, 9, 11}, 'Sept_test', 'Sept_result_2', 'bevel', {'offset': 0.1, 'offset_type': 'WIDTH'}],
['EDGE', {2, 8, 9, 12, 13, 14}, 'Saddle_test', 'Saddle_result_1', 'bevel', {'offset': 0.3, 'segments': 5}],
- ['VERT', {4}, 'Saddle_test', 'Saddle_result_2', 'bevel', {'offset': 0.6, 'segments': 6, 'vertex_only': True}],
+ ['VERT', {4}, 'Saddle_test', 'Saddle_result_2', 'bevel', {'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}],
['EDGE', {2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62, 112, 113, 114, 115},
'Bent_test', 'Bent_result_1', 'bevel', {'offset': 0.2, 'segments': 3}],
['EDGE', {1, 8, 9, 10, 11}, 'Bentlines_test', 'Bentlines_result_1', 'bevel', {'offset': 0.2, 'segments': 3}],
@@ -82,17 +82,17 @@ def main():
['EDGE', {0, 1, 2, 10}, 'Wires_test', 'Wires_test_result_1', 'bevel', {'offset': 0.3}],
# 35
['VERT', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 'Wires_test', 'Wires_test_result_2', 'bevel',
- {'offset': 0.3, 'vertex_only': True}],
+ {'offset': 0.3, 'affect': 'VERTICES'}],
['EDGE', {3, 4, 5}, 'tri', 'tri_result_1', 'bevel', {'offset': 0.2}],
['EDGE', {3, 4, 5}, 'tri', 'tri_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
['EDGE', {3, 4, 5}, 'tri', 'tri_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
['EDGE', {3, 4}, 'tri', 'tri_result_4', 'bevel', {'offset': 0.2}],
# 40
['EDGE', {3, 4}, 'tri', 'tri_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['VERT', {3}, 'tri', 'tri_result_6', 'bevel', {'offset': 0.2, 'vertex_only': True}],
- ['VERT', {3}, 'tri', 'tri_result_7', 'bevel', {'offset': 0.2, 'segments': 2, 'vertex_only': True}],
- ['VERT', {3}, 'tri', 'tri_result_8', 'bevel', {'offset': 0.2, 'segments': 3, 'vertex_only': True}],
- ['VERT', {1}, 'tri', 'tri_result_9', 'bevel', {'offset': 0.2, 'vertex_only': True}],
+ ['VERT', {3}, 'tri', 'tri_result_6', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
+ ['VERT', {3}, 'tri', 'tri_result_7', 'bevel', {'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}],
+ ['VERT', {3}, 'tri', 'tri_result_8', 'bevel', {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}],
+ ['VERT', {1}, 'tri', 'tri_result_9', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
# 45
['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_1', 'bevel', {'offset': 0.2}],
['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
@@ -104,7 +104,7 @@ def main():
['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_7', 'bevel', {'offset': 0.2}],
['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_8', 'bevel', {'offset': 0.2, 'segments': 2}],
['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_9', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['VERT', {3}, 'tri1gap', 'tri1gap_result_10', 'bevel', {'offset': 0.2, 'vertex_only': True}],
+ ['VERT', {3}, 'tri1gap', 'tri1gap_result_10', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
# 55
['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_1', 'bevel', {'offset': 0.2}],
['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
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():