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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rwxr-xr-xbuild_files/build_environment/install_deps.sh12
-rw-r--r--build_files/buildbot/master.cfg9
-rw-r--r--build_files/buildbot/slave_compile.py6
-rw-r--r--build_files/cmake/platform/platform_unix.cmake21
-rwxr-xr-xdoc/python_api/sphinx_doc_update.py7
-rw-r--r--extern/ceres/CMakeLists.txt5
-rw-r--r--extern/ceres/ChangeLog891
-rwxr-xr-xextern/ceres/bundle.sh21
-rw-r--r--extern/ceres/files.txt5
-rw-r--r--extern/ceres/include/ceres/cost_function_to_functor.h3
-rw-r--r--extern/ceres/include/ceres/covariance.h56
-rw-r--r--extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h22
-rw-r--r--extern/ceres/include/ceres/gradient_checker.h239
-rw-r--r--extern/ceres/include/ceres/internal/port.h22
-rw-r--r--extern/ceres/include/ceres/iteration_callback.h6
-rw-r--r--extern/ceres/include/ceres/jet.h106
-rw-r--r--extern/ceres/include/ceres/local_parameterization.h22
-rw-r--r--extern/ceres/include/ceres/numeric_diff_cost_function.h23
-rw-r--r--extern/ceres/include/ceres/problem.h7
-rw-r--r--extern/ceres/include/ceres/rotation.h3
-rw-r--r--extern/ceres/include/ceres/solver.h31
-rw-r--r--extern/ceres/include/ceres/version.h2
-rw-r--r--extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc22
-rw-r--r--extern/ceres/internal/ceres/covariance.cc23
-rw-r--r--extern/ceres/internal/ceres/covariance_impl.cc172
-rw-r--r--extern/ceres/internal/ceres/covariance_impl.h9
-rw-r--r--extern/ceres/internal/ceres/gradient_checker.cc276
-rw-r--r--extern/ceres/internal/ceres/gradient_checking_cost_function.cc224
-rw-r--r--extern/ceres/internal/ceres/gradient_checking_cost_function.h87
-rw-r--r--extern/ceres/internal/ceres/gradient_problem_solver.cc18
-rw-r--r--extern/ceres/internal/ceres/is_close.cc59
-rw-r--r--extern/ceres/internal/ceres/is_close.h51
-rw-r--r--extern/ceres/internal/ceres/line_search_minimizer.cc26
-rw-r--r--extern/ceres/internal/ceres/local_parameterization.cc74
-rw-r--r--extern/ceres/internal/ceres/map_util.h2
-rw-r--r--extern/ceres/internal/ceres/parameter_block.h37
-rw-r--r--extern/ceres/internal/ceres/problem.cc4
-rw-r--r--extern/ceres/internal/ceres/problem_impl.cc19
-rw-r--r--extern/ceres/internal/ceres/problem_impl.h2
-rw-r--r--extern/ceres/internal/ceres/reorder_program.cc5
-rw-r--r--extern/ceres/internal/ceres/residual_block.h2
-rw-r--r--extern/ceres/internal/ceres/schur_complement_solver.cc7
-rw-r--r--extern/ceres/internal/ceres/solver.cc61
-rw-r--r--extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc7
-rw-r--r--extern/ceres/internal/ceres/stringprintf.cc41
-rw-r--r--extern/ceres/internal/ceres/trust_region_minimizer.cc1293
-rw-r--r--extern/ceres/internal/ceres/trust_region_minimizer.h123
-rw-r--r--extern/ceres/internal/ceres/trust_region_step_evaluator.cc107
-rw-r--r--extern/ceres/internal/ceres/trust_region_step_evaluator.h122
-rw-r--r--extern/ceres/internal/ceres/trust_region_strategy.h4
-rw-r--r--intern/atomic/atomic_ops.h26
-rw-r--r--intern/atomic/intern/atomic_ops_ext.h70
-rw-r--r--intern/atomic/intern/atomic_ops_msvc.h28
-rw-r--r--intern/atomic/intern/atomic_ops_unix.h46
-rw-r--r--intern/cycles/app/cycles_standalone.cpp2
-rw-r--r--intern/cycles/app/cycles_xml.cpp25
-rw-r--r--intern/cycles/blender/CCL_api.h11
-rw-r--r--intern/cycles/blender/addon/properties.py117
-rw-r--r--intern/cycles/blender/addon/ui.py35
-rw-r--r--intern/cycles/blender/addon/version_update.py6
-rw-r--r--intern/cycles/blender/blender_python.cpp91
-rw-r--r--intern/cycles/blender/blender_sync.cpp61
-rw-r--r--intern/cycles/cmake/external_libs.cmake4
-rw-r--r--intern/cycles/device/device.cpp54
-rw-r--r--intern/cycles/device/device.h9
-rw-r--r--intern/cycles/device/device_cuda.cpp21
-rw-r--r--intern/cycles/device/device_intern.h1
-rw-r--r--intern/cycles/device/device_multi.cpp115
-rw-r--r--intern/cycles/device/device_opencl.cpp7
-rw-r--r--intern/cycles/device/opencl/opencl.h9
-rw-r--r--intern/cycles/device/opencl/opencl_util.cpp25
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/geom/geom_object.h10
-rw-r--r--intern/cycles/kernel/kernel_accumulate.h38
-rw-r--r--intern/cycles/kernel/kernel_bake.h2
-rw-r--r--intern/cycles/kernel/kernel_emission.h15
-rw-r--r--intern/cycles/kernel/kernel_passes.h16
-rw-r--r--intern/cycles/kernel/kernel_path.h173
-rw-r--r--intern/cycles/kernel/kernel_path_branched.h42
-rw-r--r--intern/cycles/kernel/kernel_path_common.h6
-rw-r--r--intern/cycles/kernel/kernel_path_surface.h12
-rw-r--r--intern/cycles/kernel/kernel_path_volume.h12
-rw-r--r--intern/cycles/kernel/kernel_random.h17
-rw-r--r--intern/cycles/kernel/kernel_shader.h9
-rw-r--r--intern/cycles/kernel/kernel_types.h5
-rw-r--r--intern/cycles/kernel/split/kernel_direct_lighting.h3
-rw-r--r--intern/cycles/kernel/svm/svm_brick.h2
-rw-r--r--intern/cycles/kernel/svm/svm_image.h3
-rw-r--r--intern/cycles/kernel/svm/svm_tex_coord.h9
-rw-r--r--intern/cycles/render/buffers.cpp8
-rw-r--r--intern/cycles/render/image.cpp270
-rw-r--r--intern/cycles/render/image.h14
-rw-r--r--intern/cycles/render/integrator.cpp8
-rw-r--r--intern/cycles/render/integrator.h2
-rw-r--r--intern/cycles/render/light.cpp5
-rw-r--r--intern/cycles/render/session.h3
-rw-r--r--intern/cycles/util/util_atomic.h2
-rw-r--r--intern/cycles/util/util_hash.h6
-rw-r--r--intern/cycles/util/util_math.h5
-rw-r--r--intern/cycles/util/util_path.cpp4
-rw-r--r--intern/cycles/util/util_stats.h4
-rw-r--r--intern/guardedalloc/intern/mallocn_guarded_impl.c12
-rw-r--r--intern/guardedalloc/intern/mallocn_lockfree_impl.c24
-rw-r--r--intern/iksolver/intern/IK_QSegment.h1
-rw-r--r--intern/iksolver/intern/IK_Solver.cpp1
-rw-r--r--intern/libmv/libmv/simple_pipeline/modal_solver_test.cc6
-rw-r--r--make.bat5
-rw-r--r--release/scripts/startup/bl_operators/mesh.py50
-rw-r--r--release/scripts/startup/bl_operators/wm.py50
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py48
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py8
-rw-r--r--release/scripts/startup/bl_ui/space_image.py17
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py7
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py13
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py3
-rw-r--r--source/blender/blenkernel/BKE_armature.h1
-rw-r--r--source/blender/blenkernel/BKE_blender_undo.h1
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_library.h8
-rw-r--r--source/blender/blenkernel/BKE_library_query.h3
-rw-r--r--source/blender/blenkernel/BKE_object_deform.h2
-rw-r--r--source/blender/blenkernel/intern/action.c4
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c11
-rw-r--r--source/blender/blenkernel/intern/blender_copybuffer.c4
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c7
-rw-r--r--source/blender/blenkernel/intern/blendfile.c4
-rw-r--r--source/blender/blenkernel/intern/cachefile.c4
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c2
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c4
-rw-r--r--source/blender/blenkernel/intern/library.c248
-rw-r--r--source/blender/blenkernel/intern/library_query.c40
-rw-r--r--source/blender/blenkernel/intern/library_remap.c47
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c5
-rw-r--r--source/blender/blenkernel/intern/object_deform.c22
-rw-r--r--source/blender/blenkernel/intern/pbvh.c2
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c17
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c36
-rw-r--r--source/blender/blenkernel/intern/smoke.c11
-rw-r--r--source/blender/blenkernel/intern/sound.c3
-rw-r--r--source/blender/blenlib/intern/task.c8
-rw-r--r--source/blender/blenloader/intern/readfile.c1
-rw-r--r--source/blender/blenloader/intern/versioning_270.c29
-rw-r--r--source/blender/blenloader/intern/writefile.c4
-rw-r--r--source/blender/bmesh/bmesh_class.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c265
-rw-r--r--source/blender/bmesh/intern/bmesh_core.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.c8
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c107
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.c75
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.c114
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.h6
-rw-r--r--source/blender/bmesh/operators/bmo_bridge.c6
-rw-r--r--source/blender/bmesh/operators/bmo_dissolve.c12
-rw-r--r--source/blender/bmesh/operators/bmo_fill_edgeloop.c2
-rw-r--r--source/blender/bmesh/operators/bmo_hull.c3
-rw-r--r--source/blender/bmesh/operators/bmo_join_triangles.c10
-rw-r--r--source/blender/bmesh/operators/bmo_removedoubles.c2
-rw-r--r--source/blender/bmesh/operators/bmo_triangulate.c5
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c2
-rw-r--r--source/blender/collada/MeshImporter.cpp15
-rw-r--r--source/blender/collada/TransformWriter.cpp5
-rw-r--r--source/blender/collada/collada.h3
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.cpp2
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cpp2
-rw-r--r--source/blender/depsgraph/CMakeLists.txt1
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h102
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc53
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cycle.cc35
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc360
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h28
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc92
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h128
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc211
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc13
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h18
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc57
-rw-r--r--source/blender/depsgraph/intern/depsgraph_intern.h12
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_debug.cc19
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_debug.h4
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node.cc30
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node.h32
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.cc81
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.h50
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_operation.cc2
-rw-r--r--source/blender/editors/animation/anim_ops.c4
-rw-r--r--source/blender/editors/armature/armature_edit.c1
-rw-r--r--source/blender/editors/armature/pose_edit.c2
-rw-r--r--source/blender/editors/armature/pose_slide.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c23
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_util.h2
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c9
-rw-r--r--source/blender/editors/interface/interface_handlers.c56
-rw-r--r--source/blender/editors/interface/interface_icons.c233
-rw-r--r--source/blender/editors/interface/resources.c14
-rw-r--r--source/blender/editors/io/io_collada.c1
-rw-r--r--source/blender/editors/mesh/editmesh_rip.c2
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c12
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c4
-rw-r--r--source/blender/editors/object/object_add.c17
-rw-r--r--source/blender/editors/object/object_relations.c16
-rw-r--r--source/blender/editors/object/object_vgroup.c5
-rw-r--r--source/blender/editors/render/render_opengl.c35
-rw-r--r--source/blender/editors/screen/screen_ops.c23
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c9
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c2
-rw-r--r--source/blender/editors/space_file/filelist.c2
-rw-r--r--source/blender/editors/space_view3d/drawobject.c4
-rw-r--r--source/blender/editors/space_view3d/drawvolume.c187
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h6
-rw-r--r--source/blender/editors/transform/transform.c4
-rw-r--r--source/blender/editors/transform/transform_conversions.c4
-rw-r--r--source/blender/editors/util/undo.c23
-rw-r--r--source/blender/gpu/GPU_shader.h1
-rw-r--r--source/blender/gpu/intern/gpu_shader.c13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl25
-rw-r--r--source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl12
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp11
-rw-r--r--source/blender/makesdna/DNA_rigidbody_types.h12
-rw-r--r--source/blender/makesdna/DNA_scene_types.h2
-rw-r--r--source/blender/makesdna/DNA_smoke_types.h23
-rw-r--r--source/blender/makesrna/intern/rna_ID.c26
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c54
-rw-r--r--source/blender/makesrna/intern/rna_rigidbody.c142
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c10
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c56
-rw-r--r--source/blender/makesrna/intern/rna_space.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c107
-rw-r--r--source/blender/makesrna/intern/rna_wm.c46
-rw-r--r--source/blender/modifiers/intern/MOD_array.c8
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c5
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c5
-rw-r--r--source/blender/python/intern/bpy_library_load.c7
-rw-r--r--source/blender/render/intern/source/shadeoutput.c10
-rw-r--r--source/blender/windowmanager/WM_types.h2
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c12
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c4
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c5
-rw-r--r--source/creator/CMakeLists.txt58
-rw-r--r--source/gameengine/VideoTexture/VideoDeckLink.cpp4
-rw-r--r--tests/python/CMakeLists.txt2
253 files changed, 6110 insertions, 3912 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 351768c5425..98635bd198a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -333,7 +333,7 @@ option(WITH_ALEMBIC "Enable Alembic Support" OFF)
option(WITH_ALEMBIC_HDF5 "Enable Legacy Alembic Support (not officially supported)" OFF)
if(APPLE)
- option(WITH_CODEC_QUICKTIME "Enable Quicktime Support" ON)
+ option(WITH_CODEC_QUICKTIME "Enable Quicktime Support" OFF)
endif()
# 3D format support
@@ -508,6 +508,12 @@ mark_as_advanced(WITH_C11)
option(WITH_CXX11 "Build with C++11 standard enabled, for development use only!" ${_cxx11_init})
mark_as_advanced(WITH_CXX11)
+# Compiler toolchain
+if(CMAKE_COMPILER_IS_GNUCC)
+ option(WITH_LINKER_GOLD "Use ld.gold linker which is usually faster than ld.bfd" ON)
+ mark_as_advanced(WITH_LINKER_GOLD)
+endif()
+
# Dependency graph
option(WITH_LEGACY_DEPSGRAPH "Build Blender with legacy dependency graph" ON)
mark_as_advanced(WITH_LEGACY_DEPSGRAPH)
@@ -731,7 +737,7 @@ elseif(WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_AUDASPACE OR WITH_INTERNATIONAL O
# Keep enabled
else()
# New dependency graph needs either Boost or C++11 for function bindings.
- if(NOT USE_CXX11)
+ if(NOT WITH_CXX11)
# Enabled but we don't need it
set(WITH_BOOST OFF)
endif()
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index d6ea7d99f04..5a02a96bdff 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -1858,6 +1858,9 @@ compile_OSL() {
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
cmake_d="$cmake_d -D USE_SIMD=sse2"
+ if [ "$USE_CXX11" = true ]; then
+ cmake_d="$cmake_d -D OSL_BUILD_CPP11=1"
+ fi
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
@@ -4023,9 +4026,6 @@ install_OTHER() {
fi
if [ "$_do_compile_llvm" = true ]; then
- install_packages_DEB libffi-dev
- # LLVM can't find the debian ffi header dir
- _FFI_INCLUDE_DIR=`dpkg -L libffi-dev | grep -e ".*/ffi.h" | sed -r 's/(.*)\/ffi.h/\1/'`
PRINT ""
compile_LLVM
have_llvm=true
@@ -4044,7 +4044,6 @@ install_OTHER() {
if [ "$_do_compile_osl" = true ]; then
if [ "$have_llvm" = true ]; then
- install_packages_DEB flex bison libtbb-dev
PRINT ""
compile_OSL
else
@@ -4063,7 +4062,6 @@ install_OTHER() {
fi
if [ "$_do_compile_osd" = true ]; then
- install_packages_DEB flex bison libtbb-dev
PRINT ""
compile_OSD
fi
@@ -4080,10 +4078,6 @@ install_OTHER() {
fi
if [ "$_do_compile_collada" = true ]; then
- install_packages_DEB libpcre3-dev
- # Find path to libxml shared lib...
- _XML2_LIB=`dpkg -L libxml2-dev | grep -e ".*/libxml2.so"`
- # No package
PRINT ""
compile_OpenCOLLADA
fi
diff --git a/build_files/buildbot/master.cfg b/build_files/buildbot/master.cfg
index 8bd23357fc6..6b7191cd57b 100644
--- a/build_files/buildbot/master.cfg
+++ b/build_files/buildbot/master.cfg
@@ -94,6 +94,7 @@ all_repositories = {
r'git://git.blender.org/blender-translations.git': 'blender-translations',
r'git://git.blender.org/blender-addons.git': 'blender-addons',
r'git://git.blender.org/blender-addons-contrib.git': 'blender-addons-contrib',
+ r'git://git.blender.org/blender-dev-tools.git': 'blender-dev-tools',
r'https://svn.blender.org/svnroot/bf-blender/': 'lib svn',
}
@@ -128,6 +129,7 @@ def schedule_force_build(name):
forcesched.CodebaseParameter(hide=True, codebase="blender-translations"),
forcesched.CodebaseParameter(hide=True, codebase="blender-addons"),
forcesched.CodebaseParameter(hide=True, codebase="blender-addons-contrib"),
+ forcesched.CodebaseParameter(hide=True, codebase="blender-dev-tools"),
forcesched.CodebaseParameter(hide=True, codebase="lib svn")],
properties=[]))
@@ -143,6 +145,7 @@ def schedule_build(name, hour, minute=0):
"blender-translations": {"repository": "", "branch": "master"},
"blender-addons": {"repository": "", "branch": "master"},
"blender-addons-contrib": {"repository": "", "branch": "master"},
+ "blender-dev-tools": {"repository": "", "branch": "master"},
"lib svn": {"repository": "", "branch": "trunk"}},
branch=current_branch,
builderNames=[name],
@@ -264,7 +267,8 @@ def generic_builder(id, libdir='', branch='', rsync=False):
for submodule in ('blender-translations',
'blender-addons',
- 'blender-addons-contrib'):
+ 'blender-addons-contrib',
+ 'blender-dev-tools'):
f.addStep(git_submodule_step(submodule))
f.addStep(git_step(branch))
@@ -299,7 +303,8 @@ add_builder(c, 'linux_glibc219_i686_cmake', '', generic_builder, hour=3)
add_builder(c, 'linux_glibc219_x86_64_cmake', '', generic_builder, hour=4)
add_builder(c, 'win32_cmake_vc2013', 'windows_vc12', generic_builder, hour=3)
add_builder(c, 'win64_cmake_vc2013', 'win64_vc12', generic_builder, hour=4)
-add_builder(c, 'win64_cmake_vc2015', 'win64_vc14', generic_builder, hour=5)
+add_builder(c, 'win32_cmake_vc2015', 'windows_vc14', generic_builder, hour=5)
+add_builder(c, 'win64_cmake_vc2015', 'win64_vc14', generic_builder, hour=6)
# STATUS TARGETS
#
diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/slave_compile.py
index c2bfd882fde..76d538ad578 100644
--- a/build_files/buildbot/slave_compile.py
+++ b/build_files/buildbot/slave_compile.py
@@ -183,10 +183,8 @@ if 'cmake' in builder:
print('Condifuration FAILED!')
sys.exit(retcode)
- if 'win32' in builder:
- command = ['msbuild', 'INSTALL.vcxproj', '/Property:PlatformToolset=v120_xp', '/p:Configuration=Release']
- elif 'win64' in builder:
- command = ['msbuild', 'INSTALL.vcxproj', '/p:Configuration=Release']
+ if 'win32' in builder or 'win64' in builder:
+ command = ['cmake', '--build', '.', '--target', target_name, '--config', 'Release']
else:
command = target_chroot_prefix + ['make', '-s', '-j2', target_name]
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index e33141f8012..62e0caa7c43 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -384,17 +384,18 @@ add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
if(CMAKE_COMPILER_IS_GNUCC)
set(PLATFORM_CFLAGS "-pipe -fPIC -funsigned-char -fno-strict-aliasing")
- # use ld.gold linker if available, could make optional
- execute_process(
- COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version
- ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
- if("${LD_VERSION}" MATCHES "GNU gold")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold")
- else()
- message(STATUS "GNU gold linker isn't available, using the default system linker.")
+ if(WITH_LINKER_GOLD)
+ execute_process(
+ COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version
+ ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
+ if("${LD_VERSION}" MATCHES "GNU gold")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold")
+ else()
+ message(STATUS "GNU gold linker isn't available, using the default system linker.")
+ endif()
+ unset(LD_VERSION)
endif()
- unset(LD_VERSION)
# CLang is the same as GCC for now.
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
diff --git a/doc/python_api/sphinx_doc_update.py b/doc/python_api/sphinx_doc_update.py
index 5301f39b2e3..c7f0367a2a0 100755
--- a/doc/python_api/sphinx_doc_update.py
+++ b/doc/python_api/sphinx_doc_update.py
@@ -142,8 +142,11 @@ def main():
zip_name = "blender_python_reference_%s" % blenver_zip # We can't use 'release' postfix here...
zip_path = os.path.join(args.mirror_dir, zip_name)
with zipfile.ZipFile(zip_path, 'w') as zf:
- for de in os.scandir(api_dir):
- zf.write(de.path, arcname=os.path.join(zip_name, de.name))
+ for dirname, _, filenames in os.walk(api_dir):
+ for filename in filenames:
+ filepath = os.path.join(dirname, filename)
+ zip_filepath = os.path.join(zip_name, os.path.relpath(filepath, api_dir))
+ zf.write(filepath, arcname=zip_filepath)
os.rename(zip_path, os.path.join(api_dir, "%s.zip" % zip_name))
# VII) Create symlinks and html redirects.
diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt
index 2ad8c543088..a6e9cd9c356 100644
--- a/extern/ceres/CMakeLists.txt
+++ b/extern/ceres/CMakeLists.txt
@@ -73,10 +73,12 @@ set(SRC
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
@@ -116,6 +118,7 @@ set(SRC
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
@@ -204,6 +207,7 @@ set(SRC
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
@@ -248,6 +252,7 @@ set(SRC
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
diff --git a/extern/ceres/ChangeLog b/extern/ceres/ChangeLog
index 0e6c195174c..ae8d42a7c95 100644
--- a/extern/ceres/ChangeLog
+++ b/extern/ceres/ChangeLog
@@ -1,659 +1,588 @@
-commit aef9c9563b08d5f39eee1576af133a84749d1b48
-Author: Alessandro Gentilini <agentilini@gmail.com>
-Date: Tue Oct 6 20:43:45 2015 +0200
+commit 8590e6e8e057adba4ec0083446d00268565bb444
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Oct 27 12:29:37 2016 -0700
- Add test for Bessel functions.
+ Remove two checks from rotation.h
+
+ This allows rotation.h to remove its dependency on glog.
- Change-Id: Ief5881e8027643d7ef627e60a88fdbad17f3d884
+ Change-Id: Ia6aede93ee51a4bd4039570dc8edd100a7045329
-commit 49c86018e00f196c4aa9bd25daccb9919917efee
-Author: Alessandro Gentilini <agentilini@gmail.com>
-Date: Wed Sep 23 21:59:44 2015 +0200
+commit e892499e8d8977b9178a760348bdd201ec5f3489
+Author: Je Hyeong Hong <jhh37@outlook.com>
+Date: Tue Oct 18 22:49:11 2016 +0100
- Add Bessel functions in order to use them in residual code.
+ Relax the tolerance in QuaternionParameterizationTestHelper.
- See "How can I use the Bessel function in the residual function?" at
- https://groups.google.com/d/msg/ceres-solver/Vh1gpqac8v0/NIK1EiWJCAAJ
+ 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().
- Change-Id: I3e80d9f9d1cadaf7177076e493ff46ace5233b76
+ Change-Id: Ic4d3f26c0acdf5f16fead80dfdc53df9e7dabbf9
-commit dfb201220c034fde00a242d0533bef3f73b2907d
-Author: Simon Rutishauser <simon.rutishauser@pix4d.com>
-Date: Tue Oct 13 07:33:58 2015 +0200
+commit 7ed9e2fb7f1dff264c5e4fbaa89ee1c4c99df269
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Wed Oct 19 04:45:23 2016 -0700
- Make miniglog threadsafe on non-windows system by using
- localtime_r() instead of localtime() for time formatting
+ Occured -> Occurred.
- Change-Id: Ib8006c685cd8ed4f374893bef56c4061ca2c9747
+ Thanks to Phillip Huebner for reporting this.
+
+ Change-Id: I9cddfbb373aeb496961d08e434fe661bff4abd29
-commit 41455566ac633e55f222bce7c4d2cb4cc33d5c72
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Sep 28 22:43:42 2015 +0100
+commit b82f97279682962d8c8ae1b6d9e801ba072a0ab1
+Author: Je Hyeong Hong <jhh37@outlook.com>
+Date: Tue Oct 18 21:18:32 2016 +0100
- Remove link-time optimisation (LTO).
+ Fix a test error in autodiff_test.cc.
- - On GCC 4.9+ although GCC supports LTO, it requires use of the
- non-default gcc-ar & gcc-ranlib. Whilst we can ensure Ceres is
- compiled with these, doing so with GCC 4.9 causes multiple definition
- linker errors of static ints inside Eigen when compiling the tests
- and examples when they are not also built with LTO.
- - On OS X (Xcode 6 & 7) after the latest update to gtest, if LTO
- is used when compiling the tests (& examples), two tests fail
- due to typeinfo::operator== (things are fine if only Ceres itself is
- compiled with LTO).
- - This patch disables LTO for all compilers. It should be revisited when
- the performance is more stable across our supported compilers.
+ 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.
- Change-Id: I17b52957faefbdeff0aa40846dc9b342db1b02e3
+ Change-Id: I6cd3379083b1a10c7cd0a9cc83fd6962bb993cc9
-commit 89c40005bfceadb4163bd16b7464b3c2ce740daf
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Sep 27 13:37:26 2015 +0100
-
- Only use LTO when compiling Ceres itself, not tests or examples.
-
- - If Ceres is built as a shared library, and LTO is enabled for Ceres
- and the tests, then type_info::operator==() incorrectly returns false
- in gtests' CheckedDowncastToActualType() in the following tests:
- -- levenberg_marquardt_strategy_test.
- -- gradient_checking_cost_function_test.
- on at least Xcode 6 & 7 as reported here:
- https://github.com/google/googletest/issues/595.
- - This does not appear to be a gtest issue, but is perhaps an LLVM bug
- or an RTTI shared library issue. Either way, disabling the use of
- LTO when compiling the test application resolves the issue.
- - Allow LTO to be enabled for GCC, if it is supported.
- - Add CMake function to allow easy appending to target properties s/t
- Ceres library-specific compile flags can be iteratively constructed.
-
- Change-Id: I923e6aae4f7cefa098cf32b2f8fc19389e7918c9
-
-commit 0794f41cca440f7f65d9a44e671f66f6e498ef7c
+commit 5690b447de5beed6bdda99b7f30f372283c2fb1a
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Sep 26 14:10:15 2015 -0700
+Date: Thu Oct 13 09:52:02 2016 -0700
- Documentation updates.
+ Fix documentation source for templated functions in rotation.h
- 1. Fix a typo in the Trust Region algorithm.
- 2. Add ARL in the list of users.
- 3. Update the version history.
+ Change-Id: Ic1b2e6f0e6eb9914f419fd0bb5af77b66252e57c
+
+commit 2f8f98f7e8940e465de126fb51282396f42bea20
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Thu Oct 13 09:35:18 2016 -0700
+
+ Prepare for 1.12.0RC1
- Change-Id: Ic286e8ef1a71af07f3890b7592dd3aed9c5f87ce
+ Change-Id: I23eaf0b46117a01440143001b74dacfa5e57cbf0
-commit 90e32a8dc437dfb0e6747ce15a1f3193c13b7d5b
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Sep 21 21:08:25 2015 +0100
+commit 55c12d2e9569fe4aeac3ba688ac36810935a37ba
+Author: Damon Kohler <damonkohler@google.com>
+Date: Wed Oct 5 16:30:31 2016 +0200
+
+ Adds package.xml to support Catkin.
+
+ Change-Id: I8ad4d36a8b036417604a54644e0bb70dd1615feb
+
+commit 0bcce6565202f5476e40f12afc0a99eb44bd9dfb
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Mon Oct 10 23:30:42 2016 -0700
- Use old minimum iOS version flags on Xcode < 7.0.
+ Fix tabs in Android.mk
- - The newer style, which are more specific and match the SDK names
- are not available on Xcode < 7.0.
+ Change-Id: Ie5ab9a8ba2b727721565e1ded242609b6df5f8f5
+
+commit e6ffe2667170d2fc435443685c0163396fc52d7b
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Mon Oct 10 22:47:08 2016 -0700
+
+ Update the version history.
- Change-Id: I2f07a0365183d2781157cdb05fd49b30ae001ac5
+ Change-Id: I9a57b0541d6cebcb695ecb364a1d4ca04ea4e06c
-commit 26cd5326a1fb99ae02c667eab9942e1308046984
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Sep 21 10:16:01 2015 +0100
+commit 0a4ccb7ee939ab35b22e26758401e039b033b176
+Author: David Gossow <dgossow@google.com>
+Date: Wed Sep 7 21:38:12 2016 +0200
- Add gtest-specific flags when building/using as a shared library.
+ Relaxing Jacobian matching in Gradient Checker test.
- - Currently these flags are only used to define the relevant DLL export
- prefix for Windows.
+ 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.
- Change-Id: I0c05207b512cb4a985390aefc779b91febdabb38
+ Change-Id: I48e804c9c705dc485ce74ddfe51037d4957c8fcb
-commit c4c79472112a49bc1340da0074af2d15b1c89749
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Sep 20 18:26:59 2015 +0100
+commit ee44fc91b59584921c1d1c8db153fda6d633b092
+Author: Je Hyeong Hong <jhh37@outlook.com>
+Date: Mon Oct 3 12:19:30 2016 +0100
- Clean up iOS.cmake to use xcrun/xcodebuild & libtool.
+ Fix an Intel compiler error in covariance_impl.cc.
- - Substantial cleanup of iOS.cmake to use xcrun & xcodebuild to
- determine the SDK & tool paths.
- - Use libtool -static to link libraries instead of ar + ranlib, which
- is not compatible with Xcode 7+, this change should be backwards
- compatible to at least Xcode 6.
- - Force locations of unordered_map & shared_ptr on iOS to work around
- check_cxx_source_compiles() running in a forked CMake instance without
- access to the variables (IOS_PLATFORM) defined by the user.
- - Minor CMake style updates.
+ 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.
- Change-Id: I5f83a60607db34d461ebe85f9dce861f53d98277
+ Change-Id: I1ecb68e89b7faf79e4153dfe6675c390d1780db4
-commit 155765bbb358f1d19f072a4b54825faf1c059910
+commit 9026d69d1ce1e0bcd21debd54a38246d85c7c6e4
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Sep 16 06:56:08 2015 -0700
+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
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Sat Sep 17 16:31:41 2016 -0700
- Import the latest version of gtest and gmock.
+ Update version history
- Change-Id: I4b686c44bba823cab1dae40efa99e31340d2b52a
+ Change-Id: Ib2f0138ed7a1879ca3b2173e54092f7ae8dd5c9d
-commit 0c4647b8f1496c97c6b9376d9c49ddc204aa08dd
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Wed Sep 16 20:01:11 2015 +0100
+commit 01e23e3d33178fdd050973666505c1080cfe04c3
+Author: David Gossow <dgossow@google.com>
+Date: Thu Sep 8 12:22:28 2016 +0200
- Remove FAQ about increasing inlining threshold for Clang.
+ Removing duplicate include directive.
- - Changing the inlining threshold for Clang as described has a minimal
- effect on user performance.
- - The problem that originally prompted the belief that it did was
- due to an erroneous CXX flag configuration (in user code).
-
- Change-Id: I03017241c0f87b8dcefb8c984ec3b192afd97fc2
+ Change-Id: I729ae6501497746d1bb615cb893ad592e16ddf3f
-commit f4b768b69afcf282568f9ab3a3f0eb8078607468
+commit 99b8210cee92cb972267537fb44bebf56f812d52
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Mon Sep 14 13:53:24 2015 -0700
+Date: Wed Sep 7 15:31:30 2016 -0700
- Lint changes from William Rucklidge
+ Update Android.mk to include new files.
- Change-Id: I0dac2549a8fa2bfd12f745a8d8a0db623b7ec1ac
+ Change-Id: Id543ee7d2a65b65c868554a17f593c0a4958e873
-commit 5f2f05c726443e35767d677daba6d25dbc2d7ff8
+commit 195d8d13a6a3962ac39ef7fcdcc6add0216eb8bc
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Fri Sep 11 22:19:38 2015 -0700
+Date: Tue Sep 6 07:12:23 2016 -0700
- Refactor system_test
+ Remove two DCHECKs from CubicHermiteSpline.
- 1. Move common test infrastructure into test_util.
- 2. system_test now only contains powells function.
- 3. Add bundle_adjustment_test.
+ They were present as debugging checks but were causing problems
+ with the build on 32bit i386 due to numerical cancellation issues,
+ where x ~ -epsilon.
- Instead of a single function which computes everything,
- there is now a test for each solver configuration which
- uses the reference solution computed by the fixture.
+ 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: I16a9a9a83a845a7aaf28762bcecf1a8ff5aee805
-
-commit 1936d47e213142b8bf29d3f548905116092b093d
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Tue Sep 8 23:27:42 2015 +0100
-
- Revert increased inline threshold (iff Clang) to exported Ceres target.
+ https://github.com/ceres-solver/ceres-solver/issues/212
- - Increasing the inline threshold results in very variable performance
- improvements, and could potentially confuse users if they are trying
- to set the inline threshold themselves.
- - As such, we no longer export our inline threshold configuration for
- Clang, but instead document how to change it in the FAQs.
+ Thanks to @NeroBurner and @debalance for reporting this.
- Change-Id: I88e2e0001e4586ba2718535845ed1e4b1a5b72bc
+ Change-Id: I66480e86d4fa0a4b621204f2ff44cc3ff8d01c04
-commit a66d89dcda47cefda83758bfb9e7374bec4ce866
+commit 83041ac84f2d67c28559c67515e0e596a3f3aa20
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Sep 5 16:50:20 2015 -0700
+Date: Fri Sep 2 19:10:35 2016 -0700
- Get ready for 1.11.0RC1
+ Fix some compiler warnings.
- Update version numbers.
- Drop CERES_VERSION_ABI macro.
+ Reported by Richard Trieu.
- Change-Id: Ib3eadabb318afe206bb196a5221b195d26cbeaa0
+ Change-Id: I202b7a7df09cc19c92582d276ccf171edf88a9fb
-commit 1ac3dd223c179fbadaed568ac532af4139c75d84
+commit 8c4623c63a2676e79e7917bb0561f903760f19b9
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Sep 5 15:30:01 2015 -0700
+Date: Thu Sep 1 00:05:09 2016 -0700
- Fix a bug in CompressedRowSparseMatrix::AppendRows
+ Update ExpectArraysClose to use ExpectClose instead of EXPECT_NEAR
- The test for CompressedRowSparseMatrix::AppendRows tries to add
- a matrix of size zero, which results in an invalid pointer deferencing
- even though that pointer is never written to.
+ The documentation for ExpectArraysClose and its implementation
+ did not match.
- Change-Id: I97dba37082bd5dad242ae1af0447a9178cd92027
-
-commit 67622b080c8d37b5e932120a53d4ce76b80543e5
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Sep 5 13:18:38 2015 -0700
-
- Fix a pointer access bug in Ridders' algorithm.
+ This change makes the polynomial_test not fail on 64bit AMD builds.
- A pointer to an Eigen matrix was being used as an array.
+ Thanks to Phillip Huebner for reporting this.
- Change-Id: Ifaea14fa3416eda5953de49afb78dc5a6ea816eb
+ Change-Id: I503f2d3317a28d5885a34f8bdbccd49d20ae9ba2
-commit 5742b7d0f14d2d170054623ccfee09ea214b8ed9
+commit 2fd39fcecb47eebce727081c9ffb8edf86c33669
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Aug 26 09:24:33 2015 -0700
+Date: Thu Sep 1 16:05:06 2016 -0700
- Improve performance of SPARSE_NORMAL_CHOLESKY + dynamic_sparsity
+ 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.
- The outer product computation logic in SparseNormalCholeskySolver
- does not work well with dynamic sparsity. The overhead of computing
- the sparsity pattern of the normal equations is only amortized if
- the sparsity is constant. If the sparsity can change from call to call
- SparseNormalCholeskySolver will actually be more expensive.
+ https://github.com/ceres-solver/ceres-solver/issues/216
- For Eigen and for CXSparse we now explicitly compute the normal
- equations using their respective matrix-matrix product routines and solve.
- Change-Id: Ifbd8ed78987cdf71640e66ed69500442526a23d4
+ Change-Id: I596481219cfbf7622d49a6511ea29193b82c8ba3
-commit d0b6cf657d6ef0dd739e958af9a5768f2eecfd35
-Author: Keir Mierle <mierle@gmail.com>
-Date: Fri Sep 4 18:43:41 2015 -0700
+commit 716f049a7b91a8f3a4632c367d9534d1d9190a81
+Author: Mike Vitus <vitus@google.com>
+Date: Wed Aug 31 13:38:30 2016 -0700
- Fix incorrect detect structure test
+ Convert pose graph 2D example to glog and gflags.
- Change-Id: I7062f3639147c40b57947790d3b18331a39a366b
+ Change-Id: I0ed75a60718ef95199bb36f33d9eb99157d11d40
-commit 0e8264cc47661651a11e2dd8570c210082963545
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sat Aug 22 16:23:05 2015 +0100
+commit 46c5ce89dda308088a5fdc238d0c126fdd2c2b58
+Author: David Gossow <dgossow@google.com>
+Date: Wed Aug 31 18:40:57 2016 +0200
- Add increased inline threshold (iff Clang) to exported Ceres target.
+ Fix compiler errors on some systems
- - When compiled with Clang, Ceres and all of the examples are compiled
- with an increased inlining-threshold, as the default value can result
- in poor Eigen performance.
- - Previously, client code using Ceres would typically not use an
- increased inlining-threshold (unless the user has specifically added
- it themselves). However, increasing the inlining threshold can result
- in significant performance improvements in auto-diffed CostFunctions.
- - This patch adds the inlining-threshold flags to the interface flags
- for the Ceres CMake target s/t any client code using Ceres (via
- CMake), and compiled with Clang, will now be compiled with the same
- increased inlining threshold as used by Ceres itself.
+ This fixes some signed-unsigned comparisons and a missing header
+ include.
- Change-Id: I31e8f1abfda140d22e85bb48aa57f028a68a415e
+ Change-Id: Ieb2bf6e905faa74851bc4ac4658d2f1da24b6ecc
-commit a1b3fce9e0a4141b973f6b4dd9b08c4c13052d52
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Aug 31 14:14:56 2015 +0100
+commit b102d53e1dd7dab132e58411183b6fffc2090590
+Author: David Gossow <dgossow@google.com>
+Date: Wed Aug 31 10:21:20 2016 +0200
- Add optional export of Ceres build directory to new features list.
+ Gradient checker multithreading bugfix.
- Change-Id: I6f1e42b41957ae9cc98fd9dcd1969ef64c4cd96f
-
-commit e46777d8df068866ef80902401a03e29348d11ae
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Mon Aug 31 12:41:54 2015 +0100
-
- Credit reporters of buildsystem bugs in version history.
+ 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: I16fe7973534cd556d97215e84268ae0b8ec4e11a
+ Change-Id: I314ef1df2be52595370d9af05851bf6da39bb45e
-commit 01548282cb620e5e3ac79a63a391cd0afd5433e4
+commit 79a28d1e49af53f67af7f3387d07e7c9b7339433
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Aug 30 22:29:27 2015 -0700
+Date: Wed Aug 31 06:47:45 2016 -0700
- Update the version history.
+ Rename a confusingly named member of Solver::Options
+
+ Solver::Options::numeric_derivative_relative_step_size to
+ Solver::Options::gradient_check_numeric_derivative_relative_step_size
- Change-Id: I29873bed31675e0108f1a44f53f7bc68976b7f98
+ Change-Id: Ib89ae3f87e588d4aba2a75361770d2cec26f07aa
-commit 2701429f770fce69ed0c77523fa43d7bc20ac6dc
+commit 358ae741c8c4545b03d95c91fa546d9a36683677
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Aug 30 21:33:57 2015 -0700
+Date: Wed Aug 31 06:58:41 2016 -0700
- Use Eigen::Dynamic instead of ceres::DYNAMIC in numeric_diff.h
+ Note that Problem::Evaluate cannot be called from an IterationCallback
- Change-Id: Iccb0284a8fb4c2160748dfae24bcd595f1d4cb5c
+ Change-Id: Ieabdc2d40715e6b547ab22156ba32e9c8444b7ed
-commit 4f049db7c2a3ee8cf9910c6eac96be6a28a5999c
-Author: Tal Ben-Nun <tbennun@gmail.com>
-Date: Wed May 13 15:43:51 2015 +0300
+commit 44044e25b14d7e623baae4505a17c913bdde59f8
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Wed Aug 31 05:50:58 2016 -0700
- Adaptive numeric differentiation using Ridders' method.
+ Update the NumTraits for Jets
- This method numerically computes function derivatives in different
- scales, extrapolating between intermediate results to conserve function
- evaluations. Adaptive differentiation is essential to produce accurate
- results for functions with noisy derivatives.
+ 1. Use AVX if EIGEN_VECTORIZE_AVX is defined.
+ 2. Make the cost of division same as the cost of multiplication.
- Full changelist:
- -Created a new type of NumericDiffMethod (RIDDERS).
- -Implemented EvaluateRiddersJacobianColumn in NumericDiff.
- -Created unit tests with f(x) = x^2 + [random noise] and
- f(x) = exp(x).
+ These are updates to the original numtraits update needed for eigen 3.3
+ that Shaheen Gandhi sent out.
- Change-Id: I2d6e924d7ff686650272f29a8c981351e6f72091
+ Change-Id: Ic1e3ed7d05a659c7badc79a894679b2dd61c51b9
-commit 070bba4b43b4b7449628bf456a10452fd2b34d28
+commit 4b6ad5d88e45ce8638c882d3e8f08161089b6dba
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Tue Aug 25 13:37:33 2015 -0700
+Date: Sat Aug 27 23:21:55 2016 -0700
- Lint fixes from William Rucklidge
+ Use ProductParameterization in bundle_adjuster.cc
- Change-Id: I719e8852859c970091df842e59c44e02e2c65827
-
-commit 887a20ca7f02a1504e35f7cabbdfb2e0842a0b0b
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Wed Aug 12 21:41:43 2015 +0100
-
- Build position independent code when compiling Ceres statically.
+ 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.
- - Previously, when Ceres was built as a static library we did not
- compile position independent code. This means that the resulting
- static library could not be linked against shared libraries, but
- could be used by executables.
- - To enable the use of a static Ceres library by other shared libraries
- as reported in [1], the static library must be generated from
- position independent code (except on Windows, where PIC does not
- apply).
+ 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.
- [1] https://github.com/Itseez/opencv_contrib/pull/290#issuecomment-130389471
+ This leads to a more than 2x improvements in the linear solver time.
- Change-Id: I99388f1784ece688f91b162d009578c5c97ddaf6
+ Change-Id: I78b8f06696f81fee54cfe1a4ae193ee8a5f8e920
-commit 860bba588b981a5718f6b73e7e840e5b8757fe65
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Tue Aug 25 09:43:21 2015 -0700
+commit bfc916cf1cf753b85c1e2ac037e2019ee891f6f9
+Author: Shaheen Gandhi <visigoth@gmail.com>
+Date: Thu Aug 4 12:10:14 2016 -0700
- Fix a bug in DetectStructure
+ Allow ceres to be used with the latest version of Eigen
- The logic for determing static/dynamic f-block size in
- DetectStructure was broken in a corner case, where the very first
- row block which was used to initialize the f_block_size contained
- more than one f blocks of varying sizes. The way the if block
- was structured, no iteration was performed on the remaining
- f-blocks and the loop failed to detect that the f-block size
- was actually changing.
-
- If in the remaining row blocks, there were no row blocks
- with varying f-block sizes, the function will erroneously
- return a static f-block size.
-
- Thanks to Johannes Schonberger for providing a reproduction for this
- rather tricky corner case.
-
- Change-Id: Ib442a041d8b7efd29f9653be6a11a69d0eccd1ec
+ Change-Id: Ief3b0f6b405484ec04ecd9ab6a1e1e5409a594c2
-commit b0cbc0f0b0a22f01724b7b647a4a94db959cc4e4
-Author: Johannes Schönberger <hannesschoenberger@gmail.com>
-Date: Thu Aug 20 14:21:30 2015 -0400
+commit edbd48ab502aa418ad9700ee5c3ada5f9268b90a
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Sun Jul 10 14:13:51 2016 +0100
- Reduce memory footprint of SubsetParameterization
+ 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.
- Change-Id: If113cb4696d5aef3e50eed01fba7a3d4143b7ec8
+ Change-Id: Ia39dac9fe746f1fc6310e08553f85f3c37349707
-commit ad2a99777786101411a971e59576ca533a297013
-Author: Sergey Sharybin <sergey.vfx@gmail.com>
-Date: Sat Aug 22 11:18:45 2015 +0200
+commit f6df6c05dd83b19fa90044106ebaca40957998ae
+Author: Mike Vitus <vitus@google.com>
+Date: Thu Aug 18 19:27:43 2016 -0700
- Fix for reoder program unit test when built without suitesparse
-
- This commit fixes failure of reorder_program_test when Ceres is built without
- any suitesparse.
+ Add an example for modeling and solving a 3D pose graph SLAM problem.
- Change-Id: Ia23ae8dfd20c482cb9cd1301f17edf9a34df3235
+ Change-Id: I750ca5f20c495edfee5f60ffedccc5bd8ba2bb37
-commit 4bf3868beca9c17615f72ec03730cddb3676acaa
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Aug 9 15:24:45 2015 -0700
+commit ac3b8e82175122e38bafaaa9cd419ba3cee11087
+Author: David Gossow <dgossow@google.com>
+Date: Fri Apr 29 16:07:11 2016 +0200
- Fix a bug in the Schur eliminator
+ Gradient checking cleanup and local parameterization bugfix
- The schur eliminator treats rows with e blocks and row with
- no e blocks separately. The template specialization logic only
- applies to the rows with e blocks.
+ 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.
- So, in cases where the rows with e-blocks have a fixed size f-block
- but the rows without e-blocks have f-blocks of varying sizes,
- DetectStructure will return a static f-block size, but we need to be
- careful that we do not blindly use that static f-block size everywhere.
+ 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.
- This patch fixes a bug where such care was not being taken, where
- it was assumed that the static f-block size could be assumed for all
- f-block sizes.
+ 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.
- A new test is added, which triggers an exception in debug mode. In
- release mode this error does not present itself, due to a peculiarity
- of the way Eigen works.
+ 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.
- Thanks to Werner Trobin for reporting this bug.
+ 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: I8ae7aabf8eed8c3f9cf74b6c74d632ba44f82581
+ Change-Id: I5e1bb97b8a899436cea25101efe5011b0bb13282
-commit 1635ce726078f00264b89d7fb6e76fd1c2796e59
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Aug 19 00:26:02 2015 -0700
+commit d4264ec10d9a270b53b5db86c0245ae8cbd2cf18
+Author: Mike Vitus <vitus@google.com>
+Date: Wed Aug 17 13:39:05 2016 -0700
- Fix a bug in the reordering code.
-
- When the user provides an ordering which starts at a non-zero group id,
- or has gaps in the groups, then CAMD, the algorithm used to reorder
- the program can crash or return garbage results.
-
- The solution is to map the ordering into grouping constraints, and then
- to re-number the groups to be contiguous using a call to
- MapValuesToContiguousRange. This was already done for CAMD based
- ordering for Schur type solvers, but was not done for SPARSE_NORMAL_CHOLESKY.
-
- Thanks to Bernhard Zeisl for not only reporting the bug but also
- providing a reproduction.
+ Add a quaternion local parameterization for Eigen's quaternion element convention.
- Change-Id: I5cfae222d701dfdb8e1bda7f0b4670a30417aa89
+ Change-Id: I7046e8b24805313c5fb6a767de581d0054fcdb83
-commit 4c3f8987e7f0c51fd367cf6d43d7eb879e79589f
-Author: Simon Rutishauser <simon.rutishauser@pix4d.com>
-Date: Thu Aug 13 11:10:44 2015 +0200
+commit fd7cab65ef30fbc33612220abed52dd5073413c4
+Author: Mike Vitus <vitus@google.com>
+Date: Wed Aug 10 09:29:12 2016 -0700
- Add missing CERES_EXPORT to ComposedLoss
+ Fix typos in the pose graph 2D example.
- Change-Id: Id7db388d41bf53e6e5704039040c9d2c6bf4c29c
+ Change-Id: Ie024ff6b6cab9f2e8011d21121a91931bd987bd1
-commit 1a740cc787b85b883a0703403a99fe49662acb79
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Tue Aug 11 18:08:05 2015 -0700
+commit 375dc348745081f89693607142d8b6744a7fb6b4
+Author: Mike Vitus <vitus@google.com>
+Date: Wed Aug 3 18:51:16 2016 -0700
- Add the option to use numeric differentiation to nist and more_garbow_hillstrom
+ Remove duplicate entry for the NIST example in the docs.
- Change-Id: If0a5caef90b524dcf5e2567c5b681987f5459401
+ Change-Id: Ic4e8f9b68b77b5235b5c96fe588cc56866dab759
-commit ea667ede5c038d6bf3d1c9ec3dbdc5072d1beec6
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Aug 9 16:56:13 2015 +0100
+commit f554681bf22d769abc12dd6d346ef65f9bb22431
+Author: Mike Vitus <vitus@google.com>
+Date: Mon Jul 25 18:30:48 2016 -0700
- Fix EIGENSPARSE option help s/t it displays in CMake ncurses GUI.
-
- - Shorten description for EIGENSPARSE to a single line, as otherwise
- it is not correctly displayed in the ncurses CMake GUI.
- - Made explicit in description that this results in an LGPL licensed
- version of Ceres (this is also made clear in the CMake log output if
- EIGENSPARSE is enabled).
+ Add an example for modeling and solving a 2D pose graph SLAM problem.
- Change-Id: I11678a9cbc7a817133c22128da01055a3cb8a26d
+ Change-Id: Ia89b12af7afa33e7b1b9a68d69cf2a0b53416737
-commit a14ec27fb28ab2e8d7f1c9d88e41101dc6c0aab5
-Author: Richard Stebbing <richie.stebbing@gmail.com>
-Date: Fri Aug 7 08:42:03 2015 -0700
+commit e1bcc6e0f51512f43aa7bfb7b0d62f7ac1d0cd4b
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Wed May 18 07:52:48 2016 -0700
- Fix SparseNormalCholeskySolver with dynamic sparsity.
-
- The previous implementation incorrectly cached the outer product matrix
- pattern even when `dynamic_sparsity = true`.
+ Add additional logging for analyzing orderings
- Change-Id: I1e58315a9b44f2f457d07c56b203ab2668bfb8a2
+ Change-Id: Ic68d2959db35254e2895f11294fb25de4d4b8a81
-commit 3dd7fced44ff00197fa9fcb1f2081d12be728062
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Aug 9 16:38:50 2015 +0100
+commit 16980b4fec846f86910c18772b8145bcb55f4728
+Author: Mike Vitus <vitus@google.com>
+Date: Fri Jul 15 13:37:49 2016 -0700
- Remove legacy dependency detection macros.
+ Delete the remove_definitons command from sampled_functions
+ CMakeLists.txt because it will be inherited from the top level examples
+ CMakeLists.txt.
- - Before the new CMake buildsystem in 1.8, Ceres used non-standard
- HINTS variables for dependencies. For backwards compatibility CMake
- macros were added to translate these legacy variables into the new
- (standard) variables.
- - As it has now been multiple releases since the legacy variables
- were used and they no longer appear in any of the documentation
- support for them has now expired.
-
- Change-Id: I2cc72927ed711142ba7943df334ee008181f86a2
+ Change-Id: I25593587df0ae84fd8ddddc589bc2a13f3777427
-commit 8b32e258ccce1eed2a50bb002add16cad13aff1e
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Aug 9 15:42:39 2015 +0100
+commit a04490be97800e78e59db5eb67fa46226738ffba
+Author: Mike Vitus <vitus@google.com>
+Date: Thu Jul 14 10:10:13 2016 -0700
- Fix failed if() condition expansion if gflags is not found.
-
- - If a CMake-ified version of gflags is not detected, then
- gflags_LIBRARIES is not set and the TARGET condition within a
- multiconditional if() statement prevents configuration.
+ Add readme for the sampled_function example.
- Change-Id: Ia92e97523d7a1478ab36539726b9540d7cfee5d0
+ Change-Id: I9468b6a7b9f2ffdd2bf9f0dd1f4e1d5f894e540c
-commit cc8d47aabb9d63ba4588ba7295058a6191c2df83
+commit ff11d0e63d4678188e8cabd40a532ba06912fe5a
Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Aug 9 15:18:42 2015 +0100
+Date: Wed Jun 29 09:31:45 2016 +0100
- Update all CMake to lowercase function name style.
+ Use _j[0,1,n]() Bessel functions on MSVC to avoid deprecation errors.
- - Updated to new CMake style where function names are all lowercase,
- this will be backwards compatible as CMake function names are
- case insensitive.
- - Updated using Emacs' M-x unscreamify-cmake-buffer.
+ - 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.
- Change-Id: If7219816f560270e59212813aeb021353a64a0e2
+ Change-Id: If7ac5dbb856748f9900be93ec0452a40c0b00524
-commit 1f106904c1f47460c35ac03258d6506bb2d60838
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Aug 9 14:55:02 2015 +0100
+commit 8ea86e1614cf77644ce782e43cde6565a54444f5
+Author: Nicolai Wojke <nwojke@uni-koblenz.de>
+Date: Mon Apr 25 14:24:41 2016 +0200
- Update minimum iOS version to 7.0 for shared_ptr/unordered_map.
-
- - In order to correctly detect shared_ptr (& unordered_map)
- the iOS version must be >= 7.0 (Xcode 5.0+). This only affects the
- SIMULATOR(64) platform builds, as the OS (device) build uses the
- latest SDK which is now likely 8.0+.
+ Fix: Copy minimizer option 'is_silent' to LinSearchDirection::Options
- Change-Id: Iefec8f03408b8cdc7a495f442ebba081f800adb0
+ Change-Id: I23b4c3383cad30033c539ac93883d77c8dd4ba1a
-commit 16ecd40523a408e7705c9fdb0e159cef2007b8ab
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sat Aug 8 17:32:31 2015 +0100
+commit 080ca4c5f2ac42620971a07f06d2d13deb7befa8
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Sun Apr 24 22:46:54 2016 -0700
- Fix bug in gflags' <= 2.1.2 exported CMake configuration.
-
- - gflags <= 2.1.2 has a bug in its exported gflags-config.cmake:
- https://github.com/gflags/gflags/issues/110 whereby it sets
- gflags_LIBRARIES to a non-existent 'gflags' target.
- - This causes linker errors if gflags is installed in a non-standard
- location (as otherwise CMake resolves gflags to -lgflags which
- links if gflags is installed somewhere on the current path).
- - We now check for this case, and search for the correct gflags imported
- target and update gflags_LIBRARIES to reference it if found, otherwise
- proceed on to the original manual search to try to find gflags.
+ Fix typos in users.rst
- Change-Id: Iceccc3ee53c7c2010e41cc45255f966e7b13d526
+ Change-Id: Ifdc67638a39403354bc9589f42a1b42cb9984dd2
-commit 56be8de007dfd65ed5a31c795eb4a08ad765f411
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Thu Jun 25 21:31:00 2015 +0100
+commit 21ab397dc55335c147fdd795899b1f8981037b09
+Author: Sameer Agarwal <sameeragarwal@google.com>
+Date: Sun Apr 24 21:13:00 2016 -0700
- Add docs for new CXX11 option & mask option for Windows.
-
- - The CXX11 option has no effect on Windows, as there, any new C++11
- features are enabled by default, as such to avoid confusion we only
- present the option for non-Windows.
+ Make some Jet comparisons exact.
- Change-Id: I38925ae3bb8c16682d404468ba95c611a519b9b9
+ Change-Id: Ia08c72f3b8779df96f5c0d5a954b2c0a1dd3a061
-commit cf863b6415ac4dbf3626e70adeac1ac0f3d87ee5
+commit ee40f954cf464087eb8943abf4d9db8917a33fbe
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Thu Aug 6 14:52:18 2015 -0700
+Date: Sun Apr 24 07:49:55 2016 -0700
- Remove the spec file needed for generating RPMs.
+ Add colmap to users.rst
- Now that ceres is part of RawHide, there is no need to carry
- this spec file with the ceres distribution.
-
- Change-Id: Icc400b9874ba05ba05b353e2658f1de94c72299e
+ Change-Id: I452a8c1dc6a3bc55734b2fc3a4002ff7939ba863
-commit 560940fa277a469c1ab34f1aa303ff1af9c3cacf
+commit 9665e099022bd06e53b0779550e9aebded7f274d
Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sat Jul 11 22:21:31 2015 -0700
+Date: Mon Apr 18 06:00:58 2016 -0700
- A refactor of the cubic interpolation code
+ Fix step norm evaluation in LineSearchMinimizer
- 1. Push the boundary handling logic into the underlying array
- object. This has two very significant impacts:
+ 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.
- a. The interpolation code becomes extremely simple to write
- and to test.
+ Change-Id: I9fef64cbb5622c9769c0413003cfb1dc6e89cfa3
+
+commit 620ca9d0668cd4a00402264fddca3cf6bd2e7265
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Mon Apr 18 15:14:11 2016 +0100
+
+ Remove use of -Werror when compiling Ceres.
- b. The user has more flexibility in implementing how out of bounds
- values are handled. We provide one default implementation.
+ - 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.
- Change-Id: Ic2f6cf9257ce7110c62e492688e5a6c8be1e7df2
+ Change-Id: I38e9ade28d4a90e53dcd918a7d470f1a1debd7b4
-commit dfdf19e111c2b0e6daeb6007728ec2f784106d49
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Wed Aug 5 15:20:57 2015 -0700
+commit 0c63bd3efbf1d41151c9fab41d4b77dc64c572c8
+Author: Mike Vitus <vitus@google.com>
+Date: Thu Apr 14 10:25:52 2016 -0700
- Lint cleanup from Jim Roseborough
+ Add floor and ceil functions to the Jet implementation.
- Change-Id: Id6845c85644d40e635ed196ca74fc51a387aade4
+ Change-Id: I72ebfb0e9ade2964dbf3a014225ead345d5ae352
-commit 7444f23ae245476a7ac8421cc2f88d6947fd3e5f
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Mon Aug 3 12:22:44 2015 -0700
+commit 9843f3280356c158d23c06a16085c6c5ba35e053
+Author: Alex Stewart <alexs.mac@gmail.com>
+Date: Mon Mar 7 21:24:32 2016 +0000
- Fix a typo in small_blas.h
-
- The reason this rather serious looking typo has not
- caused any problems uptil now is because NUM_ROW_B is
- computed but never actually used.
+ Report Ceres compile options as components in find_package().
- Thanks to Werner Trobin for pointing this out.
+ - 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.
- Change-Id: Id2b4d9326ec21baec8a85423e3270aefbafb611e
+ Change-Id: I65f1ddfd7697e6dd25bb4ac7e54f5097d3ca6266
-commit 5a48b92123b30a437f031eb24b0deaadc8f60d26
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sat Jul 4 17:59:52 2015 +0100
+commit e4d4d88bbe51b9cc0f7450171511abbea0779790
+Author: Timer <linyicx@126.com>
+Date: Fri Apr 8 15:42:18 2016 +0800
- Export Ceres build directory into local CMake package registry.
-
- - Optionally use CMake's export() functionality to export the Ceres
- build directory as a package into the local CMake package registry.
- - This enables the detection & use of Ceres from CMake *without*
- requiring that Ceres be installed.
+ Fix a spelling error in nnls_modeling.rst
- Change-Id: Ib5a7588446f490e1b405878475b6b1dd13accd1f
+ Change-Id: I341d901d3df993bc5397ed15e6cb330b0c38fd72
-commit d9790e77894ea99d38137d359d6118315b2d1601
-Author: Sameer Agarwal <sameeragarwal@google.com>
-Date: Sun Jul 12 19:39:47 2015 -0700
+commit 5512f58536e1be0d92010d8325b606e7b4733a08
+Author: Keir Mierle <mierle@gmail.com>
+Date: Thu Apr 7 12:03:16 2016 -0700
- Add ProductParameterization
+ Only use collapse() directive with OpenMP 3.0 or higher
- Often a parameter block is the Cartesian product of a number of
- manifolds. For example, a rigid transformation SE(3) = SO(3) x R^3
- In such cases, where you have the local parameterization
- of the individual manifolds available,
- ProductParameterization can be used to construct a local
- parameterization of the cartesian product.
+ Change-Id: Icba544c0494763c57eb6dc61e98379312ca15972
+
+commit d61e94da5225217cab7b4f93b72f97055094681f
+Author: Thomas Schneider <schneith@ethz.ch>
+Date: Wed Apr 6 10:40:29 2016 +0200
+
+ Add IsParameterBlockConstant to the ceres::Problem class.
- Change-Id: I4b5bcbd2407a38739c7725b129789db5c3d65a20
+ Change-Id: I7d0e828e81324443209c17fa54dd1d37605e5bfe
-commit 7b4fb69dad49eaefb5d2d47ef0d76f48ad7fef73
+commit 77d94b34741574e958a417561702d6093fba87fb
Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Sun Jun 28 21:43:46 2015 +0100
-
- Cleanup FindGflags & use installed gflags CMake config if present.
-
- - Split out gflags namespace detection methods:
- check_cxx_source_compiles() & regex, into separate functions.
- - Use installed/exported gflags CMake configuration (present for
- versions >= 2.1) if available, unless user expresses a preference not
- to, or specifies search directories, in which case fall back to manual
- search for components.
- -- Prefer installed gflags CMake configurations over exported gflags
- build directories on all OSs.
- - Remove custom version of check_cxx_source_compiles() that attempted
- to force the build type of the test project. This only worked for
- NMake on Windows, not MSVC as msbuild ignored our attempts to force
- the build type. Now we always use the regex method on Windows if
- we cannot find an installed gflags CMake configuration which works
- even on MSVC by bypassing msbuild.
- - Add default search paths for gflags on Windows.
-
- Change-Id: I083b267d97a7a5838a1314f3d41a61ae48d5a2d7
-
-commit b3063c047906d4a44503dc0187fdcbbfcdda5f38
-Author: Alex Stewart <alexs.mac@gmail.com>
-Date: Wed Jul 15 20:56:56 2015 +0100
+Date: Sun Feb 14 16:54:03 2016 +0000
- Add default glog install location on Windows to search paths.
+ Fix install path for CeresConfig.cmake to be architecture-aware.
+
+ - 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.
- Change-Id: I083d368be48986e6780c11460f5a07b2f3b6c900
+ Change-Id: If126260d7af685779487c01220ae178ac31f7aea
diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh
index 0eaf00f3989..a4f703ac33d 100755
--- a/extern/ceres/bundle.sh
+++ b/extern/ceres/bundle.sh
@@ -173,26 +173,5 @@ if(WITH_OPENMP)
)
endif()
-TEST_UNORDERED_MAP_SUPPORT()
-if(HAVE_STD_UNORDERED_MAP_HEADER)
- if(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE)
- add_definitions(-DCERES_STD_UNORDERED_MAP)
- else()
- if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE)
- add_definitions(-DCERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE)
- else()
- add_definitions(-DCERES_NO_UNORDERED_MAP)
- message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)")
- endif()
- endif()
-else()
- if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE)
- add_definitions(-DCERES_TR1_UNORDERED_MAP)
- else()
- add_definitions(-DCERES_NO_UNORDERED_MAP)
- message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)")
- endif()
-endif()
-
blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}")
EOF
diff --git a/extern/ceres/files.txt b/extern/ceres/files.txt
index f49f1fb0ded..4d973bbcdc2 100644
--- a/extern/ceres/files.txt
+++ b/extern/ceres/files.txt
@@ -149,6 +149,7 @@ 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/gradient_checker.cc
internal/ceres/gradient_checking_cost_function.cc
internal/ceres/gradient_checking_cost_function.h
internal/ceres/gradient_problem.cc
@@ -160,6 +161,8 @@ internal/ceres/householder_vector.h
internal/ceres/implicit_schur_complement.cc
internal/ceres/implicit_schur_complement.h
internal/ceres/integral_types.h
+internal/ceres/is_close.cc
+internal/ceres/is_close.h
internal/ceres/iterative_schur_complement_solver.cc
internal/ceres/iterative_schur_complement_solver.h
internal/ceres/lapack.cc
@@ -243,6 +246,8 @@ internal/ceres/trust_region_minimizer.cc
internal/ceres/trust_region_minimizer.h
internal/ceres/trust_region_preprocessor.cc
internal/ceres/trust_region_preprocessor.h
+internal/ceres/trust_region_step_evaluator.cc
+internal/ceres/trust_region_step_evaluator.h
internal/ceres/trust_region_strategy.cc
internal/ceres/trust_region_strategy.h
internal/ceres/types.cc
diff --git a/extern/ceres/include/ceres/cost_function_to_functor.h b/extern/ceres/include/ceres/cost_function_to_functor.h
index 6c67ac0f937..d2dc94725e4 100644
--- a/extern/ceres/include/ceres/cost_function_to_functor.h
+++ b/extern/ceres/include/ceres/cost_function_to_functor.h
@@ -130,7 +130,8 @@ class CostFunctionToFunctor {
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);
- CHECK_EQ(parameter_block_sizes.size(), num_parameter_blocks);
+ 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
diff --git a/extern/ceres/include/ceres/covariance.h b/extern/ceres/include/ceres/covariance.h
index dd20dc36ba1..930f96cf3ae 100644
--- a/extern/ceres/include/ceres/covariance.h
+++ b/extern/ceres/include/ceres/covariance.h
@@ -357,6 +357,28 @@ class CERES_EXPORT Covariance {
const double*> >& covariance_blocks,
Problem* problem);
+ // Compute a part of the covariance matrix.
+ //
+ // The vector parameter_blocks contains the parameter blocks that
+ // are used for computing the covariance matrix. From this vector
+ // all covariance pairs are generated. This allows the covariance
+ // estimation algorithm to only compute and store these blocks.
+ //
+ // parameter_blocks cannot contain duplicates. Bad things will
+ // happen if they do.
+ //
+ // Note that the list of covariance_blocks is only used to determine
+ // what parts of the covariance matrix are computed. The full
+ // Jacobian is used to do the computation, i.e. they do not have an
+ // impact on what part of the Jacobian is used for computation.
+ //
+ // The return value indicates the success or failure of the
+ // 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<const double*>& parameter_blocks,
+ Problem* problem);
+
// Return the block of the cross-covariance matrix corresponding to
// parameter_block1 and parameter_block2.
//
@@ -394,6 +416,40 @@ class CERES_EXPORT Covariance {
const double* parameter_block2,
double* covariance_block) const;
+ // Return the covariance matrix corresponding to all parameter_blocks.
+ //
+ // Compute must be called before calling GetCovarianceMatrix and all
+ // parameter_blocks must have been present in the vector
+ // parameter_blocks when Compute was called. Otherwise
+ // GetCovarianceMatrix returns false.
+ //
+ // covariance_matrix must point to a memory location that can store
+ // the size of the covariance matrix. The covariance matrix will be
+ // 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);
+
+ // Return the covariance matrix corresponding to parameter_blocks
+ // in the tangent space if a local parameterization is associated
+ // with one of the parameter blocks else returns the covariance
+ // matrix in the ambient space.
+ //
+ // Compute must be called before calling GetCovarianceMatrix and all
+ // parameter_blocks must have been present in the vector
+ // parameters_blocks when Compute was called. Otherwise
+ // GetCovarianceMatrix returns false.
+ //
+ // covariance_matrix must point to a memory location that can store
+ // the size of the covariance matrix. The covariance matrix will be
+ // a square matrix whose row and column count is equal to the sum of
+ // the sizes of the tangent spaces of the individual parameter
+ // blocks. The covariance matrix will be a row-major matrix.
+ bool GetCovarianceMatrixInTangentSpace(
+ const std::vector<const double*>& parameter_blocks,
+ double* covariance_matrix);
+
private:
internal::scoped_ptr<internal::CovarianceImpl> impl_;
};
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 c852d57a3fc..5770946a115 100644
--- a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h
+++ b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h
@@ -85,22 +85,6 @@ class DynamicNumericDiffCostFunction : public CostFunction {
options_(options) {
}
- // Deprecated. New users should avoid using this constructor. Instead, use the
- // constructor with NumericDiffOptions.
- DynamicNumericDiffCostFunction(
- const CostFunctor* functor,
- Ownership ownership,
- double relative_step_size)
- : functor_(functor),
- ownership_(ownership),
- options_() {
- LOG(WARNING) << "This constructor is deprecated and will be removed in "
- "a future version. Please use the NumericDiffOptions "
- "constructor instead.";
-
- options_.relative_step_size = relative_step_size;
- }
-
virtual ~DynamicNumericDiffCostFunction() {
if (ownership_ != TAKE_OWNERSHIP) {
functor_.release();
@@ -138,19 +122,19 @@ class DynamicNumericDiffCostFunction : public CostFunction {
std::vector<double> parameters_copy(parameters_size);
std::vector<double*> parameters_references_copy(block_sizes.size());
parameters_references_copy[0] = &parameters_copy[0];
- for (int block = 1; block < block_sizes.size(); ++block) {
+ for (size_t block = 1; block < block_sizes.size(); ++block) {
parameters_references_copy[block] = parameters_references_copy[block - 1]
+ block_sizes[block - 1];
}
// Copy the parameters into the local temp space.
- for (int block = 0; block < block_sizes.size(); ++block) {
+ for (size_t block = 0; block < block_sizes.size(); ++block) {
memcpy(parameters_references_copy[block],
parameters[block],
block_sizes[block] * sizeof(*parameters[block]));
}
- for (int block = 0; block < block_sizes.size(); ++block) {
+ for (size_t block = 0; block < block_sizes.size(); ++block) {
if (jacobians[block] != NULL &&
!NumericDiff<CostFunctor, method, DYNAMIC,
DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC,
diff --git a/extern/ceres/include/ceres/gradient_checker.h b/extern/ceres/include/ceres/gradient_checker.h
index 28304159b44..6d285daf1d9 100644
--- a/extern/ceres/include/ceres/gradient_checker.h
+++ b/extern/ceres/include/ceres/gradient_checker.h
@@ -27,194 +27,121 @@
// POSSIBILITY OF SUCH DAMAGE.
// Copyright 2007 Google Inc. All Rights Reserved.
//
-// Author: wjr@google.com (William Rucklidge)
-//
-// This file contains a class that exercises a cost function, to make sure
-// that it is computing reasonable derivatives. It compares the Jacobians
-// computed by the cost function with those obtained by finite
-// differences.
+// Authors: wjr@google.com (William Rucklidge),
+// keir@google.com (Keir Mierle),
+// dgossow@google.com (David Gossow)
#ifndef CERES_PUBLIC_GRADIENT_CHECKER_H_
#define CERES_PUBLIC_GRADIENT_CHECKER_H_
-#include <cstddef>
-#include <algorithm>
#include <vector>
+#include <string>
+#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/numeric_diff_cost_function.h"
+#include "ceres/local_parameterization.h"
#include "glog/logging.h"
namespace ceres {
-// An object that exercises a cost function, to compare the answers that it
-// gives with derivatives estimated using finite differencing.
+// GradientChecker compares the Jacobians returned by a cost function against
+// derivatives estimated using finite differencing.
//
-// The only likely usage of this is for testing.
+// The condition enforced is that
//
-// 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'. See prober_test.cc for an example.
+// (J_actual(i, j) - J_numeric(i, j))
+// ------------------------------------ < relative_precision
+// max(J_actual(i, j), J_numeric(i, j))
+//
+// where J_actual(i, j) is the jacobian as computed by the supplied cost
+// function (by the user) multiplied by the local parameterization Jacobian
+// and J_numeric is the jacobian as computed by finite differences, multiplied
+// by the local parameterization Jacobian as well.
//
-// This is templated similarly to NumericDiffCostFunction, as it internally
-// uses that.
-template <typename CostFunctionToProbe,
- int M = 0, int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0>
+// 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 {
public:
- // Here we stash some results from the probe, for later
- // inspection.
- struct GradientCheckResults {
- // Computed cost.
- Vector cost;
-
- // The sizes of these matrices are dictated by the cost function's
- // parameter and residual block sizes. Each vector's length will
- // term->parameter_block_sizes().size(), and each matrix is the
- // Jacobian of the residual with respect to the corresponding parameter
- // block.
+ // 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
+ // 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.
+ GradientChecker(
+ const CostFunction* function,
+ const std::vector<const LocalParameterization*>* local_parameterizations,
+ const NumericDiffOptions& options);
+
+ // Contains results from a call to Probe for later inspection.
+ struct ProbeResults {
+ // The return value of the cost function.
+ bool return_value;
+
+ // Computed residual vector.
+ Vector residuals;
+
+ // The sizes of the Jacobians below are dictated by the cost function's
+ // parameter block size and residual block sizes. If a parameter block
+ // has a local parameterization associated with it, the size of the "local"
+ // Jacobian will be determined by the local parameterization dimension and
+ // residual block size, otherwise it will be identical to the regular
+ // Jacobian.
// Derivatives as computed by the cost function.
- std::vector<Matrix> term_jacobians;
+ std::vector<Matrix> jacobians;
+
+ // Derivatives as computed by the cost function in local space.
+ std::vector<Matrix> local_jacobians;
- // Derivatives as computed by finite differencing.
- std::vector<Matrix> finite_difference_jacobians;
+ // Derivatives as computed by nuerical differentiation in local space.
+ std::vector<Matrix> numeric_jacobians;
- // Infinity-norm of term_jacobians - finite_difference_jacobians.
- double error_jacobians;
+ // Derivatives as computed by nuerical differentiation in local space.
+ std::vector<Matrix> local_numeric_jacobians;
+
+ // Contains the maximum relative error found in the local Jacobians.
+ double maximum_relative_error;
+
+ // If an error was detected, this will contain a detailed description of
+ // that error.
+ std::string error_log;
};
- // Checks the Jacobian computed by a cost function.
- //
- // probe_point: The parameter values at which to probe.
- // error_tolerance: A threshold for the infinity-norm difference
- // between the Jacobians. If the Jacobians differ by more than
- // this amount, then the probe fails.
+ // Call the cost function, compute alternative Jacobians using finite
+ // differencing and compare results. If local parameterizations are given,
+ // the Jacobians will be multiplied by the local parameterization Jacobians
+ // before performing the check, which effectively means that all errors along
+ // the null space of the local parameterization will be ignored.
+ // Returns false if the Jacobians don't match, the cost function return false,
+ // or if the cost function returns different residual when called with a
+ // Jacobian output argument vs. calling it without. Otherwise returns true.
//
- // term: The cost function to test. Not retained after this call returns.
- //
- // results: On return, the two Jacobians (and other information)
- // will be stored here. May be NULL.
+ // parameters: The parameter values at which to probe.
+ // relative_precision: A threshold for the relative difference between the
+ // Jacobians. If the Jacobians differ by more than this amount, then the
+ // probe fails.
+ // results: On return, the Jacobians (and other information) will be stored
+ // here. May be NULL.
//
// Returns true if no problems are detected and the difference between the
// Jacobians is less than error_tolerance.
- static bool Probe(double const* const* probe_point,
- double error_tolerance,
- CostFunctionToProbe *term,
- GradientCheckResults* results) {
- CHECK_NOTNULL(probe_point);
- CHECK_NOTNULL(term);
- LOG(INFO) << "-------------------- Starting Probe() --------------------";
-
- // We need a GradientCheckeresults, whether or not they supplied one.
- internal::scoped_ptr<GradientCheckResults> owned_results;
- if (results == NULL) {
- owned_results.reset(new GradientCheckResults);
- results = owned_results.get();
- }
-
- // Do a consistency check between the term and the template parameters.
- CHECK_EQ(M, term->num_residuals());
- const int num_residuals = M;
- const std::vector<int32>& block_sizes = term->parameter_block_sizes();
- const int num_blocks = block_sizes.size();
-
- CHECK_LE(num_blocks, 5) << "Unable to test functions that take more "
- << "than 5 parameter blocks";
- if (N0) {
- CHECK_EQ(N0, block_sizes[0]);
- CHECK_GE(num_blocks, 1);
- } else {
- CHECK_LT(num_blocks, 1);
- }
- if (N1) {
- CHECK_EQ(N1, block_sizes[1]);
- CHECK_GE(num_blocks, 2);
- } else {
- CHECK_LT(num_blocks, 2);
- }
- if (N2) {
- CHECK_EQ(N2, block_sizes[2]);
- CHECK_GE(num_blocks, 3);
- } else {
- CHECK_LT(num_blocks, 3);
- }
- if (N3) {
- CHECK_EQ(N3, block_sizes[3]);
- CHECK_GE(num_blocks, 4);
- } else {
- CHECK_LT(num_blocks, 4);
- }
- if (N4) {
- CHECK_EQ(N4, block_sizes[4]);
- CHECK_GE(num_blocks, 5);
- } else {
- CHECK_LT(num_blocks, 5);
- }
-
- results->term_jacobians.clear();
- results->term_jacobians.resize(num_blocks);
- results->finite_difference_jacobians.clear();
- results->finite_difference_jacobians.resize(num_blocks);
-
- internal::FixedArray<double*> term_jacobian_pointers(num_blocks);
- internal::FixedArray<double*>
- finite_difference_jacobian_pointers(num_blocks);
- for (int i = 0; i < num_blocks; i++) {
- results->term_jacobians[i].resize(num_residuals, block_sizes[i]);
- term_jacobian_pointers[i] = results->term_jacobians[i].data();
- results->finite_difference_jacobians[i].resize(
- num_residuals, block_sizes[i]);
- finite_difference_jacobian_pointers[i] =
- results->finite_difference_jacobians[i].data();
- }
- results->cost.resize(num_residuals, 1);
-
- CHECK(term->Evaluate(probe_point, results->cost.data(),
- term_jacobian_pointers.get()));
- NumericDiffCostFunction<CostFunctionToProbe, CENTRAL, M, N0, N1, N2, N3, N4>
- numeric_term(term, DO_NOT_TAKE_OWNERSHIP);
- CHECK(numeric_term.Evaluate(probe_point, results->cost.data(),
- finite_difference_jacobian_pointers.get()));
-
- results->error_jacobians = 0;
- for (int i = 0; i < num_blocks; i++) {
- Matrix jacobian_difference = results->term_jacobians[i] -
- results->finite_difference_jacobians[i];
- results->error_jacobians =
- std::max(results->error_jacobians,
- jacobian_difference.lpNorm<Eigen::Infinity>());
- }
-
- LOG(INFO) << "========== term-computed derivatives ==========";
- for (int i = 0; i < num_blocks; i++) {
- LOG(INFO) << "term_computed block " << i;
- LOG(INFO) << "\n" << results->term_jacobians[i];
- }
-
- LOG(INFO) << "========== finite-difference derivatives ==========";
- for (int i = 0; i < num_blocks; i++) {
- LOG(INFO) << "finite_difference block " << i;
- LOG(INFO) << "\n" << results->finite_difference_jacobians[i];
- }
-
- LOG(INFO) << "========== difference ==========";
- for (int i = 0; i < num_blocks; i++) {
- LOG(INFO) << "difference block " << i;
- LOG(INFO) << (results->term_jacobians[i] -
- results->finite_difference_jacobians[i]);
- }
-
- LOG(INFO) << "||difference|| = " << results->error_jacobians;
-
- return results->error_jacobians < error_tolerance;
- }
+ bool Probe(double const* const* parameters,
+ double relative_precision,
+ ProbeResults* results) const;
private:
CERES_DISALLOW_IMPLICIT_CONSTRUCTORS(GradientChecker);
+
+ std::vector<const LocalParameterization*> local_parameterizations_;
+ const CostFunction* function_;
+ internal::scoped_ptr<CostFunction> finite_diff_cost_function_;
};
} // namespace ceres
diff --git a/extern/ceres/include/ceres/internal/port.h b/extern/ceres/include/ceres/internal/port.h
index e57049dde4b..f4dcaee7bd8 100644
--- a/extern/ceres/include/ceres/internal/port.h
+++ b/extern/ceres/include/ceres/internal/port.h
@@ -33,9 +33,8 @@
// 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
@@ -50,6 +49,25 @@ using std::tr1::shared_ptr;
using std::shared_ptr;
#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);
+#endif
+} // namespace port_constants
+#endif
+
} // namespace ceres
#endif // __cplusplus
diff --git a/extern/ceres/include/ceres/iteration_callback.h b/extern/ceres/include/ceres/iteration_callback.h
index 6bab00439c5..db5d0efe53a 100644
--- a/extern/ceres/include/ceres/iteration_callback.h
+++ b/extern/ceres/include/ceres/iteration_callback.h
@@ -69,7 +69,7 @@ struct CERES_EXPORT IterationSummary {
// 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 false when iteration = 0.
+ // Note: step_is_valid is always true when iteration = 0.
bool step_is_valid;
// Step did not reduce the value of the objective function
@@ -77,7 +77,7 @@ struct CERES_EXPORT IterationSummary {
// acceptance criterion used by the non-monotonic trust region
// algorithm.
//
- // Note: step_is_nonmonotonic is false when iteration = 0;
+ // Note: step_is_nonmonotonic is always false when iteration = 0;
bool step_is_nonmonotonic;
// Whether or not the minimizer accepted this step or not. If the
@@ -89,7 +89,7 @@ struct CERES_EXPORT IterationSummary {
// relative decrease is not sufficient, the algorithm may accept the
// step and the step is declared successful.
//
- // Note: step_is_successful is false when iteration = 0.
+ // Note: step_is_successful is always true when iteration = 0.
bool step_is_successful;
// Value of the objective function.
diff --git a/extern/ceres/include/ceres/jet.h b/extern/ceres/include/ceres/jet.h
index a21fd7adb90..a104707298c 100644
--- a/extern/ceres/include/ceres/jet.h
+++ b/extern/ceres/include/ceres/jet.h
@@ -164,6 +164,7 @@
#include "Eigen/Core"
#include "ceres/fpclassify.h"
+#include "ceres/internal/port.h"
namespace ceres {
@@ -227,21 +228,23 @@ struct Jet {
T a;
// The infinitesimal part.
- //
- // Note the Eigen::DontAlign bit is needed here because this object
- // gets allocated on the stack and as part of other arrays and
- // structs. Forcing the right alignment there is the source of much
- // pain and suffering. Even if that works, passing Jets around to
- // functions by value has problems because the C++ ABI does not
- // guarantee alignment for function arguments.
- //
- // Setting the DontAlign bit prevents Eigen from using SSE for the
- // various operations on Jets. This is a small performance penalty
- // since the AutoDiff code will still expose much of the code as
- // statically sized loops to the compiler. But given the subtle
- // issues that arise due to alignment, especially when dealing with
- // multiple platforms, it seems to be a trade off worth making.
+
+ // 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
};
// Unary +
@@ -388,6 +391,8 @@ 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); }
@@ -482,10 +487,51 @@ Jet<T, N> tanh(const Jet<T, N>& f) {
return Jet<T, N>(tanh_a, tmp * f.v);
}
+// The floor function should be used with extreme care as this operation will
+// 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) {
+ return Jet<T, N>(floor(f.a));
+}
+
+// The ceil function should be used with extreme care as this operation will
+// 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) {
+ return Jet<T, N>(ceil(f.a));
+}
+
// Bessel functions of the first kind with integer order equal to 0, 1, n.
-inline double BesselJ0(double x) { return j0(x); }
-inline double BesselJ1(double x) { return j1(x); }
-inline double BesselJn(int n, double x) { return jn(n, x); }
+//
+// Microsoft has deprecated the j[0,1,n]() POSIX Bessel functions in favour of
+// _j[0,1,n](). Where available on MSVC, use _j[0,1,n]() to avoid deprecated
+// 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)
+ return _j0(x);
+#else
+ return j0(x);
+#endif
+}
+inline double BesselJ1(double x) {
+#if defined(_MSC_VER) && defined(_j1)
+ return _j1(x);
+#else
+ return j1(x);
+#endif
+}
+inline double BesselJn(int n, double x) {
+#if defined(_MSC_VER) && defined(_jn)
+ return _jn(n, x);
+#else
+ return jn(n, x);
+#endif
+}
// For the formulae of the derivatives of the Bessel functions see the book:
// Olver, Lozier, Boisvert, Clark, NIST Handbook of Mathematical Functions,
@@ -743,7 +789,15 @@ template<typename T, int N> inline Jet<T, N> ei_pow (const Jet<T, N>& x,
// strange compile errors.
template <typename T, int N>
inline std::ostream &operator<<(std::ostream &s, const Jet<T, N>& z) {
- return s << "[" << z.a << " ; " << z.v.transpose() << "]";
+ s << "[" << z.a << " ; ";
+ for (int i = 0; i < N; ++i) {
+ s << z.v[i];
+ if (i != N - 1) {
+ s << ", ";
+ }
+ }
+ s << "]";
+ return s;
}
} // namespace ceres
@@ -757,6 +811,7 @@ struct NumTraits<ceres::Jet<T, N> > {
typedef ceres::Jet<T, N> Real;
typedef ceres::Jet<T, N> NonInteger;
typedef ceres::Jet<T, N> Nested;
+ typedef ceres::Jet<T, N> Literal;
static typename ceres::Jet<T, N> dummy_precision() {
return ceres::Jet<T, N>(1e-12);
@@ -777,6 +832,21 @@ struct NumTraits<ceres::Jet<T, N> > {
HasFloatingPoint = 1,
RequireInitialization = 1
};
+
+ template<bool Vectorized>
+ struct Div {
+ enum {
+#if defined(EIGEN_VECTORIZE_AVX)
+ AVX = true,
+#else
+ AVX = false,
+#endif
+
+ // Assuming that for Jets, division is as expensive as
+ // multiplication.
+ Cost = 3
+ };
+ };
};
} // namespace Eigen
diff --git a/extern/ceres/include/ceres/local_parameterization.h b/extern/ceres/include/ceres/local_parameterization.h
index 67633de309f..379fc684921 100644
--- a/extern/ceres/include/ceres/local_parameterization.h
+++ b/extern/ceres/include/ceres/local_parameterization.h
@@ -211,6 +211,28 @@ class CERES_EXPORT QuaternionParameterization : public LocalParameterization {
virtual int LocalSize() const { return 3; }
};
+// Implements the quaternion local parameterization for Eigen's representation
+// of the quaternion. Eigen uses a different internal memory layout for the
+// elements of the quaternion than what is commonly used. Specifically, Eigen
+// stores the elements in memory as [x, y, z, w] where the real part is last
+// whereas it is typically stored first. Note, when creating an Eigen quaternion
+// through the constructor the elements are accepted in w, x, y, z order. Since
+// Ceres operates on parameter blocks which are raw double pointers this
+// difference is important and requires a different parameterization.
+//
+// Plus(x, delta) = [sin(|delta|) delta / |delta|, cos(|delta|)] * x
+// with * being the quaternion multiplication operator.
+class 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; }
+};
// This provides a parameterization for homogeneous vectors which are commonly
// used in Structure for Motion problems. One example where they are used is
diff --git a/extern/ceres/include/ceres/numeric_diff_cost_function.h b/extern/ceres/include/ceres/numeric_diff_cost_function.h
index fa96078df02..5dfaeab6241 100644
--- a/extern/ceres/include/ceres/numeric_diff_cost_function.h
+++ b/extern/ceres/include/ceres/numeric_diff_cost_function.h
@@ -206,29 +206,6 @@ class NumericDiffCostFunction
}
}
- // Deprecated. New users should avoid using this constructor. Instead, use the
- // constructor with NumericDiffOptions.
- NumericDiffCostFunction(CostFunctor* functor,
- Ownership ownership,
- int num_residuals,
- const double relative_step_size)
- :functor_(functor),
- ownership_(ownership),
- options_() {
- LOG(WARNING) << "This constructor is deprecated and will be removed in "
- "a future version. Please use the NumericDiffOptions "
- "constructor instead.";
-
- if (kNumResiduals == DYNAMIC) {
- SizedCostFunction<kNumResiduals,
- N0, N1, N2, N3, N4,
- N5, N6, N7, N8, N9>
- ::set_num_residuals(num_residuals);
- }
-
- options_.relative_step_size = relative_step_size;
- }
-
~NumericDiffCostFunction() {
if (ownership_ != TAKE_OWNERSHIP) {
functor_.release();
diff --git a/extern/ceres/include/ceres/problem.h b/extern/ceres/include/ceres/problem.h
index 409274c62c2..27ed4ef15da 100644
--- a/extern/ceres/include/ceres/problem.h
+++ b/extern/ceres/include/ceres/problem.h
@@ -309,6 +309,9 @@ class CERES_EXPORT Problem {
// Allow the indicated parameter block to vary during optimization.
void SetParameterBlockVariable(double* values);
+ // Returns true if a parameter block is set constant, and false otherwise.
+ bool IsParameterBlockConstant(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
@@ -461,6 +464,10 @@ class CERES_EXPORT Problem {
// parameter block has a local parameterization, then it contributes
// "LocalSize" entries to the gradient vector (and the number of
// columns in the jacobian).
+ //
+ // Note 3: This function cannot be called while the problem is being
+ // solved, for example it cannot be called from an IterationCallback
+ // at the end of an iteration during a solve.
bool Evaluate(const EvaluateOptions& options,
double* cost,
std::vector<double>* residuals,
diff --git a/extern/ceres/include/ceres/rotation.h b/extern/ceres/include/ceres/rotation.h
index e9496d772e4..b6a06f772c4 100644
--- a/extern/ceres/include/ceres/rotation.h
+++ b/extern/ceres/include/ceres/rotation.h
@@ -48,7 +48,6 @@
#include <algorithm>
#include <cmath>
#include <limits>
-#include "glog/logging.h"
namespace ceres {
@@ -418,7 +417,6 @@ template <typename T>
inline void EulerAnglesToRotationMatrix(const T* euler,
const int row_stride_parameter,
T* R) {
- CHECK_EQ(row_stride_parameter, 3);
EulerAnglesToRotationMatrix(euler, RowMajorAdapter3x3(R));
}
@@ -496,7 +494,6 @@ void QuaternionToRotation(const T q[4],
QuaternionToScaledRotation(q, R);
T normalizer = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
- CHECK_NE(normalizer, T(0));
normalizer = T(1) / normalizer;
for (int i = 0; i < 3; ++i) {
diff --git a/extern/ceres/include/ceres/solver.h b/extern/ceres/include/ceres/solver.h
index 318cf48cb83..0d77d242dfe 100644
--- a/extern/ceres/include/ceres/solver.h
+++ b/extern/ceres/include/ceres/solver.h
@@ -134,7 +134,7 @@ class CERES_EXPORT Solver {
trust_region_problem_dump_format_type = TEXTFILE;
check_gradients = false;
gradient_check_relative_precision = 1e-8;
- numeric_derivative_relative_step_size = 1e-6;
+ gradient_check_numeric_derivative_relative_step_size = 1e-6;
update_state_every_iteration = false;
}
@@ -701,12 +701,22 @@ class CERES_EXPORT Solver {
// this number, then the jacobian for that cost term is dumped.
double gradient_check_relative_precision;
- // Relative shift used for taking numeric derivatives. For finite
- // differencing, each dimension is evaluated at slightly shifted
- // values; for the case of central difference, this is what gets
- // evaluated:
+ // WARNING: This option only applies to the to the numeric
+ // differentiation used for checking the user provided derivatives
+ // when when Solver::Options::check_gradients is true. If you are
+ // using NumericDiffCostFunction and are interested in changing
+ // the step size for numeric differentiation in your cost
+ // function, please have a look at
+ // include/ceres/numeric_diff_options.h.
//
- // delta = numeric_derivative_relative_step_size;
+ // Relative shift used for taking numeric derivatives when
+ // Solver::Options::check_gradients is true.
+ //
+ // For finite differencing, each dimension is evaluated at
+ // slightly shifted values; for the case of central difference,
+ // this is what gets evaluated:
+ //
+ // delta = gradient_check_numeric_derivative_relative_step_size;
// f_initial = f(x)
// f_forward = f((1 + delta) * x)
// f_backward = f((1 - delta) * x)
@@ -723,7 +733,7 @@ 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 numeric_derivative_relative_step_size;
+ 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
@@ -801,6 +811,13 @@ class CERES_EXPORT Solver {
// Number of times inner iterations were performed.
int num_inner_iteration_steps;
+ // 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;
+
// All times reported below are wall times.
// When the user calls Solve, before the actual optimization
diff --git a/extern/ceres/include/ceres/version.h b/extern/ceres/include/ceres/version.h
index 66505a515c9..2f1cc297a38 100644
--- a/extern/ceres/include/ceres/version.h
+++ b/extern/ceres/include/ceres/version.h
@@ -32,7 +32,7 @@
#define CERES_PUBLIC_VERSION_H_
#define CERES_VERSION_MAJOR 1
-#define CERES_VERSION_MINOR 11
+#define CERES_VERSION_MINOR 12
#define CERES_VERSION_REVISION 0
// Classic CPP stringifcation; the extra level of indirection allows the
diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc
index 64b6ac00447..40977b74c67 100644
--- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc
+++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc
@@ -46,6 +46,7 @@ namespace internal {
using std::make_pair;
using std::pair;
using std::vector;
+using std::adjacent_find;
void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors(
const Program* program, CompressedRowSparseMatrix* jacobian) {
@@ -140,12 +141,21 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const {
// Sort the parameters by their position in the state vector.
sort(parameter_indices.begin(), parameter_indices.end());
- CHECK(unique(parameter_indices.begin(), parameter_indices.end()) ==
- parameter_indices.end())
- << "Ceres internal error: "
- << "Duplicate parameter blocks detected in a cost function. "
- << "This should never happen. Please report this to "
- << "the Ceres developers.";
+ if (adjacent_find(parameter_indices.begin(), parameter_indices.end()) !=
+ parameter_indices.end()) {
+ std::string parameter_block_description;
+ for (int j = 0; j < num_parameter_blocks; ++j) {
+ ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
+ parameter_block_description +=
+ parameter_block->ToString() + "\n";
+ }
+ LOG(FATAL) << "Ceres internal error: "
+ << "Duplicate parameter blocks detected in a cost function. "
+ << "This should never happen. Please report this to "
+ << "the Ceres developers.\n"
+ << "Residual Block: " << residual_block->ToString() << "\n"
+ << "Parameter Blocks: " << parameter_block_description;
+ }
// Update the row indices.
const int num_residuals = residual_block->NumResiduals();
diff --git a/extern/ceres/internal/ceres/covariance.cc b/extern/ceres/internal/ceres/covariance.cc
index 690847945a9..cb280a36847 100644
--- a/extern/ceres/internal/ceres/covariance.cc
+++ b/extern/ceres/internal/ceres/covariance.cc
@@ -38,6 +38,7 @@
namespace ceres {
+using std::make_pair;
using std::pair;
using std::vector;
@@ -54,6 +55,12 @@ bool Covariance::Compute(
return impl_->Compute(covariance_blocks, problem->problem_impl_.get());
}
+bool Covariance::Compute(
+ const vector<const double*>& parameter_blocks,
+ Problem* problem) {
+ return impl_->Compute(parameter_blocks, problem->problem_impl_.get());
+}
+
bool Covariance::GetCovarianceBlock(const double* parameter_block1,
const double* parameter_block2,
double* covariance_block) const {
@@ -73,4 +80,20 @@ bool Covariance::GetCovarianceBlockInTangentSpace(
covariance_block);
}
+bool Covariance::GetCovarianceMatrix(
+ const vector<const double*>& parameter_blocks,
+ double* covariance_matrix) {
+ return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks,
+ true, // ambient
+ covariance_matrix);
+}
+
+bool Covariance::GetCovarianceMatrixInTangentSpace(
+ const std::vector<const double *>& parameter_blocks,
+ double *covariance_matrix) {
+ return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks,
+ false, // tangent
+ covariance_matrix);
+}
+
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/covariance_impl.cc b/extern/ceres/internal/ceres/covariance_impl.cc
index 3e8302bed55..d698f88fa9b 100644
--- a/extern/ceres/internal/ceres/covariance_impl.cc
+++ b/extern/ceres/internal/ceres/covariance_impl.cc
@@ -36,6 +36,8 @@
#include <algorithm>
#include <cstdlib>
+#include <numeric>
+#include <sstream>
#include <utility>
#include <vector>
@@ -43,6 +45,7 @@
#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"
@@ -51,6 +54,7 @@
#include "ceres/map_util.h"
#include "ceres/parameter_block.h"
#include "ceres/problem_impl.h"
+#include "ceres/residual_block.h"
#include "ceres/suitesparse.h"
#include "ceres/wall_time.h"
#include "glog/logging.h"
@@ -61,6 +65,7 @@ namespace internal {
using std::make_pair;
using std::map;
using std::pair;
+using std::sort;
using std::swap;
using std::vector;
@@ -86,8 +91,38 @@ CovarianceImpl::CovarianceImpl(const Covariance::Options& options)
CovarianceImpl::~CovarianceImpl() {
}
+template <typename T> void CheckForDuplicates(vector<T> blocks) {
+ sort(blocks.begin(), blocks.end());
+ typename vector<T>::iterator it =
+ 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;
+ for (int i = 0; i < blocks.size(); ++i) {
+ blocks_map[blocks[i]].push_back(i);
+ }
+
+ std::ostringstream duplicates;
+ while (it != blocks.end()) {
+ duplicates << "(";
+ for (int i = 0; i < blocks_map[*it].size() - 1; ++i) {
+ duplicates << blocks_map[*it][i] << ", ";
+ }
+ duplicates << blocks_map[*it].back() << ")";
+ it = std::adjacent_find(it + 1, blocks.end());
+ if (it < blocks.end()) {
+ duplicates << " and ";
+ }
+ }
+
+ LOG(FATAL) << "Covariance::Compute called with duplicate blocks at "
+ << "indices " << duplicates.str();
+ }
+}
+
bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks,
ProblemImpl* problem) {
+ CheckForDuplicates<pair<const double*, const double*> >(covariance_blocks);
problem_ = problem;
parameter_block_to_row_index_.clear();
covariance_matrix_.reset(NULL);
@@ -97,6 +132,20 @@ bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks,
return is_valid_;
}
+bool CovarianceImpl::Compute(const vector<const double*>& parameter_blocks,
+ ProblemImpl* problem) {
+ CheckForDuplicates<const double*>(parameter_blocks);
+ CovarianceBlocks covariance_blocks;
+ for (int i = 0; i < parameter_blocks.size(); ++i) {
+ for (int j = i; j < parameter_blocks.size(); ++j) {
+ covariance_blocks.push_back(make_pair(parameter_blocks[i],
+ parameter_blocks[j]));
+ }
+ }
+
+ return Compute(covariance_blocks, problem);
+}
+
bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace(
const double* original_parameter_block1,
const double* original_parameter_block2,
@@ -120,9 +169,17 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace(
ParameterBlock* block2 =
FindOrDie(parameter_map,
const_cast<double*>(original_parameter_block2));
+
const int block1_size = block1->Size();
const int block2_size = block2->Size();
- MatrixRef(covariance_block, block1_size, block2_size).setZero();
+ const int block1_local_size = block1->LocalSize();
+ const int block2_local_size = block2->LocalSize();
+ if (!lift_covariance_to_ambient_space) {
+ MatrixRef(covariance_block, block1_local_size, block2_local_size)
+ .setZero();
+ } else {
+ MatrixRef(covariance_block, block1_size, block2_size).setZero();
+ }
return true;
}
@@ -240,6 +297,94 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace(
return true;
}
+bool CovarianceImpl::GetCovarianceMatrixInTangentOrAmbientSpace(
+ const vector<const double*>& parameters,
+ bool lift_covariance_to_ambient_space,
+ double* covariance_matrix) const {
+ CHECK(is_computed_)
+ << "Covariance::GetCovarianceMatrix called before Covariance::Compute";
+ CHECK(is_valid_)
+ << "Covariance::GetCovarianceMatrix called when Covariance::Compute "
+ << "returned false.";
+
+ const ProblemImpl::ParameterMap& parameter_map = problem_->parameter_map();
+ // For OpenMP compatibility we need to define these vectors in advance
+ const int num_parameters = parameters.size();
+ vector<int> parameter_sizes;
+ vector<int> cum_parameter_size;
+ parameter_sizes.reserve(num_parameters);
+ cum_parameter_size.resize(num_parameters + 1);
+ cum_parameter_size[0] = 0;
+ for (int i = 0; i < num_parameters; ++i) {
+ ParameterBlock* block =
+ FindOrDie(parameter_map, const_cast<double*>(parameters[i]));
+ if (lift_covariance_to_ambient_space) {
+ parameter_sizes.push_back(block->Size());
+ } else {
+ parameter_sizes.push_back(block->LocalSize());
+ }
+ }
+ std::partial_sum(parameter_sizes.begin(), parameter_sizes.end(),
+ cum_parameter_size.begin() + 1);
+ const int max_covariance_block_size =
+ *std::max_element(parameter_sizes.begin(), parameter_sizes.end());
+ const int covariance_size = cum_parameter_size.back();
+
+ // 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(
+ 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) {
+ 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;
+ if (!GetCovarianceBlockInTangentOrAmbientSpace(
+ 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);
+
+ 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;
+}
+
// Determine the sparsity pattern of the covariance matrix based on
// the block pairs requested by the user.
bool CovarianceImpl::ComputeCovarianceSparsity(
@@ -252,18 +397,28 @@ 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;
+ vector<ResidualBlock*> residual_blocks;
+ problem->GetResidualBlocks(&residual_blocks);
+
+ for (int i = 0; i < residual_blocks.size(); ++i) {
+ ResidualBlock* residual_block = residual_blocks[i];
+ parameter_blocks_in_use.insert(residual_block->parameter_blocks(),
+ residual_block->parameter_blocks() +
+ residual_block->NumParameterBlocks());
+ }
+
constant_parameter_blocks_.clear();
vector<double*>& active_parameter_blocks =
evaluate_options_.parameter_blocks;
active_parameter_blocks.clear();
for (int i = 0; i < all_parameter_blocks.size(); ++i) {
double* parameter_block = all_parameter_blocks[i];
-
ParameterBlock* block = FindOrDie(parameter_map, parameter_block);
- if (block->IsConstant()) {
- constant_parameter_blocks_.insert(parameter_block);
- } else {
+ if (!block->IsConstant() && (parameter_blocks_in_use.count(block) > 0)) {
active_parameter_blocks.push_back(parameter_block);
+ } else {
+ constant_parameter_blocks_.insert(parameter_block);
}
}
@@ -386,8 +541,8 @@ bool CovarianceImpl::ComputeCovarianceValues() {
switch (options_.algorithm_type) {
case DENSE_SVD:
return ComputeCovarianceValuesUsingDenseSVD();
-#ifndef CERES_NO_SUITESPARSE
case SUITE_SPARSE_QR:
+#ifndef CERES_NO_SUITESPARSE
return ComputeCovarianceValuesUsingSuiteSparseQR();
#else
LOG(ERROR) << "SuiteSparse is required to use the "
@@ -624,7 +779,10 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() {
if (automatic_truncation) {
break;
} else {
- LOG(ERROR) << "Cholesky factorization of J'J is not reliable. "
+ LOG(ERROR) << "Error: Covariance matrix is near rank deficient "
+ << "and the user did not specify a non-zero"
+ << "Covariance::Options::null_space_rank "
+ << "to enable the computation of a Pseudo-Inverse. "
<< "Reciprocal condition number: "
<< singular_value_ratio * singular_value_ratio << " "
<< "min_reciprocal_condition_number: "
diff --git a/extern/ceres/internal/ceres/covariance_impl.h b/extern/ceres/internal/ceres/covariance_impl.h
index eb0cd040666..a3f0761f57c 100644
--- a/extern/ceres/internal/ceres/covariance_impl.h
+++ b/extern/ceres/internal/ceres/covariance_impl.h
@@ -55,12 +55,21 @@ class CovarianceImpl {
const double*> >& covariance_blocks,
ProblemImpl* problem);
+ bool Compute(
+ const std::vector<const double*>& parameter_blocks,
+ ProblemImpl* problem);
+
bool GetCovarianceBlockInTangentOrAmbientSpace(
const double* parameter_block1,
const double* parameter_block2,
bool lift_covariance_to_ambient_space,
double* covariance_block) const;
+ bool GetCovarianceMatrixInTangentOrAmbientSpace(
+ const std::vector<const double*>& parameters,
+ bool lift_covariance_to_ambient_space,
+ double *covariance_matrix) const;
+
bool ComputeCovarianceSparsity(
const std::vector<std::pair<const double*,
const double*> >& covariance_blocks,
diff --git a/extern/ceres/internal/ceres/gradient_checker.cc b/extern/ceres/internal/ceres/gradient_checker.cc
new file mode 100644
index 00000000000..c16c141db09
--- /dev/null
+++ b/extern/ceres/internal/ceres/gradient_checker.cc
@@ -0,0 +1,276 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2016 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.
+//
+// Authors: wjr@google.com (William Rucklidge),
+// keir@google.com (Keir Mierle),
+// dgossow@google.com (David Gossow)
+
+#include "ceres/gradient_checker.h"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "ceres/is_close.h"
+#include "ceres/stringprintf.h"
+#include "ceres/types.h"
+
+namespace ceres {
+
+using internal::IsClose;
+using internal::StringAppendF;
+using internal::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace {
+// Evaluate the cost function and transform the returned Jacobians to
+// the local space of the respective local parameterizations.
+bool EvaluateCostFunction(
+ const ceres::CostFunction* function,
+ double const* const * parameters,
+ const std::vector<const ceres::LocalParameterization*>&
+ local_parameterizations,
+ Vector* residuals,
+ std::vector<Matrix>* jacobians,
+ std::vector<Matrix>* local_jacobians) {
+ CHECK_NOTNULL(residuals);
+ CHECK_NOTNULL(jacobians);
+ CHECK_NOTNULL(local_jacobians);
+
+ const vector<int32>& block_sizes = function->parameter_block_sizes();
+ const int num_parameter_blocks = block_sizes.size();
+
+ // Allocate Jacobian matrices in local space.
+ local_jacobians->resize(num_parameter_blocks);
+ vector<double*> local_jacobian_data(num_parameter_blocks);
+ for (int i = 0; i < num_parameter_blocks; ++i) {
+ int block_size = block_sizes.at(i);
+ if (local_parameterizations.at(i) != NULL) {
+ block_size = local_parameterizations.at(i)->LocalSize();
+ }
+ local_jacobians->at(i).resize(function->num_residuals(), block_size);
+ local_jacobians->at(i).setZero();
+ local_jacobian_data.at(i) = local_jacobians->at(i).data();
+ }
+
+ // Allocate Jacobian matrices in global space.
+ jacobians->resize(num_parameter_blocks);
+ vector<double*> jacobian_data(num_parameter_blocks);
+ for (int i = 0; i < num_parameter_blocks; ++i) {
+ jacobians->at(i).resize(function->num_residuals(), block_sizes.at(i));
+ jacobians->at(i).setZero();
+ jacobian_data.at(i) = jacobians->at(i).data();
+ }
+
+ // Compute residuals & jacobians.
+ CHECK_NE(0, function->num_residuals());
+ residuals->resize(function->num_residuals());
+ residuals->setZero();
+ if (!function->Evaluate(parameters, residuals->data(),
+ jacobian_data.data())) {
+ return false;
+ }
+
+ // Convert Jacobians from global to local space.
+ for (size_t i = 0; i < local_jacobians->size(); ++i) {
+ if (local_parameterizations.at(i) == NULL) {
+ local_jacobians->at(i) = jacobians->at(i);
+ } else {
+ int global_size = local_parameterizations.at(i)->GlobalSize();
+ int local_size = local_parameterizations.at(i)->LocalSize();
+ CHECK_EQ(jacobians->at(i).cols(), global_size);
+ Matrix global_J_local(global_size, local_size);
+ local_parameterizations.at(i)->ComputeJacobian(
+ parameters[i], global_J_local.data());
+ local_jacobians->at(i) = jacobians->at(i) * global_J_local;
+ }
+ }
+ return true;
+}
+} // namespace
+
+GradientChecker::GradientChecker(
+ const CostFunction* function,
+ const vector<const LocalParameterization*>* local_parameterizations,
+ const NumericDiffOptions& options) :
+ function_(function) {
+ CHECK_NOTNULL(function);
+ if (local_parameterizations != NULL) {
+ local_parameterizations_ = *local_parameterizations;
+ } else {
+ local_parameterizations_.resize(function->parameter_block_sizes().size(),
+ NULL);
+ }
+ DynamicNumericDiffCostFunction<CostFunction, CENTRAL>*
+ finite_diff_cost_function =
+ new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>(
+ function, DO_NOT_TAKE_OWNERSHIP, options);
+ finite_diff_cost_function_.reset(finite_diff_cost_function);
+
+ const vector<int32>& 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) {
+ finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]);
+ }
+ finite_diff_cost_function->SetNumResiduals(function->num_residuals());
+}
+
+bool GradientChecker::Probe(double const* const * parameters,
+ double relative_precision,
+ ProbeResults* results_param) const {
+ int num_residuals = function_->num_residuals();
+
+ // Make sure that we have a place to store results, no matter if the user has
+ // provided an output argument.
+ ProbeResults* results;
+ ProbeResults results_local;
+ if (results_param != NULL) {
+ results = results_param;
+ results->residuals.resize(0);
+ results->jacobians.clear();
+ results->numeric_jacobians.clear();
+ results->local_jacobians.clear();
+ results->local_numeric_jacobians.clear();
+ results->error_log.clear();
+ } else {
+ results = &results_local;
+ }
+ results->maximum_relative_error = 0.0;
+ results->return_value = true;
+
+ // Evaluate the derivative using the user supplied code.
+ vector<Matrix>& jacobians = results->jacobians;
+ vector<Matrix>& local_jacobians = results->local_jacobians;
+ if (!EvaluateCostFunction(function_, parameters, local_parameterizations_,
+ &results->residuals, &jacobians, &local_jacobians)) {
+ results->error_log = "Function evaluation with Jacobians failed.";
+ results->return_value = false;
+ }
+
+ // Evaluate the derivative using numeric derivatives.
+ vector<Matrix>& numeric_jacobians = results->numeric_jacobians;
+ vector<Matrix>& local_numeric_jacobians = results->local_numeric_jacobians;
+ Vector finite_diff_residuals;
+ if (!EvaluateCostFunction(finite_diff_cost_function_.get(), parameters,
+ local_parameterizations_, &finite_diff_residuals,
+ &numeric_jacobians, &local_numeric_jacobians)) {
+ results->error_log += "\nFunction evaluation with numerical "
+ "differentiation failed.";
+ results->return_value = false;
+ }
+
+ if (!results->return_value) {
+ return false;
+ }
+
+ for (int i = 0; i < num_residuals; ++i) {
+ if (!IsClose(
+ results->residuals[i],
+ finite_diff_residuals[i],
+ relative_precision,
+ NULL,
+ NULL)) {
+ results->error_log = "Function evaluation with and without Jacobians "
+ "resulted in different residuals.";
+ LOG(INFO) << results->residuals.transpose();
+ LOG(INFO) << finite_diff_residuals.transpose();
+ return false;
+ }
+ }
+
+ // See if any elements have relative error larger than the threshold.
+ int num_bad_jacobian_components = 0;
+ double& worst_relative_error = results->maximum_relative_error;
+ worst_relative_error = 0;
+
+ // Accumulate the error message for all the jacobians, since it won't get
+ // output if there are no bad jacobian components.
+ string error_log;
+ for (int k = 0; k < function_->parameter_block_sizes().size(); k++) {
+ StringAppendF(&error_log,
+ "========== "
+ "Jacobian for " "block %d: (%ld by %ld)) "
+ "==========\n",
+ k,
+ static_cast<long>(local_jacobians[k].rows()),
+ static_cast<long>(local_jacobians[k].cols()));
+ // The funny spacing creates appropriately aligned column headers.
+ error_log +=
+ " block row col user dx/dy num diff dx/dy "
+ "abs error relative error parameter residual\n";
+
+ for (int i = 0; i < local_jacobians[k].rows(); i++) {
+ for (int j = 0; j < local_jacobians[k].cols(); j++) {
+ double term_jacobian = local_jacobians[k](i, j);
+ double finite_jacobian = local_numeric_jacobians[k](i, j);
+ double relative_error, absolute_error;
+ bool bad_jacobian_entry =
+ !IsClose(term_jacobian,
+ finite_jacobian,
+ relative_precision,
+ &relative_error,
+ &absolute_error);
+ worst_relative_error = std::max(worst_relative_error, relative_error);
+
+ StringAppendF(&error_log,
+ "%6d %4d %4d %17g %17g %17g %17g %17g %17g",
+ k, i, j,
+ term_jacobian, finite_jacobian,
+ absolute_error, relative_error,
+ parameters[k][j],
+ results->residuals[i]);
+
+ if (bad_jacobian_entry) {
+ num_bad_jacobian_components++;
+ StringAppendF(
+ &error_log,
+ " ------ (%d,%d,%d) Relative error worse than %g",
+ k, i, j, relative_precision);
+ }
+ error_log += "\n";
+ }
+ }
+ }
+
+ // Since there were some bad errors, dump comprehensive debug info.
+ if (num_bad_jacobian_components) {
+ string header = StringPrintf("\nDetected %d bad Jacobian component(s). "
+ "Worst relative error was %g.\n",
+ num_bad_jacobian_components,
+ worst_relative_error);
+ results->error_log = header + "\n" + error_log;
+ return false;
+ }
+ return true;
+}
+
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc
index 580fd260e15..f2c73367891 100644
--- a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc
+++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc
@@ -26,7 +26,8 @@
// 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)
+// Authors: keir@google.com (Keir Mierle),
+// dgossow@google.com (David Gossow)
#include "ceres/gradient_checking_cost_function.h"
@@ -36,7 +37,7 @@
#include <string>
#include <vector>
-#include "ceres/cost_function.h"
+#include "ceres/gradient_checker.h"
#include "ceres/internal/eigen.h"
#include "ceres/internal/scoped_ptr.h"
#include "ceres/parameter_block.h"
@@ -59,55 +60,25 @@ using std::vector;
namespace {
-// True if x and y have an absolute relative difference less than
-// relative_precision and false otherwise. Stores the relative and absolute
-// difference in relative/absolute_error if non-NULL.
-bool IsClose(double x, double y, double relative_precision,
- double *relative_error,
- double *absolute_error) {
- double local_absolute_error;
- double local_relative_error;
- if (!absolute_error) {
- absolute_error = &local_absolute_error;
- }
- if (!relative_error) {
- relative_error = &local_relative_error;
- }
- *absolute_error = abs(x - y);
- *relative_error = *absolute_error / max(abs(x), abs(y));
- if (x == 0 || y == 0) {
- // If x or y is exactly zero, then relative difference doesn't have any
- // meaning. Take the absolute difference instead.
- *relative_error = *absolute_error;
- }
- return abs(*relative_error) < abs(relative_precision);
-}
-
class GradientCheckingCostFunction : public CostFunction {
public:
- GradientCheckingCostFunction(const CostFunction* function,
- const NumericDiffOptions& options,
- double relative_precision,
- const string& extra_info)
+ GradientCheckingCostFunction(
+ const CostFunction* function,
+ const std::vector<const LocalParameterization*>* local_parameterizations,
+ const NumericDiffOptions& options,
+ double relative_precision,
+ const string& extra_info,
+ GradientCheckingIterationCallback* callback)
: function_(function),
+ gradient_checker_(function, local_parameterizations, options),
relative_precision_(relative_precision),
- extra_info_(extra_info) {
- DynamicNumericDiffCostFunction<CostFunction, CENTRAL>*
- finite_diff_cost_function =
- new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>(
- function,
- DO_NOT_TAKE_OWNERSHIP,
- options);
-
+ extra_info_(extra_info),
+ callback_(callback) {
+ CHECK_NOTNULL(callback_);
const vector<int32>& parameter_block_sizes =
function->parameter_block_sizes();
- for (int i = 0; i < parameter_block_sizes.size(); ++i) {
- finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]);
- }
*mutable_parameter_block_sizes() = parameter_block_sizes;
set_num_residuals(function->num_residuals());
- finite_diff_cost_function->SetNumResiduals(num_residuals());
- finite_diff_cost_function_.reset(finite_diff_cost_function);
}
virtual ~GradientCheckingCostFunction() { }
@@ -120,133 +91,92 @@ class GradientCheckingCostFunction : public CostFunction {
return function_->Evaluate(parameters, residuals, NULL);
}
- int num_residuals = function_->num_residuals();
+ GradientChecker::ProbeResults results;
+ bool okay = gradient_checker_.Probe(parameters,
+ relative_precision_,
+ &results);
- // Make space for the jacobians of the two methods.
- const vector<int32>& block_sizes = function_->parameter_block_sizes();
- vector<Matrix> term_jacobians(block_sizes.size());
- vector<Matrix> finite_difference_jacobians(block_sizes.size());
- vector<double*> term_jacobian_pointers(block_sizes.size());
- vector<double*> finite_difference_jacobian_pointers(block_sizes.size());
- for (int i = 0; i < block_sizes.size(); i++) {
- term_jacobians[i].resize(num_residuals, block_sizes[i]);
- term_jacobian_pointers[i] = term_jacobians[i].data();
- finite_difference_jacobians[i].resize(num_residuals, block_sizes[i]);
- finite_difference_jacobian_pointers[i] =
- finite_difference_jacobians[i].data();
- }
-
- // Evaluate the derivative using the user supplied code.
- if (!function_->Evaluate(parameters,
- residuals,
- &term_jacobian_pointers[0])) {
- LOG(WARNING) << "Function evaluation failed.";
+ // If the cost function returned false, there's nothing we can say about
+ // the gradients.
+ if (results.return_value == false) {
return false;
}
- // Evaluate the derivative using numeric derivatives.
- finite_diff_cost_function_->Evaluate(
- parameters,
- residuals,
- &finite_difference_jacobian_pointers[0]);
+ // Copy the residuals.
+ const int num_residuals = function_->num_residuals();
+ MatrixRef(residuals, num_residuals, 1) = results.residuals;
- // See if any elements have relative error larger than the threshold.
- int num_bad_jacobian_components = 0;
- double worst_relative_error = 0;
-
- // Accumulate the error message for all the jacobians, since it won't get
- // output if there are no bad jacobian components.
- string m;
+ // Copy the original jacobian blocks into the jacobians array.
+ const vector<int32>& block_sizes = function_->parameter_block_sizes();
for (int k = 0; k < block_sizes.size(); k++) {
- // Copy the original jacobian blocks into the jacobians array.
if (jacobians[k] != NULL) {
MatrixRef(jacobians[k],
- term_jacobians[k].rows(),
- term_jacobians[k].cols()) = term_jacobians[k];
- }
-
- StringAppendF(&m,
- "========== "
- "Jacobian for " "block %d: (%ld by %ld)) "
- "==========\n",
- k,
- static_cast<long>(term_jacobians[k].rows()),
- static_cast<long>(term_jacobians[k].cols()));
- // The funny spacing creates appropriately aligned column headers.
- m += " block row col user dx/dy num diff dx/dy "
- "abs error relative error parameter residual\n";
-
- for (int i = 0; i < term_jacobians[k].rows(); i++) {
- for (int j = 0; j < term_jacobians[k].cols(); j++) {
- double term_jacobian = term_jacobians[k](i, j);
- double finite_jacobian = finite_difference_jacobians[k](i, j);
- double relative_error, absolute_error;
- bool bad_jacobian_entry =
- !IsClose(term_jacobian,
- finite_jacobian,
- relative_precision_,
- &relative_error,
- &absolute_error);
- worst_relative_error = max(worst_relative_error, relative_error);
-
- StringAppendF(&m, "%6d %4d %4d %17g %17g %17g %17g %17g %17g",
- k, i, j,
- term_jacobian, finite_jacobian,
- absolute_error, relative_error,
- parameters[k][j],
- residuals[i]);
-
- if (bad_jacobian_entry) {
- num_bad_jacobian_components++;
- StringAppendF(
- &m, " ------ (%d,%d,%d) Relative error worse than %g",
- k, i, j, relative_precision_);
- }
- m += "\n";
- }
+ results.jacobians[k].rows(),
+ results.jacobians[k].cols()) = results.jacobians[k];
}
}
- // Since there were some bad errors, dump comprehensive debug info.
- if (num_bad_jacobian_components) {
- string header = StringPrintf("Detected %d bad jacobian component(s). "
- "Worst relative error was %g.\n",
- num_bad_jacobian_components,
- worst_relative_error);
- if (!extra_info_.empty()) {
- header += "Extra info for this residual: " + extra_info_ + "\n";
- }
- LOG(WARNING) << "\n" << header << m;
+ if (!okay) {
+ std::string error_log = "Gradient Error detected!\nExtra info for "
+ "this residual: " + extra_info_ + "\n" + results.error_log;
+ callback_->SetGradientErrorDetected(error_log);
}
return true;
}
private:
const CostFunction* function_;
- internal::scoped_ptr<CostFunction> finite_diff_cost_function_;
+ GradientChecker gradient_checker_;
double relative_precision_;
string extra_info_;
+ GradientCheckingIterationCallback* callback_;
};
} // namespace
-CostFunction *CreateGradientCheckingCostFunction(
- const CostFunction *cost_function,
+GradientCheckingIterationCallback::GradientCheckingIterationCallback()
+ : gradient_error_detected_(false) {
+}
+
+CallbackReturnType GradientCheckingIterationCallback::operator()(
+ const IterationSummary& summary) {
+ if (gradient_error_detected_) {
+ LOG(ERROR)<< "Gradient error detected. Terminating solver.";
+ return SOLVER_ABORT;
+ }
+ return SOLVER_CONTINUE;
+}
+void GradientCheckingIterationCallback::SetGradientErrorDetected(
+ std::string& error_log) {
+ mutex_.Lock();
+ gradient_error_detected_ = true;
+ error_log_ += "\n" + error_log;
+ mutex_.Unlock();
+}
+
+CostFunction* CreateGradientCheckingCostFunction(
+ const CostFunction* cost_function,
+ const std::vector<const LocalParameterization*>* local_parameterizations,
double relative_step_size,
double relative_precision,
- const string& extra_info) {
+ const std::string& extra_info,
+ GradientCheckingIterationCallback* callback) {
NumericDiffOptions numeric_diff_options;
numeric_diff_options.relative_step_size = relative_step_size;
return new GradientCheckingCostFunction(cost_function,
+ local_parameterizations,
numeric_diff_options,
- relative_precision,
- extra_info);
+ relative_precision, extra_info,
+ callback);
}
-ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
- double relative_step_size,
- double relative_precision) {
+ProblemImpl* CreateGradientCheckingProblemImpl(
+ ProblemImpl* problem_impl,
+ double relative_step_size,
+ double relative_precision,
+ GradientCheckingIterationCallback* callback) {
+ CHECK_NOTNULL(callback);
// 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
@@ -260,6 +190,9 @@ ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
gradient_checking_problem_options.local_parameterization_ownership =
DO_NOT_TAKE_OWNERSHIP;
+ NumericDiffOptions numeric_diff_options;
+ numeric_diff_options.relative_step_size = relative_step_size;
+
ProblemImpl* gradient_checking_problem_impl = new ProblemImpl(
gradient_checking_problem_options);
@@ -294,19 +227,26 @@ ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
string extra_info = StringPrintf(
"Residual block id %d; depends on parameters [", i);
vector<double*> parameter_blocks;
+ vector<const LocalParameterization*> local_parameterizations;
+ parameter_blocks.reserve(residual_block->NumParameterBlocks());
+ local_parameterizations.reserve(residual_block->NumParameterBlocks());
for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) {
ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
parameter_blocks.push_back(parameter_block->mutable_user_state());
StringAppendF(&extra_info, "%p", parameter_block->mutable_user_state());
extra_info += (j < residual_block->NumParameterBlocks() - 1) ? ", " : "]";
+ local_parameterizations.push_back(problem_impl->GetParameterization(
+ parameter_block->mutable_user_state()));
}
// Wrap the original CostFunction in a GradientCheckingCostFunction.
CostFunction* gradient_checking_cost_function =
- CreateGradientCheckingCostFunction(residual_block->cost_function(),
- relative_step_size,
- relative_precision,
- extra_info);
+ new GradientCheckingCostFunction(residual_block->cost_function(),
+ &local_parameterizations,
+ numeric_diff_options,
+ relative_precision,
+ extra_info,
+ callback);
// The const_cast is necessary because
// ProblemImpl::AddResidualBlock can potentially take ownership of
diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.h b/extern/ceres/internal/ceres/gradient_checking_cost_function.h
index cf92cb72bc5..497f8e2a594 100644
--- a/extern/ceres/internal/ceres/gradient_checking_cost_function.h
+++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.h
@@ -26,7 +26,8 @@
// 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)
+// Authors: keir@google.com (Keir Mierle),
+// dgossow@google.com (David Gossow)
#ifndef CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_
#define CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_
@@ -34,50 +35,76 @@
#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 {
class ProblemImpl;
-// Creates a CostFunction that checks the jacobians that cost_function computes
-// with finite differences. Bad results are logged; required precision is
-// controlled by relative_precision and the numeric differentiation step size is
-// controlled with relative_step_size. See solver.h for a better explanation of
-// relative_step_size. Caller owns result.
-//
-// The condition enforced is that
-//
-// (J_actual(i, j) - J_numeric(i, j))
-// ------------------------------------ < relative_precision
-// max(J_actual(i, j), J_numeric(i, j))
-//
-// where J_actual(i, j) is the jacobian as computed by the supplied cost
-// function (by the user) and J_numeric is the jacobian as computed by finite
-// differences.
-//
-// Note: This is quite inefficient and is intended only for debugging.
+// Callback that collects information about gradient checking errors, and
+// will abort the solve as soon as an error occurs.
+class GradientCheckingIterationCallback : public IterationCallback {
+ public:
+ GradientCheckingIterationCallback();
+
+ // Will return SOLVER_CONTINUE until a gradient error has been detected,
+ // then return SOLVER_ABORT.
+ virtual CallbackReturnType operator()(const IterationSummary& summary);
+
+ // Notify this that a gradient error has occurred (thread safe).
+ void SetGradientErrorDetected(std::string& error_log);
+
+ // Retrieve error status (not thread safe).
+ bool gradient_error_detected() const { return gradient_error_detected_; }
+ const std::string& error_log() const { return error_log_; }
+ private:
+ bool gradient_error_detected_;
+ std::string error_log_;
+ // Mutex protecting member variables.
+ ceres::internal::Mutex mutex_;
+};
+
+// Creates a CostFunction that checks the Jacobians that cost_function computes
+// with finite differences. This API is only intended for unit tests that intend
+// to check the functionality of the GradientCheckingCostFunction
+// implementation directly.
CostFunction* CreateGradientCheckingCostFunction(
const CostFunction* cost_function,
+ const std::vector<const LocalParameterization*>* local_parameterizations,
double relative_step_size,
double relative_precision,
- const std::string& extra_info);
+ const std::string& extra_info,
+ GradientCheckingIterationCallback* callback);
-// Create a new ProblemImpl object from the input problem_impl, where
-// each CostFunctions in problem_impl are wrapped inside a
-// GradientCheckingCostFunctions. This gives us a ProblemImpl object
-// which checks its derivatives against estimates from numeric
-// differentiation everytime a ResidualBlock is evaluated.
+// Create a new ProblemImpl object from the input problem_impl, where all
+// cost functions are wrapped so that each time their Evaluate method is called,
+// an additional check is performed that compares the Jacobians computed by
+// the original cost function with alternative Jacobians computed using
+// numerical differentiation. If local parameterizations are given for any
+// parameters, the Jacobians will be compared in the local space instead of the
+// ambient space. For details on the gradient checking procedure, see the
+// documentation of the GradientChecker class. If an error is detected in any
+// iteration, the respective cost function will notify the
+// GradientCheckingIterationCallback.
+//
+// The caller owns the returned ProblemImpl object.
+//
+// Note: This is quite inefficient and is intended only for debugging.
//
// relative_step_size and relative_precision are parameters to control
// the numeric differentiation and the relative tolerance between the
// jacobian computed by the CostFunctions in problem_impl and
-// jacobians obtained by numerically differentiating them. For more
-// details see the documentation for
-// CreateGradientCheckingCostFunction above.
-ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
- double relative_step_size,
- double relative_precision);
+// jacobians obtained by numerically differentiating them. See the
+// documentation of 'numeric_derivative_relative_step_size' in solver.h for a
+// better explanation.
+ProblemImpl* CreateGradientCheckingProblemImpl(
+ ProblemImpl* problem_impl,
+ double relative_step_size,
+ double relative_precision,
+ GradientCheckingIterationCallback* callback);
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/gradient_problem_solver.cc b/extern/ceres/internal/ceres/gradient_problem_solver.cc
index 9a549c23dac..8709f8f3fbd 100644
--- a/extern/ceres/internal/ceres/gradient_problem_solver.cc
+++ b/extern/ceres/internal/ceres/gradient_problem_solver.cc
@@ -84,6 +84,12 @@ Solver::Options GradientProblemSolverOptionsToSolverOptions(
} // namespace
+bool GradientProblemSolver::Options::IsValid(std::string* error) const {
+ const Solver::Options solver_options =
+ GradientProblemSolverOptionsToSolverOptions(*this);
+ return solver_options.IsValid(error);
+}
+
GradientProblemSolver::~GradientProblemSolver() {
}
@@ -99,8 +105,6 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
using internal::SetSummaryFinalCost;
double start_time = WallTimeInSeconds();
- Solver::Options solver_options =
- GradientProblemSolverOptionsToSolverOptions(options);
*CHECK_NOTNULL(summary) = Summary();
summary->num_parameters = problem.NumParameters();
@@ -112,14 +116,16 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
summary->nonlinear_conjugate_gradient_type = options.nonlinear_conjugate_gradient_type; // NOLINT
// Check validity
- if (!solver_options.IsValid(&summary->message)) {
+ if (!options.IsValid(&summary->message)) {
LOG(ERROR) << "Terminating: " << summary->message;
return;
}
- // Assuming that the parameter blocks in the program have been
- Minimizer::Options minimizer_options;
- minimizer_options = Minimizer::Options(solver_options);
+ // TODO(sameeragarwal): This is a bit convoluted, we should be able
+ // to convert to minimizer options directly, but this will do for
+ // now.
+ Minimizer::Options minimizer_options =
+ Minimizer::Options(GradientProblemSolverOptionsToSolverOptions(options));
minimizer_options.evaluator.reset(new GradientProblemEvaluator(problem));
scoped_ptr<IterationCallback> logging_callback;
diff --git a/extern/ceres/internal/ceres/is_close.cc b/extern/ceres/internal/ceres/is_close.cc
new file mode 100644
index 00000000000..a91a17454d9
--- /dev/null
+++ b/extern/ceres/internal/ceres/is_close.cc
@@ -0,0 +1,59 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2016 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.
+//
+// Authors: keir@google.com (Keir Mierle), dgossow@google.com (David Gossow)
+
+#include "ceres/is_close.h"
+
+#include <algorithm>
+#include <cmath>
+
+namespace ceres {
+namespace internal {
+bool IsClose(double x, double y, double relative_precision,
+ double *relative_error,
+ double *absolute_error) {
+ double local_absolute_error;
+ double local_relative_error;
+ if (!absolute_error) {
+ absolute_error = &local_absolute_error;
+ }
+ if (!relative_error) {
+ relative_error = &local_relative_error;
+ }
+ *absolute_error = std::fabs(x - y);
+ *relative_error = *absolute_error / std::max(std::fabs(x), std::fabs(y));
+ if (x == 0 || y == 0) {
+ // If x or y is exactly zero, then relative difference doesn't have any
+ // meaning. Take the absolute difference instead.
+ *relative_error = *absolute_error;
+ }
+ return *relative_error < std::fabs(relative_precision);
+}
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/is_close.h b/extern/ceres/internal/ceres/is_close.h
new file mode 100644
index 00000000000..7789448c8e8
--- /dev/null
+++ b/extern/ceres/internal/ceres/is_close.h
@@ -0,0 +1,51 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2016 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.
+//
+// Authors: keir@google.com (Keir Mierle), dgossow@google.com (David Gossow)
+//
+// Utility routine for comparing two values.
+
+#ifndef CERES_INTERNAL_IS_CLOSE_H_
+#define CERES_INTERNAL_IS_CLOSE_H_
+
+namespace ceres {
+namespace internal {
+// Returns true if x and y have a relative (unsigned) difference less than
+// relative_precision and false otherwise. Stores the relative and absolute
+// difference in relative/absolute_error if non-NULL. If one of the two values
+// is exactly zero, the absolute difference will be compared, and relative_error
+// will be set to the absolute difference.
+bool IsClose(double x,
+ double y,
+ double relative_precision,
+ double *relative_error,
+ double *absolute_error);
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_IS_CLOSE_H_
diff --git a/extern/ceres/internal/ceres/line_search_minimizer.cc b/extern/ceres/internal/ceres/line_search_minimizer.cc
index 62264fb0b64..fdde1ca9c86 100644
--- a/extern/ceres/internal/ceres/line_search_minimizer.cc
+++ b/extern/ceres/internal/ceres/line_search_minimizer.cc
@@ -191,6 +191,7 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
options.line_search_sufficient_curvature_decrease;
line_search_options.max_step_expansion =
options.max_line_search_step_expansion;
+ line_search_options.is_silent = options.is_silent;
line_search_options.function = &line_search_function;
scoped_ptr<LineSearch>
@@ -341,10 +342,12 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
"as the step was valid when it was selected by the line search.";
LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
break;
- } else if (!Evaluate(evaluator,
- x_plus_delta,
- &current_state,
- &summary->message)) {
+ }
+
+ if (!Evaluate(evaluator,
+ x_plus_delta,
+ &current_state,
+ &summary->message)) {
summary->termination_type = FAILURE;
summary->message =
"Step failed to evaluate. This should not happen as the step was "
@@ -352,15 +355,17 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
summary->message;
LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
break;
- } else {
- x = x_plus_delta;
}
+ // 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.gradient_max_norm = current_state.gradient_max_norm;
iteration_summary.gradient_norm = sqrt(current_state.gradient_squared_norm);
iteration_summary.cost_change = previous_state.cost - current_state.cost;
iteration_summary.cost = current_state.cost + summary->fixed_cost;
- iteration_summary.step_norm = delta.norm();
+
iteration_summary.step_is_valid = true;
iteration_summary.step_is_successful = true;
iteration_summary.step_size = current_state.step_size;
@@ -376,6 +381,13 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
WallTimeInSeconds() - start_time
+ summary->preprocessor_time_in_seconds;
+ // Iterations inside the line search algorithm are considered
+ // 'steps' in the broader context, to distinguish these inner
+ // iterations from from the outer iterations of the line search
+ // minimizer. The number of line search steps is the total number
+ // of inner line search iterations (or steps) across the entire
+ // minimization.
+ summary->num_line_search_steps += line_search_summary.num_iterations;
summary->line_search_cost_evaluation_time_in_seconds +=
line_search_summary.cost_evaluation_time_in_seconds;
summary->line_search_gradient_evaluation_time_in_seconds +=
diff --git a/extern/ceres/internal/ceres/local_parameterization.cc b/extern/ceres/internal/ceres/local_parameterization.cc
index 82004761ec0..a6bf1f6ddcc 100644
--- a/extern/ceres/internal/ceres/local_parameterization.cc
+++ b/extern/ceres/internal/ceres/local_parameterization.cc
@@ -30,6 +30,8 @@
#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"
@@ -87,28 +89,17 @@ bool IdentityParameterization::MultiplyByJacobian(const double* x,
}
SubsetParameterization::SubsetParameterization(
- int size,
- const vector<int>& constant_parameters)
- : local_size_(size - constant_parameters.size()),
- constancy_mask_(size, 0) {
- CHECK_GT(constant_parameters.size(), 0)
- << "The set of constant parameters should contain at least "
- << "one element. If you do not wish to hold any parameters "
- << "constant, then do not use a SubsetParameterization";
-
+ int size, const vector<int>& constant_parameters)
+ : local_size_(size - constant_parameters.size()), constancy_mask_(size, 0) {
vector<int> constant = constant_parameters;
- sort(constant.begin(), constant.end());
- CHECK(unique(constant.begin(), constant.end()) == constant.end())
+ std::sort(constant.begin(), constant.end());
+ CHECK_GE(constant.front(), 0)
+ << "Indices indicating constant parameter must be greater than zero.";
+ CHECK_LT(constant.back(), size)
+ << "Indices indicating constant parameter must be less than the size "
+ << "of the parameter block.";
+ CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end())
<< "The set of constant parameters cannot contain duplicates";
- CHECK_LT(constant_parameters.size(), size)
- << "Number of parameters held constant should be less "
- << "than the size of the parameter block. If you wish "
- << "to hold the entire parameter block constant, then a "
- << "efficient way is to directly mark it as constant "
- << "instead of using a LocalParameterization to do so.";
- CHECK_GE(*min_element(constant.begin(), constant.end()), 0);
- CHECK_LT(*max_element(constant.begin(), constant.end()), size);
-
for (int i = 0; i < constant_parameters.size(); ++i) {
constancy_mask_[constant_parameters[i]] = 1;
}
@@ -129,6 +120,10 @@ bool SubsetParameterization::Plus(const double* x,
bool SubsetParameterization::ComputeJacobian(const double* x,
double* jacobian) const {
+ if (local_size_ == 0) {
+ return true;
+ }
+
MatrixRef m(jacobian, constancy_mask_.size(), local_size_);
m.setZero();
for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) {
@@ -143,6 +138,10 @@ bool SubsetParameterization::MultiplyByJacobian(const double* x,
const int num_rows,
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]) {
@@ -184,6 +183,39 @@ bool QuaternionParameterization::ComputeJacobian(const double* x,
return true;
}
+bool EigenQuaternionParameterization::Plus(const double* x_ptr,
+ const double* delta,
+ double* x_plus_delta_ptr) const {
+ Eigen::Map<Eigen::Quaterniond> x_plus_delta(x_plus_delta_ptr);
+ Eigen::Map<const Eigen::Quaterniond> x(x_ptr);
+
+ const double norm_delta =
+ sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
+ if (norm_delta > 0.0) {
+ const double sin_delta_by_delta = sin(norm_delta) / norm_delta;
+
+ // Note, in the constructor w is first.
+ Eigen::Quaterniond delta_q(cos(norm_delta),
+ sin_delta_by_delta * delta[0],
+ sin_delta_by_delta * delta[1],
+ sin_delta_by_delta * delta[2]);
+ x_plus_delta = delta_q * x;
+ } else {
+ x_plus_delta = x;
+ }
+
+ return true;
+}
+
+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
+ return true;
+}
+
HomogeneousVectorParameterization::HomogeneousVectorParameterization(int size)
: size_(size) {
CHECK_GT(size_, 1) << "The size of the homogeneous vector needs to be "
@@ -332,9 +364,9 @@ bool ProductParameterization::ComputeJacobian(const double* x,
if (!param->ComputeJacobian(x + x_cursor, buffer.get())) {
return false;
}
-
jacobian.block(x_cursor, delta_cursor, global_size, local_size)
= MatrixRef(buffer.get(), global_size, local_size);
+
delta_cursor += local_size;
x_cursor += global_size;
}
diff --git a/extern/ceres/internal/ceres/map_util.h b/extern/ceres/internal/ceres/map_util.h
index 61c531f297c..f55aee37689 100644
--- a/extern/ceres/internal/ceres/map_util.h
+++ b/extern/ceres/internal/ceres/map_util.h
@@ -67,7 +67,7 @@ FindOrDie(const Collection& collection,
// If the key is present in the map then the value associated with that
// key is returned, otherwise the value passed as a default is returned.
template <class Collection>
-const typename Collection::value_type::second_type&
+const typename Collection::value_type::second_type
FindWithDefault(const Collection& collection,
const typename Collection::value_type::first_type& key,
const typename Collection::value_type::second_type& value) {
diff --git a/extern/ceres/internal/ceres/parameter_block.h b/extern/ceres/internal/ceres/parameter_block.h
index cb7140d9582..8e21553c668 100644
--- a/extern/ceres/internal/ceres/parameter_block.h
+++ b/extern/ceres/internal/ceres/parameter_block.h
@@ -161,25 +161,34 @@ class ParameterBlock {
// 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_;
+
CHECK(new_parameterization->GlobalSize() == size_)
<< "Invalid parameterization for parameter block. The parameter block "
<< "has size " << size_ << " while the parameterization has a global "
<< "size of " << new_parameterization->GlobalSize() << ". Did you "
<< "accidentally use the wrong parameter block or parameterization?";
- if (new_parameterization != local_parameterization_) {
- CHECK(local_parameterization_ == NULL)
- << "Can't re-set the local parameterization; it leads to "
- << "ambiguous ownership.";
- local_parameterization_ = new_parameterization;
- local_parameterization_jacobian_.reset(
- new double[local_parameterization_->GlobalSize() *
- local_parameterization_->LocalSize()]);
- CHECK(UpdateLocalParameterizationJacobian())
- << "Local parameterization Jacobian computation failed for x: "
- << ConstVectorRef(state_, Size()).transpose();
- } else {
- // Ignore the case that the parameterizations match.
- }
+
+ CHECK_GT(new_parameterization->LocalSize(), 0)
+ << "Invalid parameterization. Parameterizations must have a positive "
+ << "dimensional tangent space.";
+
+ local_parameterization_ = new_parameterization;
+ local_parameterization_jacobian_.reset(
+ new double[local_parameterization_->GlobalSize() *
+ local_parameterization_->LocalSize()]);
+ CHECK(UpdateLocalParameterizationJacobian())
+ << "Local parameterization Jacobian computation failed for x: "
+ << ConstVectorRef(state_, Size()).transpose();
}
void SetUpperBound(int index, double upper_bound) {
diff --git a/extern/ceres/internal/ceres/problem.cc b/extern/ceres/internal/ceres/problem.cc
index 03b7d6afa48..730ce642036 100644
--- a/extern/ceres/internal/ceres/problem.cc
+++ b/extern/ceres/internal/ceres/problem.cc
@@ -174,6 +174,10 @@ void Problem::SetParameterBlockVariable(double* values) {
problem_impl_->SetParameterBlockVariable(values);
}
+bool Problem::IsParameterBlockConstant(double* values) const {
+ return problem_impl_->IsParameterBlockConstant(values);
+}
+
void Problem::SetParameterization(
double* values,
LocalParameterization* local_parameterization) {
diff --git a/extern/ceres/internal/ceres/problem_impl.cc b/extern/ceres/internal/ceres/problem_impl.cc
index 8547d5d3f77..4abea8b33ee 100644
--- a/extern/ceres/internal/ceres/problem_impl.cc
+++ b/extern/ceres/internal/ceres/problem_impl.cc
@@ -249,10 +249,11 @@ ResidualBlock* ProblemImpl::AddResidualBlock(
// Check for duplicate parameter blocks.
vector<double*> sorted_parameter_blocks(parameter_blocks);
sort(sorted_parameter_blocks.begin(), sorted_parameter_blocks.end());
- vector<double*>::const_iterator duplicate_items =
- unique(sorted_parameter_blocks.begin(),
- sorted_parameter_blocks.end());
- if (duplicate_items != sorted_parameter_blocks.end()) {
+ const bool has_duplicate_items =
+ (std::adjacent_find(sorted_parameter_blocks.begin(),
+ sorted_parameter_blocks.end())
+ != sorted_parameter_blocks.end());
+ if (has_duplicate_items) {
string blocks;
for (int i = 0; i < parameter_blocks.size(); ++i) {
blocks += StringPrintf(" %p ", parameter_blocks[i]);
@@ -572,6 +573,16 @@ 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.";
+
+ return parameter_block->IsConstant();
+}
+
void ProblemImpl::SetParameterBlockVariable(double* values) {
ParameterBlock* parameter_block =
FindWithDefault(parameter_block_map_, values, NULL);
diff --git a/extern/ceres/internal/ceres/problem_impl.h b/extern/ceres/internal/ceres/problem_impl.h
index f42bde6c793..a4689c362f6 100644
--- a/extern/ceres/internal/ceres/problem_impl.h
+++ b/extern/ceres/internal/ceres/problem_impl.h
@@ -128,6 +128,8 @@ class ProblemImpl {
void SetParameterBlockConstant(double* values);
void SetParameterBlockVariable(double* values);
+ bool IsParameterBlockConstant(double* values) const;
+
void SetParameterization(double* values,
LocalParameterization* local_parameterization);
const LocalParameterization* GetParameterization(double* values) const;
diff --git a/extern/ceres/internal/ceres/reorder_program.cc b/extern/ceres/internal/ceres/reorder_program.cc
index d0e8f32b3b7..a7c37107591 100644
--- a/extern/ceres/internal/ceres/reorder_program.cc
+++ b/extern/ceres/internal/ceres/reorder_program.cc
@@ -142,6 +142,11 @@ void OrderingForSparseNormalCholeskyUsingSuiteSparse(
ordering);
}
+ VLOG(2) << "Block ordering stats: "
+ << " flops: " << ss.mutable_cc()->fl
+ << " lnz : " << ss.mutable_cc()->lnz
+ << " anz : " << ss.mutable_cc()->anz;
+
ss.Free(block_jacobian_transpose);
#endif // CERES_NO_SUITESPARSE
}
diff --git a/extern/ceres/internal/ceres/residual_block.h b/extern/ceres/internal/ceres/residual_block.h
index 05e6d1f81e5..a32f1c36cd3 100644
--- a/extern/ceres/internal/ceres/residual_block.h
+++ b/extern/ceres/internal/ceres/residual_block.h
@@ -127,7 +127,7 @@ class ResidualBlock {
int index() const { return index_; }
void set_index(int index) { index_ = index; }
- std::string ToString() {
+ std::string ToString() const {
return StringPrintf("{residual block; index=%d}", index_);
}
diff --git a/extern/ceres/internal/ceres/schur_complement_solver.cc b/extern/ceres/internal/ceres/schur_complement_solver.cc
index 2491060dcdc..65449832c4c 100644
--- a/extern/ceres/internal/ceres/schur_complement_solver.cc
+++ b/extern/ceres/internal/ceres/schur_complement_solver.cc
@@ -33,6 +33,7 @@
#include <algorithm>
#include <ctime>
#include <set>
+#include <sstream>
#include <vector>
#include "ceres/block_random_access_dense_matrix.h"
@@ -563,6 +564,12 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingEigen(
// 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;
diff --git a/extern/ceres/internal/ceres/solver.cc b/extern/ceres/internal/ceres/solver.cc
index 9f3228bb0be..8411350986a 100644
--- a/extern/ceres/internal/ceres/solver.cc
+++ b/extern/ceres/internal/ceres/solver.cc
@@ -94,7 +94,7 @@ bool CommonOptionsAreValid(const Solver::Options& options, string* error) {
OPTION_GT(num_linear_solver_threads, 0);
if (options.check_gradients) {
OPTION_GT(gradient_check_relative_precision, 0.0);
- OPTION_GT(numeric_derivative_relative_step_size, 0.0);
+ OPTION_GT(gradient_check_numeric_derivative_relative_step_size, 0.0);
}
return true;
}
@@ -351,6 +351,7 @@ void PreSolveSummarize(const Solver::Options& options,
summary->dense_linear_algebra_library_type = options.dense_linear_algebra_library_type; // NOLINT
summary->dogleg_type = options.dogleg_type;
summary->inner_iteration_time_in_seconds = 0.0;
+ summary->num_line_search_steps = 0;
summary->line_search_cost_evaluation_time_in_seconds = 0.0;
summary->line_search_gradient_evaluation_time_in_seconds = 0.0;
summary->line_search_polynomial_minimization_time_in_seconds = 0.0;
@@ -495,21 +496,28 @@ void Solver::Solve(const Solver::Options& options,
// 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;
+ internal::GradientCheckingIterationCallback gradient_checking_callback;
+ Solver::Options modified_options = options;
if (options.check_gradients) {
+ modified_options.callbacks.push_back(&gradient_checking_callback);
gradient_checking_problem.reset(
CreateGradientCheckingProblemImpl(
problem_impl,
- options.numeric_derivative_relative_step_size,
- options.gradient_check_relative_precision));
+ options.gradient_check_numeric_derivative_relative_step_size,
+ options.gradient_check_relative_precision,
+ &gradient_checking_callback));
problem_impl = gradient_checking_problem.get();
program = problem_impl->mutable_program();
}
scoped_ptr<Preprocessor> preprocessor(
- Preprocessor::Create(options.minimizer_type));
+ Preprocessor::Create(modified_options.minimizer_type));
PreprocessedProblem pp;
- const bool status = preprocessor->Preprocess(options, problem_impl, &pp);
+ const bool status = preprocessor->Preprocess(modified_options, problem_impl, &pp);
summary->fixed_cost = pp.fixed_cost;
summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time;
@@ -534,6 +542,13 @@ void Solver::Solve(const Solver::Options& options,
summary->postprocessor_time_in_seconds =
WallTimeInSeconds() - postprocessor_start_time;
+ // If the gradient checker reported an error, we want to report FAILURE
+ // instead of USER_FAILURE and provide the error log.
+ if (gradient_checking_callback.gradient_error_detected()) {
+ summary->termination_type = FAILURE;
+ summary->message = gradient_checking_callback.error_log();
+ }
+
summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
}
@@ -556,6 +571,7 @@ Solver::Summary::Summary()
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),
@@ -696,16 +712,14 @@ string Solver::Summary::FullReport() const {
num_linear_solver_threads_given,
num_linear_solver_threads_used);
- if (IsSchurType(linear_solver_type_used)) {
- string given;
- StringifyOrdering(linear_solver_ordering_given, &given);
- string used;
- StringifyOrdering(linear_solver_ordering_used, &used);
- StringAppendF(&report,
- "Linear solver ordering %22s %24s\n",
- given.c_str(),
- used.c_str());
- }
+ string given;
+ StringifyOrdering(linear_solver_ordering_given, &given);
+ string used;
+ StringifyOrdering(linear_solver_ordering_used, &used);
+ StringAppendF(&report,
+ "Linear solver ordering %22s %24s\n",
+ given.c_str(),
+ used.c_str());
if (inner_iterations_given) {
StringAppendF(&report,
@@ -784,9 +798,14 @@ string Solver::Summary::FullReport() const {
num_inner_iteration_steps);
}
- const bool print_line_search_timing_information =
- minimizer_type == LINE_SEARCH ||
- (minimizer_type == TRUST_REGION && is_constrained);
+ const bool line_search_used =
+ (minimizer_type == LINE_SEARCH ||
+ (minimizer_type == TRUST_REGION && is_constrained));
+
+ if (line_search_used) {
+ StringAppendF(&report, "Line search steps % 14d\n",
+ num_line_search_steps);
+ }
StringAppendF(&report, "\nTime (in seconds):\n");
StringAppendF(&report, "Preprocessor %25.4f\n",
@@ -794,13 +813,13 @@ string Solver::Summary::FullReport() const {
StringAppendF(&report, "\n Residual evaluation %23.4f\n",
residual_evaluation_time_in_seconds);
- if (print_line_search_timing_information) {
+ if (line_search_used) {
StringAppendF(&report, " Line search cost evaluation %10.4f\n",
line_search_cost_evaluation_time_in_seconds);
}
StringAppendF(&report, " Jacobian evaluation %23.4f\n",
jacobian_evaluation_time_in_seconds);
- if (print_line_search_timing_information) {
+ if (line_search_used) {
StringAppendF(&report, " Line search gradient evaluation %6.4f\n",
line_search_gradient_evaluation_time_in_seconds);
}
@@ -815,7 +834,7 @@ string Solver::Summary::FullReport() const {
inner_iteration_time_in_seconds);
}
- if (print_line_search_timing_information) {
+ if (line_search_used) {
StringAppendF(&report, " Line search polynomial minimization %.4f\n",
line_search_polynomial_minimization_time_in_seconds);
}
diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc
index ed00879b47a..a4c2c766ddc 100644
--- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc
+++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc
@@ -33,6 +33,7 @@
#include <algorithm>
#include <cstring>
#include <ctime>
+#include <sstream>
#include "ceres/compressed_row_sparse_matrix.h"
#include "ceres/cxsparse.h"
@@ -71,6 +72,12 @@ LinearSolver::Summary SimplicialLDLTSolve(
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;
diff --git a/extern/ceres/internal/ceres/stringprintf.cc b/extern/ceres/internal/ceres/stringprintf.cc
index d1d8b5fe8ab..b3b7474d8f8 100644
--- a/extern/ceres/internal/ceres/stringprintf.cc
+++ b/extern/ceres/internal/ceres/stringprintf.cc
@@ -43,14 +43,27 @@ namespace internal {
using std::string;
-#ifdef _MSC_VER
-enum { IS_COMPILER_MSVC = 1 };
-#if _MSC_VER < 1800
-#define va_copy(d, s) ((d) = (s))
-#endif
+// 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
-enum { IS_COMPILER_MSVC = 0 };
-#endif
+// 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
@@ -71,13 +84,13 @@ void StringAppendV(string* dst, const char* format, va_list ap) {
return;
}
- if (IS_COMPILER_MSVC) {
- // Error or MSVC running out of space. MSVC 8.0 and higher
- // can be asked about space needed with the special idiom below:
- va_copy(backup_ap, ap);
- result = vsnprintf(NULL, 0, format, backup_ap);
- va_end(backup_ap);
- }
+#if defined (_MSC_VER)
+ // Error or MSVC running out of space. MSVC 8.0 and higher
+ // can be asked about space needed with the special idiom below:
+ va_copy(backup_ap, ap);
+ result = vsnprintf(NULL, 0, format, backup_ap);
+ va_end(backup_ap);
+#endif
if (result < 0) {
// Just an error.
diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.cc b/extern/ceres/internal/ceres/trust_region_minimizer.cc
index d654d0867f1..d809906ab54 100644
--- a/extern/ceres/internal/ceres/trust_region_minimizer.cc
+++ b/extern/ceres/internal/ceres/trust_region_minimizer.cc
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2016 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -43,674 +43,747 @@
#include "ceres/coordinate_descent_minimizer.h"
#include "ceres/evaluator.h"
#include "ceres/file.h"
-#include "ceres/internal/eigen.h"
-#include "ceres/internal/scoped_ptr.h"
#include "ceres/line_search.h"
-#include "ceres/linear_least_squares_problems.h"
-#include "ceres/sparse_matrix.h"
#include "ceres/stringprintf.h"
-#include "ceres/trust_region_strategy.h"
#include "ceres/types.h"
#include "ceres/wall_time.h"
#include "glog/logging.h"
+// Helper macro to simplify some of the control flow.
+#define RETURN_IF_ERROR_AND_LOG(expr) \
+ do { \
+ if (!(expr)) { \
+ LOG(ERROR) << "Terminating: " << solver_summary_->message; \
+ return; \
+ } \
+ } while (0)
+
namespace ceres {
namespace internal {
-namespace {
-LineSearch::Summary DoLineSearch(const Minimizer::Options& options,
- const Vector& x,
- const Vector& gradient,
- const double cost,
- const Vector& delta,
- Evaluator* evaluator) {
- LineSearchFunction line_search_function(evaluator);
+TrustRegionMinimizer::~TrustRegionMinimizer() {}
- LineSearch::Options line_search_options;
- line_search_options.is_silent = true;
- line_search_options.interpolation_type =
- options.line_search_interpolation_type;
- line_search_options.min_step_size = options.min_line_search_step_size;
- line_search_options.sufficient_decrease =
- options.line_search_sufficient_function_decrease;
- line_search_options.max_step_contraction =
- options.max_line_search_step_contraction;
- line_search_options.min_step_contraction =
- options.min_line_search_step_contraction;
- line_search_options.max_num_iterations =
- options.max_num_line_search_step_size_iterations;
- line_search_options.sufficient_curvature_decrease =
- options.line_search_sufficient_curvature_decrease;
- line_search_options.max_step_expansion =
- options.max_line_search_step_expansion;
- line_search_options.function = &line_search_function;
+void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
+ double* parameters,
+ Solver::Summary* solver_summary) {
+ start_time_in_secs_ = WallTimeInSeconds();
+ iteration_start_time_in_secs_ = start_time_in_secs_;
+ Init(options, parameters, solver_summary);
+ RETURN_IF_ERROR_AND_LOG(IterationZero());
+
+ // Create the TrustRegionStepEvaluator. The construction needs to be
+ // delayed to this point because we need the cost for the starting
+ // point to initialize the step evaluator.
+ step_evaluator_.reset(new TrustRegionStepEvaluator(
+ x_cost_,
+ options_.use_nonmonotonic_steps
+ ? options_.max_consecutive_nonmonotonic_steps
+ : 0));
+
+ while (FinalizeIterationAndCheckIfMinimizerCanContinue()) {
+ iteration_start_time_in_secs_ = WallTimeInSeconds();
+ iteration_summary_ = IterationSummary();
+ iteration_summary_.iteration =
+ solver_summary->iterations.back().iteration + 1;
+
+ RETURN_IF_ERROR_AND_LOG(ComputeTrustRegionStep());
+ if (!iteration_summary_.step_is_valid) {
+ RETURN_IF_ERROR_AND_LOG(HandleInvalidStep());
+ continue;
+ }
- std::string message;
- scoped_ptr<LineSearch> line_search(
- CHECK_NOTNULL(LineSearch::Create(ceres::ARMIJO,
- line_search_options,
- &message)));
- LineSearch::Summary summary;
- line_search_function.Init(x, delta);
- line_search->Search(1.0, cost, gradient.dot(delta), &summary);
- return summary;
-}
+ if (options_.is_constrained) {
+ // Use a projected line search to enforce the bounds constraints
+ // and improve the quality of the step.
+ DoLineSearch(x_, gradient_, x_cost_, &delta_);
+ }
+
+ ComputeCandidatePointAndEvaluateCost();
+ DoInnerIterationsIfNeeded();
-} // namespace
+ if (ParameterToleranceReached()) {
+ return;
+ }
+
+ if (FunctionToleranceReached()) {
+ return;
+ }
-// Compute a scaling vector that is used to improve the conditioning
-// of the Jacobian.
-void TrustRegionMinimizer::EstimateScale(const SparseMatrix& jacobian,
- double* scale) const {
- jacobian.SquaredColumnNorm(scale);
- for (int i = 0; i < jacobian.num_cols(); ++i) {
- scale[i] = 1.0 / (1.0 + sqrt(scale[i]));
+ if (IsStepSuccessful()) {
+ RETURN_IF_ERROR_AND_LOG(HandleSuccessfulStep());
+ continue;
+ }
+
+ HandleUnsuccessfulStep();
}
}
-void TrustRegionMinimizer::Init(const Minimizer::Options& options) {
+// Initialize the minimizer, allocate working space and set some of
+// the fields in the solver_summary.
+void TrustRegionMinimizer::Init(const Minimizer::Options& options,
+ double* parameters,
+ Solver::Summary* solver_summary) {
options_ = options;
sort(options_.trust_region_minimizer_iterations_to_dump.begin(),
options_.trust_region_minimizer_iterations_to_dump.end());
+
+ parameters_ = parameters;
+
+ solver_summary_ = solver_summary;
+ solver_summary_->termination_type = NO_CONVERGENCE;
+ solver_summary_->num_successful_steps = 0;
+ 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());
+
+ is_not_silent_ = !options.is_silent;
+ inner_iterations_are_enabled_ =
+ options.inner_iteration_minimizer.get() != NULL;
+ inner_iterations_were_useful_ = false;
+
+ num_parameters_ = evaluator_->NumParameters();
+ num_effective_parameters_ = evaluator_->NumEffectiveParameters();
+ num_residuals_ = evaluator_->NumResiduals();
+ num_consecutive_invalid_steps_ = 0;
+
+ x_ = ConstVectorRef(parameters_, num_parameters_);
+ x_norm_ = x_.norm();
+ residuals_.resize(num_residuals_);
+ trust_region_step_.resize(num_effective_parameters_);
+ delta_.resize(num_effective_parameters_);
+ candidate_x_.resize(num_parameters_);
+ gradient_.resize(num_effective_parameters_);
+ model_residuals_.resize(num_residuals_);
+ negative_gradient_.resize(num_effective_parameters_);
+ projected_gradient_step_.resize(num_parameters_);
+
+ // By default scaling is one, if the user requests Jacobi scaling of
+ // the Jacobian, we will compute and overwrite this vector.
+ jacobian_scaling_ = Vector::Ones(num_effective_parameters_);
+
+ x_norm_ = -1; // Invalid value
+ x_cost_ = std::numeric_limits<double>::max();
+ minimum_cost_ = x_cost_;
+ model_cost_change_ = 0.0;
}
-void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
- double* parameters,
- Solver::Summary* summary) {
- double start_time = WallTimeInSeconds();
- double iteration_start_time = start_time;
- Init(options);
-
- Evaluator* evaluator = CHECK_NOTNULL(options_.evaluator.get());
- SparseMatrix* jacobian = CHECK_NOTNULL(options_.jacobian.get());
- TrustRegionStrategy* strategy =
- CHECK_NOTNULL(options_.trust_region_strategy.get());
-
- const bool is_not_silent = !options.is_silent;
-
- // If the problem is bounds constrained, then enable the use of a
- // line search after the trust region step has been computed. This
- // line search will automatically use a projected test point onto
- // the feasible set, there by guaranteeing the feasibility of the
- // final output.
- //
- // TODO(sameeragarwal): Make line search available more generally.
- const bool use_line_search = options.is_constrained;
-
- summary->termination_type = NO_CONVERGENCE;
- summary->num_successful_steps = 0;
- summary->num_unsuccessful_steps = 0;
- summary->is_constrained = options.is_constrained;
-
- const int num_parameters = evaluator->NumParameters();
- const int num_effective_parameters = evaluator->NumEffectiveParameters();
- const int num_residuals = evaluator->NumResiduals();
-
- Vector residuals(num_residuals);
- Vector trust_region_step(num_effective_parameters);
- Vector delta(num_effective_parameters);
- Vector x_plus_delta(num_parameters);
- Vector gradient(num_effective_parameters);
- Vector model_residuals(num_residuals);
- Vector scale(num_effective_parameters);
- Vector negative_gradient(num_effective_parameters);
- Vector projected_gradient_step(num_parameters);
-
- IterationSummary iteration_summary;
- iteration_summary.iteration = 0;
- iteration_summary.step_is_valid = false;
- iteration_summary.step_is_successful = false;
- iteration_summary.cost_change = 0.0;
- iteration_summary.gradient_max_norm = 0.0;
- iteration_summary.gradient_norm = 0.0;
- iteration_summary.step_norm = 0.0;
- iteration_summary.relative_decrease = 0.0;
- iteration_summary.trust_region_radius = strategy->Radius();
- iteration_summary.eta = options_.eta;
- iteration_summary.linear_solver_iterations = 0;
- iteration_summary.step_solver_time_in_seconds = 0;
-
- VectorRef x_min(parameters, num_parameters);
- Vector x = x_min;
- // Project onto the feasible set.
- if (options.is_constrained) {
- delta.setZero();
- if (!evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
- summary->message =
+// 1. Project the initial solution onto the feasible set if needed.
+// 2. Compute the initial cost, jacobian & gradient.
+//
+// Return true if all computations can be performed successfully.
+bool TrustRegionMinimizer::IterationZero() {
+ iteration_summary_ = IterationSummary();
+ iteration_summary_.iteration = 0;
+ iteration_summary_.step_is_valid = false;
+ iteration_summary_.step_is_successful = false;
+ iteration_summary_.cost_change = 0.0;
+ iteration_summary_.gradient_max_norm = 0.0;
+ iteration_summary_.gradient_norm = 0.0;
+ iteration_summary_.step_norm = 0.0;
+ iteration_summary_.relative_decrease = 0.0;
+ iteration_summary_.eta = options_.eta;
+ iteration_summary_.linear_solver_iterations = 0;
+ iteration_summary_.step_solver_time_in_seconds = 0;
+
+ if (options_.is_constrained) {
+ delta_.setZero();
+ if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) {
+ solver_summary_->message =
"Unable to project initial point onto the feasible set.";
- summary->termination_type = FAILURE;
- LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
- return;
+ solver_summary_->termination_type = FAILURE;
+ return false;
}
- x_min = x_plus_delta;
- x = x_plus_delta;
- }
- double x_norm = x.norm();
-
- // Do initial cost and Jacobian evaluation.
- double cost = 0.0;
- if (!evaluator->Evaluate(x.data(),
- &cost,
- residuals.data(),
- gradient.data(),
- jacobian)) {
- summary->message = "Residual and Jacobian evaluation failed.";
- summary->termination_type = FAILURE;
- LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
- return;
+ x_ = candidate_x_;
+ x_norm_ = x_.norm();
}
- negative_gradient = -gradient;
- if (!evaluator->Plus(x.data(),
- negative_gradient.data(),
- projected_gradient_step.data())) {
- summary->message = "Unable to compute gradient step.";
- summary->termination_type = FAILURE;
- LOG(ERROR) << "Terminating: " << summary->message;
- return;
+ if (!EvaluateGradientAndJacobian()) {
+ return false;
}
- summary->initial_cost = cost + summary->fixed_cost;
- iteration_summary.cost = cost + summary->fixed_cost;
- iteration_summary.gradient_max_norm =
- (x - projected_gradient_step).lpNorm<Eigen::Infinity>();
- iteration_summary.gradient_norm = (x - projected_gradient_step).norm();
-
- if (iteration_summary.gradient_max_norm <= options.gradient_tolerance) {
- summary->message = StringPrintf("Gradient tolerance reached. "
- "Gradient max norm: %e <= %e",
- iteration_summary.gradient_max_norm,
- options_.gradient_tolerance);
- summary->termination_type = CONVERGENCE;
- VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
-
- // Ensure that there is an iteration summary object for iteration
- // 0 in Summary::iterations.
- iteration_summary.iteration_time_in_seconds =
- WallTimeInSeconds() - iteration_start_time;
- iteration_summary.cumulative_time_in_seconds =
- WallTimeInSeconds() - start_time +
- summary->preprocessor_time_in_seconds;
- summary->iterations.push_back(iteration_summary);
- return;
- }
+ solver_summary_->initial_cost = x_cost_ + solver_summary_->fixed_cost;
+ iteration_summary_.step_is_valid = true;
+ iteration_summary_.step_is_successful = true;
+ return true;
+}
- if (options_.jacobi_scaling) {
- EstimateScale(*jacobian, scale.data());
- jacobian->ScaleColumns(scale.data());
- } else {
- scale.setOnes();
+// For the current x_, compute
+//
+// 1. Cost
+// 2. Jacobian
+// 3. Gradient
+// 4. Scale the Jacobian if needed (and compute the scaling if we are
+// in iteration zero).
+// 5. Compute the 2 and max norm of the gradient.
+//
+// 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(),
+ &x_cost_,
+ residuals_.data(),
+ gradient_.data(),
+ jacobian_)) {
+ solver_summary_->message = "Residual and Jacobian evaluation failed.";
+ solver_summary_->termination_type = FAILURE;
+ return false;
}
- iteration_summary.iteration_time_in_seconds =
- WallTimeInSeconds() - iteration_start_time;
- iteration_summary.cumulative_time_in_seconds =
- WallTimeInSeconds() - start_time
- + summary->preprocessor_time_in_seconds;
- summary->iterations.push_back(iteration_summary);
-
- int num_consecutive_nonmonotonic_steps = 0;
- double minimum_cost = cost;
- double reference_cost = cost;
- double accumulated_reference_model_cost_change = 0.0;
- double candidate_cost = cost;
- double accumulated_candidate_model_cost_change = 0.0;
- int num_consecutive_invalid_steps = 0;
- bool inner_iterations_are_enabled =
- options.inner_iteration_minimizer.get() != NULL;
- while (true) {
- bool inner_iterations_were_useful = false;
- if (!RunCallbacks(options, iteration_summary, summary)) {
- return;
- }
+ iteration_summary_.cost = x_cost_ + solver_summary_->fixed_cost;
- iteration_start_time = WallTimeInSeconds();
- if (iteration_summary.iteration >= options_.max_num_iterations) {
- summary->message = "Maximum number of iterations reached.";
- summary->termination_type = NO_CONVERGENCE;
- VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
- return;
+ if (options_.jacobi_scaling) {
+ if (iteration_summary_.iteration == 0) {
+ // Compute a scaling vector that is used to improve the
+ // conditioning of the Jacobian.
+ //
+ // jacobian_scaling_ = diag(J'J)^{-1}
+ jacobian_->SquaredColumnNorm(jacobian_scaling_.data());
+ for (int i = 0; i < jacobian_->num_cols(); ++i) {
+ // Add one to the denominator to prevent division by zero.
+ jacobian_scaling_[i] = 1.0 / (1.0 + sqrt(jacobian_scaling_[i]));
+ }
}
- const double total_solver_time = iteration_start_time - start_time +
- summary->preprocessor_time_in_seconds;
- if (total_solver_time >= options_.max_solver_time_in_seconds) {
- summary->message = "Maximum solver time reached.";
- summary->termination_type = NO_CONVERGENCE;
- VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
+ // jacobian = jacobian * diag(J'J) ^{-1}
+ jacobian_->ScaleColumns(jacobian_scaling_.data());
+ }
+
+ // The gradient exists in the local tangent space. To account for
+ // the bounds constraints correctly, instead of just computing the
+ // norm of the gradient vector, we compute
+ //
+ // |Plus(x, -gradient) - x|
+ //
+ // Where the Plus operator lifts the negative gradient to the
+ // ambient space, adds it to x and projects it on the hypercube
+ // defined by the bounds.
+ negative_gradient_ = -gradient_;
+ if (!evaluator_->Plus(x_.data(),
+ negative_gradient_.data(),
+ projected_gradient_step_.data())) {
+ solver_summary_->message =
+ "projected_gradient_step = Plus(x, -gradient) failed.";
+ solver_summary_->termination_type = FAILURE;
+ return false;
+ }
- const double strategy_start_time = WallTimeInSeconds();
- TrustRegionStrategy::PerSolveOptions per_solve_options;
- per_solve_options.eta = options_.eta;
- if (find(options_.trust_region_minimizer_iterations_to_dump.begin(),
- options_.trust_region_minimizer_iterations_to_dump.end(),
- iteration_summary.iteration) !=
- options_.trust_region_minimizer_iterations_to_dump.end()) {
- per_solve_options.dump_format_type =
- options_.trust_region_problem_dump_format_type;
- per_solve_options.dump_filename_base =
- JoinPath(options_.trust_region_problem_dump_directory,
- StringPrintf("ceres_solver_iteration_%03d",
- iteration_summary.iteration));
+ iteration_summary_.gradient_max_norm =
+ (x_ - projected_gradient_step_).lpNorm<Eigen::Infinity>();
+ iteration_summary_.gradient_norm = (x_ - projected_gradient_step_).norm();
+ return true;
+}
+
+// 1. Add the final timing information to the iteration summary.
+// 2. Run the callbacks
+// 3. Check for termination based on
+// a. Run time
+// b. Iteration count
+// c. Max norm of the gradient
+// d. Size of the trust region radius.
+//
+// Returns true if user did not terminate the solver and none of these
+// termination criterion are met.
+bool TrustRegionMinimizer::FinalizeIterationAndCheckIfMinimizerCanContinue() {
+ if (iteration_summary_.step_is_successful) {
+ ++solver_summary_->num_successful_steps;
+ if (x_cost_ < minimum_cost_) {
+ minimum_cost_ = x_cost_;
+ VectorRef(parameters_, num_parameters_) = x_;
+ iteration_summary_.step_is_nonmonotonic = false;
} else {
- per_solve_options.dump_format_type = TEXTFILE;
- per_solve_options.dump_filename_base.clear();
+ iteration_summary_.step_is_nonmonotonic = true;
}
+ } else {
+ ++solver_summary_->num_unsuccessful_steps;
+ }
- TrustRegionStrategy::Summary strategy_summary =
- strategy->ComputeStep(per_solve_options,
- jacobian,
- residuals.data(),
- trust_region_step.data());
-
- if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) {
- summary->message =
- "Linear solver failed due to unrecoverable "
- "non-numeric causes. Please see the error log for clues. ";
- summary->termination_type = FAILURE;
- LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
+ iteration_summary_.trust_region_radius = strategy_->Radius();
+ iteration_summary_.iteration_time_in_seconds =
+ WallTimeInSeconds() - iteration_start_time_in_secs_;
+ iteration_summary_.cumulative_time_in_seconds =
+ WallTimeInSeconds() - start_time_in_secs_ +
+ solver_summary_->preprocessor_time_in_seconds;
- iteration_summary = IterationSummary();
- iteration_summary.iteration = summary->iterations.back().iteration + 1;
- iteration_summary.step_solver_time_in_seconds =
- WallTimeInSeconds() - strategy_start_time;
- iteration_summary.linear_solver_iterations =
- strategy_summary.num_iterations;
- iteration_summary.step_is_valid = false;
- iteration_summary.step_is_successful = false;
-
- double model_cost_change = 0.0;
- if (strategy_summary.termination_type != LINEAR_SOLVER_FAILURE) {
- // new_model_cost
- // = 1/2 [f + J * step]^2
- // = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ]
- // model_cost_change
- // = cost - new_model_cost
- // = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step]
- // = -f'J * step - step' * J' * J * step / 2
- model_residuals.setZero();
- jacobian->RightMultiply(trust_region_step.data(), model_residuals.data());
- model_cost_change =
- - model_residuals.dot(residuals + model_residuals / 2.0);
-
- if (model_cost_change < 0.0) {
- VLOG_IF(1, is_not_silent)
- << "Invalid step: current_cost: " << cost
- << " absolute difference " << model_cost_change
- << " relative difference " << (model_cost_change / cost);
- } else {
- iteration_summary.step_is_valid = true;
- }
- }
+ solver_summary_->iterations.push_back(iteration_summary_);
- if (!iteration_summary.step_is_valid) {
- // Invalid steps can happen due to a number of reasons, and we
- // allow a limited number of successive failures, and return with
- // FAILURE if this limit is exceeded.
- if (++num_consecutive_invalid_steps >=
- options_.max_num_consecutive_invalid_steps) {
- summary->message = StringPrintf(
- "Number of successive invalid steps more "
- "than Solver::Options::max_num_consecutive_invalid_steps: %d",
- options_.max_num_consecutive_invalid_steps);
- summary->termination_type = FAILURE;
- LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
+ if (!RunCallbacks(options_, iteration_summary_, solver_summary_)) {
+ return false;
+ }
- // We are going to try and reduce the trust region radius and
- // solve again. To do this, we are going to treat this iteration
- // as an unsuccessful iteration. Since the various callbacks are
- // still executed, we are going to fill the iteration summary
- // with data that assumes a step of length zero and no progress.
- iteration_summary.cost = cost + summary->fixed_cost;
- iteration_summary.cost_change = 0.0;
- iteration_summary.gradient_max_norm =
- summary->iterations.back().gradient_max_norm;
- iteration_summary.gradient_norm =
- summary->iterations.back().gradient_norm;
- iteration_summary.step_norm = 0.0;
- iteration_summary.relative_decrease = 0.0;
- iteration_summary.eta = options_.eta;
- } else {
- // The step is numerically valid, so now we can judge its quality.
- num_consecutive_invalid_steps = 0;
+ if (MaxSolverTimeReached()) {
+ return false;
+ }
- // Undo the Jacobian column scaling.
- delta = (trust_region_step.array() * scale.array()).matrix();
+ if (MaxSolverIterationsReached()) {
+ return false;
+ }
- // Try improving the step further by using an ARMIJO line
- // search.
- //
- // TODO(sameeragarwal): What happens to trust region sizing as
- // it interacts with the line search ?
- if (use_line_search) {
- const LineSearch::Summary line_search_summary =
- DoLineSearch(options, x, gradient, cost, delta, evaluator);
-
- summary->line_search_cost_evaluation_time_in_seconds +=
- line_search_summary.cost_evaluation_time_in_seconds;
- summary->line_search_gradient_evaluation_time_in_seconds +=
- line_search_summary.gradient_evaluation_time_in_seconds;
- summary->line_search_polynomial_minimization_time_in_seconds +=
- line_search_summary.polynomial_minimization_time_in_seconds;
- summary->line_search_total_time_in_seconds +=
- line_search_summary.total_time_in_seconds;
-
- if (line_search_summary.success) {
- delta *= line_search_summary.optimal_step_size;
- }
- }
+ if (GradientToleranceReached()) {
+ return false;
+ }
- double new_cost = std::numeric_limits<double>::max();
- if (evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
- if (!evaluator->Evaluate(x_plus_delta.data(),
- &new_cost,
- NULL,
- NULL,
- NULL)) {
- LOG_IF(WARNING, is_not_silent)
- << "Step failed to evaluate. "
- << "Treating it as a step with infinite cost";
- new_cost = std::numeric_limits<double>::max();
- }
- } else {
- LOG_IF(WARNING, is_not_silent)
- << "x_plus_delta = Plus(x, delta) failed. "
- << "Treating it as a step with infinite cost";
- }
+ if (MinTrustRegionRadiusReached()) {
+ return false;
+ }
- if (new_cost < std::numeric_limits<double>::max()) {
- // Check if performing an inner iteration will make it better.
- if (inner_iterations_are_enabled) {
- ++summary->num_inner_iteration_steps;
- double inner_iteration_start_time = WallTimeInSeconds();
- const double x_plus_delta_cost = new_cost;
- Vector inner_iteration_x = x_plus_delta;
- Solver::Summary inner_iteration_summary;
- options.inner_iteration_minimizer->Minimize(options,
- inner_iteration_x.data(),
- &inner_iteration_summary);
- if (!evaluator->Evaluate(inner_iteration_x.data(),
- &new_cost,
- NULL, NULL, NULL)) {
- VLOG_IF(2, is_not_silent) << "Inner iteration failed.";
- new_cost = x_plus_delta_cost;
- } else {
- x_plus_delta = inner_iteration_x;
- // Boost the model_cost_change, since the inner iteration
- // improvements are not accounted for by the trust region.
- model_cost_change += x_plus_delta_cost - new_cost;
- VLOG_IF(2, is_not_silent)
- << "Inner iteration succeeded; Current cost: " << cost
- << " Trust region step cost: " << x_plus_delta_cost
- << " Inner iteration cost: " << new_cost;
-
- inner_iterations_were_useful = new_cost < cost;
-
- const double inner_iteration_relative_progress =
- 1.0 - new_cost / x_plus_delta_cost;
- // Disable inner iterations once the relative improvement
- // drops below tolerance.
- inner_iterations_are_enabled =
- (inner_iteration_relative_progress >
- options.inner_iteration_tolerance);
- VLOG_IF(2, is_not_silent && !inner_iterations_are_enabled)
- << "Disabling inner iterations. Progress : "
- << inner_iteration_relative_progress;
- }
- summary->inner_iteration_time_in_seconds +=
- WallTimeInSeconds() - inner_iteration_start_time;
- }
- }
+ return true;
+}
- iteration_summary.step_norm = (x - x_plus_delta).norm();
-
- // Convergence based on parameter_tolerance.
- const double step_size_tolerance = options_.parameter_tolerance *
- (x_norm + options_.parameter_tolerance);
- if (iteration_summary.step_norm <= step_size_tolerance) {
- summary->message =
- StringPrintf("Parameter tolerance reached. "
- "Relative step_norm: %e <= %e.",
- (iteration_summary.step_norm /
- (x_norm + options_.parameter_tolerance)),
- options_.parameter_tolerance);
- summary->termination_type = CONVERGENCE;
- VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
+// Compute the trust region step using the TrustRegionStrategy chosen
+// by the user.
+//
+// If the strategy returns with LINEAR_SOLVER_FATAL_ERROR, which
+// indicates an unrecoverable error, return false. This is the only
+// condition that returns false.
+//
+// If the strategy returns with LINEAR_SOLVER_FAILURE, which indicates
+// a numerical failure that could be recovered from by retrying
+// (e.g. by increasing the strength of the regularization), we set
+// iteration_summary_.step_is_valid to false and return true.
+//
+// In all other cases, we compute the decrease in the trust region
+// model problem. In exact arithmetic, this should always be
+// positive, but due to numerical problems in the TrustRegionStrategy
+// or round off error when computing the decrease it may be
+// negative. In which case again, we set
+// iteration_summary_.step_is_valid to false.
+bool TrustRegionMinimizer::ComputeTrustRegionStep() {
+ const double strategy_start_time = WallTimeInSeconds();
+ iteration_summary_.step_is_valid = false;
+ TrustRegionStrategy::PerSolveOptions per_solve_options;
+ per_solve_options.eta = options_.eta;
+ if (find(options_.trust_region_minimizer_iterations_to_dump.begin(),
+ options_.trust_region_minimizer_iterations_to_dump.end(),
+ iteration_summary_.iteration) !=
+ options_.trust_region_minimizer_iterations_to_dump.end()) {
+ per_solve_options.dump_format_type =
+ options_.trust_region_problem_dump_format_type;
+ per_solve_options.dump_filename_base =
+ JoinPath(options_.trust_region_problem_dump_directory,
+ StringPrintf("ceres_solver_iteration_%03d",
+ iteration_summary_.iteration));
+ }
- iteration_summary.cost_change = cost - new_cost;
- const double absolute_function_tolerance =
- options_.function_tolerance * 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) / cost,
- options_.function_tolerance);
- summary->termination_type = CONVERGENCE;
- VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
+ TrustRegionStrategy::Summary strategy_summary =
+ strategy_->ComputeStep(per_solve_options,
+ jacobian_,
+ residuals_.data(),
+ trust_region_step_.data());
+
+ if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) {
+ solver_summary_->message =
+ "Linear solver failed due to unrecoverable "
+ "non-numeric causes. Please see the error log for clues. ";
+ solver_summary_->termination_type = FAILURE;
+ return false;
+ }
- const double relative_decrease =
- iteration_summary.cost_change / model_cost_change;
+ iteration_summary_.step_solver_time_in_seconds =
+ WallTimeInSeconds() - strategy_start_time;
+ iteration_summary_.linear_solver_iterations = strategy_summary.num_iterations;
- const double historical_relative_decrease =
- (reference_cost - new_cost) /
- (accumulated_reference_model_cost_change + model_cost_change);
+ if (strategy_summary.termination_type == LINEAR_SOLVER_FAILURE) {
+ return true;
+ }
- // If monotonic steps are being used, then the relative_decrease
- // is the usual ratio of the change in objective function value
- // divided by the change in model cost.
- //
- // If non-monotonic steps are allowed, then we take the maximum
- // of the relative_decrease and the
- // historical_relative_decrease, which measures the increase
- // from a reference iteration. The model cost change is
- // estimated by accumulating the model cost changes since the
- // reference iteration. The historical relative_decrease offers
- // a boost to a step which is not too bad compared to the
- // reference iteration, allowing for non-monotonic steps.
- iteration_summary.relative_decrease =
- options.use_nonmonotonic_steps
- ? std::max(relative_decrease, historical_relative_decrease)
- : relative_decrease;
-
- // Normally, the quality of a trust region step is measured by
- // the ratio
- //
- // cost_change
- // r = -----------------
- // model_cost_change
- //
- // All the change in the nonlinear objective is due to the trust
- // region step so this ratio is a good measure of the quality of
- // the trust region radius. However, when inner iterations are
- // being used, cost_change includes the contribution of the
- // inner iterations and its not fair to credit it all to the
- // trust region algorithm. So we change the ratio to be
- //
- // cost_change
- // r = ------------------------------------------------
- // (model_cost_change + inner_iteration_cost_change)
- //
- // In most cases this is fine, but it can be the case that the
- // change in solution quality due to inner iterations is so large
- // and the trust region step is so bad, that this ratio can become
- // quite small.
- //
- // This can cause the trust region loop to reject this step. To
- // get around this, we expicitly 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.
- //
- // Notice that we do not just check that cost_change is positive
- // which is a weaker condition and would render the
- // min_relative_decrease threshold useless. Instead, we keep
- // track of inner_iterations_were_useful, which is true only
- // when inner iterations lead to a net decrease in the cost.
- iteration_summary.step_is_successful =
- (inner_iterations_were_useful ||
- iteration_summary.relative_decrease >
- options_.min_relative_decrease);
-
- if (iteration_summary.step_is_successful) {
- accumulated_candidate_model_cost_change += model_cost_change;
- accumulated_reference_model_cost_change += model_cost_change;
-
- if (!inner_iterations_were_useful &&
- relative_decrease <= options_.min_relative_decrease) {
- iteration_summary.step_is_nonmonotonic = true;
- VLOG_IF(2, is_not_silent)
- << "Non-monotonic step! "
- << " relative_decrease: "
- << relative_decrease
- << " historical_relative_decrease: "
- << historical_relative_decrease;
- }
- }
- }
+ // new_model_cost
+ // = 1/2 [f + J * step]^2
+ // = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ]
+ // model_cost_change
+ // = cost - new_model_cost
+ // = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step]
+ // = -f'J * step - step' * J' * J * step / 2
+ // = -(J * step)'(f + J * step / 2)
+ model_residuals_.setZero();
+ jacobian_->RightMultiply(trust_region_step_.data(), model_residuals_.data());
+ model_cost_change_ =
+ -model_residuals_.dot(residuals_ + model_residuals_ / 2.0);
+
+ // TODO(sameeragarwal)
+ //
+ // 1. What happens if model_cost_change_ = 0
+ // 2. What happens if -epsilon <= model_cost_change_ < 0 for some
+ // small epsilon due to round off error.
+ iteration_summary_.step_is_valid = (model_cost_change_ > 0.0);
+ if (iteration_summary_.step_is_valid) {
+ // Undo the Jacobian column scaling.
+ delta_ = (trust_region_step_.array() * jacobian_scaling_.array()).matrix();
+ num_consecutive_invalid_steps_ = 0;
+ }
- if (iteration_summary.step_is_successful) {
- ++summary->num_successful_steps;
- strategy->StepAccepted(iteration_summary.relative_decrease);
-
- x = x_plus_delta;
- x_norm = x.norm();
-
- // Step looks good, evaluate the residuals and Jacobian at this
- // point.
- if (!evaluator->Evaluate(x.data(),
- &cost,
- residuals.data(),
- gradient.data(),
- jacobian)) {
- summary->message = "Residual and Jacobian evaluation failed.";
- summary->termination_type = FAILURE;
- LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
+ VLOG_IF(1, is_not_silent_ && !iteration_summary_.step_is_valid)
+ << "Invalid step: current_cost: " << x_cost_
+ << " absolute model cost change: " << model_cost_change_
+ << " relative model cost change: " << (model_cost_change_ / x_cost_);
+ return true;
+}
- negative_gradient = -gradient;
- if (!evaluator->Plus(x.data(),
- negative_gradient.data(),
- projected_gradient_step.data())) {
- summary->message =
- "projected_gradient_step = Plus(x, -gradient) failed.";
- summary->termination_type = FAILURE;
- LOG(ERROR) << "Terminating: " << summary->message;
- return;
- }
+// Invalid steps can happen due to a number of reasons, and we allow a
+// limited number of consecutive failures, and return false if this
+// limit is exceeded.
+bool TrustRegionMinimizer::HandleInvalidStep() {
+ // TODO(sameeragarwal): Should we be returning FAILURE or
+ // NO_CONVERGENCE? The solution value is still usable in many cases,
+ // it is not clear if we should declare the solver a failure
+ // entirely. For example the case where model_cost_change ~ 0.0, but
+ // just slightly negative.
+ if (++num_consecutive_invalid_steps_ >=
+ options_.max_num_consecutive_invalid_steps) {
+ solver_summary_->message = StringPrintf(
+ "Number of consecutive invalid steps more "
+ "than Solver::Options::max_num_consecutive_invalid_steps: %d",
+ options_.max_num_consecutive_invalid_steps);
+ solver_summary_->termination_type = FAILURE;
+ return false;
+ }
- iteration_summary.gradient_max_norm =
- (x - projected_gradient_step).lpNorm<Eigen::Infinity>();
- iteration_summary.gradient_norm = (x - projected_gradient_step).norm();
+ strategy_->StepIsInvalid();
+
+ // We are going to try and reduce the trust region radius and
+ // solve again. To do this, we are going to treat this iteration
+ // as an unsuccessful iteration. Since the various callbacks are
+ // still executed, we are going to fill the iteration summary
+ // with data that assumes a step of length zero and no progress.
+ iteration_summary_.cost = x_cost_ + solver_summary_->fixed_cost;
+ iteration_summary_.cost_change = 0.0;
+ iteration_summary_.gradient_max_norm =
+ solver_summary_->iterations.back().gradient_max_norm;
+ iteration_summary_.gradient_norm =
+ solver_summary_->iterations.back().gradient_norm;
+ iteration_summary_.step_norm = 0.0;
+ iteration_summary_.relative_decrease = 0.0;
+ iteration_summary_.eta = options_.eta;
+ return true;
+}
- if (options_.jacobi_scaling) {
- jacobian->ScaleColumns(scale.data());
- }
+// Use the supplied coordinate descent minimizer to perform inner
+// iterations and compute the improvement due to it. Returns the cost
+// after performing the inner iterations.
+//
+// The optimization is performed with candidate_x_ as the starting
+// point, and if the optimization is successful, candidate_x_ will be
+// updated with the optimized parameters.
+void TrustRegionMinimizer::DoInnerIterationsIfNeeded() {
+ inner_iterations_were_useful_ = false;
+ if (!inner_iterations_are_enabled_ ||
+ candidate_cost_ >= std::numeric_limits<double>::max()) {
+ return;
+ }
- // Update the best, reference and candidate iterates.
- //
- // Based on algorithm 10.1.2 (page 357) of "Trust Region
- // Methods" by Conn Gould & Toint, or equations 33-40 of
- // "Non-monotone trust-region algorithms for nonlinear
- // optimization subject to convex constraints" by Phil Toint,
- // Mathematical Programming, 77, 1997.
- if (cost < minimum_cost) {
- // A step that improves solution quality was found.
- x_min = x;
- minimum_cost = cost;
- // Set the candidate iterate to the current point.
- candidate_cost = cost;
- num_consecutive_nonmonotonic_steps = 0;
- accumulated_candidate_model_cost_change = 0.0;
- } else {
- ++num_consecutive_nonmonotonic_steps;
- if (cost > candidate_cost) {
- // The current iterate is has a higher cost than the
- // candidate iterate. Set the candidate to this point.
- VLOG_IF(2, is_not_silent)
- << "Updating the candidate iterate to the current point.";
- candidate_cost = cost;
- accumulated_candidate_model_cost_change = 0.0;
- }
-
- // At this point we have made too many non-monotonic steps and
- // we are going to reset the value of the reference iterate so
- // as to force the algorithm to descend.
- //
- // This is the case because the candidate iterate has a value
- // greater than minimum_cost but smaller than the reference
- // iterate.
- if (num_consecutive_nonmonotonic_steps ==
- options.max_consecutive_nonmonotonic_steps) {
- VLOG_IF(2, is_not_silent)
- << "Resetting the reference point to the candidate point";
- reference_cost = candidate_cost;
- accumulated_reference_model_cost_change =
- accumulated_candidate_model_cost_change;
- }
- }
- } else {
- ++summary->num_unsuccessful_steps;
- if (iteration_summary.step_is_valid) {
- strategy->StepRejected(iteration_summary.relative_decrease);
- } else {
- strategy->StepIsInvalid();
- }
- }
+ double inner_iteration_start_time = WallTimeInSeconds();
+ ++solver_summary_->num_inner_iteration_steps;
+ inner_iteration_x_ = candidate_x_;
+ Solver::Summary inner_iteration_summary;
+ 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)) {
+ VLOG_IF(2, is_not_silent_) << "Inner iteration failed.";
+ return;
+ }
- iteration_summary.cost = cost + summary->fixed_cost;
- iteration_summary.trust_region_radius = strategy->Radius();
- iteration_summary.iteration_time_in_seconds =
- WallTimeInSeconds() - iteration_start_time;
- iteration_summary.cumulative_time_in_seconds =
- WallTimeInSeconds() - start_time
- + summary->preprocessor_time_in_seconds;
- summary->iterations.push_back(iteration_summary);
-
- // If the step was successful, check for the gradient norm
- // collapsing to zero, and if the step is unsuccessful then check
- // if the trust region radius has collapsed to zero.
- //
- // For correctness (Number of IterationSummary objects, correct
- // final cost, and state update) these convergence tests need to
- // be performed at the end of the iteration.
- if (iteration_summary.step_is_successful) {
- // Gradient norm can only go down in successful steps.
- if (iteration_summary.gradient_max_norm <= options.gradient_tolerance) {
- summary->message = StringPrintf("Gradient tolerance reached. "
- "Gradient max norm: %e <= %e",
- iteration_summary.gradient_max_norm,
- options_.gradient_tolerance);
- summary->termination_type = CONVERGENCE;
- VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message;
- return;
- }
- } else {
- // Trust region radius can only go down if the step if
- // unsuccessful.
- if (iteration_summary.trust_region_radius <
- options_.min_trust_region_radius) {
- summary->message = "Termination. Minimum trust region radius reached.";
- summary->termination_type = CONVERGENCE;
- VLOG_IF(1, is_not_silent) << summary->message;
- return;
- }
- }
+ VLOG_IF(2, is_not_silent_)
+ << "Inner iteration succeeded; Current cost: " << x_cost_
+ << " Trust region step cost: " << candidate_cost_
+ << " Inner iteration cost: " << inner_iteration_cost;
+
+ candidate_x_ = inner_iteration_x_;
+
+ // Normally, the quality of a trust region step is measured by
+ // the ratio
+ //
+ // cost_change
+ // r = -----------------
+ // model_cost_change
+ //
+ // All the change in the nonlinear objective is due to the trust
+ // region step so this ratio is a good measure of the quality of
+ // the trust region radius. However, when inner iterations are
+ // being used, cost_change includes the contribution of the
+ // inner iterations and its not fair to credit it all to the
+ // trust region algorithm. So we change the ratio to be
+ //
+ // cost_change
+ // r = ------------------------------------------------
+ // (model_cost_change + inner_iteration_cost_change)
+ //
+ // Practically we do this by increasing model_cost_change by
+ // inner_iteration_cost_change.
+
+ const double inner_iteration_cost_change =
+ candidate_cost_ - inner_iteration_cost;
+ model_cost_change_ += inner_iteration_cost_change;
+ inner_iterations_were_useful_ = inner_iteration_cost < x_cost_;
+ const double inner_iteration_relative_progress =
+ 1.0 - inner_iteration_cost / candidate_cost_;
+
+ // Disable inner iterations once the relative improvement
+ // drops below tolerance.
+ inner_iterations_are_enabled_ =
+ (inner_iteration_relative_progress > options_.inner_iteration_tolerance);
+ VLOG_IF(2, is_not_silent_ && !inner_iterations_are_enabled_)
+ << "Disabling inner iterations. Progress : "
+ << inner_iteration_relative_progress;
+ candidate_cost_ = inner_iteration_cost;
+
+ solver_summary_->inner_iteration_time_in_seconds +=
+ WallTimeInSeconds() - inner_iteration_start_time;
+}
+
+// Perform a projected line search to improve the objective function
+// value along delta.
+//
+// TODO(sameeragarwal): The current implementation does not do
+// anything illegal but is incorrect and not terribly effective.
+//
+// https://github.com/ceres-solver/ceres-solver/issues/187
+void TrustRegionMinimizer::DoLineSearch(const Vector& x,
+ const Vector& gradient,
+ const double cost,
+ Vector* delta) {
+ LineSearchFunction line_search_function(evaluator_);
+
+ LineSearch::Options line_search_options;
+ line_search_options.is_silent = true;
+ line_search_options.interpolation_type =
+ options_.line_search_interpolation_type;
+ line_search_options.min_step_size = options_.min_line_search_step_size;
+ line_search_options.sufficient_decrease =
+ options_.line_search_sufficient_function_decrease;
+ line_search_options.max_step_contraction =
+ options_.max_line_search_step_contraction;
+ line_search_options.min_step_contraction =
+ options_.min_line_search_step_contraction;
+ line_search_options.max_num_iterations =
+ options_.max_num_line_search_step_size_iterations;
+ line_search_options.sufficient_curvature_decrease =
+ options_.line_search_sufficient_curvature_decrease;
+ line_search_options.max_step_expansion =
+ options_.max_line_search_step_expansion;
+ 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)));
+ LineSearch::Summary line_search_summary;
+ line_search_function.Init(x, *delta);
+ line_search->Search(1.0, cost, gradient.dot(*delta), &line_search_summary);
+
+ solver_summary_->num_line_search_steps += line_search_summary.num_iterations;
+ solver_summary_->line_search_cost_evaluation_time_in_seconds +=
+ line_search_summary.cost_evaluation_time_in_seconds;
+ solver_summary_->line_search_gradient_evaluation_time_in_seconds +=
+ line_search_summary.gradient_evaluation_time_in_seconds;
+ solver_summary_->line_search_polynomial_minimization_time_in_seconds +=
+ line_search_summary.polynomial_minimization_time_in_seconds;
+ solver_summary_->line_search_total_time_in_seconds +=
+ line_search_summary.total_time_in_seconds;
+
+ if (line_search_summary.success) {
+ *delta *= line_search_summary.optimal_step_size;
+ }
+}
+
+// Check if the maximum amount of time allowed by the user for the
+// solver has been exceeded, and if so return false after updating
+// Solver::Summary::message.
+bool TrustRegionMinimizer::MaxSolverTimeReached() {
+ const double total_solver_time =
+ WallTimeInSeconds() - start_time_in_secs_ +
+ solver_summary_->preprocessor_time_in_seconds;
+ if (total_solver_time < options_.max_solver_time_in_seconds) {
+ 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_->termination_type = NO_CONVERGENCE;
+ VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
+ return true;
+}
+
+// Check if the maximum number of iterations allowed by the user for
+// the solver has been exceeded, and if so return false after updating
+// Solver::Summary::message.
+bool TrustRegionMinimizer::MaxSolverIterationsReached() {
+ if (iteration_summary_.iteration < options_.max_num_iterations) {
+ return false;
+ }
+
+ 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;
+ return true;
+}
+
+// Check convergence based on the max norm of the gradient (only for
+// iterations where the step was declared successful).
+bool TrustRegionMinimizer::GradientToleranceReached() {
+ if (!iteration_summary_.step_is_successful ||
+ iteration_summary_.gradient_max_norm > options_.gradient_tolerance) {
+ return false;
+ }
+
+ solver_summary_->message = StringPrintf(
+ "Gradient tolerance reached. "
+ "Gradient max norm: %e <= %e",
+ iteration_summary_.gradient_max_norm,
+ options_.gradient_tolerance);
+ solver_summary_->termination_type = CONVERGENCE;
+ VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
+ return true;
+}
+
+// Check convergence based the size of the trust region radius.
+bool TrustRegionMinimizer::MinTrustRegionRadiusReached() {
+ if (iteration_summary_.trust_region_radius >
+ options_.min_trust_region_radius) {
+ 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_->termination_type = CONVERGENCE;
+ VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
+ return true;
+}
+
+// Solver::Options::parameter_tolerance based convergence check.
+bool TrustRegionMinimizer::ParameterToleranceReached() {
+ // Compute the norm of the step in the ambient space.
+ iteration_summary_.step_norm = (x_ - candidate_x_).norm();
+ const double step_size_tolerance =
+ options_.parameter_tolerance * (x_norm_ + options_.parameter_tolerance);
+
+ if (iteration_summary_.step_norm > step_size_tolerance) {
+ return false;
}
+
+ solver_summary_->message = StringPrintf(
+ "Parameter tolerance reached. "
+ "Relative step_norm: %e <= %e.",
+ (iteration_summary_.step_norm / (x_norm_ + options_.parameter_tolerance)),
+ options_.parameter_tolerance);
+ solver_summary_->termination_type = CONVERGENCE;
+ VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
+ return true;
+}
+
+// Solver::Options::function_tolerance based convergence check.
+bool TrustRegionMinimizer::FunctionToleranceReached() {
+ iteration_summary_.cost_change = x_cost_ - candidate_cost_;
+ const double absolute_function_tolerance =
+ options_.function_tolerance * x_cost_;
+
+ if (fabs(iteration_summary_.cost_change) > absolute_function_tolerance) {
+ return false;
+ }
+
+ solver_summary_->message = StringPrintf(
+ "Function tolerance reached. "
+ "|cost_change|/cost: %e <= %e",
+ fabs(iteration_summary_.cost_change) / x_cost_,
+ options_.function_tolerance);
+ solver_summary_->termination_type = CONVERGENCE;
+ VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message;
+ return true;
}
+// Compute candidate_x_ = Plus(x_, delta_)
+// Evaluate the cost of candidate_x_ as candidate_cost_.
+//
+// Failure to compute the step or the cost mean that candidate_cost_
+// is set to std::numeric_limits<double>::max(). Unlike
+// EvaluateGradientAndJacobian, failure in this function is not fatal
+// as we are only computing and evaluating a candidate point, and if
+// for some reason we are unable to evaluate it, we consider it to be
+// a point with very high cost. This allows the user to deal with edge
+// cases/constraints as part of the LocalParameterization and
+// CostFunction objects.
+void TrustRegionMinimizer::ComputeCandidatePointAndEvaluateCost() {
+ if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) {
+ LOG_IF(WARNING, is_not_silent_)
+ << "x_plus_delta = Plus(x, delta) failed. "
+ << "Treating it as a step with infinite cost";
+ candidate_cost_ = std::numeric_limits<double>::max();
+ return;
+ }
+
+ if (!evaluator_->Evaluate(
+ candidate_x_.data(), &candidate_cost_, NULL, NULL, NULL)) {
+ LOG_IF(WARNING, is_not_silent_)
+ << "Step failed to evaluate. "
+ << "Treating it as a step with infinite cost";
+ candidate_cost_ = std::numeric_limits<double>::max();
+ }
+}
+
+bool TrustRegionMinimizer::IsStepSuccessful() {
+ iteration_summary_.relative_decrease =
+ step_evaluator_->StepQuality(candidate_cost_, model_cost_change_);
+
+ // In most cases, boosting the model_cost_change by the
+ // improvement caused by the inner iterations is fine, but it can
+ // be the case that the original trust region step was so bad that
+ // the resulting improvement in the cost was negative, and the
+ // change caused by the inner iterations was large enough to
+ // improve the step, but also to make relative decrease quite
+ // small.
+ //
+ // This can cause the trust region loop to reject this step. To
+ // get around this, we expicitly 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.
+ //
+ // Notice that we do not just check that cost_change is positive
+ // which is a weaker condition and would render the
+ // min_relative_decrease threshold useless. Instead, we keep
+ // track of inner_iterations_were_useful, which is true only
+ // when inner iterations lead to a net decrease in the cost.
+ return (inner_iterations_were_useful_ ||
+ iteration_summary_.relative_decrease >
+ options_.min_relative_decrease);
+}
+
+// Declare the step successful, move to candidate_x, update the
+// derivatives and let the trust region strategy and the step
+// evaluator know that the step has been accepted.
+bool TrustRegionMinimizer::HandleSuccessfulStep() {
+ x_ = candidate_x_;
+ x_norm_ = x_.norm();
+
+ if (!EvaluateGradientAndJacobian()) {
+ return false;
+ }
+
+ iteration_summary_.step_is_successful = true;
+ strategy_->StepAccepted(iteration_summary_.relative_decrease);
+ step_evaluator_->StepAccepted(candidate_cost_, model_cost_change_);
+ return true;
+}
+
+// Declare the step unsuccessful and inform the trust region strategy.
+void TrustRegionMinimizer::HandleUnsuccessfulStep() {
+ iteration_summary_.step_is_successful = false;
+ strategy_->StepRejected(iteration_summary_.relative_decrease);
+ iteration_summary_.cost = candidate_cost_ + solver_summary_->fixed_cost;
+}
} // namespace internal
} // namespace ceres
diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.h b/extern/ceres/internal/ceres/trust_region_minimizer.h
index ed52c2642d1..43141da58a1 100644
--- a/extern/ceres/internal/ceres/trust_region_minimizer.h
+++ b/extern/ceres/internal/ceres/trust_region_minimizer.h
@@ -1,5 +1,5 @@
// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2016 Google Inc. All rights reserved.
// http://ceres-solver.org/
//
// Redistribution and use in source and binary forms, with or without
@@ -31,35 +31,136 @@
#ifndef CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
#define CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
+#include "ceres/internal/eigen.h"
+#include "ceres/internal/scoped_ptr.h"
#include "ceres/minimizer.h"
#include "ceres/solver.h"
+#include "ceres/sparse_matrix.h"
+#include "ceres/trust_region_step_evaluator.h"
+#include "ceres/trust_region_strategy.h"
#include "ceres/types.h"
namespace ceres {
namespace internal {
-// Generic trust region minimization algorithm. The heavy lifting is
-// done by a TrustRegionStrategy object passed in as part of options.
+// Generic trust region minimization algorithm.
//
// For example usage, see SolverImpl::Minimize.
class TrustRegionMinimizer : public Minimizer {
public:
- ~TrustRegionMinimizer() {}
+ ~TrustRegionMinimizer();
+
+ // This method is not thread safe.
virtual void Minimize(const Minimizer::Options& options,
double* parameters,
- Solver::Summary* summary);
+ Solver::Summary* solver_summary);
private:
- void Init(const Minimizer::Options& options);
- void EstimateScale(const SparseMatrix& jacobian, double* scale) const;
- bool MaybeDumpLinearLeastSquaresProblem(const int iteration,
- const SparseMatrix* jacobian,
- const double* residuals,
- const double* step) const;
+ void Init(const Minimizer::Options& options,
+ double* parameters,
+ Solver::Summary* solver_summary);
+ bool IterationZero();
+ bool FinalizeIterationAndCheckIfMinimizerCanContinue();
+ bool ComputeTrustRegionStep();
+
+ bool EvaluateGradientAndJacobian();
+ void ComputeCandidatePointAndEvaluateCost();
+
+ void DoLineSearch(const Vector& x,
+ const Vector& gradient,
+ const double cost,
+ Vector* delta);
+ void DoInnerIterationsIfNeeded();
+
+ bool ParameterToleranceReached();
+ bool FunctionToleranceReached();
+ bool GradientToleranceReached();
+ bool MaxSolverTimeReached();
+ bool MaxSolverIterationsReached();
+ bool MinTrustRegionRadiusReached();
+
+ bool IsStepSuccessful();
+ void HandleUnsuccessfulStep();
+ bool HandleSuccessfulStep();
+ bool HandleInvalidStep();
Minimizer::Options options_;
+
+ // These pointers are shortcuts to objects passed to the
+ // TrustRegionMinimizer. The TrustRegionMinimizer does not own them.
+ double* parameters_;
+ Solver::Summary* solver_summary_;
+ Evaluator* evaluator_;
+ SparseMatrix* jacobian_;
+ TrustRegionStrategy* strategy_;
+
+ scoped_ptr<TrustRegionStepEvaluator> step_evaluator_;
+
+ bool is_not_silent_;
+ bool inner_iterations_are_enabled_;
+ bool inner_iterations_were_useful_;
+
+ // Summary of the current iteration.
+ IterationSummary iteration_summary_;
+
+ // Dimensionality of the problem in the ambient space.
+ int num_parameters_;
+ // Dimensionality of the problem in the tangent space. This is the
+ // number of columns in the Jacobian.
+ int num_effective_parameters_;
+ // Length of the residual vector, also the number of rows in the Jacobian.
+ int num_residuals_;
+
+ // Current point.
+ Vector x_;
+ // Residuals at x_;
+ Vector residuals_;
+ // Gradient at x_.
+ Vector gradient_;
+ // Solution computed by the inner iterations.
+ Vector inner_iteration_x_;
+ // model_residuals = J * trust_region_step
+ Vector model_residuals_;
+ Vector negative_gradient_;
+ // projected_gradient_step = Plus(x, -gradient), an intermediate
+ // quantity used to compute the projected gradient norm.
+ Vector projected_gradient_step_;
+ // The step computed by the trust region strategy. If Jacobi scaling
+ // is enabled, this is a vector in the scaled space.
+ Vector trust_region_step_;
+ // The current proposal for how far the trust region algorithm
+ // thinks we should move. In the most basic case, it is just the
+ // trust_region_step_ with the Jacobi scaling undone. If bounds
+ // constraints are present, then it is the result of the projected
+ // line search.
+ Vector delta_;
+ // candidate_x = Plus(x, delta)
+ Vector candidate_x_;
+ // Scaling vector to scale the columns of the Jacobian.
+ Vector jacobian_scaling_;
+
+ // Euclidean norm of x_.
+ double x_norm_;
+ // Cost at x_.
+ double x_cost_;
+ // Minimum cost encountered up till now.
+ double minimum_cost_;
+ // How much did the trust region strategy reduce the cost of the
+ // linearized Gauss-Newton model.
+ double model_cost_change_;
+ // Cost at candidate_x_.
+ double candidate_cost_;
+
+ // Time at which the minimizer was started.
+ double start_time_in_secs_;
+ // Time at which the current iteration was started.
+ double iteration_start_time_in_secs_;
+ // Number of consecutive steps where the minimizer loop computed a
+ // numerically invalid step.
+ int num_consecutive_invalid_steps_;
};
} // namespace internal
} // namespace ceres
+
#endif // CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.cc b/extern/ceres/internal/ceres/trust_region_step_evaluator.cc
new file mode 100644
index 00000000000..c9167e623ef
--- /dev/null
+++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.cc
@@ -0,0 +1,107 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2016 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 <algorithm>
+#include "ceres/trust_region_step_evaluator.h"
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+TrustRegionStepEvaluator::TrustRegionStepEvaluator(
+ const double initial_cost,
+ const int max_consecutive_nonmonotonic_steps)
+ : max_consecutive_nonmonotonic_steps_(max_consecutive_nonmonotonic_steps),
+ minimum_cost_(initial_cost),
+ current_cost_(initial_cost),
+ reference_cost_(initial_cost),
+ candidate_cost_(initial_cost),
+ accumulated_reference_model_cost_change_(0.0),
+ accumulated_candidate_model_cost_change_(0.0),
+ num_consecutive_nonmonotonic_steps_(0){
+}
+
+double TrustRegionStepEvaluator::StepQuality(
+ const double cost,
+ const double model_cost_change) const {
+ const double relative_decrease = (current_cost_ - cost) / model_cost_change;
+ const double historical_relative_decrease =
+ (reference_cost_ - cost) /
+ (accumulated_reference_model_cost_change_ + model_cost_change);
+ return std::max(relative_decrease, historical_relative_decrease);
+}
+
+void TrustRegionStepEvaluator::StepAccepted(
+ const double cost,
+ const double model_cost_change) {
+ // Algorithm 10.1.2 from Trust Region Methods by Conn, Gould &
+ // Toint.
+ //
+ // Step 3a
+ current_cost_ = cost;
+ accumulated_candidate_model_cost_change_ += model_cost_change;
+ accumulated_reference_model_cost_change_ += model_cost_change;
+
+ // Step 3b.
+ if (current_cost_ < minimum_cost_) {
+ minimum_cost_ = current_cost_;
+ num_consecutive_nonmonotonic_steps_ = 0;
+ candidate_cost_ = current_cost_;
+ accumulated_candidate_model_cost_change_ = 0.0;
+ } else {
+ // Step 3c.
+ ++num_consecutive_nonmonotonic_steps_;
+ if (current_cost_ > candidate_cost_) {
+ candidate_cost_ = current_cost_;
+ accumulated_candidate_model_cost_change_ = 0.0;
+ }
+ }
+
+ // Step 3d.
+ //
+ // At this point we have made too many non-monotonic steps and
+ // we are going to reset the value of the reference iterate so
+ // as to force the algorithm to descend.
+ //
+ // Note: In the original algorithm by Toint, this step was only
+ // executed if the step was non-monotonic, but that would not handle
+ // the case of max_consecutive_nonmonotonic_steps = 0. The small
+ // modification of doing this always handles that corner case
+ // correctly.
+ if (num_consecutive_nonmonotonic_steps_ ==
+ max_consecutive_nonmonotonic_steps_) {
+ reference_cost_ = candidate_cost_;
+ accumulated_reference_model_cost_change_ =
+ accumulated_candidate_model_cost_change_;
+ }
+}
+
+} // namespace internal
+} // namespace ceres
diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.h b/extern/ceres/internal/ceres/trust_region_step_evaluator.h
new file mode 100644
index 00000000000..06df102a308
--- /dev/null
+++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.h
@@ -0,0 +1,122 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2016 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_TRUST_REGION_STEP_EVALUATOR_H_
+#define CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_
+
+namespace ceres {
+namespace internal {
+
+// The job of the TrustRegionStepEvaluator is to evaluate the quality
+// of a step, i.e., how the cost of a step compares with the reduction
+// in the objective of the trust region problem.
+//
+// Classic trust region methods are descent methods, in that they only
+// accept a point if it strictly reduces the value of the objective
+// function. They do this by measuring the quality of a step as
+//
+// cost_change / model_cost_change.
+//
+// Relaxing the monotonic descent requirement allows the algorithm to
+// be more efficient in the long term at the cost of some local
+// increase in the value of the objective function.
+//
+// This is because allowing for non-decreasing objective function
+// 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.
+//
+// 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.
+//
+// Based on algorithm 10.1.2 (page 357) of "Trust Region
+// Methods" by Conn Gould & Toint, or equations 33-40 of
+// "Non-monotone trust-region algorithms for nonlinear
+// optimization subject to convex constraints" by Phil Toint,
+// Mathematical Programming, 77, 1997.
+//
+// Example usage:
+//
+// TrustRegionStepEvaluator* step_evaluator = ...
+//
+// cost = ... // Compute the non-linear objective function value.
+// model_cost_change = ... // Change in the value of the trust region objective.
+// if (step_evaluator->StepQuality(cost, model_cost_change) > threshold) {
+// x = x + delta;
+// step_evaluator->StepAccepted(cost, model_cost_change);
+// }
+class TrustRegionStepEvaluator {
+ public:
+ // initial_cost is as the name implies the cost of the starting
+ // state of the trust region minimizer.
+ //
+ // 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.
+ TrustRegionStepEvaluator(double initial_cost,
+ int max_consecutive_nonmonotonic_steps);
+
+ // Return the quality of the step given its cost and the decrease in
+ // the cost of the model. model_cost_change has to be positive.
+ double StepQuality(double cost, double model_cost_change) const;
+
+ // Inform the step evaluator that a step with the given cost and
+ // model_cost_change has been accepted by the trust region
+ // minimizer.
+ void StepAccepted(double cost, double model_cost_change);
+
+ private:
+ const int max_consecutive_nonmonotonic_steps_;
+ // The minimum cost encountered up till now.
+ double minimum_cost_;
+ // The current cost of the trust region minimizer as informed by the
+ // last call to StepAccepted.
+ double current_cost_;
+ double reference_cost_;
+ double candidate_cost_;
+ // Accumulated model cost since the last time the reference model
+ // cost was updated, i.e., when a step with cost less than the
+ // current known minimum cost is accepted.
+ double accumulated_reference_model_cost_change_;
+ // Accumulated model cost since the last time the candidate model
+ // cost was updated, i.e., a non-monotonic step was taken with a
+ // cost that was greater than the current candidate cost.
+ double accumulated_candidate_model_cost_change_;
+ // Number of steps taken since the last time minimum_cost was updated.
+ int num_consecutive_nonmonotonic_steps_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_
diff --git a/extern/ceres/internal/ceres/trust_region_strategy.h b/extern/ceres/internal/ceres/trust_region_strategy.h
index 9560e67459a..36e8e981cc0 100644
--- a/extern/ceres/internal/ceres/trust_region_strategy.h
+++ b/extern/ceres/internal/ceres/trust_region_strategy.h
@@ -86,20 +86,20 @@ class TrustRegionStrategy {
struct PerSolveOptions {
PerSolveOptions()
: eta(0),
- dump_filename_base(""),
dump_format_type(TEXTFILE) {
}
// Forcing sequence for inexact solves.
double eta;
+ DumpFormatType dump_format_type;
+
// If non-empty and dump_format_type is not CONSOLE, the trust
// regions strategy will write the linear system to file(s) with
// name starting with dump_filename_base. If dump_format_type is
// CONSOLE then dump_filename_base will be ignored and the linear
// system will be written to the standard error.
std::string dump_filename_base;
- DumpFormatType dump_format_type;
};
struct Summary {
diff --git a/intern/atomic/atomic_ops.h b/intern/atomic/atomic_ops.h
index 0bc7905aa07..1107deddf94 100644
--- a/intern/atomic/atomic_ops.h
+++ b/intern/atomic/atomic_ops.h
@@ -77,32 +77,40 @@
/* Function prototypes. */
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
-ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x);
-ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x);
+ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x);
+ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x);
+ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x);
+ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x);
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new);
#endif
-ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x);
-ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x);
+ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x);
+ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x);
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new);
ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x);
+ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x);
+ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x);
ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b);
ATOMIC_INLINE uint8_t atomic_fetch_and_and_uint8(uint8_t *p, uint8_t b);
-ATOMIC_INLINE size_t atomic_add_z(size_t *p, size_t x);
-ATOMIC_INLINE size_t atomic_sub_z(size_t *p, size_t x);
+ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x);
+ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x);
+ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x);
+ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x);
ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new);
-ATOMIC_INLINE unsigned atomic_add_u(unsigned *p, unsigned x);
-ATOMIC_INLINE unsigned atomic_sub_u(unsigned *p, unsigned x);
+ATOMIC_INLINE unsigned atomic_add_and_fetch_u(unsigned *p, unsigned x);
+ATOMIC_INLINE unsigned atomic_sub_and_fetch_u(unsigned *p, unsigned x);
+ATOMIC_INLINE unsigned atomic_fetch_and_add_u(unsigned *p, unsigned x);
+ATOMIC_INLINE unsigned atomic_fetch_and_sub_u(unsigned *p, unsigned x);
ATOMIC_INLINE unsigned atomic_cas_u(unsigned *v, unsigned old, unsigned _new);
/* WARNING! Float 'atomics' are really faked ones, those are actually closer to some kind of spinlock-sync'ed operation,
* which means they are only efficient if collisions are highly unlikely (i.e. if probability of two threads
* working on the same pointer at the same time is very low). */
-ATOMIC_INLINE float atomic_add_fl(float *p, const float x);
+ATOMIC_INLINE float atomic_add_and_fetch_fl(float *p, const float x);
/******************************************************************************/
/* Include system-dependent implementations. */
diff --git a/intern/atomic/intern/atomic_ops_ext.h b/intern/atomic/intern/atomic_ops_ext.h
index 4065299d2ea..8421aa72192 100644
--- a/intern/atomic/intern/atomic_ops_ext.h
+++ b/intern/atomic/intern/atomic_ops_ext.h
@@ -56,25 +56,47 @@
/******************************************************************************/
/* size_t operations. */
-ATOMIC_INLINE size_t atomic_add_z(size_t *p, size_t x)
+ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x)
{
assert(sizeof(size_t) == LG_SIZEOF_PTR);
#if (LG_SIZEOF_PTR == 8)
- return (size_t)atomic_add_uint64((uint64_t *)p, (uint64_t)x);
+ return (size_t)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)x);
#elif (LG_SIZEOF_PTR == 4)
- return (size_t)atomic_add_uint32((uint32_t *)p, (uint32_t)x);
+ return (size_t)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)x);
#endif
}
-ATOMIC_INLINE size_t atomic_sub_z(size_t *p, size_t x)
+ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x)
{
assert(sizeof(size_t) == LG_SIZEOF_PTR);
#if (LG_SIZEOF_PTR == 8)
- return (size_t)atomic_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
+ return (size_t)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
#elif (LG_SIZEOF_PTR == 4)
- return (size_t)atomic_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
+ return (size_t)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
+#endif
+}
+
+ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x)
+{
+ assert(sizeof(size_t) == LG_SIZEOF_PTR);
+
+#if (LG_SIZEOF_PTR == 8)
+ return (size_t)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)x);
+#elif (LG_SIZEOF_PTR == 4)
+ return (size_t)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)x);
+#endif
+}
+
+ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x)
+{
+ assert(sizeof(size_t) == LG_SIZEOF_PTR);
+
+#if (LG_SIZEOF_PTR == 8)
+ return (size_t)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
+#elif (LG_SIZEOF_PTR == 4)
+ return (size_t)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
#endif
}
@@ -91,25 +113,47 @@ ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new)
/******************************************************************************/
/* unsigned operations. */
-ATOMIC_INLINE unsigned atomic_add_u(unsigned *p, unsigned x)
+ATOMIC_INLINE unsigned atomic_add_and_fetch_u(unsigned *p, unsigned x)
+{
+ assert(sizeof(unsigned) == LG_SIZEOF_INT);
+
+#if (LG_SIZEOF_INT == 8)
+ return (unsigned)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)x);
+#elif (LG_SIZEOF_INT == 4)
+ return (unsigned)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)x);
+#endif
+}
+
+ATOMIC_INLINE unsigned atomic_sub_and_fetch_u(unsigned *p, unsigned x)
+{
+ assert(sizeof(unsigned) == LG_SIZEOF_INT);
+
+#if (LG_SIZEOF_INT == 8)
+ return (unsigned)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
+#elif (LG_SIZEOF_INT == 4)
+ return (unsigned)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
+#endif
+}
+
+ATOMIC_INLINE unsigned atomic_fetch_and_add_u(unsigned *p, unsigned x)
{
assert(sizeof(unsigned) == LG_SIZEOF_INT);
#if (LG_SIZEOF_INT == 8)
- return (unsigned)atomic_add_uint64((uint64_t *)p, (uint64_t)x);
+ return (unsigned)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)x);
#elif (LG_SIZEOF_INT == 4)
- return (unsigned)atomic_add_uint32((uint32_t *)p, (uint32_t)x);
+ return (unsigned)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)x);
#endif
}
-ATOMIC_INLINE unsigned atomic_sub_u(unsigned *p, unsigned x)
+ATOMIC_INLINE unsigned atomic_fetch_and_sub_u(unsigned *p, unsigned x)
{
assert(sizeof(unsigned) == LG_SIZEOF_INT);
#if (LG_SIZEOF_INT == 8)
- return (unsigned)atomic_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
+ return (unsigned)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
#elif (LG_SIZEOF_INT == 4)
- return (unsigned)atomic_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
+ return (unsigned)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
#endif
}
@@ -127,7 +171,7 @@ ATOMIC_INLINE unsigned atomic_cas_u(unsigned *v, unsigned old, unsigned _new)
/******************************************************************************/
/* float operations. */
-ATOMIC_INLINE float atomic_add_fl(float *p, const float x)
+ATOMIC_INLINE float atomic_add_and_fetch_fl(float *p, const float x)
{
assert(sizeof(float) == sizeof(uint32_t));
diff --git a/intern/atomic/intern/atomic_ops_msvc.h b/intern/atomic/intern/atomic_ops_msvc.h
index 15ddda246d9..034ac1e3e53 100644
--- a/intern/atomic/intern/atomic_ops_msvc.h
+++ b/intern/atomic/intern/atomic_ops_msvc.h
@@ -43,12 +43,12 @@
/******************************************************************************/
/* 64-bit operations. */
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
-ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
+ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
{
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x;
}
-ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
+ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
{
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x;
}
@@ -57,16 +57,26 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne
{
return InterlockedCompareExchange64((int64_t *)v, _new, old);
}
+
+ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
+{
+ return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x);
+}
+
+ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
+{
+ return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
+}
#endif
/******************************************************************************/
/* 32-bit operations. */
-ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
+ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
{
return InterlockedExchangeAdd(p, x) + x;
}
-ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x)
+ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
{
return InterlockedExchangeAdd(p, -((int32_t)x)) - x;
}
@@ -81,6 +91,16 @@ ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
return InterlockedExchangeAdd(p, x);
}
+ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
+{
+ return InterlockedOr((long *)p, x);
+}
+
+ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
+{
+ return InterlockedAnd((long *)p, x);
+}
+
/******************************************************************************/
/* 8-bit operations. */
diff --git a/intern/atomic/intern/atomic_ops_unix.h b/intern/atomic/intern/atomic_ops_unix.h
index 55c00024244..0a3322ad2b1 100644
--- a/intern/atomic/intern/atomic_ops_unix.h
+++ b/intern/atomic/intern/atomic_ops_unix.h
@@ -58,22 +58,32 @@
/* 64-bit operations. */
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
# if (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8))
-ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
+ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
{
return __sync_add_and_fetch(p, x);
}
-ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
+ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
{
return __sync_sub_and_fetch(p, x);
}
+ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
+{
+ return __sync_fetch_and_add(p, x);
+}
+
+ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
+{
+ return __sync_fetch_and_sub(p, x);
+}
+
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
{
return __sync_val_compare_and_swap(v, old, _new);
}
# elif (defined(__amd64__) || defined(__x86_64__))
-ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
+ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
{
asm volatile (
"lock; xaddq %0, %1;"
@@ -83,7 +93,7 @@ ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
return x;
}
-ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
+ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
{
x = (uint64_t)(-(int64_t)x);
asm volatile (
@@ -94,6 +104,16 @@ ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
return x;
}
+ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
+{
+ return atomic_fetch_and_add_uint64(p, x) + x;
+}
+
+ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
+{
+ return atomic_fetch_and_sub_uint64(p, x) - x;
+}
+
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
{
uint64_t ret;
@@ -112,12 +132,12 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne
/******************************************************************************/
/* 32-bit operations. */
#if (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))
-ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
+ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
{
return __sync_add_and_fetch(p, x);
}
-ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x)
+ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
{
return __sync_sub_and_fetch(p, x);
}
@@ -127,7 +147,7 @@ ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _ne
return __sync_val_compare_and_swap(v, old, _new);
}
#elif (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
-ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
+ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
{
uint32_t ret = x;
asm volatile (
@@ -138,7 +158,7 @@ ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
return ret+x;
}
-ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x)
+ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
{
ret = (uint32_t)(-(int32_t)x);
asm volatile (
@@ -169,6 +189,16 @@ ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
return __sync_fetch_and_add(p, x);
}
+ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
+{
+ return __sync_fetch_and_or(p, x);
+}
+
+ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
+{
+ return __sync_fetch_and_and(p, x);
+}
+
#else
# error "Missing implementation for 32-bit atomic operations"
#endif
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index e8168bc15ff..b21e8630cdb 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -337,7 +337,7 @@ static void options_parse(int argc, const char **argv)
/* device names */
string device_names = "";
- string devicename = "cpu";
+ string devicename = "CPU";
bool list = false;
vector<DeviceType>& types = Device::available_types();
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index f118815099a..a0c81806350 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -210,17 +210,6 @@ static void xml_read_camera(XMLReadState& state, pugi::xml_node node)
/* Shader */
-static string xml_socket_name(const char *name)
-{
- string sname = name;
- size_t i;
-
- while((i = sname.find(" ")) != string::npos)
- sname.replace(i, 1, "");
-
- return sname;
-}
-
static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml_node graph_node)
{
xml_read_node(state, shader, graph_node);
@@ -254,7 +243,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
ShaderNode *fromnode = (ShaderNode*)graph_reader.node_map[from_node_name];
foreach(ShaderOutput *out, fromnode->outputs)
- if(string_iequals(xml_socket_name(out->name().c_str()), from_socket_name.c_str()))
+ if(string_iequals(out->socket_type.name.string(), from_socket_name.string()))
output = out;
if(!output)
@@ -267,7 +256,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
ShaderNode *tonode = (ShaderNode*)graph_reader.node_map[to_node_name];
foreach(ShaderInput *in, tonode->inputs)
- if(string_iequals(xml_socket_name(in->name().c_str()), to_socket_name.c_str()))
+ if(string_iequals(in->socket_type.name.string(), to_socket_name.string()))
input = in;
if(!input)
@@ -405,7 +394,7 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
int shader = 0;
bool smooth = state.smooth;
- /* read vertices and polygons, RIB style */
+ /* read vertices and polygons */
vector<float3> P;
vector<float> UV;
vector<int> verts, nverts;
@@ -531,8 +520,12 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
sdparams.objecttoworld = state.tfm;
}
- /* temporary for test compatibility */
- mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
+ /* we don't yet support arbitrary attributes, for now add vertex
+ * coordinates as generated coordinates if requested */
+ if (mesh->need_attribute(state.scene, ATTR_STD_GENERATED)) {
+ Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
+ memcpy(attr->data_float3(), mesh->verts.data(), sizeof(float3)*mesh->verts.size());
+ }
}
/* Light */
diff --git a/intern/cycles/blender/CCL_api.h b/intern/cycles/blender/CCL_api.h
index d3a68c4db4f..233ffc8802c 100644
--- a/intern/cycles/blender/CCL_api.h
+++ b/intern/cycles/blender/CCL_api.h
@@ -21,17 +21,6 @@
extern "C" {
#endif
-/* returns a list of devices for selection, array is empty identifier
- * terminated and must not be freed */
-
-typedef struct CCLDeviceInfo {
- char identifier[128];
- char name[512];
- int value;
-} CCLDeviceInfo;
-
-CCLDeviceInfo *CCL_compute_device_list(int device_type);
-
/* create python module _cycles used by addon */
void *CCL_python_module_init(void);
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 977d7f75bb7..27c9b922042 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -21,7 +21,8 @@ from bpy.props import (BoolProperty,
EnumProperty,
FloatProperty,
IntProperty,
- PointerProperty)
+ PointerProperty,
+ StringProperty)
# enums
@@ -122,6 +123,12 @@ enum_volume_interpolation = (
('CUBIC', "Cubic", "Smoothed high quality interpolation, but slower")
)
+enum_device_type = (
+ ('CPU', "CPU", "CPU", 0),
+ ('CUDA', "CUDA", "CUDA", 1),
+ ('OPENCL', "OpenCL", "OpenCL", 2)
+ )
+
class CyclesRenderSettings(bpy.types.PropertyGroup):
@classmethod
@@ -266,6 +273,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
description="Sample all lights (for indirect samples), rather than randomly picking one",
default=True,
)
+ cls.light_sampling_threshold = FloatProperty(
+ name="Light Sampling Threshold",
+ description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). "
+ "Zero disables the test and never ignores lights",
+ min=0.0, max=1.0,
+ default=0.05,
+ )
cls.caustics_reflective = BoolProperty(
name="Reflective Caustics",
@@ -1123,6 +1137,103 @@ class CyclesCurveSettings(bpy.types.PropertyGroup):
del bpy.types.ParticleSettings.cycles
+class CyclesDeviceSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ cls.id = StringProperty(name="ID")
+ cls.name = StringProperty(name="Name")
+ cls.use = BoolProperty(name="Use", default=True)
+ cls.type = EnumProperty(name="Type", items=enum_device_type, default='CUDA')
+
+
+class CyclesPreferences(bpy.types.AddonPreferences):
+ bl_idname = __package__
+
+ def get_device_types(self, context):
+ import _cycles
+ has_cuda, has_opencl = _cycles.get_device_types()
+ list = [('NONE', "None", "Don't use compute device", 0)]
+ if has_cuda:
+ list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1))
+ if has_opencl:
+ list.append(('OPENCL', "OpenCL", "Use OpenCL for GPU acceleration", 2))
+ return list
+
+ compute_device_type = EnumProperty(
+ name="Compute Device Type",
+ description="Device to use for computation (rendering with Cycles)",
+ items=get_device_types,
+ )
+
+ devices = bpy.props.CollectionProperty(type=CyclesDeviceSettings)
+
+ def get_devices(self):
+ import _cycles
+ # Layout of the device tuples: (Name, Type, Internal ID, Persistent ID)
+ device_list = _cycles.available_devices()
+
+ cuda_devices = []
+ opencl_devices = []
+ for device in device_list:
+ if not device[1] in {'CUDA', 'OPENCL'}:
+ continue
+
+ entry = None
+ # Try to find existing Device entry
+ for dev in self.devices:
+ if dev.id == device[2] and dev.type == device[1]:
+ entry = dev
+ break
+ # Create new entry if no existing one was found
+ if not entry:
+ entry = self.devices.add()
+ entry.id = device[2]
+ entry.name = device[0]
+ entry.type = device[1]
+
+ # Sort entries into lists
+ if entry.type == 'CUDA':
+ cuda_devices.append(entry)
+ elif entry.type == 'OPENCL':
+ opencl_devices.append(entry)
+ return cuda_devices, opencl_devices
+
+
+ def has_active_device(self):
+ import _cycles
+ device_list = _cycles.available_devices()
+ for device in device_list:
+ if device[1] != self.compute_device_type:
+ continue
+ if any(dev.use and dev.id == device[2] for dev in self.devices):
+ return True
+ return False
+
+
+ def draw_impl(self, layout, context):
+ layout.label(text="Compute Device:")
+ layout.row().prop(self, "compute_device_type", expand=True)
+
+ cuda_devices, opencl_devices = self.get_devices()
+ row = layout.row()
+
+ if cuda_devices:
+ col = row.column(align=True)
+ col.label(text="CUDA devices:")
+ for device in cuda_devices:
+ col.prop(device, "use", text=device.name, toggle=True)
+
+ if opencl_devices:
+ col = row.column(align=True)
+ col.label(text="OpenCL devices:")
+ for device in opencl_devices:
+ col.prop(device, "use", text=device.name, toggle=True)
+
+
+ def draw(self, context):
+ self.draw_impl(self.layout, context)
+
+
def register():
bpy.utils.register_class(CyclesRenderSettings)
bpy.utils.register_class(CyclesCameraSettings)
@@ -1134,6 +1245,8 @@ def register():
bpy.utils.register_class(CyclesObjectSettings)
bpy.utils.register_class(CyclesCurveRenderSettings)
bpy.utils.register_class(CyclesCurveSettings)
+ bpy.utils.register_class(CyclesDeviceSettings)
+ bpy.utils.register_class(CyclesPreferences)
def unregister():
@@ -1147,3 +1260,5 @@ def unregister():
bpy.utils.unregister_class(CyclesVisibilitySettings)
bpy.utils.unregister_class(CyclesCurveRenderSettings)
bpy.utils.unregister_class(CyclesCurveSettings)
+ bpy.utils.unregister_class(CyclesDeviceSettings)
+ bpy.utils.unregister_class(CyclesPreferences)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 52872d2b83f..f28fa0d52ba 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -53,25 +53,26 @@ class CyclesButtonsPanel:
return rd.engine in cls.COMPAT_ENGINES
+def get_device_type(context):
+ return context.user_preferences.addons[__package__].preferences.compute_device_type
+
+
def use_cpu(context):
cscene = context.scene.cycles
- device_type = context.user_preferences.system.compute_device_type
- return (device_type == 'NONE' or cscene.device == 'CPU')
+ return (get_device_type(context) == 'NONE' or cscene.device == 'CPU')
def use_opencl(context):
cscene = context.scene.cycles
- device_type = context.user_preferences.system.compute_device_type
- return (device_type == 'OPENCL' and cscene.device == 'GPU')
+ return (get_device_type(context) == 'OPENCL' and cscene.device == 'GPU')
def use_cuda(context):
cscene = context.scene.cycles
- device_type = context.user_preferences.system.compute_device_type
- return (device_type == 'CUDA' and cscene.device == 'GPU')
+ return (get_device_type(context) == 'CUDA' and cscene.device == 'GPU')
def use_branched_path(context):
@@ -85,6 +86,14 @@ def use_sample_all_lights(context):
return cscene.sample_all_lights_direct or cscene.sample_all_lights_indirect
+def show_device_selection(context):
+ type = get_device_type(context)
+ if type == 'NETWORK':
+ return True
+ if not type in {'CUDA', 'OPENCL'}:
+ return False
+ return context.user_preferences.addons[__package__].preferences.has_active_device()
+
def draw_samples_info(layout, context):
cscene = context.scene.cycles
@@ -141,7 +150,6 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
scene = context.scene
cscene = scene.cycles
- device_type = context.user_preferences.system.compute_device_type
row = layout.row(align=True)
row.menu("CYCLES_MT_sampling_presets", text=bpy.types.CYCLES_MT_sampling_presets.bl_label)
@@ -150,7 +158,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
row = layout.row()
sub = row.row()
- sub.active = device_type != 'OPENCL' or use_cpu(context)
+ sub.active = get_device_type(context) != 'OPENCL' or use_cpu(context)
sub.prop(cscene, "progressive", text="")
row.prop(cscene, "use_square_samples")
@@ -166,6 +174,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
sub.prop(cscene, "sample_clamp_direct")
sub.prop(cscene, "sample_clamp_indirect")
+ sub.prop(cscene, "light_sampling_threshold")
if cscene.progressive == 'PATH' or use_branched_path(context) is False:
col = split.column()
@@ -1605,9 +1614,13 @@ def draw_device(self, context):
layout.prop(cscene, "feature_set")
- device_type = context.user_preferences.system.compute_device_type
- if device_type in {'CUDA', 'OPENCL', 'NETWORK'}:
- layout.prop(cscene, "device")
+ split = layout.split(percentage=1/3)
+ split.label("Device:")
+ row = split.row(align=True)
+ sub = row.split(align=True)
+ sub.active = show_device_selection(context)
+ sub.prop(cscene, "device", text="")
+ row.operator("wm.addon_userpref_show", text="Preferences", icon='PREFERENCES').module = __package__
if engine.with_osl() and use_cpu(context):
layout.prop(cscene, "shading_system")
diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py
index 830723d6149..951afd37a92 100644
--- a/intern/cycles/blender/addon/version_update.py
+++ b/intern/cycles/blender/addon/version_update.py
@@ -278,3 +278,9 @@ def do_versions(self):
cscene.pixel_filter_type = cscene.filter_type
if cscene.filter_type == 'BLACKMAN_HARRIS':
cscene.filter_type = 'GAUSSIAN'
+
+ if bpy.data.version <= (2, 78, 2):
+ for scene in bpy.data.scenes:
+ cscene = scene.cycles
+ if not cscene.is_property_set("light_sampling_threshold"):
+ cscene.light_sampling_threshold = 0.0
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index a50f5edb1df..438abc49f88 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -40,10 +40,6 @@ CCL_NAMESPACE_BEGIN
namespace {
-/* Device list stored static (used by compute_device_list()). */
-static ccl::vector<CCLDeviceInfo> device_list;
-static ccl::DeviceType device_type = DEVICE_NONE;
-
/* Flag describing whether debug flags were synchronized from scene. */
bool debug_flags_set = false;
@@ -195,7 +191,6 @@ static PyObject *exit_func(PyObject * /*self*/, PyObject * /*args*/)
ShaderManager::free_memory();
TaskScheduler::free_memory();
Device::free_memory();
- device_list.free_memory();
Py_RETURN_NONE;
}
@@ -389,7 +384,12 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject * /*args*/
for(size_t i = 0; i < devices.size(); i++) {
DeviceInfo& device = devices[i];
- PyTuple_SET_ITEM(ret, i, PyUnicode_FromString(device.description.c_str()));
+ string type_name = Device::string_from_type(device.type);
+ PyObject *device_tuple = PyTuple_New(3);
+ PyTuple_SET_ITEM(device_tuple, 0, PyUnicode_FromString(device.description.c_str()));
+ PyTuple_SET_ITEM(device_tuple, 1, PyUnicode_FromString(type_name.c_str()));
+ PyTuple_SET_ITEM(device_tuple, 2, PyUnicode_FromString(device.id.c_str()));
+ PyTuple_SET_ITEM(ret, i, device_tuple);
}
return ret;
@@ -676,6 +676,20 @@ static PyObject *set_resumable_chunks_func(PyObject * /*self*/, PyObject *args)
Py_RETURN_NONE;
}
+static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
+{
+ vector<DeviceInfo>& devices = Device::available_devices();
+ bool has_cuda = false, has_opencl = false;
+ for(int i = 0; i < devices.size(); i++) {
+ has_cuda |= (devices[i].type == DEVICE_CUDA);
+ has_opencl |= (devices[i].type == DEVICE_OPENCL);
+ }
+ PyObject *list = PyTuple_New(2);
+ PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda));
+ PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_opencl));
+ return list;
+}
+
static PyMethodDef methods[] = {
{"init", init_func, METH_VARARGS, ""},
{"exit", exit_func, METH_VARARGS, ""},
@@ -703,6 +717,9 @@ static PyMethodDef methods[] = {
/* Resumable render */
{"set_resumable_chunks", set_resumable_chunks_func, METH_VARARGS, ""},
+ /* Compute Device selection */
+ {"get_device_types", get_device_types_func, METH_VARARGS, ""},
+
{NULL, NULL, 0, NULL},
};
@@ -715,47 +732,6 @@ static struct PyModuleDef module = {
NULL, NULL, NULL, NULL
};
-static CCLDeviceInfo *compute_device_list(DeviceType type)
-{
- /* create device list if it's not already done */
- if(type != device_type) {
- ccl::vector<DeviceInfo>& devices = ccl::Device::available_devices();
-
- device_type = type;
- device_list.clear();
-
- /* add devices */
- int i = 0;
-
- foreach(DeviceInfo& info, devices) {
- if(info.type == type ||
- (info.type == DEVICE_MULTI && info.multi_devices[0].type == type))
- {
- CCLDeviceInfo cinfo;
-
- strncpy(cinfo.identifier, info.id.c_str(), sizeof(cinfo.identifier));
- cinfo.identifier[info.id.length()] = '\0';
-
- strncpy(cinfo.name, info.description.c_str(), sizeof(cinfo.name));
- cinfo.name[info.description.length()] = '\0';
-
- cinfo.value = i++;
-
- device_list.push_back(cinfo);
- }
- }
-
- /* null terminate */
- if(!device_list.empty()) {
- CCLDeviceInfo cinfo = {"", "", 0};
- device_list.push_back(cinfo);
- }
- }
-
- return (device_list.empty())? NULL: &device_list[0];
-}
-
-
CCL_NAMESPACE_END
void *CCL_python_module_init()
@@ -794,24 +770,3 @@ void *CCL_python_module_init()
return (void*)mod;
}
-
-CCLDeviceInfo *CCL_compute_device_list(int device_type)
-{
- ccl::DeviceType type;
- switch(device_type) {
- case 0:
- type = ccl::DEVICE_CUDA;
- break;
- case 1:
- type = ccl::DEVICE_OPENCL;
- break;
- case 2:
- type = ccl::DEVICE_NETWORK;
- break;
- default:
- type = ccl::DEVICE_NONE;
- break;
- }
- return ccl::compute_device_list(type);
-}
-
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index f99a4889d34..ed5c681c952 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -261,8 +261,17 @@ void BlenderSync::sync_integrator()
integrator->filter_glossy = get_float(cscene, "blur_glossy");
integrator->seed = get_int(cscene, "seed");
- if(get_boolean(cscene, "use_animated_seed"))
- integrator->seed = hash_int_2d(b_scene.frame_current(), get_int(cscene, "seed"));
+ if(get_boolean(cscene, "use_animated_seed")) {
+ integrator->seed = hash_int_2d(b_scene.frame_current(),
+ get_int(cscene, "seed"));
+ if(b_scene.frame_subframe() != 0.0f) {
+ /* TODO(sergey): Ideally should be some sort of hash_merge,
+ * but this is good enough for now.
+ */
+ integrator->seed += hash_int_2d((int)(b_scene.frame_subframe() * (float)INT_MAX),
+ get_int(cscene, "seed"));
+ }
+ }
integrator->sampling_pattern = (SamplingPattern)get_enum(
cscene,
@@ -290,6 +299,7 @@ void BlenderSync::sync_integrator()
integrator->sample_all_lights_direct = get_boolean(cscene, "sample_all_lights_direct");
integrator->sample_all_lights_indirect = get_boolean(cscene, "sample_all_lights_indirect");
+ integrator->light_sampling_threshold = get_float(cscene, "light_sampling_threshold");
int diffuse_samples = get_int(cscene, "diffuse_samples");
int glossy_samples = get_int(cscene, "glossy_samples");
@@ -536,7 +546,12 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine& b_engine,
vector<DeviceInfo>& devices = Device::available_devices();
/* device default CPU */
- params.device = devices[0];
+ foreach(DeviceInfo& device, devices) {
+ if(device.type == DEVICE_CPU) {
+ params.device = device;
+ break;
+ }
+ }
if(get_enum(cscene, "device") == 2) {
/* find network device */
@@ -545,17 +560,39 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine& b_engine,
params.device = info;
}
else if(get_enum(cscene, "device") == 1) {
- /* find GPU device with given id */
- PointerRNA systemptr = b_userpref.system().ptr;
- PropertyRNA *deviceprop = RNA_struct_find_property(&systemptr, "compute_device");
- int device_id = b_userpref.system().compute_device();
+ PointerRNA b_preferences;
- const char *id;
+ BL::UserPreferences::addons_iterator b_addon_iter;
+ for(b_userpref.addons.begin(b_addon_iter); b_addon_iter != b_userpref.addons.end(); ++b_addon_iter) {
+ if(b_addon_iter->module() == "cycles") {
+ b_preferences = b_addon_iter->preferences().ptr;
+ break;
+ }
+ }
+
+ int compute_device = get_enum(b_preferences, "compute_device_type");
+
+ if(compute_device != 0) {
+ vector<DeviceInfo> used_devices;
+ RNA_BEGIN(&b_preferences, device, "devices") {
+ if(get_enum(device, "type") == compute_device && get_boolean(device, "use")) {
+ string id = get_string(device, "id");
+ foreach(DeviceInfo& info, devices) {
+ if(info.id == id) {
+ used_devices.push_back(info);
+ break;
+ }
+ }
+ }
+ } RNA_END
- if(RNA_property_enum_identifier(NULL, &systemptr, deviceprop, device_id, &id)) {
- foreach(DeviceInfo& info, devices)
- if(info.id == id)
- params.device = info;
+ if(used_devices.size() == 1) {
+ params.device = used_devices[0];
+ }
+ else if(used_devices.size() > 1) {
+ params.device = Device::get_multi_device(used_devices);
+ }
+ /* Else keep using the CPU device that was set before. */
}
}
diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index 616dd940801..403a0540963 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -44,6 +44,10 @@ if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD)
else()
message(STATUS "CUDA compiler not found, disabling WITH_CYCLES_CUDA_BINARIES")
set(WITH_CYCLES_CUDA_BINARIES OFF)
+ if(NOT WITH_CUDA_DYNLOAD)
+ message(STATUS "Additionally falling back to dynamic CUDA load")
+ set(WITH_CUDA_DYNLOAD ON)
+ endif()
endif()
endif()
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index 909ec7a6d60..ff9387b0a8a 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -258,33 +258,33 @@ Device *Device::create(DeviceInfo& info, Stats &stats, bool background)
DeviceType Device::type_from_string(const char *name)
{
- if(strcmp(name, "cpu") == 0)
+ if(strcmp(name, "CPU") == 0)
return DEVICE_CPU;
- else if(strcmp(name, "cuda") == 0)
+ else if(strcmp(name, "CUDA") == 0)
return DEVICE_CUDA;
- else if(strcmp(name, "opencl") == 0)
+ else if(strcmp(name, "OPENCL") == 0)
return DEVICE_OPENCL;
- else if(strcmp(name, "network") == 0)
+ else if(strcmp(name, "NETWORK") == 0)
return DEVICE_NETWORK;
- else if(strcmp(name, "multi") == 0)
+ else if(strcmp(name, "MULTI") == 0)
return DEVICE_MULTI;
-
+
return DEVICE_NONE;
}
string Device::string_from_type(DeviceType type)
{
if(type == DEVICE_CPU)
- return "cpu";
+ return "CPU";
else if(type == DEVICE_CUDA)
- return "cuda";
+ return "CUDA";
else if(type == DEVICE_OPENCL)
- return "opencl";
+ return "OPENCL";
else if(type == DEVICE_NETWORK)
- return "network";
+ return "NETWORK";
else if(type == DEVICE_MULTI)
- return "multi";
-
+ return "MULTI";
+
return "";
}
@@ -307,9 +307,6 @@ vector<DeviceType>& Device::available_types()
#ifdef WITH_NETWORK
types.push_back(DEVICE_NETWORK);
#endif
-#ifdef WITH_MULTI
- types.push_back(DEVICE_MULTI);
-#endif
need_types_update = false;
}
@@ -331,10 +328,6 @@ vector<DeviceInfo>& Device::available_devices()
device_opencl_info(devices);
#endif
-#ifdef WITH_MULTI
- device_multi_info(devices);
-#endif
-
device_cpu_info(devices);
#ifdef WITH_NETWORK
@@ -368,6 +361,29 @@ string Device::device_capabilities()
return capabilities;
}
+DeviceInfo Device::get_multi_device(vector<DeviceInfo> subdevices)
+{
+ assert(subdevices.size() > 1);
+
+ DeviceInfo info;
+ info.type = DEVICE_MULTI;
+ info.id = "MULTI";
+ info.description = "Multi Device";
+ info.multi_devices = subdevices;
+ info.num = 0;
+
+ info.has_bindless_textures = true;
+ info.pack_images = false;
+ foreach(DeviceInfo &device, subdevices) {
+ assert(device.type == info.multi_devices[0].type);
+
+ info.pack_images |= device.pack_images;
+ info.has_bindless_textures &= device.has_bindless_textures;
+ }
+
+ return info;
+}
+
void Device::tag_update()
{
need_types_update = true;
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index d990218a931..467b8dca354 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -51,7 +51,7 @@ class DeviceInfo {
public:
DeviceType type;
string description;
- string id;
+ string id; /* used for user preferences, should stay fixed with changing hardware config */
int num;
bool display_device;
bool advanced_shading;
@@ -71,6 +71,12 @@ public:
has_bindless_textures = false;
use_split_kernel = false;
}
+
+ bool operator==(const DeviceInfo &info) {
+ /* Multiple Devices with the same ID would be very bad. */
+ assert(id != info.id || (type == info.type && num == info.num && description == info.description));
+ return id == info.id;
+ }
};
class DeviceRequestedFeatures {
@@ -287,6 +293,7 @@ public:
static vector<DeviceType>& available_types();
static vector<DeviceInfo>& available_devices();
static string device_capabilities();
+ static DeviceInfo get_multi_device(vector<DeviceInfo> subdevices);
/* Tag devices lists for update. */
static void tag_update();
diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp
index 7f8a0bf2f43..a4818aa3b8d 100644
--- a/intern/cycles/device/device_cuda.cpp
+++ b/intern/cycles/device/device_cuda.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <climits>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -213,7 +214,8 @@ public:
return;
int major, minor;
- cuDeviceComputeCapability(&major, &minor, cuDevId);
+ cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId);
+ cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId);
cuDevArchitecture = major*100 + minor*10;
cuda_pop_context();
@@ -233,7 +235,8 @@ public:
bool support_device(const DeviceRequestedFeatures& /*requested_features*/)
{
int major, minor;
- cuDeviceComputeCapability(&major, &minor, cuDevId);
+ cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId);
+ cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId);
/* We only support sm_20 and above */
if(major < 2) {
@@ -315,7 +318,8 @@ public:
{
/* Compute cubin name. */
int major, minor;
- cuDeviceComputeCapability(&major, &minor, cuDevId);
+ cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId);
+ cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId);
/* Attempt to use kernel provided with Blender. */
if(!use_adaptive_compilation()) {
@@ -1394,8 +1398,8 @@ void device_cuda_info(vector<DeviceInfo>& devices)
if(cuDeviceGetName(name, 256, num) != CUDA_SUCCESS)
continue;
- int major, minor;
- cuDeviceComputeCapability(&major, &minor, num);
+ int major;
+ cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, num);
if(major < 2) {
continue;
}
@@ -1404,13 +1408,18 @@ void device_cuda_info(vector<DeviceInfo>& devices)
info.type = DEVICE_CUDA;
info.description = string(name);
- info.id = string_printf("CUDA_%d", num);
info.num = num;
info.advanced_shading = (major >= 2);
info.has_bindless_textures = (major >= 3);
info.pack_images = false;
+ int pci_location[3] = {0, 0, 0};
+ cuDeviceGetAttribute(&pci_location[0], CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID, num);
+ cuDeviceGetAttribute(&pci_location[1], CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, num);
+ cuDeviceGetAttribute(&pci_location[2], CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID, num);
+ info.id = string_printf("CUDA_%s_%04x:%02x:%02x", name, pci_location[0], pci_location[1], pci_location[2]);
+
/* if device has a kernel timeout, assume it is used for display */
if(cuDeviceGetAttribute(&attr, CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT, num) == CUDA_SUCCESS && attr == 1) {
info.description += " (Display)";
diff --git a/intern/cycles/device/device_intern.h b/intern/cycles/device/device_intern.h
index 47584ae6d22..de487649045 100644
--- a/intern/cycles/device/device_intern.h
+++ b/intern/cycles/device/device_intern.h
@@ -33,7 +33,6 @@ void device_cpu_info(vector<DeviceInfo>& devices);
void device_opencl_info(vector<DeviceInfo>& devices);
void device_cuda_info(vector<DeviceInfo>& devices);
void device_network_info(vector<DeviceInfo>& devices);
-void device_multi_info(vector<DeviceInfo>& devices);
string device_cpu_capabilities(void);
string device_opencl_capabilities(void);
diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp
index ef257358b22..48fd159d508 100644
--- a/intern/cycles/device/device_multi.cpp
+++ b/intern/cycles/device/device_multi.cpp
@@ -350,120 +350,5 @@ Device *device_multi_create(DeviceInfo& info, Stats &stats, bool background)
return new MultiDevice(info, stats, background);
}
-static bool device_multi_add(vector<DeviceInfo>& devices, DeviceType type, bool with_display, bool with_advanced_shading, const char *id_fmt, int num)
-{
- DeviceInfo info;
-
- /* create map to find duplicate descriptions */
- map<string, int> dupli_map;
- map<string, int>::iterator dt;
- int num_added = 0, num_display = 0;
-
- info.advanced_shading = with_advanced_shading;
- info.pack_images = false;
- info.has_bindless_textures = true;
-
- foreach(DeviceInfo& subinfo, devices) {
- if(subinfo.type == type) {
- if(subinfo.advanced_shading != info.advanced_shading)
- continue;
- if(subinfo.display_device) {
- if(with_display)
- num_display++;
- else
- continue;
- }
-
- string key = subinfo.description;
-
- if(dupli_map.find(key) == dupli_map.end())
- dupli_map[key] = 1;
- else
- dupli_map[key]++;
-
- info.multi_devices.push_back(subinfo);
- if(subinfo.display_device)
- info.display_device = true;
- info.pack_images = info.pack_images || subinfo.pack_images;
- info.has_bindless_textures = info.has_bindless_textures && subinfo.has_bindless_textures;
- num_added++;
- }
- }
-
- if(num_added <= 1 || (with_display && num_display == 0))
- return false;
-
- /* generate string */
- stringstream desc;
- vector<string> last_tokens;
- bool first = true;
-
- for(dt = dupli_map.begin(); dt != dupli_map.end(); dt++) {
- if(!first) desc << " + ";
- first = false;
-
- /* get name and count */
- string name = dt->first;
- int count = dt->second;
-
- /* strip common prefixes */
- vector<string> tokens;
- string_split(tokens, dt->first);
-
- if(tokens.size() > 1) {
- int i;
-
- for(i = 0; i < tokens.size() && i < last_tokens.size(); i++)
- if(tokens[i] != last_tokens[i])
- break;
-
- name = "";
- for(; i < tokens.size(); i++) {
- name += tokens[i];
- if(i != tokens.size() - 1)
- name += " ";
- }
- }
-
- last_tokens = tokens;
-
- /* add */
- if(count > 1)
- desc << name << " (" << count << "x)";
- else
- desc << name;
- }
-
- /* add info */
- info.type = DEVICE_MULTI;
- info.description = desc.str();
- info.id = string_printf(id_fmt, num);
- info.display_device = with_display;
- info.num = 0;
-
- if(with_display)
- devices.push_back(info);
- else
- devices.insert(devices.begin(), info);
-
- return true;
-}
-
-void device_multi_info(vector<DeviceInfo>& devices)
-{
- int num = 0;
-
- if(!device_multi_add(devices, DEVICE_CUDA, false, true, "CUDA_MULTI_%d", num++))
- device_multi_add(devices, DEVICE_CUDA, false, false, "CUDA_MULTI_%d", num++);
- if(!device_multi_add(devices, DEVICE_CUDA, true, true, "CUDA_MULTI_%d", num++))
- device_multi_add(devices, DEVICE_CUDA, true, false, "CUDA_MULTI_%d", num++);
-
- num = 0;
- if(!device_multi_add(devices, DEVICE_OPENCL, false, true, "OPENCL_MULTI_%d", num++))
- device_multi_add(devices, DEVICE_OPENCL, false, false, "OPENCL_MULTI_%d", num++);
- if(!device_multi_add(devices, DEVICE_OPENCL, true, true, "OPENCL_MULTI_%d", num++))
- device_multi_add(devices, DEVICE_OPENCL, true, false, "OPENCL_MULTI_%d", num++);
-}
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp
index 45cf6b074e9..ba94c592a5f 100644
--- a/intern/cycles/device/device_opencl.cpp
+++ b/intern/cycles/device/device_opencl.cpp
@@ -83,17 +83,22 @@ void device_opencl_info(vector<DeviceInfo>& devices)
const string& platform_name = platform_device.platform_name;
const cl_device_type device_type = platform_device.device_type;
const string& device_name = platform_device.device_name;
+ string hardware_id = platform_device.hardware_id;
+ if(hardware_id == "") {
+ hardware_id = string_printf("ID_%d", num_devices);
+ }
+
DeviceInfo info;
info.type = DEVICE_OPENCL;
info.description = string_remove_trademark(string(device_name));
info.num = num_devices;
- info.id = string_printf("OPENCL_%d", info.num);
/* We don't know if it's used for display, but assume it is. */
info.display_device = true;
info.advanced_shading = OpenCLInfo::kernel_use_advanced_shading(platform_name);
info.pack_images = true;
info.use_split_kernel = OpenCLInfo::kernel_use_split(platform_name,
device_type);
+ info.id = string("OPENCL_") + platform_name + "_" + device_name + "_" + hardware_id;
devices.push_back(info);
num_devices++;
}
diff --git a/intern/cycles/device/opencl/opencl.h b/intern/cycles/device/opencl/opencl.h
index 30a35acbb2a..054ac9014f0 100644
--- a/intern/cycles/device/opencl/opencl.h
+++ b/intern/cycles/device/opencl/opencl.h
@@ -55,17 +55,20 @@ struct OpenCLPlatformDevice {
const string& platform_name,
cl_device_id device_id,
cl_device_type device_type,
- const string& device_name)
+ const string& device_name,
+ const string& hardware_id)
: platform_id(platform_id),
platform_name(platform_name),
device_id(device_id),
device_type(device_type),
- device_name(device_name) {}
+ device_name(device_name),
+ hardware_id(hardware_id) {}
cl_platform_id platform_id;
string platform_name;
cl_device_id device_id;
cl_device_type device_type;
string device_name;
+ string hardware_id;
};
/* Contains all static OpenCL helper functions. */
@@ -83,6 +86,8 @@ public:
string *error = NULL);
static bool device_version_check(cl_device_id device,
string *error = NULL);
+ static string get_hardware_id(string platform_name,
+ cl_device_id device_id);
static void get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices,
bool force_all = false);
};
diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp
index e425ae8e2e8..36eb70b8c85 100644
--- a/intern/cycles/device/opencl/opencl_util.cpp
+++ b/intern/cycles/device/opencl/opencl_util.cpp
@@ -661,6 +661,27 @@ bool OpenCLInfo::device_version_check(cl_device_id device,
return true;
}
+string OpenCLInfo::get_hardware_id(string platform_name, cl_device_id device_id)
+{
+ if(platform_name == "AMD Accelerated Parallel Processing" || platform_name == "Apple") {
+ /* Use cl_amd_device_topology extension. */
+ cl_char topology[24];
+ if(clGetDeviceInfo(device_id, 0x4037, sizeof(topology), topology, NULL) == CL_SUCCESS && topology[0] == 1) {
+ return string_printf("%02x:%02x.%01x", topology[21], topology[22], topology[23]);
+ }
+ }
+ else if(platform_name == "NVIDIA CUDA") {
+ /* Use two undocumented options of the cl_nv_device_attribute_query extension. */
+ cl_int bus_id, slot_id;
+ if(clGetDeviceInfo(device_id, 0x4008, sizeof(cl_int), &bus_id, NULL) == CL_SUCCESS &&
+ clGetDeviceInfo(device_id, 0x4009, sizeof(cl_int), &slot_id, NULL) == CL_SUCCESS) {
+ return string_printf("%02x:%02x.%01x", bus_id, slot_id>>3, slot_id & 0x7);
+ }
+ }
+ /* No general way to get a hardware ID from OpenCL => give up. */
+ return "";
+}
+
void OpenCLInfo::get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices,
bool force_all)
{
@@ -773,11 +794,13 @@ void OpenCLInfo::get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices
continue;
}
FIRST_VLOG(2) << "Adding new device " << device_name << ".";
+ string hardware_id = get_hardware_id(platform_name, device_id);
usable_devices->push_back(OpenCLPlatformDevice(platform_id,
platform_name,
device_id,
device_type,
- device_name));
+ device_name,
+ hardware_id));
}
else {
FIRST_VLOG(2) << "Ignoring device " << device_name
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 2c95825435d..105207311c9 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -178,6 +178,7 @@ set(SRC_UTIL_HEADERS
../util/util_atomic.h
../util/util_color.h
../util/util_half.h
+ ../util/util_hash.h
../util/util_math.h
../util/util_math_fast.h
../util/util_static_assert.h
diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h
index 6b42f66b0d5..9f0fe032ba4 100644
--- a/intern/cycles/kernel/geom/geom_object.h
+++ b/intern/cycles/kernel/geom/geom_object.h
@@ -162,10 +162,14 @@ ccl_device_inline void object_inverse_position_transform(KernelGlobals *kg, cons
ccl_device_inline void object_inverse_normal_transform(KernelGlobals *kg, const ShaderData *sd, float3 *N)
{
#ifdef __OBJECT_MOTION__
- *N = normalize(transform_direction_transposed_auto(&ccl_fetch(sd, ob_tfm), *N));
+ if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP)) {
+ *N = normalize(transform_direction_transposed_auto(&ccl_fetch(sd, ob_tfm), *N));
+ }
#else
- Transform tfm = object_fetch_transform(kg, ccl_fetch(sd, object), OBJECT_TRANSFORM);
- *N = normalize(transform_direction_transposed(&tfm, *N));
+ if(ccl_fetch(sd, object) != OBJECT_NONE) {
+ Transform tfm = object_fetch_transform(kg, ccl_fetch(sd, object), OBJECT_TRANSFORM);
+ *N = normalize(transform_direction_transposed(&tfm, *N));
+ }
#endif
}
diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h
index 623c1dcaaa1..6c3ee6b8098 100644
--- a/intern/cycles/kernel/kernel_accumulate.h
+++ b/intern/cycles/kernel/kernel_accumulate.h
@@ -96,7 +96,7 @@ ccl_device_inline bool bsdf_eval_is_zero(BsdfEval *eval)
}
}
-ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float3 value)
+ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value)
{
#ifdef __PASSES__
if(eval->use_light_pass) {
@@ -115,6 +115,36 @@ ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float3 value)
}
}
+ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value)
+{
+#ifdef __PASSES__
+ if(eval->use_light_pass) {
+ eval->diffuse *= value;
+ eval->glossy *= value;
+ eval->transmission *= value;
+ eval->subsurface *= value;
+ eval->scatter *= value;
+
+ /* skipping transparent, this function is used by for eval(), will be zero then */
+ }
+ else
+ eval->diffuse *= value;
+#else
+ eval->diffuse *= value;
+#endif
+}
+
+ccl_device_inline float3 bsdf_eval_sum(BsdfEval *eval)
+{
+#ifdef __PASSES__
+ if(eval->use_light_pass) {
+ return eval->diffuse + eval->glossy + eval->transmission + eval->subsurface + eval->scatter;
+ }
+ else
+#endif
+ return eval->diffuse;
+}
+
/* Path Radiance
*
* We accumulate different render passes separately. After summing at the end
@@ -193,8 +223,7 @@ ccl_device_inline void path_radiance_bsdf_bounce(PathRadiance *L, ccl_addr_space
}
else {
/* transparent bounce before first hit, or indirectly visible through BSDF */
- float3 sum = (bsdf_eval->diffuse + bsdf_eval->glossy + bsdf_eval->transmission + bsdf_eval->transparent +
- bsdf_eval->subsurface + bsdf_eval->scatter) * inverse_pdf;
+ float3 sum = (bsdf_eval_sum(bsdf_eval) + bsdf_eval->transparent) * inverse_pdf;
*throughput *= sum;
}
}
@@ -264,8 +293,7 @@ ccl_device_inline void path_radiance_accum_light(PathRadiance *L, float3 through
}
else {
/* indirectly visible lighting after BSDF bounce */
- float3 sum = bsdf_eval->diffuse + bsdf_eval->glossy + bsdf_eval->transmission + bsdf_eval->subsurface + bsdf_eval->scatter;
- L->indirect += throughput*sum*shadow;
+ L->indirect += throughput*bsdf_eval_sum(bsdf_eval)*shadow;
}
}
else
diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h
index 84575d35d7f..c32ac6ccf41 100644
--- a/intern/cycles/kernel/kernel_bake.h
+++ b/intern/cycles/kernel/kernel_bake.h
@@ -63,7 +63,7 @@ ccl_device_inline void compute_light_pass(KernelGlobals *kg,
/* sample ambient occlusion */
if(pass_filter & BAKE_FILTER_AO) {
- kernel_path_ao(kg, sd, &emission_sd, &L_sample, &state, &rng, throughput);
+ kernel_path_ao(kg, sd, &emission_sd, &L_sample, &state, &rng, throughput, shader_bsdf_alpha(kg, sd));
}
/* sample emission */
diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h
index 9e4a631b998..8c7c651a053 100644
--- a/intern/cycles/kernel/kernel_emission.h
+++ b/intern/cycles/kernel/kernel_emission.h
@@ -94,7 +94,8 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg,
ccl_addr_space PathState *state,
Ray *ray,
BsdfEval *eval,
- bool *is_lamp)
+ bool *is_lamp,
+ float rand_terminate)
{
if(ls->pdf == 0.0f)
return false;
@@ -134,7 +135,7 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg,
shader_bsdf_eval(kg, sd, ls->D, eval, ls->pdf, ls->shader & SHADER_USE_MIS);
#endif
- bsdf_eval_mul(eval, light_eval/ls->pdf);
+ bsdf_eval_mul3(eval, light_eval/ls->pdf);
#ifdef __PASSES__
/* use visibility flag to skip lights */
@@ -155,6 +156,16 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg,
if(bsdf_eval_is_zero(eval))
return false;
+ if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) {
+ float probability = max3(bsdf_eval_sum(eval)) * kernel_data.integrator.light_inv_rr_threshold;
+ if(probability < 1.0f) {
+ if(rand_terminate >= probability) {
+ return false;
+ }
+ bsdf_eval_mul(eval, 1.0f / probability);
+ }
+ }
+
if(ls->shader & SHADER_CAST_SHADOW) {
/* setup ray */
bool transmit = (dot(ccl_fetch(sd, Ng), ls->D) < 0.0f);
diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h
index 20cf3fa931b..7aec47e4957 100644
--- a/intern/cycles/kernel/kernel_passes.h
+++ b/intern/cycles/kernel/kernel_passes.h
@@ -20,7 +20,7 @@ ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, int sam
{
ccl_global float *buf = buffer;
#if defined(__SPLIT_KERNEL__) && defined(__WORK_STEALING__)
- atomic_add_float(buf, value);
+ atomic_add_and_fetch_float(buf, value);
#else
*buf = (sample == 0)? value: *buf + value;
#endif // __SPLIT_KERNEL__ && __WORK_STEALING__
@@ -33,9 +33,9 @@ ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, int sa
ccl_global float *buf_y = buffer + 1;
ccl_global float *buf_z = buffer + 2;
- atomic_add_float(buf_x, value.x);
- atomic_add_float(buf_y, value.y);
- atomic_add_float(buf_z, value.z);
+ atomic_add_and_fetch_float(buf_x, value.x);
+ atomic_add_and_fetch_float(buf_y, value.y);
+ atomic_add_and_fetch_float(buf_z, value.z);
#else
ccl_global float3 *buf = (ccl_global float3*)buffer;
*buf = (sample == 0)? value: *buf + value;
@@ -50,10 +50,10 @@ ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, int sa
ccl_global float *buf_z = buffer + 2;
ccl_global float *buf_w = buffer + 3;
- atomic_add_float(buf_x, value.x);
- atomic_add_float(buf_y, value.y);
- atomic_add_float(buf_z, value.z);
- atomic_add_float(buf_w, value.w);
+ atomic_add_and_fetch_float(buf_x, value.x);
+ atomic_add_and_fetch_float(buf_y, value.y);
+ atomic_add_and_fetch_float(buf_z, value.z);
+ atomic_add_and_fetch_float(buf_w, value.w);
#else
ccl_global float4 *buf = (ccl_global float4*)buffer;
*buf = (sample == 0)? value: *buf + value;
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index 7558fb94478..6d89a89ed5b 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -53,6 +53,47 @@
CCL_NAMESPACE_BEGIN
+ccl_device_noinline void kernel_path_ao(KernelGlobals *kg,
+ ShaderData *sd,
+ ShaderData *emission_sd,
+ PathRadiance *L,
+ PathState *state,
+ RNG *rng,
+ float3 throughput,
+ float3 ao_alpha)
+{
+ /* todo: solve correlation */
+ float bsdf_u, bsdf_v;
+
+ path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+
+ float ao_factor = kernel_data.background.ao_factor;
+ float3 ao_N;
+ float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N);
+ float3 ao_D;
+ float ao_pdf;
+
+ sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
+
+ if(dot(ccl_fetch(sd, Ng), ao_D) > 0.0f && ao_pdf != 0.0f) {
+ Ray light_ray;
+ float3 ao_shadow;
+
+ light_ray.P = ray_offset(ccl_fetch(sd, P), ccl_fetch(sd, Ng));
+ light_ray.D = ao_D;
+ light_ray.t = kernel_data.background.ao_distance;
+#ifdef __OBJECT_MOTION__
+ light_ray.time = ccl_fetch(sd, time);
+#endif /* __OBJECT_MOTION__ */
+ light_ray.dP = ccl_fetch(sd, dP);
+ light_ray.dD = differential3_zero();
+
+ if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) {
+ path_radiance_accum_ao(L, throughput, ao_alpha, ao_bsdf, ao_shadow, state->bounce);
+ }
+ }
+}
+
ccl_device void kernel_path_indirect(KernelGlobals *kg,
ShaderData *sd,
ShaderData *emission_sd,
@@ -97,7 +138,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
state->bounce);
}
}
-#endif
+#endif /* __LAMP_MIS__ */
#ifdef __VOLUME__
/* volume attenuation, emission, scatter */
@@ -198,7 +239,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
}
}
else
-# endif
+# endif /* __VOLUME_DECOUPLED__ */
{
/* integrate along volume segment with distance sampling */
VolumeIntegrateResult result = kernel_volume_integrate(
@@ -230,10 +271,10 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
break;
}
}
-# endif
+# endif /* __VOLUME_SCATTER__ */
}
}
-#endif
+#endif /* __VOLUME__ */
if(!hit) {
#ifdef __BACKGROUND__
@@ -243,7 +284,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
throughput,
L_background,
state->bounce);
-#endif
+#endif /* __BACKGROUND__ */
break;
}
@@ -257,7 +298,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
shader_eval_surface(kg, sd, rng, state, rbsdf, state->flag, SHADER_CONTEXT_INDIRECT);
#ifdef __BRANCHED_PATH__
shader_merge_closures(sd);
-#endif
+#endif /* __BRANCHED_PATH__ */
/* blurring of bsdf after bounces, for rays that have a small likelihood
* of following this particular path (diffuse, rough glossy) */
@@ -280,7 +321,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
state->ray_pdf);
path_radiance_accum_emission(L, throughput, emission, state->bounce);
}
-#endif
+#endif /* __EMISSION__ */
/* path termination. this is a strange place to put the termination, it's
* mainly due to the mixed in MIS that we use. gives too many unneeded
@@ -305,42 +346,9 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
#ifdef __AO__
/* ambient occlusion */
if(kernel_data.integrator.use_ambient_occlusion || (sd->flag & SD_AO)) {
- float bsdf_u, bsdf_v;
- path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-
- float ao_factor = kernel_data.background.ao_factor;
- float3 ao_N;
- float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N);
- float3 ao_D;
- float ao_pdf;
- float3 ao_alpha = make_float3(0.0f, 0.0f, 0.0f);
-
- sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
-
- if(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) {
- Ray light_ray;
- float3 ao_shadow;
-
- light_ray.P = ray_offset(sd->P, sd->Ng);
- light_ray.D = ao_D;
- light_ray.t = kernel_data.background.ao_distance;
-# ifdef __OBJECT_MOTION__
- light_ray.time = sd->time;
-# endif
- light_ray.dP = sd->dP;
- light_ray.dD = differential3_zero();
-
- if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) {
- path_radiance_accum_ao(L,
- throughput,
- ao_alpha,
- ao_bsdf,
- ao_shadow,
- state->bounce);
- }
- }
+ kernel_path_ao(kg, sd, emission_sd, L, state, rng, throughput, make_float3(0.0f, 0.0f, 0.0f));
}
-#endif
+#endif /* __AO__ */
#ifdef __SUBSURFACE__
/* bssrdf scatter to a different location on the same object, replacing
@@ -372,7 +380,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
false);
}
}
-#endif
+#endif /* __SUBSURFACE__ */
#if defined(__EMISSION__) && defined(__BRANCHED_PATH__)
if(kernel_data.integrator.use_direct_light) {
@@ -387,53 +395,13 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
L,
all);
}
-#endif
+#endif /* defined(__EMISSION__) && defined(__BRANCHED_PATH__) */
if(!kernel_path_surface_bounce(kg, rng, sd, &throughput, state, L, ray))
break;
}
}
-ccl_device_noinline void kernel_path_ao(KernelGlobals *kg,
- ShaderData *sd,
- ShaderData *emission_sd,
- PathRadiance *L,
- PathState *state,
- RNG *rng,
- float3 throughput)
-{
- /* todo: solve correlation */
- float bsdf_u, bsdf_v;
-
- path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-
- float ao_factor = kernel_data.background.ao_factor;
- float3 ao_N;
- float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N);
- float3 ao_D;
- float ao_pdf;
- float3 ao_alpha = shader_bsdf_alpha(kg, sd);
-
- sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
-
- if(dot(ccl_fetch(sd, Ng), ao_D) > 0.0f && ao_pdf != 0.0f) {
- Ray light_ray;
- float3 ao_shadow;
-
- light_ray.P = ray_offset(ccl_fetch(sd, P), ccl_fetch(sd, Ng));
- light_ray.D = ao_D;
- light_ray.t = kernel_data.background.ao_distance;
-#ifdef __OBJECT_MOTION__
- light_ray.time = ccl_fetch(sd, time);
-#endif
- light_ray.dP = ccl_fetch(sd, dP);
- light_ray.dD = differential3_zero();
-
- if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow))
- path_radiance_accum_ao(L, throughput, ao_alpha, ao_bsdf, ao_shadow, state->bounce);
- }
-}
-
#ifdef __SUBSURFACE__
# ifndef __KERNEL_CUDA__
ccl_device
@@ -481,7 +449,7 @@ bool kernel_path_subsurface_scatter(
ss_indirect->need_update_volume_stack =
kernel_data.integrator.use_volumes &&
ccl_fetch(sd, flag) & SD_OBJECT_INTERSECTS_VOLUME;
-# endif
+# endif /* __VOLUME__ */
/* compute lighting with the BSDF closure */
for(int hit = 0; hit < num_hits; hit++) {
@@ -524,7 +492,7 @@ bool kernel_path_subsurface_scatter(
{
# ifdef __LAMP_MIS__
hit_state->ray_t = 0.0f;
-# endif
+# endif /* __LAMP_MIS__ */
# ifdef __VOLUME__
if(ss_indirect->need_update_volume_stack) {
@@ -539,7 +507,7 @@ bool kernel_path_subsurface_scatter(
&volume_ray,
hit_state->volume_stack);
}
-# endif
+# endif /* __VOLUME__ */
path_radiance_reset_indirect(L);
ss_indirect->num_rays++;
}
@@ -625,14 +593,14 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
#ifdef __KERNEL_DEBUG__
DebugData debug_data;
debug_data_init(&debug_data);
-#endif
+#endif /* __KERNEL_DEBUG__ */
#ifdef __SUBSURFACE__
SubsurfaceIndirectRays ss_indirect;
kernel_path_subsurface_init_indirect(&ss_indirect);
for(;;) {
-#endif
+#endif /* __SUBSURFACE__ */
/* path iteration */
for(;;) {
@@ -658,7 +626,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
bool hit = scene_intersect(kg, ray, visibility, &isect, &lcg_state, difl, extmax);
#else
bool hit = scene_intersect(kg, ray, visibility, &isect, NULL, 0.0f, 0.0f);
-#endif
+#endif /* __HAIR__ */
#ifdef __KERNEL_DEBUG__
if(state.flag & PATH_RAY_CAMERA) {
@@ -666,7 +634,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
debug_data.num_bvh_traversed_instances += isect.num_traversed_instances;
}
debug_data.num_ray_bounces++;
-#endif
+#endif /* __KERNEL_DEBUG__ */
#ifdef __LAMP_MIS__
if(kernel_data.integrator.use_lamp_mis && !(state.flag & PATH_RAY_CAMERA)) {
@@ -687,7 +655,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
if(indirect_lamp_emission(kg, &emission_sd, &state, &light_ray, &emission))
path_radiance_accum_emission(&L, throughput, emission, state.bounce);
}
-#endif
+#endif /* __LAMP_MIS__ */
#ifdef __VOLUME__
/* volume attenuation, emission, scatter */
@@ -751,7 +719,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
}
}
else
-# endif
+# endif /* __VOLUME_DECOUPLED__ */
{
/* integrate along volume segment with distance sampling */
VolumeIntegrateResult result = kernel_volume_integrate(
@@ -768,10 +736,10 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
else
break;
}
-# endif
+# endif /* __VOLUME_SCATTER__ */
}
}
-#endif
+#endif /* __VOLUME__ */
if(!hit) {
/* eval background shader if nothing hit */
@@ -780,7 +748,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
#ifdef __PASSES__
if(!(kernel_data.film.pass_flag & PASS_BACKGROUND))
-#endif
+#endif /* __PASSES__ */
break;
}
@@ -788,7 +756,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
/* sample background shader */
float3 L_background = indirect_background(kg, &emission_sd, &state, &ray);
path_radiance_accum_background(&L, throughput, L_background, state.bounce);
-#endif
+#endif /* __BACKGROUND__ */
break;
}
@@ -816,7 +784,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
if(sd.flag & SD_HOLDOUT_MASK)
break;
}
-#endif
+#endif /* __HOLDOUT__ */
/* holdout mask objects do not write data passes */
kernel_write_data_passes(kg, buffer, &L, &sd, sample, &state, throughput);
@@ -839,7 +807,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, state.ray_pdf);
path_radiance_accum_emission(&L, throughput, emission, state.bounce);
}
-#endif
+#endif /* __EMISSION__ */
/* path termination. this is a strange place to put the termination, it's
* mainly due to the mixed in MIS that we use. gives too many unneeded
@@ -851,7 +819,6 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
}
else if(probability != 1.0f) {
float terminate = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_TERMINATE);
-
if(terminate >= probability)
break;
@@ -861,9 +828,9 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
#ifdef __AO__
/* ambient occlusion */
if(kernel_data.integrator.use_ambient_occlusion || (sd.flag & SD_AO)) {
- kernel_path_ao(kg, &sd, &emission_sd, &L, &state, rng, throughput);
+ kernel_path_ao(kg, &sd, &emission_sd, &L, &state, rng, throughput, shader_bsdf_alpha(kg, &sd));
}
-#endif
+#endif /* __AO__ */
#ifdef __SUBSURFACE__
/* bssrdf scatter to a different location on the same object, replacing
@@ -918,7 +885,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
#ifdef __KERNEL_DEBUG__
kernel_write_debug_passes(kg, buffer, &state, &debug_data, sample);
-#endif
+#endif /* __KERNEL_DEBUG__ */
return make_float4(L_sum.x, L_sum.y, L_sum.z, 1.0f - L_transparent);
}
diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h
index cdb07db587a..c84727ace99 100644
--- a/intern/cycles/kernel/kernel_path_branched.h
+++ b/intern/cycles/kernel/kernel_path_branched.h
@@ -51,7 +51,7 @@ ccl_device_inline void kernel_branched_path_ao(KernelGlobals *kg,
light_ray.t = kernel_data.background.ao_distance;
#ifdef __OBJECT_MOTION__
light_ray.time = ccl_fetch(sd, time);
-#endif
+#endif /* __OBJECT_MOTION__ */
light_ray.dP = ccl_fetch(sd, dP);
light_ray.dD = differential3_zero();
@@ -169,7 +169,7 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg,
Ray volume_ray = *ray;
bool need_update_volume_stack = kernel_data.integrator.use_volumes &&
ccl_fetch(sd, flag) & SD_OBJECT_INTERSECTS_VOLUME;
-#endif
+#endif /* __VOLUME__ */
/* compute lighting with the BSDF closure */
for(int hit = 0; hit < num_hits; hit++) {
@@ -200,7 +200,7 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg,
&volume_ray,
hit_state.volume_stack);
}
-#endif
+#endif /* __VOLUME__ */
#ifdef __EMISSION__
/* direct light */
@@ -217,7 +217,7 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg,
L,
all);
}
-#endif
+#endif /* __EMISSION__ */
/* indirect light */
kernel_branched_path_surface_indirect_light(
@@ -234,7 +234,7 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg,
}
}
}
-#endif
+#endif /* __SUBSURFACE__ */
ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, int sample, Ray ray, ccl_global float *buffer)
{
@@ -256,7 +256,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
#ifdef __KERNEL_DEBUG__
DebugData debug_data;
debug_data_init(&debug_data);
-#endif
+#endif /* __KERNEL_DEBUG__ */
/* Main Loop
* Here we only handle transparency intersections from the camera ray.
@@ -285,13 +285,13 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
bool hit = scene_intersect(kg, ray, visibility, &isect, &lcg_state, difl, extmax);
#else
bool hit = scene_intersect(kg, ray, visibility, &isect, NULL, 0.0f, 0.0f);
-#endif
+#endif /* __HAIR__ */
#ifdef __KERNEL_DEBUG__
debug_data.num_bvh_traversal_steps += isect.num_traversal_steps;
debug_data.num_bvh_traversed_instances += isect.num_traversed_instances;
debug_data.num_ray_bounces++;
-#endif
+#endif /* __KERNEL_DEBUG__ */
#ifdef __VOLUME__
/* volume attenuation, emission, scatter */
@@ -432,14 +432,14 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
path_radiance_reset_indirect(&L);
}
}
-#endif
+#endif /* __VOLUME_SCATTER__ */
}
/* todo: avoid this calculation using decoupled ray marching */
kernel_volume_shadow(kg, &emission_sd, &state, &volume_ray, &throughput);
-#endif
+#endif /* __VOLUME_DECOUPLED__ */
}
-#endif
+#endif /* __VOLUME__ */
if(!hit) {
/* eval background shader if nothing hit */
@@ -448,7 +448,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
#ifdef __PASSES__
if(!(kernel_data.film.pass_flag & PASS_BACKGROUND))
-#endif
+#endif /* __PASSES__ */
break;
}
@@ -456,7 +456,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
/* sample background shader */
float3 L_background = indirect_background(kg, &emission_sd, &state, &ray);
path_radiance_accum_background(&L, throughput, L_background, state.bounce);
-#endif
+#endif /* __BACKGROUND__ */
break;
}
@@ -484,7 +484,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
if(sd.flag & SD_HOLDOUT_MASK)
break;
}
-#endif
+#endif /* __HOLDOUT__ */
/* holdout mask objects do not write data passes */
kernel_write_data_passes(kg, buffer, &L, &sd, sample, &state, throughput);
@@ -495,7 +495,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
float3 emission = indirect_primitive_emission(kg, &sd, isect.t, state.flag, state.ray_pdf);
path_radiance_accum_emission(&L, throughput, emission, state.bounce);
}
-#endif
+#endif /* __EMISSION__ */
/* transparency termination */
if(state.flag & PATH_RAY_TRANSPARENT) {
@@ -522,7 +522,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
if(kernel_data.integrator.use_ambient_occlusion || (sd.flag & SD_AO)) {
kernel_branched_path_ao(kg, &sd, &emission_sd, &L, &state, rng, throughput);
}
-#endif
+#endif /* __AO__ */
#ifdef __SUBSURFACE__
/* bssrdf scatter to a different location on the same object */
@@ -530,7 +530,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
kernel_branched_path_subsurface_scatter(kg, &sd, &indirect_sd, &emission_sd,
&L, &state, rng, &ray, throughput);
}
-#endif
+#endif /* __SUBSURFACE__ */
if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
PathState hit_state = state;
@@ -542,7 +542,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
kernel_branched_path_surface_connect_light(kg, rng,
&sd, &emission_sd, &hit_state, throughput, 1.0f, &L, all);
}
-#endif
+#endif /* __EMISSION__ */
/* indirect light */
kernel_branched_path_surface_indirect_light(kg, rng,
@@ -567,12 +567,12 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
ray.dP = sd.dP;
ray.dD.dx = -sd.dI.dx;
ray.dD.dy = -sd.dI.dy;
-#endif
+#endif /* __RAY_DIFFERENTIALS__ */
#ifdef __VOLUME__
/* enter/exit volume */
kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-#endif
+#endif /* __VOLUME__ */
}
float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
@@ -581,7 +581,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
#ifdef __KERNEL_DEBUG__
kernel_write_debug_passes(kg, buffer, &state, &debug_data, sample);
-#endif
+#endif /* __KERNEL_DEBUG__ */
return make_float4(L_sum.x, L_sum.y, L_sum.z, 1.0f - L_transparent);
}
diff --git a/intern/cycles/kernel/kernel_path_common.h b/intern/cycles/kernel/kernel_path_common.h
index 1912dfa16ed..13597eab287 100644
--- a/intern/cycles/kernel/kernel_path_common.h
+++ b/intern/cycles/kernel/kernel_path_common.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "util_hash.h"
+
CCL_NAMESPACE_BEGIN
ccl_device_inline void kernel_path_trace_setup(KernelGlobals *kg,
@@ -28,6 +30,10 @@ ccl_device_inline void kernel_path_trace_setup(KernelGlobals *kg,
int num_samples = kernel_data.integrator.aa_samples;
+ if(sample == 0) {
+ *rng_state = hash_int_2d(x, y);
+ }
+
path_rng_init(kg, rng_state, sample, num_samples, rng, x, y, &filter_u, &filter_v);
/* sample camera ray */
diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h
index 45f0f1cbfaa..fea503d06e5 100644
--- a/intern/cycles/kernel/kernel_path_surface.h
+++ b/intern/cycles/kernel/kernel_path_surface.h
@@ -49,6 +49,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
for(int j = 0; j < num_samples; j++) {
float light_u, light_v;
path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+ float terminate = path_branched_rng_light_termination(kg, &lamp_rng, state, j, num_samples);
LightSample ls;
if(lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls)) {
@@ -57,7 +58,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
if(kernel_data.integrator.pdf_triangles != 0.0f)
ls.pdf *= 2.0f;
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
@@ -79,6 +80,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
float light_t = path_branched_rng_1D(kg, rng, state, j, num_samples, PRNG_LIGHT);
float light_u, light_v;
path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v);
+ float terminate = path_branched_rng_light_termination(kg, rng, state, j, num_samples);
/* only sample triangle lights */
if(kernel_data.integrator.num_all_lights)
@@ -90,7 +92,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
if(kernel_data.integrator.num_all_lights)
ls.pdf *= 2.0f;
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
@@ -108,11 +110,12 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
float light_u, light_v;
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+ float terminate = path_state_rng_light_termination(kg, rng, state);
LightSample ls;
if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
/* sample random light */
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
@@ -210,7 +213,8 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_
LightSample ls;
if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) {
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ float terminate = path_state_rng_light_termination(kg, rng, state);
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h
index 5ee1912c913..3d3b7385d8b 100644
--- a/intern/cycles/kernel/kernel_path_volume.h
+++ b/intern/cycles/kernel/kernel_path_volume.h
@@ -48,7 +48,8 @@ ccl_device_inline void kernel_path_volume_connect_light(
if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls))
{
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ float terminate = path_state_rng_light_termination(kg, rng, state);
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
@@ -161,7 +162,8 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
if(kernel_data.integrator.pdf_triangles != 0.0f)
ls.pdf *= 2.0f;
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ float terminate = path_branched_rng_light_termination(kg, rng, state, j, num_samples);
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
@@ -209,7 +211,8 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
if(kernel_data.integrator.num_all_lights)
ls.pdf *= 2.0f;
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ float terminate = path_branched_rng_light_termination(kg, rng, state, j, num_samples);
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
@@ -246,7 +249,8 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG
/* todo: split up light_sample so we don't have to call it again with new position */
if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) {
/* sample random light */
- if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ float terminate = path_state_rng_light_termination(kg, rng, state);
+ if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* trace shadow ray */
float3 shadow;
diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h
index 4a76ffddbe7..2b767da5041 100644
--- a/intern/cycles/kernel/kernel_random.h
+++ b/intern/cycles/kernel/kernel_random.h
@@ -300,6 +300,23 @@ ccl_device_inline void path_branched_rng_2D(KernelGlobals *kg, ccl_addr_space RN
path_rng_2D(kg, rng, state->sample*num_branches + branch, state->num_samples*num_branches, state->rng_offset + dimension, fx, fy);
}
+/* Utitility functions to get light termination value, since it might not be needed in many cases. */
+ccl_device_inline float path_state_rng_light_termination(KernelGlobals *kg, ccl_addr_space RNG *rng, const ccl_addr_space PathState *state)
+{
+ if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) {
+ return path_state_rng_1D_for_decision(kg, rng, state, PRNG_LIGHT_TERMINATE);
+ }
+ return 0.0f;
+}
+
+ccl_device_inline float path_branched_rng_light_termination(KernelGlobals *kg, ccl_addr_space RNG *rng, const PathState *state, int branch, int num_branches)
+{
+ if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) {
+ return path_branched_rng_1D_for_decision(kg, rng, state, branch, num_branches, PRNG_LIGHT_TERMINATE);
+ }
+ return 0.0f;
+}
+
ccl_device_inline void path_state_branch(PathState *state, int branch, int num_branches)
{
/* path is splitting into a branch, adjust so that each branch
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 652612c19db..9d5ea53d5d8 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -285,16 +285,13 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
#ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, time);
-#endif
+ ccl_fetch(sd, time) = time;
}
else if(lamp != LAMP_NONE) {
ccl_fetch(sd, ob_tfm) = lamp_fetch_transform(kg, lamp, false);
ccl_fetch(sd, ob_itfm) = lamp_fetch_transform(kg, lamp, true);
- }
-
-#ifdef __OBJECT_MOTION__
- ccl_fetch(sd, time) = time;
#endif
+ }
/* transform into world space */
if(object_space) {
@@ -581,7 +578,7 @@ void shader_bsdf_eval(KernelGlobals *kg,
_shader_bsdf_multi_eval(kg, sd, omega_in, &pdf, -1, eval, 0.0f, 0.0f);
if(use_mis) {
float weight = power_heuristic(light_pdf, pdf);
- bsdf_eval_mul(eval, make_float3(weight, weight, weight));
+ bsdf_eval_mul(eval, weight);
}
}
}
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 96f2dd1be2c..15960dba40d 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -253,7 +253,7 @@ enum PathTraceDimension {
PRNG_LIGHT = 3,
PRNG_LIGHT_U = 4,
PRNG_LIGHT_V = 5,
- PRNG_UNUSED_3 = 6,
+ PRNG_LIGHT_TERMINATE = 6,
PRNG_TERMINATE = 7,
#ifdef __VOLUME__
@@ -1129,8 +1129,9 @@ typedef struct KernelIntegrator {
float volume_step_size;
int volume_samples;
+ float light_inv_rr_threshold;
+
int pad1;
- int pad2;
} KernelIntegrator;
static_assert_align(KernelIntegrator, 16);
diff --git a/intern/cycles/kernel/split/kernel_direct_lighting.h b/intern/cycles/kernel/split/kernel_direct_lighting.h
index 6ad736fc2c1..82ca18829d3 100644
--- a/intern/cycles/kernel/split/kernel_direct_lighting.h
+++ b/intern/cycles/kernel/split/kernel_direct_lighting.h
@@ -72,6 +72,7 @@ ccl_device char kernel_direct_lighting(
float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
float light_u, light_v;
path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+ float terminate = path_state_rng_light_termination(kg, rng, state);
LightSample ls;
if(light_sample(kg,
@@ -88,7 +89,7 @@ ccl_device char kernel_direct_lighting(
BsdfEval L_light;
bool is_lamp;
- if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp)) {
+ if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) {
/* Write intermediate data to global memory to access from
* the next kernel.
*/
diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/svm_brick.h
index 47e1ba2ba6b..14245cf0522 100644
--- a/intern/cycles/kernel/svm/svm_brick.h
+++ b/intern/cycles/kernel/svm/svm_brick.h
@@ -112,7 +112,7 @@ ccl_device void svm_node_tex_brick(KernelGlobals *kg, ShaderData *sd, float *sta
}
if(stack_valid(color_offset))
- stack_store_float3(stack, color_offset, lerp(color1, mortar, f));
+ stack_store_float3(stack, color_offset, color1*(1.0f-f) + mortar*f);
if(stack_valid(fac_offset))
stack_store_float(stack, fac_offset, f);
}
diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h
index 9606064492e..2afdf61b476 100644
--- a/intern/cycles/kernel/svm/svm_image.h
+++ b/intern/cycles/kernel/svm/svm_image.h
@@ -241,8 +241,7 @@ ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float
float3 N = ccl_fetch(sd, N);
N = ccl_fetch(sd, N);
- if(ccl_fetch(sd, object) != OBJECT_NONE)
- object_inverse_normal_transform(kg, sd, &N);
+ object_inverse_normal_transform(kg, sd, &N);
/* project from direction vector to barycentric coordinates in triangles */
N.x = fabsf(N.x);
diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h
index 6c3394adbce..c0b01262212 100644
--- a/intern/cycles/kernel/svm/svm_tex_coord.h
+++ b/intern/cycles/kernel/svm/svm_tex_coord.h
@@ -49,8 +49,7 @@ ccl_device void svm_node_tex_coord(KernelGlobals *kg,
}
case NODE_TEXCO_NORMAL: {
data = ccl_fetch(sd, N);
- if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP))
- object_inverse_normal_transform(kg, sd, &data);
+ object_inverse_normal_transform(kg, sd, &data);
break;
}
case NODE_TEXCO_CAMERA: {
@@ -131,8 +130,7 @@ ccl_device void svm_node_tex_coord_bump_dx(KernelGlobals *kg,
}
case NODE_TEXCO_NORMAL: {
data = ccl_fetch(sd, N);
- if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP))
- object_inverse_normal_transform(kg, sd, &data);
+ object_inverse_normal_transform(kg, sd, &data);
break;
}
case NODE_TEXCO_CAMERA: {
@@ -216,8 +214,7 @@ ccl_device void svm_node_tex_coord_bump_dy(KernelGlobals *kg,
}
case NODE_TEXCO_NORMAL: {
data = ccl_fetch(sd, N);
- if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP))
- object_inverse_normal_transform(kg, sd, &data);
+ object_inverse_normal_transform(kg, sd, &data);
break;
}
case NODE_TEXCO_CAMERA: {
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp
index 1e170d3a96e..cb20e811708 100644
--- a/intern/cycles/render/buffers.cpp
+++ b/intern/cycles/render/buffers.cpp
@@ -135,15 +135,7 @@ void RenderBuffers::reset(Device *device, BufferParams& params_)
/* allocate rng state */
rng_state.resize(params.width, params.height);
- uint *init_state = rng_state.resize(params.width, params.height);
- int x, y, width = params.width, height = params.height;
-
- for(y = 0; y < height; y++)
- for(x = 0; x < width; x++)
- init_state[y*width + x] = hash_int_2d(params.full_x+x, params.full_y+y);
-
device->mem_alloc(rng_state, MEM_READ_WRITE);
- device->mem_copy_to(rng_state);
}
bool RenderBuffers::copy_from_device()
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 073a0aa2ac9..7465fbd43a7 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -471,133 +471,43 @@ bool ImageManager::file_load_image_generic(Image *img, ImageInput **in, int &wid
return true;
}
-template<typename T>
-bool ImageManager::file_load_byte_image(Image *img, ImageDataType type, device_vector<T>& tex_img)
+template<TypeDesc::BASETYPE FileFormat,
+ typename StorageType,
+ typename DeviceType>
+bool ImageManager::file_load_image(Image *img,
+ ImageDataType type,
+ device_vector<DeviceType>& tex_img)
{
+ const StorageType alpha_one = (FileFormat == TypeDesc::UINT8)? 255 : 1;
ImageInput *in = NULL;
int width, height, depth, components;
-
- if(!file_load_image_generic(img, &in, width, height, depth, components))
- return false;
-
- /* read RGBA pixels */
- uchar *pixels = (uchar*)tex_img.resize(width, height, depth);
- if(pixels == NULL) {
+ if(!file_load_image_generic(img, &in, width, height, depth, components)) {
return false;
}
- bool cmyk = false;
-
- if(in) {
- if(depth <= 1) {
- int scanlinesize = width*components*sizeof(uchar);
-
- in->read_image(TypeDesc::UINT8,
- (uchar*)pixels + (((size_t)height)-1)*scanlinesize,
- AutoStride,
- -scanlinesize,
- AutoStride);
- }
- else {
- in->read_image(TypeDesc::UINT8, (uchar*)pixels);
- }
-
- cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
-
- in->close();
- delete in;
- }
- else {
- builtin_image_pixels_cb(img->filename, img->builtin_data, pixels);
- }
-
- /* Check if we actually have a byte4 slot, in case components == 1, but device
- * doesn't support single channel textures. */
- if(type == IMAGE_DATA_TYPE_BYTE4) {
- size_t num_pixels = ((size_t)width) * height * depth;
- if(cmyk) {
- /* CMYK */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+2] = (pixels[i*4+2]*pixels[i*4+3])/255;
- pixels[i*4+1] = (pixels[i*4+1]*pixels[i*4+3])/255;
- pixels[i*4+0] = (pixels[i*4+0]*pixels[i*4+3])/255;
- pixels[i*4+3] = 255;
- }
- }
- else if(components == 2) {
- /* grayscale + alpha */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = pixels[i*2+1];
- pixels[i*4+2] = pixels[i*2+0];
- pixels[i*4+1] = pixels[i*2+0];
- pixels[i*4+0] = pixels[i*2+0];
- }
- }
- else if(components == 3) {
- /* RGB */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 255;
- pixels[i*4+2] = pixels[i*3+2];
- pixels[i*4+1] = pixels[i*3+1];
- pixels[i*4+0] = pixels[i*3+0];
- }
- }
- else if(components == 1) {
- /* grayscale */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 255;
- pixels[i*4+2] = pixels[i];
- pixels[i*4+1] = pixels[i];
- pixels[i*4+0] = pixels[i];
- }
- }
-
- if(img->use_alpha == false) {
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 255;
- }
- }
- }
-
- return true;
-}
-
-template<typename T>
-bool ImageManager::file_load_float_image(Image *img, ImageDataType type, device_vector<T>& tex_img)
-{
- ImageInput *in = NULL;
- int width, height, depth, components;
-
- if(!file_load_image_generic(img, &in, width, height, depth, components))
- return false;
-
- /* read RGBA pixels */
- float *pixels = (float*)tex_img.resize(width, height, depth);
+ /* Read RGBA pixels. */
+ StorageType *pixels = (StorageType*)tex_img.resize(width, height, depth);
if(pixels == NULL) {
return false;
}
bool cmyk = false;
-
if(in) {
- float *readpixels = pixels;
- vector<float> tmppixels;
-
+ StorageType *readpixels = pixels;
+ vector<StorageType> tmppixels;
if(components > 4) {
tmppixels.resize(((size_t)width)*height*components);
readpixels = &tmppixels[0];
}
-
if(depth <= 1) {
- size_t scanlinesize = ((size_t)width)*components*sizeof(float);
- in->read_image(TypeDesc::FLOAT,
+ size_t scanlinesize = ((size_t)width)*components*sizeof(StorageType);
+ in->read_image(FileFormat,
(uchar*)readpixels + (height-1)*scanlinesize,
AutoStride,
-scanlinesize,
AutoStride);
}
else {
- in->read_image(TypeDesc::FLOAT, (uchar*)readpixels);
+ in->read_image(FileFormat, (uchar*)readpixels);
}
-
if(components > 4) {
size_t dimensions = ((size_t)width)*height;
for(size_t i = dimensions-1, pixel = 0; pixel < dimensions; pixel++, i--) {
@@ -606,30 +516,42 @@ bool ImageManager::file_load_float_image(Image *img, ImageDataType type, device_
pixels[i*4+1] = tmppixels[i*components+1];
pixels[i*4+0] = tmppixels[i*components+0];
}
-
tmppixels.clear();
}
-
cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
-
in->close();
delete in;
}
else {
- builtin_image_float_pixels_cb(img->filename, img->builtin_data, pixels);
+ if(FileFormat == TypeDesc::FLOAT) {
+ builtin_image_float_pixels_cb(img->filename,
+ img->builtin_data,
+ (float*)pixels);
+ }
+ else if(FileFormat == TypeDesc::UINT8) {
+ builtin_image_pixels_cb(img->filename,
+ img->builtin_data,
+ (uchar*)pixels);
+ }
+ else {
+ /* TODO(dingto): Support half for ImBuf. */
+ }
}
-
- /* Check if we actually have a float4 slot, in case components == 1, but device
- * doesn't support single channel textures. */
- if(type == IMAGE_DATA_TYPE_FLOAT4) {
+ /* Check if we actually have a float4 slot, in case components == 1,
+ * but device doesn't support single channel textures.
+ */
+ if(type == IMAGE_DATA_TYPE_FLOAT4 ||
+ type == IMAGE_DATA_TYPE_HALF4 ||
+ type == IMAGE_DATA_TYPE_BYTE4)
+ {
size_t num_pixels = ((size_t)width) * height * depth;
if(cmyk) {
/* CMYK */
for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 255;
pixels[i*4+2] = (pixels[i*4+2]*pixels[i*4+3])/255;
pixels[i*4+1] = (pixels[i*4+1]*pixels[i*4+3])/255;
pixels[i*4+0] = (pixels[i*4+0]*pixels[i*4+3])/255;
+ pixels[i*4+3] = alpha_one;
}
}
else if(components == 2) {
@@ -644,7 +566,7 @@ bool ImageManager::file_load_float_image(Image *img, ImageDataType type, device_
else if(components == 3) {
/* RGB */
for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 1.0f;
+ pixels[i*4+3] = alpha_one;
pixels[i*4+2] = pixels[i*3+2];
pixels[i*4+1] = pixels[i*3+1];
pixels[i*4+0] = pixels[i*3+0];
@@ -653,120 +575,18 @@ bool ImageManager::file_load_float_image(Image *img, ImageDataType type, device_
else if(components == 1) {
/* grayscale */
for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 1.0f;
+ pixels[i*4+3] = alpha_one;
pixels[i*4+2] = pixels[i];
pixels[i*4+1] = pixels[i];
pixels[i*4+0] = pixels[i];
}
}
-
if(img->use_alpha == false) {
for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 1.0f;
+ pixels[i*4+3] = alpha_one;
}
}
}
-
- return true;
-}
-
-template<typename T>
-bool ImageManager::file_load_half_image(Image *img, ImageDataType type, device_vector<T>& tex_img)
-{
- ImageInput *in = NULL;
- int width, height, depth, components;
-
- if(!file_load_image_generic(img, &in, width, height, depth, components))
- return false;
-
- /* read RGBA pixels */
- half *pixels = (half*)tex_img.resize(width, height, depth);
- if(pixels == NULL) {
- return false;
- }
-
- if(in) {
- half *readpixels = pixels;
- vector<half> tmppixels;
-
- if(components > 4) {
- tmppixels.resize(((size_t)width)*height*components);
- readpixels = &tmppixels[0];
- }
-
- if(depth <= 1) {
- size_t scanlinesize = ((size_t)width)*components*sizeof(half);
- in->read_image(TypeDesc::HALF,
- (uchar*)readpixels + (height-1)*scanlinesize,
- AutoStride,
- -scanlinesize,
- AutoStride);
- }
- else {
- in->read_image(TypeDesc::HALF, (uchar*)readpixels);
- }
-
- if(components > 4) {
- size_t dimensions = ((size_t)width)*height;
- for(size_t i = dimensions-1, pixel = 0; pixel < dimensions; pixel++, i--) {
- pixels[i*4+3] = tmppixels[i*components+3];
- pixels[i*4+2] = tmppixels[i*components+2];
- pixels[i*4+1] = tmppixels[i*components+1];
- pixels[i*4+0] = tmppixels[i*components+0];
- }
-
- tmppixels.clear();
- }
-
- in->close();
- delete in;
- }
-#if 0
- /* TODO(dingto): Support half for ImBuf. */
- else {
- builtin_image_float_pixels_cb(img->filename, img->builtin_data, pixels);
- }
-#endif
-
- /* Check if we actually have a half4 slot, in case components == 1, but device
- * doesn't support single channel textures. */
- if(type == IMAGE_DATA_TYPE_HALF4) {
- size_t num_pixels = ((size_t)width) * height * depth;
- if(components == 2) {
- /* grayscale + alpha */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = pixels[i*2+1];
- pixels[i*4+2] = pixels[i*2+0];
- pixels[i*4+1] = pixels[i*2+0];
- pixels[i*4+0] = pixels[i*2+0];
- }
- }
- else if(components == 3) {
- /* RGB */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 1.0f;
- pixels[i*4+2] = pixels[i*3+2];
- pixels[i*4+1] = pixels[i*3+1];
- pixels[i*4+0] = pixels[i*3+0];
- }
- }
- else if(components == 1) {
- /* grayscale */
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 1.0f;
- pixels[i*4+2] = pixels[i];
- pixels[i*4+1] = pixels[i];
- pixels[i*4+0] = pixels[i];
- }
- }
-
- if(img->use_alpha == false) {
- for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- pixels[i*4+3] = 1.0f;
- }
- }
- }
-
return true;
}
@@ -802,7 +622,7 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, ImageD
device->tex_free(tex_img);
}
- if(!file_load_float_image(img, type, tex_img)) {
+ if(!file_load_image<TypeDesc::FLOAT, float>(img, type, tex_img)) {
/* on failure to load, we set a 1x1 pixels pink image */
float *pixels = (float*)tex_img.resize(1, 1);
@@ -828,7 +648,7 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, ImageD
device->tex_free(tex_img);
}
- if(!file_load_float_image(img, type, tex_img)) {
+ if(!file_load_image<TypeDesc::FLOAT, float>(img, type, tex_img)) {
/* on failure to load, we set a 1x1 pixels pink image */
float *pixels = (float*)tex_img.resize(1, 1);
@@ -851,7 +671,7 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, ImageD
device->tex_free(tex_img);
}
- if(!file_load_byte_image(img, type, tex_img)) {
+ if(!file_load_image<TypeDesc::UINT8, uchar>(img, type, tex_img)) {
/* on failure to load, we set a 1x1 pixels pink image */
uchar *pixels = (uchar*)tex_img.resize(1, 1);
@@ -877,7 +697,7 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, ImageD
device->tex_free(tex_img);
}
- if(!file_load_byte_image(img, type, tex_img)) {
+ if(!file_load_image<TypeDesc::UINT8, uchar>(img, type, tex_img)) {
/* on failure to load, we set a 1x1 pixels pink image */
uchar *pixels = (uchar*)tex_img.resize(1, 1);
@@ -900,7 +720,7 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, ImageD
device->tex_free(tex_img);
}
- if(!file_load_half_image(img, type, tex_img)) {
+ if(!file_load_image<TypeDesc::HALF, half>(img, type, tex_img)) {
/* on failure to load, we set a 1x1 pixels pink image */
half *pixels = (half*)tex_img.resize(1, 1);
@@ -926,7 +746,7 @@ void ImageManager::device_load_image(Device *device, DeviceScene *dscene, ImageD
device->tex_free(tex_img);
}
- if(!file_load_half_image(img, type, tex_img)) {
+ if(!file_load_image<TypeDesc::HALF, half>(img, type, tex_img)) {
/* on failure to load, we set a 1x1 pixels pink image */
half *pixels = (half*)tex_img.resize(1, 1);
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index cca71a6bb93..1dc4bf180f8 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -109,14 +109,12 @@ private:
bool file_load_image_generic(Image *img, ImageInput **in, int &width, int &height, int &depth, int &components);
- template<typename T>
- bool file_load_byte_image(Image *img, ImageDataType type, device_vector<T>& tex_img);
-
- template<typename T>
- bool file_load_float_image(Image *img, ImageDataType type, device_vector<T>& tex_img);
-
- template<typename T>
- bool file_load_half_image(Image *img, ImageDataType type, device_vector<T>& tex_img);
+ template<TypeDesc::BASETYPE FileFormat,
+ typename StorageType,
+ typename DeviceType>
+ bool file_load_image(Image *img,
+ ImageDataType type,
+ device_vector<DeviceType>& tex_img);
int type_index_to_flattened_slot(int slot, ImageDataType type);
int flattened_slot_to_type_index(int flat_slot, ImageDataType *type);
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index 63914e57319..a9a33d2e789 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -65,6 +65,7 @@ NODE_DEFINE(Integrator)
SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true);
SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true);
+ SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.05f);
static NodeEnum method_enum;
method_enum.insert("path", PATH);
@@ -164,6 +165,13 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->sampling_pattern = sampling_pattern;
kintegrator->aa_samples = aa_samples;
+ if(light_sampling_threshold > 0.0f) {
+ kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold;
+ }
+ else {
+ kintegrator->light_inv_rr_threshold = 0.0f;
+ }
+
/* sobol directions table */
int max_samples = 1;
diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h
index 39eaaf246d4..17fdd0ef1db 100644
--- a/intern/cycles/render/integrator.h
+++ b/intern/cycles/render/integrator.h
@@ -64,8 +64,10 @@ public:
int mesh_light_samples;
int subsurface_samples;
int volume_samples;
+
bool sample_all_lights_direct;
bool sample_all_lights_indirect;
+ float light_sampling_threshold;
enum Method {
BRANCHED_PATH = 0,
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 777f3229ce6..2245c861d5a 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -43,8 +43,8 @@ static void shade_background_pixels(Device *device, DeviceScene *dscene, int res
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
- float u = x/(float)width;
- float v = y/(float)height;
+ float u = (x + 0.5f)/width;
+ float v = (y + 0.5f)/height;
uint4 in = make_uint4(__float_as_int(u), __float_as_int(v), 0, 0);
d_input_data[x + y*width] = in;
@@ -106,6 +106,7 @@ NODE_DEFINE(Light)
static NodeEnum type_enum;
type_enum.insert("point", LIGHT_POINT);
+ type_enum.insert("distant", LIGHT_DISTANT);
type_enum.insert("background", LIGHT_BACKGROUND);
type_enum.insert("area", LIGHT_AREA);
type_enum.insert("spot", LIGHT_SPOT);
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index 8bff0f9ed15..1db4692e171 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -89,8 +89,7 @@ public:
}
bool modified(const SessionParams& params)
- { return !(device.type == params.device.type
- && device.id == params.device.id
+ { return !(device == params.device
&& background == params.background
&& progressive_refine == params.progressive_refine
&& output_path == params.output_path
diff --git a/intern/cycles/util/util_atomic.h b/intern/cycles/util/util_atomic.h
index 1d1e2963348..433e41fbbb6 100644
--- a/intern/cycles/util/util_atomic.h
+++ b/intern/cycles/util/util_atomic.h
@@ -39,7 +39,7 @@ ATOMIC_INLINE void atomic_update_max_z(size_t *maximum_value, size_t value)
/* Float atomics implementation credits:
* http://suhorukov.blogspot.in/2011/12/opencl-11-atomic-operations-on-floating.html
*/
-ccl_device_inline void atomic_add_float(volatile ccl_global float *source,
+ccl_device_inline void atomic_add_and_fetch_float(volatile ccl_global float *source,
const float operand)
{
union {
diff --git a/intern/cycles/util/util_hash.h b/intern/cycles/util/util_hash.h
index 3ff2802b46d..98c3a681ff2 100644
--- a/intern/cycles/util/util_hash.h
+++ b/intern/cycles/util/util_hash.h
@@ -21,7 +21,7 @@
CCL_NAMESPACE_BEGIN
-static inline uint hash_int_2d(uint kx, uint ky)
+ccl_device_inline uint hash_int_2d(uint kx, uint ky)
{
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
@@ -44,11 +44,12 @@ static inline uint hash_int_2d(uint kx, uint ky)
#undef rot
}
-static inline uint hash_int(uint k)
+ccl_device_inline uint hash_int(uint k)
{
return hash_int_2d(k, 0);
}
+#ifndef __KERNEL_GPU__
static inline uint hash_string(const char *str)
{
uint i = 0, c;
@@ -58,6 +59,7 @@ static inline uint hash_string(const char *str)
return i;
}
+#endif
CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index e2abfcde702..3f4d3e06c0b 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -162,6 +162,11 @@ ccl_device_inline float max4(float a, float b, float c, float d)
return max(max(a, b), max(c, d));
}
+ccl_device_inline float max3(float3 a)
+{
+ return max(max(a.x, a.y), a.z);
+}
+
#ifndef __KERNEL_OPENCL__
ccl_device_inline int clamp(int a, int mn, int mx)
diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/util_path.cpp
index 62ef8fc0b48..5df262fcbbb 100644
--- a/intern/cycles/util/util_path.cpp
+++ b/intern/cycles/util/util_path.cpp
@@ -757,9 +757,9 @@ uint64_t path_modified_time(const string& path)
{
path_stat_t st;
if(path_stat(path, &st) != 0) {
- return st.st_mtime;
+ return 0;
}
- return 0;
+ return st.st_mtime;
}
bool path_remove(const string& path)
diff --git a/intern/cycles/util/util_stats.h b/intern/cycles/util/util_stats.h
index b970b017270..c21a8488c81 100644
--- a/intern/cycles/util/util_stats.h
+++ b/intern/cycles/util/util_stats.h
@@ -29,13 +29,13 @@ public:
explicit Stats(static_init_t) {}
void mem_alloc(size_t size) {
- atomic_add_z(&mem_used, size);
+ atomic_add_and_fetch_z(&mem_used, size);
atomic_update_max_z(&mem_peak, mem_used);
}
void mem_free(size_t size) {
assert(mem_used >= size);
- atomic_sub_z(&mem_used, size);
+ atomic_sub_and_fetch_z(&mem_used, size);
}
size_t mem_used;
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
index 1933e9d3ee3..76b7e072321 100644
--- a/intern/guardedalloc/intern/mallocn_guarded_impl.c
+++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c
@@ -505,8 +505,8 @@ static void make_memhead_header(MemHead *memh, size_t len, const char *str)
memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
memt->tag3 = MEMTAG3;
- atomic_add_u(&totblock, 1);
- atomic_add_z(&mem_in_use, len);
+ atomic_add_and_fetch_u(&totblock, 1);
+ atomic_add_and_fetch_z(&mem_in_use, len);
mem_lock_thread();
addtail(membase, &memh->next);
@@ -638,7 +638,7 @@ void *MEM_guarded_mapallocN(size_t len, const char *str)
if (memh != (MemHead *)-1) {
make_memhead_header(memh, len, str);
memh->mmap = 1;
- atomic_add_z(&mmap_in_use, len);
+ atomic_add_and_fetch_z(&mmap_in_use, len);
mem_lock_thread();
peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
mem_unlock_thread();
@@ -1007,8 +1007,8 @@ static void rem_memblock(MemHead *memh)
}
mem_unlock_thread();
- atomic_sub_u(&totblock, 1);
- atomic_sub_z(&mem_in_use, memh->len);
+ atomic_sub_and_fetch_u(&totblock, 1);
+ atomic_sub_and_fetch_z(&mem_in_use, memh->len);
#ifdef DEBUG_MEMDUPLINAME
if (memh->need_free_name)
@@ -1016,7 +1016,7 @@ static void rem_memblock(MemHead *memh)
#endif
if (memh->mmap) {
- atomic_sub_z(&mmap_in_use, memh->len);
+ atomic_sub_and_fetch_z(&mmap_in_use, memh->len);
#if defined(WIN32)
/* our windows mmap implementation is not thread safe */
mem_lock_thread();
diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
index a80d67c3e80..ce8a5b29ece 100644
--- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c
+++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -142,11 +142,11 @@ void MEM_lockfree_freeN(void *vmemh)
return;
}
- atomic_sub_u(&totblock, 1);
- atomic_sub_z(&mem_in_use, len);
+ atomic_sub_and_fetch_u(&totblock, 1);
+ atomic_sub_and_fetch_z(&mem_in_use, len);
if (MEMHEAD_IS_MMAP(memh)) {
- atomic_sub_z(&mmap_in_use, len);
+ atomic_sub_and_fetch_z(&mmap_in_use, len);
#if defined(WIN32)
/* our windows mmap implementation is not thread safe */
mem_lock_thread();
@@ -287,8 +287,8 @@ void *MEM_lockfree_callocN(size_t len, const char *str)
if (LIKELY(memh)) {
memh->len = len;
- atomic_add_u(&totblock, 1);
- atomic_add_z(&mem_in_use, len);
+ atomic_add_and_fetch_u(&totblock, 1);
+ atomic_add_and_fetch_z(&mem_in_use, len);
update_maximum(&peak_mem, mem_in_use);
return PTR_FROM_MEMHEAD(memh);
@@ -312,8 +312,8 @@ void *MEM_lockfree_mallocN(size_t len, const char *str)
}
memh->len = len;
- atomic_add_u(&totblock, 1);
- atomic_add_z(&mem_in_use, len);
+ atomic_add_and_fetch_u(&totblock, 1);
+ atomic_add_and_fetch_z(&mem_in_use, len);
update_maximum(&peak_mem, mem_in_use);
return PTR_FROM_MEMHEAD(memh);
@@ -361,8 +361,8 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str
memh->len = len | (size_t) MEMHEAD_ALIGN_FLAG;
memh->alignment = (short) alignment;
- atomic_add_u(&totblock, 1);
- atomic_add_z(&mem_in_use, len);
+ atomic_add_and_fetch_u(&totblock, 1);
+ atomic_add_and_fetch_z(&mem_in_use, len);
update_maximum(&peak_mem, mem_in_use);
return PTR_FROM_MEMHEAD(memh);
@@ -396,9 +396,9 @@ void *MEM_lockfree_mapallocN(size_t len, const char *str)
if (memh != (MemHead *)-1) {
memh->len = len | (size_t) MEMHEAD_MMAP_FLAG;
- atomic_add_u(&totblock, 1);
- atomic_add_z(&mem_in_use, len);
- atomic_add_z(&mmap_in_use, len);
+ atomic_add_and_fetch_u(&totblock, 1);
+ atomic_add_and_fetch_z(&mem_in_use, len);
+ atomic_add_and_fetch_z(&mmap_in_use, len);
update_maximum(&peak_mem, mem_in_use);
update_maximum(&peak_mem, mmap_in_use);
diff --git a/intern/iksolver/intern/IK_QSegment.h b/intern/iksolver/intern/IK_QSegment.h
index 74f157aa763..247807dc5e0 100644
--- a/intern/iksolver/intern/IK_QSegment.h
+++ b/intern/iksolver/intern/IK_QSegment.h
@@ -60,6 +60,7 @@
class IK_QSegment
{
public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
virtual ~IK_QSegment();
// start: a user defined translation
diff --git a/intern/iksolver/intern/IK_Solver.cpp b/intern/iksolver/intern/IK_Solver.cpp
index cefb8c7ed7b..a00db4fa2f5 100644
--- a/intern/iksolver/intern/IK_Solver.cpp
+++ b/intern/iksolver/intern/IK_Solver.cpp
@@ -42,6 +42,7 @@ using namespace std;
class IK_QSolver {
public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
IK_QSolver() : root(NULL) {
}
diff --git a/intern/libmv/libmv/simple_pipeline/modal_solver_test.cc b/intern/libmv/libmv/simple_pipeline/modal_solver_test.cc
index 8b87acd95bb..b4cae8defb2 100644
--- a/intern/libmv/libmv/simple_pipeline/modal_solver_test.cc
+++ b/intern/libmv/libmv/simple_pipeline/modal_solver_test.cc
@@ -65,9 +65,9 @@ TEST(ModalSolver, SyntheticCubeSceneMotion) {
NULL);
Mat3 expected_rotation;
- expected_rotation << 0.98215101299251, 0.17798357184544, 0.06083778292258,
- -0.16875286001759, 0.97665299913606, -0.13293378620359,
- -0.08307743323957, 0.12029450291547, 0.98925596922871;
+ expected_rotation << 0.98215101743472, 0.17798354937546, 0.06083777694542,
+ -0.16875283983360, 0.97665300495333, -0.13293376908719,
+ -0.08307742172243, 0.12029448893171, 0.98925597189636;
Mat3 &first_camera_R = reconstruction.CameraForImage(1)->R;
Mat3 &second_camera_R = reconstruction.CameraForImage(2)->R;
diff --git a/make.bat b/make.bat
index c7f2dbbf369..74148e5599d 100644
--- a/make.bat
+++ b/make.bat
@@ -4,6 +4,11 @@ REM This is for users who like to configure & build Blender with a single comman
setlocal ENABLEEXTENSIONS
set BLENDER_DIR=%~dp0
+set BLENDER_DIR_NOSPACES=%BLENDER_DIR: =%
+if not "%BLENDER_DIR%"=="%BLENDER_DIR_NOSPACES%" (
+ echo There are spaces detected in the build path "%BLENDER_DIR%", this is currently not supported, exiting....
+ goto EOF
+)
set BUILD_DIR=%BLENDER_DIR%..\build_windows
set BUILD_TYPE=Release
rem reset all variables so they do not get accidentally get carried over from previous builds
diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py
index be74f8dbc0e..58eab5436e6 100644
--- a/release/scripts/startup/bl_operators/mesh.py
+++ b/release/scripts/startup/bl_operators/mesh.py
@@ -198,3 +198,53 @@ class MeshSelectPrev(Operator):
bmesh.update_edit_mesh(me, False)
return {'FINISHED'}
+
+
+# XXX This is hackish (going forth and back from Object mode...), to be redone once we have proper support of
+# custom normals in BMesh/edit mode.
+class MehsSetNormalsFromFaces(Operator):
+ """Set the custom vertex normals from the selected faces ones"""
+ bl_idname = "mesh.set_normals_from_faces"
+ bl_label = "Set Normals From Faces"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.mode == 'EDIT_MESH' and context.edit_object.data.polygons)
+
+ def execute(self, context):
+ import mathutils
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ obj = context.active_object
+ me = obj.data
+
+ v2nors = {}
+ for p in me.polygons:
+ if not p.select:
+ continue
+ for lidx, vidx in zip(p.loop_indices, p.vertices):
+ assert(me.loops[lidx].vertex_index == vidx)
+ v2nors.setdefault(vidx, []).append(p.normal)
+
+ for nors in v2nors.values():
+ nors[:] = [sum(nors, mathutils.Vector((0, 0, 0))).normalized()]
+
+ if not me.has_custom_normals:
+ me.create_normals_split()
+ me.calc_normals_split()
+
+ normals = []
+ for l in me.loops:
+ nor = v2nors.get(l.vertex_index, [None])[0]
+ if nor is None:
+ nor = l.normal
+ normals.append(nor.to_tuple())
+
+ me.normals_split_custom_set(normals)
+
+ me.free_normals_split()
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ return {'FINISHED'}
+
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 1c97d213e05..68a25acc2db 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -935,16 +935,23 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
# detect if this is a inherited member and use that name instead
rna_parent = rna_class.bl_rna
- rna_prop = rna_parent.properties[class_prop]
- rna_parent = rna_parent.base
- while rna_parent and rna_prop == rna_parent.properties.get(class_prop):
- class_name = rna_parent.identifier
+ rna_prop = rna_parent.properties.get(class_prop)
+ if rna_prop:
rna_parent = rna_parent.base
+ while rna_parent and rna_prop == rna_parent.properties.get(class_prop):
+ class_name = rna_parent.identifier
+ rna_parent = rna_parent.base
- if do_url:
- url = ("%s/bpy.types.%s.html#bpy.types.%s.%s" % (url_prefix, class_name, class_name, class_prop))
+ if do_url:
+ url = ("%s/bpy.types.%s.html#bpy.types.%s.%s" % (url_prefix, class_name, class_name, class_prop))
+ else:
+ rna = ("bpy.types.%s.%s" % (class_name, class_prop))
else:
- rna = ("bpy.types.%s.%s" % (class_name, class_prop))
+ # We assume this is custom property, only try to generate generic url/rna_id...
+ if do_url:
+ url = ("%s/bpy.types.bpy_struct.html#bpy.types.bpy_struct.items" % (url_prefix,))
+ else:
+ rna = "bpy.types.bpy_struct"
return url if do_url else rna
@@ -2163,3 +2170,32 @@ class WM_OT_addon_expand(Operator):
info["show_expanded"] = not info["show_expanded"]
return {'FINISHED'}
+
+class WM_OT_addon_userpref_show(Operator):
+ "Show add-on user preferences"
+ bl_idname = "wm.addon_userpref_show"
+ bl_label = ""
+ bl_options = {'INTERNAL'}
+
+ module = StringProperty(
+ name="Module",
+ description="Module name of the add-on to expand",
+ )
+
+ def execute(self, context):
+ import addon_utils
+
+ module_name = self.module
+
+ modules = addon_utils.modules(refresh=False)
+ mod = addon_utils.addons_fake_modules.get(module_name)
+ if mod is not None:
+ info = addon_utils.module_bl_info(mod)
+ info["show_expanded"] = True
+
+ bpy.context.user_preferences.active_section = 'ADDONS'
+ context.window_manager.addon_filter = 'All'
+ context.window_manager.addon_search = info["name"]
+ bpy.ops.screen.userpref_show('INVOKE_DEFAULT')
+
+ return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 5416735494b..59907692fe0 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -38,6 +38,7 @@ class MESH_MT_vertex_group_specials(Menu):
layout.operator("object.vertex_group_mirror", text="Mirror Vertex Group (Topology)", icon='ARROW_LEFTRIGHT').use_topology = True
layout.operator("object.vertex_group_remove_from", icon='X', text="Remove from All Groups").use_all_groups = True
layout.operator("object.vertex_group_remove_from", icon='X', text="Clear Active Group").use_all_verts = True
+ layout.operator("object.vertex_group_remove", icon='X', text="Delete All Unlocked Groups").all_unlocked = True
layout.operator("object.vertex_group_remove", icon='X', text="Delete All Groups").all = True
layout.separator()
layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All").action = 'LOCK'
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 bc40932018d..08e07b8ed93 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -61,6 +61,9 @@ def gpencil_stroke_placement_settings(context, layout):
def gpencil_active_brush_settings_simple(context, layout):
brush = context.active_gpencil_brush
+ if brush is None:
+ layout.label("No Active Brush")
+ return
col = layout.column()
col.label("Active Brush: ")
diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py
index 38c97746f4a..9d4f51b256b 100644
--- a/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py
@@ -205,30 +205,60 @@ class PHYSICS_PT_rigid_body_constraint(PHYSICS_PT_rigidbody_constraint_panel, Pa
row = col.row(align=True)
sub = row.row(align=True)
- sub.scale_x = 0.1
- sub.prop(rbc, "use_spring_x", toggle=True, text="X")
+ sub.scale_x = 0.5
+ sub.prop(rbc, "use_spring_x", toggle=True, text="X Axis")
sub = row.row(align=True)
sub.active = rbc.use_spring_x
sub.prop(rbc, "spring_stiffness_x", text="Stiffness")
- sub.prop(rbc, "spring_damping_x")
+ sub.prop(rbc, "spring_damping_x", text="Damping")
row = col.row(align=True)
sub = row.row(align=True)
- sub.scale_x = 0.1
- sub.prop(rbc, "use_spring_y", toggle=True, text="Y")
+ sub.scale_x = 0.5
+ sub.prop(rbc, "use_spring_y", toggle=True, text="Y Axis")
sub = row.row(align=True)
sub.active = rbc.use_spring_y
sub.prop(rbc, "spring_stiffness_y", text="Stiffness")
- sub.prop(rbc, "spring_damping_y")
+ sub.prop(rbc, "spring_damping_y", text="Damping")
row = col.row(align=True)
sub = row.row(align=True)
- sub.scale_x = 0.1
- sub.prop(rbc, "use_spring_z", toggle=True, text="Z")
+ sub.scale_x = 0.5
+ sub.prop(rbc, "use_spring_z", toggle=True, text="Z Axis")
sub = row.row(align=True)
sub.active = rbc.use_spring_z
sub.prop(rbc, "spring_stiffness_z", text="Stiffness")
- sub.prop(rbc, "spring_damping_z")
+ sub.prop(rbc, "spring_damping_z", text="Damping")
+
+ col = layout.column(align=True)
+
+ row = col.row(align=True)
+ sub = row.row(align=True)
+ sub.scale_x = 0.5
+ sub.prop(rbc, "use_spring_ang_x", toggle=True, text="X Angle")
+ sub = row.row(align=True)
+ sub.active = rbc.use_spring_ang_x
+ sub.prop(rbc, "spring_stiffness_ang_x", text="Stiffness")
+ sub.prop(rbc, "spring_damping_ang_x", text="Damping")
+
+ row = col.row(align=True)
+ sub = row.row(align=True)
+ sub.scale_x = 0.5
+ sub.prop(rbc, "use_spring_ang_y", toggle=True, text="Y Angle")
+ sub = row.row(align=True)
+ sub.active = rbc.use_spring_ang_y
+ sub.prop(rbc, "spring_stiffness_ang_y", text="Stiffness")
+ sub.prop(rbc, "spring_damping_ang_y", text="Damping")
+
+ row = col.row(align=True)
+ sub = row.row(align=True)
+ sub.scale_x = 0.5
+ sub.prop(rbc, "use_spring_ang_z", toggle=True, text="Z Angle")
+ sub = row.row(align=True)
+ sub.active = rbc.use_spring_ang_z
+ sub.prop(rbc, "spring_stiffness_ang_z", text="Stiffness")
+ sub.prop(rbc, "spring_damping_ang_z", text="Damping")
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index df03f23a4b5..0374d032141 100644
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -397,6 +397,14 @@ class PHYSICS_PT_smoke_display_settings(PhysicButtonsPanel, Panel):
col.prop(domain, "vector_draw_type")
col.prop(domain, "vector_scale")
+ layout.separator()
+ layout.label(text="Color Mapping:")
+ layout.prop(domain, "use_color_ramp")
+ col = layout.column();
+ col.enabled = domain.use_color_ramp
+ col.prop(domain, "coba_field")
+ col.template_color_ramp(domain, "color_ramp", expand=True)
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index b6087184518..04b4cef9512 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -259,6 +259,20 @@ class IMAGE_MT_uvs_showhide(Menu):
layout.operator("uv.hide", text="Hide Unselected").unselected = True
+class IMAGE_MT_uvs_proportional(Menu):
+ bl_label = "Proportional Editing"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.props_enum(context.tool_settings, "proportional_edit")
+
+ layout.separator()
+
+ layout.label("Falloff:")
+ layout.props_enum(context.tool_settings, "proportional_edit_falloff")
+
+
class IMAGE_MT_uvs_transform(Menu):
bl_label = "Transform"
@@ -360,8 +374,7 @@ class IMAGE_MT_uvs(Menu):
layout.separator()
- layout.prop_menu_enum(toolsettings, "proportional_edit")
- layout.prop_menu_enum(toolsettings, "proportional_edit_falloff")
+ layout.menu("IMAGE_MT_uvs_proportional")
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index dcafac66fca..ab3ec3559e5 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -429,13 +429,6 @@ class USERPREF_PT_system(Panel):
col.separator()
- if hasattr(system, "compute_device_type"):
- col.label(text="Compute Device:")
- col.row().prop(system, "compute_device_type", expand=True)
- sub = col.row()
- sub.active = system.compute_device_type != 'CPU'
- sub.prop(system, "compute_device", text="")
-
if hasattr(system, "opensubdiv_compute_type"):
col.label(text="OpenSubdiv compute:")
col.row().prop(system, "opensubdiv_compute_type", text="")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 8a3a5d3b7e8..9de9376312c 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -710,6 +710,7 @@ class VIEW3D_MT_select_particle(Menu):
layout = self.layout
layout.operator("view3d.select_border")
+ layout.operator("view3d.select_circle")
layout.separator()
@@ -2477,6 +2478,10 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
layout.menu("VIEW3D_MT_vertex_group")
layout.menu("VIEW3D_MT_hook")
+ layout.separator()
+
+ layout.operator("object.vertex_parent_set")
+
class VIEW3D_MT_edit_mesh_edges(Menu):
bl_label = "Edges"
@@ -2731,6 +2736,10 @@ class VIEW3D_MT_edit_curve_ctrlpoints(Menu):
layout.menu("VIEW3D_MT_hook")
+ layout.separator()
+
+ layout.operator("object.vertex_parent_set")
+
class VIEW3D_MT_edit_curve_segments(Menu):
bl_label = "Segments"
@@ -2893,6 +2902,10 @@ class VIEW3D_MT_edit_lattice(Menu):
layout.separator()
+ layout.operator("object.vertex_parent_set")
+
+ layout.separator()
+
layout.menu("VIEW3D_MT_edit_proportional")
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 8019c8d2f34..3eb76a3b0f9 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -431,6 +431,7 @@ class VIEW3D_PT_tools_shading(View3DPanel, Panel):
col.label(text="Normals:")
col.operator("mesh.normals_make_consistent", text="Recalculate")
col.operator("mesh.flip_normals", text="Flip Direction")
+ col.operator("mesh.set_normals_from_faces", text="Set From Faces")
class VIEW3D_PT_tools_uvs(View3DPanel, Panel):
@@ -1559,7 +1560,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
sub.active = (brush and brush.sculpt_tool != 'MASK')
if (sculpt.detail_type_method == 'CONSTANT'):
row = sub.row(align=True)
- row.prop(sculpt, "constant_detail")
+ row.prop(sculpt, "constant_detail_resolution")
row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER')
elif (sculpt.detail_type_method == 'BRUSH'):
sub.prop(sculpt, "detail_percent")
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index c2323100205..78d6f6c7cb9 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -97,6 +97,7 @@ void BKE_armature_where_is(struct bArmature *arm);
void BKE_armature_where_is_bone(struct Bone *bone, struct Bone *prevbone, const bool use_recursion);
void BKE_pose_clear_pointers(struct bPose *pose);
void BKE_pose_rebuild(struct Object *ob, struct bArmature *arm);
+void BKE_pose_rebuild_ex(struct Object *ob, struct bArmature *arm, const bool sort_bones);
void BKE_pose_where_is(struct Scene *scene, struct Object *ob);
void BKE_pose_where_is_bone(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime, bool do_extra);
void BKE_pose_where_is_bone_tail(struct bPoseChannel *pchan);
diff --git a/source/blender/blenkernel/BKE_blender_undo.h b/source/blender/blenkernel/BKE_blender_undo.h
index 9547eeb9838..84a6d07be7d 100644
--- a/source/blender/blenkernel/BKE_blender_undo.h
+++ b/source/blender/blenkernel/BKE_blender_undo.h
@@ -42,6 +42,7 @@ extern bool BKE_undo_is_valid(const char *name);
extern void BKE_undo_reset(void);
extern void BKE_undo_number(struct bContext *C, int nr);
extern const char *BKE_undo_get_name(int nr, bool *r_active);
+extern const char *BKE_undo_get_name_last(void);
extern bool BKE_undo_save_file(const char *filename);
extern struct Main *BKE_undo_get_main(struct Scene **r_scene);
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 55142510f9e..baf8510dd0d 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -28,7 +28,7 @@
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
#define BLENDER_VERSION 278
-#define BLENDER_SUBVERSION 1
+#define BLENDER_SUBVERSION 3
/* Several breakages with 270, e.g. constraint deg vs rad */
#define BLENDER_MINVERSION 270
#define BLENDER_MINSUBVERSION 6
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index 0d82de09165..855eb10976c 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -39,6 +39,7 @@ extern "C" {
#include "BLI_compiler_attrs.h"
struct BlendThumbnail;
+struct GHash;
struct ListBase;
struct ID;
struct ImBuf;
@@ -64,7 +65,7 @@ struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_
/* library_remap.c (keep here since they're general functions) */
void BKE_libblock_free(struct Main *bmain, void *idv) ATTR_NONNULL();
-void BKE_libblock_free_ex(struct Main *bmain, void *idv, const bool do_id_user) ATTR_NONNULL();
+void BKE_libblock_free_ex(struct Main *bmain, void *idv, const bool do_id_user, const bool do_ui_user) ATTR_NONNULL();
void BKE_libblock_free_us(struct Main *bmain, void *idv) ATTR_NONNULL();
void BKE_libblock_free_data(struct Main *bmain, struct ID *id) ATTR_NONNULL();
void BKE_libblock_delete(struct Main *bmain, void *idv) ATTR_NONNULL();
@@ -125,8 +126,11 @@ void BKE_id_ui_prefix(char name[66 + 1], const struct ID *id);
void BKE_library_free(struct Library *lib);
void BKE_library_make_local(
- struct Main *bmain, const struct Library *lib, const bool untagged_only, const bool set_fake);
+ struct Main *bmain, const struct Library *lib, struct GHash *old_to_new_ids,
+ const bool untagged_only, const bool set_fake);
+void BKE_id_tag_set_atomic(struct ID *id, int tag);
+void BKE_id_tag_clear_atomic(struct ID *id, int tag);
/* use when "" is given to new_id() */
#define ID_FALLBACK_NAME N_("Untitled")
diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h
index c6b63754b57..a7470107c24 100644
--- a/source/blender/blenkernel/BKE_library_query.h
+++ b/source/blender/blenkernel/BKE_library_query.h
@@ -88,6 +88,7 @@ bool BKE_library_ID_is_locally_used(struct Main *bmain, void *idv);
bool BKE_library_ID_is_indirectly_used(struct Main *bmain, void *idv);
void BKE_library_ID_test_usages(struct Main *bmain, void *idv, bool *is_used_local, bool *is_used_linked);
-void BKE_library_tag_unused_linked_data(struct Main *bmain, const bool do_init_tag);
+void BKE_library_unused_linked_data_set_tag(struct Main *bmain, const bool do_init_tag);
+void BKE_library_indirectly_used_data_tag_clear(struct Main *bmain);
#endif /* __BKE_LIBRARY_QUERY_H__ */
diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h
index a0a885c2a04..19a2220006a 100644
--- a/source/blender/blenkernel/BKE_object_deform.h
+++ b/source/blender/blenkernel/BKE_object_deform.h
@@ -51,9 +51,11 @@ bool BKE_object_defgroup_clear(struct Object *ob, struct bDeformGroup *dg, const
bool BKE_object_defgroup_clear_all(struct Object *ob, const bool use_selection);
void BKE_object_defgroup_remove(struct Object *ob, struct bDeformGroup *defgroup);
+void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked);
void BKE_object_defgroup_remove_all(struct Object *ob);
+
/* Select helpers */
enum eVGroupSelect;
bool *BKE_object_defgroup_subset_from_select_type(
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 470098f8c7c..dcbb667adca 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -433,8 +433,8 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
chan->scaleIn = chan->scaleOut = 1.0f;
- chan->limitmin[0] = chan->limitmin[1] = chan->limitmin[2] = -180.0f;
- chan->limitmax[0] = chan->limitmax[1] = chan->limitmax[2] = 180.0f;
+ chan->limitmin[0] = chan->limitmin[1] = chan->limitmin[2] = -M_PI;
+ chan->limitmax[0] = chan->limitmax[1] = chan->limitmax[2] = M_PI;
chan->stiffness[0] = chan->stiffness[1] = chan->stiffness[2] = 0.0f;
chan->ikrotweight = chan->iklinweight = 0.0f;
unit_m4(chan->constinv);
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index e3764adb969..a5abc6beff8 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1657,7 +1657,7 @@ static bool animsys_write_rna_setting(PathResolvedRNA *anim_rna, const float val
/* for cases like duplifarmes it's only a temporary so don't
* notify anyone of updates */
if (!(id->tag & LIB_TAG_ANIM_NO_RECALC)) {
- id->tag |= LIB_TAG_ID_RECALC;
+ BKE_id_tag_set_atomic(id, LIB_TAG_ID_RECALC);
DAG_id_type_tag(G.main, GS(id->name));
}
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index c644fe09364..2b333941c6e 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -1794,6 +1794,7 @@ static void pose_proxy_synchronize(Object *ob, Object *from, int layer_protected
/* copy posechannel to temp, but restore important pointers */
pchanw = *pchanp;
+ pchanw.bone = pchan->bone;
pchanw.prev = pchan->prev;
pchanw.next = pchan->next;
pchanw.parent = pchan->parent;
@@ -1916,7 +1917,7 @@ void BKE_pose_clear_pointers(bPose *pose)
/* only after leave editmode, duplicating, validating older files, library syncing */
/* NOTE: pose->flag is set for it */
-void BKE_pose_rebuild(Object *ob, bArmature *arm)
+void BKE_pose_rebuild_ex(Object *ob, bArmature *arm, const bool sort_bones)
{
Bone *bone;
bPose *pose;
@@ -1963,8 +1964,9 @@ void BKE_pose_rebuild(Object *ob, bArmature *arm)
#ifdef WITH_LEGACY_DEPSGRAPH
/* the sorting */
/* Sorting for new dependnecy graph is done on the scene graph level. */
- if (counter > 1)
+ if (counter > 1 && sort_bones) {
DAG_pose_sort(ob);
+ }
#endif
ob->pose->flag &= ~POSE_RECALC;
@@ -1973,6 +1975,11 @@ void BKE_pose_rebuild(Object *ob, bArmature *arm)
BKE_pose_channels_hash_make(ob->pose);
}
+void BKE_pose_rebuild(Object *ob, bArmature *arm)
+{
+ BKE_pose_rebuild_ex(ob, arm, true);
+}
+
/* ********************** THE POSE SOLVER ******************* */
/* loc/rot/size to given mat4 */
diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c
index a4c28121040..e57524af546 100644
--- a/source/blender/blenkernel/intern/blender_copybuffer.c
+++ b/source/blender/blenkernel/intern/blender_copybuffer.c
@@ -101,7 +101,7 @@ bool BKE_copybuffer_read(Main *bmain_dst, const char *libname, ReportList *repor
IMB_colormanagement_check_file_config(bmain_dst);
/* Append, rather than linking. */
Library *lib = BLI_findstring(&bmain_dst->library, libname, offsetof(Library, filepath));
- BKE_library_make_local(bmain_dst, lib, true, false);
+ 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.
*/
@@ -150,7 +150,7 @@ bool BKE_copybuffer_paste(bContext *C, const char *libname, const short flag, Re
/* append, rather than linking */
lib = BLI_findstring(&bmain->library, libname, offsetof(Library, filepath));
- BKE_library_make_local(bmain, lib, true, false);
+ BKE_library_make_local(bmain, lib, NULL, true, false);
/* important we unset, otherwise these object wont
* link into other scenes from this blend file */
diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c
index d64bf7ecf43..ce6d29bbfee 100644
--- a/source/blender/blenkernel/intern/blender_undo.c
+++ b/source/blender/blenkernel/intern/blender_undo.c
@@ -319,6 +319,13 @@ const char *BKE_undo_get_name(int nr, bool *r_active)
return NULL;
}
+/* return the name of the last item */
+const char *BKE_undo_get_name_last()
+{
+ UndoElem *uel = undobase.last;
+ return (uel ? uel->name : NULL);
+}
+
/**
* Saves .blend using undo buffer.
*
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 6da68470ecc..54f709a1e5b 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -407,9 +407,9 @@ bool BKE_blendfile_read_from_memfile(
if (bfd) {
/* remove the unused screens and wm */
while (bfd->main->wm.first)
- BKE_libblock_free_ex(bfd->main, bfd->main->wm.first, true);
+ BKE_libblock_free_ex(bfd->main, bfd->main->wm.first, true, true);
while (bfd->main->screen.first)
- BKE_libblock_free_ex(bfd->main, bfd->main->screen.first, true);
+ BKE_libblock_free_ex(bfd->main, bfd->main->screen.first, true, true);
setup_app_data(C, bfd, "<memory1>", reports);
}
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index 6a08673144e..deeb35bd880 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -93,7 +93,9 @@ void BKE_cachefile_free(CacheFile *cache_file)
ABC_free_handle(cache_file->handle);
#endif
- BLI_mutex_free(cache_file->handle_mutex);
+ if (cache_file->handle_mutex) {
+ BLI_mutex_free(cache_file->handle_mutex);
+ }
BLI_freelistN(&cache_file->object_paths);
}
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 02ae123a71e..50f8423bbff 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -3284,7 +3284,7 @@ void DAG_threaded_update_handle_node_updated(void *node_v,
for (itA = node->child; itA; itA = itA->next) {
DagNode *child_node = itA->node;
if (child_node != node) {
- atomic_sub_uint32(&child_node->num_pending_parents, 1);
+ atomic_sub_and_fetch_uint32(&child_node->num_pending_parents, 1);
if (child_node->num_pending_parents == 0) {
bool need_schedule;
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index c7399047ed5..66070923153 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -2264,7 +2264,7 @@ static void dynamic_paint_create_uv_surface_neighbor_cb(void *userdata, const in
* to non--1 *before* its tri_index is set (i.e. that it cannot be used a neighbour).
*/
tPoint->neighbour_pixel = ind - 1;
- atomic_add_uint32(&tPoint->neighbour_pixel, 1);
+ atomic_add_and_fetch_uint32(&tPoint->neighbour_pixel, 1);
tPoint->tri_index = i;
/* Now calculate pixel data for this pixel as it was on polygon surface */
@@ -2289,7 +2289,7 @@ static void dynamic_paint_create_uv_surface_neighbor_cb(void *userdata, const in
/* Increase the final number of active surface points if relevant. */
if (tPoint->tri_index != -1)
- atomic_add_uint32(active_points, 1);
+ atomic_add_and_fetch_uint32(active_points, 1);
}
}
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 14612151a8e..3411eae22e1 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -73,6 +73,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_memarena.h"
@@ -128,6 +129,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+#include "atomic_ops.h"
+
/* GS reads the memory pointed at in a specific ordering.
* only use this definition, makes little and big endian systems
* work fine, in conjunction with MAKE_ID */
@@ -261,9 +264,12 @@ void id_fake_user_clear(ID *id)
}
static int id_expand_local_callback(
- void *UNUSED(user_data), struct ID *UNUSED(id_self), struct ID **id_pointer, int UNUSED(cd_flag))
+ void *UNUSED(user_data), struct ID *id_self, struct ID **id_pointer, int UNUSED(cd_flag))
{
- if (*id_pointer) {
+ /* Can hapen that we get unlinkable ID here, e.g. with shapekey referring to itself (through drivers)...
+ * Just skip it, shape key can only be either indirectly linked, or fully local, period.
+ * And let's curse one more time that stupid useless shapekey ID type! */
+ if (*id_pointer && *id_pointer != id_self && BKE_idcode_is_linkable(GS((*id_pointer)->name))) {
id_lib_extern(*id_pointer);
}
@@ -1205,46 +1211,46 @@ void BKE_main_free(Main *mainvar)
while ( (id = lb->first) ) {
#if 1
- BKE_libblock_free_ex(mainvar, id, false);
+ BKE_libblock_free_ex(mainvar, id, false, false);
#else
/* errors freeing ID's can be hard to track down,
* enable this so valgrind will give the line number in its error log */
switch (a) {
- case 0: BKE_libblock_free_ex(mainvar, id, false); break;
- case 1: BKE_libblock_free_ex(mainvar, id, false); break;
- case 2: BKE_libblock_free_ex(mainvar, id, false); break;
- case 3: BKE_libblock_free_ex(mainvar, id, false); break;
- case 4: BKE_libblock_free_ex(mainvar, id, false); break;
- case 5: BKE_libblock_free_ex(mainvar, id, false); break;
- case 6: BKE_libblock_free_ex(mainvar, id, false); break;
- case 7: BKE_libblock_free_ex(mainvar, id, false); break;
- case 8: BKE_libblock_free_ex(mainvar, id, false); break;
- case 9: BKE_libblock_free_ex(mainvar, id, false); break;
- case 10: BKE_libblock_free_ex(mainvar, id, false); break;
- case 11: BKE_libblock_free_ex(mainvar, id, false); break;
- case 12: BKE_libblock_free_ex(mainvar, id, false); break;
- case 13: BKE_libblock_free_ex(mainvar, id, false); break;
- case 14: BKE_libblock_free_ex(mainvar, id, false); break;
- case 15: BKE_libblock_free_ex(mainvar, id, false); break;
- case 16: BKE_libblock_free_ex(mainvar, id, false); break;
- case 17: BKE_libblock_free_ex(mainvar, id, false); break;
- case 18: BKE_libblock_free_ex(mainvar, id, false); break;
- case 19: BKE_libblock_free_ex(mainvar, id, false); break;
- case 20: BKE_libblock_free_ex(mainvar, id, false); break;
- case 21: BKE_libblock_free_ex(mainvar, id, false); break;
- case 22: BKE_libblock_free_ex(mainvar, id, false); break;
- case 23: BKE_libblock_free_ex(mainvar, id, false); break;
- case 24: BKE_libblock_free_ex(mainvar, id, false); break;
- case 25: BKE_libblock_free_ex(mainvar, id, false); break;
- case 26: BKE_libblock_free_ex(mainvar, id, false); break;
- case 27: BKE_libblock_free_ex(mainvar, id, false); break;
- case 28: BKE_libblock_free_ex(mainvar, id, false); break;
- case 29: BKE_libblock_free_ex(mainvar, id, false); break;
- case 30: BKE_libblock_free_ex(mainvar, id, false); break;
- case 31: BKE_libblock_free_ex(mainvar, id, false); break;
- case 32: BKE_libblock_free_ex(mainvar, id, false); break;
- case 33: BKE_libblock_free_ex(mainvar, id, false); break;
- case 34: BKE_libblock_free_ex(mainvar, id, false); break;
+ case 0: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 1: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 2: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 3: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 4: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 5: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 6: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 7: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 8: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 9: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 10: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 11: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 12: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 13: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 14: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 15: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 16: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 17: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 18: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 19: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 20: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 21: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 22: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 23: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 24: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 25: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 26: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 27: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 28: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 29: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 30: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 31: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 32: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 33: BKE_libblock_free_ex(mainvar, id, false, false); break;
+ case 34: BKE_libblock_free_ex(mainvar, id, false, false); break;
default:
BLI_assert(0);
break;
@@ -1626,30 +1632,28 @@ void BKE_main_id_clear_newpoins(Main *bmain)
* \param untagged_only If true, only make local datablocks not tagged with LIB_TAG_PRE_EXISTING.
* \param set_fake If true, set fake user on all localized datablocks (except group and objects ones).
*/
-/* XXX TODO This function should probably be reworked.
- *
- * Old (2.77) version was simply making (tagging) datablocks as local, without actually making any check whether
+/* Note: Old (2.77) version was simply making (tagging) datablocks as local, without actually making any check whether
* they were also indirectly used or not...
*
- * Current version uses regular id_make_local callback, but this is not super-efficient since this ends up
+ * Current version uses regular id_make_local callback, which is not super-efficient since this ends up
* duplicating some IDs and then removing original ones (due to missing knowledge of which ID uses some other ID).
*
- * We could first check all IDs and detect those to be made local that are only used by other local or future-local
- * datablocks, and directly tag those as local (instead of going through id_make_local) maybe...
- *
- * We'll probably need at some point a true dependency graph between datablocks, but for now this should work
- * good enough (performances is not a critical point here anyway).
+ * However, we now have a first check that allows us to use 'direct localization' of a lot of IDs, so performances
+ * are now *reasonably* OK.
*/
-void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged_only, const bool set_fake)
+void BKE_library_make_local(
+ Main *bmain, const Library *lib, GHash *old_to_new_ids, const bool untagged_only, const bool set_fake)
{
ListBase *lbarray[MAX_LIBARRAY];
- ID *id, *id_next;
+ ID *id;
int a;
+ LinkNode *todo_ids = NULL;
LinkNode *copied_ids = NULL;
LinkNode *linked_loop_candidates = NULL;
- MemArena *linklist_mem = BLI_memarena_new(256 * sizeof(copied_ids), __func__);
+ MemArena *linklist_mem = BLI_memarena_new(512 * sizeof(*todo_ids), __func__);
+ /* Step 1: Detect datablocks to make local. */
for (a = set_listbasepointers(bmain, lbarray); a--; ) {
id = lbarray[a]->first;
@@ -1657,54 +1661,80 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
* by real datablocks responsible of them. */
const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name)));
- for (; id; id = id_next) {
+ for (; id; id = id->next) {
id->newid = NULL;
id->tag &= ~LIB_TAG_DOIT;
- id_next = id->next; /* id is possibly being inserted again */
- /* The check on the second line (LIB_TAG_PRE_EXISTING) is done so its
+ if (id->lib == NULL) {
+ id->tag &= ~(LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW);
+ }
+ /* The check on the fourth line (LIB_TAG_PRE_EXISTING) is done so its
* possible to tag data you don't want to be made local, used for
* appending data, so any libdata already linked wont become local
- * (very nasty to discover all your links are lost after appending)
- * */
- if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) &&
- ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING)))
+ * (very nasty to discover all your links are lost after appending).
+ * Also, never ever make proxified objects local, would not make any sense. */
+ else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) &&
+ ELEM(lib, NULL, id->lib) &&
+ !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) &&
+ ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING)))
{
- if (lib == NULL || id->lib == lib) {
- if (id->lib) {
- /* In this specific case, we do want to make ID local even if it has no local usage yet... */
- if (GS(id->name) == ID_OB) {
- /* Special case for objects because we don't want proxy pointers to be
- * cleared yet. This will happen down the road in this function.
- */
- BKE_object_make_local_ex(bmain, (Object*)id, true, false);
- }
- else {
- id_make_local(bmain, id, false, true);
- }
-
- if (id->newid) {
- BLI_linklist_prepend_arena(&copied_ids, id, linklist_mem);
- }
- }
- else {
- id->tag &= ~(LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW);
- }
- }
+ BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem);
+ id->tag |= LIB_TAG_DOIT;
+ }
+ }
+ }
- if (set_fake) {
- if (!ELEM(GS(id->name), ID_OB, ID_GR)) {
- /* do not set fake user on objects, groups (instancing) */
- id_fake_user_set(id);
- }
- }
+ /* Step 2: Check which datablocks we can directly make local (because they are only used by already, or future,
+ * local data), others will need to be duplicated and further processed later. */
+ BKE_library_indirectly_used_data_tag_clear(bmain);
+
+ /* Step 3: Make IDs local, either directly (quick and simple), or using generic process,
+ * which involves more complex checks and might instead create a local copy of original linked ID. */
+ for (LinkNode *it = todo_ids, *it_next; it; it = it_next) {
+ it_next = it->next;
+ id = it->link;
+
+ if (id->tag & LIB_TAG_DOIT) {
+ /* We know all users of this object are local or will be made fully local, even if currently there are
+ * some indirect usages. So instead of making a copy that se'll likely get rid of later, directly make
+ * that data block local. Saves a tremendous amount of time with complex scenes... */
+ id_clear_lib_data_ex(bmain, id, true);
+ BKE_id_expand_local(id);
+ id->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ /* In this specific case, we do want to make ID local even if it has no local usage yet... */
+ if (GS(id->name) == ID_OB) {
+ /* Special case for objects because we don't want proxy pointers to be
+ * cleared yet. This will happen down the road in this function.
+ */
+ BKE_object_make_local_ex(bmain, (Object*)id, true, false);
+ }
+ else {
+ id_make_local(bmain, id, false, true);
+ }
+
+ if (id->newid) {
+ /* Reuse already allocated LinkNode (transferring it from todo_ids to copied_ids). */
+ BLI_linklist_prepend_nlink(&copied_ids, id, it);
+ }
+ }
+
+ if (set_fake) {
+ if (!ELEM(GS(id->name), ID_OB, ID_GR)) {
+ /* do not set fake user on objects, groups (instancing) */
+ id_fake_user_set(id);
}
}
}
- /* We have to remap local usages of old (linked) ID to new (local) id in a second loop, as lbarray ordering is not
- * enough to ensure us we did catch all dependencies (e.g. if making local a parent object before its child...).
- * See T48907. */
+ /* At this point, we are done with directly made local IDs. Now we have to handle duplicated ones, since their
+ * remaining linked original counterpart may not be needed anymore... */
+ todo_ids = NULL;
+
+ /* Step 4: We have to remap local usages of old (linked) ID to new (local) id in a separated loop,
+ * as lbarray ordering is not enough to ensure us we did catch all dependencies
+ * (e.g. if making local a parent object before its child...). See T48907. */
for (LinkNode *it = copied_ids; it; it = it->next) {
id = it->link;
@@ -1712,9 +1742,18 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
BLI_assert(id->lib != NULL);
BKE_libblock_remap(bmain, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE);
+ if (old_to_new_ids) {
+ BLI_ghash_insert(old_to_new_ids, id, id->newid);
+ }
+
+ /* Special hack for groups... Thing is, since we can't instantiate them here, we need to ensure
+ * they remain 'alive' (only instantiation is a real group 'user'... *sigh* See T49722. */
+ if (GS(id->name) == ID_GR && (id->tag & LIB_TAG_INDIRECT) != 0) {
+ id_us_ensure_real(id->newid);
+ }
}
- /* Third step: remove datablocks that have been copied to be localized and are no more used in the end...
+ /* Step 5: remove datablocks that have been copied to be localized and are no more used in the end...
* Note that we may have to loop more than once here, to tackle dependencies between linked objects... */
bool do_loop = true;
while (do_loop) {
@@ -1761,11 +1800,6 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
}
}
- /* Special hack for groups... Thing is, since we can't instantiate them here, we need to ensure
- * they remain 'alive' (only instantiation is a real group 'user'... *sigh* See T49722. */
- else if (GS(id->name) == ID_GR && (id->tag & LIB_TAG_INDIRECT) != 0) {
- id_us_ensure_real(id->newid);
- }
if (!is_local) {
if (!is_lib) { /* Not used at all, we can free it! */
@@ -1773,7 +1807,8 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
it->link = NULL;
do_loop = true;
}
- else { /* Only used by linked data, potential candidate to ugly lib-only dependency cycles... */
+ /* Only used by linked data, potential candidate to ugly lib-only dependency cycles... */
+ else if ((id->tag & LIB_TAG_DOIT) == 0) { /* Check TAG_DOIT to avoid adding same ID several times... */
/* Note that we store the node, not directly ID pointer, that way if it->link is set to NULL
* later we can skip it in lib-dependency cycles search later. */
BLI_linklist_prepend_arena(&linked_loop_candidates, it, linklist_mem);
@@ -1791,9 +1826,9 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
}
}
- /* Fourth step: Try to find circle dependencies between indirectly-linked-only datablocks.
+ /* Step 6: Try to find circle dependencies between indirectly-linked-only datablocks.
* Those are fake 'usages' that prevent their deletion. See T49775 for nice ugly case. */
- BKE_library_tag_unused_linked_data(bmain, false);
+ BKE_library_unused_linked_data_set_tag(bmain, false);
for (LinkNode *it = linked_loop_candidates; it; it = it->next) {
if (it->link == NULL) {
continue;
@@ -1806,16 +1841,23 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
/* Note: in theory here we are only handling datablocks forming exclusive linked dependency-cycles-based
* archipelagos, so no need to check again after we have deleted one, as done in previous step. */
if (id->tag & LIB_TAG_DOIT) {
+ /* Object's deletion rely on valid ob->data, but ob->data may have already been freed here...
+ * Setting it to NULL may not be 100% correct, but should be safe and do the work. */
+ if (GS(id->name) == ID_OB) {
+ ((Object *)id)->data = NULL;
+ }
+
/* Note: *in theory* IDs tagged here are fully *outside* of file scope, totally unused, so we can
* directly wipe them out without caring about clearing their usages.
* However, this is a highly-risky presumption, and nice crasher in case something goes wrong here.
* So for 2.78a will keep the safe option, and switch to more efficient one in master later. */
-#if 0
- BKE_libblock_free_ex(bmain, id, false);
+#if 1
+ BKE_libblock_free_ex(bmain, id, false, true);
#else
BKE_libblock_unlink(bmain, id, false, false);
BKE_libblock_free(bmain, id);
#endif
+ ((LinkNode *)it->link)->link = NULL; /* Not strictly necessary, but safer (see T49903)... */
it->link = NULL;
}
}
@@ -1888,3 +1930,13 @@ void BKE_library_filepath_set(Library *lib, const char *filepath)
BLI_path_abs(lib->filepath, basepath);
}
}
+
+void BKE_id_tag_set_atomic(ID *id, int tag)
+{
+ atomic_fetch_and_or_uint32((uint32_t *)&id->tag, tag);
+}
+
+void BKE_id_tag_clear_atomic(ID *id, int tag)
+{
+ atomic_fetch_and_and_uint32((uint32_t *)&id->tag, ~tag);
+}
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c
index cec7fbacd80..fa75c906fb1 100644
--- a/source/blender/blenkernel/intern/library_query.c
+++ b/source/blender/blenkernel/intern/library_query.c
@@ -1170,8 +1170,9 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo
*is_used_linked = (iter.count_indirect != 0);
}
-
-static int foreach_libblock_tag_unused_linked_data_callback(void *user_data, ID *self_id, ID **id_p, int UNUSED(cb_flag))
+/* ***** IDs usages.checking/tagging. ***** */
+static int foreach_libblock_used_linked_data_tag_clear_cb(
+ void *user_data, ID *self_id, ID **id_p, int UNUSED(cb_flag))
{
bool *is_changed = user_data;
@@ -1206,7 +1207,7 @@ static int foreach_libblock_tag_unused_linked_data_callback(void *user_data, ID
* \param do_init_tag if \a true, all linked data are checked, if \a false, only linked datablocks already tagged with
* LIB_TAG_DOIT are checked.
*/
-void BKE_library_tag_unused_linked_data(Main *bmain, const bool do_init_tag)
+void BKE_library_unused_linked_data_set_tag(Main *bmain, const bool do_init_tag)
{
ListBase *lb_array[MAX_LIBARRAY];
@@ -1232,7 +1233,38 @@ void BKE_library_tag_unused_linked_data(Main *bmain, const bool do_init_tag)
while (i--) {
for (ID *id = lb_array[i]->first; id; id = id->next) {
- BKE_library_foreach_ID_link(id, foreach_libblock_tag_unused_linked_data_callback, &do_loop, IDWALK_NOP);
+ if (id->tag & LIB_TAG_DOIT) {
+ /* Unused ID (so far), no need to check it further. */
+ continue;
+ }
+ BKE_library_foreach_ID_link(id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_NOP);
+ }
+ }
+ }
+}
+
+/**
+ * Untag linked data blocks used by other untagged linked datablocks.
+ * Used to detect datablocks that we can forcefully make local (instead of copying them to later get rid of original):
+ * All datablocks we want to make local are tagged by caller, after this function has ran caller knows datablocks still
+ * tagged can directly be made local, since they are only used by other datablocks that will also be made fully local.
+ */
+void BKE_library_indirectly_used_data_tag_clear(Main *bmain)
+{
+ ListBase *lb_array[MAX_LIBARRAY];
+
+ bool do_loop = true;
+ while (do_loop) {
+ int i = set_listbasepointers(bmain, lb_array);
+ do_loop = false;
+
+ while (i--) {
+ for (ID *id = lb_array[i]->first; id; id = id->next) {
+ if (id->lib == NULL || id->tag & LIB_TAG_DOIT) {
+ /* Local or non-indirectly-used ID (so far), no need to check it further. */
+ continue;
+ }
+ BKE_library_foreach_ID_link(id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_NOP);
}
}
}
diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c
index b468e6436c8..40441034171 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -666,46 +666,23 @@ void BKE_libblock_relink_ex(
}
}
-static void animdata_dtar_clear_cb(ID *UNUSED(id), AnimData *adt, void *userdata)
-{
- ChannelDriver *driver;
- FCurve *fcu;
-
- /* find the driver this belongs to and update it */
- for (fcu = adt->drivers.first; fcu; fcu = fcu->next) {
- driver = fcu->driver;
-
- if (driver) {
- DriverVar *dvar;
- for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
- DRIVER_TARGETS_USED_LOOPER(dvar)
- {
- if (dtar->id == userdata)
- dtar->id = NULL;
- }
- DRIVER_TARGETS_LOOPER_END
- }
- }
- }
-}
-
-void BKE_libblock_free_data(Main *bmain, ID *id)
+void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id)
{
if (id->properties) {
IDP_FreeProperty(id->properties);
MEM_freeN(id->properties);
}
-
- /* this ID may be a driver target! */
- BKE_animdata_main_cb(bmain, animdata_dtar_clear_cb, (void *)id);
}
/**
* used in headerbuttons.c image.c mesh.c screen.c sound.c and library.c
*
* \param do_id_user: if \a true, try to release other ID's 'references' hold by \a idv.
+ * (only applies to main database)
+ * \param do_ui_user: similar to do_id_user but makes sure UI does not hold references to
+ * \a id.
*/
-void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user)
+void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const bool do_ui_user)
{
ID *id = idv;
short type = GS(id->name);
@@ -830,12 +807,14 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user)
/* avoid notifying on removed data */
BKE_main_lock(bmain);
- if (free_notifier_reference_cb) {
- free_notifier_reference_cb(id);
- }
+ if (do_ui_user) {
+ if (free_notifier_reference_cb) {
+ free_notifier_reference_cb(id);
+ }
- if (remap_editor_id_reference_cb) {
- remap_editor_id_reference_cb(id, NULL);
+ if (remap_editor_id_reference_cb) {
+ remap_editor_id_reference_cb(id, NULL);
+ }
}
BLI_remlink(lb, id);
@@ -848,7 +827,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user)
void BKE_libblock_free(Main *bmain, void *idv)
{
- BKE_libblock_free_ex(bmain, idv, true);
+ BKE_libblock_free_ex(bmain, idv, true, true);
}
void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index fa113ef5eef..a3fe73e4b11 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -58,6 +58,7 @@
#include "BLI_strict_flags.h"
+#include "atomic_ops.h"
#include "mikktspace.h"
// #define DEBUG_TIME
@@ -236,7 +237,9 @@ static void mesh_calc_normals_poly_accum_task_cb(void *userdata, const int pidx)
const float fac = saacos(-dot_v3v3(cur_edge, prev_edge));
/* accumulate */
- madd_v3_v3fl(vnors[ml[i].v], pnor, fac);
+ for (int k = 3; k--; ) {
+ atomic_add_and_fetch_fl(&vnors[ml[i].v][k], pnor[k] * fac);
+ }
prev_edge = cur_edge;
}
}
diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c
index b5f63588423..b5e1ded35bb 100644
--- a/source/blender/blenkernel/intern/object_deform.c
+++ b/source/blender/blenkernel/intern/object_deform.c
@@ -408,8 +408,9 @@ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup)
/**
* Remove all vgroups from object. Work in Object and Edit modes.
+ * When only_unlocked=true, locked vertex groups are not removed.
*/
-void BKE_object_defgroup_remove_all(Object *ob)
+void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked)
{
bDeformGroup *dg = (bDeformGroup *)ob->defbase.first;
const bool edit_mode = BKE_object_is_in_editmode_vgroup(ob);
@@ -418,10 +419,12 @@ void BKE_object_defgroup_remove_all(Object *ob)
while (dg) {
bDeformGroup *next_dg = dg->next;
- if (edit_mode)
- object_defgroup_remove_edit_mode(ob, dg);
- else
- object_defgroup_remove_object_mode(ob, dg);
+ if (!only_unlocked || (dg->flag & DG_LOCK_WEIGHT) == 0) {
+ if (edit_mode)
+ object_defgroup_remove_edit_mode(ob, dg);
+ else
+ object_defgroup_remove_object_mode(ob, dg);
+ }
dg = next_dg;
}
@@ -446,6 +449,15 @@ void BKE_object_defgroup_remove_all(Object *ob)
}
/**
+ * Remove all vgroups from object. Work in Object and Edit modes.
+ */
+void BKE_object_defgroup_remove_all(struct Object *ob)
+{
+ BKE_object_defgroup_remove_all_ex(ob, false);
+}
+
+
+/**
* Get MDeformVert vgroup data from given object. Should only be used in Object mode.
*
* \return True if the id type supports weights.
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index ff69f381b06..4fe4d6e75a6 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -977,7 +977,7 @@ static void pbvh_update_normals_accum_task_cb(void *userdata, const int n)
* Not exact equivalent though, since atomicity is only ensured for one component
* of the vector at a time, but here it shall not make any sensible difference. */
for (int k = 3; k--; ) {
- atomic_add_fl(&vnors[v][k], fn[k]);
+ atomic_add_and_fetch_fl(&vnors[v][k], fn[k]);
}
}
}
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index 55f9f384081..a821578db1a 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -148,8 +148,7 @@ BLI_INLINE void bm_face_as_array_index_tri(BMFace *f, int r_index[3])
*
* Its assumed that \a l_radial_first is never forming the target face.
*/
-static bool bm_face_exists_tri_from_loop_vert(
- BMLoop *l_radial_first, BMVert *v_opposite, BMFace **r_face_existing)
+static BMFace *bm_face_exists_tri_from_loop_vert(BMLoop *l_radial_first, BMVert *v_opposite)
{
BLI_assert(!ELEM(v_opposite, l_radial_first->v, l_radial_first->next->v, l_radial_first->prev->v));
if (l_radial_first->radial_next != l_radial_first) {
@@ -157,12 +156,11 @@ static bool bm_face_exists_tri_from_loop_vert(
do {
BLI_assert(l_radial_iter->f->len == 3);
if (l_radial_iter->prev->v == v_opposite) {
- *r_face_existing = l_radial_iter->f;
- return true;
+ return l_radial_iter->f;
}
} while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first);
}
- return false;
+ return NULL;
}
/**
@@ -519,7 +517,7 @@ static BMFace *pbvh_bmesh_face_create(
PBVHNode *node = &bvh->nodes[node_index];
/* ensure we never add existing face */
- BLI_assert(BM_face_exists(v_tri, 3, NULL) == false);
+ BLI_assert(!BM_face_exists(v_tri, 3));
BMFace *f = BM_face_create(bvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP);
f->head.hflag = f_example->head.hflag;
@@ -1313,18 +1311,17 @@ static void pbvh_bmesh_collapse_edge(
* deletion as well. Prevents extraneous "flaps" from being
* created. */
#if 0
- if (UNLIKELY(BM_face_exists(v_tri, 3, &existing_face)))
+ if (UNLIKELY(existing_face = BM_face_exists(v_tri, 3)))
#else
- if (UNLIKELY(bm_face_exists_tri_from_loop_vert(l->next, v_conn, &existing_face)))
+ if (UNLIKELY(existing_face = bm_face_exists_tri_from_loop_vert(l->next, v_conn)))
#endif
{
- BLI_assert(existing_face);
BLI_buffer_append(deleted_faces, BMFace *, existing_face);
}
else {
BMVert *v_tri[3] = {v_conn, l->next->v, l->prev->v};
- BLI_assert(BM_face_exists(v_tri, 3, NULL) == false);
+ BLI_assert(!BM_face_exists(v_tri, 3));
BMEdge *e_tri[3];
PBVHNode *n = pbvh_bmesh_node_from_face(bvh, f);
int ni = n - bvh->nodes;
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index c3ae5736aa9..ebf9f017731 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -65,13 +65,22 @@
#include "BKE_rigidbody.h"
#include "BKE_scene.h"
-#ifdef WITH_BULLET
-
/* ************************************** */
/* Memory Management */
/* Freeing Methods --------------------- */
+#ifndef WITH_BULLET
+
+static void RB_dworld_remove_constraint(void *UNUSED(world), void *UNUSED(con)) {}
+static void RB_dworld_remove_body(void *UNUSED(world), void *UNUSED(body)) {}
+static void RB_dworld_delete(void *UNUSED(world)) {}
+static void RB_body_delete(void *UNUSED(body)) {}
+static void RB_shape_delete(void *UNUSED(shape)) {}
+static void RB_constraint_delete(void *UNUSED(con)) {}
+
+#endif
+
/* Free rigidbody world */
void BKE_rigidbody_free_world(RigidBodyWorld *rbw)
{
@@ -165,6 +174,8 @@ void BKE_rigidbody_free_constraint(Object *ob)
ob->rigidbody_constraint = NULL;
}
+#ifdef WITH_BULLET
+
/* Copying Methods --------------------- */
/* These just copy the data, clearing out references to physics objects.
@@ -804,6 +815,18 @@ static void rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, b
RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_stiffness_z);
RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_damping_z);
+ RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->flag & RBC_FLAG_USE_SPRING_ANG_X);
+ RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->spring_stiffness_ang_x);
+ RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->spring_damping_ang_x);
+
+ RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->flag & RBC_FLAG_USE_SPRING_ANG_Y);
+ RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->spring_stiffness_ang_y);
+ RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->spring_damping_ang_y);
+
+ RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->flag & RBC_FLAG_USE_SPRING_ANG_Z);
+ RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->spring_stiffness_ang_z);
+ RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->spring_damping_ang_z);
+
RB_constraint_set_equilibrium_6dof_spring(rbc->physics_constraint);
/* fall-through */
case RBC_TYPE_6DOF:
@@ -1072,9 +1095,15 @@ RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short ty
rbc->spring_damping_x = 0.5f;
rbc->spring_damping_y = 0.5f;
rbc->spring_damping_z = 0.5f;
+ rbc->spring_damping_ang_x = 0.5f;
+ rbc->spring_damping_ang_y = 0.5f;
+ rbc->spring_damping_ang_z = 0.5f;
rbc->spring_stiffness_x = 10.0f;
rbc->spring_stiffness_y = 10.0f;
rbc->spring_stiffness_z = 10.0f;
+ rbc->spring_stiffness_ang_x = 10.0f;
+ rbc->spring_stiffness_ang_y = 10.0f;
+ rbc->spring_stiffness_ang_z = 10.0f;
rbc->motor_lin_max_impulse = 1.0f;
rbc->motor_lin_target_velocity = 1.0f;
@@ -1602,9 +1631,6 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
# pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
-void BKE_rigidbody_free_world(RigidBodyWorld *rbw) {}
-void BKE_rigidbody_free_object(Object *ob) {}
-void BKE_rigidbody_free_constraint(Object *ob) {}
struct RigidBodyOb *BKE_rigidbody_copy_object(Object *ob) { return NULL; }
struct RigidBodyCon *BKE_rigidbody_copy_constraint(Object *ob) { return NULL; }
void BKE_rigidbody_relink_constraint(RigidBodyCon *rbc) {}
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index 05540f51588..e8970d416e9 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -360,6 +360,10 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd)
BKE_ptcache_free_list(&(smd->domain->ptcaches[0]));
smd->domain->point_cache[0] = NULL;
+ if (smd->domain->coba) {
+ MEM_freeN(smd->domain->coba);
+ }
+
MEM_freeN(smd->domain);
smd->domain = NULL;
}
@@ -544,6 +548,9 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
smd->domain->slice_depth = 0.5f;
smd->domain->slice_axis = 0;
smd->domain->vector_scale = 1.0f;
+
+ smd->domain->coba = NULL;
+ smd->domain->coba_field = FLUID_FIELD_DENSITY;
}
else if (smd->type & MOD_SMOKE_TYPE_FLOW)
{
@@ -646,6 +653,10 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData
tsmd->domain->draw_velocity = smd->domain->draw_velocity;
tsmd->domain->vector_draw_type = smd->domain->vector_draw_type;
tsmd->domain->vector_scale = smd->domain->vector_scale;
+
+ if (smd->domain->coba) {
+ tsmd->domain->coba = MEM_dupallocN(smd->domain->coba);
+ }
}
else if (tsmd->flow) {
tsmd->flow->psys = smd->flow->psys;
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index 3132a8e27e7..f20885b1e8f 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -251,7 +251,8 @@ void BKE_sound_init(struct Main *bmain)
void BKE_sound_init_main(struct Main *bmain)
{
#ifdef WITH_JACK
- AUD_setSynchronizerCallback(sound_sync_callback, bmain);
+ if (sound_device)
+ AUD_setSynchronizerCallback(sound_sync_callback, bmain);
#else
(void)bmain; /* unused */
#endif
diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c
index 436cd2b8fde..fc2d9674c2f 100644
--- a/source/blender/blenlib/intern/task.c
+++ b/source/blender/blenlib/intern/task.c
@@ -237,7 +237,7 @@ static void task_pool_num_decrease(TaskPool *pool, size_t done)
BLI_assert(pool->num >= done);
pool->num -= done;
- atomic_sub_z(&pool->currently_running_tasks, done);
+ atomic_sub_and_fetch_z(&pool->currently_running_tasks, done);
pool->done += done;
if (pool->num == 0)
@@ -292,7 +292,7 @@ static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task
continue;
}
- if (atomic_add_z(&pool->currently_running_tasks, 1) <= pool->num_threads ||
+ if (atomic_add_and_fetch_z(&pool->currently_running_tasks, 1) <= pool->num_threads ||
pool->num_threads == 0)
{
*task = current_task;
@@ -301,7 +301,7 @@ static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task
break;
}
else {
- atomic_sub_z(&pool->currently_running_tasks, 1);
+ atomic_sub_and_fetch_z(&pool->currently_running_tasks, 1);
}
}
if (!found_task)
@@ -669,7 +669,7 @@ void BLI_task_pool_work_and_wait(TaskPool *pool)
/* if found task, do it, otherwise wait until other tasks are done */
if (found_task) {
/* run task */
- atomic_add_z(&pool->currently_running_tasks, 1);
+ atomic_add_and_fetch_z(&pool->currently_running_tasks, 1);
work_task->run(pool, work_task->taskdata, 0);
/* delete task */
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 0de883fe7bf..5c460c7b176 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -5095,6 +5095,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
smd->domain->tex = NULL;
smd->domain->tex_shadow = NULL;
smd->domain->tex_wt = NULL;
+ smd->domain->coba = newdataadr(fd, smd->domain->coba);
smd->domain->effector_weights = newdataadr(fd, smd->domain->effector_weights);
if (!smd->domain->effector_weights)
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 14e02c9ffb6..8133d0496fa 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -53,6 +53,7 @@
#include "DNA_actuator_types.h"
#include "DNA_view3d_types.h"
#include "DNA_smoke_types.h"
+#include "DNA_rigidbody_types.h"
#include "DNA_genfile.h"
@@ -1426,7 +1427,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
- {
+ if (!MAIN_VERSION_ATLEAST(main, 278, 3)) {
for (Scene *scene = main->scene.first; scene != NULL; scene = scene->id.next) {
if (scene->toolsettings != NULL) {
ToolSettings *ts = scene->toolsettings;
@@ -1438,5 +1439,31 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "RigidBodyCon", "float", "spring_stiffness_ang_x")) {
+ Object *ob;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ RigidBodyCon *rbc = ob->rigidbody_constraint;
+ if (rbc) {
+ rbc->spring_stiffness_ang_x = 10.0;
+ rbc->spring_stiffness_ang_y = 10.0;
+ rbc->spring_stiffness_ang_z = 10.0;
+ rbc->spring_damping_ang_x = 0.5;
+ rbc->spring_damping_ang_y = 0.5;
+ rbc->spring_damping_ang_z = 0.5;
+ }
+ }
+ }
+
+ /* constant detail for sculpting is now a resolution value instead of
+ * a percentage, we reuse old DNA struct member but convert it */
+ for (Scene *scene = main->scene.first; scene != NULL; scene = scene->id.next) {
+ if (scene->toolsettings != NULL) {
+ ToolSettings *ts = scene->toolsettings;
+ if (ts->sculpt && ts->sculpt->constant_detail != 0.0f) {
+ ts->sculpt->constant_detail = 100.0f / ts->sculpt->constant_detail;
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index b02b76118a2..f2fef5d476b 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1729,6 +1729,10 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
smd->domain->point_cache[1]->step = 1;
write_pointcaches(wd, &(smd->domain->ptcaches[1]));
+
+ if (smd->domain->coba) {
+ writestruct(wd, DATA, ColorBand, 1, smd->domain->coba);
+ }
}
writestruct(wd, DATA, SmokeDomainSettings, 1, smd->domain);
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index 72ea7bd7f5d..104df625ee6 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -245,7 +245,7 @@ typedef struct BMesh {
/* ID of the shape key this bmesh came from */
int shapenr;
- int walkers, totflags;
+ int totflags;
ListBase selected;
BMFace *act_face;
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index 4d92baab6eb..132a7ccd4fa 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -626,7 +626,7 @@ void BM_elem_attrs_copy(BMesh *bm_src, BMesh *bm_dst, const void *ele_src, void
BM_elem_attrs_copy_ex(bm_src, bm_dst, ele_src, ele_dst, BM_ELEM_SELECT);
}
-void BM_elem_select_copy(BMesh *bm_dst, BMesh *UNUSED(bm_src), void *ele_dst_v, const void *ele_src_v)
+void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v)
{
BMHeader *ele_dst = ele_dst_v;
const BMHeader *ele_src = ele_src_v;
diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h
index 06bc5465a19..9c6483de42b 100644
--- a/source/blender/bmesh/intern/bmesh_construct.h
+++ b/source/blender/bmesh/intern/bmesh_construct.h
@@ -58,7 +58,7 @@ void BM_elem_attrs_copy_ex(
BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v,
const char hflag_mask);
void BM_elem_attrs_copy(BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v);
-void BM_elem_select_copy(BMesh *bm_dst, BMesh *bm_src, void *ele_dst_v, const void *ele_src_v);
+void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v);
void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const struct BMAllocTemplate *allocsize);
BMesh *BM_mesh_copy(BMesh *bm_old);
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index e83b752947c..0cd91107171 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -287,7 +287,7 @@ static BMLoop *bm_face_boundary_add(
#endif
BMLoop *l = bm_loop_create(bm, startv, starte, f, NULL /* starte->l */, create_flag);
- bmesh_radial_append(starte, l);
+ bmesh_radial_loop_append(starte, l);
#ifdef USE_BMESH_HOLES
lst->first = lst->last = l;
@@ -295,8 +295,6 @@ static BMLoop *bm_face_boundary_add(
#else
f->l_first = l;
#endif
-
- l->f = f;
return l;
}
@@ -446,26 +444,20 @@ BMFace *BM_face_create(
if (create_flag & BM_CREATE_NO_DOUBLE) {
/* Check if face already exists */
- const bool is_overlap = BM_face_exists(verts, len, &f);
- if (is_overlap) {
+ f = BM_face_exists(verts, len);
+ if (f != NULL) {
return f;
}
- else {
- BLI_assert(f == NULL);
- }
}
f = bm_face_create__internal(bm);
startl = lastl = bm_face_boundary_add(bm, f, verts[0], edges[0], create_flag);
-
- startl->v = verts[0];
- startl->e = edges[0];
+
for (i = 1; i < len; i++) {
l = bm_loop_create(bm, verts[i], edges[i], f, NULL /* edges[i]->l */, create_flag);
-
- l->f = f;
- bmesh_radial_append(edges[i], l);
+
+ bmesh_radial_loop_append(edges[i], l);
l->prev = lastl;
lastl->next = l;
@@ -904,7 +896,7 @@ void BM_face_kill(BMesh *bm, BMFace *f)
do {
l_next = l_iter->next;
- bmesh_radial_loop_remove(l_iter, l_iter->e);
+ bmesh_radial_loop_remove(l_iter->e, l_iter);
bm_kill_only_loop(bm, l_iter);
} while ((l_iter = l_next) != l_first);
@@ -949,7 +941,7 @@ void BM_face_kill_loose(BMesh *bm, BMFace *f)
l_next = l_iter->next;
e = l_iter->e;
- bmesh_radial_loop_remove(l_iter, e);
+ bmesh_radial_loop_remove(e, l_iter);
bm_kill_only_loop(bm, l_iter);
if (e->l == NULL) {
@@ -981,23 +973,8 @@ void BM_face_kill_loose(BMesh *bm, BMFace *f)
*/
void BM_edge_kill(BMesh *bm, BMEdge *e)
{
-
- if (e->l) {
- BMLoop *l = e->l, *lnext, *startl = e->l;
-
- do {
- lnext = l->radial_next;
- if (lnext->f == l->f) {
- BM_face_kill(bm, l->f);
- break;
- }
-
- BM_face_kill(bm, l->f);
-
- if (l == lnext)
- break;
- l = lnext;
- } while (l != startl);
+ while (e->l) {
+ BM_face_kill(bm, e->l->f);
}
bmesh_disk_edge_remove(e, e->v1);
@@ -1011,15 +988,8 @@ void BM_edge_kill(BMesh *bm, BMEdge *e)
*/
void BM_vert_kill(BMesh *bm, BMVert *v)
{
- if (v->e) {
- BMEdge *e, *e_next;
-
- e = v->e;
- while (v->e) {
- e_next = bmesh_disk_edge_next(e, v);
- BM_edge_kill(bm, e);
- e = e_next;
- }
+ while (v->e) {
+ BM_edge_kill(bm, v->e);
}
bm_kill_only_vert(bm, v);
@@ -1046,78 +1016,73 @@ static int UNUSED_FUNCTION(bm_loop_length)(BMLoop *l)
* \brief Loop Reverse
*
* Changes the winding order of a face from CW to CCW or vice versa.
- * This euler is a bit peculiar in comparison to others as it is its
- * own inverse.
- *
- * BMESH_TODO: reinsert validation code.
*
* \param cd_loop_mdisp_offset: Cached result of `CustomData_get_offset(&bm->ldata, CD_MDISPS)`.
* \param use_loop_mdisp_flip: When set, flip the Z-depth of the mdisp,
* (use when flipping normals, disable when mirroring, eg: symmetrize).
- *
- * \return Success
*/
-static bool bm_loop_reverse_loop(
+void bmesh_loop_reverse(
BMesh *bm, BMFace *f,
-#ifdef USE_BMESH_HOLES
- BMLoopList *lst,
-#endif
const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip)
{
+ BMLoop *l_first = f->l_first;
-#ifdef USE_BMESH_HOLES
- BMLoop *l_first = lst->first;
+ /* track previous cycles radial state */
+ BMEdge *e_prev = l_first->prev->e;
+ BMLoop *l_prev_radial_next = l_first->prev->radial_next;
+ BMLoop *l_prev_radial_prev = l_first->prev->radial_prev;
+ bool is_prev_boundary = l_prev_radial_next == l_prev_radial_next->radial_next;
+
+ BMLoop *l_iter = l_first;
+ do {
+ BMEdge *e_iter = l_iter->e;
+ BMLoop *l_iter_radial_next = l_iter->radial_next;
+ BMLoop *l_iter_radial_prev = l_iter->radial_prev;
+ bool is_iter_boundary = l_iter_radial_next == l_iter_radial_next->radial_next;
+
+#if 0
+ bmesh_radial_loop_remove(e_iter, l_iter);
+ bmesh_radial_loop_append(e_prev, l_iter);
#else
- BMLoop *l_first = f->l_first;
-#endif
+ /* inline loop reversal */
+ if (is_prev_boundary) {
+ /* boundary */
+ l_iter->radial_next = l_iter;
+ l_iter->radial_prev = l_iter;
+ }
+ else {
+ /* non-boundary, replace radial links */
+ l_iter->radial_next = l_prev_radial_next;
+ l_iter->radial_prev = l_prev_radial_prev;
+ l_prev_radial_next->radial_prev = l_iter;
+ l_prev_radial_prev->radial_next = l_iter;
+ }
- const int len = f->len;
- BMLoop *l_iter, *oldprev, *oldnext;
- BMEdge **edar = BLI_array_alloca(edar, len);
- int i, j, edok;
+ if (e_iter->l == l_iter) {
+ e_iter->l = l_iter->next;
+ }
+ l_iter->e = e_prev;
+#endif
- for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
- bmesh_radial_loop_remove(l_iter, (edar[i] = l_iter->e));
- }
+ SWAP(BMLoop *, l_iter->next, l_iter->prev);
- /* actually reverse the loop */
- for (i = 0, l_iter = l_first; i < len; i++) {
- oldnext = l_iter->next;
- oldprev = l_iter->prev;
- l_iter->next = oldprev;
- l_iter->prev = oldnext;
- l_iter = oldnext;
-
if (cd_loop_mdisp_offset != -1) {
MDisps *md = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset);
BKE_mesh_mdisp_flip(md, use_loop_mdisp_flip);
}
- }
- if (len == 2) { /* two edged face */
- /* do some verification here! */
- l_first->e = edar[1];
- l_first->next->e = edar[0];
- }
- else {
- for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
- edok = 0;
- for (j = 0; j < len; j++) {
- edok = BM_verts_in_edge(l_iter->v, l_iter->next->v, edar[j]);
- if (edok) {
- l_iter->e = edar[j];
- break;
- }
- }
- }
- }
- /* rebuild radial */
- for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next)
- bmesh_radial_append(l_iter->e, l_iter);
+ e_prev = e_iter;
+ l_prev_radial_next = l_iter_radial_next;
+ l_prev_radial_prev = l_iter_radial_prev;
+ is_prev_boundary = is_iter_boundary;
+
+ /* step to next (now swapped) */
+ } while ((l_iter = l_iter->prev) != l_first);
#ifndef NDEBUG
/* validate radial */
- for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
+ int i;
+ for (i = 0, l_iter = l_first; i < f->len; i++, l_iter = l_iter->next) {
BM_CHECK_ELEMENT(l_iter);
BM_CHECK_ELEMENT(l_iter->e);
BM_CHECK_ELEMENT(l_iter->v);
@@ -1129,22 +1094,6 @@ static bool bm_loop_reverse_loop(
/* Loop indices are no more valid! */
bm->elem_index_dirty |= BM_LOOP;
-
- return true;
-}
-
-/**
- * \brief Flip the faces direction
- */
-bool bmesh_loop_reverse(
- BMesh *bm, BMFace *f,
- const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip)
-{
-#ifdef USE_BMESH_HOLES
- return bm_loop_reverse_loop(bm, f, f->loops.first, cd_loop_mdisp_offset, use_loop_mdisp_flip);
-#else
- return bm_loop_reverse_loop(bm, f, cd_loop_mdisp_offset, use_loop_mdisp_flip);
-#endif
}
static void bm_elements_systag_enable(void *veles, int tot, const char api_flag)
@@ -1193,7 +1142,11 @@ static int UNUSED_FUNCTION(bm_vert_systag_count_disk)(BMVert *v, const char api_
return i;
}
-static bool disk_is_flagged(BMVert *v, const char api_flag)
+/**
+ * Return true when the vertex is manifold,
+ * attached to faces which are all flagged.
+ */
+static bool bm_vert_is_manifold_flagged(BMVert *v, const char api_flag)
{
BMEdge *e = v->e;
@@ -1252,7 +1205,7 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
BLI_array_staticdeclare(deledges, BM_DEFAULT_NGON_STACK_SIZE);
BLI_array_staticdeclare(delverts, BM_DEFAULT_NGON_STACK_SIZE);
BMVert *v1 = NULL, *v2 = NULL;
- int i, tote = 0;
+ int i;
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
if (UNLIKELY(!totface)) {
@@ -1282,13 +1235,10 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
v1 = l_iter->v;
v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
}
- tote++;
}
else if (rlen == 2) {
- int d1, d2;
-
- d1 = disk_is_flagged(l_iter->e->v1, _FLAG_JF);
- d2 = disk_is_flagged(l_iter->e->v2, _FLAG_JF);
+ const bool d1 = bm_vert_is_manifold_flagged(l_iter->e->v1, _FLAG_JF);
+ const bool d2 = bm_vert_is_manifold_flagged(l_iter->e->v2, _FLAG_JF);
if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
/* don't remove an edge it makes up the side of another face
@@ -1332,7 +1282,8 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
}
/* create region face */
- f_new = tote ? BM_face_create_ngon(bm, v1, v2, edges, tote, faces[0], BM_CREATE_NOP) : NULL;
+ f_new = BLI_array_count(edges) ?
+ BM_face_create_ngon(bm, v1, v2, edges, BLI_array_count(edges), faces[0], BM_CREATE_NOP) : NULL;
if (UNLIKELY(f_new == NULL)) {
/* Invalid boundary region to join faces */
goto error;
@@ -1350,10 +1301,11 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
} while (l2 != l_iter);
if (l2 != l_iter) {
- /* I think this is correct? */
+ /* loops share an edge, shared vert depends on winding */
if (l2->v != l_iter->v) {
l2 = l2->next;
}
+ BLI_assert(l_iter->v == l2->v);
BM_elem_attrs_copy(bm, bm, l2, l_iter);
}
@@ -1362,22 +1314,15 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
#ifdef USE_BMESH_HOLES
/* add holes */
BLI_movelisttolist(&f_new->loops, &holes);
-#endif
/* update loop face pointer */
-#ifdef USE_BMESH_HOLES
- for (lst = f_new->loops.first; lst; lst = lst->next)
-#endif
- {
-#ifdef USE_BMESH_HOLES
+ for (lst = f_new->loops.first; lst; lst = lst->next) {
l_iter = l_first = lst->first;
-#else
- l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
-#endif
do {
l_iter->f = f_new;
} while ((l_iter = l_iter->next) != l_first);
}
+#endif
bm_elements_systag_disable(faces, totface, _FLAG_JF);
BM_ELEM_API_FLAG_DISABLE(f_new, _FLAG_JF);
@@ -1585,8 +1530,8 @@ BMFace *bmesh_sfme(
} while ((l_iter = l_iter->next) != l_first);
/* link up the new loops into the new edges radial */
- bmesh_radial_append(e, l_f1);
- bmesh_radial_append(e, l_f2);
+ bmesh_radial_loop_append(e, l_f1);
+ bmesh_radial_loop_append(e, l_f2);
f2->len = f2len;
@@ -1693,18 +1638,18 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e)
#ifndef NDEBUG
int radlen = bmesh_radial_length(l_next);
#endif
- int first1 = 0, first2 = 0;
+ bool is_first = true;
/* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
while (l_next) {
l = l_next;
l->f->len++;
l_next = l_next != l_next->radial_next ? l_next->radial_next : NULL;
- bmesh_radial_loop_remove(l, NULL);
+ bmesh_radial_loop_unlink(l);
l_new = bm_loop_create(bm, NULL, NULL, l->f, l, 0);
l_new->prev = l;
- l_new->next = (l->next);
+ l_new->next = l->next;
l_new->prev->next = l_new;
l_new->next->prev = l_new;
l_new->v = v_new;
@@ -1715,36 +1660,26 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e)
l->e = e_new;
/* append l into e_new's rad cycle */
- if (!first1) {
- first1 = 1;
- l->radial_next = l->radial_prev = NULL;
- }
-
- if (!first2) {
- first2 = 1;
+ if (is_first) {
+ is_first = false;
l->radial_next = l->radial_prev = NULL;
}
- bmesh_radial_append(l_new->e, l_new);
- bmesh_radial_append(l->e, l);
+ bmesh_radial_loop_append(l_new->e, l_new);
+ bmesh_radial_loop_append(l->e, l);
}
else if (BM_verts_in_edge(l_new->v, l_new->next->v, e_new)) {
l_new->e = e_new;
l->e = e;
/* append l into e_new's rad cycle */
- if (!first1) {
- first1 = 1;
+ if (is_first) {
+ is_first = false;
l->radial_next = l->radial_prev = NULL;
}
- if (!first2) {
- first2 = 1;
- l->radial_next = l->radial_prev = NULL;
- }
-
- bmesh_radial_append(l_new->e, l_new);
- bmesh_radial_append(l->e, l);
+ bmesh_radial_loop_append(l_new->e, l_new);
+ bmesh_radial_loop_append(l->e, l);
}
}
@@ -1839,7 +1774,6 @@ BMEdge *bmesh_jekv(
BMEdge *e_old;
BMVert *v_old, *v_target;
BMLoop *l_kill;
- bool halt = false;
#ifndef NDEBUG
int radlen, i;
bool edok;
@@ -1860,9 +1794,9 @@ BMEdge *bmesh_jekv(
e_old = bmesh_disk_edge_next(e_kill, v_kill);
v_target = BM_edge_other_vert(e_kill, v_kill);
v_old = BM_edge_other_vert(e_old, v_kill);
- halt = BM_verts_in_edge(v_kill, v_target, e_old); /* check for double edges */
-
- if (halt) {
+
+ /* check for double edges */
+ if (BM_verts_in_edge(v_kill, v_target, e_old)) {
return NULL;
}
else {
@@ -2413,7 +2347,7 @@ void bmesh_vert_separate(
v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
if (copy_select) {
- BM_elem_select_copy(bm, bm, v_new, v);
+ BM_elem_select_copy(bm, v_new, v);
}
while ((e = BLI_SMALLSTACK_POP(edges))) {
@@ -2471,18 +2405,13 @@ static void bmesh_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separate)
do {
BMEdge *e_orig = n_orig->link;
LinkNode *n_step = n_orig->next;
- LinkNode *n_prev = n_orig;
do {
BMEdge *e = n_step->link;
BLI_assert(e != e_orig);
if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2)) {
BM_edge_splice(bm, e_orig, e);
- n_prev->next = n_step->next;
- n_step = n_prev;
}
- } while ((void)
- (n_prev = n_step),
- (n_step = n_step->next));
+ } while ((n_step = n_step->next));
} while ((n_orig = n_orig->next) && n_orig->next);
} while ((edges_separate = edges_separate->next));
@@ -2619,8 +2548,8 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src)
l = e_src->l;
BLI_assert(BM_vert_in_edge(e_dst, l->v));
BLI_assert(BM_vert_in_edge(e_dst, l->next->v));
- bmesh_radial_loop_remove(l, e_src);
- bmesh_radial_append(e_dst, l);
+ bmesh_radial_loop_remove(e_src, l);
+ bmesh_radial_loop_append(e_dst, l);
}
BLI_assert(bmesh_radial_length(e_src->l) == 0);
@@ -2667,12 +2596,12 @@ void bmesh_edge_separate(
}
e_new = BM_edge_create(bm, e->v1, e->v2, e, BM_CREATE_NOP);
- bmesh_radial_loop_remove(l_sep, e);
- bmesh_radial_append(e_new, l_sep);
+ bmesh_radial_loop_remove(e, l_sep);
+ bmesh_radial_loop_append(e_new, l_sep);
l_sep->e = e_new;
if (copy_select) {
- BM_elem_select_copy(bm, bm, e_new, e);
+ BM_elem_select_copy(bm, e_new, e);
}
BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
@@ -2855,8 +2784,8 @@ BMVert *bmesh_urmv_loop_multi(
do {
l_next = l_iter->radial_next;
if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
- bmesh_radial_loop_remove(l_iter, e);
- bmesh_radial_append(e_new, l_iter);
+ bmesh_radial_loop_remove(e, l_iter);
+ bmesh_radial_loop_append(e_new, l_iter);
l_iter->e = e_new;
}
} while ((l_iter = l_next) != l_first);
diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h
index fb5702bc574..f72e9d7b198 100644
--- a/source/blender/bmesh/intern/bmesh_core.h
+++ b/source/blender/bmesh/intern/bmesh_core.h
@@ -75,7 +75,7 @@ void bmesh_vert_separate(
BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len,
const bool copy_select);
-bool bmesh_loop_reverse(
+void bmesh_loop_reverse(
BMesh *bm, BMFace *f,
const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip);
diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c
index 961b10d848a..96154f051f9 100644
--- a/source/blender/bmesh/intern/bmesh_iterators.c
+++ b/source/blender/bmesh/intern/bmesh_iterators.c
@@ -485,9 +485,9 @@ void bmiter__face_of_vert_begin(struct BMIter__face_of_vert *iter)
{
((BMIter *)iter)->count = bmesh_disk_facevert_count(iter->vdata);
if (((BMIter *)iter)->count) {
- iter->e_first = bmesh_disk_faceedge_find_first(iter->vdata->e, iter->vdata);
+ iter->l_first = bmesh_disk_faceloop_find_first(iter->vdata->e, iter->vdata);
+ iter->e_first = iter->l_first->e;
iter->e_next = iter->e_first;
- iter->l_first = bmesh_radial_faceloop_find_first(iter->e_first->l, iter->vdata);
iter->l_next = iter->l_first;
}
else {
@@ -526,9 +526,9 @@ void bmiter__loop_of_vert_begin(struct BMIter__loop_of_vert *iter)
{
((BMIter *)iter)->count = bmesh_disk_facevert_count(iter->vdata);
if (((BMIter *)iter)->count) {
- iter->e_first = bmesh_disk_faceedge_find_first(iter->vdata->e, iter->vdata);
+ iter->l_first = bmesh_disk_faceloop_find_first(iter->vdata->e, iter->vdata);
+ iter->e_first = iter->l_first->e;
iter->e_next = iter->e_first;
- iter->l_first = bmesh_radial_faceloop_find_first(iter->e_first->l, iter->vdata);
iter->l_next = iter->l_first;
}
else {
diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c
index d3c847de64e..03165beb329 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -104,7 +104,6 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
*/
bool BM_disk_dissolve(BMesh *bm, BMVert *v)
{
- BMFace *f, *f2;
BMEdge *e, *keepedge = NULL, *baseedge = NULL;
int len = 0;
@@ -132,16 +131,17 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
#if 0
/* handle specific case for three-valence. solve it by
* increasing valence to four. this may be hackish. . */
- BMLoop *loop = e->l;
- if (loop->v == v) loop = loop->next;
- if (!BM_face_split(bm, loop->f, v, loop->v, NULL, NULL, false))
+ BMLoop *l_a = BM_face_vert_share_loop(e->l->f, v);
+ BMLoop *l_b = (e->l->v == v) ? e->l->next : e->l;
+
+ if (!BM_face_split(bm, e->l->f, l_a, l_b, NULL, NULL, false))
return false;
if (!BM_disk_dissolve(bm, v)) {
return false;
}
#else
- if (UNLIKELY(!BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e, true))) {
+ 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))) {
@@ -159,11 +159,10 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
}
/* handle two-valence */
- f = e->l->f;
- f2 = e->l->radial_next->f;
-
- if (f != f2 && !BM_faces_join_pair(bm, f, f2, e, true)) {
- return false;
+ if (e->l != e->l->radial_next) {
+ if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true)) {
+ return false;
+ }
}
return true;
@@ -176,9 +175,9 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
done = true;
e = v->e;
do {
- f = NULL;
+ BMFace *f = NULL;
if (BM_edge_is_manifold(e) && (e != baseedge) && (e != keepedge)) {
- f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e, true);
+ f = BM_faces_join_pair(bm, e->l, e->l->radial_next, true);
/* return if couldn't join faces in manifold
* conditions */
/* !disabled for testing why bad things happen */
@@ -204,12 +203,9 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
if (e->l) {
/* get remaining two faces */
- f = e->l->f;
- f2 = e->l->radial_next->f;
-
- if (f != f2) {
+ if (e->l != e->l->radial_next) {
/* join two remaining faces */
- if (!BM_faces_join_pair(bm, f, f2, e, true)) {
+ if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true)) {
return false;
}
}
@@ -224,30 +220,24 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
*
* Joins two adjacent faces together.
*
- * Because this method calls to #BM_faces_join to do its work, if a pair
- * of faces share multiple edges, the pair of faces will be joined at
- * every edge (not just edge \a e). This part of the functionality might need
- * to be reconsidered.
+ * \note This method calls to #BM_faces_join to do its work.
+ * This means connected edges which also share the two faces will be joined.
*
* If the windings do not match the winding of the new face will follow
- * \a f_a's winding (i.e. \a f_b will be reversed before the join).
+ * \a l_a's winding (i.e. \a l_b will be reversed before the join).
*
- * \return pointer to the combined face
+ * \return The combined face or NULL on failure.
*/
-BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f_a, BMFace *f_b, BMEdge *e, const bool do_del)
+BMFace *BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del)
{
- BMFace *faces[2] = {f_a, f_b};
-
- BMLoop *l_a = BM_face_edge_share_loop(f_a, e);
- BMLoop *l_b = BM_face_edge_share_loop(f_b, e);
-
- BLI_assert(l_a && l_b);
+ BLI_assert((l_a != l_b) && (l_a->e == l_b->e));
if (l_a->v == l_b->v) {
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- bmesh_loop_reverse(bm, f_b, cd_loop_mdisp_offset, true);
+ bmesh_loop_reverse(bm, l_b->f, cd_loop_mdisp_offset, true);
}
-
+
+ BMFace *faces[2] = {l_a->f, l_b->f};
return BM_faces_join(bm, faces, 2, do_del);
}
@@ -551,7 +541,7 @@ BMEdge *BM_vert_collapse_edge(
BMVert *tv2 = BM_edge_other_vert(e2, v_kill);
if (tv2) {
/* only action, other calls here only get the edge to return */
- e_new = bmesh_jekv(bm, e_kill, v_kill, do_del);
+ e_new = bmesh_jekv(bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
}
}
}
@@ -598,18 +588,13 @@ BMVert *BM_edge_collapse(
BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
{
BMVert *v_new, *v_other;
+ BMEdge *e_new;
BMFace **oldfaces = NULL;
- BMEdge *e_dummy;
BLI_array_staticdeclare(oldfaces, 32);
const int cd_loop_mdisp_offset = BM_edge_is_wire(e) ? -1 : CustomData_get_offset(&bm->ldata, CD_MDISPS);
BLI_assert(BM_vert_in_edge(e, v) == true);
- /* we need this for handling multi-res */
- if (!r_e) {
- r_e = &e_dummy;
- }
-
/* do we have a multi-res layer? */
if (cd_loop_mdisp_offset != -1) {
BMLoop *l;
@@ -630,17 +615,20 @@ BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
}
v_other = BM_edge_other_vert(e, v);
- v_new = bmesh_semv(bm, v, e, r_e);
+ v_new = bmesh_semv(bm, v, e, &e_new);
+ if (r_e != NULL) {
+ *r_e = e_new;
+ }
BLI_assert(v_new != NULL);
- BLI_assert(BM_vert_in_edge(*r_e, v) && BM_vert_in_edge(*r_e, v_new));
+ BLI_assert(BM_vert_in_edge(e_new, v) && BM_vert_in_edge(e_new, v_new));
BLI_assert(BM_vert_in_edge(e, v_new) && BM_vert_in_edge(e, v_other));
sub_v3_v3v3(v_new->co, v_other->co, v->co);
madd_v3_v3v3fl(v_new->co, v->co, v_new->co, fac);
- (*r_e)->head.hflag = e->head.hflag;
- BM_elem_attrs_copy(bm, bm, e, *r_e);
+ e_new->head.hflag = e->head.hflag;
+ BM_elem_attrs_copy(bm, bm, e, e_new);
/* v->v_new->v2 */
BM_data_interp_face_vert_edge(bm, v_other, v, v_new, e, fac);
@@ -656,7 +644,7 @@ BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
BM_face_calc_center_mean(oldfaces[i], f_center_old);
for (j = 0; j < 2; j++) {
- BMEdge *e1 = j ? *r_e : e;
+ BMEdge *e1 = j ? e_new : e;
BMLoop *l;
l = e1->l;
@@ -689,7 +677,7 @@ BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
/* fix boundaries a bit, doesn't work too well quite yet */
#if 0
for (j = 0; j < 2; j++) {
- BMEdge *e1 = j ? *r_e : e;
+ BMEdge *e1 = j ? e_new : e;
BMLoop *l, *l2;
l = e1->l;
@@ -991,6 +979,7 @@ BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_f
BMLoop *l1, *l2;
BMFace *f;
BMEdge *e_new = NULL;
+ char f_active_prev = 0;
char f_hflag_prev_1;
char f_hflag_prev_2;
@@ -1041,8 +1030,18 @@ BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_f
f_hflag_prev_1 = l1->f->head.hflag;
f_hflag_prev_2 = l2->f->head.hflag;
+ /* maintain active face */
+ if (bm->act_face == l1->f) {
+ f_active_prev = 1;
+ }
+ else if (bm->act_face == l2->f) {
+ f_active_prev = 2;
+ }
+
+ const bool is_flipped = !BM_edge_is_contiguous(e);
+
/* don't delete the edge, manually remove the edge after so we can copy its attributes */
- f = BM_faces_join_pair(bm, l1->f, l2->f, e, true);
+ f = BM_faces_join_pair(bm, BM_face_edge_share_loop(l1->f, e), BM_face_edge_share_loop(l2->f, e), true);
if (f == NULL) {
return NULL;
@@ -1062,6 +1061,22 @@ BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_f
if (BM_edge_face_pair(e_new, &fa, &fb)) {
fa->head.hflag = f_hflag_prev_1;
fb->head.hflag = f_hflag_prev_2;
+
+ if (f_active_prev == 1) {
+ bm->act_face = fa;
+ }
+ else if (f_active_prev == 2) {
+ bm->act_face = fb;
+ }
+
+ if (is_flipped) {
+ BM_face_normal_flip(bm, fb);
+
+ if (ccw) {
+ /* needed otherwise ccw toggles direction */
+ e_new->l = e_new->l->radial_next;
+ }
+ }
}
}
else {
diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h
index 2e557e3b606..5e95e9a2cc7 100644
--- a/source/blender/bmesh/intern/bmesh_mods.h
+++ b/source/blender/bmesh/intern/bmesh_mods.h
@@ -31,7 +31,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v);
bool BM_disk_dissolve(BMesh *bm, BMVert *v);
-BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e, const bool do_del);
+BMFace *BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del);
/** see: bmesh_polygon_edgenet.h for #BM_face_split_edgenet */
diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c
index b5f9575aff5..7ca5640578a 100644
--- a/source/blender/bmesh/intern/bmesh_queries.c
+++ b/source/blender/bmesh/intern/bmesh_queries.c
@@ -102,17 +102,10 @@ BMLoop *BM_loop_other_edge_loop(BMLoop *l, BMVert *v)
*/
BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
{
- BMIter liter;
- BMLoop *l_iter;
+ BMLoop *l_iter = BM_face_vert_share_loop(f, v);
BLI_assert(BM_edge_exists(v_prev, v) != NULL);
- BM_ITER_ELEM (l_iter, &liter, v, BM_LOOPS_OF_VERT) {
- if (l_iter->f == f) {
- break;
- }
- }
-
if (l_iter) {
if (l_iter->prev->v == v_prev) {
return l_iter->next;
@@ -149,7 +142,6 @@ BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
* The faces loop direction is ignored.
* </pre>
*/
-
BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v)
{
#if 0 /* works but slow */
@@ -178,9 +170,6 @@ BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v)
return l->next->next;
}
}
-
-
-
#endif
}
@@ -392,17 +381,7 @@ BMFace *BM_vert_pair_share_face_by_angle(
*/
BMLoop *BM_vert_find_first_loop(BMVert *v)
{
- BMEdge *e;
-
- if (!v->e)
- return NULL;
-
- e = bmesh_disk_faceedge_find_first(v->e, v);
-
- if (!e)
- return NULL;
-
- return bmesh_radial_faceloop_find_first(e->l, v);
+ return v->e ? bmesh_disk_faceloop_find_first(v->e, v) : NULL;
}
/**
@@ -878,9 +857,18 @@ int BM_vert_face_count_ex(const BMVert *v, int count_max)
*
* same as ``BM_vert_face_count(v) != 0`` or ``BM_vert_find_first_loop(v) == NULL``
*/
-bool BM_vert_face_check(BMVert *v)
+bool BM_vert_face_check(const BMVert *v)
{
- return v->e && (bmesh_disk_faceedge_find_first(v->e, v) != NULL);
+ if (v->e != NULL) {
+ const BMEdge *e_iter, *e_first;
+ e_first = e_iter = v->e;
+ do {
+ if (e_iter->l != NULL) {
+ return true;
+ }
+ } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
+ }
+ return false;
}
/**
@@ -910,7 +898,7 @@ bool BM_vert_is_wire(const BMVert *v)
* A vertex is non-manifold if it meets the following conditions:
* 1: Loose - (has no edges/faces incident upon it).
* 2: Joins two distinct regions - (two pyramids joined at the tip).
- * 3: Is part of a an edge with more than 2 faces.
+ * 3: Is part of an edge with more than 2 faces.
* 4: Is part of a wire edge.
*/
bool BM_vert_is_manifold(const BMVert *v)
@@ -926,7 +914,8 @@ bool BM_vert_is_manifold(const BMVert *v)
/* count edges while looking for non-manifold edges */
e_first = e_iter = v->e;
- l_first = e_iter->l ? e_iter->l : NULL;
+ /* may be null */
+ l_first = e_iter->l;
do {
/* loose edge or edge shared by more than two faces,
* edges with 1 face user are OK, otherwise we could
@@ -1924,7 +1913,7 @@ BMEdge *BM_edge_find_double(BMEdge *e)
*
* \note there used to be a BM_face_exists_overlap function that checks for partial overlap.
*/
-bool BM_face_exists(BMVert **varr, int len, BMFace **r_existface)
+BMFace *BM_face_exists(BMVert **varr, int len)
{
if (varr[0]->e) {
BMEdge *e_iter, *e_first;
@@ -1963,10 +1952,7 @@ bool BM_face_exists(BMVert **varr, int len, BMFace **r_existface)
}
if (i_walk == len) {
- if (r_existface) {
- *r_existface = l_iter_radial->f;
- }
- return true;
+ return l_iter_radial->f;
}
}
} while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial);
@@ -1975,10 +1961,7 @@ bool BM_face_exists(BMVert **varr, int len, BMFace **r_existface)
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, varr[0])) != e_first);
}
- if (r_existface) {
- *r_existface = NULL;
- }
- return false;
+ return NULL;
}
@@ -2121,26 +2104,21 @@ bool BM_face_exists_multi_edge(BMEdge **earr, int len)
* \note The face may contain other verts \b not in \a varr.
*
* \note Its possible there are more than one overlapping faces,
- * in this case the first one found will be assigned to \a r_f_overlap.
+ * in this case the first one found will be returned.
*
* \param varr Array of unordered verts.
* \param len \a varr array length.
- * \param r_f_overlap The overlapping face to return.
- * \return Success
+ * \return The face or NULL.
*/
-bool BM_face_exists_overlap(BMVert **varr, const int len, BMFace **r_f_overlap)
+BMFace *BM_face_exists_overlap(BMVert **varr, const int len)
{
BMIter viter;
BMFace *f;
int i;
- bool is_overlap = false;
+ BMFace *f_overlap = NULL;
LinkNode *f_lnk = NULL;
- if (r_f_overlap) {
- *r_f_overlap = NULL;
- }
-
#ifdef DEBUG
/* check flag isn't already set */
for (i = 0; i < len; i++) {
@@ -2154,10 +2132,7 @@ bool BM_face_exists_overlap(BMVert **varr, const int len, BMFace **r_f_overlap)
BM_ITER_ELEM (f, &viter, varr[i], BM_FACES_OF_VERT) {
if (BM_ELEM_API_FLAG_TEST(f, _FLAG_OVERLAP) == 0) {
if (len <= BM_verts_in_face_count(varr, len, f)) {
- if (r_f_overlap)
- *r_f_overlap = f;
-
- is_overlap = true;
+ f_overlap = f;
break;
}
@@ -2171,7 +2146,7 @@ bool BM_face_exists_overlap(BMVert **varr, const int len, BMFace **r_f_overlap)
BM_ELEM_API_FLAG_DISABLE((BMFace *)f_lnk->link, _FLAG_OVERLAP);
}
- return is_overlap;
+ return f_overlap;
}
/**
diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h
index 10e4b9a15aa..a6a37767ee9 100644
--- a/source/blender/bmesh/intern/bmesh_queries.h
+++ b/source/blender/bmesh/intern/bmesh_queries.h
@@ -86,7 +86,7 @@ BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e) ATTR_WARN_UNUSED_RESULT AT
bool BM_vert_is_edge_pair(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
bool BM_vert_edge_pair(BMVert *v, BMEdge **r_e_a, BMEdge **r_e_b);
-bool BM_vert_face_check(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+bool BM_vert_face_check(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
bool BM_vert_is_wire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
@@ -135,12 +135,12 @@ BMLoop *BM_face_find_longest_loop(BMFace *f) ATTR_WARN_UNUSED_RESULT ATTR_NONNUL
BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
BMEdge *BM_edge_find_double(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-bool BM_face_exists(BMVert **varr, int len, BMFace **r_existface) ATTR_NONNULL(1);
+BMFace* BM_face_exists(BMVert **varr, int len) ATTR_NONNULL(1);
bool BM_face_exists_multi(BMVert **varr, BMEdge **earr, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
bool BM_face_exists_multi_edge(BMEdge **earr, int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-bool BM_face_exists_overlap(BMVert **varr, const int len, BMFace **r_f_overlap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+BMFace *BM_face_exists_overlap(BMVert **varr, const int len) ATTR_WARN_UNUSED_RESULT;
bool BM_face_exists_overlap_subset(BMVert **varr, const int len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
int BM_face_share_face_count(BMFace *f_a, BMFace *f_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c
index cb302139a4c..8e484841568 100644
--- a/source/blender/bmesh/intern/bmesh_structure.c
+++ b/source/blender/bmesh/intern/bmesh_structure.c
@@ -143,7 +143,7 @@ void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src)
* to store non-manifold conditions since BM does not keep track of region/shell information.
*
* Functions relating to this cycle:
- * - #bmesh_radial_append
+ * - #bmesh_radial_loop_append
* - #bmesh_radial_loop_remove
* - #bmesh_radial_facevert_count
* - #bmesh_radial_facevert_check
@@ -264,10 +264,12 @@ bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
{
BMEdge *e_iter;
- if (!BM_vert_in_edge(e, v))
+ if (!BM_vert_in_edge(e, v)) {
return false;
- if (bmesh_disk_count_ex(v, len + 1) != len || len == 0)
+ }
+ if (len == 0 || bmesh_disk_count_ex(v, len + 1) != len) {
return false;
+ }
e_iter = e;
do {
@@ -336,13 +338,28 @@ int bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max)
*/
BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v)
{
- const BMEdge *e_find = e;
+ const BMEdge *e_iter = e;
do {
- if (e_find->l && bmesh_radial_facevert_check(e_find->l, v)) {
- return (BMEdge *)e_find;
+ if (e_iter->l != NULL) {
+ return (BMEdge *)((e_iter->l->v == v) ? e_iter : e_iter->l->next->e);
}
- } while ((e_find = bmesh_disk_edge_next(e_find, v)) != e);
+ } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
+ return NULL;
+}
+/**
+ * Special case for BM_LOOPS_OF_VERT & BM_FACES_OF_VERT, avoids 2x calls.
+ *
+ * The returned BMLoop.e matches the result of #bmesh_disk_faceedge_find_first
+ */
+BMLoop *bmesh_disk_faceloop_find_first(const BMEdge *e, const BMVert *v)
+{
+ const BMEdge *e_iter = e;
+ do {
+ if (e_iter->l != NULL) {
+ return (e_iter->l->v == v) ? e_iter->l : e_iter->l->next;
+ }
+ } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
return NULL;
}
@@ -389,6 +406,30 @@ bool bmesh_radial_validate(int radlen, BMLoop *l)
return true;
}
+void bmesh_radial_loop_append(BMEdge *e, BMLoop *l)
+{
+ if (e->l == NULL) {
+ e->l = l;
+ l->radial_next = l->radial_prev = l;
+ }
+ else {
+ l->radial_prev = e->l;
+ l->radial_next = e->l->radial_next;
+
+ e->l->radial_next->radial_prev = l;
+ e->l->radial_next = l;
+
+ e->l = l;
+ }
+
+ if (UNLIKELY(l->e && l->e != e)) {
+ /* l is already in a radial cycle for a different edge */
+ BMESH_ASSERT(0);
+ }
+
+ l->e = e;
+}
+
/**
* \brief BMESH RADIAL REMOVE LOOP
*
@@ -397,28 +438,27 @@ bool bmesh_radial_validate(int radlen, BMLoop *l)
* updated (in the case that the edge's link into the radial
* cycle was the loop which is being removed from the cycle).
*/
-void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e)
+void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l)
{
/* if e is non-NULL, l must be in the radial cycle of e */
- if (UNLIKELY(e && e != l->e)) {
+ if (UNLIKELY(e != l->e)) {
BMESH_ASSERT(0);
}
if (l->radial_next != l) {
- if (e && l == e->l)
+ if (l == e->l) {
e->l = l->radial_next;
+ }
l->radial_next->radial_prev = l->radial_prev;
l->radial_prev->radial_next = l->radial_next;
}
else {
- if (e) {
- if (l == e->l) {
- e->l = NULL;
- }
- else {
- BMESH_ASSERT(0);
- }
+ if (l == e->l) {
+ e->l = NULL;
+ }
+ else {
+ BMESH_ASSERT(0);
}
}
@@ -428,6 +468,22 @@ void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e)
l->e = NULL;
}
+/**
+ * A version of #bmesh_radial_loop_remove which only performs the radial unlink,
+ * leaving the edge untouched.
+ */
+void bmesh_radial_loop_unlink(BMLoop *l)
+{
+ if (l->radial_next != l) {
+ l->radial_next->radial_prev = l->radial_prev;
+ l->radial_prev->radial_next = l->radial_next;
+ }
+
+ /* l is no longer in a radial cycle; empty the links
+ * to the cycle and the link back to an edge */
+ l->radial_next = l->radial_prev = NULL;
+ l->e = NULL;
+}
/**
* \brief BME RADIAL FIND FIRST FACE VERT
@@ -484,30 +540,6 @@ int bmesh_radial_length(const BMLoop *l)
return i;
}
-void bmesh_radial_append(BMEdge *e, BMLoop *l)
-{
- if (e->l == NULL) {
- e->l = l;
- l->radial_next = l->radial_prev = l;
- }
- else {
- l->radial_prev = e->l;
- l->radial_next = e->l->radial_next;
-
- e->l->radial_next->radial_prev = l;
- e->l->radial_next = l;
-
- e->l = l;
- }
-
- if (UNLIKELY(l->e && l->e != e)) {
- /* l is already in a radial cycle for a different edge */
- BMESH_ASSERT(0);
- }
-
- l->e = e;
-}
-
/**
* \brief RADIAL COUNT FACE VERT
*
diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h
index 07f94796bb2..0efb25da37c 100644
--- a/source/blender/bmesh/intern/bmesh_structure.h
+++ b/source/blender/bmesh/intern/bmesh_structure.h
@@ -52,11 +52,13 @@ BLI_INLINE BMEdge *bmesh_disk_edge_prev(const BMEdge *e, const BMVert *v) ATTR_W
int bmesh_disk_facevert_count_ex(const BMVert *v, const int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
int bmesh_disk_facevert_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+BMLoop *bmesh_disk_faceloop_find_first(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
BMEdge *bmesh_disk_faceedge_find_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/* RADIAL CYCLE MANAGMENT */
-void bmesh_radial_append(BMEdge *e, BMLoop *l) ATTR_NONNULL();
-void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e) ATTR_NONNULL(1);
+void bmesh_radial_loop_append(BMEdge *e, BMLoop *l) ATTR_NONNULL();
+void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l) ATTR_NONNULL();
+void bmesh_radial_loop_unlink(BMLoop *l) ATTR_NONNULL();
/* note:
* bmesh_radial_loop_next(BMLoop *l) / prev.
* just use member access l->radial_next, l->radial_prev now */
diff --git a/source/blender/bmesh/operators/bmo_bridge.c b/source/blender/bmesh/operators/bmo_bridge.c
index 6ef0fd6b084..61179d7be70 100644
--- a/source/blender/bmesh/operators/bmo_bridge.c
+++ b/source/blender/bmesh/operators/bmo_bridge.c
@@ -398,7 +398,8 @@ static void bridge_loop_pair(
if (v_b != v_b_next) {
BMVert *v_arr[4] = {v_a, v_b, v_b_next, v_a_next};
- if (BM_face_exists(v_arr, 4, &f) == false) {
+ f = BM_face_exists(v_arr, 4);
+ if (f == NULL) {
/* copy if loop data if its is missing on one ring */
f = BM_face_create_verts(bm, v_arr, 4, NULL, BM_CREATE_NOP, true);
@@ -411,7 +412,8 @@ static void bridge_loop_pair(
}
else {
BMVert *v_arr[3] = {v_a, v_b, v_a_next};
- if (BM_face_exists(v_arr, 3, &f) == false) {
+ f = BM_face_exists(v_arr, 3);
+ if (f == NULL) {
/* fan-fill a triangle */
f = BM_face_create_verts(bm, v_arr, 3, NULL, BM_CREATE_NOP, true);
diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c
index 05efb14a699..6e3a8a1473d 100644
--- a/source/blender/bmesh/operators/bmo_dissolve.c
+++ b/source/blender/bmesh/operators/bmo_dissolve.c
@@ -322,12 +322,12 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
}
BMO_ITER (e, &eiter, op->slots_in, "edges", BM_EDGE) {
- BMFace *fa, *fb;
- if (BM_edge_face_pair(e, &fa, &fb)) {
+ BMLoop *l_a, *l_b;
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
BMFace *f_new;
/* join faces */
- f_new = BM_faces_join_pair(bm, fa, fb, e, false);
+ f_new = BM_faces_join_pair(bm, l_a, l_b, false);
if (f_new) {
/* maintain active face */
@@ -437,12 +437,12 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
if (!BMO_vert_flag_test(bm, v, VERT_MARK_PAIR)) {
BM_ITER_ELEM (e, &itersub, v, BM_EDGES_OF_VERT) {
- BMFace *fa, *fb;
- if (BM_edge_face_pair(e, &fa, &fb)) {
+ BMLoop *l_a, *l_b;
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
BMFace *f_new;
/* join faces */
- f_new = BM_faces_join_pair(bm, fa, fb, e, false);
+ f_new = BM_faces_join_pair(bm, l_a, l_b, false);
/* maintain active face */
if (act_face && bm->act_face == NULL) {
diff --git a/source/blender/bmesh/operators/bmo_fill_edgeloop.c b/source/blender/bmesh/operators/bmo_fill_edgeloop.c
index c68130bc11d..f33a60ccc5c 100644
--- a/source/blender/bmesh/operators/bmo_fill_edgeloop.c
+++ b/source/blender/bmesh/operators/bmo_fill_edgeloop.c
@@ -136,7 +136,7 @@ void bmo_edgeloop_fill_exec(BMesh *bm, BMOperator *op)
i++;
} while ((v != f_verts[0]));
- if (BM_face_exists(f_verts, i, NULL) == false) {
+ if (!BM_face_exists(f_verts, i)) {
BMFace *f;
/* don't use calc_edges option because we already have the edges */
diff --git a/source/blender/bmesh/operators/bmo_hull.c b/source/blender/bmesh/operators/bmo_hull.c
index 9c41e4f2115..81ec2860cf7 100644
--- a/source/blender/bmesh/operators/bmo_hull.c
+++ b/source/blender/bmesh/operators/bmo_hull.c
@@ -119,7 +119,8 @@ static void hull_output_triangles(BMesh *bm, GSet *hull_triangles)
};
BMFace *f, *example = NULL;
- if (BM_face_exists(t->v, 3, &f)) {
+ f = BM_face_exists(t->v, 3);
+ if (f != NULL) {
/* If the operator is run with "use_existing_faces"
* disabled, but an output face in the hull is the
* same as a face in the existing mesh, it should not
diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c
index bc620e4a020..655fb346976 100644
--- a/source/blender/bmesh/operators/bmo_join_triangles.c
+++ b/source/blender/bmesh/operators/bmo_join_triangles.c
@@ -361,16 +361,16 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
qsort(jedges, totedge, sizeof(*jedges), BLI_sortutil_cmp_float);
for (i = 0; i < totedge; i++) {
- BMFace *f_a, *f_b;
+ BMLoop *l_a, *l_b;
e = jedges[i].data;
- f_a = e->l->f;
- f_b = e->l->radial_next->f;
+ l_a = e->l;
+ l_b = e->l->radial_next;
/* check if another edge already claimed this face */
- if ((f_a->len == 3) && (f_b->len == 3)) {
+ if ((l_a->f->len == 3) && (l_b->f->len == 3)) {
BMFace *f_new;
- f_new = BM_faces_join_pair(bm, f_a, f_b, e, true);
+ f_new = BM_faces_join_pair(bm, l_a, l_b, true);
if (f_new) {
BMO_face_flag_enable(bm, f_new, FACE_OUT);
}
diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c
index 6da591b23a0..0ad8247e539 100644
--- a/source/blender/bmesh/operators/bmo_removedoubles.c
+++ b/source/blender/bmesh/operators/bmo_removedoubles.c
@@ -160,7 +160,7 @@ finally:
}
if (STACK_SIZE(edges) >= 3) {
- if (!BM_face_exists(verts, STACK_SIZE(edges), NULL)) {
+ if (!BM_face_exists(verts, STACK_SIZE(edges))) {
BMFace *f_new = BM_face_create(bm, verts, edges, STACK_SIZE(edges), f, BM_CREATE_NOP);
BLI_assert(f_new != f);
diff --git a/source/blender/bmesh/operators/bmo_triangulate.c b/source/blender/bmesh/operators/bmo_triangulate.c
index 8938d086c1a..6bd3174d27a 100644
--- a/source/blender/bmesh/operators/bmo_triangulate.c
+++ b/source/blender/bmesh/operators/bmo_triangulate.c
@@ -253,10 +253,7 @@ void bmo_triangle_fill_exec(BMesh *bm, BMOperator *op)
if (BMO_edge_flag_test(bm, e, ELE_NEW)) {
/* in rare cases the edges face will have already been removed from the edge */
if (LIKELY(e->l)) {
- BMFace *f_new = BM_faces_join_pair(
- bm, e->l->f,
- e->l->radial_next->f, e,
- false); /* join faces */
+ BMFace *f_new = BM_faces_join_pair(bm, e->l, e->l->radial_next, false);
if (f_new) {
BMO_face_flag_enable(bm, f_new, ELE_NEW);
BM_edge_kill(bm, e);
diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
index 978cceee37c..e2c36299ddf 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
@@ -322,9 +322,7 @@ void BM_mesh_decimate_dissolve_ex(
i = BM_elem_index_get(e);
if (BM_edge_is_manifold(e)) {
- f_new = BM_faces_join_pair(bm, e->l->f,
- e->l->radial_next->f, e,
- false); /* join faces */
+ f_new = BM_faces_join_pair(bm, e->l, e->l->radial_next, false);
if (f_new) {
BMLoop *l_first, *l_iter;
diff --git a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
index 0fc571bc0a8..92300ae66a2 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
@@ -74,7 +74,7 @@ static bool bm_vert_dissolve_fan_test(BMVert *v)
((tot_edge == 3) && (tot_edge_boundary == 0) && (tot_edge_manifold == 3)) ||
((tot_edge == 3) && (tot_edge_boundary == 2) && (tot_edge_manifold == 1)))
{
- if (!BM_face_exists(varr, tot_edge, NULL)) {
+ if (!BM_face_exists(varr, tot_edge)) {
return true;
}
}
diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp
index 6ff6de33d56..8f3bf88af65 100644
--- a/source/blender/collada/MeshImporter.cpp
+++ b/source/blender/collada/MeshImporter.cpp
@@ -317,11 +317,6 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su
}
}
- if (mesh->getPositions().empty()) {
- fprintf(stderr, "ERROR: Mesh %s has no vertices.\n", name.c_str());
- return false;
- }
-
return true;
}
@@ -329,11 +324,15 @@ void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me)
{
// vertices
COLLADAFW::MeshVertexData& pos = mesh->getPositions();
+ if (pos.empty()) {
+ return;
+ }
+
int stride = pos.getStride(0);
if (stride == 0) stride = 3;
-
- me->totvert = mesh->getPositions().getFloatValues()->getCount() / stride;
- me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert);
+
+ me->totvert = pos.getFloatValues()->getCount() / stride;
+ me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert);
MVert *mvert;
int i;
diff --git a/source/blender/collada/TransformWriter.cpp b/source/blender/collada/TransformWriter.cpp
index b16e2e2b0d3..908111ebae6 100644
--- a/source/blender/collada/TransformWriter.cpp
+++ b/source/blender/collada/TransformWriter.cpp
@@ -113,11 +113,6 @@ void TransformWriter::add_node_transform_ob(COLLADASW::Node& node, Object *ob, B
node.addMatrix("transform",d_obmat);
break;
}
- case BC_TRANSFORMATION_TYPE_BOTH:
- {
- node.addMatrix("transform",d_obmat);
- /* fall-through */
- }
case BC_TRANSFORMATION_TYPE_TRANSROTLOC:
{
float loc[3], rot[3], scale[3];
diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h
index 0017c66836a..a4416608584 100644
--- a/source/blender/collada/collada.h
+++ b/source/blender/collada/collada.h
@@ -43,8 +43,7 @@ typedef enum BC_export_mesh_type {
typedef enum BC_export_transformation_type {
BC_TRANSFORMATION_TYPE_MATRIX,
- BC_TRANSFORMATION_TYPE_TRANSROTLOC,
- BC_TRANSFORMATION_TYPE_BOTH
+ BC_TRANSFORMATION_TYPE_TRANSROTLOC
} BC_export_transformation_type;
struct bContext;
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cpp b/source/blender/compositor/intern/COM_ExecutionGroup.cpp
index e5c2b8ace4e..9a47c6b2438 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cpp
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cpp
@@ -383,7 +383,7 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo
if (this->m_chunkExecutionStates[chunkNumber] == COM_ES_SCHEDULED)
this->m_chunkExecutionStates[chunkNumber] = COM_ES_EXECUTED;
- atomic_add_u(&this->m_chunksFinished, 1);
+ atomic_add_and_fetch_u(&this->m_chunksFinished, 1);
if (memoryBuffers) {
for (unsigned int index = 0; index < this->m_cachedMaxReadBufferOffset; index++) {
MemoryBuffer *buffer = memoryBuffers[index];
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cpp b/source/blender/compositor/operations/COM_TextureOperation.cpp
index bba5c8702b8..6bfd8ae3888 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cpp
+++ b/source/blender/compositor/operations/COM_TextureOperation.cpp
@@ -118,7 +118,7 @@ void TextureBaseOperation::executePixelSampled(float output[4], float x, float y
* interpolaiton and (b) in such configuration multitex() sinply floor's the value
* which often produces artifacts.
*/
- if ((m_texture->imaflag & TEX_INTERPOL) == 0) {
+ if (m_texture != NULL && (m_texture->imaflag & TEX_INTERPOL) == 0) {
u += 0.5f / cx;
v += 0.5f / cy;
}
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index fd2a521bec5..ab12a8d5b3e 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -45,6 +45,7 @@ set(SRC
intern/builder/deg_builder_nodes.cc
intern/builder/deg_builder_pchanmap.cc
intern/builder/deg_builder_relations.cc
+ intern/builder/deg_builder_relations_keys.cc
intern/builder/deg_builder_transitive.cc
intern/debug/deg_debug_graphviz.cc
intern/eval/deg_eval.cc
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 0945da439ef..fdc86540171 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -51,8 +51,12 @@ extern "C" {
/* Graph Building -------------------------------- */
-/* Build depsgraph for the given scene, and dump results in given graph container */
-void DEG_graph_build_from_scene(struct Depsgraph *graph, struct Main *bmain, struct Scene *scene);
+/* Build depsgraph for the given scene, and dump results in given
+ * graph container.
+ */
+void DEG_graph_build_from_scene(struct Depsgraph *graph,
+ struct Main *bmain,
+ struct Scene *scene);
/* Tag relations from the given graph for update. */
void DEG_graph_tag_relations_update(struct Depsgraph *graph);
@@ -85,31 +89,69 @@ struct CacheFile;
struct Object;
typedef enum eDepsSceneComponentType {
- DEG_SCENE_COMP_PARAMETERS, /* Parameters Component - Default when nothing else fits (i.e. just SDNA property setting) */
- DEG_SCENE_COMP_ANIMATION, /* Animation Component */ // XXX: merge in with parameters?
- DEG_SCENE_COMP_SEQUENCER, /* Sequencer Component (Scene Only) */
+ /* Parameters Component - Default when nothing else fits
+ * (i.e. just SDNA property setting).
+ */
+ DEG_SCENE_COMP_PARAMETERS,
+ /* Animation Component
+ * TODO(sergey): merge in with parameters?
+ */
+ DEG_SCENE_COMP_ANIMATION,
+ /* Sequencer Component (Scene Only). */
+ DEG_SCENE_COMP_SEQUENCER,
} eDepsSceneComponentType;
typedef enum eDepsObjectComponentType {
- DEG_OB_COMP_PARAMETERS, /* Parameters Component - Default when nothing else fits (i.e. just SDNA property setting) */
- DEG_OB_COMP_PROXY, /* Generic "Proxy-Inherit" Component */ // XXX: Also for instancing of subgraphs?
- DEG_OB_COMP_ANIMATION, /* Animation Component */ // XXX: merge in with parameters?
- DEG_OB_COMP_TRANSFORM, /* Transform Component (Parenting/Constraints) */
- DEG_OB_COMP_GEOMETRY, /* Geometry Component (DerivedMesh/Displist) */
-
+ /* Parameters Component - Default when nothing else fits
+ * (i.e. just SDNA property setting).
+ */
+ DEG_OB_COMP_PARAMETERS,
+ /* Generic "Proxy-Inherit" Component.
+ * TODO(sergey): Also for instancing of subgraphs?
+ */
+ DEG_OB_COMP_PROXY,
+ /* Animation Component.
+ *
+ * TODO(sergey): merge in with parameters?
+ */
+ DEG_OB_COMP_ANIMATION,
+ /* Transform Component (Parenting/Constraints) */
+ DEG_OB_COMP_TRANSFORM,
+ /* Geometry Component (DerivedMesh/Displist) */
+ DEG_OB_COMP_GEOMETRY,
+
/* Evaluation-Related Outer Types (with Subdata) */
- DEG_OB_COMP_EVAL_POSE, /* Pose Component - Owner/Container of Bones Eval */
- DEG_OB_COMP_BONE, /* Bone Component - Child/Subcomponent of Pose */
-
- DEG_OB_COMP_EVAL_PARTICLES, /* Particle Systems Component */
- DEG_OB_COMP_SHADING, /* Material Shading Component */
- DEG_OB_COMP_CACHE, /* Cache Component */
+
+ /* Pose Component - Owner/Container of Bones Eval */
+ DEG_OB_COMP_EVAL_POSE,
+ /* Bone Component - Child/Subcomponent of Pose */
+ DEG_OB_COMP_BONE,
+
+ /* Particle Systems Component */
+ DEG_OB_COMP_EVAL_PARTICLES,
+ /* Material Shading Component */
+ DEG_OB_COMP_SHADING,
+ /* Cache Component */
+ DEG_OB_COMP_CACHE,
} eDepsObjectComponentType;
-void DEG_add_scene_relation(struct DepsNodeHandle *node, struct Scene *scene, eDepsSceneComponentType component, const char *description);
-void DEG_add_object_relation(struct DepsNodeHandle *node, struct Object *ob, eDepsObjectComponentType component, const char *description);
-void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description);
-void DEG_add_object_cache_relation(struct DepsNodeHandle *handle, struct CacheFile *cache_file, eDepsObjectComponentType component, const char *description);
+void DEG_add_scene_relation(struct DepsNodeHandle *node,
+ struct Scene *scene,
+ eDepsSceneComponentType component,
+ const char *description);
+void DEG_add_object_relation(struct DepsNodeHandle *node, struct
+ Object *ob,
+ eDepsObjectComponentType component,
+ const char *description);
+void DEG_add_bone_relation(struct DepsNodeHandle *handle,
+ struct Object *ob,
+ const char *bone_name,
+ eDepsObjectComponentType component,
+ const char *description);
+void DEG_add_object_cache_relation(struct DepsNodeHandle *handle,
+ struct CacheFile *cache_file,
+ eDepsObjectComponentType component,
+ const char *description);
/* TODO(sergey): Remove once all geometry update is granular. */
void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short flag);
@@ -117,8 +159,22 @@ void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short fla
/* Utility functions for physics modifiers */
typedef bool (*DEG_CollobjFilterFunction)(struct Object *obj, struct ModifierData *md);
-void DEG_add_collision_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name);
-void DEG_add_forcefield_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name);
+void DEG_add_collision_relations(struct DepsNodeHandle *handle,
+ struct Scene *scene,
+ Object *ob,
+ struct Group *group,
+ int layer,
+ unsigned int modifier_type,
+ DEG_CollobjFilterFunction fn,
+ bool dupli,
+ const char *name);
+void DEG_add_forcefield_relations(struct DepsNodeHandle *handle,
+ struct Scene *scene,
+ Object *ob,
+ struct EffectorWeights *eff,
+ bool add_absorption,
+ int skip_forcefield,
+ const char *name);
/* ************************************************ */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index 6169100d574..8939e4cc93a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -34,6 +34,8 @@
#include <stack>
#include "DNA_anim_types.h"
+#include "DNA_object_types.h"
+#include "DNA_ID.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@@ -56,10 +58,46 @@ string deg_fcurve_id_name(const FCurve *fcu)
return string(fcu->rna_path) + index_buf;
}
+static bool check_object_needs_evaluation(Object *object)
+{
+ if (object->recalc & OB_RECALC_ALL) {
+ /* Object is tagged for update anyway, no need to re-tag it. */
+ return false;
+ }
+ if (object->type == OB_MESH) {
+ return object->derivedFinal == NULL;
+ }
+ else if (ELEM(object->type,
+ OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE))
+ {
+ return object->curve_cache == NULL;
+ }
+ return false;
+}
+
void deg_graph_build_finalize(Depsgraph *graph)
{
+ /* STEP 1: Make sure new invisible dependencies are ready for use.
+ *
+ * TODO(sergey): This might do a bit of extra tagging, but it's kinda nice
+ * to do it ahead of a time and don't spend time on flushing updates on
+ * every frame change.
+ */
+ GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash)
+ {
+ if (id_node->layers == 0 || 1) {
+ ID *id = id_node->id;
+ if (GS(id->name) == ID_OB) {
+ Object *object = (Object *)id;
+ if (check_object_needs_evaluation(object)) {
+ id_node->tag_update(graph);
+ }
+ }
+ }
+ }
+ GHASH_FOREACH_END();
+ /* STEP 2: Flush visibility layers from children to parent. */
std::stack<OperationDepsNode *> stack;
-
foreach (OperationDepsNode *node, graph->operations) {
IDDepsNode *id_node = node->owner->owner;
node->done = 0;
@@ -78,7 +116,6 @@ void deg_graph_build_finalize(Depsgraph *graph)
node->owner->layers = id_node->layers;
id_node->id->tag |= LIB_TAG_DOIT;
}
-
while (!stack.empty()) {
OperationDepsNode *node = stack.top();
stack.pop();
@@ -104,8 +141,9 @@ void deg_graph_build_finalize(Depsgraph *graph)
}
}
}
-
- /* Re-tag IDs for update if it was tagged before the relations update tag. */
+ /* STEP 3: Re-tag IDs for update if it was tagged before the relations
+ * update tag.
+ */
GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash)
{
GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp, id_node->components)
@@ -121,6 +159,13 @@ void deg_graph_build_finalize(Depsgraph *graph)
id_node->tag_update(graph);
id->tag &= ~LIB_TAG_DOIT;
}
+ else if (GS(id->name) == ID_OB) {
+ Object *object = (Object *)id;
+ if (object->recalc & OB_RECALC_ALL) {
+ id_node->tag_update(graph);
+ id->tag &= ~LIB_TAG_DOIT;
+ }
+ }
id_node->finalize_build();
}
GHASH_FOREACH_END();
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
index 225cc64ae4d..9b37aaa12ff 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
@@ -56,12 +56,14 @@ struct StackEntry {
void deg_graph_detect_cycles(Depsgraph *graph)
{
- /* Not is not visited at all during traversal. */
- const int NODE_NOT_VISITED = 0;
- /* Node has been visited during traversal and not in current stack. */
- const int NODE_VISITED = 1;
- /* Node has been visited during traversal and is in current stack. */
- const int NODE_IN_STACK = 2;
+ enum {
+ /* Not is not visited at all during traversal. */
+ NODE_NOT_VISITED = 0,
+ /* Node has been visited during traversal and not in current stack. */
+ NODE_VISITED = 1,
+ /* Node has been visited during traversal and is in current stack. */
+ NODE_IN_STACK = 2,
+ };
std::stack<StackEntry> traversal_stack;
foreach (OperationDepsNode *node, graph->operations) {
@@ -77,21 +79,23 @@ void deg_graph_detect_cycles(Depsgraph *graph)
entry.from = NULL;
entry.via_relation = NULL;
traversal_stack.push(entry);
- node->done = NODE_IN_STACK;
+ node->tag = NODE_IN_STACK;
}
else {
- node->done = NODE_NOT_VISITED;
+ node->tag = NODE_NOT_VISITED;
}
+ node->done = 0;
}
while (!traversal_stack.empty()) {
- StackEntry &entry = traversal_stack.top();
+ StackEntry& entry = traversal_stack.top();
OperationDepsNode *node = entry.node;
bool all_child_traversed = true;
- foreach (DepsRelation *rel, node->outlinks) {
+ for (int i = node->done; i < node->outlinks.size(); ++i) {
+ DepsRelation *rel = node->outlinks[i];
if (rel->to->type == DEPSNODE_TYPE_OPERATION) {
OperationDepsNode *to = (OperationDepsNode *)rel->to;
- if (to->done == NODE_IN_STACK) {
+ if (to->tag == NODE_IN_STACK) {
printf("Dependency cycle detected:\n");
printf(" '%s' depends on '%s' through '%s'\n",
to->full_identifier().c_str(),
@@ -107,23 +111,24 @@ void deg_graph_detect_cycles(Depsgraph *graph)
current->via_relation->name);
current = current->from;
}
- /* TODO(sergey): So called roussian rlette cycle solver. */
+ /* TODO(sergey): So called russian roulette cycle solver. */
rel->flag |= DEPSREL_FLAG_CYCLIC;
}
- else if (to->done == NODE_NOT_VISITED) {
+ else if (to->tag == NODE_NOT_VISITED) {
StackEntry new_entry;
new_entry.node = to;
new_entry.from = &entry;
new_entry.via_relation = rel;
traversal_stack.push(new_entry);
- to->done = NODE_IN_STACK;
+ to->tag = NODE_IN_STACK;
all_child_traversed = false;
+ node->done = i;
break;
}
}
}
if (all_child_traversed) {
- node->done = NODE_VISITED;
+ node->tag = NODE_VISITED;
traversal_stack.pop();
}
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 1812384440f..12050e3e003 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -34,7 +34,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include "MEM_guardedalloc.h"
@@ -109,6 +108,40 @@ extern "C" {
namespace DEG {
+namespace {
+
+struct BuilderWalkUserData {
+ DepsgraphNodeBuilder *builder;
+ Scene *scene;
+};
+
+static void modifier_walk(void *user_data,
+ struct Object * /*ob*/,
+ struct Object **obpoin,
+ int /*cd_flag*/)
+{
+ BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
+ if (*obpoin) {
+ data->builder->build_object(data->scene, NULL, *obpoin);
+ }
+}
+
+void constraint_walk(bConstraint * /*con*/,
+ ID **idpoin,
+ bool /*is_reference*/,
+ void *user_data)
+{
+ BuilderWalkUserData *data = (BuilderWalkUserData *)user_data;
+ if (*idpoin) {
+ ID *id = *idpoin;
+ if (GS(id->name) == ID_OB) {
+ data->builder->build_object(data->scene, NULL, (Object *)id);
+ }
+ }
+}
+
+} /* namespace */
+
/* ************ */
/* Node Builder */
@@ -131,8 +164,7 @@ RootDepsNode *DepsgraphNodeBuilder::add_root_node()
IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id)
{
- const char *idtype_name = BKE_idcode_to_name(GS(id->name));
- return m_graph->add_id_node(id, string(id->name + 2) + "[" + idtype_name + "]");
+ return m_graph->add_id_node(id, id->name);
}
TimeSourceDepsNode *DepsgraphNodeBuilder::add_time_source(ID *id)
@@ -179,7 +211,7 @@ TimeSourceDepsNode *DepsgraphNodeBuilder::add_time_source(ID *id)
ComponentDepsNode *DepsgraphNodeBuilder::add_component_node(
ID *id,
eDepsNode_Type comp_type,
- const string &comp_name)
+ const char *comp_name)
{
IDDepsNode *id_node = add_id_node(id);
ComponentDepsNode *comp_node = id_node->add_component(comp_type, comp_name);
@@ -192,15 +224,19 @@ OperationDepsNode *DepsgraphNodeBuilder::add_operation_node(
eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string &description)
+ const char *name,
+ int name_tag)
{
- OperationDepsNode *op_node = comp_node->has_operation(opcode, description);
+ OperationDepsNode *op_node = comp_node->has_operation(opcode,
+ name,
+ name_tag);
if (op_node == NULL) {
- op_node = comp_node->add_operation(optype, op, opcode, description);
+ op_node = comp_node->add_operation(optype, op, opcode, name, name_tag);
m_graph->operations.push_back(op_node);
}
else {
- fprintf(stderr, "add_operation: Operation already exists - %s has %s at %p\n",
+ fprintf(stderr,
+ "add_operation: Operation already exists - %s has %s at %p\n",
comp_node->identifier().c_str(),
op_node->identifier().c_str(),
op_node);
@@ -212,14 +248,15 @@ OperationDepsNode *DepsgraphNodeBuilder::add_operation_node(
OperationDepsNode *DepsgraphNodeBuilder::add_operation_node(
ID *id,
eDepsNode_Type comp_type,
- const string &comp_name,
+ const char *comp_name,
eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string &description)
+ const char *name,
+ int name_tag)
{
ComponentDepsNode *comp_node = add_component_node(id, comp_type, comp_name);
- return add_operation_node(comp_node, optype, op, opcode, description);
+ return add_operation_node(comp_node, optype, op, opcode, name, name_tag);
}
OperationDepsNode *DepsgraphNodeBuilder::add_operation_node(
@@ -228,38 +265,54 @@ OperationDepsNode *DepsgraphNodeBuilder::add_operation_node(
eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string& description)
+ const char *name,
+ int name_tag)
{
- return add_operation_node(id, comp_type, "", optype, op, opcode, description);
+ return add_operation_node(id,
+ comp_type,
+ "",
+ optype,
+ op,
+ opcode,
+ name,
+ name_tag);
}
bool DepsgraphNodeBuilder::has_operation_node(ID *id,
eDepsNode_Type comp_type,
- const string &comp_name,
+ const char *comp_name,
eDepsOperation_Code opcode,
- const string &description)
+ const char *name,
+ int name_tag)
{
- return find_operation_node(id, comp_type, comp_name, opcode, description) != NULL;
+ return find_operation_node(id,
+ comp_type,
+ comp_name,
+ opcode,
+ name,
+ name_tag) != NULL;
}
OperationDepsNode *DepsgraphNodeBuilder::find_operation_node(
ID *id,
eDepsNode_Type comp_type,
- const string &comp_name,
+ const char *comp_name,
eDepsOperation_Code opcode,
- const string &description)
+ const char *name,
+ int name_tag)
{
ComponentDepsNode *comp_node = add_component_node(id, comp_type, comp_name);
- return comp_node->has_operation(opcode, description);
+ return comp_node->has_operation(opcode, name, name_tag);
}
OperationDepsNode *DepsgraphNodeBuilder::find_operation_node(
ID *id,
eDepsNode_Type comp_type,
eDepsOperation_Code opcode,
- const string& description)
+ const char *name,
+ int name_tag)
{
- return find_operation_node(id, comp_type, "", opcode, description);
+ return find_operation_node(id, comp_type, "", opcode, name, name_tag);
}
/* **** Build functions for entity nodes **** */
@@ -386,19 +439,22 @@ SubgraphDepsNode *DepsgraphNodeBuilder::build_subgraph(Group *group)
{
/*Object *ob = go->ob;*/
- /* Each "group object" is effectively a separate instance of the underlying
- * object data. When the group is evaluated, the transform results and/or
- * some other attributes end up getting overridden by the group
+ /* Each "group object" is effectively a separate instance of the
+ * underlying object data. When the group is evaluated, the transform
+ * results and/or some other attributes end up getting overridden by
+ * the group.
*/
}
- /* create a node for representing subgraph */
+ /* Create a node for representing subgraph. */
SubgraphDepsNode *subgraph_node = m_graph->add_subgraph_node(&group->id);
subgraph_node->graph = subgraph;
- /* make a copy of the data this node will need? */
- // XXX: do we do this now, or later?
- // TODO: need API function which queries graph's ID's hash, and duplicates those blocks thoroughly with all outside links removed...
+ /* Make a copy of the data this node will need? */
+ /* XXX: do we do this now, or later? */
+ /* TODO: need API function which queries graph's ID's hash, and duplicates
+ * those blocks thoroughly with all outside links removed.
+ */
return subgraph_node;
}
@@ -407,18 +463,40 @@ void DepsgraphNodeBuilder::build_object(Scene *scene, Base *base, Object *ob)
{
if (ob->id.tag & LIB_TAG_DOIT) {
IDDepsNode *id_node = m_graph->find_id_node(&ob->id);
- id_node->layers |= base->lay;
+ if (base != NULL) {
+ id_node->layers |= base->lay;
+ }
return;
}
+ ob->id.tag |= LIB_TAG_DOIT;
IDDepsNode *id_node = add_id_node(&ob->id);
- id_node->layers |= base->lay;
+ if (base != NULL) {
+ id_node->layers |= base->lay;
+ }
ob->customdata_mask = 0;
- /* standard components */
+ /* Standard components. */
build_object_transform(scene, ob);
- /* object data */
+ if (ob->parent != NULL) {
+ build_object(scene, NULL, ob->parent);
+ }
+ if (ob->modifiers.first != NULL) {
+ BuilderWalkUserData data;
+ data.builder = this;
+ data.scene = scene;
+ modifiers_foreachObjectLink(ob, modifier_walk, &data);
+ }
+ if (ob->constraints.first != NULL) {
+ BuilderWalkUserData data;
+ data.builder = this;
+ data.scene = scene;
+ modifiers_foreachObjectLink(ob, modifier_walk, &data);
+ BKE_constraints_id_loop(&ob->constraints, constraint_walk, &data);
+ }
+
+ /* Object data. */
if (ob->data) {
/* type-specific data... */
switch (ob->type) {
@@ -428,15 +506,6 @@ void DepsgraphNodeBuilder::build_object(Scene *scene, Base *base, Object *ob)
case OB_SURF:
case OB_MBALL:
case OB_LATTICE:
- {
- /* TODO(sergey): This way using this object's
- * properties as driver target works fine.
- *
- * Does this depend on other nodes?
- */
- add_operation_node(&ob->id, DEPSNODE_TYPE_PARAMETERS, DEPSOP_TYPE_POST, NULL,
- DEG_OPCODE_PLACEHOLDER, "Parameters Eval");
-
build_obdata_geom(scene, ob);
/* TODO(sergey): Only for until we support granular
* update of curves.
@@ -448,7 +517,6 @@ void DepsgraphNodeBuilder::build_object(Scene *scene, Base *base, Object *ob)
}
}
break;
- }
case OB_ARMATURE: /* Pose */
if (ID_IS_LINKED_DATABLOCK(ob) && ob->proxy_from != NULL) {
@@ -617,12 +685,17 @@ OperationDepsNode *DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcu)
OperationDepsNode *driver_op = find_operation_node(id,
DEPSNODE_TYPE_PARAMETERS,
DEG_OPCODE_DRIVER,
- deg_fcurve_id_name(fcu));
+ fcu->rna_path,
+ fcu->array_index);
if (driver_op == NULL) {
- driver_op = add_operation_node(id, DEPSNODE_TYPE_PARAMETERS,
- DEPSOP_TYPE_EXEC, function_bind(BKE_animsys_eval_driver, _1, id, fcu),
- DEG_OPCODE_DRIVER, deg_fcurve_id_name(fcu));
+ driver_op = add_operation_node(id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEPSOP_TYPE_EXEC,
+ function_bind(BKE_animsys_eval_driver, _1, id, fcu),
+ DEG_OPCODE_DRIVER,
+ fcu->rna_path,
+ fcu->array_index);
}
/* tag "scripted expression" drivers as needing Python (due to GIL issues, etc.) */
@@ -807,7 +880,7 @@ void DepsgraphNodeBuilder::build_rig(Scene *scene, Object *ob)
/* Rebuild pose if not up to date. */
if (ob->pose == NULL || (ob->pose->flag & POSE_RECALC)) {
- BKE_pose_rebuild(ob, arm);
+ BKE_pose_rebuild_ex(ob, arm, false);
/* XXX: Without this animation gets lost in certain circumstances
* after loading file. Need to investigate further since it does
* not happen with simple scenes..
@@ -972,6 +1045,18 @@ void DepsgraphNodeBuilder::build_obdata_geom(Scene *scene, Object *ob)
{
ID *obdata = (ID *)ob->data;
+ /* TODO(sergey): This way using this object's properties as driver target
+ * works fine.
+ *
+ * Does this depend on other nodes?
+ */
+ add_operation_node(&ob->id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEPSOP_TYPE_POST,
+ NULL,
+ DEG_OPCODE_PLACEHOLDER,
+ "Parameters Eval");
+
/* Temporary uber-update node, which does everything.
* It is for the being we're porting old dependencies into the new system.
* We'll get rid of this node as soon as all the granular update functions
@@ -979,35 +1064,45 @@ void DepsgraphNodeBuilder::build_obdata_geom(Scene *scene, Object *ob)
*
* TODO(sergey): Get rid of this node.
*/
- add_operation_node(&ob->id, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_POST, function_bind(BKE_object_eval_uber_data, _1, scene, ob),
+ add_operation_node(&ob->id,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_POST,
+ function_bind(BKE_object_eval_uber_data, _1, scene, ob),
DEG_OPCODE_GEOMETRY_UBEREVAL);
- add_operation_node(&ob->id, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_INIT, NULL,
- DEG_OPCODE_PLACEHOLDER, "Eval Init");
+ add_operation_node(&ob->id,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_INIT,
+ NULL,
+ DEG_OPCODE_PLACEHOLDER,
+ "Eval Init");
// TODO: "Done" operation
/* Modifiers */
if (ob->modifiers.first) {
- ModifierData *md;
-
- for (md = (ModifierData *)ob->modifiers.first; md; md = md->next) {
- add_operation_node(&ob->id, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_EXEC, function_bind(BKE_object_eval_modifier, _1, scene, ob, md),
- DEG_OPCODE_GEOMETRY_MODIFIER, md->name);
+ for (ModifierData *md = (ModifierData *)ob->modifiers.first;
+ md != NULL;
+ md = md->next)
+ {
+ add_operation_node(&ob->id,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_EXEC,
+ function_bind(BKE_object_eval_modifier,
+ _1,
+ scene,
+ ob,
+ md),
+ DEG_OPCODE_GEOMETRY_MODIFIER,
+ md->name);
}
}
/* materials */
if (ob->totcol) {
- int a;
-
- for (a = 1; a <= ob->totcol; a++) {
+ for (int a = 1; a <= ob->totcol; a++) {
Material *ma = give_current_material(ob, a);
-
- if (ma) {
+ if (ma != NULL) {
// XXX?!
ComponentDepsNode *geom_node = add_component_node(&ob->id, DEPSNODE_TYPE_GEOMETRY);
build_material(geom_node, ma);
@@ -1032,16 +1127,23 @@ void DepsgraphNodeBuilder::build_obdata_geom(Scene *scene, Object *ob)
build_animdata(obdata);
- /* nodes for result of obdata's evaluation, and geometry evaluation on object */
+ /* Nodes for result of obdata's evaluation, and geometry
+ * evaluation on object.
+ */
switch (ob->type) {
case OB_MESH:
{
//Mesh *me = (Mesh *)ob->data;
/* evaluation operations */
- add_operation_node(obdata, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_INIT, function_bind(BKE_mesh_eval_geometry, _1, (Mesh *)obdata),
- DEG_OPCODE_PLACEHOLDER, "Geometry Eval");
+ add_operation_node(obdata,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_INIT,
+ function_bind(BKE_mesh_eval_geometry,
+ _1,
+ (Mesh *)obdata),
+ DEG_OPCODE_PLACEHOLDER,
+ "Geometry Eval");
break;
}
@@ -1049,48 +1151,76 @@ void DepsgraphNodeBuilder::build_obdata_geom(Scene *scene, Object *ob)
{
Object *mom = BKE_mball_basis_find(scene, ob);
- /* motherball - mom depends on children! */
+ /* Motherball - mom depends on children! */
if (mom == ob) {
/* metaball evaluation operations */
/* NOTE: only the motherball gets evaluated! */
- add_operation_node(obdata, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_INIT, function_bind(BKE_mball_eval_geometry, _1, (MetaBall *)obdata),
- DEG_OPCODE_PLACEHOLDER, "Geometry Eval");
+ add_operation_node(obdata,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_INIT,
+ function_bind(BKE_mball_eval_geometry,
+ _1,
+ (MetaBall *)obdata),
+ DEG_OPCODE_PLACEHOLDER,
+ "Geometry Eval");
}
break;
}
case OB_CURVE:
+ case OB_SURF:
case OB_FONT:
{
- /* curve evaluation operations */
+ /* Curve/nurms evaluation operations. */
/* - calculate curve geometry (including path) */
- add_operation_node(obdata, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_INIT, function_bind(BKE_curve_eval_geometry, _1, (Curve *)obdata),
- DEG_OPCODE_PLACEHOLDER, "Geometry Eval");
-
- /* - calculate curve path - this is used by constraints, etc. */
- add_operation_node(obdata, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_EXEC, function_bind(BKE_curve_eval_path, _1, (Curve *)obdata),
- DEG_OPCODE_GEOMETRY_PATH, "Path");
- break;
- }
+ add_operation_node(obdata,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_INIT,
+ function_bind(BKE_curve_eval_geometry,
+ _1,
+ (Curve *)obdata),
+ DEG_OPCODE_PLACEHOLDER,
+ "Geometry Eval");
+
+ /* Calculate curve path - this is used by constraints, etc. */
+ if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
+ add_operation_node(obdata,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_EXEC,
+ function_bind(BKE_curve_eval_path,
+ _1,
+ (Curve *)obdata),
+ DEG_OPCODE_GEOMETRY_PATH,
+ "Path");
+ }
- case OB_SURF: /* Nurbs Surface */
- {
- /* nurbs evaluation operations */
- add_operation_node(obdata, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_INIT, function_bind(BKE_curve_eval_geometry, _1, (Curve *)obdata),
- DEG_OPCODE_PLACEHOLDER, "Geometry Eval");
+ /* Make sure objects used for bevel.taper are in the graph.
+ * NOTE: This objects might be not linked to the scene.
+ */
+ Curve *cu = (Curve *)obdata;
+ if (cu->bevobj != NULL) {
+ build_object(scene, NULL, cu->bevobj);
+ }
+ if (cu->taperobj != NULL) {
+ build_object(scene, NULL, cu->bevobj);
+ }
+ if (ob->type == OB_FONT && cu->textoncurve != NULL) {
+ build_object(scene, NULL, cu->textoncurve);
+ }
break;
}
- case OB_LATTICE: /* Lattice */
+ case OB_LATTICE:
{
- /* lattice evaluation operations */
- add_operation_node(obdata, DEPSNODE_TYPE_GEOMETRY,
- DEPSOP_TYPE_INIT, function_bind(BKE_lattice_eval_geometry, _1, (Lattice *)obdata),
- DEG_OPCODE_PLACEHOLDER, "Geometry Eval");
+ /* Lattice evaluation operations. */
+ add_operation_node(obdata,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEPSOP_TYPE_INIT,
+ function_bind(BKE_lattice_eval_geometry,
+ _1,
+ (Lattice *)obdata),
+ DEG_OPCODE_PLACEHOLDER,
+ "Geometry Eval");
break;
}
}
@@ -1171,15 +1301,20 @@ void DepsgraphNodeBuilder::build_nodetree(DepsNode *owner_node, bNodeTree *ntree
/* nodetree's nodes... */
for (bNode *bnode = (bNode *)ntree->nodes.first; bnode; bnode = bnode->next) {
- if (bnode->id) {
- if (GS(bnode->id->name) == ID_MA) {
- build_material(owner_node, (Material *)bnode->id);
+ ID *id = bnode->id;
+ if (id != NULL) {
+ short id_type = GS(id->name);
+ if (id_type == ID_MA) {
+ build_material(owner_node, (Material *)id);
}
- else if (bnode->type == ID_TE) {
- build_texture(owner_node, (Tex *)bnode->id);
+ else if (id_type == ID_TE) {
+ build_texture(owner_node, (Tex *)id);
+ }
+ else if (id_type == ID_IM) {
+ build_image((Image *)id);
}
else if (bnode->type == NODE_GROUP) {
- bNodeTree *group_ntree = (bNodeTree *)bnode->id;
+ bNodeTree *group_ntree = (bNodeTree *)id;
if ((group_ntree->id.tag & LIB_TAG_DOIT) == 0) {
build_nodetree(owner_node, group_ntree);
}
@@ -1236,10 +1371,33 @@ void DepsgraphNodeBuilder::build_texture(DepsNode *owner_node, Tex *tex)
return;
}
tex_id->tag |= LIB_TAG_DOIT;
- /* texture itself */
+ /* Texture itself. */
build_animdata(tex_id);
- /* texture's nodetree */
+ /* Texture's nodetree. */
build_nodetree(owner_node, tex->nodetree);
+ /* Special cases for different IDs which texture uses. */
+ if (tex->type == TEX_IMAGE) {
+ if (tex->ima != NULL) {
+ build_image(tex->ima);
+ }
+ }
+}
+
+void DepsgraphNodeBuilder::build_image(Image *image) {
+ ID *image_id = &image->id;
+ if (image_id->tag & LIB_TAG_DOIT) {
+ return;
+ }
+ image_id->tag |= LIB_TAG_DOIT;
+ /* Image ID node itself. */
+ add_id_node(image_id);
+ /* Placeholder so we can add relations and tag ID node for update. */
+ add_operation_node(image_id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEPSOP_TYPE_EXEC,
+ NULL,
+ DEG_OPCODE_PLACEHOLDER,
+ "Image Eval");
}
void DepsgraphNodeBuilder::build_compositor(Scene *scene)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index f378f076804..72dc73357bf 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -38,6 +38,7 @@ struct bGPdata;
struct ListBase;
struct GHash;
struct ID;
+struct Image;
struct FCurve;
struct Group;
struct Key;
@@ -75,43 +76,49 @@ struct DepsgraphNodeBuilder {
ComponentDepsNode *add_component_node(ID *id,
eDepsNode_Type comp_type,
- const string& comp_name = "");
+ const char *comp_name = "");
OperationDepsNode *add_operation_node(ComponentDepsNode *comp_node,
eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string& description = "");
+ const char *name = "",
+ int name_tag = -1);
OperationDepsNode *add_operation_node(ID *id,
eDepsNode_Type comp_type,
- const string& comp_name,
+ const char *comp_name,
eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string& description = "");
+ const char *name = "",
+ int name_tag = -1);
OperationDepsNode *add_operation_node(ID *id,
eDepsNode_Type comp_type,
eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string& description = "");
+ const char *name = "",
+ int name_tag = -1);
bool has_operation_node(ID *id,
eDepsNode_Type comp_type,
- const string& comp_name,
+ const char *comp_name,
eDepsOperation_Code opcode,
- const string& description = "");
+ const char *name = "",
+ int name_tag = -1);
OperationDepsNode *find_operation_node(ID *id,
eDepsNode_Type comp_type,
- const string &comp_name,
+ const char *comp_name,
eDepsOperation_Code opcode,
- const string &description = "");
+ const char *name = "",
+ int name_tag = -1);
OperationDepsNode *find_operation_node(ID *id,
eDepsNode_Type comp_type,
eDepsOperation_Code opcode,
- const string &description = "");
+ const char *name = "",
+ int name_tag = -1);
void build_scene(Main *bmain, Scene *scene);
SubgraphDepsNode *build_subgraph(Group *group);
@@ -142,6 +149,7 @@ struct DepsgraphNodeBuilder {
void build_material(DepsNode *owner_node, Material *ma);
void build_texture(DepsNode *owner_node, Tex *tex);
void build_texture_stack(DepsNode *owner_node, MTex **texture_stack);
+ void build_image(Image *image);
void build_world(World *world);
void build_compositor(Scene *scene);
void build_gpencil(bGPdata *gpd);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 09c7d9ab9de..fe75de5e350 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -34,13 +34,12 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <cstring> /* required for STREQ later on. */
#include "MEM_guardedalloc.h"
extern "C" {
#include "BLI_blenlib.h"
-#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_action_types.h"
@@ -211,10 +210,12 @@ OperationDepsNode *DepsgraphRelationBuilder::find_node(
return NULL;
}
- OperationDepsNode *op_node = comp_node->find_operation(key.opcode, key.name);
+ OperationDepsNode *op_node = comp_node->find_operation(key.opcode,
+ key.name,
+ key.name_tag);
if (!op_node) {
fprintf(stderr, "find_node_operation: Failed for (%s, '%s')\n",
- DEG_OPNAMES[key.opcode], key.name.c_str());
+ DEG_OPNAMES[key.opcode], key.name);
}
return op_node;
}
@@ -236,7 +237,7 @@ OperationDepsNode *DepsgraphRelationBuilder::has_node(
if (!comp_node) {
return NULL;
}
- return comp_node->has_operation(key.opcode, key.name);
+ return comp_node->has_operation(key.opcode, key.name, key.name_tag);
}
void DepsgraphRelationBuilder::add_time_relation(TimeSourceDepsNode *timesrc,
@@ -449,6 +450,7 @@ void DepsgraphRelationBuilder::build_object(Main *bmain, Scene *scene, Object *o
if (ob->id.tag & LIB_TAG_DOIT) {
return;
}
+ ob->id.tag |= LIB_TAG_DOIT;
/* Object Transforms */
eDepsOperation_Code base_op = (ob->parent) ? DEG_OPCODE_TRANSFORM_PARENT : DEG_OPCODE_TRANSFORM_LOCAL;
@@ -500,7 +502,7 @@ void DepsgraphRelationBuilder::build_object(Main *bmain, Scene *scene, Object *o
build_animdata(&ob->id);
// XXX: This should be hooked up by the build_animdata code
- if (ob->adt && (ob->adt->action || ob->adt->nla_tracks.first)) {
+ if (needs_animdata_node(&ob->id)) {
ComponentKey adt_key(&ob->id, DEPSNODE_TYPE_ANIMATION);
add_relation(adt_key, local_transform_key, DEPSREL_TYPE_OPERATION, "Object Animation");
}
@@ -837,11 +839,66 @@ void DepsgraphRelationBuilder::build_animdata(ID *id)
/* drivers */
for (FCurve *fcu = (FCurve *)adt->drivers.first; fcu; fcu = fcu->next) {
- OperationKey driver_key(id, DEPSNODE_TYPE_PARAMETERS, DEG_OPCODE_DRIVER, deg_fcurve_id_name(fcu));
+ OperationKey driver_key(id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEG_OPCODE_DRIVER,
+ fcu->rna_path,
+ fcu->array_index);
/* create the driver's relations to targets */
build_driver(id, fcu);
+ /* Special case for array drivers: we can not multithread them because
+ * of the way how they work internally: animation system will write the
+ * whole array back to RNA even when changing individual array value.
+ *
+ * Some tricky things here:
+ * - array_index is -1 for single channel drivers, meaning we only have
+ * to do some magic when array_index is not -1.
+ * - We do relation from next array index to a previous one, so we don't
+ * have to deal with array index 0.
+ *
+ * TODO(sergey): Avoid liner lookup somehow.
+ */
+ if (fcu->array_index > 0) {
+ FCurve *fcu_prev = NULL;
+ for (FCurve *fcu_candidate = (FCurve *)adt->drivers.first;
+ fcu_candidate != NULL;
+ fcu_candidate = fcu_candidate->next)
+ {
+ /* Writing to different RNA paths is */
+ if (!STREQ(fcu_candidate->rna_path, fcu->rna_path)) {
+ continue;
+ }
+ /* We only do relation from previous fcurve to previous one. */
+ if (fcu_candidate->array_index >= fcu->array_index) {
+ continue;
+ }
+ /* Choose fcurve with highest possible array index. */
+ if (fcu_prev == NULL ||
+ fcu_candidate->array_index > fcu_prev->array_index)
+ {
+ fcu_prev = fcu_candidate;
+ }
+ }
+ if (fcu_prev != NULL) {
+ OperationKey prev_driver_key(id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEG_OPCODE_DRIVER,
+ fcu_prev->rna_path,
+ fcu_prev->array_index);
+ OperationKey driver_key(id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEG_OPCODE_DRIVER,
+ fcu->rna_path,
+ fcu->array_index);
+ add_relation(prev_driver_key,
+ driver_key,
+ DEPSREL_TYPE_OPERATION,
+ "[Driver Order]");
+ }
+ }
+
/* prevent driver from occurring before own animation... */
if (adt->action || adt->nla_tracks.first) {
add_relation(adt_key, driver_key, DEPSREL_TYPE_OPERATION,
@@ -853,7 +910,11 @@ void DepsgraphRelationBuilder::build_animdata(ID *id)
void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu)
{
ChannelDriver *driver = fcu->driver;
- OperationKey driver_key(id, DEPSNODE_TYPE_PARAMETERS, DEG_OPCODE_DRIVER, deg_fcurve_id_name(fcu));
+ OperationKey driver_key(id,
+ DEPSNODE_TYPE_PARAMETERS,
+ DEG_OPCODE_DRIVER,
+ fcu->rna_path,
+ fcu->array_index);
bPoseChannel *pchan = NULL;
/* create dependency between driver and data affected by it */
@@ -1356,10 +1417,10 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *ob,
if (data->poletar != NULL) {
if ((data->poletar->type == OB_ARMATURE) && (data->polesubtarget[0])) {
// XXX: same armature issues - ready vs done?
- ComponentKey target_key(&data->poletar->id, DEPSNODE_TYPE_BONE, data->subtarget);
+ ComponentKey target_key(&data->poletar->id, DEPSNODE_TYPE_BONE, data->polesubtarget);
add_relation(target_key, solver_key, DEPSREL_TYPE_TRANSFORM, con->name);
}
- else if (ELEM(data->poletar->type, OB_MESH, OB_LATTICE) && (data->subtarget[0])) {
+ else if (ELEM(data->poletar->type, OB_MESH, OB_LATTICE) && (data->polesubtarget[0])) {
/* vertex group target */
/* NOTE: for now, we don't need to represent vertex groups separately... */
ComponentKey target_key(&data->poletar->id, DEPSNODE_TYPE_GEOMETRY);
@@ -1527,7 +1588,7 @@ void DepsgraphRelationBuilder::build_rig(Scene *scene, Object *ob)
"Armature Eval");
add_relation(armature_key, init_key, DEPSREL_TYPE_COMPONENT_ORDER, "Data dependency");
- if (ob->adt && (ob->adt->action || ob->adt->nla_tracks.first)) {
+ if (needs_animdata_node(&ob->id)) {
ComponentKey animation_key(&ob->id, DEPSNODE_TYPE_ANIMATION);
add_relation(animation_key, init_key, DEPSREL_TYPE_OPERATION, "Rig Animation");
}
@@ -1765,7 +1826,7 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje
* for either the modifier needing time, or that it is animated.
*/
/* XXX: Remove this hack when these links are added as part of build_animdata() instead */
- if (modifier_dependsOnTime(md) == false) {
+ if (modifier_dependsOnTime(md) == false && needs_animdata_node(&ob->id)) {
ComponentKey animation_key(&ob->id, DEPSNODE_TYPE_ANIMATION);
add_relation(animation_key, mod_key, DEPSREL_TYPE_OPERATION, "Modifier Animation");
}
@@ -1848,15 +1909,18 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje
// XXX: these needs geom data, but where is geom stored?
if (cu->bevobj) {
ComponentKey bevob_key(&cu->bevobj->id, DEPSNODE_TYPE_GEOMETRY);
+ build_object(bmain, scene, cu->bevobj);
add_relation(bevob_key, geom_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Curve Bevel");
}
if (cu->taperobj) {
ComponentKey taperob_key(&cu->taperobj->id, DEPSNODE_TYPE_GEOMETRY);
+ build_object(bmain, scene, cu->taperobj);
add_relation(taperob_key, geom_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Curve Taper");
}
if (ob->type == OB_FONT) {
if (cu->textoncurve) {
- ComponentKey textoncurve_key(&cu->taperobj->id, DEPSNODE_TYPE_GEOMETRY);
+ ComponentKey textoncurve_key(&cu->textoncurve->id, DEPSNODE_TYPE_GEOMETRY);
+ build_object(bmain, scene, cu->textoncurve);
add_relation(textoncurve_key, geom_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Text on Curve");
}
}
@@ -2063,7 +2127,7 @@ bool DepsgraphRelationBuilder::needs_animdata_node(ID *id)
{
AnimData *adt = BKE_animdata_from_id(id);
if (adt != NULL) {
- return adt->action != NULL;
+ return (adt->action != NULL) || (adt->nla_tracks.first != NULL);
}
return false;
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 5240aa24b54..8d8ad6772b8 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -81,108 +81,83 @@ struct ComponentDepsNode;
struct OperationDepsNode;
struct RootPChanMap;
-struct RootKey
-{
- RootKey() {}
+struct RootKey {
+ RootKey();
};
struct TimeSourceKey
{
- TimeSourceKey() : id(NULL) {}
- TimeSourceKey(ID *id) : id(id) {}
+ TimeSourceKey();
+ TimeSourceKey(ID *id);
- string identifier() const
- {
- return string("TimeSourceKey");
- }
+ string identifier() const;
ID *id;
};
struct ComponentKey
{
- ComponentKey() :
- id(NULL), type(DEPSNODE_TYPE_UNDEFINED), name("")
- {}
- ComponentKey(ID *id, eDepsNode_Type type, const string &name = "") :
- id(id), type(type), name(name)
- {}
-
- string identifier() const
- {
- const char *idname = (id) ? id->name : "<None>";
+ ComponentKey();
+ ComponentKey(ID *id, eDepsNode_Type type, const char *name = "");
- char typebuf[5];
- BLI_snprintf(typebuf, sizeof(typebuf), "%d", type);
-
- return string("ComponentKey(") + idname + ", " + typebuf + ", '" + name + "')";
- }
+ string identifier() const;
ID *id;
eDepsNode_Type type;
- string name;
+ const char *name;
};
struct OperationKey
{
- OperationKey() :
- id(NULL), component_type(DEPSNODE_TYPE_UNDEFINED), component_name(""), opcode(DEG_OPCODE_OPERATION), name("")
- {}
-
- OperationKey(ID *id, eDepsNode_Type component_type, const string &name) :
- id(id), component_type(component_type), component_name(""), opcode(DEG_OPCODE_OPERATION), name(name)
- {}
- OperationKey(ID *id, eDepsNode_Type component_type, const string &component_name, const string &name) :
- id(id), component_type(component_type), component_name(component_name), opcode(DEG_OPCODE_OPERATION), name(name)
- {}
-
- OperationKey(ID *id, eDepsNode_Type component_type, eDepsOperation_Code opcode) :
- id(id), component_type(component_type), component_name(""), opcode(opcode), name("")
- {}
- OperationKey(ID *id, eDepsNode_Type component_type, const string &component_name, eDepsOperation_Code opcode) :
- id(id), component_type(component_type), component_name(component_name), opcode(opcode), name("")
- {}
-
- OperationKey(ID *id, eDepsNode_Type component_type, eDepsOperation_Code opcode, const string &name) :
- id(id), component_type(component_type), component_name(""), opcode(opcode), name(name)
- {}
- OperationKey(ID *id, eDepsNode_Type component_type, const string &component_name, eDepsOperation_Code opcode, const string &name) :
- id(id), component_type(component_type), component_name(component_name), opcode(opcode), name(name)
- {}
-
- string identifier() const
- {
- char typebuf[5];
- BLI_snprintf(typebuf, sizeof(typebuf), "%d", component_type);
-
- return string("OperationKey(") + "t: " + typebuf + ", cn: '" + component_name + "', c: " + DEG_OPNAMES[opcode] + ", n: '" + name + "')";
- }
-
+ OperationKey();
+ OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *name,
+ int name_tag = -1);
+ OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *component_name,
+ const char *name,
+ int name_tag);
+
+ OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ eDepsOperation_Code opcode);
+ OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *component_name,
+ eDepsOperation_Code opcode);
+
+ OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag = -1);
+ OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *component_name,
+ eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag = -1);
+
+ string identifier() const;
ID *id;
eDepsNode_Type component_type;
- string component_name;
+ const char *component_name;
eDepsOperation_Code opcode;
- string name;
+ const char *name;
+ int name_tag;
};
struct RNAPathKey
{
- // Note: see depsgraph_build.cpp for implementation
+ /* NOTE: see depsgraph_build.cpp for implementation */
RNAPathKey(ID *id, const char *path);
- RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop) :
- id(id), ptr(ptr), prop(prop)
- {}
-
- string identifier() const
- {
- const char *id_name = (id) ? id->name : "<No ID>";
- const char *prop_name = (prop) ? RNA_property_identifier(prop) : "<No Prop>";
-
- return string("RnaPathKey(") + "id: " + id_name + ", prop: " + prop_name + "')";
- }
+ RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop);
+ string identifier() const;
ID *id;
PointerRNA ptr;
@@ -270,7 +245,7 @@ protected:
template <typename KeyType>
DepsNodeHandle create_node_handle(const KeyType& key,
- const string& default_name = "");
+ const char *default_name = "");
bool needs_animdata_node(ID *id);
@@ -280,7 +255,7 @@ private:
struct DepsNodeHandle
{
- DepsNodeHandle(DepsgraphRelationBuilder *builder, OperationDepsNode *node, const string &default_name = "") :
+ DepsNodeHandle(DepsgraphRelationBuilder *builder, OperationDepsNode *node, const char *default_name = "") :
builder(builder),
node(node),
default_name(default_name)
@@ -290,7 +265,7 @@ struct DepsNodeHandle
DepsgraphRelationBuilder *builder;
OperationDepsNode *node;
- const string &default_name;
+ const char *default_name;
};
/* Utilities for Builders ----------------------------------------------------- */
@@ -318,6 +293,7 @@ void DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from,
else {
if (!op_from) {
/* XXX TODO handle as error or report if needed */
+ node_from = find_node(key_from);
fprintf(stderr, "add_relation(%d, %s) - Could not find op_from (%s)\n",
type, description, key_from.identifier().c_str());
}
@@ -383,7 +359,7 @@ void DepsgraphRelationBuilder::add_node_handle_relation(
template <typename KeyType>
DepsNodeHandle DepsgraphRelationBuilder::create_node_handle(
const KeyType &key,
- const string &default_name)
+ const char *default_name)
{
return DepsNodeHandle(this, find_node(key), default_name);
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
new file mode 100644
index 00000000000..7ada04e8f74
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
@@ -0,0 +1,211 @@
+/*
+ * ***** 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) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * Original Author: Joshua Leung
+ * Contributor(s): Based on original depsgraph.c code - Blender Foundation (2005-2013)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/depsgraph/intern/builder/deg_builder_relations.cc
+ * \ingroup depsgraph
+ *
+ * Methods for constructing depsgraph
+ */
+
+#include "intern/builder/deg_builder_relations.h"
+
+namespace DEG {
+
+/////////////////////////////////////////
+// Root.
+
+RootKey::RootKey()
+{
+}
+
+/////////////////////////////////////////
+// Time source.
+
+TimeSourceKey::TimeSourceKey()
+ : id(NULL)
+{
+}
+
+TimeSourceKey::TimeSourceKey(ID *id)
+ : id(id)
+{
+}
+
+string TimeSourceKey::identifier() const
+{
+ return string("TimeSourceKey");
+}
+
+/////////////////////////////////////////
+// Component.
+
+ComponentKey::ComponentKey()
+ : id(NULL),
+ type(DEPSNODE_TYPE_UNDEFINED),
+ name("")
+{
+}
+
+ComponentKey::ComponentKey(ID *id, eDepsNode_Type type, const char *name)
+ : id(id),
+ type(type),
+ name(name)
+{
+}
+
+string ComponentKey::identifier() const
+{
+ const char *idname = (id) ? id->name : "<None>";
+ char typebuf[5];
+ BLI_snprintf(typebuf, sizeof(typebuf), "%d", type);
+ return string("ComponentKey(") +
+ idname + ", " + typebuf + ", '" + name + "')";
+}
+
+/////////////////////////////////////////
+// Operation.
+
+OperationKey::OperationKey()
+ : id(NULL),
+ component_type(DEPSNODE_TYPE_UNDEFINED),
+ component_name(""),
+ opcode(DEG_OPCODE_OPERATION),
+ name(""),
+ name_tag(-1)
+{
+}
+
+OperationKey::OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *name,
+ int name_tag)
+ : id(id),
+ component_type(component_type),
+ component_name(""),
+ opcode(DEG_OPCODE_OPERATION),
+ name(name),
+ name_tag(name_tag)
+{
+}
+
+OperationKey::OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *component_name,
+ const char *name,
+ int name_tag)
+ : id(id),
+ component_type(component_type),
+ component_name(component_name),
+ opcode(DEG_OPCODE_OPERATION),
+ name(name),
+ name_tag(name_tag)
+{
+}
+
+OperationKey::OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ eDepsOperation_Code opcode)
+ : id(id),
+ component_type(component_type),
+ component_name(""),
+ opcode(opcode),
+ name(""),
+ name_tag(-1)
+{
+}
+
+OperationKey::OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *component_name,
+ eDepsOperation_Code opcode)
+ : id(id),
+ component_type(component_type),
+ component_name(component_name),
+ opcode(opcode),
+ name(""),
+ name_tag(-1)
+{
+}
+
+OperationKey::OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag)
+ : id(id),
+ component_type(component_type),
+ component_name(""),
+ opcode(opcode),
+ name(name),
+ name_tag(name_tag)
+{
+}
+
+OperationKey::OperationKey(ID *id,
+ eDepsNode_Type component_type,
+ const char *component_name,
+ eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag)
+ : id(id),
+ component_type(component_type),
+ component_name(component_name),
+ opcode(opcode),
+ name(name),
+ name_tag(name_tag)
+{
+}
+
+string OperationKey::identifier() const
+{
+ char typebuf[5];
+ BLI_snprintf(typebuf, sizeof(typebuf), "%d", component_type);
+ return string("OperationKey(") +
+ "t: " + typebuf +
+ ", cn: '" + component_name +
+ "', c: " + DEG_OPNAMES[opcode] +
+ ", n: '" + name + "')";
+}
+
+/////////////////////////////////////////
+// RNA path.
+
+RNAPathKey::RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop)
+ : id(id),
+ ptr(ptr),
+ prop(prop)
+{
+}
+
+string RNAPathKey::identifier() const
+{
+ const char *id_name = (id) ? id->name : "<No ID>";
+ const char *prop_name = (prop) ? RNA_property_identifier(prop) : "<No Prop>";
+ return string("RnaPathKey(") + "id: " + id_name +
+ ", prop: " + prop_name + "')";
+}
+
+} // namespace DEG
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
index 70cd5f11a47..0d56ce71c7d 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
@@ -321,7 +321,7 @@ static void deg_debug_graphviz_node_single(const DebugContext &ctx,
static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx,
const DepsNode *node)
{
- string name = node->identifier().c_str();
+ string name = node->identifier();
if (node->type == DEPSNODE_TYPE_ID_REF) {
IDDepsNode *id_node = (IDDepsNode *)node;
char buf[256];
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index 2b7c63767ab..3502267d9ca 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -32,8 +32,6 @@
#include "intern/depsgraph.h" /* own include */
-#include <string.h>
-
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
@@ -53,6 +51,8 @@ extern "C" {
#include "RNA_access.h"
}
+#include <cstring>
+
#include "DEG_depsgraph.h"
#include "intern/nodes/deg_node.h"
@@ -116,7 +116,7 @@ static bool pointer_to_component_node_criteria(const PointerRNA *ptr,
const PropertyRNA *prop,
ID **id,
eDepsNode_Type *type,
- string *subdata)
+ const char **subdata)
{
if (!ptr->type)
return false;
@@ -232,7 +232,7 @@ DepsNode *Depsgraph::find_node_from_pointer(const PointerRNA *ptr,
{
ID *id;
eDepsNode_Type type;
- string name;
+ const char *name;
/* Get querying conditions. */
if (pointer_to_id_node_criteria(ptr, prop, &id)) {
@@ -240,8 +240,9 @@ DepsNode *Depsgraph::find_node_from_pointer(const PointerRNA *ptr,
}
else if (pointer_to_component_node_criteria(ptr, prop, &id, &type, &name)) {
IDDepsNode *id_node = find_id_node(id);
- if (id_node)
+ if (id_node != NULL) {
return id_node->find_component(type, name);
+ }
}
return NULL;
@@ -328,7 +329,7 @@ IDDepsNode *Depsgraph::find_id_node(const ID *id) const
return reinterpret_cast<IDDepsNode *>(BLI_ghash_lookup(id_hash, id));
}
-IDDepsNode *Depsgraph::add_id_node(ID *id, const string &name)
+IDDepsNode *Depsgraph::add_id_node(ID *id, const char *name)
{
IDDepsNode *id_node = find_id_node(id);
if (!id_node) {
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index 08b264f8497..e668facd645 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -101,22 +101,6 @@ struct Depsgraph {
~Depsgraph();
/**
- * Find node which matches the specified description.
- *
- * \param id: ID block that is associated with this
- * \param subdata: identifier used for sub-ID data (e.g. bone)
- * \param type: type of node we're dealing with
- * \param name: custom identifier assigned to node
- *
- * \return A node matching the required characteristics if it exists
- * or NULL if no such node exists in the graph.
- */
- DepsNode *find_node(const ID *id,
- eDepsNode_Type type,
- const string &subdata,
- const string &name);
-
- /**
* Convenience wrapper to find node given just pointer + property.
*
* \param ptr: pointer to the data that node will represent
@@ -136,7 +120,7 @@ struct Depsgraph {
void clear_subgraph_nodes();
IDDepsNode *find_id_node(const ID *id) const;
- IDDepsNode *add_id_node(ID *id, const string &name = "");
+ IDDepsNode *add_id_node(ID *id, const char *name = "");
void remove_id_node(const ID *id);
void clear_id_nodes();
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 7a3b19e82c6..9952f714145 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -32,6 +32,8 @@
#include "MEM_guardedalloc.h"
+// #define DEBUG_TIME
+
extern "C" {
#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
@@ -41,6 +43,11 @@ extern "C" {
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
+#ifdef DEBUG_TIME
+# include "PIL_time.h"
+# include "PIL_time_utildefines.h"
+#endif
+
#include "BKE_main.h"
#include "BKE_collision.h"
#include "BKE_effect.h"
@@ -190,6 +197,10 @@ void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag)
*/
void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene)
{
+#ifdef DEBUG_TIME
+ TIMEIT_START(DEG_graph_build_from_scene);
+#endif
+
DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
/* 1) Generate all the nodes in the graph first */
@@ -239,6 +250,10 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene)
abort();
}
#endif
+
+#ifdef DEBUG_TIME
+ TIMEIT_END(DEG_graph_build_from_scene);
+#endif
}
/* Tag graph relations for update. */
@@ -309,7 +324,15 @@ void DEG_scene_graph_free(Scene *scene)
}
}
-void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name)
+void DEG_add_collision_relations(DepsNodeHandle *handle,
+ Scene *scene,
+ Object *ob,
+ Group *group,
+ int layer,
+ unsigned int modifier_type,
+ DEG_CollobjFilterFunction fn,
+ bool dupli,
+ const char *name)
{
unsigned int numcollobj;
Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli);
@@ -327,7 +350,13 @@ void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *o
MEM_freeN(collobjs);
}
-void DEG_add_forcefield_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name)
+void DEG_add_forcefield_relations(DepsNodeHandle *handle,
+ Scene *scene,
+ Object *ob,
+ EffectorWeights *effector_weights,
+ bool add_absorption,
+ int skip_forcefield,
+ const char *name)
{
ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
@@ -339,17 +368,33 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle, Scene *scene, Object *
if (eff->psys) {
DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_EVAL_PARTICLES, name);
- /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */
+ /* TODO: remove this when/if EVAL_PARTICLES is sufficient
+ * for up to date particles.
+ */
DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_GEOMETRY, name);
}
if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
- DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_TRANSFORM, "Smoke Force Domain");
- DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_GEOMETRY, "Smoke Force Domain");
+ DEG_add_object_relation(handle,
+ eff->pd->f_source,
+ DEG_OB_COMP_TRANSFORM,
+ "Smoke Force Domain");
+ DEG_add_object_relation(handle,
+ eff->pd->f_source,
+ DEG_OB_COMP_GEOMETRY,
+ "Smoke Force Domain");
}
if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
- DEG_add_collision_relations(handle, scene, ob, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption");
+ DEG_add_collision_relations(handle,
+ scene,
+ ob,
+ NULL,
+ eff->ob->lay,
+ eModifierType_Collision,
+ NULL,
+ true,
+ "Force Absorption");
}
}
}
diff --git a/source/blender/depsgraph/intern/depsgraph_intern.h b/source/blender/depsgraph/intern/depsgraph_intern.h
index e5d3d1f5861..2d8e7dc841c 100644
--- a/source/blender/depsgraph/intern/depsgraph_intern.h
+++ b/source/blender/depsgraph/intern/depsgraph_intern.h
@@ -63,8 +63,8 @@ struct DepsNodeFactory {
virtual const char *tname() const = 0;
virtual DepsNode *create_node(const ID *id,
- const string &subdata,
- const string &name) const = 0;
+ const char *subdata,
+ const char *name) const = 0;
};
template <class NodeType>
@@ -73,7 +73,7 @@ struct DepsNodeFactoryImpl : public DepsNodeFactory {
eDepsNode_Class tclass() const { return NodeType::typeinfo.tclass; }
const char *tname() const { return NodeType::typeinfo.tname; }
- DepsNode *create_node(const ID *id, const string &subdata, const string &name) const
+ DepsNode *create_node(const ID *id, const char *subdata, const char *name) const
{
DepsNode *node = OBJECT_GUARDED_NEW(NodeType);
@@ -81,12 +81,14 @@ struct DepsNodeFactoryImpl : public DepsNodeFactory {
node->type = type();
node->tclass = tclass();
- if (!name.empty())
+ if (name[0] != '\0') {
/* set name if provided ... */
node->name = name;
- else
+ }
+ else {
/* ... otherwise use default type name */
node->name = tname();
+ }
node->init(id, subdata);
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 4f27dab258d..e8ed03666a6 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -31,7 +31,7 @@
*/
#include <stdio.h>
-#include <cstring>
+#include <cstring> /* required for memset */
#include <queue>
extern "C" {
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index c3fd202d832..e926f83bcbe 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -152,7 +152,7 @@ static void deg_task_run_func(TaskPool *pool,
}
if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) {
BLI_assert(child->num_links_pending > 0);
- atomic_sub_uint32(&child->num_links_pending, 1);
+ atomic_sub_and_fetch_uint32(&child->num_links_pending, 1);
}
if (child->num_links_pending == 0) {
bool is_scheduled = atomic_fetch_and_or_uint8(
@@ -287,7 +287,7 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers,
{
if (dec_parents) {
BLI_assert(node->num_links_pending > 0);
- atomic_sub_uint32(&node->num_links_pending, 1);
+ atomic_sub_and_fetch_uint32(&node->num_links_pending, 1);
}
if (node->num_links_pending == 0) {
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc
index 67d64aae8bf..060544a4407 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc
@@ -30,10 +30,10 @@
* Implementation of tools for debugging the depsgraph
*/
-#include <cstring>
-
#include "intern/eval/deg_eval_debug.h"
+#include <cstring> /* required for STREQ later on. */
+
extern "C" {
#include "BLI_listbase.h"
#include "BLI_ghash.h"
@@ -53,10 +53,10 @@ namespace DEG {
DepsgraphStats *DepsgraphDebug::stats = NULL;
-static string get_component_name(eDepsNode_Type type, const string &name = "")
+static string get_component_name(eDepsNode_Type type, const char *name = "")
{
DepsNodeFactory *factory = deg_get_node_factory(type);
- if (name.empty()) {
+ if (name[0] != '\0') {
return string(factory->tname());
}
else {
@@ -116,7 +116,7 @@ void DepsgraphDebug::task_started(Depsgraph *graph,
*/
DepsgraphStatsComponent *comp_stats =
get_component_stats(id, get_component_name(comp->type,
- comp->name),
+ comp->name).c_str(),
true);
times_clear(comp_stats->times);
}
@@ -146,7 +146,7 @@ void DepsgraphDebug::task_completed(Depsgraph *graph,
DepsgraphStatsComponent *comp_stats =
get_component_stats(id,
get_component_name(comp->type,
- comp->name),
+ comp->name).c_str(),
true);
times_add(comp_stats->times, time);
}
@@ -226,7 +226,7 @@ DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create)
DepsgraphStatsComponent *DepsgraphDebug::get_component_stats(
DepsgraphStatsID *id_stats,
- const string &name,
+ const char *name,
bool create)
{
DepsgraphStatsComponent *comp_stats;
@@ -234,13 +234,14 @@ DepsgraphStatsComponent *DepsgraphDebug::get_component_stats(
comp_stats != NULL;
comp_stats = comp_stats->next)
{
- if (STREQ(comp_stats->name, name.c_str()))
+ if (STREQ(comp_stats->name, name)) {
break;
+ }
}
if (!comp_stats && create) {
comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent),
"Depsgraph Component Stats");
- BLI_strncpy(comp_stats->name, name.c_str(), sizeof(comp_stats->name));
+ BLI_strncpy(comp_stats->name, name, sizeof(comp_stats->name));
BLI_addtail(&id_stats->components, comp_stats);
}
return comp_stats;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.h b/source/blender/depsgraph/intern/eval/deg_eval_debug.h
index 9109019eb2d..0bbe88cc9ca 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_debug.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.h
@@ -66,10 +66,10 @@ struct DepsgraphDebug {
static DepsgraphStatsID *get_id_stats(ID *id, bool create);
static DepsgraphStatsComponent *get_component_stats(DepsgraphStatsID *id_stats,
- const string &name,
+ const char *name,
bool create);
static DepsgraphStatsComponent *get_component_stats(ID *id,
- const string &name,
+ const char *name,
bool create)
{
return get_component_stats(get_id_stats(id, create), name, create);
diff --git a/source/blender/depsgraph/intern/nodes/deg_node.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc
index eb408f293de..57b25c10670 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node.cc
+++ b/source/blender/depsgraph/intern/nodes/deg_node.cc
@@ -31,7 +31,7 @@
#include "intern/nodes/deg_node.h"
#include <stdio.h>
-#include <string.h>
+#include <cstring> /* required for STREQ later on. */
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@@ -72,7 +72,7 @@ DepsNode::TypeInfo::TypeInfo(eDepsNode_Type type, const char *tname)
DepsNode::DepsNode()
{
- name[0] = '\0';
+ name = "";
}
DepsNode::~DepsNode()
@@ -122,7 +122,7 @@ RootDepsNode::~RootDepsNode()
OBJECT_GUARDED_DELETE(time_source, TimeSourceDepsNode);
}
-TimeSourceDepsNode *RootDepsNode::add_time_source(const string &name)
+TimeSourceDepsNode *RootDepsNode::add_time_source(const char *name)
{
if (!time_source) {
DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_TIMESOURCE);
@@ -142,12 +142,24 @@ static DepsNodeFactoryImpl<TimeSourceDepsNode> DNTI_TIMESOURCE;
/* ID Node ================================================ */
+IDDepsNode::ComponentIDKey::ComponentIDKey(eDepsNode_Type type,
+ const char *name)
+ : type(type), name(name)
+{
+}
+
+bool IDDepsNode::ComponentIDKey::operator== (const ComponentIDKey &other) const
+{
+ return type == other.type &&
+ STREQ(name, other.name);
+}
+
static unsigned int id_deps_node_hash_key(const void *key_v)
{
const IDDepsNode::ComponentIDKey *key =
reinterpret_cast<const IDDepsNode::ComponentIDKey *>(key_v);
return hash_combine(BLI_ghashutil_uinthash(key->type),
- BLI_ghashutil_strhash_p(key->name.c_str()));
+ BLI_ghashutil_strhash_p(key->name));
}
static bool id_deps_node_hash_key_cmp(const void *a, const void *b)
@@ -173,7 +185,7 @@ static void id_deps_node_hash_value_free(void *value_v)
}
/* Initialize 'id' node - from pointer data given. */
-void IDDepsNode::init(const ID *id, const string &UNUSED(subdata))
+void IDDepsNode::init(const ID *id, const char *UNUSED(subdata))
{
/* Store ID-pointer. */
BLI_assert(id != NULL);
@@ -204,14 +216,14 @@ IDDepsNode::~IDDepsNode()
}
ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type,
- const string &name) const
+ const char *name) const
{
ComponentIDKey key(type, name);
return reinterpret_cast<ComponentDepsNode *>(BLI_ghash_lookup(components, &key));
}
ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type,
- const string &name)
+ const char *name)
{
ComponentDepsNode *comp_node = find_component(type, name);
if (!comp_node) {
@@ -226,7 +238,7 @@ ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type,
return comp_node;
}
-void IDDepsNode::remove_component(eDepsNode_Type type, const string &name)
+void IDDepsNode::remove_component(eDepsNode_Type type, const char *name)
{
ComponentDepsNode *comp_node = find_component(type, name);
if (comp_node) {
@@ -281,7 +293,7 @@ static DepsNodeFactoryImpl<IDDepsNode> DNTI_ID_REF;
/* Subgraph Node ========================================== */
/* Initialize 'subgraph' node - from pointer data given. */
-void SubgraphDepsNode::init(const ID *id, const string &UNUSED(subdata))
+void SubgraphDepsNode::init(const ID *id, const char *UNUSED(subdata))
{
/* Store ID-ref if provided. */
this->root_id = (ID *)id;
diff --git a/source/blender/depsgraph/intern/nodes/deg_node.h b/source/blender/depsgraph/intern/nodes/deg_node.h
index b2262c4bd12..7c2f53840b6 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node.h
+++ b/source/blender/depsgraph/intern/nodes/deg_node.h
@@ -32,6 +32,8 @@
#include "intern/depsgraph_types.h"
+#include "BLI_utildefines.h"
+
struct ID;
struct GHash;
struct Scene;
@@ -57,7 +59,7 @@ struct DepsNode {
};
/* Identifier - mainly for debugging purposes. */
- string name;
+ const char *name;
/* Structural type of node. */
eDepsNode_Type type;
@@ -78,8 +80,9 @@ struct DepsNode {
/* Nodes which depend on this one. */
Relations outlinks;
- /* Generic tag for traversal algorithms */
+ /* Generic tags for traversal algorithms. */
int done;
+ int tag;
/* Methods. */
@@ -90,7 +93,7 @@ struct DepsNode {
string full_identifier() const;
virtual void init(const ID * /*id*/,
- const string &/*subdata*/) {}
+ const char * /*subdata*/) {}
virtual void tag_update(Depsgraph * /*graph*/) {}
@@ -129,7 +132,7 @@ struct RootDepsNode : public DepsNode {
RootDepsNode();
~RootDepsNode();
- TimeSourceDepsNode *add_time_source(const string &name = "");
+ TimeSourceDepsNode *add_time_source(const char *name = "");
/* scene that this corresponds to */
Scene *scene;
@@ -143,26 +146,21 @@ struct RootDepsNode : public DepsNode {
/* ID-Block Reference */
struct IDDepsNode : public DepsNode {
struct ComponentIDKey {
- ComponentIDKey(eDepsNode_Type type, const string &name = "")
- : type(type), name(name) {}
-
- bool operator== (const ComponentIDKey &other) const
- {
- return type == other.type && name == other.name;
- }
+ ComponentIDKey(eDepsNode_Type type, const char *name = "");
+ bool operator==(const ComponentIDKey &other) const;
eDepsNode_Type type;
- string name;
+ const char *name;
};
- void init(const ID *id, const string &subdata);
+ void init(const ID *id, const char *subdata);
~IDDepsNode();
ComponentDepsNode *find_component(eDepsNode_Type type,
- const string &name = "") const;
+ const char *name = "") const;
ComponentDepsNode *add_component(eDepsNode_Type type,
- const string &name = "");
- void remove_component(eDepsNode_Type type, const string &name = "");
+ const char *name = "");
+ void remove_component(eDepsNode_Type type, const char *name = "");
void clear_components();
void tag_update(Depsgraph *graph);
@@ -189,7 +187,7 @@ struct IDDepsNode : public DepsNode {
/* Subgraph Reference. */
struct SubgraphDepsNode : public DepsNode {
- void init(const ID *id, const string &subdata);
+ void init(const ID *id, const char *subdata);
~SubgraphDepsNode();
/* Instanced graph. */
diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc
index 01f33b6368b..06f91ac7fdc 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc
@@ -31,7 +31,7 @@
#include "intern/nodes/deg_node_component.h"
#include <stdio.h>
-#include <string.h>
+#include <cstring> /* required for STREQ later on. */
extern "C" {
#include "BLI_utildefines.h"
@@ -53,12 +53,50 @@ namespace DEG {
/* Standard Component Methods ============================= */
+ComponentDepsNode::OperationIDKey::OperationIDKey()
+ : opcode(DEG_OPCODE_OPERATION),
+ name(""),
+ name_tag(-1)
+{
+}
+
+ComponentDepsNode::OperationIDKey::OperationIDKey(eDepsOperation_Code opcode)
+ : opcode(opcode),
+ name(""),
+ name_tag(-1)
+{
+}
+
+ComponentDepsNode::OperationIDKey::OperationIDKey(eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag)
+ : opcode(opcode),
+ name(name),
+ name_tag(name_tag)
+{
+}
+
+string ComponentDepsNode::OperationIDKey::identifier() const
+{
+ char codebuf[5];
+ BLI_snprintf(codebuf, sizeof(codebuf), "%d", opcode);
+ return string("OperationIDKey(") + codebuf + ", " + name + ")";
+}
+
+bool ComponentDepsNode::OperationIDKey::operator==(
+ const OperationIDKey &other) const
+{
+ return (opcode == other.opcode) &&
+ (STREQ(name, other.name)) &&
+ (name_tag == other.name_tag);
+}
+
static unsigned int comp_node_hash_key(const void *key_v)
{
const ComponentDepsNode::OperationIDKey *key =
reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(key_v);
return hash_combine(BLI_ghashutil_uinthash(key->opcode),
- BLI_ghashutil_strhash_p(key->name.c_str()));
+ BLI_ghashutil_strhash_p(key->name));
}
static bool comp_node_hash_key_cmp(const void *a, const void *b)
@@ -95,7 +133,7 @@ ComponentDepsNode::ComponentDepsNode() :
/* Initialize 'component' node - from pointer data given */
void ComponentDepsNode::init(const ID * /*id*/,
- const string & /*subdata*/)
+ const char * /*subdata*/)
{
/* hook up eval context? */
// XXX: maybe this needs a special API?
@@ -114,7 +152,7 @@ ComponentDepsNode::~ComponentDepsNode()
string ComponentDepsNode::identifier() const
{
- string &idname = this->owner->name;
+ string idname = this->owner->name;
char typebuf[16];
sprintf(typebuf, "(%d)", type);
@@ -139,9 +177,11 @@ OperationDepsNode *ComponentDepsNode::find_operation(OperationIDKey key) const
}
}
-OperationDepsNode *ComponentDepsNode::find_operation(eDepsOperation_Code opcode, const string &name) const
+OperationDepsNode *ComponentDepsNode::find_operation(eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag) const
{
- OperationIDKey key(opcode, name);
+ OperationIDKey key(opcode, name, name_tag);
return find_operation(key);
}
@@ -151,21 +191,26 @@ OperationDepsNode *ComponentDepsNode::has_operation(OperationIDKey key) const
}
OperationDepsNode *ComponentDepsNode::has_operation(eDepsOperation_Code opcode,
- const string &name) const
+ const char *name,
+ int name_tag) const
{
- OperationIDKey key(opcode, name);
+ OperationIDKey key(opcode, name, name_tag);
return has_operation(key);
}
-OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype, DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &name)
+OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype,
+ DepsEvalOperationCb op,
+ eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag)
{
- OperationDepsNode *op_node = has_operation(opcode, name);
+ OperationDepsNode *op_node = has_operation(opcode, name, name_tag);
if (!op_node) {
DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_OPERATION);
op_node = (OperationDepsNode *)factory->create_node(this->owner->id, "", name);
/* register opnode in this component's operation set */
- OperationIDKey *key = OBJECT_GUARDED_NEW(OperationIDKey, opcode, name);
+ OperationIDKey *key = OBJECT_GUARDED_NEW(OperationIDKey, opcode, name, name_tag);
BLI_ghash_insert(operations_map, key, op_node);
/* set as entry/exit node of component (if appropriate) */
@@ -197,16 +242,6 @@ OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype,
return op_node;
}
-void ComponentDepsNode::remove_operation(eDepsOperation_Code opcode, const string &name)
-{
- /* unregister */
- OperationIDKey key(opcode, name);
- BLI_ghash_remove(operations_map,
- &key,
- comp_node_hash_key_free,
- comp_node_hash_key_free);
-}
-
void ComponentDepsNode::clear_operations()
{
if (operations_map != NULL) {
@@ -337,7 +372,7 @@ static DepsNodeFactoryImpl<PoseComponentDepsNode> DNTI_EVAL_POSE;
/* Bone Component ========================================= */
/* Initialize 'bone component' node - from pointer data given */
-void BoneComponentDepsNode::init(const ID *id, const string &subdata)
+void BoneComponentDepsNode::init(const ID *id, const char *subdata)
{
/* generic component-node... */
ComponentDepsNode::init(id, subdata);
@@ -350,7 +385,7 @@ void BoneComponentDepsNode::init(const ID *id, const string &subdata)
/* bone-specific node data */
Object *ob = (Object *)id;
- this->pchan = BKE_pose_channel_find_name(ob->pose, subdata.c_str());
+ this->pchan = BKE_pose_channel_find_name(ob->pose, subdata);
}
DEG_DEPSNODE_DEFINE(BoneComponentDepsNode, DEPSNODE_TYPE_BONE, "Bone Component");
diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h
index 7dec8eaaa90..969771a29c9 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_component.h
+++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h
@@ -53,50 +53,38 @@ struct ComponentDepsNode : public DepsNode {
struct OperationIDKey
{
eDepsOperation_Code opcode;
- string name;
-
-
- OperationIDKey() :
- opcode(DEG_OPCODE_OPERATION), name("")
- {}
- OperationIDKey(eDepsOperation_Code opcode) :
- opcode(opcode), name("")
- {}
- OperationIDKey(eDepsOperation_Code opcode, const string &name) :
- opcode(opcode), name(name)
- {}
-
- string identifier() const
- {
- char codebuf[5];
- BLI_snprintf(codebuf, sizeof(codebuf), "%d", opcode);
-
- return string("OperationIDKey(") + codebuf + ", " + name + ")";
- }
-
- bool operator==(const OperationIDKey &other) const
- {
- return (opcode == other.opcode) && (name == other.name);
- }
+ const char *name;
+ int name_tag;
+
+ OperationIDKey();
+ OperationIDKey(eDepsOperation_Code opcode);
+ OperationIDKey(eDepsOperation_Code opcode,
+ const char *name,
+ int name_tag);
+
+ string identifier() const;
+ bool operator==(const OperationIDKey &other) const;
};
/* Typedef for container of operations */
ComponentDepsNode();
~ComponentDepsNode();
- void init(const ID *id, const string &subdata);
+ void init(const ID *id, const char *subdata);
string identifier() const;
/* Find an existing operation, will throw an assert() if it does not exist. */
OperationDepsNode *find_operation(OperationIDKey key) const;
OperationDepsNode *find_operation(eDepsOperation_Code opcode,
- const string &name) const;
+ const char *name,
+ int name_tag) const;
/* Check operation exists and return it. */
OperationDepsNode *has_operation(OperationIDKey key) const;
OperationDepsNode *has_operation(eDepsOperation_Code opcode,
- const string &name) const;
+ const char *name,
+ int name_tag) const;
/**
* Create a new node for representing an operation and add this to graph
@@ -114,9 +102,9 @@ struct ComponentDepsNode : public DepsNode {
OperationDepsNode *add_operation(eDepsOperation_Type optype,
DepsEvalOperationCb op,
eDepsOperation_Code opcode,
- const string &name);
+ const char *name,
+ int name_tag);
- void remove_operation(eDepsOperation_Code opcode, const string &name);
void clear_operations();
void tag_update(Depsgraph *graph);
@@ -194,7 +182,7 @@ struct PoseComponentDepsNode : public ComponentDepsNode {
/* Bone Component */
struct BoneComponentDepsNode : public ComponentDepsNode {
- void init(const ID *id, const string &subdata);
+ void init(const ID *id, const char *subdata);
struct bPoseChannel *pchan; /* the bone that this component represents */
diff --git a/source/blender/depsgraph/intern/nodes/deg_node_operation.cc b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc
index 5847af29ac2..9eed4dfe8d8 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc
@@ -68,7 +68,7 @@ string OperationDepsNode::full_identifier() const
{
string owner_str = "";
if (owner->type == DEPSNODE_TYPE_BONE) {
- owner_str = owner->owner->name + "." + owner->name;
+ owner_str = string(owner->owner->name) + "." + owner->name;
}
else {
owner_str = owner->owner->name;
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index 262ce0b9e23..c0d6963acbb 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -57,6 +57,7 @@
#include "ED_anim_api.h"
#include "ED_screen.h"
#include "ED_sequencer.h"
+#include "ED_util.h"
#include "anim_intern.h"
@@ -263,7 +264,8 @@ static void ANIM_OT_change_frame(wmOperatorType *ot)
ot->poll = change_frame_poll;
/* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR;
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR | OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* rna */
ot->prop = RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index ece0f18e96e..47e73f9b777 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -1328,6 +1328,7 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
ED_armature_sync_selection(arm->edbo);
+ BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index 5015829f868..322476dcca0 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -626,7 +626,7 @@ void POSE_OT_flip_names(wmOperatorType *ot)
/* api callbacks */
ot->exec = pose_flip_names_exec;
- ot->poll = ED_operator_posemode;
+ ot->poll = ED_operator_posemode_local;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index cd0ea23e2d3..8e8345d34c9 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -685,6 +685,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->type) {
case LEFTMOUSE: /* confirm */
case RETKEY:
+ case PADENTER:
{
/* return to normal cursor and header status */
ED_area_headerprint(pso->sa, NULL);
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 7dcbe2cc24c..ae83e899649 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -84,7 +84,8 @@
static int gp_data_add_exec(bContext *C, wmOperator *op)
{
bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
-
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
return OPERATOR_CANCELLED;
@@ -95,6 +96,15 @@ static int gp_data_add_exec(bContext *C, wmOperator *op)
id_us_min(&gpd->id);
*gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
+
+ /* if not exist brushes, create a new set */
+ if (ts) {
+ if (BLI_listbase_is_empty(&ts->gp_brushes)) {
+ /* create new brushes */
+ BKE_gpencil_brush_init_presets(ts);
+ }
+ }
+
}
/* notifiers */
@@ -174,7 +184,8 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot)
static int gp_layer_add_exec(bContext *C, wmOperator *op)
{
bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
-
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
/* if there's no existing Grease-Pencil data there, add some */
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
@@ -183,6 +194,14 @@ static int gp_layer_add_exec(bContext *C, wmOperator *op)
if (*gpd_ptr == NULL)
*gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
+ /* if not exist brushes, create a new set */
+ if (ts) {
+ if (BLI_listbase_is_empty(&ts->gp_brushes)) {
+ /* create new brushes */
+ BKE_gpencil_brush_init_presets(ts);
+ }
+ }
+
/* add new layer now */
BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 6a558d1c185..ec09add56b8 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -183,6 +183,7 @@ int ED_operator_uvmap(struct bContext *C);
int ED_operator_posemode_exclusive(struct bContext *C);
int ED_operator_posemode_context(struct bContext *C);
int ED_operator_posemode(struct bContext *C);
+int ED_operator_posemode_local(struct bContext *C);
int ED_operator_mask(struct bContext *C);
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index f5968397f65..a4afa958450 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -52,6 +52,8 @@ void ED_OT_flush_edits(struct wmOperatorType *ot);
/* undo.c */
void ED_undo_push(struct bContext *C, const char *str);
void ED_undo_push_op(struct bContext *C, struct wmOperator *op);
+void ED_undo_grouped_push(struct bContext *C, const char *str);
+void ED_undo_grouped_push_op(struct bContext *C, struct wmOperator *op);
void ED_undo_pop_op(struct bContext *C, struct wmOperator *op);
void ED_undo_pop(struct bContext *C);
void ED_undo_redo(struct bContext *C);
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 31598a44b09..d7f06b7db13 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -1083,6 +1083,15 @@ static int depthdropper_poll(bContext *C)
return 1;
}
}
+ else {
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (rv3d && rv3d->persp == RV3D_CAMOB) {
+ View3D *v3d = CTX_wm_view3d(C);
+ if (v3d->camera && v3d->camera->data && !ID_IS_LINKED_DATABLOCK(v3d->camera->data)) {
+ return 1;
+ }
+ }
+ }
return 0;
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 863f5e3852c..f3eeadb6604 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -2219,7 +2219,7 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB
/* ******************* copy and paste ******************** */
/* c = copy, v = paste */
-static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, char mode)
+static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, const char mode, const bool copy_array)
{
int buf_paste_len = 0;
const char *buf_paste = "";
@@ -2255,6 +2255,46 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data,
if (but->poin == NULL && but->rnapoin.data == NULL) {
/* pass */
}
+ else if (copy_array && but->rnapoin.data && but->rnaprop &&
+ ELEM(RNA_property_subtype(but->rnaprop), PROP_COLOR, PROP_TRANSLATION, PROP_DIRECTION,
+ PROP_VELOCITY, PROP_ACCELERATION, PROP_MATRIX, PROP_EULER, PROP_QUATERNION, PROP_AXISANGLE,
+ PROP_XYZ, PROP_XYZ_LENGTH, PROP_COLOR_GAMMA, PROP_COORDS))
+ {
+ float values[4];
+ int array_length = RNA_property_array_length(&but->rnapoin, but->rnaprop);
+
+ if (mode == 'c') {
+ char buf_copy[UI_MAX_DRAW_STR];
+
+ if (array_length == 4) {
+ values[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
+ }
+ else {
+ values[3] = 0.0f;
+ }
+ ui_but_v3_get(but, values);
+
+ BLI_snprintf(buf_copy, sizeof(buf_copy), "[%f, %f, %f, %f]", values[0], values[1], values[2], values[3]);
+ WM_clipboard_text_set(buf_copy, 0);
+ }
+ else {
+ if (sscanf(buf_paste, "[%f, %f, %f, %f]", &values[0], &values[1], &values[2], &values[3]) >= array_length) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ ui_but_v3_set(but, values);
+ if (but->rnaprop && array_length == 4) {
+ RNA_property_float_set_index(&but->rnapoin, but->rnaprop, 3, values[3]);
+ }
+ data->value = values[but->rnaindex];
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else {
+ WM_report(RPT_ERROR, "Paste expected 4 numbers, formatted: '[n, n, n, n]'");
+ show_report = true;
+ }
+ }
+ }
else if (mode == 'c') {
/* Get many decimal places, then strip trailing zeros.
* note: too high values start to give strange results */
@@ -6617,15 +6657,22 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa)
{
bScreen *sc = CTX_wm_screen(C);
+ const bool has_panel_category = UI_panel_category_is_visible(ar);
+ const bool any_item_visible = has_panel_category;
PointerRNA ptr;
uiPopupMenu *pup;
uiLayout *layout;
+ if (!any_item_visible) {
+ return;
+ }
+
RNA_pointer_create(&sc->id, &RNA_Panel, pa, &ptr);
pup = UI_popup_menu_begin(C, IFACE_("Panel"), ICON_NONE);
layout = UI_popup_menu_layout(pup);
- if (UI_panel_category_is_visible(ar)) {
+
+ if (has_panel_category) {
char tmpstr[80];
BLI_snprintf(tmpstr, sizeof(tmpstr), "%s" UI_SEP_CHAR_S "%s", IFACE_("Pin"), IFACE_("Shift+Left Mouse"));
uiItemR(layout, &ptr, "use_pin", 0, tmpstr, ICON_NONE);
@@ -6636,7 +6683,6 @@ void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa)
uiBut *but = block->buttons.last;
but->flag |= UI_BUT_HAS_SEP_CHAR;
}
-
}
UI_popup_menu_end(C, pup);
}
@@ -6959,7 +7005,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) {
/* handle copy-paste */
if (ELEM(event->type, CKEY, VKEY) && event->val == KM_PRESS &&
- IS_EVENT_MOD(event, ctrl, oskey) && !event->shift && !event->alt)
+ IS_EVENT_MOD(event, ctrl, oskey) && !event->shift)
{
/* Specific handling for listrows, we try to find their overlapping tex button. */
if (but->type == UI_BTYPE_LISTROW) {
@@ -6969,7 +7015,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
data = but->active;
}
}
- ui_but_copy_paste(C, but, data, (event->type == CKEY) ? 'c' : 'v');
+ ui_but_copy_paste(C, but, data, (event->type == CKEY) ? 'c' : 'v', event->alt);
return WM_UI_HANDLER_BREAK;
}
/* handle drop */
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index ff9d2840e9c..65b12fcd64e 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -213,173 +213,6 @@ static void viconutil_set_point(GLint pt[2], int x, int y)
pt[1] = y;
}
-static void viconutil_draw_tri(GLint(*pts)[2])
-{
- glBegin(GL_TRIANGLES);
- glVertex2iv(pts[0]);
- glVertex2iv(pts[1]);
- glVertex2iv(pts[2]);
- glEnd();
-}
-
-static void viconutil_draw_lineloop(GLint(*pts)[2], int numPoints)
-{
- int i;
-
- glBegin(GL_LINE_LOOP);
- for (i = 0; i < numPoints; i++) {
- glVertex2iv(pts[i]);
- }
- glEnd();
-}
-
-static void viconutil_draw_lineloop_smooth(GLint(*pts)[2], int numPoints)
-{
- glEnable(GL_LINE_SMOOTH);
- viconutil_draw_lineloop(pts, numPoints);
- glDisable(GL_LINE_SMOOTH);
-}
-
-static void viconutil_draw_points(GLint(*pts)[2], int numPoints, int pointSize)
-{
- int i;
-
- glBegin(GL_QUADS);
- for (i = 0; i < numPoints; i++) {
- int x = pts[i][0], y = pts[i][1];
-
- glVertex2i(x - pointSize, y - pointSize);
- glVertex2i(x + pointSize, y - pointSize);
- glVertex2i(x + pointSize, y + pointSize);
- glVertex2i(x - pointSize, y + pointSize);
- }
- glEnd();
-}
-
-/* Drawing functions */
-
-static void vicon_x_draw(int x, int y, int w, int h, float alpha)
-{
- x += 3;
- y += 3;
- w -= 6;
- h -= 6;
-
- glEnable(GL_LINE_SMOOTH);
-
- glLineWidth(2.5);
-
- glColor4f(0.0, 0.0, 0.0, alpha);
- glBegin(GL_LINES);
- glVertex2i(x, y);
- glVertex2i(x + w, y + h);
- glVertex2i(x + w, y);
- glVertex2i(x, y + h);
- glEnd();
-
- glDisable(GL_LINE_SMOOTH);
-}
-
-static void vicon_view3d_draw(int x, int y, int w, int h, float alpha)
-{
- int cx = x + w / 2;
- int cy = y + h / 2;
- int d = MAX2(2, h / 3);
-
- glColor4f(0.5, 0.5, 0.5, alpha);
- glBegin(GL_LINES);
- glVertex2i(x, cy - d);
- glVertex2i(x + w, cy - d);
- glVertex2i(x, cy + d);
- glVertex2i(x + w, cy + d);
-
- glVertex2i(cx - d, y);
- glVertex2i(cx - d, y + h);
- glVertex2i(cx + d, y);
- glVertex2i(cx + d, y + h);
- glEnd();
-
- glColor4f(0.0, 0.0, 0.0, alpha);
- glBegin(GL_LINES);
- glVertex2i(x, cy);
- glVertex2i(x + w, cy);
- glVertex2i(cx, y);
- glVertex2i(cx, y + h);
- glEnd();
-}
-
-static void vicon_edit_draw(int x, int y, int w, int h, float alpha)
-{
- GLint pts[4][2];
-
- viconutil_set_point(pts[0], x + 3, y + 3);
- viconutil_set_point(pts[1], x + w - 3, y + 3);
- viconutil_set_point(pts[2], x + w - 3, y + h - 3);
- viconutil_set_point(pts[3], x + 3, y + h - 3);
-
- glColor4f(0.0, 0.0, 0.0, alpha);
- viconutil_draw_lineloop(pts, 4);
-
- glColor3f(1, 1, 0.0);
- viconutil_draw_points(pts, 4, 1);
-}
-
-static void vicon_editmode_hlt_draw(int x, int y, int w, int h, float alpha)
-{
- GLint pts[3][2];
-
- viconutil_set_point(pts[0], x + w / 2, y + h - 2);
- viconutil_set_point(pts[1], x + 3, y + 4);
- viconutil_set_point(pts[2], x + w - 3, y + 4);
-
- glColor4f(0.5, 0.5, 0.5, alpha);
- viconutil_draw_tri(pts);
-
- glColor4f(0.0, 0.0, 0.0, 1);
- viconutil_draw_lineloop_smooth(pts, 3);
-
- glColor3f(1, 1, 0.0);
- viconutil_draw_points(pts, 3, 1);
-}
-
-static void vicon_editmode_dehlt_draw(int x, int y, int w, int h, float UNUSED(alpha))
-{
- GLint pts[3][2];
-
- viconutil_set_point(pts[0], x + w / 2, y + h - 2);
- viconutil_set_point(pts[1], x + 3, y + 4);
- viconutil_set_point(pts[2], x + w - 3, y + 4);
-
- glColor4f(0.0f, 0.0f, 0.0f, 1);
- viconutil_draw_lineloop_smooth(pts, 3);
-
- glColor3f(0.9f, 0.9f, 0.9f);
- viconutil_draw_points(pts, 3, 1);
-}
-
-static void vicon_disclosure_tri_right_draw(int x, int y, int w, int UNUSED(h), float alpha)
-{
- GLint pts[3][2];
- int cx = x + w / 2;
- int cy = y + w / 2;
- int d = w / 3, d2 = w / 5;
-
- viconutil_set_point(pts[0], cx - d2, cy + d);
- viconutil_set_point(pts[1], cx - d2, cy - d);
- viconutil_set_point(pts[2], cx + d2, cy);
-
- glBegin(GL_TRIANGLES);
- glColor4f(0.8f, 0.8f, 0.8f, alpha);
- glVertex2iv(pts[0]);
- glVertex2iv(pts[1]);
- glColor4f(0.3f, 0.3f, 0.3f, alpha);
- glVertex2iv(pts[2]);
- glEnd();
-
- glColor4f(0.0f, 0.0f, 0.0f, 1);
- viconutil_draw_lineloop_smooth(pts, 3);
-}
-
static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float alpha)
{
GLint pts[3][2];
@@ -400,63 +233,6 @@ static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float
glEnd();
}
-static void vicon_disclosure_tri_down_draw(int x, int y, int w, int UNUSED(h), float alpha)
-{
- GLint pts[3][2];
- int cx = x + w / 2;
- int cy = y + w / 2;
- int d = w / 3, d2 = w / 5;
-
- viconutil_set_point(pts[0], cx + d, cy + d2);
- viconutil_set_point(pts[1], cx - d, cy + d2);
- viconutil_set_point(pts[2], cx, cy - d2);
-
- glBegin(GL_TRIANGLES);
- glColor4f(0.8f, 0.8f, 0.8f, alpha);
- glVertex2iv(pts[0]);
- glVertex2iv(pts[1]);
- glColor4f(0.3f, 0.3f, 0.3f, alpha);
- glVertex2iv(pts[2]);
- glEnd();
-
- glColor4f(0.0f, 0.0f, 0.0f, 1);
- viconutil_draw_lineloop_smooth(pts, 3);
-}
-
-static void vicon_move_up_draw(int x, int y, int w, int h, float UNUSED(alpha))
-{
- int d = -2;
-
- glEnable(GL_LINE_SMOOTH);
- glLineWidth(1);
- glColor3f(0.0, 0.0, 0.0);
-
- glBegin(GL_LINE_STRIP);
- glVertex2i(x + w / 2 - d * 2, y + h / 2 + d);
- glVertex2i(x + w / 2, y + h / 2 - d + 1);
- glVertex2i(x + w / 2 + d * 2, y + h / 2 + d);
- glEnd();
-
- glDisable(GL_LINE_SMOOTH);
-}
-
-static void vicon_move_down_draw(int x, int y, int w, int h, float UNUSED(alpha))
-{
- int d = 2;
-
- glEnable(GL_LINE_SMOOTH);
- glLineWidth(1);
- glColor3f(0.0, 0.0, 0.0);
-
- glBegin(GL_LINE_STRIP);
- glVertex2i(x + w / 2 - d * 2, y + h / 2 + d);
- glVertex2i(x + w / 2, y + h / 2 - d - 1);
- glVertex2i(x + w / 2 + d * 2, y + h / 2 + d);
- glEnd();
-
- glDisable(GL_LINE_SMOOTH);
-}
-
static void vicon_keytype_draw_wrapper(int x, int y, int w, int h, float alpha, short key_type)
{
/* init dummy theme state for Action Editor - where these colors are defined
@@ -782,15 +558,6 @@ static void init_internal_icons(void)
}
}
- def_internal_vicon(VICO_VIEW3D_VEC, vicon_view3d_draw);
- def_internal_vicon(VICO_EDIT_VEC, vicon_edit_draw);
- def_internal_vicon(VICO_EDITMODE_VEC_DEHLT, vicon_editmode_dehlt_draw);
- def_internal_vicon(VICO_EDITMODE_VEC_HLT, vicon_editmode_hlt_draw);
- def_internal_vicon(VICO_DISCLOSURE_TRI_RIGHT_VEC, vicon_disclosure_tri_right_draw);
- def_internal_vicon(VICO_DISCLOSURE_TRI_DOWN_VEC, vicon_disclosure_tri_down_draw);
- def_internal_vicon(VICO_MOVE_UP_VEC, vicon_move_up_draw);
- def_internal_vicon(VICO_MOVE_DOWN_VEC, vicon_move_down_draw);
- def_internal_vicon(VICO_X_VEC, vicon_x_draw);
def_internal_vicon(VICO_SMALL_TRI_RIGHT_VEC, vicon_small_tri_right_draw);
def_internal_vicon(VICO_KEYTYPE_KEYFRAME_VEC, vicon_keytype_keyframe_draw);
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index 5e24dc96255..539284030c2 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -2750,17 +2750,21 @@ void init_userdef_do_versions(void)
}
}
+ if (!USER_VERSION_ATLEAST(278, 3)) {
+ for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
+ /* Keyframe Indicators (were using wrong alpha) */
+ btheme->tv3d.time_keyframe[3] = btheme->tv3d.time_gp_keyframe[3] = 255;
+ btheme->ttime.time_keyframe[3] = btheme->ttime.time_gp_keyframe[3] = 255;
+ }
+ }
+
/**
* Include next version bump.
*
* (keep this block even if it becomes empty).
*/
{
- for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
- /* Keyframe Indicators (were using wrong alpha) */
- btheme->tv3d.time_keyframe[3] = btheme->tv3d.time_gp_keyframe[3] = 255;
- btheme->ttime.time_keyframe[3] = btheme->ttime.time_gp_keyframe[3] = 255;
- }
+
}
if (U.pixelsize == 0.0f)
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 8659100df87..baae92f962e 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -305,7 +305,6 @@ void WM_OT_collada_export(wmOperatorType *ot)
static EnumPropertyItem prop_bc_export_transformation_type[] = {
{BC_TRANSFORMATION_TYPE_MATRIX, "matrix", 0, "Matrix", "Use <matrix> to specify transformations"},
{BC_TRANSFORMATION_TYPE_TRANSROTLOC, "transrotloc", 0, "TransRotLoc", "Use <translate>, <rotate>, <scale> to specify transformations"},
- {BC_TRANSFORMATION_TYPE_BOTH, "both", 0, "Both", "Use <matrix> AND <translate>, <rotate>, <scale> to specify transformations"},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c
index e31e4096ded..e05ce727e22 100644
--- a/source/blender/editors/mesh/editmesh_rip.c
+++ b/source/blender/editors/mesh/editmesh_rip.c
@@ -496,7 +496,7 @@ static void edbm_tagged_loop_pairs_do_fill_faces(BMesh *bm, UnorderedLoopPair *u
}
/* face should never exist */
- BLI_assert(BM_face_exists(f_verts, f_verts[3] ? 4 : 3, &f) == false);
+ BLI_assert(!BM_face_exists(f_verts, f_verts[3] ? 4 : 3));
f = BM_face_create_verts(bm, f_verts, f_verts[3] ? 4 : 3, f_example, BM_CREATE_NOP, true);
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 7e31deba2c7..c57b0215d46 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -1561,6 +1561,18 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
/* edges may rotate into hidden vertices, if this does _not_ run we get an ilogical state */
BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, true);
BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
+
+ const int tot_rotate = BMO_slot_buffer_count(bmop.slots_out, "edges.out");
+ const int tot_failed = tot - tot_rotate;
+ if (tot_failed != 0) {
+ /* If some edges fail to rotate, we need to re-select them,
+ * otherwise we can end up with invalid selection
+ * (unselected edge between 2 selected faces). */
+ BM_mesh_elem_hflag_enable_test(em->bm, BM_EDGE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG);
+
+ BKE_reportf(op->reports, RPT_WARNING, "Unable to rotate %d edge(s)", tot_failed);
+ }
+
EDBM_selectmode_flush(em);
if (!EDBM_op_finish(em, &bmop, op, true)) {
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 4fc61e0912e..438c3acdb11 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -1139,7 +1139,6 @@ BMEdge *EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e)
BMFace *EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
{
- BMFace *f_mirr = NULL;
BMVert **v_mirr_arr = BLI_array_alloca(v_mirr_arr, f->len);
BMLoop *l_iter, *l_first;
@@ -1152,8 +1151,7 @@ BMFace *EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
}
} while ((l_iter = l_iter->next) != l_first);
- BM_face_exists(v_mirr_arr, f->len, &f_mirr);
- return f_mirr;
+ return BM_face_exists(v_mirr_arr, f->len);
}
void EDBM_verts_mirror_cache_clear(BMEditMesh *em, BMVert *v)
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 9f91feee4c6..8e64cdc9751 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1307,7 +1307,9 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base,
if (use_hierarchy || use_base_parent) {
dupli_gh = BLI_ghash_ptr_new(__func__);
- parent_gh = BLI_ghash_new(dupliobject_hash, dupliobject_cmp, __func__);
+ if (use_hierarchy) {
+ parent_gh = BLI_ghash_new(dupliobject_hash, dupliobject_cmp, __func__);
+ }
}
for (dob = lb->first; dob; dob = dob->next) {
@@ -1344,10 +1346,17 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base,
copy_m4_m4(ob->obmat, dob->mat);
BKE_object_apply_mat4(ob, ob->obmat, false, false);
- if (dupli_gh)
+ if (dupli_gh) {
BLI_ghash_insert(dupli_gh, dob, ob);
- if (parent_gh)
- BLI_ghash_insert(parent_gh, dob, ob);
+ }
+ if (parent_gh) {
+ void **val;
+ /* Due to nature of hash/comparison of this ghash, a lot of duplis may be considered as 'the same',
+ * this avoids trying to insert same key several time and raise asserts in debug builds... */
+ if (!BLI_ghash_ensure_p(parent_gh, dob, &val)) {
+ *val = ob;
+ }
+ }
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
}
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index d1232fd2aab..f448e925dd9 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1740,10 +1740,16 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in
clear_sca_new_poins(); /* sensor/contr/act */
- /* newid may still have some trash from Outliner tree building,
- * so clear that first to avoid errors [#26002] */
- for (ob = bmain->object.first; ob; ob = ob->id.next)
- ob->id.newid = NULL;
+ /* newid may still have some trash from Outliner tree building, so clear that first to avoid errors, see T26002.
+ * We have to clear whole datablocks, not only Object one may be accessed here, see T49905. */
+ ListBase *lbarray[MAX_LIBARRAY];
+ int a = set_listbasepointers(bmain, lbarray);
+ while (a--) {
+ ListBase *lb = lbarray[a];
+ for (ID *id = lb->first; id; id = id->next) {
+ id->newid = NULL;
+ }
+ }
/* duplicate (must set newid) */
for (base = FIRSTBASE; base; base = base->next) {
@@ -2235,7 +2241,7 @@ static int make_local_exec(bContext *C, wmOperator *op)
"Orphan library objects added to the current scene to avoid loss");
}
- BKE_library_make_local(bmain, NULL, false, false); /* NULL is all libs */
+ BKE_library_make_local(bmain, NULL, NULL, false, false); /* NULL is all libs */
WM_event_add_notifier(C, NC_WINDOW, NULL);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index bd016b7fcfb..56f59dca9a1 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -886,7 +886,7 @@ static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum)
const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
/* warning, this lookup is _not_ fast */
- if (cd_dvert_offset != -1) {
+ if (cd_dvert_offset != -1 && vertnum < em->bm->totvert) {
BMVert *eve;
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
eve = BM_vert_at_index(em->bm, vertnum);
@@ -2604,6 +2604,8 @@ static int vertex_group_remove_exec(bContext *C, wmOperator *op)
if (RNA_boolean_get(op->ptr, "all"))
BKE_object_defgroup_remove_all(ob);
+ else if (RNA_boolean_get(op->ptr, "all_unlocked"))
+ BKE_object_defgroup_remove_all_ex(ob, true);
else
vgroup_delete_active(ob);
@@ -2633,6 +2635,7 @@ void OBJECT_OT_vertex_group_remove(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all vertex groups");
+ RNA_def_boolean(ot->srna, "all_unlocked", 0, "All Unlocked", "Remove all unlocked vertex groups");
}
static int vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 9d9ccf2f3ba..16842efb436 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -315,6 +315,12 @@ static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr)
RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id);
IMB_freeImBuf(out);
}
+ else if (gpd){
+ /* If there are no strips, Grease Pencil still needs a buffer to draw on */
+ ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect);
+ RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id);
+ IMB_freeImBuf(out);
+ }
if (gpd) {
int i;
@@ -479,23 +485,24 @@ static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, Rende
/* copy image data from rectf */
// XXX: Needs conversion.
unsigned char *src = (unsigned char *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32;
- float *dest = rp->rect;
-
- int x, y, rectx, recty;
- rectx = rr->rectx;
- recty = rr->recty;
- for (y = 0; y < recty; y++) {
- for (x = 0; x < rectx; x++) {
- unsigned char *pixSrc = src + 4 * (rectx * y + x);
- if (pixSrc[3] > 0) {
- float *pixDest = dest + 4 * (rectx * y + x);
- float float_src[4];
- srgb_to_linearrgb_uchar4(float_src, pixSrc);
- addAlphaOverFloat(pixDest, float_src);
+ if (src != NULL) {
+ float *dest = rp->rect;
+
+ int x, y, rectx, recty;
+ rectx = rr->rectx;
+ recty = rr->recty;
+ for (y = 0; y < recty; y++) {
+ for (x = 0; x < rectx; x++) {
+ unsigned char *pixSrc = src + 4 * (rectx * y + x);
+ if (pixSrc[3] > 0) {
+ float *pixDest = dest + 4 * (rectx * y + x);
+ float float_src[4];
+ srgb_to_linearrgb_uchar4(float_src, pixSrc);
+ addAlphaOverFloat(pixDest, float_src);
+ }
}
}
}
-
/* back layer status */
i = 0;
for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) {
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 860a865466a..c69e01422e0 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -442,6 +442,17 @@ int ED_operator_posemode(bContext *C)
return 0;
}
+int ED_operator_posemode_local(bContext *C)
+{
+ if (ED_operator_posemode(C)) {
+ Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
+ bArmature *arm = ob->data;
+ return !(ID_IS_LINKED_DATABLOCK(&ob->id) ||
+ ID_IS_LINKED_DATABLOCK(&arm->id));
+ }
+ return false;
+}
+
/* wrapper for ED_space_image_show_uvedit */
int ED_operator_uvedit(bContext *C)
{
@@ -2136,7 +2147,8 @@ static void SCREEN_OT_frame_offset(wmOperatorType *ot)
ot->exec = frame_offset_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = 0;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* rna */
RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
@@ -2189,7 +2201,8 @@ static void SCREEN_OT_frame_jump(wmOperatorType *ot)
ot->exec = frame_jump_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* rna */
RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
@@ -2295,7 +2308,8 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
ot->exec = keyframe_jump_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* properties */
RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
@@ -2357,7 +2371,8 @@ static void SCREEN_OT_marker_jump(wmOperatorType *ot)
ot->exec = marker_jump_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* properties */
RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index fe0fb3f5035..53434b18d06 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -4688,7 +4688,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st
sculpt_restore_mesh(sd, ob);
if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) {
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, sd->constant_detail / 100.0f);
+ BKE_pbvh_bmesh_detail_size_set(ss->pbvh, 1.0f / sd->constant_detail);
}
else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f);
@@ -5406,7 +5406,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
if (!ts->sculpt->detail_percent)
ts->sculpt->detail_percent = 25;
if (ts->sculpt->constant_detail == 0.0f)
- ts->sculpt->constant_detail = 30.0f;
+ ts->sculpt->constant_detail = 3.0f;
/* Set sane default tiling offsets */
if (!ts->sculpt->paint.tile_offset[0]) ts->sculpt->paint.tile_offset[0] = 1.0f;
@@ -5543,7 +5543,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
size = max_fff(bb_max[0], bb_max[1], bb_max[2]);
/* update topology size */
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, sd->constant_detail / 100.0f);
+ BKE_pbvh_bmesh_detail_size_set(ss->pbvh, 1.0f / sd->constant_detail);
sculpt_undo_push_begin("Dynamic topology flood fill");
sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS);
@@ -5608,7 +5608,8 @@ static void sample_detail(bContext *C, int ss_co[2])
ray_start, ray_normal, false);
if (srd.hit) {
- sd->constant_detail = srd.detail * 100.0f;
+ /* convert edge length to detail resolution */
+ sd->constant_detail = 1.0f / srd.detail;
}
}
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 58c538c4ee5..72de7e5c81c 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -587,6 +587,8 @@ static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUS
last_category = user->category;
}
+
+ UI_block_flag_enable(block, UI_BLOCK_NO_FLIP);
}
void uiTemplateTextureUser(uiLayout *layout, bContext *C)
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 6af36ea6778..83469a48165 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -2510,7 +2510,7 @@ static void filelist_readjob_do(
* Using an atomic operation to avoid having to lock thread...
* Note that we do not really need this here currently, since there is a single listing thread, but better
* remain consistent about threading! */
- *((uint32_t *)entry->uuid) = atomic_add_uint32((uint32_t *)filelist->filelist_intern.curr_uuid, 1);
+ *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32((uint32_t *)filelist->filelist_intern.curr_uuid, 1);
/* Only thing we change in direntry here, so we need to free it first. */
MEM_freeN(entry->relpath);
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index ea40d4eb5e1..dd282c427f6 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -7915,10 +7915,6 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
if (!render_override && sds->draw_velocity) {
draw_smoke_velocity(sds, viewnormal);
}
-
-#ifdef SMOKE_DEBUG_HEAT
- draw_smoke_heat(smd->domain, ob);
-#endif
}
}
diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c
index b0e21601b9c..27ecbf83db5 100644
--- a/source/blender/editors/space_view3d/drawvolume.c
+++ b/source/blender/editors/space_view3d/drawvolume.c
@@ -1,4 +1,4 @@
-/*
+/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
@@ -41,6 +41,7 @@
#include "BLI_math.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_texture.h"
#include "BKE_particle.h"
#include "smoke_API.h"
@@ -62,28 +63,33 @@ struct GPUTexture;
# include "PIL_time_utildefines.h"
#endif
-static GPUTexture *create_flame_spectrum_texture(void)
+/* *************************** Transfer functions *************************** */
+
+enum {
+ TFUNC_FLAME_SPECTRUM = 0,
+ TFUNC_COLOR_RAMP = 1,
+};
+
+#define TFUNC_WIDTH 256
+
+static void create_flame_spectrum_texture(float *data)
{
-#define SPEC_WIDTH 256
#define FIRE_THRESH 7
#define MAX_FIRE_ALPHA 0.06f
#define FULL_ON_FIRE 100
- GPUTexture *tex;
- int i, j, k;
- float *spec_data = MEM_mallocN(SPEC_WIDTH * 4 * sizeof(float), "spec_data");
- float *spec_pixels = MEM_mallocN(SPEC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels");
+ float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels");
- blackbody_temperature_to_rgb_table(spec_data, SPEC_WIDTH, 1500, 3000);
+ blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000);
- for (i = 0; i < 16; i++) {
- for (j = 0; j < 16; j++) {
- for (k = 0; k < SPEC_WIDTH; k++) {
- int index = (j * SPEC_WIDTH * 16 + i * SPEC_WIDTH + k) * 4;
+ for (int i = 0; i < 16; i++) {
+ for (int j = 0; j < 16; j++) {
+ for (int k = 0; k < TFUNC_WIDTH; k++) {
+ int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4;
if (k >= FIRE_THRESH) {
- spec_pixels[index] = (spec_data[k * 4]);
- spec_pixels[index + 1] = (spec_data[k * 4 + 1]);
- spec_pixels[index + 2] = (spec_data[k * 4 + 2]);
+ spec_pixels[index] = (data[k * 4]);
+ spec_pixels[index + 1] = (data[k * 4 + 1]);
+ spec_pixels[index + 2] = (data[k * 4 + 2]);
spec_pixels[index + 3] = MAX_FIRE_ALPHA * (
(k > FULL_ON_FIRE) ? 1.0f : (k - FIRE_THRESH) / ((float)FULL_ON_FIRE - FIRE_THRESH));
}
@@ -94,19 +100,69 @@ static GPUTexture *create_flame_spectrum_texture(void)
}
}
- tex = GPU_texture_create_1D(SPEC_WIDTH, spec_pixels, NULL);
+ memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH);
- MEM_freeN(spec_data);
MEM_freeN(spec_pixels);
-#undef SPEC_WIDTH
#undef FIRE_THRESH
#undef MAX_FIRE_ALPHA
#undef FULL_ON_FIRE
+}
+
+static void create_color_ramp(const ColorBand *coba, float *data)
+{
+ for (int i = 0; i < TFUNC_WIDTH; i++) {
+ do_colorband(coba, (float)i / TFUNC_WIDTH, &data[i * 4]);
+ }
+}
+
+static GPUTexture *create_transfer_function(int type, const ColorBand *coba)
+{
+ float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__);
+
+ switch (type) {
+ case TFUNC_FLAME_SPECTRUM:
+ create_flame_spectrum_texture(data);
+ break;
+ case TFUNC_COLOR_RAMP:
+ create_color_ramp(coba, data);
+ break;
+ }
+
+ GPUTexture *tex = GPU_texture_create_1D(TFUNC_WIDTH, data, NULL);
+
+ MEM_freeN(data);
return tex;
}
+static GPUTexture *create_field_texture(SmokeDomainSettings *sds)
+{
+ float *field = NULL;
+
+ switch (sds->coba_field) {
+#ifdef WITH_SMOKE
+ case FLUID_FIELD_DENSITY: field = smoke_get_density(sds->fluid); break;
+ case FLUID_FIELD_HEAT: field = smoke_get_heat(sds->fluid); break;
+ case FLUID_FIELD_FUEL: field = smoke_get_fuel(sds->fluid); break;
+ case FLUID_FIELD_REACT: field = smoke_get_react(sds->fluid); break;
+ case FLUID_FIELD_FLAME: field = smoke_get_flame(sds->fluid); break;
+ case FLUID_FIELD_VELOCITY_X: field = smoke_get_velocity_x(sds->fluid); break;
+ case FLUID_FIELD_VELOCITY_Y: field = smoke_get_velocity_y(sds->fluid); break;
+ case FLUID_FIELD_VELOCITY_Z: field = smoke_get_velocity_z(sds->fluid); break;
+ case FLUID_FIELD_COLOR_R: field = smoke_get_color_r(sds->fluid); break;
+ case FLUID_FIELD_COLOR_G: field = smoke_get_color_g(sds->fluid); break;
+ case FLUID_FIELD_COLOR_B: field = smoke_get_color_b(sds->fluid); break;
+ case FLUID_FIELD_FORCE_X: field = smoke_get_force_x(sds->fluid); break;
+ case FLUID_FIELD_FORCE_Y: field = smoke_get_force_y(sds->fluid); break;
+ case FLUID_FIELD_FORCE_Z: field = smoke_get_force_z(sds->fluid); break;
+#endif
+ default: return NULL;
+ }
+
+ return GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], 1, field);
+}
+
typedef struct VolumeSlicer {
float size[3];
float min[3];
@@ -347,6 +403,7 @@ static int create_view_aligned_slices(VolumeSlicer *slicer,
}
static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture *tex_spec,
+ GPUTexture *tex_tfunc, GPUTexture *tex_coba,
bool use_fire, const float min[3],
const float ob_sizei[3], const float invsize[3])
{
@@ -359,6 +416,8 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture
int densityscale_location;
int spec_location, flame_location;
int shadow_location, actcol_location;
+ int tfunc_location = 0;
+ int coba_location = 0;
if (use_fire) {
spec_location = GPU_shader_get_uniform(shader, "spectrum_texture");
@@ -370,6 +429,11 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture
soot_location = GPU_shader_get_uniform(shader, "soot_texture");
stepsize_location = GPU_shader_get_uniform(shader, "step_size");
densityscale_location = GPU_shader_get_uniform(shader, "density_scale");
+
+ if (sds->use_coba) {
+ tfunc_location = GPU_shader_get_uniform(shader, "transfer_texture");
+ coba_location = GPU_shader_get_uniform(shader, "color_band_texture");
+ }
}
GPU_shader_bind(shader);
@@ -397,6 +461,14 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture
if ((sds->active_fields & SM_ACTIVE_COLORS) == 0)
mul_v3_v3(active_color, sds->active_color);
GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color);
+
+ if (sds->use_coba) {
+ GPU_texture_bind(tex_tfunc, 4);
+ GPU_shader_uniform_texture(shader, tfunc_location, tex_tfunc);
+
+ GPU_texture_bind(tex_coba, 5);
+ GPU_shader_uniform_texture(shader, coba_location, tex_coba);
+ }
}
GPU_shader_uniform_vector(shader, min_location, 3, 1, min);
@@ -404,7 +476,8 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture
GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize);
}
-static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool use_fire)
+static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec,
+ GPUTexture *tex_tfunc, GPUTexture *tex_coba, bool use_fire)
{
GPU_shader_unbind();
@@ -417,20 +490,30 @@ static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool u
}
else {
GPU_texture_unbind(sds->tex_shadow);
+
+ if (sds->use_coba) {
+ GPU_texture_unbind(tex_tfunc);
+ GPU_texture_free(tex_tfunc);
+
+ GPU_texture_unbind(tex_coba);
+ GPU_texture_free(tex_coba);
+ }
}
}
static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const VolumeSlicer *slicer,
const float ob_sizei[3], const float invsize[3], const int num_points, const bool do_fire)
{
- GPUTexture *tex_spec = (do_fire) ? create_flame_spectrum_texture() : NULL;
+ GPUTexture *tex_spec = (do_fire) ? create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL) : NULL;
+ GPUTexture *tex_tfunc = (sds->use_coba) ? create_transfer_function(TFUNC_COLOR_RAMP, sds->coba) : NULL;
+ GPUTexture *tex_coba = (sds->use_coba) ? create_field_texture(sds) : NULL;
GLuint vertex_buffer;
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer->verts[0][0], GL_STATIC_DRAW);
- bind_shader(sds, shader, tex_spec, do_fire, slicer->min, ob_sizei, invsize);
+ bind_shader(sds, shader, tex_spec, tex_tfunc, tex_coba, do_fire, slicer->min, ob_sizei, invsize);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, NULL);
@@ -439,7 +522,7 @@ static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const Volum
glDisableClientState(GL_VERTEX_ARRAY);
- unbind_shader(sds, tex_spec, do_fire);
+ unbind_shader(sds, tex_spec, tex_tfunc, tex_coba, do_fire);
/* cleanup */
@@ -459,7 +542,16 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame;
- GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE);
+ GPUBuiltinShader builtin_shader;
+
+ if (sds->use_coba) {
+ builtin_shader = GPU_SHADER_SMOKE_COBA;
+ }
+ else {
+ builtin_shader = GPU_SHADER_SMOKE;
+ }
+
+ GPUShader *shader = GPU_shader_get_builtin_shader(builtin_shader);
if (!shader) {
fprintf(stderr, "Unable to create GLSL smoke shader.\n");
@@ -549,7 +641,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
draw_buffer(sds, shader, &slicer, ob_sizei, invsize, num_points, false);
/* Draw fire separately (T47639). */
- if (use_fire) {
+ if (use_fire && !sds->use_coba) {
glBlendFunc(GL_ONE, GL_ONE);
draw_buffer(sds, fire_shader, &slicer, ob_sizei, invsize, num_points, true);
}
@@ -759,50 +851,3 @@ void draw_smoke_velocity(SmokeDomainSettings *domain, float viewnormal[3])
UNUSED_VARS(domain, viewnormal);
#endif
}
-
-#ifdef SMOKE_DEBUG_HEAT
-void draw_smoke_heat(SmokeDomainSettings *domain, Object *ob)
-{
- float x, y, z;
- float x0, y0, z0;
- int *base_res = domain->base_res;
- int *res = domain->res;
- int *res_min = domain->res_min;
- int *res_max = domain->res_max;
- float *heat = smoke_get_heat(domain->fluid);
-
- float min[3];
- float *cell_size = domain->cell_size;
- float step_size = ((float)max_iii(base_res[0], base_res[1], base_res[2])) / 16.f;
- float vf = domain->scale / 16.f * 2.f; /* velocity factor */
-
- /* set first position so that it doesn't jump when domain moves */
- x0 = res_min[0] + fmod(-(float)domain->shift[0] + res_min[0], step_size);
- y0 = res_min[1] + fmod(-(float)domain->shift[1] + res_min[1], step_size);
- z0 = res_min[2] + fmod(-(float)domain->shift[2] + res_min[2], step_size);
- if (x0 < res_min[0]) x0 += step_size;
- if (y0 < res_min[1]) y0 += step_size;
- if (z0 < res_min[2]) z0 += step_size;
- add_v3_v3v3(min, domain->p0, domain->obj_shift_f);
-
- for (x = floor(x0); x < res_max[0]; x += step_size)
- for (y = floor(y0); y < res_max[1]; y += step_size)
- for (z = floor(z0); z < res_max[2]; z += step_size) {
- int index = (floor(x) - res_min[0]) + (floor(y) - res_min[1]) * res[0] + (floor(z) - res_min[2]) * res[0] * res[1];
-
- float pos[3] = {min[0] + ((float)x + 0.5f) * cell_size[0], min[1] + ((float)y + 0.5f) * cell_size[1], min[2] + ((float)z + 0.5f) * cell_size[2]};
-
- /* draw heat as different sized points */
- if (heat[index] >= 0.01f) {
- float col_gb = 1.0f - heat[index];
- CLAMP(col_gb, 0.0f, 1.0f);
- glColor3f(1.0f, col_gb, col_gb);
- glPointSize(24.0f * heat[index]);
-
- glBegin(GL_POINTS);
- glVertex3f(pos[0], pos[1], pos[2]);
- glEnd();
- }
- }
-}
-#endif
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 0e2cb95dd89..b11f42bcfef 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -296,14 +296,8 @@ void draw_smoke_volume(struct SmokeDomainSettings *sds, struct Object *ob,
const float min[3], const float max[3],
const float viewnormal[3]);
-//#define SMOKE_DEBUG_HEAT
-
void draw_smoke_velocity(struct SmokeDomainSettings *domain, float viewnormal[3]);
-#ifdef SMOKE_DEBUG_HEAT
-void draw_smoke_heat(struct SmokeDomainSettings *domain, struct Object *ob);
-#endif
-
/* workaround for trivial but noticeable camera bug caused by imprecision
* between view border calculation in 2D/3D space, workaround for bug [#28037].
* without this define we get the old behavior which is to try and align them
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index ef6cff19181..daf0aed59e7 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -3392,7 +3392,9 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3])
}
protectedTransBits(td->protectflag, vec);
- add_v3_v3v3(td->loc, td->iloc, vec);
+ if (td->loc) {
+ add_v3_v3v3(td->loc, td->iloc, vec);
+ }
constraintTransLim(t, td);
}
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 9c266890d6d..ce3d903b8f6 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -5583,7 +5583,7 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob,
if (tmode == TFM_TRANSLATION) {
do_loc = true;
}
- else if (tmode == TFM_ROTATION) {
+ else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
if (v3d->around == V3D_AROUND_ACTIVE) {
if (ob != OBACT)
do_loc = true;
@@ -5728,7 +5728,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o
else
do_loc = true;
}
- else if (tmode == TFM_ROTATION) {
+ else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
if (ELEM(v3d->around, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE))
do_loc = true;
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index ee6700666c0..4a9311416b3 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -217,6 +217,19 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
return OPERATOR_FINISHED;
}
+void ED_undo_grouped_push(bContext *C, const char *str)
+{
+ /* do nothing if previous undo task is the same as this one (or from the same undo group) */
+ const char *last_undo = BKE_undo_get_name_last();
+
+ if (last_undo && STREQ(str, last_undo)) {
+ return;
+ }
+
+ /* push as usual */
+ ED_undo_push(C, str);
+}
+
void ED_undo_pop(bContext *C)
{
ed_undo_step(C, 1, NULL);
@@ -232,6 +245,16 @@ void ED_undo_push_op(bContext *C, wmOperator *op)
ED_undo_push(C, op->type->name);
}
+void ED_undo_grouped_push_op(bContext *C, wmOperator *op)
+{
+ if (op->type->undo_group[0] != '\0') {
+ ED_undo_grouped_push(C, op->type->undo_group);
+ }
+ else {
+ ED_undo_grouped_push(C, op->type->name);
+ }
+}
+
void ED_undo_pop_op(bContext *C, wmOperator *op)
{
/* search back a couple of undo's, in case something else added pushes */
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 762329ee077..5b94db6e120 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -89,6 +89,7 @@ typedef enum GPUBuiltinShader {
GPU_SHADER_SEP_GAUSSIAN_BLUR = 1,
GPU_SHADER_SMOKE = 2,
GPU_SHADER_SMOKE_FIRE = 3,
+ GPU_SHADER_SMOKE_COBA = 4,
} GPUBuiltinShader;
GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader);
diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c
index 5cfb323bc4b..14f2764b009 100644
--- a/source/blender/gpu/intern/gpu_shader.c
+++ b/source/blender/gpu/intern/gpu_shader.c
@@ -68,6 +68,7 @@ static struct GPUShadersGlobal {
GPUShader *sep_gaussian_blur;
GPUShader *smoke;
GPUShader *smoke_fire;
+ GPUShader *smoke_coba;
/* cache for shader fx. Those can exist in combinations so store them here */
GPUShader *fx_shaders[MAX_FX_SHADERS * 2];
} shaders;
@@ -623,6 +624,13 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
NULL, NULL, NULL, 0, 0, 0);
retval = GG.shaders.smoke_fire;
break;
+ case GPU_SHADER_SMOKE_COBA:
+ if (!GG.shaders.smoke_coba)
+ GG.shaders.smoke_coba = GPU_shader_create(
+ datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl,
+ NULL, NULL, "#define USE_COBA;\n", 0, 0, 0);
+ retval = GG.shaders.smoke_coba;
+ break;
}
if (retval == NULL)
@@ -734,6 +742,11 @@ void GPU_shader_free_builtin_shaders(void)
GG.shaders.smoke_fire = NULL;
}
+ if (GG.shaders.smoke_coba) {
+ GPU_shader_free(GG.shaders.smoke_coba);
+ GG.shaders.smoke_coba = NULL;
+ }
+
for (i = 0; i < 2 * MAX_FX_SHADERS; ++i) {
if (GG.shaders.fx_shaders[i]) {
GPU_shader_free(GG.shaders.fx_shaders[i]);
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index 67da8201f66..4416b6494f9 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -2822,7 +2822,7 @@ void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 c
}
#ifdef BIT_OPERATIONS
-vec2 calc_brick_texture(vec3 p, float mortar_size, float bias,
+vec2 calc_brick_texture(vec3 p, float mortar_size, float mortar_smooth, float bias,
float brick_width, float row_height,
float offset_amount, int offset_frequency,
float squash_amount, int squash_frequency)
@@ -2843,17 +2843,26 @@ vec2 calc_brick_texture(vec3 p, float mortar_size, float bias,
x = (p.x + offset) - brick_width * bricknum;
y = p.y - row_height * rownum;
- return vec2(clamp((integer_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias), 0.0, 1.0),
- (x < mortar_size || y < mortar_size ||
- x > (brick_width - mortar_size) ||
- y > (row_height - mortar_size)) ? 1.0 : 0.0);
+ float tint = clamp((integer_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias), 0.0, 1.0);
+
+ float min_dist = min(min(x, y), min(brick_width - x, row_height - y));
+ if(min_dist >= mortar_size) {
+ return vec2(tint, 0.0);
+ }
+ else if(mortar_smooth == 0.0) {
+ return vec2(tint, 1.0);
+ }
+ else {
+ min_dist = 1.0 - min_dist/mortar_size;
+ return vec2(tint, smoothstep(0.0, mortar_smooth, min_dist));
+ }
}
#endif
void node_tex_brick(vec3 co,
vec4 color1, vec4 color2,
vec4 mortar, float scale,
- float mortar_size, float bias,
+ float mortar_size, float mortar_smooth, float bias,
float brick_width, float row_height,
float offset_amount, float offset_frequency,
float squash_amount, float squash_frequency,
@@ -2861,7 +2870,7 @@ void node_tex_brick(vec3 co,
{
#ifdef BIT_OPERATIONS
vec2 f2 = calc_brick_texture(co * scale,
- mortar_size, bias,
+ mortar_size, mortar_smooth, bias,
brick_width, row_height,
offset_amount, int(offset_frequency),
squash_amount, int(squash_frequency));
@@ -2871,7 +2880,7 @@ void node_tex_brick(vec3 co,
float facm = 1.0 - tint;
color1 = facm * color1 + tint * color2;
}
- color = (f == 1.0) ? mortar : color1;
+ color = mix(color1, mortar, f);
fac = f;
#else
color = vec4(1.0);
diff --git a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl
index fd790009e02..6ded453225e 100644
--- a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl
@@ -8,10 +8,17 @@ uniform float density_scale;
uniform sampler3D soot_texture;
uniform sampler3D shadow_texture;
+#ifdef USE_COBA
+uniform sampler1D transfer_texture;
+uniform sampler3D color_band_texture;
+#endif
+
void main()
{
/* compute color and density from volume texture */
vec4 soot = texture3D(soot_texture, coords);
+
+#ifndef USE_COBA
vec3 soot_color;
if (soot.a != 0) {
soot_color = active_color * soot.rgb / soot.a;
@@ -31,6 +38,11 @@ void main()
/* premultiply alpha */
vec4 color = vec4(soot_alpha * soot_color, soot_alpha);
+#else
+ float color_band = texture3D(color_band_texture, coords).r;
+ vec4 transfer_function = texture1D(transfer_texture, color_band);
+ vec4 color = transfer_function * density_scale;
+#endif
gl_FragColor = color;
}
diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp
index b8ed780397f..d58340965a7 100644
--- a/source/blender/ikplugin/intern/itasc_plugin.cpp
+++ b/source/blender/ikplugin/intern/itasc_plugin.cpp
@@ -1763,20 +1763,15 @@ void itasc_initialize_tree(struct Scene *scene, Object *ob, float ctime)
}
// if at least one tree, create the scenes from the PoseTree stored in the channels
// postpone until execute_tree: this way the pose constraint are included
- //if (count)
- // create_scene(scene, ob, ctime);
- //itasc_update_param(ob->pose);
+ if (count)
+ create_scene(scene, ob, ctime);
+ itasc_update_param(ob->pose);
// make sure we don't rebuilt until the user changes something important
ob->pose->flag &= ~POSE_WAS_REBUILT;
}
void itasc_execute_tree(struct Scene *scene, Object *ob, bPoseChannel *pchan_root, float ctime)
{
- if (!ob->pose->ikdata) {
- // IK tree not yet created, no it now
- create_scene(scene, ob, ctime);
- itasc_update_param(ob->pose);
- }
if (ob->pose->ikdata) {
IK_Data *ikdata = (IK_Data *)ob->pose->ikdata;
bItasc *ikparam = (bItasc *) ob->pose->ikparam;
diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h
index 5d76ffe57b5..381ee5d40e5 100644
--- a/source/blender/makesdna/DNA_rigidbody_types.h
+++ b/source/blender/makesdna/DNA_rigidbody_types.h
@@ -226,10 +226,16 @@ typedef struct RigidBodyCon {
float spring_stiffness_x;
float spring_stiffness_y;
float spring_stiffness_z;
+ float spring_stiffness_ang_x;
+ float spring_stiffness_ang_y;
+ float spring_stiffness_ang_z;
/* amount of velocity lost over time */
float spring_damping_x;
float spring_damping_y;
float spring_damping_z;
+ float spring_damping_ang_x;
+ float spring_damping_ang_y;
+ float spring_damping_ang_z;
/* motor settings */
float motor_lin_target_velocity; /* linear velocity the motor tries to hold */
@@ -295,7 +301,11 @@ typedef enum eRigidBodyCon_Flag {
RBC_FLAG_USE_SPRING_Z = (1 << 13),
/* motors */
RBC_FLAG_USE_MOTOR_LIN = (1 << 14),
- RBC_FLAG_USE_MOTOR_ANG = (1 << 15)
+ RBC_FLAG_USE_MOTOR_ANG = (1 << 15),
+ /* angular springs */
+ RBC_FLAG_USE_SPRING_ANG_X = (1 << 16),
+ RBC_FLAG_USE_SPRING_ANG_Y = (1 << 17),
+ RBC_FLAG_USE_SPRING_ANG_Z = (1 << 18)
} eRigidBodyCon_Flag;
/* ******************************** */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 9b0781ebe70..f5e71ae59a9 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1103,7 +1103,7 @@ typedef struct Sculpt {
float gravity_factor;
/* scale for constant detail size */
- float constant_detail;
+ float constant_detail; /* Constant detail resolution (Blender unit / constant_detail) */
float detail_percent;
float pad;
diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h
index 8f197c8c42c..fb81e2b6402 100644
--- a/source/blender/makesdna/DNA_smoke_types.h
+++ b/source/blender/makesdna/DNA_smoke_types.h
@@ -77,6 +77,23 @@ enum {
VECTOR_DRAW_STREAMLINE = 1,
};
+enum {
+ FLUID_FIELD_DENSITY = 0,
+ FLUID_FIELD_HEAT = 1,
+ FLUID_FIELD_FUEL = 2,
+ FLUID_FIELD_REACT = 3,
+ FLUID_FIELD_FLAME = 4,
+ FLUID_FIELD_VELOCITY_X = 5,
+ FLUID_FIELD_VELOCITY_Y = 6,
+ FLUID_FIELD_VELOCITY_Z = 7,
+ FLUID_FIELD_COLOR_R = 8,
+ FLUID_FIELD_COLOR_G = 9,
+ FLUID_FIELD_COLOR_B = 10,
+ FLUID_FIELD_FORCE_X = 11,
+ FLUID_FIELD_FORCE_Y = 12,
+ FLUID_FIELD_FORCE_Z = 13,
+};
+
/* cache compression */
#define SM_CACHE_LIGHT 0
#define SM_CACHE_HEAVY 1
@@ -193,9 +210,13 @@ typedef struct SmokeDomainSettings {
float slice_per_voxel;
float slice_depth;
float display_thickness;
+
+ struct ColorBand *coba;
float vector_scale;
char vector_draw_type;
- char pad2[3];
+ char use_coba;
+ char coba_field; /* simulation field used for the color mapping */
+ char pad2;
char cache_filename[1024];
} SmokeDomainSettings;
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 280ad4aa9b1..5174c957834 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -35,6 +35,7 @@
#include "BLI_utildefines.h"
#include "BKE_icons.h"
+#include "BKE_object.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -347,6 +348,20 @@ static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id)
}
}
+static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, int clear_proxy)
+{
+ /* Special case, as we can't rely on id_make_local(); it clears proxies. */
+ if (!clear_proxy && GS(self->name) == ID_OB) {
+ BKE_object_make_local_ex(bmain, (Object *)self, false, clear_proxy);
+ }
+ else {
+ id_make_local(bmain, self, false, false);
+ }
+
+ return self->newid ? self->newid : self;
+}
+
+
static AnimData * rna_ID_animation_data_create(ID *id, Main *bmain)
{
AnimData *adt = BKE_animdata_add_id(id);
@@ -999,6 +1014,17 @@ static void rna_def_ID(BlenderRNA *brna)
parm = RNA_def_pointer(func, "new_id", "ID", "", "New ID to use");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ func = RNA_def_function(srna, "make_local", "rna_ID_make_local");
+ RNA_def_function_ui_description(func, "Make this datablock local, return local one "
+ "(may be a copy of the original, in case it is also indirectly used)");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
+ RNA_def_boolean(func, "clear_proxy", true, "",
+ "Whether to clear proxies (the default behavior); can cause proxies to be duplicated"
+ " when still referred to from another library");
+ RNA_def_property_flag(parm, PROP_PYFUNC_OPTIONAL);
+ parm = RNA_def_pointer(func, "id", "ID", "", "This ID, or the new ID if it was copied");
+ RNA_def_function_return(func, parm);
+
func = RNA_def_function(srna, "user_of_id", "BKE_library_ID_use_ID");
RNA_def_function_ui_description(func, "Count the number of times that ID uses/references given one");
parm = RNA_def_pointer(func, "id", "ID", "", "ID to count usages");
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index cc290eab59d..594c1752328 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -585,7 +585,7 @@ void RNA_def_main_cameras(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "camera", "Camera", "", "Camera to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this camera before deleting it "
"(WARNING: will also delete objects instancing that camera data)");
@@ -624,7 +624,7 @@ void RNA_def_main_scenes(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this scene before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this scene before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_scenes_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -665,7 +665,7 @@ void RNA_def_main_objects(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "object", "Object", "", "Object to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this object before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this object before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_objects_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -702,7 +702,7 @@ void RNA_def_main_materials(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "material", "Material", "", "Material to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this material before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this material before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_materials_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -746,7 +746,7 @@ void RNA_def_main_node_groups(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "tree", "NodeTree", "", "Node tree to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this node tree before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this node tree before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_node_groups_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -805,7 +805,7 @@ void RNA_def_main_meshes(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this mesh before deleting it "
"(WARNING: will also delete objects instancing that mesh data)");
@@ -845,7 +845,7 @@ void RNA_def_main_lamps(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "lamp", "Lamp", "", "Lamp to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this lamp before deleting it "
"(WARNING: will also delete objects instancing that lamp data)");
@@ -963,7 +963,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "image", "Image", "", "Image to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this image before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this image before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_images_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1000,7 +1000,7 @@ void RNA_def_main_lattices(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "lattice", "Lattice", "", "Lattice to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this lattice before deleting it "
"(WARNING: will also delete objects instancing that lattice data)");
@@ -1040,7 +1040,7 @@ void RNA_def_main_curves(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "curve", "Curve", "", "Curve to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this curve before deleting it "
"(WARNING: will also delete objects instancing that curve data)");
@@ -1078,7 +1078,7 @@ void RNA_def_main_metaballs(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "metaball", "MetaBall", "", "Metaball to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this metaball before deleting it "
"(WARNING: will also delete objects instancing that metaball data)");
@@ -1118,7 +1118,7 @@ void RNA_def_main_fonts(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "vfont", "VectorFont", "", "Font to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this font before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this font before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_fonts_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1156,7 +1156,7 @@ void RNA_def_main_textures(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "texture", "Texture", "", "Texture to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this texture before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this texture before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_textures_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1193,7 +1193,7 @@ void RNA_def_main_brushes(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "brush", "Brush", "", "Brush to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this brush before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this brush before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_brushes_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1230,7 +1230,7 @@ void RNA_def_main_worlds(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "world", "World", "", "World to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this world before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this world before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_worlds_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1267,7 +1267,7 @@ void RNA_def_main_groups(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "group", "Group", "", "Group to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this group before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this group before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_groups_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1304,7 +1304,7 @@ void RNA_def_main_speakers(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "speaker", "Speaker", "", "Speaker to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this speaker before deleting it "
"(WARNING: will also delete objects instancing that speaker data)");
@@ -1343,7 +1343,7 @@ void RNA_def_main_texts(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "text", "Text", "", "Text to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this text before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this text before deleting it");
/* load func */
func = RNA_def_function(srna, "load", "rna_Main_texts_load");
@@ -1393,7 +1393,7 @@ void RNA_def_main_sounds(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "sound", "Sound", "", "Sound to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this sound before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this sound before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_sounds_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1430,7 +1430,7 @@ void RNA_def_main_armatures(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "armature", "Armature", "", "Armature to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "",
+ RNA_def_boolean(func, "do_unlink", true, "",
"Unlink all usages of this armature before deleting it "
"(WARNING: will also delete objects instancing that armature data)");
@@ -1468,7 +1468,7 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "action", "Action", "", "Action to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this action before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this action before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_actions_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1504,7 +1504,7 @@ void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "particle", "ParticleSettings", "", "Particle Settings to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of those particle settings before deleting them");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of those particle settings before deleting them");
func = RNA_def_function(srna, "tag", "rna_Main_particles_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1540,7 +1540,7 @@ void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "palette", "Palette", "", "Palette to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this palette before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this palette before deleting it");
func = RNA_def_function(srna, "tag", "rna_Main_palettes_tag");
parm = RNA_def_boolean(func, "value", 0, "Value", "");
@@ -1610,7 +1610,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "grease_pencil", "GreasePencil", "", "Grease Pencil to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this grease pencil before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this grease pencil before deleting it");
prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -1639,7 +1639,7 @@ void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "clip", "MovieClip", "", "Movie clip to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this movie clip before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this movie clip before deleting it");
/* load func */
func = RNA_def_function(srna, "load", "rna_Main_movieclip_load");
@@ -1691,7 +1691,7 @@ void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "mask", "Mask", "", "Mask to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this mask before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this mask before deleting it");
prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -1728,7 +1728,7 @@ void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_pointer(func, "linestyle", "FreestyleLineStyle", "", "Line style to remove");
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
- RNA_def_boolean(func, "do_unlink", false, "", "Unlink all usages of this line style before deleting it");
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of this line style before deleting it");
prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c
index bdf001ed0e1..85a34a94746 100644
--- a/source/blender/makesrna/intern/rna_rigidbody.c
+++ b/source/blender/makesrna/intern/rna_rigidbody.c
@@ -507,6 +507,45 @@ static void rna_RigidBodyCon_spring_stiffness_z_set(PointerRNA *ptr, float value
#endif
}
+static void rna_RigidBodyCon_spring_stiffness_ang_x_set(PointerRNA *ptr, float value)
+{
+ RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
+
+ rbc->spring_stiffness_ang_x = value;
+
+#ifdef WITH_BULLET
+ if (rbc->physics_constraint && rbc->type == RBC_TYPE_6DOF_SPRING && (rbc->flag & RBC_FLAG_USE_SPRING_ANG_X)) {
+ RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, value);
+ }
+#endif
+}
+
+static void rna_RigidBodyCon_spring_stiffness_ang_y_set(PointerRNA *ptr, float value)
+{
+ RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
+
+ rbc->spring_stiffness_ang_y = value;
+
+#ifdef WITH_BULLET
+ if (rbc->physics_constraint && rbc->type == RBC_TYPE_6DOF_SPRING && (rbc->flag & RBC_FLAG_USE_SPRING_ANG_Y)) {
+ RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, value);
+ }
+#endif
+}
+
+static void rna_RigidBodyCon_spring_stiffness_ang_z_set(PointerRNA *ptr, float value)
+{
+ RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
+
+ rbc->spring_stiffness_ang_z = value;
+
+#ifdef WITH_BULLET
+ if (rbc->physics_constraint && rbc->type == RBC_TYPE_6DOF_SPRING && (rbc->flag & RBC_FLAG_USE_SPRING_ANG_Z)) {
+ RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, value);
+ }
+#endif
+}
+
static void rna_RigidBodyCon_spring_damping_x_set(PointerRNA *ptr, float value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
@@ -544,6 +583,43 @@ static void rna_RigidBodyCon_spring_damping_z_set(PointerRNA *ptr, float value)
#endif
}
+static void rna_RigidBodyCon_spring_damping_ang_x_set(PointerRNA *ptr, float value)
+{
+ RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
+
+ rbc->spring_damping_ang_x = value;
+
+#ifdef WITH_BULLET
+ if (rbc->physics_constraint && rbc->type == RBC_TYPE_6DOF_SPRING && (rbc->flag & RBC_FLAG_USE_SPRING_ANG_X)) {
+ RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_X, value);
+ }
+#endif
+}
+
+static void rna_RigidBodyCon_spring_damping_ang_y_set(PointerRNA *ptr, float value)
+{
+ RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
+
+ rbc->spring_damping_ang_y = value;
+#ifdef WITH_BULLET
+ if (rbc->physics_constraint && rbc->type == RBC_TYPE_6DOF_SPRING && (rbc->flag & RBC_FLAG_USE_SPRING_ANG_Y)) {
+ RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Y, value);
+ }
+#endif
+}
+
+static void rna_RigidBodyCon_spring_damping_ang_z_set(PointerRNA *ptr, float value)
+{
+ RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
+
+ rbc->spring_damping_ang_z = value;
+#ifdef WITH_BULLET
+ if (rbc->physics_constraint && rbc->type == RBC_TYPE_6DOF_SPRING && (rbc->flag & RBC_FLAG_USE_SPRING_ANG_Z)) {
+ RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_ANG_Z, value);
+ }
+#endif
+}
+
static void rna_RigidBodyCon_motor_lin_max_impulse_set(PointerRNA *ptr, float value)
{
RigidBodyCon *rbc = (RigidBodyCon *)ptr->data;
@@ -1061,6 +1137,21 @@ static void rna_def_rigidbody_constraint(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Z Spring", "Enable spring on Z axis");
RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+ prop = RNA_def_property(srna, "use_spring_ang_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_SPRING_ANG_X);
+ RNA_def_property_ui_text(prop, "X Angle Spring", "Enable spring on X rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
+ prop = RNA_def_property(srna, "use_spring_ang_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_SPRING_ANG_Y);
+ RNA_def_property_ui_text(prop, "Y Angle Spring", "Enable spring on Y rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
+ prop = RNA_def_property(srna, "use_spring_ang_z", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_SPRING_ANG_Z);
+ RNA_def_property_ui_text(prop, "Z Angle Spring", "Enable spring on Z rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
prop = RNA_def_property(srna, "use_motor_lin", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_MOTOR_LIN);
RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_use_motor_lin_set");
@@ -1178,6 +1269,33 @@ static void rna_def_rigidbody_constraint(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Z Axis Stiffness", "Stiffness on the Z axis");
RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+ prop = RNA_def_property(srna, "spring_stiffness_ang_x", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "spring_stiffness_ang_x");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, 3);
+ RNA_def_property_float_default(prop, 10.0f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_spring_stiffness_ang_x_set", NULL);
+ RNA_def_property_ui_text(prop, "X Angle Stiffness", "Stiffness on the X rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
+ prop = RNA_def_property(srna, "spring_stiffness_ang_y", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "spring_stiffness_ang_y");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, 3);
+ RNA_def_property_float_default(prop, 10.0f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_spring_stiffness_ang_y_set", NULL);
+ RNA_def_property_ui_text(prop, "Y Angle Stiffness", "Stiffness on the Y rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
+ prop = RNA_def_property(srna, "spring_stiffness_ang_z", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "spring_stiffness_ang_z");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, 3);
+ RNA_def_property_float_default(prop, 10.0f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_spring_stiffness_ang_z_set", NULL);
+ RNA_def_property_ui_text(prop, "Z Angle Stiffness", "Stiffness on the Z rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
prop = RNA_def_property(srna, "spring_damping_x", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "spring_damping_x");
RNA_def_property_range(prop, 0.0f, 1.0f);
@@ -1202,6 +1320,30 @@ static void rna_def_rigidbody_constraint(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Damping Z", "Damping on the Z axis");
RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+ prop = RNA_def_property(srna, "spring_damping_ang_x", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "spring_damping_ang_x");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_float_default(prop, 0.5f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_spring_damping_ang_x_set", NULL);
+ RNA_def_property_ui_text(prop, "Damping X Angle", "Damping on the X rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
+ prop = RNA_def_property(srna, "spring_damping_ang_y", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "spring_damping_ang_y");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_float_default(prop, 0.5f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_spring_damping_ang_y_set", NULL);
+ RNA_def_property_ui_text(prop, "Damping Y Angle", "Damping on the Y rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
+ prop = RNA_def_property(srna, "spring_damping_ang_z", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "spring_damping_ang_z");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_float_default(prop, 0.5f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_RigidBodyCon_spring_damping_ang_z_set", NULL);
+ RNA_def_property_ui_text(prop, "Damping Z Angle", "Damping on the Z rotational axis");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset");
+
prop = RNA_def_property(srna, "motor_lin_target_velocity", PROP_FLOAT, PROP_UNIT_VELOCITY);
RNA_def_property_float_sdna(prop, NULL, "motor_lin_target_velocity");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index e169ef0d822..7f405f0fb1f 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -600,10 +600,12 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Detail Percentage", "Maximum edge length for dynamic topology sculpting (in brush percenage)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
- prop = RNA_def_property(srna, "constant_detail", PROP_FLOAT, PROP_PERCENTAGE);
- RNA_def_property_range(prop, 0.001, 10000.0);
- RNA_def_property_ui_range(prop, 0.1, 100.0, 10, 2);
- RNA_def_property_ui_text(prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (as percentage of blender unit)");
+ prop = RNA_def_property(srna, "constant_detail_resolution", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "constant_detail");
+ RNA_def_property_range(prop, 0.0001, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.001, 1000.0, 10, 2);
+ RNA_def_property_ui_text(prop, "Resolution", "Maximum edge length for dynamic topology sculpting (as divisor "
+ "of blender unit - higher value means smaller edge length)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_smooth_shading", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index ce18c86b8d0..15950e67671 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -30,6 +30,7 @@
#include <limits.h>
#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "rna_internal.h"
@@ -53,6 +54,7 @@
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_particle.h"
+#include "BKE_texture.h"
#include "smoke_API.h"
@@ -383,13 +385,24 @@ static void rna_SmokeFlow_uvlayer_set(PointerRNA *ptr, const char *value)
rna_object_uvlayer_name_set(ptr, value, flow->uvlayer_name, sizeof(flow->uvlayer_name));
}
+static void rna_Smoke_use_color_ramp_set(PointerRNA *ptr, int value)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+
+ sds->use_coba = value;
+
+ if (value && sds->coba == NULL) {
+ sds->coba = add_colorband(false);
+ }
+}
+
static void rna_SmokeModifier_cache_filename_get(PointerRNA *ptr, char *filename)
{
- SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
- PTCacheID pid;
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ PTCacheID pid;
- BKE_ptcache_id_from_smoke(&pid, ptr->id.data, sds->smd);
- ptcache_filename(&pid, filename, sds->smd->time, 1, 1);
+ BKE_ptcache_id_from_smoke(&pid, ptr->id.data, sds->smd);
+ ptcache_filename(&pid, filename, sds->smd->time, 1, 1);
}
#else
@@ -815,6 +828,41 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Scale", "Multiplier for scaling the vectors");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+ /* --------- Color mapping. --------- */
+
+ prop = RNA_def_property(srna, "use_color_ramp", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_coba", 0);
+ RNA_def_property_boolean_funcs(prop, NULL, "rna_Smoke_use_color_ramp_set");
+ RNA_def_property_ui_text(prop, "Use Color Ramp",
+ "Render a simulation field while mapping its voxels values to the colors of a ramp");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ static EnumPropertyItem coba_field_items[] = {
+ {FLUID_FIELD_COLOR_R, "COLOR_R", 0, "Red", "Red component of the color field"},
+ {FLUID_FIELD_COLOR_G, "COLOR_G", 0, "Green", "Green component of the color field"},
+ {FLUID_FIELD_COLOR_B, "COLOR_B", 0, "Blue", "Blue component of the color field"},
+ {FLUID_FIELD_DENSITY, "DENSITY", 0, "Density", "Quantity of soot in the fluid"},
+ {FLUID_FIELD_FLAME, "FLAME", 0, "Flame", "Flame field"},
+ {FLUID_FIELD_FUEL, "FUEL", 0, "Fuel", "Fuel field"},
+ {FLUID_FIELD_HEAT, "HEAT", 0, "Heat", "Temperature of the fluid"},
+ {FLUID_FIELD_VELOCITY_X, "VELOCITY_X", 0, "X Velocity", "X component of the velocity field"},
+ {FLUID_FIELD_VELOCITY_Y, "VELOCITY_Y", 0, "Y Velocity", "Y component of the velocity field"},
+ {FLUID_FIELD_VELOCITY_Z, "VELOCITY_Z", 0, "Z Velocity", "Z component of the velocity field"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ prop = RNA_def_property(srna, "coba_field", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "coba_field");
+ RNA_def_property_enum_items(prop, coba_field_items);
+ RNA_def_property_ui_text(prop, "Field", "Simulation field to color map");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "coba");
+ RNA_def_property_struct_type(prop, "ColorRamp");
+ RNA_def_property_ui_text(prop, "Color Ramp", "");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
prop = RNA_def_property(srna, "cache_filename", PROP_STRING, PROP_NONE);
RNA_def_property_string_maxlength(prop, 1024);
RNA_def_property_string_funcs(prop, "rna_SmokeModifier_cache_filename_get", NULL, NULL);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index ffcf12edb2d..b262e6412e3 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -1980,7 +1980,7 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
static EnumPropertyItem other_uv_filter_items[] = {
{SI_FILTER_ALL, "ALL", 0, "All", "No filter, show all islands from other objects"},
{SI_FILTER_SAME_IMAGE, "SAME_IMAGE", ICON_IMAGE_DATA, "Same Image",
- "Only show others' UV islads who's active image matches image of the active face"},
+ "Only show others' UV islands whose active image matches image of the active face"},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 8ad016007f4..10807c32b91 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -52,15 +52,6 @@
#include "BLT_lang.h"
#include "GPU_buffers.h"
-#ifdef WITH_CYCLES
-static EnumPropertyItem compute_device_type_items[] = {
- {USER_COMPUTE_DEVICE_NONE, "NONE", 0, "None", "Don't use compute device"},
- {USER_COMPUTE_DEVICE_CUDA, "CUDA", 0, "CUDA", "Use CUDA for GPU acceleration"},
- {USER_COMPUTE_DEVICE_OPENCL, "OPENCL", 0, "OpenCL", "Use OpenCL for GPU acceleration"},
- { 0, NULL, 0, NULL, NULL}
-};
-#endif
-
#ifdef WITH_OPENSUBDIV
static EnumPropertyItem opensubdiv_compute_type_items[] = {
{USER_OPENSUBDIV_COMPUTE_NONE, "NONE", 0, "None", ""},
@@ -124,8 +115,6 @@ static EnumPropertyItem rna_enum_language_default_items[] = {
#include "UI_interface.h"
-#include "CCL_api.h"
-
#ifdef WITH_OPENSUBDIV
# include "opensubdiv_capi.h"
#endif
@@ -476,78 +465,6 @@ static PointerRNA rna_Theme_space_list_generic_get(PointerRNA *ptr)
}
-#ifdef WITH_CYCLES
-static EnumPropertyItem *rna_userdef_compute_device_type_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop), bool *r_free)
-{
- EnumPropertyItem *item = NULL;
- int totitem = 0;
-
- /* add supported device types */
- RNA_enum_items_add_value(&item, &totitem, compute_device_type_items, USER_COMPUTE_DEVICE_NONE);
- if (CCL_compute_device_list(0))
- RNA_enum_items_add_value(&item, &totitem, compute_device_type_items, USER_COMPUTE_DEVICE_CUDA);
- if (CCL_compute_device_list(1))
- RNA_enum_items_add_value(&item, &totitem, compute_device_type_items, USER_COMPUTE_DEVICE_OPENCL);
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- return item;
-}
-
-static int rna_userdef_compute_device_get(PointerRNA *UNUSED(ptr))
-{
- if (U.compute_device_type == USER_COMPUTE_DEVICE_NONE)
- return 0;
-
- return U.compute_device_id;
-}
-
-static EnumPropertyItem *rna_userdef_compute_device_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop), bool *r_free)
-{
- EnumPropertyItem tmp = {0, "", 0, "", ""};
- EnumPropertyItem *item = NULL;
- int totitem = 0;
-
- if (U.compute_device_type == USER_COMPUTE_DEVICE_NONE) {
- /* only add a single CPU device */
- tmp.value = 0;
- tmp.name = "CPU";
- tmp.identifier = "CPU";
- RNA_enum_item_add(&item, &totitem, &tmp);
- }
- else {
- /* get device list from cycles. it would be good to make this generic
- * once we have more subsystems using opencl, for now this is easiest */
- int opencl = (U.compute_device_type == USER_COMPUTE_DEVICE_OPENCL);
- CCLDeviceInfo *devices = CCL_compute_device_list(opencl);
- int a;
-
- if (devices) {
- for (a = 0; devices[a].identifier[0]; a++) {
- tmp.value = devices[a].value;
- tmp.identifier = devices[a].identifier;
- tmp.name = devices[a].name;
- RNA_enum_item_add(&item, &totitem, &tmp);
- }
- }
- else {
- tmp.value = 0;
- tmp.name = "CPU";
- tmp.identifier = "CPU";
- RNA_enum_item_add(&item, &totitem, &tmp);
- }
- }
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- return item;
-}
-#endif
-
#ifdef WITH_OPENSUBDIV
static EnumPropertyItem *rna_userdef_opensubdiv_compute_type_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop), bool *r_free)
@@ -3977,13 +3894,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
-#ifdef WITH_CYCLES
- static EnumPropertyItem compute_device_items[] = {
- {0, "CPU", 0, "CPU", ""},
- { 0, NULL, 0, NULL, NULL}
- };
-#endif
-
static EnumPropertyItem image_draw_methods[] = {
{IMAGE_DRAW_METHOD_2DTEXTURE, "2DTEXTURE", 0, "2D Texture", "Use CPU for display transform and draw image with 2D texture"},
{IMAGE_DRAW_METHOD_GLSL, "GLSL", 0, "GLSL", "Use GLSL shaders for display transform and draw image with 2D texture"},
@@ -4275,23 +4185,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
"Draw tool/property regions over the main region, when using Triple Buffer");
RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-#ifdef WITH_CYCLES
- prop = RNA_def_property(srna, "compute_device_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
- RNA_def_property_enum_sdna(prop, NULL, "compute_device_type");
- RNA_def_property_enum_items(prop, compute_device_type_items);
- RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_userdef_compute_device_type_itemf");
- RNA_def_property_ui_text(prop, "Compute Device Type", "Device to use for computation (rendering with Cycles)");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_PROPERTIES, NULL);
-
- prop = RNA_def_property(srna, "compute_device", PROP_ENUM, PROP_NONE);
- RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
- RNA_def_property_enum_sdna(prop, NULL, "compute_device_id");
- RNA_def_property_enum_items(prop, compute_device_items);
- RNA_def_property_enum_funcs(prop, "rna_userdef_compute_device_get", NULL, "rna_userdef_compute_device_itemf");
- RNA_def_property_ui_text(prop, "Compute Device", "Device to use for computation");
-#endif
-
#ifdef WITH_OPENSUBDIV
prop = RNA_def_property(srna, "opensubdiv_compute_type", PROP_ENUM, PROP_NONE);
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 90081a93188..35c9c9bcc89 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -419,6 +419,7 @@ static EnumPropertyItem keymap_modifiers_items[] = {
static EnumPropertyItem operator_flag_items[] = {
{OPTYPE_REGISTER, "REGISTER", 0, "Register", "Display in the info window and support the redo toolbar panel"},
{OPTYPE_UNDO, "UNDO", 0, "Undo", "Push an undo event (needed for operator redo)"},
+ {OPTYPE_UNDO_GROUPED, "UNDO_GROUPED", 0, "Grouped Undo", "Push a single undo event for repetead instances of this operator"},
{OPTYPE_BLOCKING, "BLOCKING", 0, "Blocking", "Block anything else from using the cursor"},
{OPTYPE_MACRO, "MACRO", 0, "Macro", "Use to check if an operator is a macro"},
{OPTYPE_GRAB_CURSOR, "GRAB_CURSOR", 0, "Grab Pointer",
@@ -1139,6 +1140,7 @@ static char _operator_idname[OP_MAX_TYPENAME];
static char _operator_name[OP_MAX_TYPENAME];
static char _operator_descr[RNA_DYN_DESCR_MAX];
static char _operator_ctxt[RNA_DYN_DESCR_MAX];
+static char _operator_undo_group[OP_MAX_TYPENAME];
static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *data, const char *identifier,
StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free)
{
@@ -1153,10 +1155,11 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *
dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */
dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */
dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */
+ dummyot.undo_group = _operator_undo_group; /* only assigne the pointer, string is NULL'd */
RNA_pointer_create(NULL, &RNA_Operator, &dummyop, &dummyotr);
/* clear in case they are left unset */
- _operator_idname[0] = _operator_name[0] = _operator_descr[0] = '\0';
+ _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_undo_group[0] = '\0';
/* We have to set default op context! */
strcpy(_operator_ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
@@ -1210,9 +1213,10 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *
int namelen = strlen(_operator_name) + 1;
int desclen = strlen(_operator_descr) + 1;
int ctxtlen = strlen(_operator_ctxt) + 1;
+ int ugrouplen = strlen(_operator_undo_group) + 1;
char *ch;
/* 2 terminators and 3 to convert a.b -> A_OT_b */
- ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen), "_operator_idname");
+ ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen + ugrouplen), "_operator_idname");
WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */
dummyot.idname = ch;
ch += idlen;
@@ -1224,6 +1228,9 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *
ch += desclen;
strcpy(ch, _operator_ctxt);
dummyot.translation_context = ch;
+ ch += ctxtlen;
+ strcpy(ch, _operator_undo_group);
+ dummyot.undo_group = ch;
}
}
@@ -1280,10 +1287,11 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v
dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */
dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */
dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */
+ dummyot.undo_group = _operator_undo_group; /* only assigne the pointer, string is NULL'd */
RNA_pointer_create(NULL, &RNA_Macro, &dummyop, &dummyotr);
/* clear in case they are left unset */
- _operator_idname[0] = _operator_name[0] = _operator_descr[0] = '\0';
+ _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_undo_group[0] = '\0';
/* We have to set default op context! */
strcpy(_operator_ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
@@ -1297,9 +1305,10 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v
int namelen = strlen(_operator_name) + 1;
int desclen = strlen(_operator_descr) + 1;
int ctxtlen = strlen(_operator_ctxt) + 1;
+ int ugrouplen = strlen(_operator_undo_group) + 1;
char *ch;
/* 2 terminators and 3 to convert a.b -> A_OT_b */
- ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen), "_operator_idname");
+ ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen + ugrouplen), "_operator_idname");
WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */
dummyot.idname = ch;
ch += idlen;
@@ -1311,6 +1320,9 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v
ch += desclen;
strcpy(ch, _operator_ctxt);
dummyot.translation_context = ch;
+ ch += ctxtlen;
+ strcpy(ch, _operator_undo_group);
+ dummyot.undo_group = ch;
}
if (strlen(identifier) >= sizeof(dummyop.idname)) {
@@ -1401,6 +1413,16 @@ static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value)
assert(!"setting the bl_description on a non-builtin operator");
}
+static void rna_Operator_bl_undo_group_set(PointerRNA *ptr, const char *value)
+{
+ wmOperator *data = (wmOperator *)(ptr->data);
+ char *str = (char *)data->type->undo_group;
+ if (!str[0])
+ BLI_strncpy(str, value, OP_MAX_TYPENAME); /* utf8 already ensured */
+ else
+ assert(!"setting the bl_undo_group on a non-builtin operator");
+}
+
static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
wmKeyMapItem *kmi = ptr->data;
@@ -1509,6 +1531,14 @@ static void rna_def_operator(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+ prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
+ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
+ RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
RNA_def_property_enum_items(prop, operator_flag_items);
@@ -1587,6 +1617,14 @@ static void rna_def_macro_operator(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+ prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
+ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
+ RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
RNA_def_property_enum_items(prop, operator_flag_items);
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index ecbc3891e8c..b049457e640 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -143,17 +143,17 @@ static void updateDepsgraph(ModifierData *md,
{
ArrayModifierData *amd = (ArrayModifierData *)md;
if (amd->start_cap != NULL) {
- DEG_add_object_relation(node, amd->start_cap, DEG_OB_COMP_TRANSFORM, "Hook Modifier Start Cap");
+ DEG_add_object_relation(node, amd->start_cap, DEG_OB_COMP_TRANSFORM, "Array Modifier Start Cap");
}
if (amd->end_cap != NULL) {
- DEG_add_object_relation(node, amd->end_cap, DEG_OB_COMP_TRANSFORM, "Hook Modifier End Cap");
+ DEG_add_object_relation(node, amd->end_cap, DEG_OB_COMP_TRANSFORM, "Array Modifier End Cap");
}
if (amd->curve_ob) {
- DEG_add_object_relation(node, amd->end_cap, DEG_OB_COMP_GEOMETRY, "Hook Modifier Curve");
+ DEG_add_object_relation(node, amd->curve_ob, DEG_OB_COMP_GEOMETRY, "Array Modifier Curve");
DEG_add_special_eval_flag(scene->depsgraph, &amd->curve_ob->id, DAG_EVAL_NEED_CURVE_PATH);
}
if (amd->offset_ob != NULL) {
- DEG_add_object_relation(node, amd->offset_ob, DEG_OB_COMP_TRANSFORM, "Hook Modifier Offset");
+ DEG_add_object_relation(node, amd->offset_ob, DEG_OB_COMP_TRANSFORM, "Array Modifier Offset");
}
}
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 83c4ca7984c..9186b10d8ca 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -145,12 +145,9 @@ static void updateDepsgraph(ModifierData *md,
HookModifierData *hmd = (HookModifierData *)md;
if (hmd->object != NULL) {
if (hmd->subtarget[0]) {
- DEG_add_bone_relation(node, hmd->object, hmd->subtarget, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
DEG_add_bone_relation(node, hmd->object, hmd->subtarget, DEG_OB_COMP_BONE, "Hook Modifier");
}
- else {
- DEG_add_object_relation(node, hmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
- }
+ DEG_add_object_relation(node, hmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
}
/* We need own transformation as well. */
DEG_add_object_relation(node, ob, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 1d951bae48b..b20c03bee28 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -2233,7 +2233,7 @@ static PyObject *bpy_bmfaceseq_new(BPy_BMElemSeq *self, PyObject *args)
}
/* check if the face exists */
- if (BM_face_exists(vert_array, vert_seq_len, NULL)) {
+ if (BM_face_exists(vert_array, vert_seq_len) != NULL) {
PyErr_SetString(PyExc_ValueError,
"faces.new(verts): face already exists");
goto cleanup;
@@ -2426,7 +2426,8 @@ static PyObject *bpy_bmfaceseq_get__method(BPy_BMElemSeq *self, PyObject *args)
return NULL;
}
- if (BM_face_exists(vert_array, vert_seq_len, &f)) {
+ f = BM_face_exists(vert_array, vert_seq_len);
+ if (f != NULL) {
ret = BPy_BMFace_CreatePyObject(bm, f);
}
else {
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index ec69abbb1df..15f3c665fcf 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -33,6 +33,7 @@
#include <stddef.h>
#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
#include "BLI_string.h"
#include "BLI_linklist.h"
#include "BLI_path_util.h"
@@ -405,6 +406,8 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
BLO_blendhandle_close(self->blo_handle);
self->blo_handle = NULL;
+ GHash *old_to_new_ids = BLI_ghash_ptr_new(__func__);
+
/* copied from wm_operator.c */
{
/* mark all library linked objects to be updated */
@@ -412,7 +415,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
/* append, rather than linking */
if ((self->flag & FILE_LINK) == 0) {
- BKE_library_make_local(bmain, lib, true, false);
+ BKE_library_make_local(bmain, lib, old_to_new_ids, true, false);
}
}
@@ -439,6 +442,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
ID *id;
id = PyCapsule_GetPointer(item, NULL);
+ id = BLI_ghash_lookup_default(old_to_new_ids, id, id);
Py_DECREF(item);
RNA_id_pointer_create(id, &id_ptr);
@@ -452,6 +456,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
}
#endif /* USE_RNA_DATABLOCKS */
+ BLI_ghash_free(old_to_new_ids, NULL, NULL);
Py_RETURN_NONE;
}
}
diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c
index 9dec2698720..3d6462e09a0 100644
--- a/source/blender/render/intern/source/shadeoutput.c
+++ b/source/blender/render/intern/source/shadeoutput.c
@@ -2064,11 +2064,13 @@ static float lamp_get_data_internal(ShadeInput *shi, GroupObject *go, float col[
if (lar->mode & LA_SHAD_TEX)
do_lamp_tex(lar, lv, shi, shadow, LA_SHAD_TEX);
- lamp_get_shadow(lar, shi, inp, shadfac, shi->depth);
+ if (R.r.mode & R_SHADOW) {
+ lamp_get_shadow(lar, shi, inp, shadfac, shi->depth);
- shadow[0] = 1.0f - ((1.0f - shadfac[0] * shadfac[3]) * (1.0f - shadow[0]));
- shadow[1] = 1.0f - ((1.0f - shadfac[1] * shadfac[3]) * (1.0f - shadow[1]));
- shadow[2] = 1.0f - ((1.0f - shadfac[2] * shadfac[3]) * (1.0f - shadow[2]));
+ shadow[0] = 1.0f - ((1.0f - shadfac[0] * shadfac[3]) * (1.0f - shadow[0]));
+ shadow[1] = 1.0f - ((1.0f - shadfac[1] * shadfac[3]) * (1.0f - shadow[1]));
+ shadow[2] = 1.0f - ((1.0f - shadfac[2] * shadfac[3]) * (1.0f - shadow[2]));
+ }
}
return visifac;
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 0fe3e8a0fcf..cd46e24264d 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -138,6 +138,7 @@ enum {
OPTYPE_INTERNAL = (1 << 6),
OPTYPE_LOCK_BYPASS = (1 << 7), /* Allow operator to run when interface is locked */
+ OPTYPE_UNDO_GROUPED = (1 << 8), /* Special type of undo which doesn't store itself multiple times */
};
/* context to call operator in for WM_operator_name_call */
@@ -522,6 +523,7 @@ typedef struct wmOperatorType {
const char *idname; /* unique identifier */
const char *translation_context;
const char *description; /* tooltips and python docs */
+ const char *undo_group; /* identifier to group operators together */
/* this callback executes the operator without any interactive input,
* parameters may be provided through operator properties. cannot use
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index dc34e8015c9..d2b0acd836b 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -727,10 +727,13 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat)
/* we don't want to do undo pushes for operators that are being
* called from operators that already do an undo push. usually
* this will happen for python operators that call C operators */
- if (wm->op_undo_depth == 0)
+ if (wm->op_undo_depth == 0) {
if (op->type->flag & OPTYPE_UNDO)
ED_undo_push_op(C, op);
-
+ else if (op->type->flag & OPTYPE_UNDO_GROUPED)
+ ED_undo_grouped_push_op(C, op);
+ }
+
if (repeat == 0) {
if (G.debug & G_DEBUG_WM) {
char *buf = WM_operator_pystring(C, op, false, true);
@@ -1854,9 +1857,12 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
wm->op_undo_depth--;
/* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
- if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
+ if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
if (handler->op->type->flag & OPTYPE_UNDO)
ED_undo_push_op(C, handler->op);
+ else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED)
+ ED_undo_grouped_push_op(C, handler->op);
+ }
if (handler->op->reports->list.first) {
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index e872ec1a524..3b733f9558c 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -419,7 +419,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive");
if (use_recursive) {
- BKE_library_make_local(bmain, NULL, true, set_fake);
+ BKE_library_make_local(bmain, NULL, NULL, true, set_fake);
}
else {
LinkNode *itemlink;
@@ -430,7 +430,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id;
if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) {
- BKE_library_make_local(bmain, new_id->lib, true, set_fake);
+ BKE_library_make_local(bmain, new_id->lib, NULL, true, set_fake);
BLI_gset_insert(done_libraries, new_id->lib);
}
}
diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c
index d8a4ddc8d4f..6040dfff644 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -142,7 +142,6 @@ struct wmWindowManager;
# pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
-#include "../../intern/cycles/blender/CCL_api.h"
#include "../../intern/dualcon/dualcon.h"
#include "../../intern/elbeem/extern/elbeem.h"
#include "../blender/blenkernel/BKE_modifier.h"
@@ -770,10 +769,6 @@ void *dualcon(const DualConInput *input_mesh,
float scale,
int depth) RET_ZERO
-/* intern/cycles */
-struct CCLDeviceInfo;
-struct CCLDeviceInfo *CCL_compute_device_list(int opencl) RET_NULL
-
/* compositor */
void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering,
const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index f65688e1304..10af0d5489e 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -1153,13 +1153,12 @@ if(WIN32 AND NOT WITH_PYTHON_MODULE)
COMPONENT Blender
DESTINATION "."
)
-
+ if(CMAKE_CL_64)
+ set(_WIN_PLATFORM x64)
+ else()
+ set(_WIN_PLATFORM x86)
+ endif()
if(MSVC12_REDIST_DIR)
- if(CMAKE_CL_64)
- set(_WIN_PLATFORM x64)
- else()
- set(_WIN_PLATFORM x86)
- endif()
install(
FILES
${MSVC12_REDIST_DIR}/${_WIN_PLATFORM}/Microsoft.VC120.CRT/msvcp120.dll
@@ -1173,4 +1172,51 @@ if(WIN32 AND NOT WITH_PYTHON_MODULE)
)
endif()
endif()
+
+ if(MSVC14_REDIST_DIR)
+ set(KITSDIRx86 "$ENV{${ProgramFilesX86_NAME}}/Windows Kits/10/")
+ set(KITSDIR "$ENV{ProgramFiles}/Windows Kits/10/")
+ if(IS_DIRECTORY ${KITSDIR})
+ set(KITSPATH "${KITSDIR}/Redist/ucrt/DLLs/${_WIN_PLATFORM}")
+ else()
+ if(IS_DIRECTORY ${KITSDIRx86})
+ set(KITSPATH "${KITSDIRx86}/Redist/ucrt/DLLs/${_WIN_PLATFORM}")
+ else()
+ message(FATAL_ERROR "Windows 10 SDK directory not found")
+ endif()
+ endif()
+
+ install(
+ FILES
+ ${KITSPATH}/api-ms-win-core-file-l1-2-0.dll
+ ${KITSPATH}/api-ms-win-core-file-l2-1-0.dll
+ ${KITSPATH}/api-ms-win-core-localization-l1-2-0.dll
+ ${KITSPATH}/api-ms-win-core-processthreads-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-core-processthreads-l1-1-1.dll
+ ${KITSPATH}/api-ms-win-core-synch-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-core-synch-l1-2-0.dll
+ ${KITSPATH}/api-ms-win-core-timezone-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-conio-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-convert-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-environment-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-filesystem-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-heap-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-locale-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-math-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-process-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-runtime-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-stdio-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-string-l1-1-0.dll
+ ${KITSPATH}/api-ms-win-crt-time-l1-1-0.dll
+ ${KITSPATH}/ucrtbase.dll
+ ${MSVC14_REDIST_DIR}/${_WIN_PLATFORM}/Microsoft.VC140.CRT/vcruntime140.dll
+ DESTINATION "."
+ )
+ if(WITH_OPENMP)
+ install(
+ FILES ${MSVC14_REDIST_DIR}/${_WIN_PLATFORM}/Microsoft.VC140.OpenMP/vcomp140.dll
+ DESTINATION "."
+ )
+ endif()
+ endif()
endif()
diff --git a/source/gameengine/VideoTexture/VideoDeckLink.cpp b/source/gameengine/VideoTexture/VideoDeckLink.cpp
index 4f5e34896fc..c588a4b33cf 100644
--- a/source/gameengine/VideoTexture/VideoDeckLink.cpp
+++ b/source/gameengine/VideoTexture/VideoDeckLink.cpp
@@ -544,12 +544,12 @@ HRESULT STDMETHODCALLTYPE PinnedMemoryAllocator::QueryInterface(REFIID /*iid*/,
ULONG STDMETHODCALLTYPE PinnedMemoryAllocator::AddRef(void)
{
- return atomic_add_uint32(&mRefCount, 1U);
+ return atomic_add_and_fetch_uint32(&mRefCount, 1U);
}
ULONG STDMETHODCALLTYPE PinnedMemoryAllocator::Release(void)
{
- uint32_t newCount = atomic_sub_uint32(&mRefCount, 1U);
+ uint32_t newCount = atomic_sub_and_fetch_uint32(&mRefCount, 1U);
if (newCount == 0)
delete this;
return (ULONG)newCount;
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 8487c6a2094..a29b612e0ef 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -130,7 +130,7 @@ add_test(export_obj_cube ${TEST_BLENDER_EXE}
--run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_cube.obj',use_selection=False\)
--md5_source=${TEST_OUT_DIR}/export_obj_cube.obj
--md5_source=${TEST_OUT_DIR}/export_obj_cube.mtl
- --md5=826f74e6b7a2128b0b61a52071ada36e --md5_method=FILE
+ --md5=e80660437ad9bfe082849641c361a233 --md5_method=FILE
)
add_test(export_obj_nurbs ${TEST_BLENDER_EXE}