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:
authorLukas Tönne <lukas.toenne@gmail.com>2016-08-09 17:37:15 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2016-08-09 17:37:15 +0300
commit4a801f6c6f0421ffd515c12422dd197441440520 (patch)
tree5415e844b460bb2aa07b9467c72e13abc2e61228
parent49c63d46db8c055152d9e431e89405f9b51a4bbe (diff)
parent02719521d2e25abcc8ffcccc086d3a651986f52f (diff)
Merge branch 'master' into object_nodesobject_nodes
-rw-r--r--CMakeLists.txt71
-rwxr-xr-xbuild_files/build_environment/install_deps.sh187
-rw-r--r--build_files/buildbot/slave_compile.py7
-rw-r--r--build_files/cmake/Modules/FindAlembic.cmake70
-rw-r--r--build_files/cmake/Modules/FindHDF5.cmake69
-rw-r--r--build_files/cmake/Modules/GTestTesting.cmake2
-rw-r--r--build_files/cmake/clang_array_check.py4
-rwxr-xr-xbuild_files/cmake/cmake_consistency_check.py10
-rw-r--r--build_files/cmake/cmake_consistency_check_config.py2
-rwxr-xr-xbuild_files/cmake/cmake_netbeans_project.py26
-rwxr-xr-xbuild_files/cmake/cmake_qtcreator_project.py68
-rw-r--r--build_files/cmake/cmake_static_check_clang_array.py4
-rw-r--r--build_files/cmake/cmake_static_check_cppcheck.py4
-rw-r--r--build_files/cmake/cmake_static_check_smatch.py4
-rw-r--r--build_files/cmake/cmake_static_check_sparse.py4
-rw-r--r--build_files/cmake/cmake_static_check_splint.py4
-rw-r--r--build_files/cmake/config/blender_full.cmake1
-rw-r--r--build_files/cmake/config/blender_lite.cmake1
-rw-r--r--build_files/cmake/config/bpy_module.cmake1
-rwxr-xr-xbuild_files/cmake/example_scripts/make_quicky.py2
-rw-r--r--build_files/cmake/macros.cmake9
-rwxr-xr-xbuild_files/cmake/project_info.py9
-rw-r--r--build_files/cmake/project_source_info.py2
-rwxr-xr-xbuild_files/package_spec/build_archive.py2
-rwxr-xr-xdoc/manpage/blender.1.py2
-rw-r--r--doc/python_api/epy/IDProp.py132
-rw-r--r--doc/python_api/sphinx_changelog_gen.py113
-rw-r--r--doc/python_api/sphinx_doc_gen.py308
-rw-r--r--extern/CMakeLists.txt1
-rw-r--r--extern/curve_fit_nd/README.blender5
-rw-r--r--extern/curve_fit_nd/intern/curve_fit_cubic.c24
-rw-r--r--extern/curve_fit_nd/intern/curve_fit_cubic_refit.c46
-rw-r--r--extern/curve_fit_nd/intern/generic_heap.h4
-rw-r--r--extern/gmock/CMakeLists.txt66
-rw-r--r--extern/gmock/LICENSE28
-rw-r--r--extern/gmock/README.blender7
-rw-r--r--extern/gmock/README.md333
-rw-r--r--extern/gmock/include/gmock/gmock-actions.h1205
-rw-r--r--extern/gmock/include/gmock/gmock-cardinalities.h147
-rw-r--r--extern/gmock/include/gmock/gmock-generated-actions.h2377
-rw-r--r--extern/gmock/include/gmock/gmock-generated-function-mockers.h1095
-rw-r--r--extern/gmock/include/gmock/gmock-generated-matchers.h2179
-rw-r--r--extern/gmock/include/gmock/gmock-generated-nice-strict.h397
-rw-r--r--extern/gmock/include/gmock/gmock-matchers.h4399
-rw-r--r--extern/gmock/include/gmock/gmock-more-actions.h246
-rw-r--r--extern/gmock/include/gmock/gmock-more-matchers.h58
-rw-r--r--extern/gmock/include/gmock/gmock-spec-builders.h1847
-rw-r--r--extern/gmock/include/gmock/gmock.h94
-rw-r--r--extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h8
-rw-r--r--extern/gmock/include/gmock/internal/custom/gmock-matchers.h39
-rw-r--r--extern/gmock/include/gmock/internal/custom/gmock-port.h46
-rw-r--r--extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h279
-rw-r--r--extern/gmock/include/gmock/internal/gmock-internal-utils.h511
-rw-r--r--extern/gmock/include/gmock/internal/gmock-port.h91
-rw-r--r--extern/gmock/src/gmock-all.cc47
-rw-r--r--extern/gmock/src/gmock-cardinalities.cc156
-rw-r--r--extern/gmock/src/gmock-internal-utils.cc174
-rw-r--r--extern/gmock/src/gmock-matchers.cc498
-rw-r--r--extern/gmock/src/gmock-spec-builders.cc823
-rw-r--r--extern/gmock/src/gmock.cc183
-rw-r--r--extern/gmock/src/gmock_main.cc54
-rw-r--r--extern/gtest/README.blender6
-rw-r--r--extern/gtest/include/gtest/gtest-param-test.h47
-rw-r--r--extern/gtest/include/gtest/gtest-printers.h244
-rw-r--r--extern/gtest/include/gtest/gtest-typed-test.h8
-rw-r--r--extern/gtest/include/gtest/gtest.h401
-rw-r--r--extern/gtest/include/gtest/internal/custom/gtest-port.h69
-rw-r--r--extern/gtest/include/gtest/internal/custom/gtest-printers.h42
-rw-r--r--extern/gtest/include/gtest/internal/custom/gtest.h41
-rw-r--r--extern/gtest/include/gtest/internal/gtest-internal.h192
-rw-r--r--extern/gtest/include/gtest/internal/gtest-linked_ptr.h14
-rw-r--r--extern/gtest/include/gtest/internal/gtest-param-util-generated.h83
-rw-r--r--extern/gtest/include/gtest/internal/gtest-param-util.h152
-rw-r--r--extern/gtest/include/gtest/internal/gtest-port-arch.h93
-rw-r--r--extern/gtest/include/gtest/internal/gtest-port.h986
-rw-r--r--extern/gtest/include/gtest/internal/gtest-tuple.h8
-rw-r--r--extern/gtest/src/gtest-death-test.cc44
-rw-r--r--extern/gtest/src/gtest-filepath.cc13
-rw-r--r--extern/gtest/src/gtest-internal-inl.h55
-rw-r--r--extern/gtest/src/gtest-port.cc576
-rw-r--r--extern/gtest/src/gtest-printers.cc12
-rw-r--r--extern/gtest/src/gtest-test-part.cc6
-rw-r--r--extern/gtest/src/gtest-typed-test.cc40
-rw-r--r--extern/gtest/src/gtest.cc739
-rw-r--r--intern/audaspace/jack/AUD_JackLibrary.cpp15
-rw-r--r--intern/cycles/CMakeLists.txt8
-rw-r--r--intern/cycles/app/cycles_xml.cpp8
-rw-r--r--intern/cycles/blender/addon/engine.py3
-rw-r--r--intern/cycles/blender/addon/osl.py6
-rw-r--r--intern/cycles/blender/addon/properties.py51
-rw-r--r--intern/cycles/blender/addon/ui.py51
-rw-r--r--intern/cycles/blender/addon/version_update.py1
-rw-r--r--intern/cycles/blender/blender_mesh.cpp372
-rw-r--r--intern/cycles/blender/blender_object.cpp32
-rw-r--r--intern/cycles/blender/blender_shader.cpp13
-rw-r--r--intern/cycles/blender/blender_util.h29
-rw-r--r--intern/cycles/device/device_cuda.cpp212
-rw-r--r--intern/cycles/device/device_opencl.cpp1
-rw-r--r--intern/cycles/kernel/CMakeLists.txt20
-rw-r--r--intern/cycles/kernel/bvh/bvh_shadow_all.h27
-rw-r--r--intern/cycles/kernel/bvh/bvh_subsurface.h17
-rw-r--r--intern/cycles/kernel/bvh/bvh_traversal.h21
-rw-r--r--intern/cycles/kernel/bvh/bvh_volume.h13
-rw-r--r--intern/cycles/kernel/bvh/bvh_volume_all.h33
-rw-r--r--intern/cycles/kernel/bvh/qbvh_shadow_all.h11
-rw-r--r--intern/cycles/kernel/bvh/qbvh_volume_all.h18
-rw-r--r--intern/cycles/kernel/closure/alloc.h90
-rw-r--r--intern/cycles/kernel/closure/bsdf.h96
-rw-r--r--intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h60
-rw-r--r--intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h35
-rw-r--r--intern/cycles/kernel/closure/bsdf_diffuse.h33
-rw-r--r--intern/cycles/kernel/closure/bsdf_diffuse_ramp.h35
-rw-r--r--intern/cycles/kernel/closure/bsdf_hair.h71
-rw-r--r--intern/cycles/kernel/closure/bsdf_microfacet.h158
-rw-r--r--intern/cycles/kernel/closure/bsdf_microfacet_multi.h135
-rw-r--r--intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h13
-rw-r--r--intern/cycles/kernel/closure/bsdf_oren_nayar.h45
-rw-r--r--intern/cycles/kernel/closure/bsdf_phong_ramp.h56
-rw-r--r--intern/cycles/kernel/closure/bsdf_reflection.h7
-rw-r--r--intern/cycles/kernel/closure/bsdf_refraction.h9
-rw-r--r--intern/cycles/kernel/closure/bsdf_toon.h78
-rw-r--r--intern/cycles/kernel/closure/bssrdf.h117
-rw-r--r--intern/cycles/kernel/closure/volume.h26
-rw-r--r--intern/cycles/kernel/geom/geom.h2
-rw-r--r--intern/cycles/kernel/geom/geom_attribute.h61
-rw-r--r--intern/cycles/kernel/geom/geom_curve.h24
-rw-r--r--intern/cycles/kernel/geom/geom_motion_triangle.h6
-rw-r--r--intern/cycles/kernel/geom/geom_object.h12
-rw-r--r--intern/cycles/kernel/geom/geom_patch.h343
-rw-r--r--intern/cycles/kernel/geom/geom_primitive.h72
-rw-r--r--intern/cycles/kernel/geom/geom_subd_triangle.h349
-rw-r--r--intern/cycles/kernel/geom/geom_triangle.h38
-rw-r--r--intern/cycles/kernel/geom/geom_triangle_intersect.h26
-rw-r--r--intern/cycles/kernel/geom/geom_volume.h31
-rw-r--r--intern/cycles/kernel/kernel_bake.h8
-rw-r--r--intern/cycles/kernel/kernel_camera.h13
-rw-r--r--intern/cycles/kernel/kernel_compat_cuda.h7
-rw-r--r--intern/cycles/kernel/kernel_compat_opencl.h1
-rw-r--r--intern/cycles/kernel/kernel_light.h32
-rw-r--r--intern/cycles/kernel/kernel_path.h8
-rw-r--r--intern/cycles/kernel/kernel_path_branched.h14
-rw-r--r--intern/cycles/kernel/kernel_path_surface.h9
-rw-r--r--intern/cycles/kernel/kernel_path_volume.h10
-rw-r--r--intern/cycles/kernel/kernel_projection.h15
-rw-r--r--intern/cycles/kernel/kernel_shader.h121
-rw-r--r--intern/cycles/kernel/kernel_shadow.h7
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h45
-rw-r--r--intern/cycles/kernel/kernel_textures.h8
-rw-r--r--intern/cycles/kernel/kernel_types.h148
-rw-r--r--intern/cycles/kernel/kernel_volume.h16
-rw-r--r--intern/cycles/kernel/osl/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/osl/background.cpp20
-rw-r--r--intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp46
-rw-r--r--intern/cycles/kernel/osl/bsdf_phong_ramp.cpp48
-rw-r--r--intern/cycles/kernel/osl/emissive.cpp23
-rw-r--r--intern/cycles/kernel/osl/osl_bssrdf.cpp102
-rw-r--r--intern/cycles/kernel/osl/osl_bssrdf.h61
-rw-r--r--intern/cycles/kernel/osl/osl_closures.cpp248
-rw-r--r--intern/cycles/kernel/osl/osl_closures.h110
-rw-r--r--intern/cycles/kernel/osl/osl_globals.h3
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp15
-rw-r--r--intern/cycles/kernel/osl/osl_shader.cpp302
-rw-r--r--intern/cycles/kernel/osl/osl_shader.h12
-rw-r--r--intern/cycles/kernel/shaders/node_hair_bsdf.osl45
-rw-r--r--intern/cycles/kernel/shaders/node_ramp_util.h89
-rw-r--r--intern/cycles/kernel/shaders/node_rgb_curves.osl48
-rw-r--r--intern/cycles/kernel/shaders/node_rgb_ramp.osl21
-rw-r--r--intern/cycles/kernel/shaders/node_vector_curves.osl48
-rw-r--r--intern/cycles/kernel/svm/svm.h4
-rw-r--r--intern/cycles/kernel/svm/svm_attribute.h116
-rw-r--r--intern/cycles/kernel/svm/svm_closure.h490
-rw-r--r--intern/cycles/kernel/svm/svm_geometry.h18
-rw-r--r--intern/cycles/kernel/svm/svm_image.h3
-rw-r--r--intern/cycles/kernel/svm/svm_math_util.h10
-rw-r--r--intern/cycles/kernel/svm/svm_ramp.h45
-rw-r--r--intern/cycles/kernel/svm/svm_ramp_util.h97
-rw-r--r--intern/cycles/kernel/svm/svm_tex_coord.h53
-rw-r--r--intern/cycles/kernel/svm/svm_types.h3
-rw-r--r--intern/cycles/kernel/svm/svm_wireframe.h10
-rw-r--r--intern/cycles/render/CMakeLists.txt1
-rw-r--r--intern/cycles/render/attribute.cpp128
-rw-r--r--intern/cycles/render/attribute.h17
-rw-r--r--intern/cycles/render/constant_fold.cpp252
-rw-r--r--intern/cycles/render/constant_fold.h15
-rw-r--r--intern/cycles/render/graph.cpp16
-rw-r--r--intern/cycles/render/light.cpp204
-rw-r--r--intern/cycles/render/light.h4
-rw-r--r--intern/cycles/render/mesh.cpp529
-rw-r--r--intern/cycles/render/mesh.h62
-rw-r--r--intern/cycles/render/mesh_displace.cpp160
-rw-r--r--intern/cycles/render/mesh_subdivision.cpp567
-rw-r--r--intern/cycles/render/nodes.cpp122
-rw-r--r--intern/cycles/render/nodes.h22
-rw-r--r--intern/cycles/render/object.cpp84
-rw-r--r--intern/cycles/render/object.h2
-rw-r--r--intern/cycles/render/osl.cpp2
-rw-r--r--intern/cycles/render/scene.h4
-rw-r--r--intern/cycles/render/shader.cpp10
-rw-r--r--intern/cycles/render/shader.h11
-rw-r--r--intern/cycles/render/svm.cpp9
-rw-r--r--intern/cycles/subd/CMakeLists.txt7
-rw-r--r--intern/cycles/subd/subd_dice.cpp166
-rw-r--r--intern/cycles/subd/subd_dice.h46
-rw-r--r--intern/cycles/subd/subd_mesh.cpp419
-rw-r--r--intern/cycles/subd/subd_mesh.h78
-rw-r--r--intern/cycles/subd/subd_patch.cpp26
-rw-r--r--intern/cycles/subd/subd_patch.h18
-rw-r--r--intern/cycles/subd/subd_patch_table.cpp297
-rw-r--r--intern/cycles/subd/subd_patch_table.h63
-rw-r--r--intern/cycles/subd/subd_split.cpp199
-rw-r--r--intern/cycles/subd/subd_split.h8
-rw-r--r--intern/cycles/test/CMakeLists.txt40
-rw-r--r--intern/cycles/test/render_graph_finalize_test.cpp1532
-rw-r--r--intern/cycles/util/util_math.h42
-rw-r--r--intern/cycles/util/util_path.cpp18
-rw-r--r--intern/cycles/util/util_string.cpp9
-rw-r--r--intern/cycles/util/util_string.h5
-rw-r--r--intern/cycles/util/util_transform.h9
-rw-r--r--intern/cycles/util/util_vector.h5
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm10
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp22
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp21
-rw-r--r--intern/opensubdiv/gpu_shader_opensubd_display.glsl4
-rw-r--r--intern/opensubdiv/opensubdiv_gpu_capi.cc82
-rw-r--r--intern/smoke/intern/FLUID_3D.cpp141
-rw-r--r--intern/smoke/intern/FLUID_3D.h5
-rwxr-xr-xrelease/bin/blender-thumbnailer.py2
-rw-r--r--release/datafiles/blender_icons.svg489
-rw-r--r--release/datafiles/blender_icons16/icon16_restrict_color_off.datbin0 -> 1048 bytes
-rw-r--r--release/datafiles/blender_icons16/icon16_restrict_color_on.datbin0 -> 1048 bytes
-rw-r--r--release/datafiles/blender_icons32/icon32_restrict_color_off.datbin0 -> 4120 bytes
-rw-r--r--release/datafiles/blender_icons32/icon32_restrict_color_on.datbin0 -> 4120 bytes
-rwxr-xr-xrelease/datafiles/ctodata.py2
-rw-r--r--release/scripts/freestyle/modules/parameter_editor.py15
-rw-r--r--release/scripts/modules/addon_utils.py4
-rw-r--r--release/scripts/modules/bl_previews_utils/bl_previews_render.py5
-rwxr-xr-xrelease/scripts/modules/blend_render_info.py2
-rw-r--r--release/scripts/modules/bpy/__init__.py12
-rw-r--r--release/scripts/modules/bpy/path.py32
-rw-r--r--release/scripts/modules/bpy_extras/keyconfig_utils.py34
-rw-r--r--release/scripts/modules/bpy_extras/view3d_utils.py4
-rw-r--r--release/scripts/modules/bpy_restrict_state.py2
-rw-r--r--release/scripts/modules/bpy_types.py7
-rw-r--r--release/scripts/modules/console/complete_import.py10
-rw-r--r--release/scripts/modules/console/complete_namespace.py4
-rw-r--r--release/scripts/modules/console/intellisense.py14
-rw-r--r--release/scripts/modules/console_python.py18
-rw-r--r--release/scripts/modules/keyingsets_utils.py3
-rw-r--r--release/scripts/modules/progress_report.py3
-rw-r--r--release/scripts/modules/rna_info.py40
-rw-r--r--release/scripts/modules/rna_xml.py5
-rw-r--r--release/scripts/modules/sys_info.py23
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py13
-rw-r--r--release/scripts/startup/bl_ui/properties_data_bone.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py44
-rw-r--r--release/scripts/startup/bl_ui/properties_game.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py483
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_cloth.py53
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py27
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_field.py8
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py12
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_rigidbody.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py18
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_softbody.py11
-rw-r--r--release/scripts/startup/bl_ui/properties_world.py4
-rw-r--r--release/scripts/startup/bl_ui/space_clip.py25
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py1
-rw-r--r--release/scripts/startup/bl_ui/space_image.py24
-rw-r--r--release/scripts/startup/bl_ui/space_info.py4
-rw-r--r--release/scripts/startup/bl_ui/space_node.py28
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py14
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py28
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py14
-rw-r--r--release/scripts/startup/keyingsets_builtins.py1
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/CMakeLists.txt4
-rw-r--r--source/blender/alembic/ABC_alembic.h110
-rw-r--r--source/blender/alembic/CMakeLists.txt81
-rw-r--r--source/blender/alembic/intern/abc_camera.cc162
-rw-r--r--source/blender/alembic/intern/abc_camera.h61
-rw-r--r--source/blender/alembic/intern/abc_curves.cc355
-rw-r--r--source/blender/alembic/intern/abc_curves.h65
-rw-r--r--source/blender/alembic/intern/abc_customdata.cc379
-rw-r--r--source/blender/alembic/intern/abc_customdata.h93
-rw-r--r--source/blender/alembic/intern/abc_exporter.cc600
-rw-r--r--source/blender/alembic/intern/abc_exporter.h112
-rw-r--r--source/blender/alembic/intern/abc_hair.cc290
-rw-r--r--source/blender/alembic/intern/abc_hair.h66
-rw-r--r--source/blender/alembic/intern/abc_mesh.cc1213
-rw-r--r--source/blender/alembic/intern/abc_mesh.h152
-rw-r--r--source/blender/alembic/intern/abc_nurbs.cc367
-rw-r--r--source/blender/alembic/intern/abc_nurbs.h63
-rw-r--r--source/blender/alembic/intern/abc_object.cc238
-rw-r--r--source/blender/alembic/intern/abc_object.h169
-rw-r--r--source/blender/alembic/intern/abc_points.cc198
-rw-r--r--source/blender/alembic/intern/abc_points.h70
-rw-r--r--source/blender/alembic/intern/abc_transform.cc152
-rw-r--r--source/blender/alembic/intern/abc_transform.h73
-rw-r--r--source/blender/alembic/intern/abc_util.cc437
-rw-r--r--source/blender/alembic/intern/abc_util.h125
-rw-r--r--source/blender/alembic/intern/alembic_capi.cc1136
-rw-r--r--source/blender/blenkernel/BKE_animsys.h1
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_cachefile.h67
-rw-r--r--source/blender/blenkernel/BKE_cloth.h1
-rw-r--r--source/blender/blenkernel/BKE_colortools.h2
-rw-r--r--source/blender/blenkernel/BKE_context.h9
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h6
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h69
-rw-r--r--source/blender/blenkernel/BKE_idcode.h2
-rw-r--r--source/blender/blenkernel/BKE_library.h3
-rw-r--r--source/blender/blenkernel/BKE_main.h5
-rw-r--r--source/blender/blenkernel/BKE_movieclip.h3
-rw-r--r--source/blender/blenkernel/BKE_paint.h4
-rw-r--r--source/blender/blenkernel/BKE_particle.h7
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h2
-rw-r--r--source/blender/blenkernel/BKE_tracking.h1
-rw-r--r--source/blender/blenkernel/CMakeLists.txt9
-rw-r--r--source/blender/blenkernel/intern/action.c5
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c343
-rw-r--r--source/blender/blenkernel/intern/armature.c5
-rw-r--r--source/blender/blenkernel/intern/bpath.c7
-rw-r--r--source/blender/blenkernel/intern/brush.c5
-rw-r--r--source/blender/blenkernel/intern/cachefile.c173
-rw-r--r--source/blender/blenkernel/intern/camera.c5
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c2
-rw-r--r--source/blender/blenkernel/intern/cloth.c25
-rw-r--r--source/blender/blenkernel/intern/colortools.c85
-rw-r--r--source/blender/blenkernel/intern/constraint.c74
-rw-r--r--source/blender/blenkernel/intern/context.c20
-rw-r--r--source/blender/blenkernel/intern/curve.c5
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c52
-rw-r--r--source/blender/blenkernel/intern/fcurve.c98
-rw-r--r--source/blender/blenkernel/intern/gpencil.c834
-rw-r--r--source/blender/blenkernel/intern/group.c5
-rw-r--r--source/blender/blenkernel/intern/idcode.c53
-rw-r--r--source/blender/blenkernel/intern/idprop.c11
-rw-r--r--source/blender/blenkernel/intern/image.c6
-rw-r--r--source/blender/blenkernel/intern/key.c5
-rw-r--r--source/blender/blenkernel/intern/lamp.c5
-rw-r--r--source/blender/blenkernel/intern/lattice.c5
-rw-r--r--source/blender/blenkernel/intern/library.c230
-rw-r--r--source/blender/blenkernel/intern/library_query.c71
-rw-r--r--source/blender/blenkernel/intern/library_remap.c7
-rw-r--r--source/blender/blenkernel/intern/linestyle.c5
-rw-r--r--source/blender/blenkernel/intern/mask.c5
-rw-r--r--source/blender/blenkernel/intern/material.c8
-rw-r--r--source/blender/blenkernel/intern/mball.c5
-rw-r--r--source/blender/blenkernel/intern/mesh.c5
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_mapping.c1
-rw-r--r--source/blender/blenkernel/intern/movieclip.c26
-rw-r--r--source/blender/blenkernel/intern/node.c5
-rw-r--r--source/blender/blenkernel/intern/object.c5
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c1
-rw-r--r--source/blender/blenkernel/intern/paint.c38
-rw-r--r--source/blender/blenkernel/intern/particle.c12
-rw-r--r--source/blender/blenkernel/intern/particle_system.c43
-rw-r--r--source/blender/blenkernel/intern/pointcache.c4
-rw-r--r--source/blender/blenkernel/intern/scene.c23
-rw-r--r--source/blender/blenkernel/intern/sequencer.c49
-rw-r--r--source/blender/blenkernel/intern/smoke.c25
-rw-r--r--source/blender/blenkernel/intern/softbody.c150
-rw-r--r--source/blender/blenkernel/intern/speaker.c5
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c18
-rw-r--r--source/blender/blenkernel/intern/text.c5
-rw-r--r--source/blender/blenkernel/intern/texture.c5
-rw-r--r--source/blender/blenkernel/intern/tracking.c129
-rw-r--r--source/blender/blenkernel/intern/world.c5
-rw-r--r--source/blender/blenlib/intern/BLI_heap.c3
-rw-r--r--source/blender/blenlib/intern/scanfill_utils.c2
-rw-r--r--source/blender/blenloader/CMakeLists.txt7
-rw-r--r--source/blender/blenloader/intern/readfile.c149
-rw-r--r--source/blender/blenloader/intern/versioning_270.c109
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c5
-rw-r--r--source/blender/blenloader/intern/writefile.c37
-rw-r--r--source/blender/blentranslation/BLT_translation.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.c2
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.cpp2
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h4
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h10
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc33
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc24
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc6
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc10
-rw-r--r--source/blender/depsgraph/intern/depsgraph_eval.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc3
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc17
-rw-r--r--source/blender/depsgraph/intern/depsgraph_types.h5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc22
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.h2
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node.cc2
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node.h2
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.cc9
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.h6
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c91
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c7
-rw-r--r--source/blender/editors/animation/anim_filter.c72
-rw-r--r--source/blender/editors/animation/keyframes_draw.c32
-rw-r--r--source/blender/editors/animation/keyframing.c13
-rw-r--r--source/blender/editors/curve/editcurve_paint.c4
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c563
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c12
-rw-r--r--source/blender/editors/gpencil/gpencil_brush.c403
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c55
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c1830
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c345
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h127
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c80
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c482
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c129
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c379
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_gpencil.h12
-rw-r--r--source/blender/editors/include/ED_keyframes_draw.h3
-rw-r--r--source/blender/editors/include/UI_icons.h4
-rw-r--r--source/blender/editors/include/UI_interface.h7
-rw-r--r--source/blender/editors/interface/interface_handlers.c4
-rw-r--r--source/blender/editors/interface/interface_icons.c2
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_regions.c32
-rw-r--r--source/blender/editors/interface/interface_templates.c87
-rw-r--r--source/blender/editors/io/CMakeLists.txt14
-rw-r--r--source/blender/editors/io/io_alembic.c458
-rw-r--r--source/blender/editors/io/io_alembic.h37
-rw-r--r--source/blender/editors/io/io_cache.c162
-rw-r--r--source/blender/editors/io/io_cache.h37
-rw-r--r--source/blender/editors/io/io_ops.c16
-rw-r--r--source/blender/editors/object/object_add.c17
-rw-r--r--source/blender/editors/object/object_constraint.c7
-rw-r--r--source/blender/editors/object/object_relations.c14
-rw-r--r--source/blender/editors/render/render_opengl.c97
-rw-r--r--source/blender/editors/render/render_preview.c2
-rw-r--r--source/blender/editors/screen/screen_context.c64
-rw-r--r--source/blender/editors/screen/screen_edit.c1
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c5
-rw-r--r--source/blender/editors/space_action/action_buttons.c2
-rw-r--r--source/blender/editors/space_action/action_edit.c2
-rw-r--r--source/blender/editors/space_clip/clip_toolbar.c2
-rw-r--r--source/blender/editors/space_clip/space_clip.c2
-rw-r--r--source/blender/editors/space_file/filelist.c7
-rw-r--r--source/blender/editors/space_file/filesel.c4
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c7
-rw-r--r--source/blender/editors/space_image/image_buttons.c2
-rw-r--r--source/blender/editors/space_logic/logic_buttons.c2
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c3
-rw-r--r--source/blender/editors/space_nla/nla_channels.c1
-rw-r--r--source/blender/editors/space_node/drawnode.c2
-rw-r--r--source/blender/editors/space_node/node_buttons.c2
-rw-r--r--source/blender/editors/space_node/node_group.c45
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c7
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h2
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c11
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c11
-rw-r--r--source/blender/editors/space_text/text_header.c2
-rw-r--r--source/blender/editors/space_time/space_time.c69
-rw-r--r--source/blender/editors/space_view3d/drawmesh.c12
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/drawvolume.c6
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_ruler.c12
-rw-r--r--source/blender/editors/transform/transform.c158
-rw-r--r--source/blender/editors/transform/transform.h1
-rw-r--r--source/blender/editors/transform/transform_conversions.c84
-rw-r--r--source/blender/editors/transform/transform_generics.c4
-rw-r--r--source/blender/editors/transform/transform_manipulator.c55
-rw-r--r--source/blender/editors/transform/transform_snap.c9
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp1
-rw-r--r--source/blender/freestyle/intern/python/BPy_ViewShape.cpp14
-rw-r--r--source/blender/freestyle/intern/scene_graph/Rep.h18
-rw-r--r--source/blender/freestyle/intern/view_map/BoxGrid.cpp10
-rw-r--r--source/blender/freestyle/intern/view_map/Silhouette.h15
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMap.h8
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp1
-rw-r--r--source/blender/freestyle/intern/winged_edge/WEdge.cpp1
-rw-r--r--source/blender/freestyle/intern/winged_edge/WEdge.h13
-rw-r--r--source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp1
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c6
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h1
-rw-r--r--source/blender/gpu/intern/gpu_material.c3
-rw-r--r--source/blender/gpu/intern/gpu_texture.c16
-rw-r--r--source/blender/makesdna/DNA_ID.h122
-rw-r--r--source/blender/makesdna/DNA_action_types.h4
-rw-r--r--source/blender/makesdna/DNA_anim_types.h4
-rw-r--r--source/blender/makesdna/DNA_cachefile_types.h84
-rw-r--r--source/blender/makesdna/DNA_cloth_types.h2
-rw-r--r--source/blender/makesdna/DNA_color_types.h3
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h7
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h144
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h22
-rw-r--r--source/blender/makesdna/DNA_object_force.h2
-rw-r--r--source/blender/makesdna/DNA_particle_types.h1
-rw-r--r--source/blender/makesdna/DNA_scene_types.h23
-rw-r--r--source/blender/makesdna/DNA_screen_types.h17
-rw-r--r--source/blender/makesdna/DNA_space_types.h1
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h10
-rw-r--r--source/blender/makesrna/RNA_enum_types.h2
-rw-r--r--source/blender/makesrna/RNA_types.h10
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt8
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_ID.c9
-rw-r--r--source/blender/makesrna/intern/rna_access.c19
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c169
-rw-r--r--source/blender/makesrna/intern/rna_cloth.c22
-rw-r--r--source/blender/makesrna/intern/rna_color.c3
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c26
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c8
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c825
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_main.c7
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c18
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c46
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c14
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c5
-rw-r--r--source/blender/makesrna/intern/rna_particle.c4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c317
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c111
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c31
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c16
-rw-r--r--source/blender/makesrna/intern/rna_space.c4
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c5
-rw-r--r--source/blender/modifiers/CMakeLists.txt8
-rw-r--r--source/blender/modifiers/MOD_modifiertypes.h1
-rw-r--r--source/blender/modifiers/intern/MOD_fluidsim_util.c2
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c197
-rw-r--r--source/blender/modifiers/intern/MOD_util.c1
-rw-r--r--source/blender/physics/intern/BPH_mass_spring.cpp8
-rw-r--r--source/blender/python/BPY_extern.h3
-rw-r--r--source/blender/python/bmesh/bmesh_py_ops_call.c8
-rw-r--r--source/blender/python/generic/idprop_py_api.c142
-rw-r--r--source/blender/python/generic/py_capi_utils.c29
-rw-r--r--source/blender/python/generic/py_capi_utils.h1
-rw-r--r--source/blender/python/intern/CMakeLists.txt13
-rw-r--r--source/blender/python/intern/bpy_app.c3
-rw-r--r--source/blender/python/intern/bpy_app_alembic.c113
-rw-r--r--source/blender/python/intern/bpy_app_alembic.h38
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c7
-rw-r--r--source/blender/python/intern/bpy_app_translations.c2
-rw-r--r--source/blender/python/intern/bpy_driver.c73
-rw-r--r--source/blender/python/intern/bpy_driver.h3
-rw-r--r--source/blender/python/intern/bpy_intern_string.c6
-rw-r--r--source/blender/python/intern/bpy_intern_string.h2
-rw-r--r--source/blender/python/intern/bpy_library_load.c4
-rw-r--r--source/blender/python/intern/bpy_rna_driver.c21
-rw-r--r--source/blender/python/intern/bpy_rna_driver.h4
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Vector.c790
-rw-r--r--source/blender/render/extern/include/RE_pipeline.h3
-rw-r--r--source/blender/render/intern/include/render_result.h3
-rw-r--r--source/blender/render/intern/raytrace/rayobject_rtbuild.cpp4
-rw-r--r--source/blender/render/intern/source/pipeline.c29
-rw-r--r--source/blender/render/intern/source/render_result.c5
-rw-r--r--source/blender/windowmanager/WM_api.h3
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c5
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c18
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c2
-rw-r--r--source/blender/windowmanager/intern/wm_window.c15
-rw-r--r--source/blenderplayer/CMakeLists.txt4
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c20
-rw-r--r--source/gameengine/GamePlayer/ghost/GPG_ghost.cpp3
-rw-r--r--source/gameengine/Ketsji/KX_Scene.cpp2
-rw-r--r--tests/gtests/testing/mock_log.h162
-rwxr-xr-xtests/python/cycles_render_tests.py15
-rw-r--r--tests/python/pep8.py13
578 files changed, 48259 insertions, 7252 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1dfa838a5a2..281aa52cae3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -323,6 +323,10 @@ option(WITH_CODEC_AVI "Enable Blenders own AVI file support (raw/jpeg)
option(WITH_CODEC_FFMPEG "Enable FFMPeg Support (http://ffmpeg.org)" ${_init_CODEC_FFMPEG})
option(WITH_CODEC_SNDFILE "Enable libsndfile Support (http://www.mega-nerd.com/libsndfile)" OFF)
+# Alembic support
+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)
endif()
@@ -393,6 +397,7 @@ option(WITH_CYCLES "Enable Cycles Render Engine" ON)
option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF)
option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF)
option(WITH_CYCLES_OSL "Build Cycles with OSL support" ${_init_CYCLES_OSL})
+option(WITH_CYCLES_OPENSUBDIV "Build Cycles with OpenSubdiv support" ON)
option(WITH_CYCLES_CUDA_BINARIES "Build Cycles CUDA binaries" OFF)
set(CYCLES_CUDA_BINARIES_ARCH sm_20 sm_21 sm_30 sm_35 sm_37 sm_50 sm_52 CACHE STRING "CUDA architectures to build binaries for")
mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH)
@@ -720,6 +725,11 @@ if(WITH_OPENIMAGEIO)
set(WITH_IMAGE_TIFF ON)
endif()
+# auto enable alembic linking dependencies
+if(WITH_ALEMBIC)
+ set(WITH_IMAGE_OPENEXR ON)
+endif()
+
# don't store paths to libs for portable distribution
if(WITH_INSTALL_PORTABLE)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
@@ -1091,6 +1101,20 @@ if(UNIX AND NOT APPLE)
endif()
endif()
+ if(WITH_ALEMBIC)
+ find_package_wrapper(Alembic)
+
+ if(WITH_ALEMBIC_HDF5)
+ set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
+ find_package_wrapper(HDF5)
+ endif()
+
+ if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+ set(WITH_ALEMBIC OFF)
+ set(WITH_ALEMBIC_HDF5 OFF)
+ endif()
+ endif()
+
if(WITH_BOOST)
# uses in build instructions to override include and library variables
if(NOT BOOST_CUSTOM)
@@ -1326,10 +1350,8 @@ elseif(WIN32)
# MSVC11 needs _ALLOW_KEYWORD_MACROS to build
add_definitions(-D_ALLOW_KEYWORD_MACROS)
- if(CMAKE_CL_64)
- # We want to support Vista level ABI for x64
- add_definitions(-D_WIN32_WINNT=0x600)
- endif()
+ # We want to support Vista level ABI
+ add_definitions(-D_WIN32_WINNT=0x600)
# Make cmake find the msvc redistributables
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
@@ -1660,6 +1682,14 @@ elseif(WIN32)
set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib)
endif()
+ if(WITH_ALEMBIC)
+ set(ALEMBIC ${LIBDIR}/alembic)
+ set(ALEMBIC_INCLUDE_DIR ${ALEMBIC}/include)
+ set(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR})
+ set(ALEMBIC_LIBPATH ${ALEMBIC}/lib)
+ set(ALEMBIC_LIBRARIES optimized alembic debug alembic_d)
+ endif()
+
if(WITH_MOD_CLOTH_ELTOPO)
set(LAPACK ${LIBDIR}/lapack)
# set(LAPACK_INCLUDE_DIR ${LAPACK}/include)
@@ -1959,6 +1989,23 @@ elseif(WIN32)
set(OPENVDB_DEFINITIONS)
endif()
+ if(WITH_ALEMBIC)
+ # TODO(sergey): For until someone drops by and compiles libraries for
+ # MinGW we allow users to compile their own Alembic library and use
+ # that via find_package(),
+ #
+ # Once precompiled libraries are there we'll use hardcoded locations.
+ find_package_wrapper(Alembic)
+ if(WITH_ALEMBIC_HDF5)
+ set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
+ find_package_wrapper(HDF5)
+ endif()
+ if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+ set(WITH_ALEMBIC OFF)
+ set(WITH_ALEMBIC_HDF5 OFF)
+ endif()
+ endif()
+
set(PLATFORM_LINKFLAGS "-Xlinker --stack=2097152")
## DISABLE - causes linking errors
@@ -2043,6 +2090,14 @@ elseif(APPLE)
endif()
endif()
+ if(WITH_ALEMBIC)
+ set(ALEMBIC ${LIBDIR}/alembic)
+ set(ALEMBIC_INCLUDE_DIR ${ALEMBIC}/include)
+ set(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR})
+ set(ALEMBIC_LIBPATH ${ALEMBIC}/lib)
+ set(ALEMBIC_LIBRARIES Alembic)
+ endif()
+
if(WITH_OPENSUBDIV)
set(OPENSUBDIV ${LIBDIR}/opensubdiv)
set(OPENSUBDIV_LIBPATH ${OPENSUBDIV}/lib)
@@ -2452,6 +2507,11 @@ if(WITH_CYCLES)
)
endif()
endif()
+
+ if(WITH_CYCLES_OPENSUBDIV AND NOT WITH_OPENSUBDIV)
+ message(STATUS "WITH_CYCLES_OPENSUBDIV requires WITH_OPENSUBDIV to be ON, turning OFF")
+ set(WITH_CYCLES_OPENSUBDIV OFF)
+ endif()
endif()
if(WITH_INTERNATIONAL)
@@ -2504,8 +2564,6 @@ else()
endif()
unset(_SYSTEM_BIG_ENDIAN)
endif()
-
-
if(WITH_IMAGE_OPENJPEG)
if(WITH_SYSTEM_OPENJPEG)
# dealt with above
@@ -3215,6 +3273,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_FREESTYLE)
info_cfg_option(WITH_OPENCOLORIO)
info_cfg_option(WITH_OPENVDB)
+ info_cfg_option(WITH_ALEMBIC)
info_cfg_text("Compiler Options:")
info_cfg_option(WITH_BUILDINFO)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 068ac665623..7230fc47105 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -29,13 +29,13 @@ getopt \
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,\
force-all,force-python,force-numpy,force-boost,\
force-ocio,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
-force-ffmpeg,force-opencollada,\
+force-ffmpeg,force-opencollada,force-alembic,\
build-all,build-python,build-numpy,build-boost,\
build-ocio,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\
-build-ffmpeg,build-opencollada,\
+build-ffmpeg,build-opencollada,build-alembic,\
skip-python,skip-numpy,skip-boost,\
skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\
-skip-ffmpeg,skip-opencollada \
+skip-ffmpeg,skip-opencollada,skip-alembic \
-- "$@" \
)
@@ -167,6 +167,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--build-openvdb
Force the build of OpenVDB.
+ --build-alembic
+ Force the build of Alembic.
+
--build-opencollada
Force the build of OpenCOLLADA.
@@ -216,6 +219,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--force-openvdb
Force the rebuild of OpenVDB.
+ --force-alembic
+ Force the rebuild of Alembic.
+
--force-opencollada
Force the rebuild of OpenCOLLADA.
@@ -258,6 +264,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--skip-openvdb
Unconditionally skip OpenVDB installation/building.
+ --skip-alembic
+ Unconditionally skip Alembic installation/building.
+
--skip-opencollada
Unconditionally skip OpenCOLLADA installation/building.
@@ -343,6 +352,13 @@ OPENVDB_FORCE_BUILD=false
OPENVDB_FORCE_REBUILD=false
OPENVDB_SKIP=false
+# Alembic needs to be compiled for now
+ALEMBIC_VERSION="1.6.0"
+ALEMBIC_VERSION_MIN=$ALEMBIC_VERSION
+ALEMBIC_FORCE_BUILD=false
+ALEMBIC_FORCE_REBUILD=false
+ALEMBIC_SKIP=false
+
# Version??
OPENCOLLADA_VERSION="1.3"
OPENCOLLADA_FORCE_BUILD=false
@@ -525,6 +541,7 @@ while true; do
OPENVDB_FORCE_BUILD=true
OPENCOLLADA_FORCE_BUILD=true
FFMPEG_FORCE_BUILD=true
+ ALEMBIC_FORCE_BUILD=true
shift; continue
;;
--build-python)
@@ -567,6 +584,9 @@ while true; do
--build-ffmpeg)
FFMPEG_FORCE_BUILD=true; shift; continue
;;
+ --build-alembic)
+ ALEMBIC_FORCE_BUILD=true; shift; continue
+ ;;
--force-all)
PYTHON_FORCE_REBUILD=true
NUMPY_FORCE_REBUILD=true
@@ -580,6 +600,7 @@ while true; do
OPENVDB_FORCE_REBUILD=true
OPENCOLLADA_FORCE_REBUILD=true
FFMPEG_FORCE_REBUILD=true
+ ALEMBIC_FORCE_REBUILD=true
shift; continue
;;
--force-python)
@@ -620,6 +641,9 @@ while true; do
--force-ffmpeg)
FFMPEG_FORCE_REBUILD=true; shift; continue
;;
+ --force-alembic)
+ ALEMBIC_FORCE_REBUILD=true; shift; continue
+ ;;
--skip-python)
PYTHON_SKIP=true; shift; continue
;;
@@ -656,6 +680,9 @@ while true; do
--skip-ffmpeg)
FFMPEG_SKIP=true; shift; continue
;;
+ --skip-alembic)
+ ALEMBIC_SKIP=true; shift; continue
+ ;;
--)
# no more arguments to parse
break
@@ -683,7 +710,7 @@ NUMPY_SOURCE=( "http://sourceforge.net/projects/numpy/files/NumPy/$NUMPY_VERSION
_boost_version_nodots=`echo "$BOOST_VERSION" | sed -r 's/\./_/g'`
BOOST_SOURCE=( "http://sourceforge.net/projects/boost/files/boost/$BOOST_VERSION/boost_$_boost_version_nodots.tar.bz2/download" )
-BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams"
+BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams --with-python --with-program_options"
OCIO_SOURCE=( "https://github.com/imageworks/OpenColorIO/tarball/v$OCIO_VERSION" )
@@ -727,6 +754,12 @@ OPENVDB_SOURCE=( "https://github.com/dreamworksanimation/openvdb/archive/v${OPEN
#~ OPENVDB_SOURCE_REPO_UID="404659fffa659da075d1c9416e4fc939139a84ee"
#~ OPENVDB_SOURCE_REPO_BRANCH="dev"
+ALEMBIC_USE_REPO=false
+ALEMBIC_SOURCE=( "https://github.com/alembic/alembic/archive/${ALEMBIC_VERSION}.tar.gz" )
+# ALEMBIC_SOURCE_REPO=( "https://github.com/alembic/alembic.git" )
+# ALEMBIC_SOURCE_REPO_UID="e6c90d4faa32c4550adeaaf3f556dad4b73a92bb"
+# ALEMBIC_SOURCE_REPO_BRANCH="master"
+
OPENCOLLADA_SOURCE=( "https://github.com/KhronosGroup/OpenCOLLADA.git" )
OPENCOLLADA_REPO_UID="3335ac164e68b2512a40914b14c74db260e6ff7d"
OPENCOLLADA_REPO_BRANCH="master"
@@ -767,7 +800,8 @@ You may also want to build them yourself (optional ones are [between brackets]):
* [OpenShadingLanguage $OSL_VERSION_MIN] (from $OSL_SOURCE_REPO, branch $OSL_SOURCE_REPO_BRANCH, commit $OSL_SOURCE_REPO_UID).
* [OpenSubDiv $OSD_VERSION_MIN] (from $OSD_SOURCE_REPO, branch $OSD_SOURCE_REPO_BRANCH, commit $OSD_SOURCE_REPO_UID).
* [OpenVDB $OPENVDB_VERSION_MIN] (from $OPENVDB_SOURCE), [Blosc $OPENVDB_BLOSC_VERSION] (from $OPENVDB_BLOSC_SOURCE).
- * [OpenCollada] (from $OPENCOLLADA_SOURCE, branch $OPENCOLLADA_REPO_BRANCH, commit $OPENCOLLADA_REPO_UID).\""
+ * [OpenCollada] (from $OPENCOLLADA_SOURCE, branch $OPENCOLLADA_REPO_BRANCH, commit $OPENCOLLADA_REPO_UID).
+ * [Alembic $ALEMBIC_VERSION] (from $ALEMBIC_SOURCE).\""
if [ "$DO_SHOW_DEPS" = true ]; then
PRINT ""
@@ -1118,7 +1152,7 @@ compile_Boost() {
fi
# To be changed each time we make edits that would modify the compiled result!
- boost_magic=10
+ boost_magic=11
_init_boost
@@ -2138,6 +2172,102 @@ compile_OPENVDB() {
run_ldconfig "openvdb"
}
+#### Build Alembic ####
+_init_alembic() {
+ _src=$SRC/alembic-$ALEMBIC_VERSION
+ _git=false
+ _inst=$INST/alembic-$ALEMBIC_VERSION
+ _inst_shortcut=$INST/alembic
+}
+
+clean_ALEMBIC() {
+ _init_alembic
+ _clean
+}
+
+compile_ALEMBIC() {
+ if [ "$NO_BUILD" = true ]; then
+ WARNING "--no-build enabled, Alembic will not be compiled!"
+ return
+ fi
+
+ compile_HDF5
+ PRINT ""
+
+ # To be changed each time we make edits that would modify the compiled result!
+ alembic_magic=2
+ _init_alembic
+
+ # Clean install if needed!
+ magic_compile_check alembic-$ALEMBIC_VERSION $alembic_magic
+ if [ $? -eq 1 -o "$ALEMBIC_FORCE_REBUILD" = true ]; then
+ clean_ALEMBIC
+ fi
+
+ if [ ! -d $_inst ]; then
+ INFO "Building Alembic-$ALEMBIC_VERSION"
+
+ prepare_opt
+
+ if [ ! -d $_src -o true ]; then
+ mkdir -p $SRC
+ download ALEMBIC_SOURCE[@] "$_src.tar.gz"
+
+ INFO "Unpacking Alembic-$ALEMBIC_VERSION"
+ tar -C $SRC -xf $_src.tar.gz
+ fi
+
+ cd $_src
+
+ cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst"
+
+ if [ -d $INST/boost ]; then
+ cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost"
+ cmake_d="$cmake_d -D USE_STATIC_BOOST=ON"
+ else
+ cmake_d="$cmake_d -D USE_STATIC_BOOST=OFF"
+ fi
+
+ if [ "$_with_built_openexr" = true ]; then
+ cmake_d="$cmake_d -D ILMBASE_ROOT=$INST/openexr"
+ cmake_d="$cmake_d -D USE_ARNOLD=OFF"
+ cmake_d="$cmake_d -D USE_BINARIES=OFF"
+ cmake_d="$cmake_d -D USE_EXAMPLES=OFF"
+ cmake_d="$cmake_d -D USE_HDF5=OFF"
+ cmake_d="$cmake_d -D USE_MAYA=OFF"
+ cmake_d="$cmake_d -D USE_PRMAN=OFF"
+ cmake_d="$cmake_d -D USE_PYALEMBIC=OFF"
+ cmake_d="$cmake_d -D USE_STATIC_HDF5=OFF"
+ cmake_d="$cmake_d -D ALEMBIC_ILMBASE_LINK_STATIC=OFF"
+ cmake_d="$cmake_d -D ALEMBIC_SHARED_LIBS=OFF"
+ cmake_d="$cmake_d -D ALEMBIC_LIB_USES_BOOST=ON"
+ cmake_d="$cmake_d -D ALEMBIC_LIB_USES_TR1=OFF"
+ INFO "ILMBASE_ROOT=$INST/openexr"
+ fi
+
+ cmake $cmake_d ./
+ make -j$THREADS install
+ make clean
+
+ if [ -d $_inst ]; then
+ _create_inst_shortcut
+ else
+ ERROR "Alembic-$ALEMBIC_VERSION failed to compile, exiting"
+ exit 1
+ fi
+
+ magic_compile_set alembic-$ALEMBIC_VERSION $alembic_magic
+
+ cd $CWD
+ INFO "Done compiling Alembic-$ALEMBIC_VERSION!"
+ else
+ INFO "Own Alembic-$ALEMBIC_VERSION is up to date, nothing to do!"
+ INFO "If you want to force rebuild of this lib, use the --force-alembic option."
+ fi
+
+ run_ldconfig "alembic"
+}
+
#### Build OpenCOLLADA ####
_init_opencollada() {
_src=$SRC/OpenCOLLADA-$OPENCOLLADA_VERSION
@@ -2746,6 +2876,17 @@ install_DEB() {
fi
fi
+ PRINT ""
+ if [ "$ALEMBIC_SKIP" = true ]; then
+ WARNING "Skipping Alembic installation, as requested..."
+ elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then
+ INFO "Forced Alembic building, as requested..."
+ compile_ALEMBIC
+ else
+ # No package currently, only HDF5!
+ compile_ALEMBIC
+ fi
+
if [ "$WITH_OPENCOLLADA" = true ]; then
_do_compile_collada=false
@@ -3283,6 +3424,17 @@ install_RPM() {
compile_OPENVDB
fi
+ PRINT ""
+ if [ "$ALEMBIC_SKIP" = true ]; then
+ WARNING "Skipping Alembic installation, as requested..."
+ elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then
+ INFO "Forced Alembic building, as requested..."
+ compile_ALEMBIC
+ else
+ # No package currently!
+ compile_ALEMBIC
+ fi
+
if [ "$WITH_OPENCOLLADA" = true ]; then
PRINT ""
@@ -3693,6 +3845,16 @@ install_ARCH() {
fi
fi
+ PRINT ""
+ if [ "$ALEMBIC_SKIP" = true ]; then
+ WARNING "Skipping Alembic installation, as requested..."
+ elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then
+ INFO "Forced Alembic building, as requested..."
+ compile_ALEMBIC
+ else
+ compile_ALEMBIC
+ fi
+
if [ "$WITH_OPENCOLLADA" = true ]; then
PRINT ""
@@ -4000,7 +4162,7 @@ print_info() {
_buildargs="-U *SNDFILE* -U *PYTHON* -U *BOOST* -U *Boost*"
_buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CYCLES*"
- _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG*"
+ _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC*"
_1="-D WITH_CODEC_SNDFILE=ON"
PRINT " $_1"
@@ -4106,6 +4268,17 @@ print_info() {
_buildargs="$_buildargs $_1"
fi
+ if [ "$ALEMBIC_SKIP" = false ]; then
+ _1="-D WITH_ALEMBIC=ON"
+ PRINT " $_1"
+ _buildargs="$_buildargs $_1"
+ if [ -d $INST/alembic ]; then
+ _1="-D ALEMBIC_ROOT_DIR=$INST/alembic"
+ PRINT " $_1"
+ _buildargs="$_buildargs $_1"
+ fi
+ fi
+
if [ "$NO_SYSTEM_GLEW" = true ]; then
_1="-D WITH_SYSTEM_GLEW=OFF"
PRINT " $_1"
diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/slave_compile.py
index 5e06c7057ce..d763ddfb3e0 100644
--- a/build_files/buildbot/slave_compile.py
+++ b/build_files/buildbot/slave_compile.py
@@ -73,6 +73,7 @@ if 'cmake' in builder:
if builder.endswith('x86_64_10_6_cmake'):
cmake_extra_options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64')
cmake_extra_options.append('-DCUDA_NVCC_EXECUTABLE=/usr/local/cuda-hack/bin/nvcc')
+ cmake_extra_options.append('-DCUDA_NVCC8_EXECUTABLE=/usr/local/cuda8-hack/bin/nvcc')
elif builder.startswith('win'):
if builder.endswith('_vc2015'):
@@ -89,6 +90,8 @@ if 'cmake' in builder:
elif builder.startswith('win32'):
bits = 32
cmake_options.extend(['-G', 'Visual Studio 12 2013'])
+ cmake_extra_options.append('-DCUDA_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v7.5/bin/nvcc.exe')
+ cmake_extra_options.append('-DCUDA_NVCC8_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0/bin/nvcc.exe')
elif builder.startswith('linux'):
tokens = builder.split("_")
@@ -108,10 +111,14 @@ if 'cmake' in builder:
cuda_chroot_name = 'buildbot_' + deb_name + '_x86_64'
targets = ['player', 'blender', 'cuda']
+ cmake_extra_options.append('-DCUDA_NVCC_EXECUTABLE=/usr/local/cuda-7.5/bin/nvcc')
+ cmake_extra_options.append('-DCUDA_NVCC8_EXECUTABLE=/usr/local/cuda-8.0/bin/nvcc')
+
cmake_options.append("-C" + os.path.join(blender_dir, cmake_config_file))
# Prepare CMake options needed to configure cuda binaries compilation.
cuda_cmake_options.append("-DWITH_CYCLES_CUDA_BINARIES=%s" % ('ON' if build_cubins else 'OFF'))
+ cuda_cmake_options.append("-DCYCLES_CUDA_BINARIES_ARCH=sm_20;sm_21;sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61")
if build_cubins or 'cuda' in targets:
if bits == 32:
cuda_cmake_options.append("-DCUDA_64_BIT_DEVICE_CODE=OFF")
diff --git a/build_files/cmake/Modules/FindAlembic.cmake b/build_files/cmake/Modules/FindAlembic.cmake
new file mode 100644
index 00000000000..1f61b5ef462
--- /dev/null
+++ b/build_files/cmake/Modules/FindAlembic.cmake
@@ -0,0 +1,70 @@
+# - Find Alembic library
+# Find the native Alembic includes and libraries
+# This module defines
+# ALEMBIC_INCLUDE_DIRS, where to find Alembic headers, Set when
+# ALEMBIC_INCLUDE_DIR is found.
+# ALEMBIC_LIBRARIES, libraries to link against to use Alembic.
+# ALEMBIC_ROOT_DIR, The base directory to search for Alembic.
+# This can also be an environment variable.
+# ALEMBIC_FOUND, If false, do not try to use Alembic.
+#
+
+#=============================================================================
+# Copyright 2016 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If ALEMBIC_ROOT_DIR was defined in the environment, use it.
+IF(NOT ALEMBIC_ROOT_DIR AND NOT $ENV{ALEMBIC_ROOT_DIR} STREQUAL "")
+ SET(ALEMBIC_ROOT_DIR $ENV{ALEMBIC_ROOT_DIR})
+ENDIF()
+
+SET(_alembic_SEARCH_DIRS
+ ${ALEMBIC_ROOT_DIR}
+ /usr/local
+ /sw # Fink
+ /opt/local # DarwinPorts
+ /opt/csw # Blastwave
+ /opt/lib/alembic
+)
+
+FIND_PATH(ALEMBIC_INCLUDE_DIR
+ NAMES
+ Alembic/Abc/All.h
+ HINTS
+ ${_alembic_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+)
+
+FIND_LIBRARY(ALEMBIC_LIBRARY
+ NAMES
+ Alembic
+ HINTS
+ ${_alembic_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib lib/static
+)
+
+# handle the QUIETLY and REQUIRED arguments and set ALEMBIC_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(ALEMBIC DEFAULT_MSG ALEMBIC_LIBRARY ALEMBIC_INCLUDE_DIR)
+
+IF(ALEMBIC_FOUND)
+ SET(ALEMBIC_LIBRARIES ${ALEMBIC_LIBRARY})
+ SET(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR})
+ENDIF(ALEMBIC_FOUND)
+
+MARK_AS_ADVANCED(
+ ALEMBIC_INCLUDE_DIR
+ ALEMBIC_LIBRARY
+)
+
+UNSET(_alembic_SEARCH_DIRS)
diff --git a/build_files/cmake/Modules/FindHDF5.cmake b/build_files/cmake/Modules/FindHDF5.cmake
new file mode 100644
index 00000000000..56ceda8fb5e
--- /dev/null
+++ b/build_files/cmake/Modules/FindHDF5.cmake
@@ -0,0 +1,69 @@
+# - Find HDF5 library
+# Find the native HDF5 includes and libraries
+# This module defines
+# HDF5_INCLUDE_DIRS, where to find hdf5.h, Set when HDF5_INCLUDE_DIR is found.
+# HDF5_LIBRARIES, libraries to link against to use HDF5.
+# HDF5_ROOT_DIR, The base directory to search for HDF5.
+# This can also be an environment variable.
+# HDF5_FOUND, If false, do not try to use HDF5.
+#
+
+#=============================================================================
+# Copyright 2016 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If HDF5_ROOT_DIR was defined in the environment, use it.
+IF(NOT HDF5_ROOT_DIR AND NOT $ENV{HDF5_ROOT_DIR} STREQUAL "")
+ SET(HDF5_ROOT_DIR $ENV{HDF5_ROOT_DIR})
+ENDIF()
+
+SET(_hdf5_SEARCH_DIRS
+ ${HDF5_ROOT_DIR}
+ /usr/local
+ /sw # Fink
+ /opt/local # DarwinPorts
+ /opt/csw # Blastwave
+ /opt/lib/hdf5
+)
+
+FIND_LIBRARY(HDF5_LIBRARY
+ NAMES
+ hdf5
+ HINTS
+ ${_hdf5_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib
+)
+
+FIND_PATH(HDF5_INCLUDE_DIR
+ NAMES
+ hdf5.h
+ HINTS
+ ${_hdf5_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+)
+
+# handle the QUIETLY and REQUIRED arguments and set HDF5_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(HDF5 DEFAULT_MSG HDF5_LIBRARY HDF5_INCLUDE_DIR)
+
+IF(HDF5_FOUND)
+ SET(HDF5_LIBRARIES ${HDF5_LIBRARY})
+ SET(HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIR})
+ENDIF(HDF5_FOUND)
+
+MARK_AS_ADVANCED(
+ HDF5_INCLUDE_DIR
+ HDF5_LIBRARY
+)
+
+UNSET(_hdf5_SEARCH_DIRS)
diff --git a/build_files/cmake/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake
index 3ccca1f199c..96c06ef8eb5 100644
--- a/build_files/cmake/Modules/GTestTesting.cmake
+++ b/build_files/cmake/Modules/GTestTesting.cmake
@@ -23,6 +23,7 @@ macro(BLENDER_SRC_GTEST_EX NAME SRC EXTRA_LIBS DO_ADD_TEST)
${CMAKE_SOURCE_DIR}/extern/glog/src
${CMAKE_SOURCE_DIR}/extern/gflags/src
${CMAKE_SOURCE_DIR}/extern/gtest/include
+ ${CMAKE_SOURCE_DIR}/extern/gmock/include
)
unset(_current_include_directories)
@@ -33,6 +34,7 @@ macro(BLENDER_SRC_GTEST_EX NAME SRC EXTRA_LIBS DO_ADD_TEST)
bf_testing_main
bf_intern_guardedalloc
extern_gtest
+ extern_gmock
# needed for glog
${PTHREADS_LIBRARIES}
extern_glog
diff --git a/build_files/cmake/clang_array_check.py b/build_files/cmake/clang_array_check.py
index 9f22a9c43c1..6eaaebce52f 100644
--- a/build_files/cmake/clang_array_check.py
+++ b/build_files/cmake/clang_array_check.py
@@ -239,8 +239,8 @@ def file_check_arg_sizes(tu):
if 0:
print("---",
" <~> ".join(
- [" ".join([t.spelling for t in C.get_tokens()])
- for C in node.get_children()]
+ [" ".join([t.spelling for t in C.get_tokens()])
+ for C in node.get_children()]
))
# print(node.location)
diff --git a/build_files/cmake/cmake_consistency_check.py b/build_files/cmake/cmake_consistency_check.py
index 6cd66b7640c..3d06790758a 100755
--- a/build_files/cmake/cmake_consistency_check.py
+++ b/build_files/cmake/cmake_consistency_check.py
@@ -29,11 +29,11 @@ if not sys.version.startswith("3"):
sys.exit(1)
from cmake_consistency_check_config import (
- IGNORE,
- UTF8_CHECK,
- SOURCE_DIR,
- BUILD_DIR,
- )
+ IGNORE,
+ UTF8_CHECK,
+ SOURCE_DIR,
+ BUILD_DIR,
+)
import os
diff --git a/build_files/cmake/cmake_consistency_check_config.py b/build_files/cmake/cmake_consistency_check_config.py
index 7e7cd40dbea..9ec1c095a9c 100644
--- a/build_files/cmake/cmake_consistency_check_config.py
+++ b/build_files/cmake/cmake_consistency_check_config.py
@@ -31,7 +31,7 @@ IGNORE = (
"extern/carve/patches/files/random.h",
"intern/audaspace/SRC/AUD_SRCResampleFactory.h",
"intern/audaspace/SRC/AUD_SRCResampleReader.h",
- )
+)
UTF8_CHECK = True
diff --git a/build_files/cmake/cmake_netbeans_project.py b/build_files/cmake/cmake_netbeans_project.py
index 8d66c101c6a..5b074b6975a 100755
--- a/build_files/cmake/cmake_netbeans_project.py
+++ b/build_files/cmake/cmake_netbeans_project.py
@@ -37,19 +37,19 @@ if not project_info.init(sys.argv[-1]):
sys.exit(1)
from project_info import (
- SIMPLE_PROJECTFILE,
- SOURCE_DIR,
- CMAKE_DIR,
- PROJECT_DIR,
- source_list,
- is_project_file,
- is_c_header,
- # is_py,
- cmake_advanced_info,
- cmake_compiler_defines,
- cmake_cache_var,
- project_name_get,
- )
+ SIMPLE_PROJECTFILE,
+ SOURCE_DIR,
+ CMAKE_DIR,
+ PROJECT_DIR,
+ source_list,
+ is_project_file,
+ is_c_header,
+ # is_py,
+ cmake_advanced_info,
+ cmake_compiler_defines,
+ cmake_cache_var,
+ project_name_get,
+)
import os
diff --git a/build_files/cmake/cmake_qtcreator_project.py b/build_files/cmake/cmake_qtcreator_project.py
index 9c0a02a7a0c..231784df588 100755
--- a/build_files/cmake/cmake_qtcreator_project.py
+++ b/build_files/cmake/cmake_qtcreator_project.py
@@ -43,17 +43,17 @@ def quote_define(define):
def create_qtc_project_main(name):
from project_info import (
- SIMPLE_PROJECTFILE,
- SOURCE_DIR,
- # CMAKE_DIR,
- PROJECT_DIR,
- source_list,
- is_project_file,
- is_c_header,
- cmake_advanced_info,
- cmake_compiler_defines,
- project_name_get,
- )
+ SIMPLE_PROJECTFILE,
+ SOURCE_DIR,
+ # CMAKE_DIR,
+ PROJECT_DIR,
+ source_list,
+ is_project_file,
+ is_c_header,
+ cmake_advanced_info,
+ cmake_compiler_defines,
+ project_name_get,
+ )
files = list(source_list(SOURCE_DIR, filename_check=is_project_file))
files_rel = [os.path.relpath(f, start=PROJECT_DIR) for f in files]
@@ -69,7 +69,7 @@ def create_qtc_project_main(name):
with open(os.path.join(PROJECT_DIR, "%s.includes" % FILE_NAME), 'w') as f:
f.write("\n".join(sorted(list(set(os.path.dirname(f)
- for f in files_rel if is_c_header(f))))))
+ for f in files_rel if is_c_header(f))))))
qtc_prj = os.path.join(PROJECT_DIR, "%s.creator" % FILE_NAME)
with open(qtc_prj, 'w') as f:
@@ -87,7 +87,7 @@ def create_qtc_project_main(name):
# for some reason it doesnt give all internal includes
includes = list(set(includes) | set(os.path.dirname(f)
- for f in files_rel if is_c_header(f)))
+ for f in files_rel if is_c_header(f)))
includes.sort()
# be tricky, get the project name from CMake if we can!
@@ -125,13 +125,13 @@ def create_qtc_project_main(name):
def create_qtc_project_python(name):
from project_info import (
- SOURCE_DIR,
- # CMAKE_DIR,
- PROJECT_DIR,
- source_list,
- is_py,
- project_name_get,
- )
+ SOURCE_DIR,
+ # CMAKE_DIR,
+ PROJECT_DIR,
+ source_list,
+ is_py,
+ project_name_get,
+ )
files = list(source_list(SOURCE_DIR, filename_check=is_py))
files_rel = [os.path.relpath(f, start=PROJECT_DIR) for f in files]
@@ -161,24 +161,24 @@ def argparse_create():
import argparse
parser = argparse.ArgumentParser(
- description="This script generates Qt Creator project files for Blender",
- )
+ description="This script generates Qt Creator project files for Blender",
+ )
parser.add_argument(
- "-n", "--name",
- dest="name",
- metavar='NAME', type=str,
- help="Override default project name (\"Blender\")",
- required=False,
- )
+ "-n", "--name",
+ dest="name",
+ metavar='NAME', type=str,
+ help="Override default project name (\"Blender\")",
+ required=False,
+ )
parser.add_argument(
- "-b", "--build-dir",
- dest="build_dir",
- metavar='BUILD_DIR', type=str,
- help="Specify the build path (or fallback to the $PWD)",
- required=False,
- )
+ "-b", "--build-dir",
+ dest="build_dir",
+ metavar='BUILD_DIR', type=str,
+ help="Specify the build path (or fallback to the $PWD)",
+ required=False,
+ )
return parser
diff --git a/build_files/cmake/cmake_static_check_clang_array.py b/build_files/cmake/cmake_static_check_clang_array.py
index 597d1d2b980..7b3b73985fa 100644
--- a/build_files/cmake/cmake_static_check_clang_array.py
+++ b/build_files/cmake/cmake_static_check_clang_array.py
@@ -32,7 +32,7 @@ USE_QUIET = (os.environ.get("QUIET", None) is not None)
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
- ]
+]
CHECKER_BIN = "python2"
@@ -42,7 +42,7 @@ CHECKER_ARGS = [
"-I" + os.path.join(project_source_info.SOURCE_DIR, "extern", "glew", "include"),
# stupid but needed
"-Dbool=char"
- ]
+]
def main():
diff --git a/build_files/cmake/cmake_static_check_cppcheck.py b/build_files/cmake/cmake_static_check_cppcheck.py
index 3504b005adc..ef4ee71dec2 100644
--- a/build_files/cmake/cmake_static_check_cppcheck.py
+++ b/build_files/cmake/cmake_static_check_cppcheck.py
@@ -32,7 +32,7 @@ USE_QUIET = (os.environ.get("QUIET", None) is not None)
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
- ]
+]
CHECKER_BIN = "cppcheck"
@@ -43,7 +43,7 @@ CHECKER_ARGS = [
"--max-configs=1", # speeds up execution
# "--check-config", # when includes are missing
"--enable=all", # if you want sixty hundred pedantic suggestions
- ]
+]
if USE_QUIET:
CHECKER_ARGS.append("--quiet")
diff --git a/build_files/cmake/cmake_static_check_smatch.py b/build_files/cmake/cmake_static_check_smatch.py
index f525187e22c..29afdb79819 100644
--- a/build_files/cmake/cmake_static_check_smatch.py
+++ b/build_files/cmake/cmake_static_check_smatch.py
@@ -25,13 +25,13 @@
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
- ]
+]
CHECKER_BIN = "smatch"
CHECKER_ARGS = [
"--full-path",
"--two-passes",
- ]
+]
import project_source_info
import subprocess
diff --git a/build_files/cmake/cmake_static_check_sparse.py b/build_files/cmake/cmake_static_check_sparse.py
index 6d1c4e3d6a2..600370cca25 100644
--- a/build_files/cmake/cmake_static_check_sparse.py
+++ b/build_files/cmake/cmake_static_check_sparse.py
@@ -25,11 +25,11 @@
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
- ]
+]
CHECKER_BIN = "sparse"
CHECKER_ARGS = [
- ]
+]
import project_source_info
import subprocess
diff --git a/build_files/cmake/cmake_static_check_splint.py b/build_files/cmake/cmake_static_check_splint.py
index 46d8808e89d..069d1b7ff25 100644
--- a/build_files/cmake/cmake_static_check_splint.py
+++ b/build_files/cmake/cmake_static_check_splint.py
@@ -25,7 +25,7 @@
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
- ]
+]
CHECKER_BIN = "splint"
@@ -61,7 +61,7 @@ CHECKER_ARGS = [
# dummy, witjout this splint complains with:
# /usr/include/bits/confname.h:31:27: *** Internal Bug at cscannerHelp.c:2428: Unexpanded macro not function or constant: int _PC_MAX_CANON
"-D_PC_MAX_CANON=0",
- ]
+]
import project_source_info
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index b50b42416fb..634d4f431d4 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -4,6 +4,7 @@
# cmake -C../blender/build_files/cmake/config/blender_full.cmake ../blender
#
+set(WITH_ALEMBIC ON CACHE BOOL "" FORCE)
set(WITH_BUILDINFO ON CACHE BOOL "" FORCE)
set(WITH_BULLET ON CACHE BOOL "" FORCE)
set(WITH_CODEC_AVI ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index 3c53ee7ae23..46b7d48b494 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -8,6 +8,7 @@
set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE)
set(WITH_SYSTEM_GLEW ON CACHE BOOL "" FORCE)
+set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE)
set(WITH_BUILDINFO OFF CACHE BOOL "" FORCE)
set(WITH_BULLET OFF CACHE BOOL "" FORCE)
set(WITH_CODEC_AVI OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/bpy_module.cmake b/build_files/cmake/config/bpy_module.cmake
index 41140151f04..854d6e49370 100644
--- a/build_files/cmake/config/bpy_module.cmake
+++ b/build_files/cmake/config/bpy_module.cmake
@@ -32,3 +32,4 @@ set(WITH_OPENCOLLADA OFF CACHE BOOL "" FORCE)
set(WITH_INTERNATIONAL OFF CACHE BOOL "" FORCE)
set(WITH_BULLET OFF CACHE BOOL "" FORCE)
set(WITH_OPENVDB OFF CACHE BOOL "" FORCE)
+set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/example_scripts/make_quicky.py b/build_files/cmake/example_scripts/make_quicky.py
index 76d4df32f86..49c284e354d 100755
--- a/build_files/cmake/example_scripts/make_quicky.py
+++ b/build_files/cmake/example_scripts/make_quicky.py
@@ -74,7 +74,7 @@ def main():
"rebuild_cache",
"depend",
"cmake_check_build_system",
- ])
+ ])
targets -= set(bad)
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index eb4282fd1d9..4e366b65216 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -333,6 +333,11 @@ function(SETUP_LIBDIRS)
link_directories(${LLVM_LIBPATH})
endif()
+ if(WITH_ALEMBIC)
+ link_directories(${ALEMBIC_LIBPATH})
+ link_directories(${HDF5_LIBPATH})
+ endif()
+
if(WIN32 AND NOT UNIX)
link_directories(${PTHREADS_LIBPATH})
endif()
@@ -434,6 +439,9 @@ function(setup_liblinks
endif()
endif()
target_link_libraries(${target} ${JPEG_LIBRARIES})
+ if(WITH_ALEMBIC)
+ target_link_libraries(${target} ${ALEMBIC_LIBRARIES} ${HDF5_LIBRARIES})
+ endif()
if(WITH_IMAGE_OPENEXR)
target_link_libraries(${target} ${OPENEXR_LIBRARIES})
endif()
@@ -610,6 +618,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
bf_imbuf_openimageio
bf_imbuf_dds
bf_collada
+ bf_alembic
bf_intern_elbeem
bf_intern_memutil
bf_intern_guardedalloc
diff --git a/build_files/cmake/project_info.py b/build_files/cmake/project_info.py
index cfcd9df65c8..deea844034c 100755
--- a/build_files/cmake/project_info.py
+++ b/build_files/cmake/project_info.py
@@ -39,7 +39,7 @@ __all__ = (
"is_py",
"cmake_advanced_info",
"cmake_compiler_defines",
- "project_name_get"
+ "project_name_get",
"init",
)
@@ -214,7 +214,12 @@ def cmake_advanced_info():
def cmake_cache_var(var):
cache_file = open(join(CMAKE_DIR, "CMakeCache.txt"), encoding='utf-8')
- lines = [l_strip for l in cache_file for l_strip in (l.strip(),) if l_strip if not l_strip.startswith("//") if not l_strip.startswith("#")]
+ lines = [
+ l_strip for l in cache_file
+ for l_strip in (l.strip(),)
+ if l_strip
+ if not l_strip.startswith(("//", "#"))
+ ]
cache_file.close()
for l in lines:
diff --git a/build_files/cmake/project_source_info.py b/build_files/cmake/project_source_info.py
index 45d732c2422..3ba26513491 100644
--- a/build_files/cmake/project_source_info.py
+++ b/build_files/cmake/project_source_info.py
@@ -23,7 +23,7 @@
__all__ = (
"build_info",
"SOURCE_DIR",
- )
+)
import sys
diff --git a/build_files/package_spec/build_archive.py b/build_files/package_spec/build_archive.py
index 6a7ec8a0faa..bd00984c74b 100755
--- a/build_files/package_spec/build_archive.py
+++ b/build_files/package_spec/build_archive.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# <pep8 compliant>
diff --git a/doc/manpage/blender.1.py b/doc/manpage/blender.1.py
index cdd2d7ca2df..52856717b4a 100755
--- a/doc/manpage/blender.1.py
+++ b/doc/manpage/blender.1.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# ##### BEGIN GPL LICENSE BLOCK #####
#
diff --git a/doc/python_api/epy/IDProp.py b/doc/python_api/epy/IDProp.py
deleted file mode 100644
index 1fc26d7f65b..00000000000
--- a/doc/python_api/epy/IDProp.py
+++ /dev/null
@@ -1,132 +0,0 @@
-class IDGroup:
- """
- The IDGroup Type
- ================
- This type supports both iteration and the []
- operator to get child ID properties.
-
- You can also add new properties using the [] operator.
- For example::
-
- group['a float!'] = 0.0
- group['an int!'] = 0
- group['a string!'] = "hi!"
- group['an array!'] = [0, 0, 1.0, 0]
-
- group['a subgroup!] = {"float": 0.0, "an int": 1.0, "an array": [1, 2],
- "another subgroup": {"a": 0.0, "str": "bleh"}}
-
- Note that for arrays, the array type defaults to int unless a float is found
- while scanning the template list; if any floats are found, then the whole
- array is float. Note that double-precision floating point numbers are used for
- python-created float ID properties and arrays (though the internal C api does
- support single-precision floats, and the python code will read them).
-
- You can also delete properties with the del operator. For example:
-
- del group['property']
-
- To get the type of a property, use the type() operator, for example::
-
- if type(group['bleh']) == str: pass
-
- To tell if the property is a group or array type, import the Blender.Types module and test
- against IDGroupType and IDArrayType, like so::
-
- from Blender.Types import IDGroupType, IDArrayType.
-
- if type(group['bleghr']) == IDGroupType:
- (do something)
-
- @ivar name: The name of the property
- @type name: string
- """
-
- def pop(item):
- """
- Pop an item from the group property.
- @type item: string
- @param item: The item name.
- @rtype: can be dict, list, int, float or string.
- @return: The removed property.
- """
-
- def update(updatedict):
- """
- Updates items in the dict, similar to normal python
- dictionary method .update().
- @type updatedict: dict
- @param updatedict: A dict of simple types to derive updated/new IDProperties from.
- @rtype: None
- @return: None
- """
-
- def keys():
- """
- Returns a list of the keys in this property group.
- @rtype: list of strings.
- @return: a list of the keys in this property group.
- """
-
- def values():
- """
- Returns a list of the values in this property group.
-
- Note that unless a value is itself a property group or an array, you
- cannot change it by changing the values in this list, you must change them
- in the parent property group.
-
- For example,
-
- group['some_property'] = new_value
-
- . . .is correct, while,
-
- values = group.values()
- values[0] = new_value
-
- . . .is wrong.
-
- @rtype: list of strings.
- @return: a list of the values in this property group.
- """
-
- def iteritems():
- """
- Implements the python dictionary iteritmes method.
-
- For example::
-
- for k, v in group.iteritems():
- print "Property name: " + k
- print "Property value: " + str(v)
-
- @rtype: an iterator that spits out items of the form [key, value]
- @return: an iterator.
- """
-
- def convert_to_pyobject():
- """
- Converts the entire property group to a purely python form.
-
- @rtype: dict
- @return: A python dictionary representing the property group
- """
-
-class IDArray:
- """
- The IDArray Type
- ================
-
- @ivar type: returns the type of the array, can be either IDP_Int or IDP_Float
- """
-
- def __getitem__(index):
- pass
-
- def __setitem__(index, value):
- pass
-
- def __len__():
- pass
-
diff --git a/doc/python_api/sphinx_changelog_gen.py b/doc/python_api/sphinx_changelog_gen.py
index 0b083c31c58..4cbb418e326 100644
--- a/doc/python_api/sphinx_changelog_gen.py
+++ b/doc/python_api/sphinx_changelog_gen.py
@@ -48,7 +48,8 @@ python doc/python_api/sphinx_changelog_gen.py -- \
'''
{"module.name":
{"parent.class":
- {"basic_type", "member_name": ("Name", type, range, length, default, descr, f_args, f_arg_types, f_ret_types)}, ...
+ {"basic_type", "member_name":
+ ("Name", type, range, length, default, descr, f_args, f_arg_types, f_ret_types)}, ...
}, ...
}
'''
@@ -99,34 +100,34 @@ def api_dump():
prop_range = None
dump_class[prop_id] = (
- "prop_rna", # basic_type
- prop.name, # name
- prop_type, # type
- prop_range, # range
- prop_length, # length
- prop.default, # default
- prop.description, # descr
- Ellipsis, # f_args
- Ellipsis, # f_arg_types
- Ellipsis, # f_ret_types
- )
+ "prop_rna", # basic_type
+ prop.name, # name
+ prop_type, # type
+ prop_range, # range
+ prop_length, # length
+ prop.default, # default
+ prop.description, # descr
+ Ellipsis, # f_args
+ Ellipsis, # f_arg_types
+ Ellipsis, # f_ret_types
+ )
del props
# python props, tricky since we dont know much about them.
for prop_id, attr in struct_info.get_py_properties():
dump_class[prop_id] = (
- "prop_py", # basic_type
- Ellipsis, # name
- Ellipsis, # type
- Ellipsis, # range
- Ellipsis, # length
- Ellipsis, # default
- attr.__doc__, # descr
- Ellipsis, # f_args
- Ellipsis, # f_arg_types
- Ellipsis, # f_ret_types
- )
+ "prop_py", # basic_type
+ Ellipsis, # name
+ Ellipsis, # type
+ Ellipsis, # range
+ Ellipsis, # length
+ Ellipsis, # default
+ attr.__doc__, # descr
+ Ellipsis, # f_args
+ Ellipsis, # f_arg_types
+ Ellipsis, # f_ret_types
+ )
# kludge func -> props
funcs = [(func.identifier, func) for func in struct_info.functions]
@@ -137,17 +138,17 @@ def api_dump():
func_args_type = tuple([prop.type for prop in func.args])
dump_class[func_id] = (
- "func_rna", # basic_type
- Ellipsis, # name
- Ellipsis, # type
- Ellipsis, # range
- Ellipsis, # length
- Ellipsis, # default
- func.description, # descr
- func_args_ids, # f_args
- func_args_type, # f_arg_types
- func_ret_types, # f_ret_types
- )
+ "func_rna", # basic_type
+ Ellipsis, # name
+ Ellipsis, # type
+ Ellipsis, # range
+ Ellipsis, # length
+ Ellipsis, # default
+ func.description, # descr
+ func_args_ids, # f_args
+ func_args_type, # f_arg_types
+ func_ret_types, # f_ret_types
+ )
del funcs
# kludge func -> props
@@ -158,17 +159,17 @@ def api_dump():
func_args_ids = tuple(inspect.getargspec(attr).args)
dump_class[func_id] = (
- "func_py", # basic_type
- Ellipsis, # name
- Ellipsis, # type
- Ellipsis, # range
- Ellipsis, # length
- Ellipsis, # default
- attr.__doc__, # descr
- func_args_ids, # f_args
- Ellipsis, # f_arg_types
- Ellipsis, # f_ret_types
- )
+ "func_py", # basic_type
+ Ellipsis, # name
+ Ellipsis, # type
+ Ellipsis, # range
+ Ellipsis, # length
+ Ellipsis, # default
+ attr.__doc__, # descr
+ func_args_ids, # f_args
+ Ellipsis, # f_arg_types
+ Ellipsis, # f_ret_types
+ )
del funcs
import pprint
@@ -336,15 +337,19 @@ def main():
parser = argparse.ArgumentParser(description=usage_text, epilog=epilog)
- parser.add_argument("--dump", dest="dump", action='store_true',
- help="When set the api will be dumped into blender_version.py")
-
- parser.add_argument("--api_from", dest="api_from", metavar='FILE',
- help="File to compare from (previous version)")
- parser.add_argument("--api_to", dest="api_to", metavar='FILE',
- help="File to compare from (current)")
- parser.add_argument("--api_out", dest="api_out", metavar='FILE',
- help="Output sphinx changelog")
+ parser.add_argument(
+ "--dump", dest="dump", action='store_true',
+ help="When set the api will be dumped into blender_version.py")
+
+ parser.add_argument(
+ "--api_from", dest="api_from", metavar='FILE',
+ help="File to compare from (previous version)")
+ parser.add_argument(
+ "--api_to", dest="api_to", metavar='FILE',
+ help="File to compare from (current)")
+ parser.add_argument(
+ "--api_out", dest="api_out", metavar='FILE',
+ help="Output sphinx changelog")
args = parser.parse_args(argv) # In this example we wont use the args
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 74ff23ee2a3..2a3213e4e6f 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -24,7 +24,7 @@ SCRIPT_HELP_MSG = """
API dump in RST files
---------------------
- Run this script from blenders root path once you have compiled blender
+ Run this script from Blender's root path once you have compiled Blender
./blender.bin --background -noaudio --python doc/python_api/sphinx_doc_gen.py
@@ -61,14 +61,14 @@ Sphinx: PDF generation
"""
try:
- import bpy # blender module
+ import bpy # Blender module
except ImportError:
print("\nERROR: this script must run from inside Blender")
print(SCRIPT_HELP_MSG)
import sys
sys.exit()
-import rna_info # blender module
+import rna_info # Blender module
def rna_info_BuildRNAInfo_cache():
@@ -181,15 +181,13 @@ def handle_args():
dest="log",
default=False,
action='store_true',
- help=(
- "Log the output of the api dump and sphinx|latex "
- "warnings and errors (default=False).\n"
- "If given, save logs in:\n"
- "* OUTPUT_DIR/.bpy.log\n"
- "* OUTPUT_DIR/.sphinx-build.log\n"
- "* OUTPUT_DIR/.sphinx-build_pdf.log\n"
- "* OUTPUT_DIR/.latex_make.log",
- ),
+ help="Log the output of the api dump and sphinx|latex "
+ "warnings and errors (default=False).\n"
+ "If given, save logs in:\n"
+ "* OUTPUT_DIR/.bpy.log\n"
+ "* OUTPUT_DIR/.sphinx-build.log\n"
+ "* OUTPUT_DIR/.sphinx-build_pdf.log\n"
+ "* OUTPUT_DIR/.latex_make.log",
required=False)
# parse only the args passed after '--'
@@ -224,12 +222,12 @@ if not ARGS.partial:
FILTER_BPY_OPS = None
FILTER_BPY_TYPES = None
EXCLUDE_INFO_DOCS = False
- EXCLUDE_MODULES = ()
+ EXCLUDE_MODULES = []
else:
# can manually edit this too:
- #FILTER_BPY_OPS = ("import.scene", ) # allow
- #FILTER_BPY_TYPES = ("bpy_struct", "Operator", "ID") # allow
+ # FILTER_BPY_OPS = ("import.scene", ) # allow
+ # FILTER_BPY_TYPES = ("bpy_struct", "Operator", "ID") # allow
EXCLUDE_INFO_DOCS = True
EXCLUDE_MODULES = [
"aud",
@@ -262,6 +260,7 @@ else:
"bpy_extras",
"gpu",
"gpu.offscreen",
+ "idprop.types",
"mathutils",
"mathutils.bvhtree",
"mathutils.geometry",
@@ -275,7 +274,7 @@ else:
"freestyle.shaders",
"freestyle.types",
"freestyle.utils",
- ]
+ ]
# ------
# Filter
@@ -301,7 +300,9 @@ else:
del m
del fnmatch
- BPY_LOGGER.debug("Partial Doc Build, Skipping: %s\n" % "\n ".join(sorted(EXCLUDE_MODULES)))
+ BPY_LOGGER.debug(
+ "Partial Doc Build, Skipping: %s\n" %
+ "\n ".join(sorted(EXCLUDE_MODULES)))
#
# done filtering
@@ -311,19 +312,21 @@ try:
__import__("aud")
except ImportError:
BPY_LOGGER.debug("Warning: Built without 'aud' module, docs incomplete...")
- EXCLUDE_MODULES = list(EXCLUDE_MODULES) + ["aud"]
+ EXCLUDE_MODULES.append("aud")
try:
__import__("freestyle")
except ImportError:
BPY_LOGGER.debug("Warning: Built without 'freestyle' module, docs incomplete...")
- EXCLUDE_MODULES = list(EXCLUDE_MODULES) + ["freestyle",
- "freestyle.chainingiterators",
- "freestyle.functions",
- "freestyle.predicates",
- "freestyle.shaders",
- "freestyle.types",
- "freestyle.utils"]
+ EXCLUDE_MODULES.extend([
+ "freestyle",
+ "freestyle.chainingiterators",
+ "freestyle.functions",
+ "freestyle.predicates",
+ "freestyle.shaders",
+ "freestyle.types",
+ "freestyle.utils",
+ ])
# Source files we use, and need to copy to the OUTPUT_DIR
# to have working out-of-source builds.
@@ -340,7 +343,7 @@ EXTRA_SOURCE_FILES = (
"../examples/bge.texture.py",
"../examples/bmesh.ops.1.py",
"../examples/bpy.app.translations.py",
- )
+)
# examples
@@ -357,52 +360,59 @@ RST_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "rst"))
# extra info, not api reference docs
# stored in ./rst/info_*
INFO_DOCS = (
- ("info_quickstart.rst", "Blender/Python Quickstart: new to blender/scripting and want to get your feet wet?"),
- ("info_overview.rst", "Blender/Python API Overview: a more complete explanation of python integration"),
- ("info_tutorial_addon.rst", "Blender/Python Addon Tutorial: a step by step guide on how to write an addon from scratch"),
- ("info_api_reference.rst", "Blender/Python API Reference Usage: examples of how to use the API reference docs"),
- ("info_best_practice.rst", "Best Practice: Conventions to follow for writing good scripts"),
- ("info_tips_and_tricks.rst", "Tips and Tricks: Hints to help you while writing scripts for blender"),
- ("info_gotcha.rst", "Gotcha's: some of the problems you may come up against when writing scripts"),
- )
+ ("info_quickstart.rst",
+ "Blender/Python Quickstart: new to Blender/scripting and want to get your feet wet?"),
+ ("info_overview.rst",
+ "Blender/Python API Overview: a more complete explanation of Python integration"),
+ ("info_tutorial_addon.rst",
+ "Blender/Python Addon Tutorial: a step by step guide on how to write an addon from scratch"),
+ ("info_api_reference.rst",
+ "Blender/Python API Reference Usage: examples of how to use the API reference docs"),
+ ("info_best_practice.rst",
+ "Best Practice: Conventions to follow for writing good scripts"),
+ ("info_tips_and_tricks.rst",
+ "Tips and Tricks: Hints to help you while writing scripts for Blender"),
+ ("info_gotcha.rst",
+ "Gotcha's: some of the problems you may come up against when writing scripts"),
+)
# only support for properties atm.
RNA_BLACKLIST = {
# XXX messes up PDF!, really a bug but for now just workaround.
"UserPreferencesSystem": {"language", }
- }
+}
MODULE_GROUPING = {
"bmesh.types": (
- ("Base Mesh Type", '-'),
- "BMesh",
- ("Mesh Elements", '-'),
- "BMVert",
- "BMEdge",
- "BMFace",
- "BMLoop",
- ("Sequence Accessors", '-'),
- "BMElemSeq",
- "BMVertSeq",
- "BMEdgeSeq",
- "BMFaceSeq",
- "BMLoopSeq",
- "BMIter",
- ("Selection History", '-'),
- "BMEditSelSeq",
- "BMEditSelIter",
- ("Custom-Data Layer Access", '-'),
- "BMLayerAccessVert",
- "BMLayerAccessEdge",
- "BMLayerAccessFace",
- "BMLayerAccessLoop",
- "BMLayerCollection",
- "BMLayerItem",
- ("Custom-Data Layer Types", '-'),
- "BMLoopUV",
- "BMDeformVert"
- )
- }
+ ("Base Mesh Type", '-'),
+ "BMesh",
+ ("Mesh Elements", '-'),
+ "BMVert",
+ "BMEdge",
+ "BMFace",
+ "BMLoop",
+ ("Sequence Accessors", '-'),
+ "BMElemSeq",
+ "BMVertSeq",
+ "BMEdgeSeq",
+ "BMFaceSeq",
+ "BMLoopSeq",
+ "BMIter",
+ ("Selection History", '-'),
+ "BMEditSelSeq",
+ "BMEditSelIter",
+ ("Custom-Data Layer Access", '-'),
+ "BMLayerAccessVert",
+ "BMLayerAccessEdge",
+ "BMLayerAccessFace",
+ "BMLayerAccessLoop",
+ "BMLayerCollection",
+ "BMLayerItem",
+ ("Custom-Data Layer Types", '-'),
+ "BMLoopUV",
+ "BMDeformVert"
+ )
+}
# --------------------configure compile time options----------------------------
@@ -478,10 +488,10 @@ MethodDescriptorType = type(dict.get)
GetSetDescriptorType = type(int.real)
StaticMethodType = type(staticmethod(lambda: None))
from types import (
- MemberDescriptorType,
- MethodType,
- FunctionType,
- )
+ MemberDescriptorType,
+ MethodType,
+ FunctionType,
+)
_BPY_STRUCT_FAKE = "bpy_struct"
_BPY_PROP_COLLECTION_FAKE = "bpy_prop_collection"
@@ -501,7 +511,7 @@ escape_rst.trans = str.maketrans({
"|": "\\|",
"*": "\\*",
"\\": "\\\\",
- })
+})
def is_struct_seq(value):
@@ -746,7 +756,7 @@ def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_
def pyprop2sphinx(ident, fw, identifier, py_prop):
'''
- python property to sphinx
+ Python property to sphinx
'''
# readonly properties use "data" directive, variables use "attribute" directive
if py_prop.fset is None:
@@ -846,7 +856,8 @@ def pymodule2sphinx(basepath, module_name, module, title):
# naughty, we also add getset's into PyStructs, this is not typical py but also not incorrect.
# type_name is only used for examples and messages
- type_name = str(type(module)).strip("<>").split(" ", 1)[-1][1:-1] # "<class 'bpy.app.handlers'>" --> bpy.app.handlers
+ # "<class 'bpy.app.handlers'>" --> bpy.app.handlers
+ type_name = str(type(module)).strip("<>").split(" ", 1)[-1][1:-1]
if type(descr) == types.GetSetDescriptorType:
py_descr2sphinx("", fw, descr, module_name, type_name, key)
attribute_set.add(key)
@@ -907,7 +918,8 @@ def pymodule2sphinx(basepath, module_name, module, title):
for attribute, value, value_type in module_dir_value_type:
if value_type == FunctionType:
pyfunc2sphinx("", fw, module_name, None, attribute, value, is_class=False)
- elif value_type in {types.BuiltinMethodType, types.BuiltinFunctionType}: # both the same at the moment but to be future proof
+ # both the same at the moment but to be future proof
+ elif value_type in {types.BuiltinMethodType, types.BuiltinFunctionType}:
# note: can't get args from these, so dump the string as is
# this means any module used like this must have fully formatted docstrings.
py_c_func2sphinx("", fw, module_name, None, attribute, value, is_class=False)
@@ -973,7 +985,7 @@ def pymodule2sphinx(basepath, module_name, module, title):
if type(descr) == ClassMethodDescriptorType:
py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
- # needed for pure python classes
+ # needed for pure Python classes
for key, descr in descr_items:
if type(descr) == FunctionType:
pyfunc2sphinx(" ", fw, module_name, type_name, key, descr, is_class=True)
@@ -996,7 +1008,7 @@ def pymodule2sphinx(basepath, module_name, module, title):
file.close()
-# Changes in blender will force errors here
+# Changes in Blender will force errors here
context_type_map = {
"active_base": ("ObjectBase", False),
"active_bone": ("EditBone", False),
@@ -1084,9 +1096,10 @@ def pycontext2sphinx(basepath):
fw(title_string("Context Access (bpy.context)", "="))
fw(".. module:: bpy.context\n")
fw("\n")
- fw("The context members available depend on the area of blender which is currently being accessed.\n")
+ fw("The context members available depend on the area of Blender which is currently being accessed.\n")
fw("\n")
- fw("Note that all context values are readonly, but may be modified through the data api or by running operators\n\n")
+ fw("Note that all context values are readonly,\n")
+ fw("but may be modified through the data api or by running operators\n\n")
def write_contex_cls():
@@ -1109,7 +1122,8 @@ def pycontext2sphinx(basepath):
if prop.identifier in struct_blacklist:
continue
- type_descr = prop.get_type_description(class_fmt=":class:`bpy.types.%s`", collection_id=_BPY_PROP_COLLECTION_ID)
+ type_descr = prop.get_type_description(
+ class_fmt=":class:`bpy.types.%s`", collection_id=_BPY_PROP_COLLECTION_ID)
fw(".. data:: %s\n\n" % prop.identifier)
if prop.description:
fw(" %s\n\n" % prop.description)
@@ -1129,7 +1143,7 @@ def pycontext2sphinx(basepath):
del write_contex_cls
# end
- # nasty, get strings directly from blender because there is no other way to get it
+ # nasty, get strings directly from Blender because there is no other way to get it
import ctypes
context_strings = (
@@ -1165,7 +1179,9 @@ def pycontext2sphinx(basepath):
# for member in sorted(unique):
# print(' "%s": ("", False),' % member)
if len(context_type_map) > len(unique):
- raise Exception("Some types are not used: %s" % str([member for member in context_type_map if member not in unique]))
+ raise Exception(
+ "Some types are not used: %s" %
+ str([member for member in context_type_map if member not in unique]))
else:
pass # will have raised an error above
@@ -1244,11 +1260,11 @@ def pyrna2sphinx(basepath):
fw(ident + ":%s%s: %s\n" % (id_type, identifier, type_descr))
def write_struct(struct):
- #if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
- # return
+ # if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
+ # return
- #if not struct.identifier == "Object":
- # return
+ # if not struct.identifier == "Object":
+ # return
filepath = os.path.join(basepath, "bpy.types.%s.rst" % struct.identifier)
file = open(filepath, "w", encoding="utf-8")
@@ -1289,7 +1305,11 @@ def pyrna2sphinx(basepath):
fw(", ".join((":class:`%s`" % base_id) for base_id in base_ids))
fw("\n\n")
- subclass_ids = [s.identifier for s in structs.values() if s.base is struct if not rna_info.rna_id_ignore(s.identifier)]
+ subclass_ids = [
+ s.identifier for s in structs.values()
+ if s.base is struct
+ if not rna_info.rna_id_ignore(s.identifier)
+ ]
subclass_ids.sort()
if subclass_ids:
fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in subclass_ids) + "\n\n")
@@ -1340,7 +1360,7 @@ def pyrna2sphinx(basepath):
fw(" :type: %s\n\n" % type_descr)
- # python attributes
+ # Python attributes
py_properties = struct.get_py_properties()
py_prop = None
for identifier, py_prop in py_properties:
@@ -1350,7 +1370,8 @@ def pyrna2sphinx(basepath):
for func in struct.functions:
args_str = ", ".join(prop.get_arg_default(force=False) for prop in func.args)
- fw(" .. %s:: %s(%s)\n\n" % ("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
+ fw(" .. %s:: %s(%s)\n\n" %
+ ("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
fw(" %s\n\n" % func.description)
for prop in func.args:
@@ -1361,8 +1382,10 @@ def pyrna2sphinx(basepath):
elif func.return_values: # multiple return values
fw(" :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
for prop in func.return_values:
- # TODO, pyrna_enum2sphinx for multiple return values... actually dont think we even use this but still!!!
- type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
+ # TODO, pyrna_enum2sphinx for multiple return values... actually dont
+ # think we even use this but still!!!
+ type_descr = prop.get_type_description(
+ as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
descr = prop.description
if not descr:
descr = prop.name
@@ -1375,7 +1398,7 @@ def pyrna2sphinx(basepath):
fw("\n")
- # python methods
+ # Python methods
py_funcs = struct.get_py_functions()
py_func = None
@@ -1398,7 +1421,10 @@ def pyrna2sphinx(basepath):
del lines[:]
if _BPY_STRUCT_FAKE:
- descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
+ descr_items = [
+ (key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items())
+ if not key.startswith("__")
+ ]
if _BPY_STRUCT_FAKE:
for key, descr in descr_items:
@@ -1494,19 +1520,28 @@ def pyrna2sphinx(basepath):
fw("\n")
if use_subclasses:
- subclass_ids = [s.identifier for s in structs.values() if s.base is None if not rna_info.rna_id_ignore(s.identifier)]
+ subclass_ids = [
+ s.identifier for s in structs.values()
+ if s.base is None
+ if not rna_info.rna_id_ignore(s.identifier)
+ ]
if subclass_ids:
fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
fw(".. class:: %s\n\n" % class_name)
fw(" %s\n\n" % descr_str)
fw(" .. note::\n\n")
- fw(" Note that bpy.types.%s is not actually available from within blender, it only exists for the purpose of documentation.\n\n" % class_name)
+ fw(" Note that bpy.types.%s is not actually available from within Blender,\n"
+ " it only exists for the purpose of documentation.\n\n" % class_name)
- descr_items = [(key, descr) for key, descr in sorted(class_value.__dict__.items()) if not key.startswith("__")]
+ descr_items = [
+ (key, descr) for key, descr in sorted(class_value.__dict__.items())
+ if not key.startswith("__")
+ ]
for key, descr in descr_items:
- if type(descr) == MethodDescriptorType: # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
+ # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
+ if type(descr) == MethodDescriptorType:
py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
for key, descr in descr_items:
@@ -1517,11 +1552,15 @@ def pyrna2sphinx(basepath):
# write fake classes
if _BPY_STRUCT_FAKE:
class_value = bpy.types.Struct.__bases__[0]
- fake_bpy_type(class_value, _BPY_STRUCT_FAKE, "built-in base class for all classes in bpy.types.", use_subclasses=True)
+ fake_bpy_type(
+ class_value, _BPY_STRUCT_FAKE,
+ "built-in base class for all classes in bpy.types.", use_subclasses=True)
if _BPY_PROP_COLLECTION_FAKE:
class_value = bpy.data.objects.__class__
- fake_bpy_type(class_value, _BPY_PROP_COLLECTION_FAKE, "built-in class used for all collections.", use_subclasses=False)
+ fake_bpy_type(
+ class_value, _BPY_PROP_COLLECTION_FAKE,
+ "built-in class used for all collections.", use_subclasses=False)
# operators
def write_ops():
@@ -1633,11 +1672,13 @@ def write_rst_contents(basepath):
fw(title_string("Blender Documentation Contents", "%", double=True))
fw("\n")
- fw("Welcome, this document is an API reference for Blender %s, built %s.\n" % (BLENDER_VERSION_DOTS, BLENDER_DATE))
+ fw("Welcome, this document is an API reference for Blender %s, built %s.\n" %
+ (BLENDER_VERSION_DOTS, BLENDER_DATE))
fw("\n")
# fw("`A PDF version of this document is also available <%s>`_\n" % BLENDER_PDF_FILENAME)
- fw("This site can be downloaded for offline use `Download the full Documentation (zipped HTML files) <%s>`_\n" % BLENDER_ZIP_FILENAME)
+ fw("This site can be downloaded for offline use `Download the full Documentation (zipped HTML files) <%s>`_\n" %
+ BLENDER_ZIP_FILENAME)
fw("\n")
@@ -1670,7 +1711,7 @@ def write_rst_contents(basepath):
# C modules
"bpy.props",
- )
+ )
for mod in app_modules:
if mod not in EXCLUDE_MODULES:
@@ -1691,9 +1732,10 @@ def write_rst_contents(basepath):
"freestyle", "bgl", "blf",
"gpu", "gpu.offscreen",
"aud", "bpy_extras",
+ "idprop.types",
# bmesh, submodules are in own page
"bmesh",
- )
+ )
for mod in standalone_modules:
if mod not in EXCLUDE_MODULES:
@@ -1731,7 +1773,8 @@ def write_rst_contents(basepath):
fw(" * mesh creation and editing functions\n")
fw(" \n")
fw(" These parts of the API are relatively stable and are unlikely to change significantly\n")
- fw(" * data API, access to attributes of blender data such as mesh verts, material color, timeline frames and scene objects\n")
+ fw(" * data API, access to attributes of Blender data such as mesh verts, material color,\n")
+ fw(" timeline frames and scene objects\n")
fw(" * user interface functions for defining buttons, creation of menus, headers, panels\n")
fw(" * render engine integration\n")
fw(" * modules: bgl, mathutils & game engine.\n")
@@ -1803,11 +1846,11 @@ def write_rst_data(basepath):
fw(title_string("Data Access (bpy.data)", "="))
fw(".. module:: bpy\n")
fw("\n")
- fw("This module is used for all blender/python access.\n")
+ fw("This module is used for all Blender/Python access.\n")
fw("\n")
fw(".. data:: data\n")
fw("\n")
- fw(" Access to blenders internal data\n")
+ fw(" Access to Blender's internal data\n")
fw("\n")
fw(" :type: :class:`bpy.types.BlendData`\n")
fw("\n")
@@ -1822,37 +1865,38 @@ def write_rst_importable_modules(basepath):
Write the rst files of importable modules
'''
importable_modules = {
- # python_modules
- "bpy.path" : "Path Utilities",
- "bpy.utils" : "Utilities",
- "bpy_extras" : "Extra Utilities",
+ # Python_modules
+ "bpy.path": "Path Utilities",
+ "bpy.utils": "Utilities",
+ "bpy_extras": "Extra Utilities",
# C_modules
- "aud" : "Audio System",
- "blf" : "Font Drawing",
- "gpu.offscreen" : "GPU Off-Screen Buffer",
- "bmesh" : "BMesh Module",
- "bmesh.types" : "BMesh Types",
- "bmesh.utils" : "BMesh Utilities",
- "bmesh.geometry" : "BMesh Geometry Utilities",
- "bpy.app" : "Application Data",
- "bpy.app.handlers" : "Application Handlers",
- "bpy.app.translations" : "Application Translations",
- "bpy.props" : "Property Definitions",
- "mathutils" : "Math Types & Utilities",
- "mathutils.geometry" : "Geometry Utilities",
- "mathutils.bvhtree" : "BVHTree Utilities",
- "mathutils.kdtree" : "KDTree Utilities",
+ "aud": "Audio System",
+ "blf": "Font Drawing",
+ "gpu.offscreen": "GPU Off-Screen Buffer",
+ "bmesh": "BMesh Module",
+ "bmesh.types": "BMesh Types",
+ "bmesh.utils": "BMesh Utilities",
+ "bmesh.geometry": "BMesh Geometry Utilities",
+ "bpy.app": "Application Data",
+ "bpy.app.handlers": "Application Handlers",
+ "bpy.app.translations": "Application Translations",
+ "bpy.props": "Property Definitions",
+ "idprop.types": "ID Property Access",
+ "mathutils": "Math Types & Utilities",
+ "mathutils.geometry": "Geometry Utilities",
+ "mathutils.bvhtree": "BVHTree Utilities",
+ "mathutils.kdtree": "KDTree Utilities",
"mathutils.interpolate": "Interpolation Utilities",
- "mathutils.noise" : "Noise Utilities",
- "freestyle" : "Freestyle Module",
- "freestyle.types" : "Freestyle Types",
- "freestyle.predicates" : "Freestyle Predicates",
- "freestyle.functions" : "Freestyle Functions",
- "freestyle.chainingiterators" : "Freestyle Chaining Iterators",
- "freestyle.shaders" : "Freestyle Shaders",
- "freestyle.utils" : "Freestyle Utilities",
- }
+ "mathutils.noise": "Noise Utilities",
+ "freestyle": "Freestyle Module",
+ "freestyle.types": "Freestyle Types",
+ "freestyle.predicates": "Freestyle Predicates",
+ "freestyle.functions": "Freestyle Functions",
+ "freestyle.chainingiterators": "Freestyle Chaining Iterators",
+ "freestyle.shaders": "Freestyle Shaders",
+ "freestyle.utils": "Freestyle Utilities",
+ }
for mod_name, mod_descr in importable_modules.items():
if mod_name not in EXCLUDE_MODULES:
module = __import__(mod_name,
@@ -1867,7 +1911,7 @@ def copy_handwritten_rsts(basepath):
for info, info_desc in INFO_DOCS:
shutil.copy2(os.path.join(RST_DIR, info), basepath)
- # TODO put this docs in blender's code and use import as per modules above
+ # TODO put this docs in Blender's code and use import as per modules above
handwritten_modules = [
"bge.logic",
"bge.render",
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index b046e9626b6..45aa4a9d94d 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -105,6 +105,7 @@ endif()
if(WITH_GTESTS)
add_subdirectory(gtest)
+ add_subdirectory(gmock)
endif()
if(WITH_SDL AND WITH_SDL_DYNLOAD)
diff --git a/extern/curve_fit_nd/README.blender b/extern/curve_fit_nd/README.blender
new file mode 100644
index 00000000000..db520ea524e
--- /dev/null
+++ b/extern/curve_fit_nd/README.blender
@@ -0,0 +1,5 @@
+Project: Curve-Fit-nD
+URL: https://github.com/ideasman42/curve-fit-nd
+License: BSD 3-Clause
+Upstream version: Unknown (Last Release)
+Local modifications: None
diff --git a/extern/curve_fit_nd/intern/curve_fit_cubic.c b/extern/curve_fit_nd/intern/curve_fit_cubic.c
index 24b216d32ff..9b4f1869c02 100644
--- a/extern/curve_fit_nd/intern/curve_fit_cubic.c
+++ b/extern/curve_fit_nd/intern/curve_fit_cubic.c
@@ -614,7 +614,7 @@ static void cubic_from_points_offset_fallback(
double dists[2] = {0, 0};
- const double *pt = points_offset;
+ const double *pt = &points_offset[dims];
for (uint i = 1; i < points_offset_len - 1; i++, pt += dims) {
for (uint k = 0; k < 2; k++) {
sub_vn_vnvn(tmp, p0, pt, dims);
@@ -623,13 +623,13 @@ static void cubic_from_points_offset_fallback(
}
}
- float alpha_l = (dists[0] / 0.75) / dot_vnvn(tan_l, a[0], dims);
- float alpha_r = (dists[1] / 0.75) / -dot_vnvn(tan_r, a[1], dims);
+ double alpha_l = (dists[0] / 0.75) / dot_vnvn(tan_l, a[0], dims);
+ double alpha_r = (dists[1] / 0.75) / -dot_vnvn(tan_r, a[1], dims);
- if (!(alpha_l > 0.0f)) {
+ if (!(alpha_l > 0.0)) {
alpha_l = dir_dist / 3.0;
}
- if (!(alpha_r > 0.0f)) {
+ if (!(alpha_r > 0.0)) {
alpha_r = dir_dist / 3.0;
}
@@ -742,7 +742,11 @@ static void cubic_from_points(
!(alpha_r >= 0.0))
{
#ifdef USE_CIRCULAR_FALLBACK
- alpha_l = alpha_r = points_calc_cubic_scale(p0, p3, tan_l, tan_r, points_offset_coords_length, dims);
+ double alpha_test = points_calc_cubic_scale(p0, p3, tan_l, tan_r, points_offset_coords_length, dims);
+ if (!isfinite(alpha_test)) {
+ alpha_test = len_vnvn(p0, p3, dims) / 3.0;
+ }
+ alpha_l = alpha_r = alpha_test;
#else
alpha_l = alpha_r = len_vnvn(p0, p3, dims) / 3.0;
#endif
@@ -804,7 +808,11 @@ static void cubic_from_points(
p2_dist_sq > dist_sq_max)
{
#ifdef USE_CIRCULAR_FALLBACK
- alpha_l = alpha_r = points_calc_cubic_scale(p0, p3, tan_l, tan_r, points_offset_coords_length, dims);
+ double alpha_test = points_calc_cubic_scale(p0, p3, tan_l, tan_r, points_offset_coords_length, dims);
+ if (!isfinite(alpha_test)) {
+ alpha_test = len_vnvn(p0, p3, dims) / 3.0;
+ }
+ alpha_l = alpha_r = alpha_test;
#else
alpha_l = alpha_r = len_vnvn(p0, p3, dims) / 3.0;
#endif
@@ -888,7 +896,7 @@ static double points_calc_coord_length(
}
assert(!is_almost_zero(r_u[points_offset_len - 1]));
const double w = r_u[points_offset_len - 1];
- for (uint i = 0; i < points_offset_len; i++) {
+ for (uint i = 1; i < points_offset_len; i++) {
r_u[i] /= w;
}
return w;
diff --git a/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c b/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c
index c701f28f131..b51535bab10 100644
--- a/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c
+++ b/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c
@@ -43,6 +43,10 @@
* While re-fitting, remove knots that fall below the error threshold.
*/
+#ifdef _MSC_VER
+# define _USE_MATH_DEFINES
+#endif
+
#include <math.h>
#include <float.h>
#include <stdbool.h>
@@ -51,8 +55,7 @@
#include <string.h>
#include <stdlib.h>
-
-#include <stdio.h>
+typedef unsigned int uint;
#include "curve_fit_inline.h"
#include "../curve_fit_nd.h"
@@ -101,9 +104,6 @@
# define UNLIKELY(x) (x)
#endif
-
-typedef unsigned int uint;
-
struct PointData {
const double *points;
uint points_len;
@@ -947,14 +947,17 @@ static uint curve_incremental_simplify_corners(
dims);
if (split_index != SPLIT_POINT_INVALID) {
+ const double *co_prev = &params.pd->points[k_prev->index * dims];
+ const double *co_next = &params.pd->points[k_next->index * dims];
+ const double *co_split = &params.pd->points[split_index * dims];
- project_vn_vnvn(k_proj_ref, &pd->points[k_prev->index * dims], k_prev->tan[1], dims);
- project_vn_vnvn(k_proj_split, &pd->points[split_index * dims], k_prev->tan[1], dims);
+ project_vn_vnvn_normalized(k_proj_ref, co_prev, k_prev->tan[1], dims);
+ project_vn_vnvn_normalized(k_proj_split, co_split, k_prev->tan[1], dims);
if (len_squared_vnvn(k_proj_ref, k_proj_split, dims) < error_sq_2x_max) {
- project_vn_vnvn(k_proj_ref, &pd->points[k_next->index * dims], k_next->tan[0], dims);
- project_vn_vnvn(k_proj_split, &pd->points[split_index * dims], k_next->tan[0], dims);
+ project_vn_vnvn_normalized(k_proj_ref, co_next, k_next->tan[0], dims);
+ project_vn_vnvn_normalized(k_proj_split, co_split, k_next->tan[0], dims);
if (len_squared_vnvn(k_proj_ref, k_proj_split, dims) < error_sq_2x_max) {
@@ -1156,8 +1159,22 @@ int curve_fit_cubic_to_points_refit_db(
k->handles[1] = len_next / 3;
}
#else
- if (is_cyclic) {
- len_prev = normalize_vn_vnvn(tan_prev, &points[(knots_len - 2) * dims], &points[(knots_len - 1) * dims], dims);
+ if (knots_len < 2) {
+ /* NOP, set dummy values */
+ for (uint i = 0; i < knots_len; i++) {
+ struct Knot *k = &knots[i];
+ zero_vn(k->tan[0], dims);
+ zero_vn(k->tan[1], dims);
+ k->handles[0] = 0.0;
+ k->handles[1] = 0.0;
+#ifdef USE_LENGTH_CACHE
+ points_length_cache[i] = 0.0;
+#endif
+ }
+ }
+ else if (is_cyclic) {
+ len_prev = normalize_vn_vnvn(
+ tan_prev, &points[(knots_len - 2) * dims], &points[(knots_len - 1) * dims], dims);
for (uint i_curr = knots_len - 1, i_next = 0; i_next < knots_len; i_curr = i_next++) {
struct Knot *k = &knots[i_curr];
#ifdef USE_LENGTH_CACHE
@@ -1177,10 +1194,11 @@ int curve_fit_cubic_to_points_refit_db(
}
else {
#ifdef USE_LENGTH_CACHE
- points_length_cache[0] = 0.0;
- points_length_cache[1] =
+ points_length_cache[0] = 0.0;
+ points_length_cache[1] =
#endif
- len_prev = normalize_vn_vnvn(tan_prev, &points[0 * dims], &points[1 * dims], dims);
+ len_prev = normalize_vn_vnvn(
+ tan_prev, &points[0 * dims], &points[1 * dims], dims);
copy_vnvn(knots[0].tan[0], tan_prev, dims);
copy_vnvn(knots[0].tan[1], tan_prev, dims);
knots[0].handles[0] = len_prev / 3;
diff --git a/extern/curve_fit_nd/intern/generic_heap.h b/extern/curve_fit_nd/intern/generic_heap.h
index 74327c761e4..e39344cf076 100644
--- a/extern/curve_fit_nd/intern/generic_heap.h
+++ b/extern/curve_fit_nd/intern/generic_heap.h
@@ -24,8 +24,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef __GENERIC_HEAP_IMPL_H__
-#define __GENERIC_HEAP_IMPL_H__
+#ifndef __GENERIC_HEAP_H__
+#define __GENERIC_HEAP_H__
/** \file generic_heap.h
* \ingroup curve_fit
diff --git a/extern/gmock/CMakeLists.txt b/extern/gmock/CMakeLists.txt
new file mode 100644
index 00000000000..c0c87009328
--- /dev/null
+++ b/extern/gmock/CMakeLists.txt
@@ -0,0 +1,66 @@
+# ***** 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) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ include
+)
+
+set(INC_SYS
+ ../gtest/include
+)
+
+set(SRC
+ # src/gmock-all.cc
+
+ src/gmock-cardinalities.cc
+ src/gmock.cc
+ src/gmock-internal-utils.cc
+ src/gmock_main.cc
+ src/gmock-matchers.cc
+ src/gmock-spec-builders.cc
+)
+
+set(SRC_HEADERS
+ include/gmock/gmock-actions.h
+ include/gmock/gmock-cardinalities.h
+ include/gmock/gmock-generated-actions.h
+ include/gmock/gmock-generated-function-mockers.h
+ include/gmock/gmock-generated-matchers.h
+ include/gmock/gmock-generated-nice-strict.h
+ include/gmock/gmock.h
+ include/gmock/gmock-matchers.h
+ include/gmock/gmock-more-actions.h
+ include/gmock/gmock-more-matchers.h
+ include/gmock/gmock-spec-builders.h
+ include/gmock/internal/custom/gmock-generated-actions.h
+ include/gmock/internal/custom/gmock-matchers.h
+ include/gmock/internal/custom/gmock-port.h
+ include/gmock/internal/gmock-generated-internal-utils.h
+ include/gmock/internal/gmock-internal-utils.h
+ include/gmock/internal/gmock-port.h
+)
+
+include_directories(${INC})
+include_directories(SYSTEM ${INC_SYS})
+add_library(extern_gmock ${SRC} ${SRC_HEADERS})
diff --git a/extern/gmock/LICENSE b/extern/gmock/LICENSE
new file mode 100644
index 00000000000..1941a11f8ce
--- /dev/null
+++ b/extern/gmock/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/extern/gmock/README.blender b/extern/gmock/README.blender
new file mode 100644
index 00000000000..41dda92c19c
--- /dev/null
+++ b/extern/gmock/README.blender
@@ -0,0 +1,7 @@
+Project: Google C++ Testing Framework
+URL: https://github.com/google/googletest
+License: New BSD
+Upstream version: 1.7.0 (ec44c6c)
+Local modifications:
+
+None.
diff --git a/extern/gmock/README.md b/extern/gmock/README.md
new file mode 100644
index 00000000000..332beab3881
--- /dev/null
+++ b/extern/gmock/README.md
@@ -0,0 +1,333 @@
+## Google Mock ##
+
+The Google C++ mocking framework.
+
+### Overview ###
+
+Google's framework for writing and using C++ mock classes.
+It can help you derive better designs of your system and write better tests.
+
+It is inspired by:
+
+ * [jMock](http://www.jmock.org/),
+ * [EasyMock](http://www.easymock.org/), and
+ * [Hamcrest](http://code.google.com/p/hamcrest/),
+
+and designed with C++'s specifics in mind.
+
+Google mock:
+
+ * lets you create mock classes trivially using simple macros.
+ * supports a rich set of matchers and actions.
+ * handles unordered, partially ordered, or completely ordered expectations.
+ * is extensible by users.
+
+We hope you find it useful!
+
+### Features ###
+
+ * Provides a declarative syntax for defining mocks.
+ * Can easily define partial (hybrid) mocks, which are a cross of real
+ and mock objects.
+ * Handles functions of arbitrary types and overloaded functions.
+ * Comes with a rich set of matchers for validating function arguments.
+ * Uses an intuitive syntax for controlling the behavior of a mock.
+ * Does automatic verification of expectations (no record-and-replay needed).
+ * Allows arbitrary (partial) ordering constraints on
+ function calls to be expressed,.
+ * Lets a user extend it by defining new matchers and actions.
+ * Does not use exceptions.
+ * Is easy to learn and use.
+
+Please see the project page above for more information as well as the
+mailing list for questions, discussions, and development. There is
+also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please
+join us!
+
+Please note that code under [scripts/generator](scripts/generator/) is
+from [cppclean](http://code.google.com/p/cppclean/) and released under
+the Apache License, which is different from Google Mock's license.
+
+## Getting Started ##
+
+If you are new to the project, we suggest that you read the user
+documentation in the following order:
+
+ * Learn the [basics](../googletest/docs/Primer.md) of
+ Google Test, if you choose to use Google Mock with it (recommended).
+ * Read [Google Mock for Dummies](docs/ForDummies.md).
+ * Read the instructions below on how to build Google Mock.
+
+You can also watch Zhanyong's [talk](http://www.youtube.com/watch?v=sYpCyLI47rM) on Google Mock's usage and implementation.
+
+Once you understand the basics, check out the rest of the docs:
+
+ * [CheatSheet](docs/CheatSheet.md) - all the commonly used stuff
+ at a glance.
+ * [CookBook](docs/CookBook.md) - recipes for getting things done,
+ including advanced techniques.
+
+If you need help, please check the
+[KnownIssues](docs/KnownIssues.md) and
+[FrequentlyAskedQuestions](docs/FrequentlyAskedQuestions.md) before
+posting a question on the
+[discussion group](http://groups.google.com/group/googlemock).
+
+
+### Using Google Mock Without Google Test ###
+
+Google Mock is not a testing framework itself. Instead, it needs a
+testing framework for writing tests. Google Mock works seamlessly
+with [Google Test](http://code.google.com/p/googletest/), but
+you can also use it with [any C++ testing framework](googlemock/ForDummies.md#Using_Google_Mock_with_Any_Testing_Framework).
+
+### Requirements for End Users ###
+
+Google Mock is implemented on top of [Google Test](
+http://github.com/google/googletest/), and depends on it.
+You must use the bundled version of Google Test when using Google Mock.
+
+You can also easily configure Google Mock to work with another testing
+framework, although it will still need Google Test. Please read
+["Using_Google_Mock_with_Any_Testing_Framework"](
+ docs/ForDummies.md#Using_Google_Mock_with_Any_Testing_Framework)
+for instructions.
+
+Google Mock depends on advanced C++ features and thus requires a more
+modern compiler. The following are needed to use Google Mock:
+
+#### Linux Requirements ####
+
+ * GNU-compatible Make or "gmake"
+ * POSIX-standard shell
+ * POSIX(-2) Regular Expressions (regex.h)
+ * C++98-standard-compliant compiler (e.g. GCC 3.4 or newer)
+
+#### Windows Requirements ####
+
+ * Microsoft Visual C++ 8.0 SP1 or newer
+
+#### Mac OS X Requirements ####
+
+ * Mac OS X 10.4 Tiger or newer
+ * Developer Tools Installed
+
+### Requirements for Contributors ###
+
+We welcome patches. If you plan to contribute a patch, you need to
+build Google Mock and its tests, which has further requirements:
+
+ * Automake version 1.9 or newer
+ * Autoconf version 2.59 or newer
+ * Libtool / Libtoolize
+ * Python version 2.3 or newer (for running some of the tests and
+ re-generating certain source files from templates)
+
+### Building Google Mock ###
+
+#### Preparing to Build (Unix only) ####
+
+If you are using a Unix system and plan to use the GNU Autotools build
+system to build Google Mock (described below), you'll need to
+configure it now.
+
+To prepare the Autotools build system:
+
+ cd googlemock
+ autoreconf -fvi
+
+To build Google Mock and your tests that use it, you need to tell your
+build system where to find its headers and source files. The exact
+way to do it depends on which build system you use, and is usually
+straightforward.
+
+This section shows how you can integrate Google Mock into your
+existing build system.
+
+Suppose you put Google Mock in directory `${GMOCK_DIR}` and Google Test
+in `${GTEST_DIR}` (the latter is `${GMOCK_DIR}/gtest` by default). To
+build Google Mock, create a library build target (or a project as
+called by Visual Studio and Xcode) to compile
+
+ ${GTEST_DIR}/src/gtest-all.cc and ${GMOCK_DIR}/src/gmock-all.cc
+
+with
+
+ ${GTEST_DIR}/include and ${GMOCK_DIR}/include
+
+in the system header search path, and
+
+ ${GTEST_DIR} and ${GMOCK_DIR}
+
+in the normal header search path. Assuming a Linux-like system and gcc,
+something like the following will do:
+
+ g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
+ -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \
+ -pthread -c ${GTEST_DIR}/src/gtest-all.cc
+ g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
+ -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \
+ -pthread -c ${GMOCK_DIR}/src/gmock-all.cc
+ ar -rv libgmock.a gtest-all.o gmock-all.o
+
+(We need -pthread as Google Test and Google Mock use threads.)
+
+Next, you should compile your test source file with
+${GTEST\_DIR}/include and ${GMOCK\_DIR}/include in the header search
+path, and link it with gmock and any other necessary libraries:
+
+ g++ -isystem ${GTEST_DIR}/include -isystem ${GMOCK_DIR}/include \
+ -pthread path/to/your_test.cc libgmock.a -o your_test
+
+As an example, the make/ directory contains a Makefile that you can
+use to build Google Mock on systems where GNU make is available
+(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
+Mock's own tests. Instead, it just builds the Google Mock library and
+a sample test. You can use it as a starting point for your own build
+script.
+
+If the default settings are correct for your environment, the
+following commands should succeed:
+
+ cd ${GMOCK_DIR}/make
+ make
+ ./gmock_test
+
+If you see errors, try to tweak the contents of
+[make/Makefile](make/Makefile) to make them go away.
+
+### Windows ###
+
+The msvc/2005 directory contains VC++ 2005 projects and the msvc/2010
+directory contains VC++ 2010 projects for building Google Mock and
+selected tests.
+
+Change to the appropriate directory and run "msbuild gmock.sln" to
+build the library and tests (or open the gmock.sln in the MSVC IDE).
+If you want to create your own project to use with Google Mock, you'll
+have to configure it to use the `gmock_config` propety sheet. For that:
+
+ * Open the Property Manager window (View | Other Windows | Property Manager)
+ * Right-click on your project and select "Add Existing Property Sheet..."
+ * Navigate to `gmock_config.vsprops` or `gmock_config.props` and select it.
+ * In Project Properties | Configuration Properties | General | Additional
+ Include Directories, type <path to Google Mock>/include.
+
+### Tweaking Google Mock ###
+
+Google Mock can be used in diverse environments. The default
+configuration may not work (or may not work well) out of the box in
+some environments. However, you can easily tweak Google Mock by
+defining control macros on the compiler command line. Generally,
+these macros are named like `GTEST_XYZ` and you define them to either 1
+or 0 to enable or disable a certain feature.
+
+We list the most frequently used macros below. For a complete list,
+see file [${GTEST\_DIR}/include/gtest/internal/gtest-port.h](
+../googletest/include/gtest/internal/gtest-port.h).
+
+### Choosing a TR1 Tuple Library ###
+
+Google Mock uses the C++ Technical Report 1 (TR1) tuple library
+heavily. Unfortunately TR1 tuple is not yet widely available with all
+compilers. The good news is that Google Test 1.4.0+ implements a
+subset of TR1 tuple that's enough for Google Mock's need. Google Mock
+will automatically use that implementation when the compiler doesn't
+provide TR1 tuple.
+
+Usually you don't need to care about which tuple library Google Test
+and Google Mock use. However, if your project already uses TR1 tuple,
+you need to tell Google Test and Google Mock to use the same TR1 tuple
+library the rest of your project uses, or the two tuple
+implementations will clash. To do that, add
+
+ -DGTEST_USE_OWN_TR1_TUPLE=0
+
+to the compiler flags while compiling Google Test, Google Mock, and
+your tests. If you want to force Google Test and Google Mock to use
+their own tuple library, just add
+
+ -DGTEST_USE_OWN_TR1_TUPLE=1
+
+to the compiler flags instead.
+
+If you want to use Boost's TR1 tuple library with Google Mock, please
+refer to the Boost website (http://www.boost.org/) for how to obtain
+it and set it up.
+
+### As a Shared Library (DLL) ###
+
+Google Mock is compact, so most users can build and link it as a static
+library for the simplicity. Google Mock can be used as a DLL, but the
+same DLL must contain Google Test as well. See
+[Google Test's README][gtest_readme]
+for instructions on how to set up necessary compiler settings.
+
+### Tweaking Google Mock ###
+
+Most of Google Test's control macros apply to Google Mock as well.
+Please see [Google Test's README][gtest_readme] for how to tweak them.
+
+### Upgrading from an Earlier Version ###
+
+We strive to keep Google Mock releases backward compatible.
+Sometimes, though, we have to make some breaking changes for the
+users' long-term benefits. This section describes what you'll need to
+do if you are upgrading from an earlier version of Google Mock.
+
+#### Upgrading from 1.1.0 or Earlier ####
+
+You may need to explicitly enable or disable Google Test's own TR1
+tuple library. See the instructions in section "[Choosing a TR1 Tuple
+Library](../googletest/#choosing-a-tr1-tuple-library)".
+
+#### Upgrading from 1.4.0 or Earlier ####
+
+On platforms where the pthread library is available, Google Test and
+Google Mock use it in order to be thread-safe. For this to work, you
+may need to tweak your compiler and/or linker flags. Please see the
+"[Multi-threaded Tests](../googletest#multi-threaded-tests
+)" section in file Google Test's README for what you may need to do.
+
+If you have custom matchers defined using `MatcherInterface` or
+`MakePolymorphicMatcher()`, you'll need to update their definitions to
+use the new matcher API (
+[monomorphic](http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Monomorphic_Matchers),
+[polymorphic](http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Polymorphic_Matchers)).
+Matchers defined using `MATCHER()` or `MATCHER_P*()` aren't affected.
+
+### Developing Google Mock ###
+
+This section discusses how to make your own changes to Google Mock.
+
+#### Testing Google Mock Itself ####
+
+To make sure your changes work as intended and don't break existing
+functionality, you'll want to compile and run Google Test's own tests.
+For that you'll need Autotools. First, make sure you have followed
+the instructions above to configure Google Mock.
+Then, create a build output directory and enter it. Next,
+
+ ${GMOCK_DIR}/configure # try --help for more info
+
+Once you have successfully configured Google Mock, the build steps are
+standard for GNU-style OSS packages.
+
+ make # Standard makefile following GNU conventions
+ make check # Builds and runs all tests - all should pass.
+
+Note that when building your project against Google Mock, you are building
+against Google Test as well. There is no need to configure Google Test
+separately.
+
+#### Contributing a Patch ####
+
+We welcome patches.
+Please read the [Developer's Guide](docs/DevGuide.md)
+for how you can contribute. In particular, make sure you have signed
+the Contributor License Agreement, or we won't be able to accept the
+patch.
+
+Happy testing!
+
+[gtest_readme]: ../googletest/README.md "googletest"
diff --git a/extern/gmock/include/gmock/gmock-actions.h b/extern/gmock/include/gmock/gmock-actions.h
new file mode 100644
index 00000000000..b3f654af348
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-actions.h
@@ -0,0 +1,1205 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used actions.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+
+#ifndef _WIN32_WCE
+# include <errno.h>
+#endif
+
+#include <algorithm>
+#include <string>
+
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+
+#if GTEST_HAS_STD_TYPE_TRAITS_ // Defined by gtest-port.h via gmock-port.h.
+#include <type_traits>
+#endif
+
+namespace testing {
+
+// To implement an action Foo, define:
+// 1. a class FooAction that implements the ActionInterface interface, and
+// 2. a factory function that creates an Action object from a
+// const FooAction*.
+//
+// The two-level delegation design follows that of Matcher, providing
+// consistency for extension developers. It also eases ownership
+// management as Action objects can now be copied like plain values.
+
+namespace internal {
+
+template <typename F1, typename F2>
+class ActionAdaptor;
+
+// BuiltInDefaultValueGetter<T, true>::Get() returns a
+// default-constructed T value. BuiltInDefaultValueGetter<T,
+// false>::Get() crashes with an error.
+//
+// This primary template is used when kDefaultConstructible is true.
+template <typename T, bool kDefaultConstructible>
+struct BuiltInDefaultValueGetter {
+ static T Get() { return T(); }
+};
+template <typename T>
+struct BuiltInDefaultValueGetter<T, false> {
+ static T Get() {
+ Assert(false, __FILE__, __LINE__,
+ "Default action undefined for the function return type.");
+ return internal::Invalid<T>();
+ // The above statement will never be reached, but is required in
+ // order for this function to compile.
+ }
+};
+
+// BuiltInDefaultValue<T>::Get() returns the "built-in" default value
+// for type T, which is NULL when T is a raw pointer type, 0 when T is
+// a numeric type, false when T is bool, or "" when T is string or
+// std::string. In addition, in C++11 and above, it turns a
+// default-constructed T value if T is default constructible. For any
+// other type T, the built-in default T value is undefined, and the
+// function will abort the process.
+template <typename T>
+class BuiltInDefaultValue {
+ public:
+#if GTEST_HAS_STD_TYPE_TRAITS_
+ // This function returns true iff type T has a built-in default value.
+ static bool Exists() {
+ return ::std::is_default_constructible<T>::value;
+ }
+
+ static T Get() {
+ return BuiltInDefaultValueGetter<
+ T, ::std::is_default_constructible<T>::value>::Get();
+ }
+
+#else // GTEST_HAS_STD_TYPE_TRAITS_
+ // This function returns true iff type T has a built-in default value.
+ static bool Exists() {
+ return false;
+ }
+
+ static T Get() {
+ return BuiltInDefaultValueGetter<T, false>::Get();
+ }
+
+#endif // GTEST_HAS_STD_TYPE_TRAITS_
+};
+
+// This partial specialization says that we use the same built-in
+// default value for T and const T.
+template <typename T>
+class BuiltInDefaultValue<const T> {
+ public:
+ static bool Exists() { return BuiltInDefaultValue<T>::Exists(); }
+ static T Get() { return BuiltInDefaultValue<T>::Get(); }
+};
+
+// This partial specialization defines the default values for pointer
+// types.
+template <typename T>
+class BuiltInDefaultValue<T*> {
+ public:
+ static bool Exists() { return true; }
+ static T* Get() { return NULL; }
+};
+
+// The following specializations define the default values for
+// specific types we care about.
+#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \
+ template <> \
+ class BuiltInDefaultValue<type> { \
+ public: \
+ static bool Exists() { return true; } \
+ static type Get() { return value; } \
+ }
+
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT
+#if GTEST_HAS_GLOBAL_STRING
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, "");
+#endif // GTEST_HAS_GLOBAL_STRING
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, "");
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0');
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0');
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0');
+
+// There's no need for a default action for signed wchar_t, as that
+// type is the same as wchar_t for gcc, and invalid for MSVC.
+//
+// There's also no need for a default action for unsigned wchar_t, as
+// that type is the same as unsigned int for gcc, and invalid for
+// MSVC.
+#if GMOCK_WCHAR_T_IS_NATIVE_
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT
+#endif
+
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0);
+
+#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_
+
+} // namespace internal
+
+// When an unexpected function call is encountered, Google Mock will
+// let it return a default value if the user has specified one for its
+// return type, or if the return type has a built-in default value;
+// otherwise Google Mock won't know what value to return and will have
+// to abort the process.
+//
+// The DefaultValue<T> class allows a user to specify the
+// default value for a type T that is both copyable and publicly
+// destructible (i.e. anything that can be used as a function return
+// type). The usage is:
+//
+// // Sets the default value for type T to be foo.
+// DefaultValue<T>::Set(foo);
+template <typename T>
+class DefaultValue {
+ public:
+ // Sets the default value for type T; requires T to be
+ // copy-constructable and have a public destructor.
+ static void Set(T x) {
+ delete producer_;
+ producer_ = new FixedValueProducer(x);
+ }
+
+ // Provides a factory function to be called to generate the default value.
+ // This method can be used even if T is only move-constructible, but it is not
+ // limited to that case.
+ typedef T (*FactoryFunction)();
+ static void SetFactory(FactoryFunction factory) {
+ delete producer_;
+ producer_ = new FactoryValueProducer(factory);
+ }
+
+ // Unsets the default value for type T.
+ static void Clear() {
+ delete producer_;
+ producer_ = NULL;
+ }
+
+ // Returns true iff the user has set the default value for type T.
+ static bool IsSet() { return producer_ != NULL; }
+
+ // Returns true if T has a default return value set by the user or there
+ // exists a built-in default value.
+ static bool Exists() {
+ return IsSet() || internal::BuiltInDefaultValue<T>::Exists();
+ }
+
+ // Returns the default value for type T if the user has set one;
+ // otherwise returns the built-in default value. Requires that Exists()
+ // is true, which ensures that the return value is well-defined.
+ static T Get() {
+ return producer_ == NULL ?
+ internal::BuiltInDefaultValue<T>::Get() : producer_->Produce();
+ }
+
+ private:
+ class ValueProducer {
+ public:
+ virtual ~ValueProducer() {}
+ virtual T Produce() = 0;
+ };
+
+ class FixedValueProducer : public ValueProducer {
+ public:
+ explicit FixedValueProducer(T value) : value_(value) {}
+ virtual T Produce() { return value_; }
+
+ private:
+ const T value_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer);
+ };
+
+ class FactoryValueProducer : public ValueProducer {
+ public:
+ explicit FactoryValueProducer(FactoryFunction factory)
+ : factory_(factory) {}
+ virtual T Produce() { return factory_(); }
+
+ private:
+ const FactoryFunction factory_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer);
+ };
+
+ static ValueProducer* producer_;
+};
+
+// This partial specialization allows a user to set default values for
+// reference types.
+template <typename T>
+class DefaultValue<T&> {
+ public:
+ // Sets the default value for type T&.
+ static void Set(T& x) { // NOLINT
+ address_ = &x;
+ }
+
+ // Unsets the default value for type T&.
+ static void Clear() {
+ address_ = NULL;
+ }
+
+ // Returns true iff the user has set the default value for type T&.
+ static bool IsSet() { return address_ != NULL; }
+
+ // Returns true if T has a default return value set by the user or there
+ // exists a built-in default value.
+ static bool Exists() {
+ return IsSet() || internal::BuiltInDefaultValue<T&>::Exists();
+ }
+
+ // Returns the default value for type T& if the user has set one;
+ // otherwise returns the built-in default value if there is one;
+ // otherwise aborts the process.
+ static T& Get() {
+ return address_ == NULL ?
+ internal::BuiltInDefaultValue<T&>::Get() : *address_;
+ }
+
+ private:
+ static T* address_;
+};
+
+// This specialization allows DefaultValue<void>::Get() to
+// compile.
+template <>
+class DefaultValue<void> {
+ public:
+ static bool Exists() { return true; }
+ static void Get() {}
+};
+
+// Points to the user-set default value for type T.
+template <typename T>
+typename DefaultValue<T>::ValueProducer* DefaultValue<T>::producer_ = NULL;
+
+// Points to the user-set default value for type T&.
+template <typename T>
+T* DefaultValue<T&>::address_ = NULL;
+
+// Implement this interface to define an action for function type F.
+template <typename F>
+class ActionInterface {
+ public:
+ typedef typename internal::Function<F>::Result Result;
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ ActionInterface() {}
+ virtual ~ActionInterface() {}
+
+ // Performs the action. This method is not const, as in general an
+ // action can have side effects and be stateful. For example, a
+ // get-the-next-element-from-the-collection action will need to
+ // remember the current element.
+ virtual Result Perform(const ArgumentTuple& args) = 0;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface);
+};
+
+// An Action<F> is a copyable and IMMUTABLE (except by assignment)
+// object that represents an action to be taken when a mock function
+// of type F is called. The implementation of Action<T> is just a
+// linked_ptr to const ActionInterface<T>, so copying is fairly cheap.
+// Don't inherit from Action!
+//
+// You can view an object implementing ActionInterface<F> as a
+// concrete action (including its current state), and an Action<F>
+// object as a handle to it.
+template <typename F>
+class Action {
+ public:
+ typedef typename internal::Function<F>::Result Result;
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ // Constructs a null Action. Needed for storing Action objects in
+ // STL containers.
+ Action() : impl_(NULL) {}
+
+ // Constructs an Action from its implementation. A NULL impl is
+ // used to represent the "do-default" action.
+ explicit Action(ActionInterface<F>* impl) : impl_(impl) {}
+
+ // Copy constructor.
+ Action(const Action& action) : impl_(action.impl_) {}
+
+ // This constructor allows us to turn an Action<Func> object into an
+ // Action<F>, as long as F's arguments can be implicitly converted
+ // to Func's and Func's return type can be implicitly converted to
+ // F's.
+ template <typename Func>
+ explicit Action(const Action<Func>& action);
+
+ // Returns true iff this is the DoDefault() action.
+ bool IsDoDefault() const { return impl_.get() == NULL; }
+
+ // Performs the action. Note that this method is const even though
+ // the corresponding method in ActionInterface is not. The reason
+ // is that a const Action<F> means that it cannot be re-bound to
+ // another concrete action, not that the concrete action it binds to
+ // cannot change state. (Think of the difference between a const
+ // pointer and a pointer to const.)
+ Result Perform(const ArgumentTuple& args) const {
+ internal::Assert(
+ !IsDoDefault(), __FILE__, __LINE__,
+ "You are using DoDefault() inside a composite action like "
+ "DoAll() or WithArgs(). This is not supported for technical "
+ "reasons. Please instead spell out the default action, or "
+ "assign the default action to an Action variable and use "
+ "the variable in various places.");
+ return impl_->Perform(args);
+ }
+
+ private:
+ template <typename F1, typename F2>
+ friend class internal::ActionAdaptor;
+
+ internal::linked_ptr<ActionInterface<F> > impl_;
+};
+
+// The PolymorphicAction class template makes it easy to implement a
+// polymorphic action (i.e. an action that can be used in mock
+// functions of than one type, e.g. Return()).
+//
+// To define a polymorphic action, a user first provides a COPYABLE
+// implementation class that has a Perform() method template:
+//
+// class FooAction {
+// public:
+// template <typename Result, typename ArgumentTuple>
+// Result Perform(const ArgumentTuple& args) const {
+// // Processes the arguments and returns a result, using
+// // tr1::get<N>(args) to get the N-th (0-based) argument in the tuple.
+// }
+// ...
+// };
+//
+// Then the user creates the polymorphic action using
+// MakePolymorphicAction(object) where object has type FooAction. See
+// the definition of Return(void) and SetArgumentPointee<N>(value) for
+// complete examples.
+template <typename Impl>
+class PolymorphicAction {
+ public:
+ explicit PolymorphicAction(const Impl& impl) : impl_(impl) {}
+
+ template <typename F>
+ operator Action<F>() const {
+ return Action<F>(new MonomorphicImpl<F>(impl_));
+ }
+
+ private:
+ template <typename F>
+ class MonomorphicImpl : public ActionInterface<F> {
+ public:
+ typedef typename internal::Function<F>::Result Result;
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+ virtual Result Perform(const ArgumentTuple& args) {
+ return impl_.template Perform<Result>(args);
+ }
+
+ private:
+ Impl impl_;
+
+ GTEST_DISALLOW_ASSIGN_(MonomorphicImpl);
+ };
+
+ Impl impl_;
+
+ GTEST_DISALLOW_ASSIGN_(PolymorphicAction);
+};
+
+// Creates an Action from its implementation and returns it. The
+// created Action object owns the implementation.
+template <typename F>
+Action<F> MakeAction(ActionInterface<F>* impl) {
+ return Action<F>(impl);
+}
+
+// Creates a polymorphic action from its implementation. This is
+// easier to use than the PolymorphicAction<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+// MakePolymorphicAction(foo);
+// vs
+// PolymorphicAction<TypeOfFoo>(foo);
+template <typename Impl>
+inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) {
+ return PolymorphicAction<Impl>(impl);
+}
+
+namespace internal {
+
+// Allows an Action<F2> object to pose as an Action<F1>, as long as F2
+// and F1 are compatible.
+template <typename F1, typename F2>
+class ActionAdaptor : public ActionInterface<F1> {
+ public:
+ typedef typename internal::Function<F1>::Result Result;
+ typedef typename internal::Function<F1>::ArgumentTuple ArgumentTuple;
+
+ explicit ActionAdaptor(const Action<F2>& from) : impl_(from.impl_) {}
+
+ virtual Result Perform(const ArgumentTuple& args) {
+ return impl_->Perform(args);
+ }
+
+ private:
+ const internal::linked_ptr<ActionInterface<F2> > impl_;
+
+ GTEST_DISALLOW_ASSIGN_(ActionAdaptor);
+};
+
+// Helper struct to specialize ReturnAction to execute a move instead of a copy
+// on return. Useful for move-only types, but could be used on any type.
+template <typename T>
+struct ByMoveWrapper {
+ explicit ByMoveWrapper(T value) : payload(internal::move(value)) {}
+ T payload;
+};
+
+// Implements the polymorphic Return(x) action, which can be used in
+// any function that returns the type of x, regardless of the argument
+// types.
+//
+// Note: The value passed into Return must be converted into
+// Function<F>::Result when this action is cast to Action<F> rather than
+// when that action is performed. This is important in scenarios like
+//
+// MOCK_METHOD1(Method, T(U));
+// ...
+// {
+// Foo foo;
+// X x(&foo);
+// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x));
+// }
+//
+// In the example above the variable x holds reference to foo which leaves
+// scope and gets destroyed. If copying X just copies a reference to foo,
+// that copy will be left with a hanging reference. If conversion to T
+// makes a copy of foo, the above code is safe. To support that scenario, we
+// need to make sure that the type conversion happens inside the EXPECT_CALL
+// statement, and conversion of the result of Return to Action<T(U)> is a
+// good place for that.
+//
+template <typename R>
+class ReturnAction {
+ public:
+ // Constructs a ReturnAction object from the value to be returned.
+ // 'value' is passed by value instead of by const reference in order
+ // to allow Return("string literal") to compile.
+ explicit ReturnAction(R value) : value_(new R(internal::move(value))) {}
+
+ // This template type conversion operator allows Return(x) to be
+ // used in ANY function that returns x's type.
+ template <typename F>
+ operator Action<F>() const {
+ // Assert statement belongs here because this is the best place to verify
+ // conditions on F. It produces the clearest error messages
+ // in most compilers.
+ // Impl really belongs in this scope as a local class but can't
+ // because MSVC produces duplicate symbols in different translation units
+ // in this case. Until MS fixes that bug we put Impl into the class scope
+ // and put the typedef both here (for use in assert statement) and
+ // in the Impl class. But both definitions must be the same.
+ typedef typename Function<F>::Result Result;
+ GTEST_COMPILE_ASSERT_(
+ !is_reference<Result>::value,
+ use_ReturnRef_instead_of_Return_to_return_a_reference);
+ return Action<F>(new Impl<R, F>(value_));
+ }
+
+ private:
+ // Implements the Return(x) action for a particular function type F.
+ template <typename R_, typename F>
+ class Impl : public ActionInterface<F> {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+ // The implicit cast is necessary when Result has more than one
+ // single-argument constructor (e.g. Result is std::vector<int>) and R
+ // has a type conversion operator template. In that case, value_(value)
+ // won't compile as the compiler doesn't known which constructor of
+ // Result to call. ImplicitCast_ forces the compiler to convert R to
+ // Result without considering explicit constructors, thus resolving the
+ // ambiguity. value_ is then initialized using its copy constructor.
+ explicit Impl(const linked_ptr<R>& value)
+ : value_before_cast_(*value),
+ value_(ImplicitCast_<Result>(value_before_cast_)) {}
+
+ virtual Result Perform(const ArgumentTuple&) { return value_; }
+
+ private:
+ GTEST_COMPILE_ASSERT_(!is_reference<Result>::value,
+ Result_cannot_be_a_reference_type);
+ // We save the value before casting just in case it is being cast to a
+ // wrapper type.
+ R value_before_cast_;
+ Result value_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl);
+ };
+
+ // Partially specialize for ByMoveWrapper. This version of ReturnAction will
+ // move its contents instead.
+ template <typename R_, typename F>
+ class Impl<ByMoveWrapper<R_>, F> : public ActionInterface<F> {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+ explicit Impl(const linked_ptr<R>& wrapper)
+ : performed_(false), wrapper_(wrapper) {}
+
+ virtual Result Perform(const ArgumentTuple&) {
+ GTEST_CHECK_(!performed_)
+ << "A ByMove() action should only be performed once.";
+ performed_ = true;
+ return internal::move(wrapper_->payload);
+ }
+
+ private:
+ bool performed_;
+ const linked_ptr<R> wrapper_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ const linked_ptr<R> value_;
+
+ GTEST_DISALLOW_ASSIGN_(ReturnAction);
+};
+
+// Implements the ReturnNull() action.
+class ReturnNullAction {
+ public:
+ // Allows ReturnNull() to be used in any pointer-returning function. In C++11
+ // this is enforced by returning nullptr, and in non-C++11 by asserting a
+ // pointer type on compile time.
+ template <typename Result, typename ArgumentTuple>
+ static Result Perform(const ArgumentTuple&) {
+#if GTEST_LANG_CXX11
+ return nullptr;
+#else
+ GTEST_COMPILE_ASSERT_(internal::is_pointer<Result>::value,
+ ReturnNull_can_be_used_to_return_a_pointer_only);
+ return NULL;
+#endif // GTEST_LANG_CXX11
+ }
+};
+
+// Implements the Return() action.
+class ReturnVoidAction {
+ public:
+ // Allows Return() to be used in any void-returning function.
+ template <typename Result, typename ArgumentTuple>
+ static void Perform(const ArgumentTuple&) {
+ CompileAssertTypesEqual<void, Result>();
+ }
+};
+
+// Implements the polymorphic ReturnRef(x) action, which can be used
+// in any function that returns a reference to the type of x,
+// regardless of the argument types.
+template <typename T>
+class ReturnRefAction {
+ public:
+ // Constructs a ReturnRefAction object from the reference to be returned.
+ explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT
+
+ // This template type conversion operator allows ReturnRef(x) to be
+ // used in ANY function that returns a reference to x's type.
+ template <typename F>
+ operator Action<F>() const {
+ typedef typename Function<F>::Result Result;
+ // Asserts that the function return type is a reference. This
+ // catches the user error of using ReturnRef(x) when Return(x)
+ // should be used, and generates some helpful error message.
+ GTEST_COMPILE_ASSERT_(internal::is_reference<Result>::value,
+ use_Return_instead_of_ReturnRef_to_return_a_value);
+ return Action<F>(new Impl<F>(ref_));
+ }
+
+ private:
+ // Implements the ReturnRef(x) action for a particular function type F.
+ template <typename F>
+ class Impl : public ActionInterface<F> {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+ explicit Impl(T& ref) : ref_(ref) {} // NOLINT
+
+ virtual Result Perform(const ArgumentTuple&) {
+ return ref_;
+ }
+
+ private:
+ T& ref_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ T& ref_;
+
+ GTEST_DISALLOW_ASSIGN_(ReturnRefAction);
+};
+
+// Implements the polymorphic ReturnRefOfCopy(x) action, which can be
+// used in any function that returns a reference to the type of x,
+// regardless of the argument types.
+template <typename T>
+class ReturnRefOfCopyAction {
+ public:
+ // Constructs a ReturnRefOfCopyAction object from the reference to
+ // be returned.
+ explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT
+
+ // This template type conversion operator allows ReturnRefOfCopy(x) to be
+ // used in ANY function that returns a reference to x's type.
+ template <typename F>
+ operator Action<F>() const {
+ typedef typename Function<F>::Result Result;
+ // Asserts that the function return type is a reference. This
+ // catches the user error of using ReturnRefOfCopy(x) when Return(x)
+ // should be used, and generates some helpful error message.
+ GTEST_COMPILE_ASSERT_(
+ internal::is_reference<Result>::value,
+ use_Return_instead_of_ReturnRefOfCopy_to_return_a_value);
+ return Action<F>(new Impl<F>(value_));
+ }
+
+ private:
+ // Implements the ReturnRefOfCopy(x) action for a particular function type F.
+ template <typename F>
+ class Impl : public ActionInterface<F> {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+ explicit Impl(const T& value) : value_(value) {} // NOLINT
+
+ virtual Result Perform(const ArgumentTuple&) {
+ return value_;
+ }
+
+ private:
+ T value_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ const T value_;
+
+ GTEST_DISALLOW_ASSIGN_(ReturnRefOfCopyAction);
+};
+
+// Implements the polymorphic DoDefault() action.
+class DoDefaultAction {
+ public:
+ // This template type conversion operator allows DoDefault() to be
+ // used in any function.
+ template <typename F>
+ operator Action<F>() const { return Action<F>(NULL); }
+};
+
+// Implements the Assign action to set a given pointer referent to a
+// particular value.
+template <typename T1, typename T2>
+class AssignAction {
+ public:
+ AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {}
+
+ template <typename Result, typename ArgumentTuple>
+ void Perform(const ArgumentTuple& /* args */) const {
+ *ptr_ = value_;
+ }
+
+ private:
+ T1* const ptr_;
+ const T2 value_;
+
+ GTEST_DISALLOW_ASSIGN_(AssignAction);
+};
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Implements the SetErrnoAndReturn action to simulate return from
+// various system calls and libc functions.
+template <typename T>
+class SetErrnoAndReturnAction {
+ public:
+ SetErrnoAndReturnAction(int errno_value, T result)
+ : errno_(errno_value),
+ result_(result) {}
+ template <typename Result, typename ArgumentTuple>
+ Result Perform(const ArgumentTuple& /* args */) const {
+ errno = errno_;
+ return result_;
+ }
+
+ private:
+ const int errno_;
+ const T result_;
+
+ GTEST_DISALLOW_ASSIGN_(SetErrnoAndReturnAction);
+};
+
+#endif // !GTEST_OS_WINDOWS_MOBILE
+
+// Implements the SetArgumentPointee<N>(x) action for any function
+// whose N-th argument (0-based) is a pointer to x's type. The
+// template parameter kIsProto is true iff type A is ProtocolMessage,
+// proto2::Message, or a sub-class of those.
+template <size_t N, typename A, bool kIsProto>
+class SetArgumentPointeeAction {
+ public:
+ // Constructs an action that sets the variable pointed to by the
+ // N-th function argument to 'value'.
+ explicit SetArgumentPointeeAction(const A& value) : value_(value) {}
+
+ template <typename Result, typename ArgumentTuple>
+ void Perform(const ArgumentTuple& args) const {
+ CompileAssertTypesEqual<void, Result>();
+ *::testing::get<N>(args) = value_;
+ }
+
+ private:
+ const A value_;
+
+ GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
+};
+
+template <size_t N, typename Proto>
+class SetArgumentPointeeAction<N, Proto, true> {
+ public:
+ // Constructs an action that sets the variable pointed to by the
+ // N-th function argument to 'proto'. Both ProtocolMessage and
+ // proto2::Message have the CopyFrom() method, so the same
+ // implementation works for both.
+ explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) {
+ proto_->CopyFrom(proto);
+ }
+
+ template <typename Result, typename ArgumentTuple>
+ void Perform(const ArgumentTuple& args) const {
+ CompileAssertTypesEqual<void, Result>();
+ ::testing::get<N>(args)->CopyFrom(*proto_);
+ }
+
+ private:
+ const internal::linked_ptr<Proto> proto_;
+
+ GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
+};
+
+// Implements the InvokeWithoutArgs(f) action. The template argument
+// FunctionImpl is the implementation type of f, which can be either a
+// function pointer or a functor. InvokeWithoutArgs(f) can be used as an
+// Action<F> as long as f's type is compatible with F (i.e. f can be
+// assigned to a tr1::function<F>).
+template <typename FunctionImpl>
+class InvokeWithoutArgsAction {
+ public:
+ // The c'tor makes a copy of function_impl (either a function
+ // pointer or a functor).
+ explicit InvokeWithoutArgsAction(FunctionImpl function_impl)
+ : function_impl_(function_impl) {}
+
+ // Allows InvokeWithoutArgs(f) to be used as any action whose type is
+ // compatible with f.
+ template <typename Result, typename ArgumentTuple>
+ Result Perform(const ArgumentTuple&) { return function_impl_(); }
+
+ private:
+ FunctionImpl function_impl_;
+
+ GTEST_DISALLOW_ASSIGN_(InvokeWithoutArgsAction);
+};
+
+// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+class InvokeMethodWithoutArgsAction {
+ public:
+ InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr)
+ : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {}
+
+ template <typename Result, typename ArgumentTuple>
+ Result Perform(const ArgumentTuple&) const {
+ return (obj_ptr_->*method_ptr_)();
+ }
+
+ private:
+ Class* const obj_ptr_;
+ const MethodPtr method_ptr_;
+
+ GTEST_DISALLOW_ASSIGN_(InvokeMethodWithoutArgsAction);
+};
+
+// Implements the IgnoreResult(action) action.
+template <typename A>
+class IgnoreResultAction {
+ public:
+ explicit IgnoreResultAction(const A& action) : action_(action) {}
+
+ template <typename F>
+ operator Action<F>() const {
+ // Assert statement belongs here because this is the best place to verify
+ // conditions on F. It produces the clearest error messages
+ // in most compilers.
+ // Impl really belongs in this scope as a local class but can't
+ // because MSVC produces duplicate symbols in different translation units
+ // in this case. Until MS fixes that bug we put Impl into the class scope
+ // and put the typedef both here (for use in assert statement) and
+ // in the Impl class. But both definitions must be the same.
+ typedef typename internal::Function<F>::Result Result;
+
+ // Asserts at compile time that F returns void.
+ CompileAssertTypesEqual<void, Result>();
+
+ return Action<F>(new Impl<F>(action_));
+ }
+
+ private:
+ template <typename F>
+ class Impl : public ActionInterface<F> {
+ public:
+ typedef typename internal::Function<F>::Result Result;
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ explicit Impl(const A& action) : action_(action) {}
+
+ virtual void Perform(const ArgumentTuple& args) {
+ // Performs the action and ignores its result.
+ action_.Perform(args);
+ }
+
+ private:
+ // Type OriginalFunction is the same as F except that its return
+ // type is IgnoredValue.
+ typedef typename internal::Function<F>::MakeResultIgnoredValue
+ OriginalFunction;
+
+ const Action<OriginalFunction> action_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ const A action_;
+
+ GTEST_DISALLOW_ASSIGN_(IgnoreResultAction);
+};
+
+// A ReferenceWrapper<T> object represents a reference to type T,
+// which can be either const or not. It can be explicitly converted
+// from, and implicitly converted to, a T&. Unlike a reference,
+// ReferenceWrapper<T> can be copied and can survive template type
+// inference. This is used to support by-reference arguments in the
+// InvokeArgument<N>(...) action. The idea was from "reference
+// wrappers" in tr1, which we don't have in our source tree yet.
+template <typename T>
+class ReferenceWrapper {
+ public:
+ // Constructs a ReferenceWrapper<T> object from a T&.
+ explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {} // NOLINT
+
+ // Allows a ReferenceWrapper<T> object to be implicitly converted to
+ // a T&.
+ operator T&() const { return *pointer_; }
+ private:
+ T* pointer_;
+};
+
+// Allows the expression ByRef(x) to be printed as a reference to x.
+template <typename T>
+void PrintTo(const ReferenceWrapper<T>& ref, ::std::ostream* os) {
+ T& value = ref;
+ UniversalPrinter<T&>::Print(value, os);
+}
+
+// Does two actions sequentially. Used for implementing the DoAll(a1,
+// a2, ...) action.
+template <typename Action1, typename Action2>
+class DoBothAction {
+ public:
+ DoBothAction(Action1 action1, Action2 action2)
+ : action1_(action1), action2_(action2) {}
+
+ // This template type conversion operator allows DoAll(a1, ..., a_n)
+ // to be used in ANY function of compatible type.
+ template <typename F>
+ operator Action<F>() const {
+ return Action<F>(new Impl<F>(action1_, action2_));
+ }
+
+ private:
+ // Implements the DoAll(...) action for a particular function type F.
+ template <typename F>
+ class Impl : public ActionInterface<F> {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+ typedef typename Function<F>::MakeResultVoid VoidResult;
+
+ Impl(const Action<VoidResult>& action1, const Action<F>& action2)
+ : action1_(action1), action2_(action2) {}
+
+ virtual Result Perform(const ArgumentTuple& args) {
+ action1_.Perform(args);
+ return action2_.Perform(args);
+ }
+
+ private:
+ const Action<VoidResult> action1_;
+ const Action<F> action2_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ Action1 action1_;
+ Action2 action2_;
+
+ GTEST_DISALLOW_ASSIGN_(DoBothAction);
+};
+
+} // namespace internal
+
+// An Unused object can be implicitly constructed from ANY value.
+// This is handy when defining actions that ignore some or all of the
+// mock function arguments. For example, given
+//
+// MOCK_METHOD3(Foo, double(const string& label, double x, double y));
+// MOCK_METHOD3(Bar, double(int index, double x, double y));
+//
+// instead of
+//
+// double DistanceToOriginWithLabel(const string& label, double x, double y) {
+// return sqrt(x*x + y*y);
+// }
+// double DistanceToOriginWithIndex(int index, double x, double y) {
+// return sqrt(x*x + y*y);
+// }
+// ...
+// EXEPCT_CALL(mock, Foo("abc", _, _))
+// .WillOnce(Invoke(DistanceToOriginWithLabel));
+// EXEPCT_CALL(mock, Bar(5, _, _))
+// .WillOnce(Invoke(DistanceToOriginWithIndex));
+//
+// you could write
+//
+// // We can declare any uninteresting argument as Unused.
+// double DistanceToOrigin(Unused, double x, double y) {
+// return sqrt(x*x + y*y);
+// }
+// ...
+// EXEPCT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin));
+// EXEPCT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin));
+typedef internal::IgnoredValue Unused;
+
+// This constructor allows us to turn an Action<From> object into an
+// Action<To>, as long as To's arguments can be implicitly converted
+// to From's and From's return type cann be implicitly converted to
+// To's.
+template <typename To>
+template <typename From>
+Action<To>::Action(const Action<From>& from)
+ : impl_(new internal::ActionAdaptor<To, From>(from)) {}
+
+// Creates an action that returns 'value'. 'value' is passed by value
+// instead of const reference - otherwise Return("string literal")
+// will trigger a compiler error about using array as initializer.
+template <typename R>
+internal::ReturnAction<R> Return(R value) {
+ return internal::ReturnAction<R>(internal::move(value));
+}
+
+// Creates an action that returns NULL.
+inline PolymorphicAction<internal::ReturnNullAction> ReturnNull() {
+ return MakePolymorphicAction(internal::ReturnNullAction());
+}
+
+// Creates an action that returns from a void function.
+inline PolymorphicAction<internal::ReturnVoidAction> Return() {
+ return MakePolymorphicAction(internal::ReturnVoidAction());
+}
+
+// Creates an action that returns the reference to a variable.
+template <typename R>
+inline internal::ReturnRefAction<R> ReturnRef(R& x) { // NOLINT
+ return internal::ReturnRefAction<R>(x);
+}
+
+// Creates an action that returns the reference to a copy of the
+// argument. The copy is created when the action is constructed and
+// lives as long as the action.
+template <typename R>
+inline internal::ReturnRefOfCopyAction<R> ReturnRefOfCopy(const R& x) {
+ return internal::ReturnRefOfCopyAction<R>(x);
+}
+
+// Modifies the parent action (a Return() action) to perform a move of the
+// argument instead of a copy.
+// Return(ByMove()) actions can only be executed once and will assert this
+// invariant.
+template <typename R>
+internal::ByMoveWrapper<R> ByMove(R x) {
+ return internal::ByMoveWrapper<R>(internal::move(x));
+}
+
+// Creates an action that does the default action for the give mock function.
+inline internal::DoDefaultAction DoDefault() {
+ return internal::DoDefaultAction();
+}
+
+// Creates an action that sets the variable pointed by the N-th
+// (0-based) function argument to 'value'.
+template <size_t N, typename T>
+PolymorphicAction<
+ internal::SetArgumentPointeeAction<
+ N, T, internal::IsAProtocolMessage<T>::value> >
+SetArgPointee(const T& x) {
+ return MakePolymorphicAction(internal::SetArgumentPointeeAction<
+ N, T, internal::IsAProtocolMessage<T>::value>(x));
+}
+
+#if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN)
+// This overload allows SetArgPointee() to accept a string literal.
+// GCC prior to the version 4.0 and Symbian C++ compiler cannot distinguish
+// this overload from the templated version and emit a compile error.
+template <size_t N>
+PolymorphicAction<
+ internal::SetArgumentPointeeAction<N, const char*, false> >
+SetArgPointee(const char* p) {
+ return MakePolymorphicAction(internal::SetArgumentPointeeAction<
+ N, const char*, false>(p));
+}
+
+template <size_t N>
+PolymorphicAction<
+ internal::SetArgumentPointeeAction<N, const wchar_t*, false> >
+SetArgPointee(const wchar_t* p) {
+ return MakePolymorphicAction(internal::SetArgumentPointeeAction<
+ N, const wchar_t*, false>(p));
+}
+#endif
+
+// The following version is DEPRECATED.
+template <size_t N, typename T>
+PolymorphicAction<
+ internal::SetArgumentPointeeAction<
+ N, T, internal::IsAProtocolMessage<T>::value> >
+SetArgumentPointee(const T& x) {
+ return MakePolymorphicAction(internal::SetArgumentPointeeAction<
+ N, T, internal::IsAProtocolMessage<T>::value>(x));
+}
+
+// Creates an action that sets a pointer referent to a given value.
+template <typename T1, typename T2>
+PolymorphicAction<internal::AssignAction<T1, T2> > Assign(T1* ptr, T2 val) {
+ return MakePolymorphicAction(internal::AssignAction<T1, T2>(ptr, val));
+}
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Creates an action that sets errno and returns the appropriate error.
+template <typename T>
+PolymorphicAction<internal::SetErrnoAndReturnAction<T> >
+SetErrnoAndReturn(int errval, T result) {
+ return MakePolymorphicAction(
+ internal::SetErrnoAndReturnAction<T>(errval, result));
+}
+
+#endif // !GTEST_OS_WINDOWS_MOBILE
+
+// Various overloads for InvokeWithoutArgs().
+
+// Creates an action that invokes 'function_impl' with no argument.
+template <typename FunctionImpl>
+PolymorphicAction<internal::InvokeWithoutArgsAction<FunctionImpl> >
+InvokeWithoutArgs(FunctionImpl function_impl) {
+ return MakePolymorphicAction(
+ internal::InvokeWithoutArgsAction<FunctionImpl>(function_impl));
+}
+
+// Creates an action that invokes the given method on the given object
+// with no argument.
+template <class Class, typename MethodPtr>
+PolymorphicAction<internal::InvokeMethodWithoutArgsAction<Class, MethodPtr> >
+InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) {
+ return MakePolymorphicAction(
+ internal::InvokeMethodWithoutArgsAction<Class, MethodPtr>(
+ obj_ptr, method_ptr));
+}
+
+// Creates an action that performs an_action and throws away its
+// result. In other words, it changes the return type of an_action to
+// void. an_action MUST NOT return void, or the code won't compile.
+template <typename A>
+inline internal::IgnoreResultAction<A> IgnoreResult(const A& an_action) {
+ return internal::IgnoreResultAction<A>(an_action);
+}
+
+// Creates a reference wrapper for the given L-value. If necessary,
+// you can explicitly specify the type of the reference. For example,
+// suppose 'derived' is an object of type Derived, ByRef(derived)
+// would wrap a Derived&. If you want to wrap a const Base& instead,
+// where Base is a base class of Derived, just write:
+//
+// ByRef<const Base>(derived)
+template <typename T>
+inline internal::ReferenceWrapper<T> ByRef(T& l_value) { // NOLINT
+ return internal::ReferenceWrapper<T>(l_value);
+}
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
diff --git a/extern/gmock/include/gmock/gmock-cardinalities.h b/extern/gmock/include/gmock/gmock-cardinalities.h
new file mode 100644
index 00000000000..fc315f92ab5
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-cardinalities.h
@@ -0,0 +1,147 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used cardinalities. More
+// cardinalities can be defined by the user implementing the
+// CardinalityInterface interface if necessary.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+
+#include <limits.h>
+#include <ostream> // NOLINT
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// To implement a cardinality Foo, define:
+// 1. a class FooCardinality that implements the
+// CardinalityInterface interface, and
+// 2. a factory function that creates a Cardinality object from a
+// const FooCardinality*.
+//
+// The two-level delegation design follows that of Matcher, providing
+// consistency for extension developers. It also eases ownership
+// management as Cardinality objects can now be copied like plain values.
+
+// The implementation of a cardinality.
+class CardinalityInterface {
+ public:
+ virtual ~CardinalityInterface() {}
+
+ // Conservative estimate on the lower/upper bound of the number of
+ // calls allowed.
+ virtual int ConservativeLowerBound() const { return 0; }
+ virtual int ConservativeUpperBound() const { return INT_MAX; }
+
+ // Returns true iff call_count calls will satisfy this cardinality.
+ virtual bool IsSatisfiedByCallCount(int call_count) const = 0;
+
+ // Returns true iff call_count calls will saturate this cardinality.
+ virtual bool IsSaturatedByCallCount(int call_count) const = 0;
+
+ // Describes self to an ostream.
+ virtual void DescribeTo(::std::ostream* os) const = 0;
+};
+
+// A Cardinality is a copyable and IMMUTABLE (except by assignment)
+// object that specifies how many times a mock function is expected to
+// be called. The implementation of Cardinality is just a linked_ptr
+// to const CardinalityInterface, so copying is fairly cheap.
+// Don't inherit from Cardinality!
+class GTEST_API_ Cardinality {
+ public:
+ // Constructs a null cardinality. Needed for storing Cardinality
+ // objects in STL containers.
+ Cardinality() {}
+
+ // Constructs a Cardinality from its implementation.
+ explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {}
+
+ // Conservative estimate on the lower/upper bound of the number of
+ // calls allowed.
+ int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); }
+ int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); }
+
+ // Returns true iff call_count calls will satisfy this cardinality.
+ bool IsSatisfiedByCallCount(int call_count) const {
+ return impl_->IsSatisfiedByCallCount(call_count);
+ }
+
+ // Returns true iff call_count calls will saturate this cardinality.
+ bool IsSaturatedByCallCount(int call_count) const {
+ return impl_->IsSaturatedByCallCount(call_count);
+ }
+
+ // Returns true iff call_count calls will over-saturate this
+ // cardinality, i.e. exceed the maximum number of allowed calls.
+ bool IsOverSaturatedByCallCount(int call_count) const {
+ return impl_->IsSaturatedByCallCount(call_count) &&
+ !impl_->IsSatisfiedByCallCount(call_count);
+ }
+
+ // Describes self to an ostream
+ void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+
+ // Describes the given actual call count to an ostream.
+ static void DescribeActualCallCountTo(int actual_call_count,
+ ::std::ostream* os);
+
+ private:
+ internal::linked_ptr<const CardinalityInterface> impl_;
+};
+
+// Creates a cardinality that allows at least n calls.
+GTEST_API_ Cardinality AtLeast(int n);
+
+// Creates a cardinality that allows at most n calls.
+GTEST_API_ Cardinality AtMost(int n);
+
+// Creates a cardinality that allows any number of calls.
+GTEST_API_ Cardinality AnyNumber();
+
+// Creates a cardinality that allows between min and max calls.
+GTEST_API_ Cardinality Between(int min, int max);
+
+// Creates a cardinality that allows exactly n calls.
+GTEST_API_ Cardinality Exactly(int n);
+
+// Creates a cardinality from its implementation.
+inline Cardinality MakeCardinality(const CardinalityInterface* c) {
+ return Cardinality(c);
+}
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
diff --git a/extern/gmock/include/gmock/gmock-generated-actions.h b/extern/gmock/include/gmock/gmock-generated-actions.h
new file mode 100644
index 00000000000..b5a889c0c3a
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-generated-actions.h
@@ -0,0 +1,2377 @@
+// This file was GENERATED by a script. DO NOT EDIT BY HAND!!!
+
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used variadic actions.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
+
+#include "gmock/gmock-actions.h"
+#include "gmock/internal/gmock-port.h"
+
+namespace testing {
+namespace internal {
+
+// InvokeHelper<F> knows how to unpack an N-tuple and invoke an N-ary
+// function or method with the unpacked values, where F is a function
+// type that takes N arguments.
+template <typename Result, typename ArgumentTuple>
+class InvokeHelper;
+
+template <typename R>
+class InvokeHelper<R, ::testing::tuple<> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<>&) {
+ return function();
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<>&) {
+ return (obj_ptr->*method_ptr)();
+ }
+};
+
+template <typename R, typename A1>
+class InvokeHelper<R, ::testing::tuple<A1> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1>& args) {
+ return function(get<0>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2>
+class InvokeHelper<R, ::testing::tuple<A1, A2> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2>& args) {
+ return function(get<0>(args), get<1>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3,
+ A4>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4,
+ A5>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args), get<4>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4, A5>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args), get<4>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
+ A6>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args), get<4>(args), get<5>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4, A5, A6>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args), get<4>(args), get<5>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
+ A6, A7>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args), get<4>(args), get<5>(args), get<6>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4, A5, A6,
+ A7>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args), get<4>(args), get<5>(args),
+ get<6>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
+ A6, A7, A8>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args), get<4>(args), get<5>(args), get<6>(args),
+ get<7>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4, A5, A6, A7,
+ A8>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args), get<4>(args), get<5>(args),
+ get<6>(args), get<7>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
+ A6, A7, A8, A9>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args), get<4>(args), get<5>(args), get<6>(args),
+ get<7>(args), get<8>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8,
+ A9>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args), get<4>(args), get<5>(args),
+ get<6>(args), get<7>(args), get<8>(args));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9,
+ typename A10>
+class InvokeHelper<R, ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10> > {
+ public:
+ template <typename Function>
+ static R Invoke(Function function, const ::testing::tuple<A1, A2, A3, A4, A5,
+ A6, A7, A8, A9, A10>& args) {
+ return function(get<0>(args), get<1>(args), get<2>(args),
+ get<3>(args), get<4>(args), get<5>(args), get<6>(args),
+ get<7>(args), get<8>(args), get<9>(args));
+ }
+
+ template <class Class, typename MethodPtr>
+ static R InvokeMethod(Class* obj_ptr,
+ MethodPtr method_ptr,
+ const ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8,
+ A9, A10>& args) {
+ return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args),
+ get<2>(args), get<3>(args), get<4>(args), get<5>(args),
+ get<6>(args), get<7>(args), get<8>(args), get<9>(args));
+ }
+};
+
+// An INTERNAL macro for extracting the type of a tuple field. It's
+// subject to change without notice - DO NOT USE IN USER CODE!
+#define GMOCK_FIELD_(Tuple, N) \
+ typename ::testing::tuple_element<N, Tuple>::type
+
+// SelectArgs<Result, ArgumentTuple, k1, k2, ..., k_n>::type is the
+// type of an n-ary function whose i-th (1-based) argument type is the
+// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple
+// type, and whose return type is Result. For example,
+// SelectArgs<int, ::testing::tuple<bool, char, double, long>, 0, 3>::type
+// is int(bool, long).
+//
+// SelectArgs<Result, ArgumentTuple, k1, k2, ..., k_n>::Select(args)
+// returns the selected fields (k1, k2, ..., k_n) of args as a tuple.
+// For example,
+// SelectArgs<int, tuple<bool, char, double>, 2, 0>::Select(
+// ::testing::make_tuple(true, 'a', 2.5))
+// returns tuple (2.5, true).
+//
+// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be
+// in the range [0, 10]. Duplicates are allowed and they don't have
+// to be in an ascending or descending order.
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4, int k5, int k6, int k7, int k8, int k9, int k10>
+class SelectArgs {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+ GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
+ GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9),
+ GMOCK_FIELD_(ArgumentTuple, k10));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
+ get<k8>(args), get<k9>(args), get<k10>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple>
+class SelectArgs<Result, ArgumentTuple,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef Result type();
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& /* args */) {
+ return SelectedArgs();
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1>
+class SelectArgs<Result, ArgumentTuple,
+ k1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4, int k5>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, k5, -1, -1, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args), get<k5>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4, int k5, int k6>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, k5, k6, -1, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+ GMOCK_FIELD_(ArgumentTuple, k6));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args), get<k5>(args), get<k6>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4, int k5, int k6, int k7>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, k5, k6, k7, -1, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+ GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4, int k5, int k6, int k7, int k8>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, k5, k6, k7, k8, -1, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+ GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
+ GMOCK_FIELD_(ArgumentTuple, k8));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
+ get<k8>(args));
+ }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+ int k4, int k5, int k6, int k7, int k8, int k9>
+class SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, k5, k6, k7, k8, k9, -1> {
+ public:
+ typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+ GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+ GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+ GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
+ GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9));
+ typedef typename Function<type>::ArgumentTuple SelectedArgs;
+ static SelectedArgs Select(const ArgumentTuple& args) {
+ return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+ get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
+ get<k8>(args), get<k9>(args));
+ }
+};
+
+#undef GMOCK_FIELD_
+
+// Implements the WithArgs action.
+template <typename InnerAction, int k1 = -1, int k2 = -1, int k3 = -1,
+ int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
+ int k9 = -1, int k10 = -1>
+class WithArgsAction {
+ public:
+ explicit WithArgsAction(const InnerAction& action) : action_(action) {}
+
+ template <typename F>
+ operator Action<F>() const { return MakeAction(new Impl<F>(action_)); }
+
+ private:
+ template <typename F>
+ class Impl : public ActionInterface<F> {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+ explicit Impl(const InnerAction& action) : action_(action) {}
+
+ virtual Result Perform(const ArgumentTuple& args) {
+ return action_.Perform(SelectArgs<Result, ArgumentTuple, k1, k2, k3, k4,
+ k5, k6, k7, k8, k9, k10>::Select(args));
+ }
+
+ private:
+ typedef typename SelectArgs<Result, ArgumentTuple,
+ k1, k2, k3, k4, k5, k6, k7, k8, k9, k10>::type InnerFunctionType;
+
+ Action<InnerFunctionType> action_;
+ };
+
+ const InnerAction action_;
+
+ GTEST_DISALLOW_ASSIGN_(WithArgsAction);
+};
+
+// A macro from the ACTION* family (defined later in this file)
+// defines an action that can be used in a mock function. Typically,
+// these actions only care about a subset of the arguments of the mock
+// function. For example, if such an action only uses the second
+// argument, it can be used in any mock function that takes >= 2
+// arguments where the type of the second argument is compatible.
+//
+// Therefore, the action implementation must be prepared to take more
+// arguments than it needs. The ExcessiveArg type is used to
+// represent those excessive arguments. In order to keep the compiler
+// error messages tractable, we define it in the testing namespace
+// instead of testing::internal. However, this is an INTERNAL TYPE
+// and subject to change without notice, so a user MUST NOT USE THIS
+// TYPE DIRECTLY.
+struct ExcessiveArg {};
+
+// A helper class needed for implementing the ACTION* macros.
+template <typename Result, class Impl>
+class ActionHelper {
+ public:
+ static Result Perform(Impl* impl, const ::testing::tuple<>& args) {
+ return impl->template gmock_PerformImpl<>(args, ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0>& args) {
+ return impl->template gmock_PerformImpl<A0>(args, get<0>(args),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1>& args) {
+ return impl->template gmock_PerformImpl<A0, A1>(args, get<0>(args),
+ get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2>(args, get<0>(args),
+ get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2,
+ A3>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3>(args, get<0>(args),
+ get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3, typename A4>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3,
+ A4>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4>(args,
+ get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
+ ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
+ A5>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5>(args,
+ get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
+ get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
+ A5, A6>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6>(args,
+ get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
+ get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
+ A5, A6, A7>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6,
+ A7>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+ get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
+ A5, A6, A7, A8>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7,
+ A8>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+ get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
+ ExcessiveArg());
+ }
+
+ template <typename A0, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9>
+ static Result Perform(Impl* impl, const ::testing::tuple<A0, A1, A2, A3, A4,
+ A5, A6, A7, A8, A9>& args) {
+ return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7, A8,
+ A9>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+ get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
+ get<9>(args));
+ }
+};
+
+} // namespace internal
+
+// Various overloads for Invoke().
+
+// WithArgs<N1, N2, ..., Nk>(an_action) creates an action that passes
+// the selected arguments of the mock function to an_action and
+// performs it. It serves as an adaptor between actions with
+// different argument lists. C++ doesn't support default arguments for
+// function templates, so we have to overload it.
+template <int k1, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1>(action);
+}
+
+template <int k1, int k2, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2>(action);
+}
+
+template <int k1, int k2, int k3, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3>(action);
+}
+
+template <int k1, int k2, int k3, int k4, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7,
+ typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6,
+ k7>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+ typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7,
+ k8>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+ int k9, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8, k9>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
+ k9>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+ int k9, int k10, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
+ k9, k10>
+WithArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
+ k9, k10>(action);
+}
+
+// Creates an action that does actions a1, a2, ..., sequentially in
+// each invocation.
+template <typename Action1, typename Action2>
+inline internal::DoBothAction<Action1, Action2>
+DoAll(Action1 a1, Action2 a2) {
+ return internal::DoBothAction<Action1, Action2>(a1, a2);
+}
+
+template <typename Action1, typename Action2, typename Action3>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ Action3> >
+DoAll(Action1 a1, Action2 a2, Action3 a3) {
+ return DoAll(a1, DoAll(a2, a3));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, Action4> > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) {
+ return DoAll(a1, DoAll(a2, a3, a4));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4, typename Action5>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+ Action5> > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) {
+ return DoAll(a1, DoAll(a2, a3, a4, a5));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4, typename Action5, typename Action6>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+ internal::DoBothAction<Action5, Action6> > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) {
+ return DoAll(a1, DoAll(a2, a3, a4, a5, a6));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4, typename Action5, typename Action6, typename Action7>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+ internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+ Action7> > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+ Action7 a7) {
+ return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4, typename Action5, typename Action6, typename Action7,
+ typename Action8>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+ internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+ internal::DoBothAction<Action7, Action8> > > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+ Action7 a7, Action8 a8) {
+ return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4, typename Action5, typename Action6, typename Action7,
+ typename Action8, typename Action9>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+ internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+ internal::DoBothAction<Action7, internal::DoBothAction<Action8,
+ Action9> > > > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+ Action7 a7, Action8 a8, Action9 a9) {
+ return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+ typename Action4, typename Action5, typename Action6, typename Action7,
+ typename Action8, typename Action9, typename Action10>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+ internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+ internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+ internal::DoBothAction<Action7, internal::DoBothAction<Action8,
+ internal::DoBothAction<Action9, Action10> > > > > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+ Action7 a7, Action8 a8, Action9 a9, Action10 a10) {
+ return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10));
+}
+
+} // namespace testing
+
+// The ACTION* family of macros can be used in a namespace scope to
+// define custom actions easily. The syntax:
+//
+// ACTION(name) { statements; }
+//
+// will define an action with the given name that executes the
+// statements. The value returned by the statements will be used as
+// the return value of the action. Inside the statements, you can
+// refer to the K-th (0-based) argument of the mock function by
+// 'argK', and refer to its type by 'argK_type'. For example:
+//
+// ACTION(IncrementArg1) {
+// arg1_type temp = arg1;
+// return ++(*temp);
+// }
+//
+// allows you to write
+//
+// ...WillOnce(IncrementArg1());
+//
+// You can also refer to the entire argument tuple and its type by
+// 'args' and 'args_type', and refer to the mock function type and its
+// return type by 'function_type' and 'return_type'.
+//
+// Note that you don't need to specify the types of the mock function
+// arguments. However rest assured that your code is still type-safe:
+// you'll get a compiler error if *arg1 doesn't support the ++
+// operator, or if the type of ++(*arg1) isn't compatible with the
+// mock function's return type, for example.
+//
+// Sometimes you'll want to parameterize the action. For that you can use
+// another macro:
+//
+// ACTION_P(name, param_name) { statements; }
+//
+// For example:
+//
+// ACTION_P(Add, n) { return arg0 + n; }
+//
+// will allow you to write:
+//
+// ...WillOnce(Add(5));
+//
+// Note that you don't need to provide the type of the parameter
+// either. If you need to reference the type of a parameter named
+// 'foo', you can write 'foo_type'. For example, in the body of
+// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type
+// of 'n'.
+//
+// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support
+// multi-parameter actions.
+//
+// For the purpose of typing, you can view
+//
+// ACTION_Pk(Foo, p1, ..., pk) { ... }
+//
+// as shorthand for
+//
+// template <typename p1_type, ..., typename pk_type>
+// FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// In particular, you can provide the template type arguments
+// explicitly when invoking Foo(), as in Foo<long, bool>(5, false);
+// although usually you can rely on the compiler to infer the types
+// for you automatically. You can assign the result of expression
+// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ...,
+// pk_type>. This can be useful when composing actions.
+//
+// You can also overload actions with different numbers of parameters:
+//
+// ACTION_P(Plus, a) { ... }
+// ACTION_P2(Plus, a, b) { ... }
+//
+// While it's tempting to always use the ACTION* macros when defining
+// a new action, you should also consider implementing ActionInterface
+// or using MakePolymorphicAction() instead, especially if you need to
+// use the action a lot. While these approaches require more work,
+// they give you more control on the types of the mock function
+// arguments and the action parameters, which in general leads to
+// better compiler error messages that pay off in the long run. They
+// also allow overloading actions based on parameter types (as opposed
+// to just based on the number of parameters).
+//
+// CAVEAT:
+//
+// ACTION*() can only be used in a namespace scope. The reason is
+// that C++ doesn't yet allow function-local types to be used to
+// instantiate templates. The up-coming C++0x standard will fix this.
+// Once that's done, we'll consider supporting using ACTION*() inside
+// a function.
+//
+// MORE INFORMATION:
+//
+// To learn more about using these macros, please search for 'ACTION'
+// on http://code.google.com/p/googlemock/wiki/CookBook.
+
+// An internal macro needed for implementing ACTION*().
+#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\
+ const args_type& args GTEST_ATTRIBUTE_UNUSED_, \
+ arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_, \
+ arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_, \
+ arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_, \
+ arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_, \
+ arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_, \
+ arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_, \
+ arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_, \
+ arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_, \
+ arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_, \
+ arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_
+
+// Sometimes you want to give an action explicit template parameters
+// that cannot be inferred from its value parameters. ACTION() and
+// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that
+// and can be viewed as an extension to ACTION() and ACTION_P*().
+//
+// The syntax:
+//
+// ACTION_TEMPLATE(ActionName,
+// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),
+// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }
+//
+// defines an action template that takes m explicit template
+// parameters and n value parameters. name_i is the name of the i-th
+// template parameter, and kind_i specifies whether it's a typename,
+// an integral constant, or a template. p_i is the name of the i-th
+// value parameter.
+//
+// Example:
+//
+// // DuplicateArg<k, T>(output) converts the k-th argument of the mock
+// // function to type T and copies it to *output.
+// ACTION_TEMPLATE(DuplicateArg,
+// HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
+// AND_1_VALUE_PARAMS(output)) {
+// *output = T(::testing::get<k>(args));
+// }
+// ...
+// int n;
+// EXPECT_CALL(mock, Foo(_, _))
+// .WillOnce(DuplicateArg<1, unsigned char>(&n));
+//
+// To create an instance of an action template, write:
+//
+// ActionName<t1, ..., t_m>(v1, ..., v_n)
+//
+// where the ts are the template arguments and the vs are the value
+// arguments. The value argument types are inferred by the compiler.
+// If you want to explicitly specify the value argument types, you can
+// provide additional template arguments:
+//
+// ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
+//
+// where u_i is the desired type of v_i.
+//
+// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the
+// number of value parameters, but not on the number of template
+// parameters. Without the restriction, the meaning of the following
+// is unclear:
+//
+// OverloadedAction<int, bool>(x);
+//
+// Are we using a single-template-parameter action where 'bool' refers
+// to the type of x, or are we using a two-template-parameter action
+// where the compiler is asked to infer the type of x?
+//
+// Implementation notes:
+//
+// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and
+// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for
+// implementing ACTION_TEMPLATE. The main trick we use is to create
+// new macro invocations when expanding a macro. For example, we have
+//
+// #define ACTION_TEMPLATE(name, template_params, value_params)
+// ... GMOCK_INTERNAL_DECL_##template_params ...
+//
+// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...)
+// to expand to
+//
+// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ...
+//
+// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the
+// preprocessor will continue to expand it to
+//
+// ... typename T ...
+//
+// This technique conforms to the C++ standard and is portable. It
+// allows us to implement action templates using O(N) code, where N is
+// the maximum number of template/value parameters supported. Without
+// using it, we'd have to devote O(N^2) amount of code to implement all
+// combinations of m and n.
+
+// Declares the template parameters.
+#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0
+#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+ name1) kind0 name0, kind1 name1
+#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2) kind0 name0, kind1 name1, kind2 name2
+#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \
+ kind3 name3
+#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \
+ kind2 name2, kind3 name3, kind4 name4
+#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \
+ kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5
+#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+ name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \
+ kind5 name5, kind6 name6
+#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+ kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \
+ kind4 name4, kind5 name5, kind6 name6, kind7 name7
+#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+ kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \
+ kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \
+ kind8 name8
+#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+ name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+ name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \
+ kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \
+ kind6 name6, kind7 name7, kind8 name8, kind9 name9
+
+// Lists the template parameters.
+#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0
+#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+ name1) name0, name1
+#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2) name0, name1, name2
+#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3) name0, name1, name2, name3
+#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \
+ name4
+#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \
+ name2, name3, name4, name5
+#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+ name6) name0, name1, name2, name3, name4, name5, name6
+#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+ kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7
+#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+ kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \
+ name6, name7, name8
+#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+ name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+ name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \
+ name3, name4, name5, name6, name7, name8, name9
+
+// Declares the types of value parameters.
+#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \
+ typename p0##_type, typename p1##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \
+ typename p0##_type, typename p1##_type, typename p2##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+ typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+ typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+ typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6) , typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \
+ typename p2##_type, typename p3##_type, typename p4##_type, \
+ typename p5##_type, typename p6##_type, typename p7##_type, \
+ typename p8##_type, typename p9##_type
+
+// Initializes the value parameters.
+#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\
+ ()
+#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\
+ (p0##_type gmock_p0) : p0(gmock_p0)
+#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\
+ (p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), p1(gmock_p1)
+#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2)
+#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3)
+#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3), p4(gmock_p4)
+#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5)
+#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6)
+#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7)
+#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7, \
+ p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+ p8(gmock_p8)
+#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8, p9)\
+ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+ p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+ p8(gmock_p8), p9(gmock_p9)
+
+// Declares the fields for storing the value parameters.
+#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0;
+#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \
+ p1##_type p1;
+#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \
+ p1##_type p1; p2##_type p2;
+#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \
+ p1##_type p1; p2##_type p2; p3##_type p3;
+#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+ p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4;
+#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+ p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+ p5##_type p5;
+#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+ p5##_type p5; p6##_type p6;
+#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+ p5##_type p5; p6##_type p6; p7##_type p7;
+#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+ p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8;
+#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+ p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \
+ p9##_type p9;
+
+// Lists the value parameters.
+#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0
+#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1
+#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2
+#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3
+#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \
+ p2, p3, p4
+#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \
+ p1, p2, p3, p4, p5
+#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6) p0, p1, p2, p3, p4, p5, p6
+#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7) p0, p1, p2, p3, p4, p5, p6, p7
+#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8
+#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9
+
+// Lists the value parameter types.
+#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \
+ p1##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \
+ p1##_type, p2##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+ p0##_type, p1##_type, p2##_type, p3##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+ p0##_type, p1##_type, p2##_type, p3##_type, p4##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+ p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \
+ p6##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, p7##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, p7##_type, p8##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, p7##_type, p8##_type, p9##_type
+
+// Declares the value parameters.
+#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0
+#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \
+ p1##_type p1
+#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \
+ p1##_type p1, p2##_type p2
+#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \
+ p1##_type p1, p2##_type p2, p3##_type p3
+#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+ p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4
+#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+ p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+ p5##_type p5
+#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+ p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+ p5##_type p5, p6##_type p6
+#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+ p5##_type p5, p6##_type p6, p7##_type p7
+#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8
+#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+ p9##_type p9
+
+// The suffix of the class template implementing the action template.
+#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P
+#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2
+#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3
+#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4
+#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5
+#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6
+#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7
+#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7) P8
+#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8) P9
+#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+ p7, p8, p9) P10
+
+// The name of the class template implementing the action template.
+#define GMOCK_ACTION_CLASS_(name, value_params)\
+ GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params)
+
+#define ACTION_TEMPLATE(name, template_params, value_params)\
+ template <GMOCK_INTERNAL_DECL_##template_params\
+ GMOCK_INTERNAL_DECL_TYPE_##value_params>\
+ class GMOCK_ACTION_CLASS_(name, value_params) {\
+ public:\
+ explicit GMOCK_ACTION_CLASS_(name, value_params)\
+ GMOCK_INTERNAL_INIT_##value_params {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ GMOCK_INTERNAL_DEFN_##value_params\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(\
+ new gmock_Impl<F>(GMOCK_INTERNAL_LIST_##value_params));\
+ }\
+ GMOCK_INTERNAL_DEFN_##value_params\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\
+ };\
+ template <GMOCK_INTERNAL_DECL_##template_params\
+ GMOCK_INTERNAL_DECL_TYPE_##value_params>\
+ inline GMOCK_ACTION_CLASS_(name, value_params)<\
+ GMOCK_INTERNAL_LIST_##template_params\
+ GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\
+ GMOCK_INTERNAL_DECL_##value_params) {\
+ return GMOCK_ACTION_CLASS_(name, value_params)<\
+ GMOCK_INTERNAL_LIST_##template_params\
+ GMOCK_INTERNAL_LIST_TYPE_##value_params>(\
+ GMOCK_INTERNAL_LIST_##value_params);\
+ }\
+ template <GMOCK_INTERNAL_DECL_##template_params\
+ GMOCK_INTERNAL_DECL_TYPE_##value_params>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ GMOCK_ACTION_CLASS_(name, value_params)<\
+ GMOCK_INTERNAL_LIST_##template_params\
+ GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl<F>::\
+ gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION(name)\
+ class name##Action {\
+ public:\
+ name##Action() {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl() {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>());\
+ }\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##Action);\
+ };\
+ inline name##Action name() {\
+ return name##Action();\
+ }\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##Action::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P(name, p0)\
+ template <typename p0##_type>\
+ class name##ActionP {\
+ public:\
+ explicit name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0));\
+ }\
+ p0##_type p0;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP);\
+ };\
+ template <typename p0##_type>\
+ inline name##ActionP<p0##_type> name(p0##_type p0) {\
+ return name##ActionP<p0##_type>(p0);\
+ }\
+ template <typename p0##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP<p0##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P2(name, p0, p1)\
+ template <typename p0##_type, typename p1##_type>\
+ class name##ActionP2 {\
+ public:\
+ name##ActionP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
+ p1(gmock_p1) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
+ p1(gmock_p1) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP2);\
+ };\
+ template <typename p0##_type, typename p1##_type>\
+ inline name##ActionP2<p0##_type, p1##_type> name(p0##_type p0, \
+ p1##_type p1) {\
+ return name##ActionP2<p0##_type, p1##_type>(p0, p1);\
+ }\
+ template <typename p0##_type, typename p1##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP2<p0##_type, p1##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P3(name, p0, p1, p2)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type>\
+ class name##ActionP3 {\
+ public:\
+ name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP3);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type>\
+ inline name##ActionP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
+ p1##_type p1, p2##_type p2) {\
+ return name##ActionP3<p0##_type, p1##_type, p2##_type>(p0, p1, p2);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP3<p0##_type, p1##_type, \
+ p2##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P4(name, p0, p1, p2, p3)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type>\
+ class name##ActionP4 {\
+ public:\
+ name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP4);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type>\
+ inline name##ActionP4<p0##_type, p1##_type, p2##_type, \
+ p3##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+ p3##_type p3) {\
+ return name##ActionP4<p0##_type, p1##_type, p2##_type, p3##_type>(p0, p1, \
+ p2, p3);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP4<p0##_type, p1##_type, p2##_type, \
+ p3##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P5(name, p0, p1, p2, p3, p4)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type>\
+ class name##ActionP5 {\
+ public:\
+ name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, \
+ p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \
+ p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP5);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type>\
+ inline name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4) {\
+ return name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type>(p0, p1, p2, p3, p4);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type>\
+ class name##ActionP6 {\
+ public:\
+ name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP6);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type>\
+ inline name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+ p3##_type p3, p4##_type p4, p5##_type p5) {\
+ return name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type>\
+ class name##ActionP7 {\
+ public:\
+ name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
+ p6(gmock_p6) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+ p6));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP7);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type>\
+ inline name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type> name(p0##_type p0, p1##_type p1, \
+ p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+ p6##_type p6) {\
+ return name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, p6);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type>\
+ class name##ActionP8 {\
+ public:\
+ name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6, \
+ p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \
+ p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \
+ p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+ p6, p7));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP8);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type>\
+ inline name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type> name(p0##_type p0, \
+ p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+ p6##_type p6, p7##_type p7) {\
+ return name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, p3, p4, p5, \
+ p6, p7);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, \
+ p7##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type>\
+ class name##ActionP9 {\
+ public:\
+ name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+ p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+ p8(gmock_p8) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7, \
+ p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7), p8(gmock_p8) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+ p6, p7, p8));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP9);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type>\
+ inline name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, \
+ p8##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \
+ p8##_type p8) {\
+ return name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type>(p0, p1, p2, \
+ p3, p4, p5, p6, p7, p8);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, p7##_type, \
+ p8##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type, \
+ typename p9##_type>\
+ class name##ActionP10 {\
+ public:\
+ name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+ p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+ template <typename F>\
+ class gmock_Impl : public ::testing::ActionInterface<F> {\
+ public:\
+ typedef F function_type;\
+ typedef typename ::testing::internal::Function<F>::Result return_type;\
+ typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+ args_type;\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+ p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+ virtual return_type Perform(const args_type& args) {\
+ return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+ Perform(this, args);\
+ }\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+ arg9_type arg9) const;\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ p9##_type p9;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename F> operator ::testing::Action<F>() const {\
+ return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+ p6, p7, p8, p9));\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ p9##_type p9;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##ActionP10);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type, \
+ typename p9##_type>\
+ inline name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+ p9##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+ p9##_type p9) {\
+ return name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>(p0, \
+ p1, p2, p3, p4, p5, p6, p7, p8, p9);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type, \
+ typename p9##_type>\
+ template <typename F>\
+ template <typename arg0_type, typename arg1_type, typename arg2_type, \
+ typename arg3_type, typename arg4_type, typename arg5_type, \
+ typename arg6_type, typename arg7_type, typename arg8_type, \
+ typename arg9_type>\
+ typename ::testing::internal::Function<F>::Result\
+ name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, p7##_type, p8##_type, \
+ p9##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+namespace testing {
+
+
+// The ACTION*() macros trigger warning C4100 (unreferenced formal
+// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in
+// the macro definition, as the warnings are generated when the macro
+// is expanded and macro expansion cannot contain #pragma. Therefore
+// we suppress them here.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#endif
+
+// Various overloads for InvokeArgument<N>().
+//
+// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th
+// (0-based) argument, which must be a k-ary callable, of the mock
+// function, with arguments a1, a2, ..., a_k.
+//
+// Notes:
+//
+// 1. The arguments are passed by value by default. If you need to
+// pass an argument by reference, wrap it inside ByRef(). For
+// example,
+//
+// InvokeArgument<1>(5, string("Hello"), ByRef(foo))
+//
+// passes 5 and string("Hello") by value, and passes foo by
+// reference.
+//
+// 2. If the callable takes an argument by reference but ByRef() is
+// not used, it will receive the reference to a copy of the value,
+// instead of the original value. For example, when the 0-th
+// argument of the mock function takes a const string&, the action
+//
+// InvokeArgument<0>(string("Hello"))
+//
+// makes a copy of the temporary string("Hello") object and passes a
+// reference of the copy, instead of the original temporary object,
+// to the callable. This makes it easy for a user to define an
+// InvokeArgument action from temporary values and have it performed
+// later.
+
+namespace internal {
+namespace invoke_argument {
+
+// Appears in InvokeArgumentAdl's argument list to help avoid
+// accidental calls to user functions of the same name.
+struct AdlTag {};
+
+// InvokeArgumentAdl - a helper for InvokeArgument.
+// The basic overloads are provided here for generic functors.
+// Overloads for other custom-callables are provided in the
+// internal/custom/callback-actions.h header.
+
+template <typename R, typename F>
+R InvokeArgumentAdl(AdlTag, F f) {
+ return f();
+}
+template <typename R, typename F, typename A1>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1) {
+ return f(a1);
+}
+template <typename R, typename F, typename A1, typename A2>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2) {
+ return f(a1, a2);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3) {
+ return f(a1, a2, a3);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4) {
+ return f(a1, a2, a3, a4);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+ return f(a1, a2, a3, a4, a5);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+ return f(a1, a2, a3, a4, a5, a6);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+ A7 a7) {
+ return f(a1, a2, a3, a4, a5, a6, a7);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7, typename A8>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+ A7 a7, A8 a8) {
+ return f(a1, a2, a3, a4, a5, a6, a7, a8);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7, typename A8,
+ typename A9>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+ A7 a7, A8 a8, A9 a9) {
+ return f(a1, a2, a3, a4, a5, a6, a7, a8, a9);
+}
+template <typename R, typename F, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7, typename A8,
+ typename A9, typename A10>
+R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+ A7 a7, A8 a8, A9 a9, A10 a10) {
+ return f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
+}
+} // namespace invoke_argument
+} // namespace internal
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_0_VALUE_PARAMS()) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args));
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(p0)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_2_VALUE_PARAMS(p0, p1)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_3_VALUE_PARAMS(p0, p1, p2)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3, p4);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3, p4, p5);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
+ using internal::invoke_argument::InvokeArgumentAdl;
+ return InvokeArgumentAdl<return_type>(
+ internal::invoke_argument::AdlTag(),
+ ::testing::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+}
+
+// Various overloads for ReturnNew<T>().
+//
+// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new
+// instance of type T, constructed on the heap with constructor arguments
+// a1, a2, ..., and a_k. The caller assumes ownership of the returned value.
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_0_VALUE_PARAMS()) {
+ return new T();
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_1_VALUE_PARAMS(p0)) {
+ return new T(p0);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_2_VALUE_PARAMS(p0, p1)) {
+ return new T(p0, p1);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_3_VALUE_PARAMS(p0, p1, p2)) {
+ return new T(p0, p1, p2);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
+ return new T(p0, p1, p2, p3);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
+ return new T(p0, p1, p2, p3, p4);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
+ return new T(p0, p1, p2, p3, p4, p5);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
+ return new T(p0, p1, p2, p3, p4, p5, p6);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) {
+ return new T(p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
+ return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+ HAS_1_TEMPLATE_PARAMS(typename, T),
+ AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
+ return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+} // namespace testing
+
+// Include any custom actions added by the local installation.
+// We must include this header at the end to make sure it can use the
+// declarations from this file.
+#include "gmock/internal/custom/gmock-generated-actions.h"
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
diff --git a/extern/gmock/include/gmock/gmock-generated-function-mockers.h b/extern/gmock/include/gmock/gmock-generated-function-mockers.h
new file mode 100644
index 00000000000..4fa5ca94849
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-generated-function-mockers.h
@@ -0,0 +1,1095 @@
+// This file was GENERATED by command:
+// pump.py gmock-generated-function-mockers.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements function mockers of various arities.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
+
+#include "gmock/gmock-spec-builders.h"
+#include "gmock/internal/gmock-internal-utils.h"
+
+#if GTEST_HAS_STD_FUNCTION_
+# include <functional>
+#endif
+
+namespace testing {
+namespace internal {
+
+template <typename F>
+class FunctionMockerBase;
+
+// Note: class FunctionMocker really belongs to the ::testing
+// namespace. However if we define it in ::testing, MSVC will
+// complain when classes in ::testing::internal declare it as a
+// friend class template. To workaround this compiler bug, we define
+// FunctionMocker in ::testing::internal and import it into ::testing.
+template <typename F>
+class FunctionMocker;
+
+template <typename R>
+class FunctionMocker<R()> : public
+ internal::FunctionMockerBase<R()> {
+ public:
+ typedef R F();
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With() {
+ return this->current_spec();
+ }
+
+ R Invoke() {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple());
+ }
+};
+
+template <typename R, typename A1>
+class FunctionMocker<R(A1)> : public
+ internal::FunctionMockerBase<R(A1)> {
+ public:
+ typedef R F(A1);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1));
+ }
+};
+
+template <typename R, typename A1, typename A2>
+class FunctionMocker<R(A1, A2)> : public
+ internal::FunctionMockerBase<R(A1, A2)> {
+ public:
+ typedef R F(A1, A2);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class FunctionMocker<R(A1, A2, A3)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3)> {
+ public:
+ typedef R F(A1, A2, A3);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class FunctionMocker<R(A1, A2, A3, A4)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4)> {
+ public:
+ typedef R F(A1, A2, A3, A4);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+class FunctionMocker<R(A1, A2, A3, A4, A5)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4, A5)> {
+ public:
+ typedef R F(A1, A2, A3, A4, A5);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6)> {
+ public:
+ typedef R F(A1, A2, A3, A4, A5, A6);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+ const Matcher<A6>& m6) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
+ m6));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ typedef R F(A1, A2, A3, A4, A5, A6, A7);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+ const Matcher<A6>& m6, const Matcher<A7>& m7) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
+ m6, m7));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8)> {
+ public:
+ typedef R F(A1, A2, A3, A4, A5, A6, A7, A8);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+ const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
+ m6, m7, m8));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
+ public:
+ typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+ const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8,
+ const Matcher<A9>& m9) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
+ m6, m7, m8, m9));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9));
+ }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9,
+ typename A10>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> : public
+ internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> {
+ public:
+ typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+ MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+ const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+ const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8,
+ const Matcher<A9>& m9, const Matcher<A10>& m10) {
+ this->current_spec().SetMatchers(::testing::make_tuple(m1, m2, m3, m4, m5,
+ m6, m7, m8, m9, m10));
+ return this->current_spec();
+ }
+
+ R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9,
+ A10 a10) {
+ // Even though gcc and MSVC don't enforce it, 'this->' is required
+ // by the C++ standard [14.6.4] here, as the base class type is
+ // dependent on the template argument (and thus shouldn't be
+ // looked into when resolving InvokeWith).
+ return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9,
+ a10));
+ }
+};
+
+} // namespace internal
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file. However, the FunctionMocker class template
+// is meant to be defined in the ::testing namespace. The following
+// line is just a trick for working around a bug in MSVC 8.0, which
+// cannot handle it if we define FunctionMocker in ::testing.
+using internal::FunctionMocker;
+
+// GMOCK_RESULT_(tn, F) expands to the result type of function type F.
+// We define this as a variadic macro in case F contains unprotected
+// commas (the same reason that we use variadic macros in other places
+// in this file).
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_RESULT_(tn, ...) \
+ tn ::testing::internal::Function<__VA_ARGS__>::Result
+
+// The type of argument N of the given function type.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_ARG_(tn, N, ...) \
+ tn ::testing::internal::Function<__VA_ARGS__>::Argument##N
+
+// The matcher type for argument N of the given function type.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_MATCHER_(tn, N, ...) \
+ const ::testing::Matcher<GMOCK_ARG_(tn, N, __VA_ARGS__)>&
+
+// The variable for mocking the given method.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_MOCKER_(arity, constness, Method) \
+ GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ ) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 0), \
+ this_method_does_not_take_0_arguments); \
+ GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(0, constness, Method).Invoke(); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method() constness { \
+ GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(0, constness, Method).With(); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD1_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 1), \
+ this_method_does_not_take_1_argument); \
+ GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(1, constness, Method).Invoke(gmock_a1); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \
+ GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 2), \
+ this_method_does_not_take_2_arguments); \
+ GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \
+ GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD3_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 3), \
+ this_method_does_not_take_3_arguments); \
+ GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(3, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness { \
+ GMOCK_MOCKER_(3, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(3, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD4_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 4), \
+ this_method_does_not_take_4_arguments); \
+ GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(4, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4) constness { \
+ GMOCK_MOCKER_(4, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(4, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD5_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 5), \
+ this_method_does_not_take_5_arguments); \
+ GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(5, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5) constness { \
+ GMOCK_MOCKER_(5, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(5, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD6_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 6), \
+ this_method_does_not_take_6_arguments); \
+ GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(6, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6) constness { \
+ GMOCK_MOCKER_(6, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(6, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(6, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD7_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 7), \
+ this_method_does_not_take_7_arguments); \
+ GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(7, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7) constness { \
+ GMOCK_MOCKER_(7, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(7, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(7, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD8_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \
+ GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 8), \
+ this_method_does_not_take_8_arguments); \
+ GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(8, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \
+ GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8) constness { \
+ GMOCK_MOCKER_(8, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(8, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(8, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD9_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \
+ GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \
+ GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 9), \
+ this_method_does_not_take_9_arguments); \
+ GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(9, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \
+ gmock_a9); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \
+ GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \
+ GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9) constness { \
+ GMOCK_MOCKER_(9, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(9, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \
+ gmock_a9); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(9, constness, \
+ Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD10_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \
+ GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \
+ GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \
+ GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \
+ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \
+ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \
+ == 10), \
+ this_method_does_not_take_10_arguments); \
+ GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \
+ return GMOCK_MOCKER_(10, constness, Method).Invoke(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
+ gmock_a10); \
+ } \
+ ::testing::MockSpec<__VA_ARGS__>& \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
+ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
+ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
+ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
+ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
+ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \
+ GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \
+ GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9, \
+ GMOCK_MATCHER_(tn, 10, \
+ __VA_ARGS__) gmock_a10) constness { \
+ GMOCK_MOCKER_(10, constness, Method).RegisterOwner(this); \
+ return GMOCK_MOCKER_(10, constness, Method).With(gmock_a1, gmock_a2, \
+ gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
+ gmock_a10); \
+ } \
+ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(10, constness, \
+ Method)
+
+#define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD1(m, ...) GMOCK_METHOD1_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD3(m, ...) GMOCK_METHOD3_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD4(m, ...) GMOCK_METHOD4_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD5(m, ...) GMOCK_METHOD5_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD6(m, ...) GMOCK_METHOD6_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD7(m, ...) GMOCK_METHOD7_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD8(m, ...) GMOCK_METHOD8_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD9(m, ...) GMOCK_METHOD9_(, , , m, __VA_ARGS__)
+#define MOCK_METHOD10(m, ...) GMOCK_METHOD10_(, , , m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0(m, ...) GMOCK_METHOD0_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1(m, ...) GMOCK_METHOD1_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3(m, ...) GMOCK_METHOD3_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4(m, ...) GMOCK_METHOD4_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5(m, ...) GMOCK_METHOD5_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6(m, ...) GMOCK_METHOD6_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7(m, ...) GMOCK_METHOD7_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8(m, ...) GMOCK_METHOD8_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9(m, ...) GMOCK_METHOD9_(, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10(m, ...) GMOCK_METHOD10_(, const, , m, __VA_ARGS__)
+
+#define MOCK_METHOD0_T(m, ...) GMOCK_METHOD0_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD1_T(m, ...) GMOCK_METHOD1_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD2_T(m, ...) GMOCK_METHOD2_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD3_T(m, ...) GMOCK_METHOD3_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD4_T(m, ...) GMOCK_METHOD4_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD5_T(m, ...) GMOCK_METHOD5_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD6_T(m, ...) GMOCK_METHOD6_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD7_T(m, ...) GMOCK_METHOD7_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD8_T(m, ...) GMOCK_METHOD8_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD9_T(m, ...) GMOCK_METHOD9_(typename, , , m, __VA_ARGS__)
+#define MOCK_METHOD10_T(m, ...) GMOCK_METHOD10_(typename, , , m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_T(m, ...) \
+ GMOCK_METHOD0_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_T(m, ...) \
+ GMOCK_METHOD1_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_T(m, ...) \
+ GMOCK_METHOD2_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_T(m, ...) \
+ GMOCK_METHOD3_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_T(m, ...) \
+ GMOCK_METHOD4_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_T(m, ...) \
+ GMOCK_METHOD5_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_T(m, ...) \
+ GMOCK_METHOD6_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_T(m, ...) \
+ GMOCK_METHOD7_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_T(m, ...) \
+ GMOCK_METHOD8_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_T(m, ...) \
+ GMOCK_METHOD9_(typename, const, , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_T(m, ...) \
+ GMOCK_METHOD10_(typename, const, , m, __VA_ARGS__)
+
+#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD0_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD1_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD2_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD3_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD4_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD5_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD6_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD7_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD8_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD9_(, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD10_(, , ct, m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD0_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD1_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD2_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD3_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD4_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD5_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD6_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD7_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD8_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD9_(, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD10_(, const, ct, m, __VA_ARGS__)
+
+#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD0_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD1_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD2_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD3_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD4_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD5_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD6_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD7_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD8_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD9_(typename, , ct, m, __VA_ARGS__)
+#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD10_(typename, , ct, m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD0_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD1_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD2_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD3_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD4_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD5_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD6_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD7_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD8_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD9_(typename, const, ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
+ GMOCK_METHOD10_(typename, const, ct, m, __VA_ARGS__)
+
+// A MockFunction<F> class has one mock method whose type is F. It is
+// useful when you just want your test code to emit some messages and
+// have Google Mock verify the right messages are sent (and perhaps at
+// the right times). For example, if you are exercising code:
+//
+// Foo(1);
+// Foo(2);
+// Foo(3);
+//
+// and want to verify that Foo(1) and Foo(3) both invoke
+// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write:
+//
+// TEST(FooTest, InvokesBarCorrectly) {
+// MyMock mock;
+// MockFunction<void(string check_point_name)> check;
+// {
+// InSequence s;
+//
+// EXPECT_CALL(mock, Bar("a"));
+// EXPECT_CALL(check, Call("1"));
+// EXPECT_CALL(check, Call("2"));
+// EXPECT_CALL(mock, Bar("a"));
+// }
+// Foo(1);
+// check.Call("1");
+// Foo(2);
+// check.Call("2");
+// Foo(3);
+// }
+//
+// The expectation spec says that the first Bar("a") must happen
+// before check point "1", the second Bar("a") must happen after check
+// point "2", and nothing should happen between the two check
+// points. The explicit check points make it easy to tell which
+// Bar("a") is called by which call to Foo().
+//
+// MockFunction<F> can also be used to exercise code that accepts
+// std::function<F> callbacks. To do so, use AsStdFunction() method
+// to create std::function proxy forwarding to original object's Call.
+// Example:
+//
+// TEST(FooTest, RunsCallbackWithBarArgument) {
+// MockFunction<int(string)> callback;
+// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1));
+// Foo(callback.AsStdFunction());
+// }
+template <typename F>
+class MockFunction;
+
+template <typename R>
+class MockFunction<R()> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD0_T(Call, R());
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R()> AsStdFunction() {
+ return [this]() -> R {
+ return this->Call();
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0>
+class MockFunction<R(A0)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD1_T(Call, R(A0));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0)> AsStdFunction() {
+ return [this](A0 a0) -> R {
+ return this->Call(a0);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1>
+class MockFunction<R(A0, A1)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD2_T(Call, R(A0, A1));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1)> AsStdFunction() {
+ return [this](A0 a0, A1 a1) -> R {
+ return this->Call(a0, a1);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2>
+class MockFunction<R(A0, A1, A2)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD3_T(Call, R(A0, A1, A2));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2) -> R {
+ return this->Call(a0, a1, a2);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3>
+class MockFunction<R(A0, A1, A2, A3)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD4_T(Call, R(A0, A1, A2, A3));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3) -> R {
+ return this->Call(a0, a1, a2, a3);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+ typename A4>
+class MockFunction<R(A0, A1, A2, A3, A4)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD5_T(Call, R(A0, A1, A2, A3, A4));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3, A4)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) -> R {
+ return this->Call(a0, a1, a2, a3, a4);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+class MockFunction<R(A0, A1, A2, A3, A4, A5)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD6_T(Call, R(A0, A1, A2, A3, A4, A5));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3, A4, A5)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) -> R {
+ return this->Call(a0, a1, a2, a3, a4, a5);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD7_T(Call, R(A0, A1, A2, A3, A4, A5, A6));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3, A4, A5, A6)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) -> R {
+ return this->Call(a0, a1, a2, a3, a4, a5, a6);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD8_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3, A4, A5, A6, A7)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) -> R {
+ return this->Call(a0, a1, a2, a3, a4, a5, a6, a7);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7, typename A8>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7, A8)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD9_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3, A4, A5, A6, A7, A8)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7,
+ A8 a8) -> R {
+ return this->Call(a0, a1, a2, a3, a4, a5, a6, a7, a8);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7, typename A8,
+ typename A9>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
+ public:
+ MockFunction() {}
+
+ MOCK_METHOD10_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9));
+
+#if GTEST_HAS_STD_FUNCTION_
+ std::function<R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> AsStdFunction() {
+ return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7,
+ A8 a8, A9 a9) -> R {
+ return this->Call(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+ };
+ }
+#endif // GTEST_HAS_STD_FUNCTION_
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction);
+};
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
diff --git a/extern/gmock/include/gmock/gmock-generated-matchers.h b/extern/gmock/include/gmock/gmock-generated-matchers.h
new file mode 100644
index 00000000000..57056fd91d2
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-generated-matchers.h
@@ -0,0 +1,2179 @@
+// This file was GENERATED by command:
+// pump.py gmock-generated-matchers.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used variadic matchers.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
+
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+#include "gmock/gmock-matchers.h"
+
+namespace testing {
+namespace internal {
+
+// The type of the i-th (0-based) field of Tuple.
+#define GMOCK_FIELD_TYPE_(Tuple, i) \
+ typename ::testing::tuple_element<i, Tuple>::type
+
+// TupleFields<Tuple, k0, ..., kn> is for selecting fields from a
+// tuple of type Tuple. It has two members:
+//
+// type: a tuple type whose i-th field is the ki-th field of Tuple.
+// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple.
+//
+// For example, in class TupleFields<tuple<bool, char, int>, 2, 0>, we have:
+//
+// type is tuple<int, bool>, and
+// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true).
+
+template <class Tuple, int k0 = -1, int k1 = -1, int k2 = -1, int k3 = -1,
+ int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
+ int k9 = -1>
+class TupleFields;
+
+// This generic version is used when there are 10 selectors.
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
+ int k7, int k8, int k9>
+class TupleFields {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+ GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
+ GMOCK_FIELD_TYPE_(Tuple, k7), GMOCK_FIELD_TYPE_(Tuple, k8),
+ GMOCK_FIELD_TYPE_(Tuple, k9)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+ get<k5>(t), get<k6>(t), get<k7>(t), get<k8>(t), get<k9>(t));
+ }
+};
+
+// The following specialization is used for 0 ~ 9 selectors.
+
+template <class Tuple>
+class TupleFields<Tuple, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<> type;
+ static type GetSelectedFields(const Tuple& /* t */) {
+ return type();
+ }
+};
+
+template <class Tuple, int k0>
+class TupleFields<Tuple, k0, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1>
+class TupleFields<Tuple, k0, k1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2>
+class TupleFields<Tuple, k0, k1, k2, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3>
+class TupleFields<Tuple, k0, k1, k2, k3, -1, -1, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, -1, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, -1, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+ GMOCK_FIELD_TYPE_(Tuple, k5)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+ get<k5>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, -1, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+ GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+ get<k5>(t), get<k6>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
+ int k7>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, k7, -1, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+ GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
+ GMOCK_FIELD_TYPE_(Tuple, k7)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+ get<k5>(t), get<k6>(t), get<k7>(t));
+ }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
+ int k7, int k8>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, k7, k8, -1> {
+ public:
+ typedef ::testing::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+ GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+ GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+ GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
+ GMOCK_FIELD_TYPE_(Tuple, k7), GMOCK_FIELD_TYPE_(Tuple, k8)> type;
+ static type GetSelectedFields(const Tuple& t) {
+ return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+ get<k5>(t), get<k6>(t), get<k7>(t), get<k8>(t));
+ }
+};
+
+#undef GMOCK_FIELD_TYPE_
+
+// Implements the Args() matcher.
+template <class ArgsTuple, int k0 = -1, int k1 = -1, int k2 = -1, int k3 = -1,
+ int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
+ int k9 = -1>
+class ArgsMatcherImpl : public MatcherInterface<ArgsTuple> {
+ public:
+ // ArgsTuple may have top-level const or reference modifiers.
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple;
+ typedef typename internal::TupleFields<RawArgsTuple, k0, k1, k2, k3, k4, k5,
+ k6, k7, k8, k9>::type SelectedArgs;
+ typedef Matcher<const SelectedArgs&> MonomorphicInnerMatcher;
+
+ template <typename InnerMatcher>
+ explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher)
+ : inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {}
+
+ virtual bool MatchAndExplain(ArgsTuple args,
+ MatchResultListener* listener) const {
+ const SelectedArgs& selected_args = GetSelectedArgs(args);
+ if (!listener->IsInterested())
+ return inner_matcher_.Matches(selected_args);
+
+ PrintIndices(listener->stream());
+ *listener << "are " << PrintToString(selected_args);
+
+ StringMatchResultListener inner_listener;
+ const bool match = inner_matcher_.MatchAndExplain(selected_args,
+ &inner_listener);
+ PrintIfNotEmpty(inner_listener.str(), listener->stream());
+ return match;
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "are a tuple ";
+ PrintIndices(os);
+ inner_matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "are a tuple ";
+ PrintIndices(os);
+ inner_matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ static SelectedArgs GetSelectedArgs(ArgsTuple args) {
+ return TupleFields<RawArgsTuple, k0, k1, k2, k3, k4, k5, k6, k7, k8,
+ k9>::GetSelectedFields(args);
+ }
+
+ // Prints the indices of the selected fields.
+ static void PrintIndices(::std::ostream* os) {
+ *os << "whose fields (";
+ const int indices[10] = { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 };
+ for (int i = 0; i < 10; i++) {
+ if (indices[i] < 0)
+ break;
+
+ if (i >= 1)
+ *os << ", ";
+
+ *os << "#" << indices[i];
+ }
+ *os << ") ";
+ }
+
+ const MonomorphicInnerMatcher inner_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl);
+};
+
+template <class InnerMatcher, int k0 = -1, int k1 = -1, int k2 = -1,
+ int k3 = -1, int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1,
+ int k8 = -1, int k9 = -1>
+class ArgsMatcher {
+ public:
+ explicit ArgsMatcher(const InnerMatcher& inner_matcher)
+ : inner_matcher_(inner_matcher) {}
+
+ template <typename ArgsTuple>
+ operator Matcher<ArgsTuple>() const {
+ return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k0, k1, k2, k3, k4, k5,
+ k6, k7, k8, k9>(inner_matcher_));
+ }
+
+ private:
+ const InnerMatcher inner_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(ArgsMatcher);
+};
+
+// A set of metafunctions for computing the result type of AllOf.
+// AllOf(m1, ..., mN) returns
+// AllOfResultN<decltype(m1), ..., decltype(mN)>::type.
+
+// Although AllOf isn't defined for one argument, AllOfResult1 is defined
+// to simplify the implementation.
+template <typename M1>
+struct AllOfResult1 {
+ typedef M1 type;
+};
+
+template <typename M1, typename M2>
+struct AllOfResult2 {
+ typedef BothOfMatcher<
+ typename AllOfResult1<M1>::type,
+ typename AllOfResult1<M2>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3>
+struct AllOfResult3 {
+ typedef BothOfMatcher<
+ typename AllOfResult1<M1>::type,
+ typename AllOfResult2<M2, M3>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4>
+struct AllOfResult4 {
+ typedef BothOfMatcher<
+ typename AllOfResult2<M1, M2>::type,
+ typename AllOfResult2<M3, M4>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5>
+struct AllOfResult5 {
+ typedef BothOfMatcher<
+ typename AllOfResult2<M1, M2>::type,
+ typename AllOfResult3<M3, M4, M5>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6>
+struct AllOfResult6 {
+ typedef BothOfMatcher<
+ typename AllOfResult3<M1, M2, M3>::type,
+ typename AllOfResult3<M4, M5, M6>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7>
+struct AllOfResult7 {
+ typedef BothOfMatcher<
+ typename AllOfResult3<M1, M2, M3>::type,
+ typename AllOfResult4<M4, M5, M6, M7>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8>
+struct AllOfResult8 {
+ typedef BothOfMatcher<
+ typename AllOfResult4<M1, M2, M3, M4>::type,
+ typename AllOfResult4<M5, M6, M7, M8>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9>
+struct AllOfResult9 {
+ typedef BothOfMatcher<
+ typename AllOfResult4<M1, M2, M3, M4>::type,
+ typename AllOfResult5<M5, M6, M7, M8, M9>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9, typename M10>
+struct AllOfResult10 {
+ typedef BothOfMatcher<
+ typename AllOfResult5<M1, M2, M3, M4, M5>::type,
+ typename AllOfResult5<M6, M7, M8, M9, M10>::type
+ > type;
+};
+
+// A set of metafunctions for computing the result type of AnyOf.
+// AnyOf(m1, ..., mN) returns
+// AnyOfResultN<decltype(m1), ..., decltype(mN)>::type.
+
+// Although AnyOf isn't defined for one argument, AnyOfResult1 is defined
+// to simplify the implementation.
+template <typename M1>
+struct AnyOfResult1 {
+ typedef M1 type;
+};
+
+template <typename M1, typename M2>
+struct AnyOfResult2 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult1<M1>::type,
+ typename AnyOfResult1<M2>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3>
+struct AnyOfResult3 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult1<M1>::type,
+ typename AnyOfResult2<M2, M3>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4>
+struct AnyOfResult4 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult2<M1, M2>::type,
+ typename AnyOfResult2<M3, M4>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5>
+struct AnyOfResult5 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult2<M1, M2>::type,
+ typename AnyOfResult3<M3, M4, M5>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6>
+struct AnyOfResult6 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult3<M1, M2, M3>::type,
+ typename AnyOfResult3<M4, M5, M6>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7>
+struct AnyOfResult7 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult3<M1, M2, M3>::type,
+ typename AnyOfResult4<M4, M5, M6, M7>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8>
+struct AnyOfResult8 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult4<M1, M2, M3, M4>::type,
+ typename AnyOfResult4<M5, M6, M7, M8>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9>
+struct AnyOfResult9 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult4<M1, M2, M3, M4>::type,
+ typename AnyOfResult5<M5, M6, M7, M8, M9>::type
+ > type;
+};
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9, typename M10>
+struct AnyOfResult10 {
+ typedef EitherOfMatcher<
+ typename AnyOfResult5<M1, M2, M3, M4, M5>::type,
+ typename AnyOfResult5<M6, M7, M8, M9, M10>::type
+ > type;
+};
+
+} // namespace internal
+
+// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
+// fields of it matches a_matcher. C++ doesn't support default
+// arguments for function templates, so we have to overload it.
+template <typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher>(matcher);
+}
+
+template <int k1, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1>(matcher);
+}
+
+template <int k1, int k2, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2>(matcher);
+}
+
+template <int k1, int k2, int k3, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7,
+ typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6,
+ k7>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+ typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7,
+ k8>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+ int k9, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8, k9>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8,
+ k9>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+ int k9, int k10, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8, k9,
+ k10>
+Args(const InnerMatcher& matcher) {
+ return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8,
+ k9, k10>(matcher);
+}
+
+// ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with
+// n elements, where the i-th element in the container must
+// match the i-th argument in the list. Each argument of
+// ElementsAre() can be either a value or a matcher. We support up to
+// 10 arguments.
+//
+// The use of DecayArray in the implementation allows ElementsAre()
+// to accept string literals, whose type is const char[N], but we
+// want to treat them as const char*.
+//
+// NOTE: Since ElementsAre() cares about the order of the elements, it
+// must not be used with containers whose elements's order is
+// undefined (e.g. hash_map).
+
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<> >
+ElementsAre() {
+ typedef ::testing::tuple<> Args;
+ return internal::ElementsAreMatcher<Args>(Args());
+}
+
+template <typename T1>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type> >
+ElementsAre(const T1& e1) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1));
+}
+
+template <typename T1, typename T2>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type> >
+ElementsAre(const T1& e1, const T2& e2) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2));
+}
+
+template <typename T1, typename T2, typename T3>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3));
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7, const T8& e8) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7,
+ e8));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7,
+ e8, e9));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+inline internal::ElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type,
+ typename internal::DecayArray<T10>::type> >
+ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9,
+ const T10& e10) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type,
+ typename internal::DecayArray<T10>::type> Args;
+ return internal::ElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5, e6, e7,
+ e8, e9, e10));
+}
+
+// UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension
+// that matches n elements in any order. We support up to n=10 arguments.
+
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<> >
+UnorderedElementsAre() {
+ typedef ::testing::tuple<> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args());
+}
+
+template <typename T1>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type> >
+UnorderedElementsAre(const T1& e1) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1));
+}
+
+template <typename T1, typename T2>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2));
+}
+
+template <typename T1, typename T2, typename T3>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3));
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
+ e6));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
+ e6, e7));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7, const T8& e8) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
+ e6, e7, e8));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
+ e6, e7, e8, e9));
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+inline internal::UnorderedElementsAreMatcher<
+ ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type,
+ typename internal::DecayArray<T10>::type> >
+UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+ const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9,
+ const T10& e10) {
+ typedef ::testing::tuple<
+ typename internal::DecayArray<T1>::type,
+ typename internal::DecayArray<T2>::type,
+ typename internal::DecayArray<T3>::type,
+ typename internal::DecayArray<T4>::type,
+ typename internal::DecayArray<T5>::type,
+ typename internal::DecayArray<T6>::type,
+ typename internal::DecayArray<T7>::type,
+ typename internal::DecayArray<T8>::type,
+ typename internal::DecayArray<T9>::type,
+ typename internal::DecayArray<T10>::type> Args;
+ return internal::UnorderedElementsAreMatcher<Args>(Args(e1, e2, e3, e4, e5,
+ e6, e7, e8, e9, e10));
+}
+
+// AllOf(m1, m2, ..., mk) matches any value that matches all of the given
+// sub-matchers. AllOf is called fully qualified to prevent ADL from firing.
+
+template <typename M1, typename M2>
+inline typename internal::AllOfResult2<M1, M2>::type
+AllOf(M1 m1, M2 m2) {
+ return typename internal::AllOfResult2<M1, M2>::type(
+ m1,
+ m2);
+}
+
+template <typename M1, typename M2, typename M3>
+inline typename internal::AllOfResult3<M1, M2, M3>::type
+AllOf(M1 m1, M2 m2, M3 m3) {
+ return typename internal::AllOfResult3<M1, M2, M3>::type(
+ m1,
+ ::testing::AllOf(m2, m3));
+}
+
+template <typename M1, typename M2, typename M3, typename M4>
+inline typename internal::AllOfResult4<M1, M2, M3, M4>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4) {
+ return typename internal::AllOfResult4<M1, M2, M3, M4>::type(
+ ::testing::AllOf(m1, m2),
+ ::testing::AllOf(m3, m4));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5>
+inline typename internal::AllOfResult5<M1, M2, M3, M4, M5>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) {
+ return typename internal::AllOfResult5<M1, M2, M3, M4, M5>::type(
+ ::testing::AllOf(m1, m2),
+ ::testing::AllOf(m3, m4, m5));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6>
+inline typename internal::AllOfResult6<M1, M2, M3, M4, M5, M6>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) {
+ return typename internal::AllOfResult6<M1, M2, M3, M4, M5, M6>::type(
+ ::testing::AllOf(m1, m2, m3),
+ ::testing::AllOf(m4, m5, m6));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7>
+inline typename internal::AllOfResult7<M1, M2, M3, M4, M5, M6, M7>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) {
+ return typename internal::AllOfResult7<M1, M2, M3, M4, M5, M6, M7>::type(
+ ::testing::AllOf(m1, m2, m3),
+ ::testing::AllOf(m4, m5, m6, m7));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8>
+inline typename internal::AllOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) {
+ return typename internal::AllOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type(
+ ::testing::AllOf(m1, m2, m3, m4),
+ ::testing::AllOf(m5, m6, m7, m8));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9>
+inline typename internal::AllOfResult9<M1, M2, M3, M4, M5, M6, M7, M8, M9>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) {
+ return typename internal::AllOfResult9<M1, M2, M3, M4, M5, M6, M7, M8,
+ M9>::type(
+ ::testing::AllOf(m1, m2, m3, m4),
+ ::testing::AllOf(m5, m6, m7, m8, m9));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9, typename M10>
+inline typename internal::AllOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
+ M10>::type
+AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
+ return typename internal::AllOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
+ M10>::type(
+ ::testing::AllOf(m1, m2, m3, m4, m5),
+ ::testing::AllOf(m6, m7, m8, m9, m10));
+}
+
+// AnyOf(m1, m2, ..., mk) matches any value that matches any of the given
+// sub-matchers. AnyOf is called fully qualified to prevent ADL from firing.
+
+template <typename M1, typename M2>
+inline typename internal::AnyOfResult2<M1, M2>::type
+AnyOf(M1 m1, M2 m2) {
+ return typename internal::AnyOfResult2<M1, M2>::type(
+ m1,
+ m2);
+}
+
+template <typename M1, typename M2, typename M3>
+inline typename internal::AnyOfResult3<M1, M2, M3>::type
+AnyOf(M1 m1, M2 m2, M3 m3) {
+ return typename internal::AnyOfResult3<M1, M2, M3>::type(
+ m1,
+ ::testing::AnyOf(m2, m3));
+}
+
+template <typename M1, typename M2, typename M3, typename M4>
+inline typename internal::AnyOfResult4<M1, M2, M3, M4>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4) {
+ return typename internal::AnyOfResult4<M1, M2, M3, M4>::type(
+ ::testing::AnyOf(m1, m2),
+ ::testing::AnyOf(m3, m4));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5>
+inline typename internal::AnyOfResult5<M1, M2, M3, M4, M5>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) {
+ return typename internal::AnyOfResult5<M1, M2, M3, M4, M5>::type(
+ ::testing::AnyOf(m1, m2),
+ ::testing::AnyOf(m3, m4, m5));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6>
+inline typename internal::AnyOfResult6<M1, M2, M3, M4, M5, M6>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) {
+ return typename internal::AnyOfResult6<M1, M2, M3, M4, M5, M6>::type(
+ ::testing::AnyOf(m1, m2, m3),
+ ::testing::AnyOf(m4, m5, m6));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7>
+inline typename internal::AnyOfResult7<M1, M2, M3, M4, M5, M6, M7>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) {
+ return typename internal::AnyOfResult7<M1, M2, M3, M4, M5, M6, M7>::type(
+ ::testing::AnyOf(m1, m2, m3),
+ ::testing::AnyOf(m4, m5, m6, m7));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8>
+inline typename internal::AnyOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) {
+ return typename internal::AnyOfResult8<M1, M2, M3, M4, M5, M6, M7, M8>::type(
+ ::testing::AnyOf(m1, m2, m3, m4),
+ ::testing::AnyOf(m5, m6, m7, m8));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9>
+inline typename internal::AnyOfResult9<M1, M2, M3, M4, M5, M6, M7, M8, M9>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) {
+ return typename internal::AnyOfResult9<M1, M2, M3, M4, M5, M6, M7, M8,
+ M9>::type(
+ ::testing::AnyOf(m1, m2, m3, m4),
+ ::testing::AnyOf(m5, m6, m7, m8, m9));
+}
+
+template <typename M1, typename M2, typename M3, typename M4, typename M5,
+ typename M6, typename M7, typename M8, typename M9, typename M10>
+inline typename internal::AnyOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
+ M10>::type
+AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) {
+ return typename internal::AnyOfResult10<M1, M2, M3, M4, M5, M6, M7, M8, M9,
+ M10>::type(
+ ::testing::AnyOf(m1, m2, m3, m4, m5),
+ ::testing::AnyOf(m6, m7, m8, m9, m10));
+}
+
+} // namespace testing
+
+
+// The MATCHER* family of macros can be used in a namespace scope to
+// define custom matchers easily.
+//
+// Basic Usage
+// ===========
+//
+// The syntax
+//
+// MATCHER(name, description_string) { statements; }
+//
+// defines a matcher with the given name that executes the statements,
+// which must return a bool to indicate if the match succeeds. Inside
+// the statements, you can refer to the value being matched by 'arg',
+// and refer to its type by 'arg_type'.
+//
+// The description string documents what the matcher does, and is used
+// to generate the failure message when the match fails. Since a
+// MATCHER() is usually defined in a header file shared by multiple
+// C++ source files, we require the description to be a C-string
+// literal to avoid possible side effects. It can be empty, in which
+// case we'll use the sequence of words in the matcher name as the
+// description.
+//
+// For example:
+//
+// MATCHER(IsEven, "") { return (arg % 2) == 0; }
+//
+// allows you to write
+//
+// // Expects mock_foo.Bar(n) to be called where n is even.
+// EXPECT_CALL(mock_foo, Bar(IsEven()));
+//
+// or,
+//
+// // Verifies that the value of some_expression is even.
+// EXPECT_THAT(some_expression, IsEven());
+//
+// If the above assertion fails, it will print something like:
+//
+// Value of: some_expression
+// Expected: is even
+// Actual: 7
+//
+// where the description "is even" is automatically calculated from the
+// matcher name IsEven.
+//
+// Argument Type
+// =============
+//
+// Note that the type of the value being matched (arg_type) is
+// determined by the context in which you use the matcher and is
+// supplied to you by the compiler, so you don't need to worry about
+// declaring it (nor can you). This allows the matcher to be
+// polymorphic. For example, IsEven() can be used to match any type
+// where the value of "(arg % 2) == 0" can be implicitly converted to
+// a bool. In the "Bar(IsEven())" example above, if method Bar()
+// takes an int, 'arg_type' will be int; if it takes an unsigned long,
+// 'arg_type' will be unsigned long; and so on.
+//
+// Parameterizing Matchers
+// =======================
+//
+// Sometimes you'll want to parameterize the matcher. For that you
+// can use another macro:
+//
+// MATCHER_P(name, param_name, description_string) { statements; }
+//
+// For example:
+//
+// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }
+//
+// will allow you to write:
+//
+// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));
+//
+// which may lead to this message (assuming n is 10):
+//
+// Value of: Blah("a")
+// Expected: has absolute value 10
+// Actual: -9
+//
+// Note that both the matcher description and its parameter are
+// printed, making the message human-friendly.
+//
+// In the matcher definition body, you can write 'foo_type' to
+// reference the type of a parameter named 'foo'. For example, in the
+// body of MATCHER_P(HasAbsoluteValue, value) above, you can write
+// 'value_type' to refer to the type of 'value'.
+//
+// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to
+// support multi-parameter matchers.
+//
+// Describing Parameterized Matchers
+// =================================
+//
+// The last argument to MATCHER*() is a string-typed expression. The
+// expression can reference all of the matcher's parameters and a
+// special bool-typed variable named 'negation'. When 'negation' is
+// false, the expression should evaluate to the matcher's description;
+// otherwise it should evaluate to the description of the negation of
+// the matcher. For example,
+//
+// using testing::PrintToString;
+//
+// MATCHER_P2(InClosedRange, low, hi,
+// string(negation ? "is not" : "is") + " in range [" +
+// PrintToString(low) + ", " + PrintToString(hi) + "]") {
+// return low <= arg && arg <= hi;
+// }
+// ...
+// EXPECT_THAT(3, InClosedRange(4, 6));
+// EXPECT_THAT(3, Not(InClosedRange(2, 4)));
+//
+// would generate two failures that contain the text:
+//
+// Expected: is in range [4, 6]
+// ...
+// Expected: is not in range [2, 4]
+//
+// If you specify "" as the description, the failure message will
+// contain the sequence of words in the matcher name followed by the
+// parameter values printed as a tuple. For example,
+//
+// MATCHER_P2(InClosedRange, low, hi, "") { ... }
+// ...
+// EXPECT_THAT(3, InClosedRange(4, 6));
+// EXPECT_THAT(3, Not(InClosedRange(2, 4)));
+//
+// would generate two failures that contain the text:
+//
+// Expected: in closed range (4, 6)
+// ...
+// Expected: not (in closed range (2, 4))
+//
+// Types of Matcher Parameters
+// ===========================
+//
+// For the purpose of typing, you can view
+//
+// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
+//
+// as shorthand for
+//
+// template <typename p1_type, ..., typename pk_type>
+// FooMatcherPk<p1_type, ..., pk_type>
+// Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// When you write Foo(v1, ..., vk), the compiler infers the types of
+// the parameters v1, ..., and vk for you. If you are not happy with
+// the result of the type inference, you can specify the types by
+// explicitly instantiating the template, as in Foo<long, bool>(5,
+// false). As said earlier, you don't get to (or need to) specify
+// 'arg_type' as that's determined by the context in which the matcher
+// is used. You can assign the result of expression Foo(p1, ..., pk)
+// to a variable of type FooMatcherPk<p1_type, ..., pk_type>. This
+// can be useful when composing matchers.
+//
+// While you can instantiate a matcher template with reference types,
+// passing the parameters by pointer usually makes your code more
+// readable. If, however, you still want to pass a parameter by
+// reference, be aware that in the failure message generated by the
+// matcher you will see the value of the referenced object but not its
+// address.
+//
+// Explaining Match Results
+// ========================
+//
+// Sometimes the matcher description alone isn't enough to explain why
+// the match has failed or succeeded. For example, when expecting a
+// long string, it can be very helpful to also print the diff between
+// the expected string and the actual one. To achieve that, you can
+// optionally stream additional information to a special variable
+// named result_listener, whose type is a pointer to class
+// MatchResultListener:
+//
+// MATCHER_P(EqualsLongString, str, "") {
+// if (arg == str) return true;
+//
+// *result_listener << "the difference: "
+/// << DiffStrings(str, arg);
+// return false;
+// }
+//
+// Overloading Matchers
+// ====================
+//
+// You can overload matchers with different numbers of parameters:
+//
+// MATCHER_P(Blah, a, description_string1) { ... }
+// MATCHER_P2(Blah, a, b, description_string2) { ... }
+//
+// Caveats
+// =======
+//
+// When defining a new matcher, you should also consider implementing
+// MatcherInterface or using MakePolymorphicMatcher(). These
+// approaches require more work than the MATCHER* macros, but also
+// give you more control on the types of the value being matched and
+// the matcher parameters, which may leads to better compiler error
+// messages when the matcher is used wrong. They also allow
+// overloading matchers based on parameter types (as opposed to just
+// based on the number of parameters).
+//
+// MATCHER*() can only be used in a namespace scope. The reason is
+// that C++ doesn't yet allow function-local types to be used to
+// instantiate templates. The up-coming C++0x standard will fix this.
+// Once that's done, we'll consider supporting using MATCHER*() inside
+// a function.
+//
+// More Information
+// ================
+//
+// To learn more about using these macros, please search for 'MATCHER'
+// on http://code.google.com/p/googlemock/wiki/CookBook.
+
+#define MATCHER(name, description)\
+ class name##Matcher {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl()\
+ {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<>()));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>());\
+ }\
+ name##Matcher() {\
+ }\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##Matcher);\
+ };\
+ inline name##Matcher name() {\
+ return name##Matcher();\
+ }\
+ template <typename arg_type>\
+ bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P(name, p0, description)\
+ template <typename p0##_type>\
+ class name##MatcherP {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ explicit gmock_Impl(p0##_type gmock_p0)\
+ : p0(gmock_p0) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type>(p0)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0));\
+ }\
+ explicit name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\
+ }\
+ p0##_type p0;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP);\
+ };\
+ template <typename p0##_type>\
+ inline name##MatcherP<p0##_type> name(p0##_type p0) {\
+ return name##MatcherP<p0##_type>(p0);\
+ }\
+ template <typename p0##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP<p0##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P2(name, p0, p1, description)\
+ template <typename p0##_type, typename p1##_type>\
+ class name##MatcherP2 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\
+ : p0(gmock_p0), p1(gmock_p1) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type>(p0, p1)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1));\
+ }\
+ name##MatcherP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
+ p1(gmock_p1) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP2);\
+ };\
+ template <typename p0##_type, typename p1##_type>\
+ inline name##MatcherP2<p0##_type, p1##_type> name(p0##_type p0, \
+ p1##_type p1) {\
+ return name##MatcherP2<p0##_type, p1##_type>(p0, p1);\
+ }\
+ template <typename p0##_type, typename p1##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP2<p0##_type, \
+ p1##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P3(name, p0, p1, p2, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type>\
+ class name##MatcherP3 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type>(p0, p1, \
+ p2)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2));\
+ }\
+ name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP3);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type>\
+ inline name##MatcherP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
+ p1##_type p1, p2##_type p2) {\
+ return name##MatcherP3<p0##_type, p1##_type, p2##_type>(p0, p1, p2);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP3<p0##_type, p1##_type, \
+ p2##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P4(name, p0, p1, p2, p3, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type>\
+ class name##MatcherP4 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, \
+ p3##_type>(p0, p1, p2, p3)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3));\
+ }\
+ name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP4);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type>\
+ inline name##MatcherP4<p0##_type, p1##_type, p2##_type, \
+ p3##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+ p3##_type p3) {\
+ return name##MatcherP4<p0##_type, p1##_type, p2##_type, p3##_type>(p0, \
+ p1, p2, p3);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP4<p0##_type, p1##_type, p2##_type, \
+ p3##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type>\
+ class name##MatcherP5 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+ p4(gmock_p4) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type>(p0, p1, p2, p3, p4)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3, p4));\
+ }\
+ name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, \
+ p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP5);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type>\
+ inline name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4) {\
+ return name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type>(p0, p1, p2, p3, p4);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type>\
+ class name##MatcherP6 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+ p4(gmock_p4), p5(gmock_p5) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5));\
+ }\
+ name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP6);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type>\
+ inline name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+ p3##_type p3, p4##_type p4, p5##_type p5) {\
+ return name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type>\
+ class name##MatcherP7 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+ p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, \
+ p6)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6));\
+ }\
+ name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
+ p6(gmock_p6) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP7);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type>\
+ inline name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type> name(p0##_type p0, p1##_type p1, \
+ p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+ p6##_type p6) {\
+ return name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, p6);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type>\
+ class name##MatcherP8 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+ p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, \
+ p3, p4, p5, p6, p7)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7));\
+ }\
+ name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6, \
+ p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP8);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type>\
+ inline name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type> name(p0##_type p0, \
+ p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+ p6##_type p6, p7##_type p7) {\
+ return name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, p3, p4, p5, \
+ p6, p7);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, \
+ p7##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type>\
+ class name##MatcherP9 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+ p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+ p8(gmock_p8) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, \
+ p8##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8));\
+ }\
+ name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+ p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+ p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+ p8(gmock_p8) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP9);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type>\
+ inline name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, \
+ p8##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \
+ p8##_type p8) {\
+ return name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type>(p0, p1, p2, \
+ p3, p4, p5, p6, p7, p8);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+ p5##_type, p6##_type, p7##_type, \
+ p8##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type, \
+ typename p9##_type>\
+ class name##MatcherP10 {\
+ public:\
+ template <typename arg_type>\
+ class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+ public:\
+ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+ p9##_type gmock_p9)\
+ : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+ p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+ p8(gmock_p8), p9(gmock_p9) {}\
+ virtual bool MatchAndExplain(\
+ arg_type arg, ::testing::MatchResultListener* result_listener) const;\
+ virtual void DescribeTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(false);\
+ }\
+ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
+ *gmock_os << FormatDescription(true);\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ p9##_type p9;\
+ private:\
+ ::testing::internal::string FormatDescription(bool negation) const {\
+ const ::testing::internal::string gmock_description = (description);\
+ if (!gmock_description.empty())\
+ return gmock_description;\
+ return ::testing::internal::FormatMatcherDescription(\
+ negation, #name, \
+ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+ ::testing::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+ p9##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\
+ }\
+ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
+ };\
+ template <typename arg_type>\
+ operator ::testing::Matcher<arg_type>() const {\
+ return ::testing::Matcher<arg_type>(\
+ new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\
+ }\
+ name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \
+ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+ p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
+ p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+ p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {\
+ }\
+ p0##_type p0;\
+ p1##_type p1;\
+ p2##_type p2;\
+ p3##_type p3;\
+ p4##_type p4;\
+ p5##_type p5;\
+ p6##_type p6;\
+ p7##_type p7;\
+ p8##_type p8;\
+ p9##_type p9;\
+ private:\
+ GTEST_DISALLOW_ASSIGN_(name##MatcherP10);\
+ };\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type, \
+ typename p9##_type>\
+ inline name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+ p9##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+ p9##_type p9) {\
+ return name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>(p0, \
+ p1, p2, p3, p4, p5, p6, p7, p8, p9);\
+ }\
+ template <typename p0##_type, typename p1##_type, typename p2##_type, \
+ typename p3##_type, typename p4##_type, typename p5##_type, \
+ typename p6##_type, typename p7##_type, typename p8##_type, \
+ typename p9##_type>\
+ template <typename arg_type>\
+ bool name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+ p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+ p9##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
+ arg_type arg, \
+ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
+ const
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/gmock-generated-nice-strict.h b/extern/gmock/include/gmock/gmock-generated-nice-strict.h
new file mode 100644
index 00000000000..4095f4d5bc7
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-generated-nice-strict.h
@@ -0,0 +1,397 @@
+// This file was GENERATED by command:
+// pump.py gmock-generated-nice-strict.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Implements class templates NiceMock, NaggyMock, and StrictMock.
+//
+// Given a mock class MockFoo that is created using Google Mock,
+// NiceMock<MockFoo> is a subclass of MockFoo that allows
+// uninteresting calls (i.e. calls to mock methods that have no
+// EXPECT_CALL specs), NaggyMock<MockFoo> is a subclass of MockFoo
+// that prints a warning when an uninteresting call occurs, and
+// StrictMock<MockFoo> is a subclass of MockFoo that treats all
+// uninteresting calls as errors.
+//
+// Currently a mock is naggy by default, so MockFoo and
+// NaggyMock<MockFoo> behave like the same. However, we will soon
+// switch the default behavior of mocks to be nice, as that in general
+// leads to more maintainable tests. When that happens, MockFoo will
+// stop behaving like NaggyMock<MockFoo> and start behaving like
+// NiceMock<MockFoo>.
+//
+// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of
+// their respective base class, with up-to 10 arguments. Therefore
+// you can write NiceMock<MockFoo>(5, "a") to construct a nice mock
+// where MockFoo has a constructor that accepts (int, const char*),
+// for example.
+//
+// A known limitation is that NiceMock<MockFoo>, NaggyMock<MockFoo>,
+// and StrictMock<MockFoo> only works for mock methods defined using
+// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class.
+// If a mock method is defined in a base class of MockFoo, the "nice"
+// or "strict" modifier may not affect it, depending on the compiler.
+// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT
+// supported.
+//
+// Another known limitation is that the constructors of the base mock
+// cannot have arguments passed by non-const reference, which are
+// banned by the Google C++ style guide anyway.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
+
+#include "gmock/gmock-spec-builders.h"
+#include "gmock/internal/gmock-port.h"
+
+namespace testing {
+
+template <class MockClass>
+class NiceMock : public MockClass {
+ public:
+ // We don't factor out the constructor body to a common method, as
+ // we have to avoid a possible clash with members of MockClass.
+ NiceMock() {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ // C++ doesn't (yet) allow inheritance of constructors, so we have
+ // to define it for each arity.
+ template <typename A1>
+ explicit NiceMock(const A1& a1) : MockClass(a1) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+ template <typename A1, typename A2>
+ NiceMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3,
+ const A4& a4) : MockClass(a1, a2, a3, a4) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
+ a6, a7) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
+ a2, a3, a4, a5, a6, a7, a8) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8,
+ const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9, typename A10>
+ NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
+ const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
+ ::testing::Mock::AllowUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ virtual ~NiceMock() {
+ ::testing::Mock::UnregisterCallReaction(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock);
+};
+
+template <class MockClass>
+class NaggyMock : public MockClass {
+ public:
+ // We don't factor out the constructor body to a common method, as
+ // we have to avoid a possible clash with members of MockClass.
+ NaggyMock() {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ // C++ doesn't (yet) allow inheritance of constructors, so we have
+ // to define it for each arity.
+ template <typename A1>
+ explicit NaggyMock(const A1& a1) : MockClass(a1) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+ template <typename A1, typename A2>
+ NaggyMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3,
+ const A4& a4) : MockClass(a1, a2, a3, a4) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
+ a6, a7) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
+ a2, a3, a4, a5, a6, a7, a8) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8,
+ const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9, typename A10>
+ NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
+ const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
+ ::testing::Mock::WarnUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ virtual ~NaggyMock() {
+ ::testing::Mock::UnregisterCallReaction(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock);
+};
+
+template <class MockClass>
+class StrictMock : public MockClass {
+ public:
+ // We don't factor out the constructor body to a common method, as
+ // we have to avoid a possible clash with members of MockClass.
+ StrictMock() {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ // C++ doesn't (yet) allow inheritance of constructors, so we have
+ // to define it for each arity.
+ template <typename A1>
+ explicit StrictMock(const A1& a1) : MockClass(a1) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+ template <typename A1, typename A2>
+ StrictMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3,
+ const A4& a4) : MockClass(a1, a2, a3, a4) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
+ a6, a7) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
+ a2, a3, a4, a5, a6, a7, a8) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8,
+ const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9, typename A10>
+ StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+ const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
+ const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
+ ::testing::Mock::FailUninterestingCalls(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ virtual ~StrictMock() {
+ ::testing::Mock::UnregisterCallReaction(
+ internal::ImplicitCast_<MockClass*>(this));
+ }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock);
+};
+
+// The following specializations catch some (relatively more common)
+// user errors of nesting nice and strict mocks. They do NOT catch
+// all possible errors.
+
+// These specializations are declared but not defined, as NiceMock,
+// NaggyMock, and StrictMock cannot be nested.
+
+template <typename MockClass>
+class NiceMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class NiceMock<NaggyMock<MockClass> >;
+template <typename MockClass>
+class NiceMock<StrictMock<MockClass> >;
+
+template <typename MockClass>
+class NaggyMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class NaggyMock<NaggyMock<MockClass> >;
+template <typename MockClass>
+class NaggyMock<StrictMock<MockClass> >;
+
+template <typename MockClass>
+class StrictMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class StrictMock<NaggyMock<MockClass> >;
+template <typename MockClass>
+class StrictMock<StrictMock<MockClass> >;
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
diff --git a/extern/gmock/include/gmock/gmock-matchers.h b/extern/gmock/include/gmock/gmock-matchers.h
new file mode 100644
index 00000000000..33b37a7a5d6
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-matchers.h
@@ -0,0 +1,4399 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used argument matchers. More
+// matchers can be defined by the user implementing the
+// MatcherInterface<T> interface if necessary.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+
+#include <math.h>
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <ostream> // NOLINT
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+#if GTEST_HAS_STD_INITIALIZER_LIST_
+# include <initializer_list> // NOLINT -- must be after gtest.h
+#endif
+
+namespace testing {
+
+// To implement a matcher Foo for type T, define:
+// 1. a class FooMatcherImpl that implements the
+// MatcherInterface<T> interface, and
+// 2. a factory function that creates a Matcher<T> object from a
+// FooMatcherImpl*.
+//
+// The two-level delegation design makes it possible to allow a user
+// to write "v" instead of "Eq(v)" where a Matcher is expected, which
+// is impossible if we pass matchers by pointers. It also eases
+// ownership management as Matcher objects can now be copied like
+// plain values.
+
+// MatchResultListener is an abstract class. Its << operator can be
+// used by a matcher to explain why a value matches or doesn't match.
+//
+// TODO(wan@google.com): add method
+// bool InterestedInWhy(bool result) const;
+// to indicate whether the listener is interested in why the match
+// result is 'result'.
+class MatchResultListener {
+ public:
+ // Creates a listener object with the given underlying ostream. The
+ // listener does not own the ostream, and does not dereference it
+ // in the constructor or destructor.
+ explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
+ virtual ~MatchResultListener() = 0; // Makes this class abstract.
+
+ // Streams x to the underlying ostream; does nothing if the ostream
+ // is NULL.
+ template <typename T>
+ MatchResultListener& operator<<(const T& x) {
+ if (stream_ != NULL)
+ *stream_ << x;
+ return *this;
+ }
+
+ // Returns the underlying ostream.
+ ::std::ostream* stream() { return stream_; }
+
+ // Returns true iff the listener is interested in an explanation of
+ // the match result. A matcher's MatchAndExplain() method can use
+ // this information to avoid generating the explanation when no one
+ // intends to hear it.
+ bool IsInterested() const { return stream_ != NULL; }
+
+ private:
+ ::std::ostream* const stream_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener);
+};
+
+inline MatchResultListener::~MatchResultListener() {
+}
+
+// An instance of a subclass of this knows how to describe itself as a
+// matcher.
+class MatcherDescriberInterface {
+ public:
+ virtual ~MatcherDescriberInterface() {}
+
+ // Describes this matcher to an ostream. The function should print
+ // a verb phrase that describes the property a value matching this
+ // matcher should have. The subject of the verb phrase is the value
+ // being matched. For example, the DescribeTo() method of the Gt(7)
+ // matcher prints "is greater than 7".
+ virtual void DescribeTo(::std::ostream* os) const = 0;
+
+ // Describes the negation of this matcher to an ostream. For
+ // example, if the description of this matcher is "is greater than
+ // 7", the negated description could be "is not greater than 7".
+ // You are not required to override this when implementing
+ // MatcherInterface, but it is highly advised so that your matcher
+ // can produce good error messages.
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "not (";
+ DescribeTo(os);
+ *os << ")";
+ }
+};
+
+// The implementation of a matcher.
+template <typename T>
+class MatcherInterface : public MatcherDescriberInterface {
+ public:
+ // Returns true iff the matcher matches x; also explains the match
+ // result to 'listener' if necessary (see the next paragraph), in
+ // the form of a non-restrictive relative clause ("which ...",
+ // "whose ...", etc) that describes x. For example, the
+ // MatchAndExplain() method of the Pointee(...) matcher should
+ // generate an explanation like "which points to ...".
+ //
+ // Implementations of MatchAndExplain() should add an explanation of
+ // the match result *if and only if* they can provide additional
+ // information that's not already present (or not obvious) in the
+ // print-out of x and the matcher's description. Whether the match
+ // succeeds is not a factor in deciding whether an explanation is
+ // needed, as sometimes the caller needs to print a failure message
+ // when the match succeeds (e.g. when the matcher is used inside
+ // Not()).
+ //
+ // For example, a "has at least 10 elements" matcher should explain
+ // what the actual element count is, regardless of the match result,
+ // as it is useful information to the reader; on the other hand, an
+ // "is empty" matcher probably only needs to explain what the actual
+ // size is when the match fails, as it's redundant to say that the
+ // size is 0 when the value is already known to be empty.
+ //
+ // You should override this method when defining a new matcher.
+ //
+ // It's the responsibility of the caller (Google Mock) to guarantee
+ // that 'listener' is not NULL. This helps to simplify a matcher's
+ // implementation when it doesn't care about the performance, as it
+ // can talk to 'listener' without checking its validity first.
+ // However, in order to implement dummy listeners efficiently,
+ // listener->stream() may be NULL.
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
+
+ // Inherits these methods from MatcherDescriberInterface:
+ // virtual void DescribeTo(::std::ostream* os) const = 0;
+ // virtual void DescribeNegationTo(::std::ostream* os) const;
+};
+
+// A match result listener that stores the explanation in a string.
+class StringMatchResultListener : public MatchResultListener {
+ public:
+ StringMatchResultListener() : MatchResultListener(&ss_) {}
+
+ // Returns the explanation accumulated so far.
+ internal::string str() const { return ss_.str(); }
+
+ // Clears the explanation accumulated so far.
+ void Clear() { ss_.str(""); }
+
+ private:
+ ::std::stringstream ss_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener);
+};
+
+namespace internal {
+
+struct AnyEq {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a == b; }
+};
+struct AnyNe {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a != b; }
+};
+struct AnyLt {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a < b; }
+};
+struct AnyGt {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a > b; }
+};
+struct AnyLe {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a <= b; }
+};
+struct AnyGe {
+ template <typename A, typename B>
+ bool operator()(const A& a, const B& b) const { return a >= b; }
+};
+
+// A match result listener that ignores the explanation.
+class DummyMatchResultListener : public MatchResultListener {
+ public:
+ DummyMatchResultListener() : MatchResultListener(NULL) {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener);
+};
+
+// A match result listener that forwards the explanation to a given
+// ostream. The difference between this and MatchResultListener is
+// that the former is concrete.
+class StreamMatchResultListener : public MatchResultListener {
+ public:
+ explicit StreamMatchResultListener(::std::ostream* os)
+ : MatchResultListener(os) {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
+};
+
+// An internal class for implementing Matcher<T>, which will derive
+// from it. We put functionalities common to all Matcher<T>
+// specializations here to avoid code duplication.
+template <typename T>
+class MatcherBase {
+ public:
+ // Returns true iff the matcher matches x; also explains the match
+ // result to 'listener'.
+ bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ return impl_->MatchAndExplain(x, listener);
+ }
+
+ // Returns true iff this matcher matches x.
+ bool Matches(T x) const {
+ DummyMatchResultListener dummy;
+ return MatchAndExplain(x, &dummy);
+ }
+
+ // Describes this matcher to an ostream.
+ void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+
+ // Describes the negation of this matcher to an ostream.
+ void DescribeNegationTo(::std::ostream* os) const {
+ impl_->DescribeNegationTo(os);
+ }
+
+ // Explains why x matches, or doesn't match, the matcher.
+ void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+ StreamMatchResultListener listener(os);
+ MatchAndExplain(x, &listener);
+ }
+
+ // Returns the describer for this matcher object; retains ownership
+ // of the describer, which is only guaranteed to be alive when
+ // this matcher object is alive.
+ const MatcherDescriberInterface* GetDescriber() const {
+ return impl_.get();
+ }
+
+ protected:
+ MatcherBase() {}
+
+ // Constructs a matcher from its implementation.
+ explicit MatcherBase(const MatcherInterface<T>* impl)
+ : impl_(impl) {}
+
+ virtual ~MatcherBase() {}
+
+ private:
+ // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar
+ // interfaces. The former dynamically allocates a chunk of memory
+ // to hold the reference count, while the latter tracks all
+ // references using a circular linked list without allocating
+ // memory. It has been observed that linked_ptr performs better in
+ // typical scenarios. However, shared_ptr can out-perform
+ // linked_ptr when there are many more uses of the copy constructor
+ // than the default constructor.
+ //
+ // If performance becomes a problem, we should see if using
+ // shared_ptr helps.
+ ::testing::internal::linked_ptr<const MatcherInterface<T> > impl_;
+};
+
+} // namespace internal
+
+// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
+// object that can check whether a value of type T matches. The
+// implementation of Matcher<T> is just a linked_ptr to const
+// MatcherInterface<T>, so copying is fairly cheap. Don't inherit
+// from Matcher!
+template <typename T>
+class Matcher : public internal::MatcherBase<T> {
+ public:
+ // Constructs a null matcher. Needed for storing Matcher objects in STL
+ // containers. A default-constructed matcher is not yet initialized. You
+ // cannot use it until a valid value has been assigned to it.
+ explicit Matcher() {} // NOLINT
+
+ // Constructs a matcher from its implementation.
+ explicit Matcher(const MatcherInterface<T>* impl)
+ : internal::MatcherBase<T>(impl) {}
+
+ // Implicit constructor here allows people to write
+ // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
+ Matcher(T value); // NOLINT
+};
+
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a string
+// matcher is expected.
+template <>
+class GTEST_API_ Matcher<const internal::string&>
+ : public internal::MatcherBase<const internal::string&> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<const internal::string&>* impl)
+ : internal::MatcherBase<const internal::string&>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a string object.
+ Matcher(const internal::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+};
+
+template <>
+class GTEST_API_ Matcher<internal::string>
+ : public internal::MatcherBase<internal::string> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<internal::string>* impl)
+ : internal::MatcherBase<internal::string>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a string object.
+ Matcher(const internal::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+};
+
+#if GTEST_HAS_STRING_PIECE_
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a StringPiece
+// matcher is expected.
+template <>
+class GTEST_API_ Matcher<const StringPiece&>
+ : public internal::MatcherBase<const StringPiece&> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<const StringPiece&>* impl)
+ : internal::MatcherBase<const StringPiece&>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a string object.
+ Matcher(const internal::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+
+ // Allows the user to pass StringPieces directly.
+ Matcher(StringPiece s); // NOLINT
+};
+
+template <>
+class GTEST_API_ Matcher<StringPiece>
+ : public internal::MatcherBase<StringPiece> {
+ public:
+ Matcher() {}
+
+ explicit Matcher(const MatcherInterface<StringPiece>* impl)
+ : internal::MatcherBase<StringPiece>(impl) {}
+
+ // Allows the user to write str instead of Eq(str) sometimes, where
+ // str is a string object.
+ Matcher(const internal::string& s); // NOLINT
+
+ // Allows the user to write "foo" instead of Eq("foo") sometimes.
+ Matcher(const char* s); // NOLINT
+
+ // Allows the user to pass StringPieces directly.
+ Matcher(StringPiece s); // NOLINT
+};
+#endif // GTEST_HAS_STRING_PIECE_
+
+// The PolymorphicMatcher class template makes it easy to implement a
+// polymorphic matcher (i.e. a matcher that can match values of more
+// than one type, e.g. Eq(n) and NotNull()).
+//
+// To define a polymorphic matcher, a user should provide an Impl
+// class that has a DescribeTo() method and a DescribeNegationTo()
+// method, and define a member function (or member function template)
+//
+// bool MatchAndExplain(const Value& value,
+// MatchResultListener* listener) const;
+//
+// See the definition of NotNull() for a complete example.
+template <class Impl>
+class PolymorphicMatcher {
+ public:
+ explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
+
+ // Returns a mutable reference to the underlying matcher
+ // implementation object.
+ Impl& mutable_impl() { return impl_; }
+
+ // Returns an immutable reference to the underlying matcher
+ // implementation object.
+ const Impl& impl() const { return impl_; }
+
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new MonomorphicImpl<T>(impl_));
+ }
+
+ private:
+ template <typename T>
+ class MonomorphicImpl : public MatcherInterface<T> {
+ public:
+ explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ impl_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ impl_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ return impl_.MatchAndExplain(x, listener);
+ }
+
+ private:
+ const Impl impl_;
+
+ GTEST_DISALLOW_ASSIGN_(MonomorphicImpl);
+ };
+
+ Impl impl_;
+
+ GTEST_DISALLOW_ASSIGN_(PolymorphicMatcher);
+};
+
+// Creates a matcher from its implementation. This is easier to use
+// than the Matcher<T> constructor as it doesn't require you to
+// explicitly write the template argument, e.g.
+//
+// MakeMatcher(foo);
+// vs
+// Matcher<const string&>(foo);
+template <typename T>
+inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
+ return Matcher<T>(impl);
+}
+
+// Creates a polymorphic matcher from its implementation. This is
+// easier to use than the PolymorphicMatcher<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+// MakePolymorphicMatcher(foo);
+// vs
+// PolymorphicMatcher<TypeOfFoo>(foo);
+template <class Impl>
+inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
+ return PolymorphicMatcher<Impl>(impl);
+}
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// The MatcherCastImpl class template is a helper for implementing
+// MatcherCast(). We need this helper in order to partially
+// specialize the implementation of MatcherCast() (C++ allows
+// class/struct templates to be partially specialized, but not
+// function templates.).
+
+// This general version is used when MatcherCast()'s argument is a
+// polymorphic matcher (i.e. something that can be converted to a
+// Matcher but is not one yet; for example, Eq(value)) or a value (for
+// example, "hello").
+template <typename T, typename M>
+class MatcherCastImpl {
+ public:
+ static Matcher<T> Cast(const M& polymorphic_matcher_or_value) {
+ // M can be a polymorhic matcher, in which case we want to use
+ // its conversion operator to create Matcher<T>. Or it can be a value
+ // that should be passed to the Matcher<T>'s constructor.
+ //
+ // We can't call Matcher<T>(polymorphic_matcher_or_value) when M is a
+ // polymorphic matcher because it'll be ambiguous if T has an implicit
+ // constructor from M (this usually happens when T has an implicit
+ // constructor from any type).
+ //
+ // It won't work to unconditionally implict_cast
+ // polymorphic_matcher_or_value to Matcher<T> because it won't trigger
+ // a user-defined conversion from M to T if one exists (assuming M is
+ // a value).
+ return CastImpl(
+ polymorphic_matcher_or_value,
+ BooleanConstant<
+ internal::ImplicitlyConvertible<M, Matcher<T> >::value>());
+ }
+
+ private:
+ static Matcher<T> CastImpl(const M& value, BooleanConstant<false>) {
+ // M can't be implicitly converted to Matcher<T>, so M isn't a polymorphic
+ // matcher. It must be a value then. Use direct initialization to create
+ // a matcher.
+ return Matcher<T>(ImplicitCast_<T>(value));
+ }
+
+ static Matcher<T> CastImpl(const M& polymorphic_matcher_or_value,
+ BooleanConstant<true>) {
+ // M is implicitly convertible to Matcher<T>, which means that either
+ // M is a polymorhpic matcher or Matcher<T> has an implicit constructor
+ // from M. In both cases using the implicit conversion will produce a
+ // matcher.
+ //
+ // Even if T has an implicit constructor from M, it won't be called because
+ // creating Matcher<T> would require a chain of two user-defined conversions
+ // (first to create T from M and then to create Matcher<T> from T).
+ return polymorphic_matcher_or_value;
+ }
+};
+
+// This more specialized version is used when MatcherCast()'s argument
+// is already a Matcher. This only compiles when type T can be
+// statically converted to type U.
+template <typename T, typename U>
+class MatcherCastImpl<T, Matcher<U> > {
+ public:
+ static Matcher<T> Cast(const Matcher<U>& source_matcher) {
+ return Matcher<T>(new Impl(source_matcher));
+ }
+
+ private:
+ class Impl : public MatcherInterface<T> {
+ public:
+ explicit Impl(const Matcher<U>& source_matcher)
+ : source_matcher_(source_matcher) {}
+
+ // We delegate the matching logic to the source matcher.
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ return source_matcher_.MatchAndExplain(static_cast<U>(x), listener);
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ source_matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ source_matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ const Matcher<U> source_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+};
+
+// This even more specialized version is used for efficiently casting
+// a matcher to its own type.
+template <typename T>
+class MatcherCastImpl<T, Matcher<T> > {
+ public:
+ static Matcher<T> Cast(const Matcher<T>& matcher) { return matcher; }
+};
+
+} // namespace internal
+
+// In order to be safe and clear, casting between different matcher
+// types is done explicitly via MatcherCast<T>(m), which takes a
+// matcher m and returns a Matcher<T>. It compiles only when T can be
+// statically converted to the argument type of m.
+template <typename T, typename M>
+inline Matcher<T> MatcherCast(const M& matcher) {
+ return internal::MatcherCastImpl<T, M>::Cast(matcher);
+}
+
+// Implements SafeMatcherCast().
+//
+// We use an intermediate class to do the actual safe casting as Nokia's
+// Symbian compiler cannot decide between
+// template <T, M> ... (M) and
+// template <T, U> ... (const Matcher<U>&)
+// for function templates but can for member function templates.
+template <typename T>
+class SafeMatcherCastImpl {
+ public:
+ // This overload handles polymorphic matchers and values only since
+ // monomorphic matchers are handled by the next one.
+ template <typename M>
+ static inline Matcher<T> Cast(const M& polymorphic_matcher_or_value) {
+ return internal::MatcherCastImpl<T, M>::Cast(polymorphic_matcher_or_value);
+ }
+
+ // This overload handles monomorphic matchers.
+ //
+ // In general, if type T can be implicitly converted to type U, we can
+ // safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
+ // contravariant): just keep a copy of the original Matcher<U>, convert the
+ // argument from type T to U, and then pass it to the underlying Matcher<U>.
+ // The only exception is when U is a reference and T is not, as the
+ // underlying Matcher<U> may be interested in the argument's address, which
+ // is not preserved in the conversion from T to U.
+ template <typename U>
+ static inline Matcher<T> Cast(const Matcher<U>& matcher) {
+ // Enforce that T can be implicitly converted to U.
+ GTEST_COMPILE_ASSERT_((internal::ImplicitlyConvertible<T, U>::value),
+ T_must_be_implicitly_convertible_to_U);
+ // Enforce that we are not converting a non-reference type T to a reference
+ // type U.
+ GTEST_COMPILE_ASSERT_(
+ internal::is_reference<T>::value || !internal::is_reference<U>::value,
+ cannot_convert_non_referentce_arg_to_reference);
+ // In case both T and U are arithmetic types, enforce that the
+ // conversion is not lossy.
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU;
+ const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther;
+ const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther;
+ GTEST_COMPILE_ASSERT_(
+ kTIsOther || kUIsOther ||
+ (internal::LosslessArithmeticConvertible<RawT, RawU>::value),
+ conversion_of_arithmetic_types_must_be_lossless);
+ return MatcherCast<T>(matcher);
+ }
+};
+
+template <typename T, typename M>
+inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher) {
+ return SafeMatcherCastImpl<T>::Cast(polymorphic_matcher);
+}
+
+// A<T>() returns a matcher that matches any value of type T.
+template <typename T>
+Matcher<T> A();
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// If the explanation is not empty, prints it to the ostream.
+inline void PrintIfNotEmpty(const internal::string& explanation,
+ ::std::ostream* os) {
+ if (explanation != "" && os != NULL) {
+ *os << ", " << explanation;
+ }
+}
+
+// Returns true if the given type name is easy to read by a human.
+// This is used to decide whether printing the type of a value might
+// be helpful.
+inline bool IsReadableTypeName(const string& type_name) {
+ // We consider a type name readable if it's short or doesn't contain
+ // a template or function type.
+ return (type_name.length() <= 20 ||
+ type_name.find_first_of("<(") == string::npos);
+}
+
+// Matches the value against the given matcher, prints the value and explains
+// the match result to the listener. Returns the match result.
+// 'listener' must not be NULL.
+// Value cannot be passed by const reference, because some matchers take a
+// non-const argument.
+template <typename Value, typename T>
+bool MatchPrintAndExplain(Value& value, const Matcher<T>& matcher,
+ MatchResultListener* listener) {
+ if (!listener->IsInterested()) {
+ // If the listener is not interested, we do not need to construct the
+ // inner explanation.
+ return matcher.Matches(value);
+ }
+
+ StringMatchResultListener inner_listener;
+ const bool match = matcher.MatchAndExplain(value, &inner_listener);
+
+ UniversalPrint(value, listener->stream());
+#if GTEST_HAS_RTTI
+ const string& type_name = GetTypeName<Value>();
+ if (IsReadableTypeName(type_name))
+ *listener->stream() << " (of type " << type_name << ")";
+#endif
+ PrintIfNotEmpty(inner_listener.str(), listener->stream());
+
+ return match;
+}
+
+// An internal helper class for doing compile-time loop on a tuple's
+// fields.
+template <size_t N>
+class TuplePrefix {
+ public:
+ // TuplePrefix<N>::Matches(matcher_tuple, value_tuple) returns true
+ // iff the first N fields of matcher_tuple matches the first N
+ // fields of value_tuple, respectively.
+ template <typename MatcherTuple, typename ValueTuple>
+ static bool Matches(const MatcherTuple& matcher_tuple,
+ const ValueTuple& value_tuple) {
+ return TuplePrefix<N - 1>::Matches(matcher_tuple, value_tuple)
+ && get<N - 1>(matcher_tuple).Matches(get<N - 1>(value_tuple));
+ }
+
+ // TuplePrefix<N>::ExplainMatchFailuresTo(matchers, values, os)
+ // describes failures in matching the first N fields of matchers
+ // against the first N fields of values. If there is no failure,
+ // nothing will be streamed to os.
+ template <typename MatcherTuple, typename ValueTuple>
+ static void ExplainMatchFailuresTo(const MatcherTuple& matchers,
+ const ValueTuple& values,
+ ::std::ostream* os) {
+ // First, describes failures in the first N - 1 fields.
+ TuplePrefix<N - 1>::ExplainMatchFailuresTo(matchers, values, os);
+
+ // Then describes the failure (if any) in the (N - 1)-th (0-based)
+ // field.
+ typename tuple_element<N - 1, MatcherTuple>::type matcher =
+ get<N - 1>(matchers);
+ typedef typename tuple_element<N - 1, ValueTuple>::type Value;
+ Value value = get<N - 1>(values);
+ StringMatchResultListener listener;
+ if (!matcher.MatchAndExplain(value, &listener)) {
+ // TODO(wan): include in the message the name of the parameter
+ // as used in MOCK_METHOD*() when possible.
+ *os << " Expected arg #" << N - 1 << ": ";
+ get<N - 1>(matchers).DescribeTo(os);
+ *os << "\n Actual: ";
+ // We remove the reference in type Value to prevent the
+ // universal printer from printing the address of value, which
+ // isn't interesting to the user most of the time. The
+ // matcher's MatchAndExplain() method handles the case when
+ // the address is interesting.
+ internal::UniversalPrint(value, os);
+ PrintIfNotEmpty(listener.str(), os);
+ *os << "\n";
+ }
+ }
+};
+
+// The base case.
+template <>
+class TuplePrefix<0> {
+ public:
+ template <typename MatcherTuple, typename ValueTuple>
+ static bool Matches(const MatcherTuple& /* matcher_tuple */,
+ const ValueTuple& /* value_tuple */) {
+ return true;
+ }
+
+ template <typename MatcherTuple, typename ValueTuple>
+ static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */,
+ const ValueTuple& /* values */,
+ ::std::ostream* /* os */) {}
+};
+
+// TupleMatches(matcher_tuple, value_tuple) returns true iff all
+// matchers in matcher_tuple match the corresponding fields in
+// value_tuple. It is a compiler error if matcher_tuple and
+// value_tuple have different number of fields or incompatible field
+// types.
+template <typename MatcherTuple, typename ValueTuple>
+bool TupleMatches(const MatcherTuple& matcher_tuple,
+ const ValueTuple& value_tuple) {
+ // Makes sure that matcher_tuple and value_tuple have the same
+ // number of fields.
+ GTEST_COMPILE_ASSERT_(tuple_size<MatcherTuple>::value ==
+ tuple_size<ValueTuple>::value,
+ matcher_and_value_have_different_numbers_of_fields);
+ return TuplePrefix<tuple_size<ValueTuple>::value>::
+ Matches(matcher_tuple, value_tuple);
+}
+
+// Describes failures in matching matchers against values. If there
+// is no failure, nothing will be streamed to os.
+template <typename MatcherTuple, typename ValueTuple>
+void ExplainMatchFailureTupleTo(const MatcherTuple& matchers,
+ const ValueTuple& values,
+ ::std::ostream* os) {
+ TuplePrefix<tuple_size<MatcherTuple>::value>::ExplainMatchFailuresTo(
+ matchers, values, os);
+}
+
+// TransformTupleValues and its helper.
+//
+// TransformTupleValuesHelper hides the internal machinery that
+// TransformTupleValues uses to implement a tuple traversal.
+template <typename Tuple, typename Func, typename OutIter>
+class TransformTupleValuesHelper {
+ private:
+ typedef ::testing::tuple_size<Tuple> TupleSize;
+
+ public:
+ // For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'.
+ // Returns the final value of 'out' in case the caller needs it.
+ static OutIter Run(Func f, const Tuple& t, OutIter out) {
+ return IterateOverTuple<Tuple, TupleSize::value>()(f, t, out);
+ }
+
+ private:
+ template <typename Tup, size_t kRemainingSize>
+ struct IterateOverTuple {
+ OutIter operator() (Func f, const Tup& t, OutIter out) const {
+ *out++ = f(::testing::get<TupleSize::value - kRemainingSize>(t));
+ return IterateOverTuple<Tup, kRemainingSize - 1>()(f, t, out);
+ }
+ };
+ template <typename Tup>
+ struct IterateOverTuple<Tup, 0> {
+ OutIter operator() (Func /* f */, const Tup& /* t */, OutIter out) const {
+ return out;
+ }
+ };
+};
+
+// Successively invokes 'f(element)' on each element of the tuple 't',
+// appending each result to the 'out' iterator. Returns the final value
+// of 'out'.
+template <typename Tuple, typename Func, typename OutIter>
+OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) {
+ return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out);
+}
+
+// Implements A<T>().
+template <typename T>
+class AnyMatcherImpl : public MatcherInterface<T> {
+ public:
+ virtual bool MatchAndExplain(
+ T /* x */, MatchResultListener* /* listener */) const { return true; }
+ virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; }
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ // This is mostly for completeness' safe, as it's not very useful
+ // to write Not(A<bool>()). However we cannot completely rule out
+ // such a possibility, and it doesn't hurt to be prepared.
+ *os << "never matches";
+ }
+};
+
+// Implements _, a matcher that matches any value of any
+// type. This is a polymorphic matcher, so we need a template type
+// conversion operator to make it appearing as a Matcher<T> for any
+// type T.
+class AnythingMatcher {
+ public:
+ template <typename T>
+ operator Matcher<T>() const { return A<T>(); }
+};
+
+// Implements a matcher that compares a given value with a
+// pre-supplied value using one of the ==, <=, <, etc, operators. The
+// two values being compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq(5) can be
+// used to match an int, a short, a double, etc). Therefore we use
+// a template type conversion operator in the implementation.
+//
+// The following template definition assumes that the Rhs parameter is
+// a "bare" type (i.e. neither 'const T' nor 'T&').
+template <typename D, typename Rhs, typename Op>
+class ComparisonBase {
+ public:
+ explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
+ template <typename Lhs>
+ operator Matcher<Lhs>() const {
+ return MakeMatcher(new Impl<Lhs>(rhs_));
+ }
+
+ private:
+ template <typename Lhs>
+ class Impl : public MatcherInterface<Lhs> {
+ public:
+ explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
+ virtual bool MatchAndExplain(
+ Lhs lhs, MatchResultListener* /* listener */) const {
+ return Op()(lhs, rhs_);
+ }
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << D::Desc() << " ";
+ UniversalPrint(rhs_, os);
+ }
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << D::NegatedDesc() << " ";
+ UniversalPrint(rhs_, os);
+ }
+ private:
+ Rhs rhs_;
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+ Rhs rhs_;
+ GTEST_DISALLOW_ASSIGN_(ComparisonBase);
+};
+
+template <typename Rhs>
+class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
+ public:
+ explicit EqMatcher(const Rhs& rhs)
+ : ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { }
+ static const char* Desc() { return "is equal to"; }
+ static const char* NegatedDesc() { return "isn't equal to"; }
+};
+template <typename Rhs>
+class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
+ public:
+ explicit NeMatcher(const Rhs& rhs)
+ : ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { }
+ static const char* Desc() { return "isn't equal to"; }
+ static const char* NegatedDesc() { return "is equal to"; }
+};
+template <typename Rhs>
+class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
+ public:
+ explicit LtMatcher(const Rhs& rhs)
+ : ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { }
+ static const char* Desc() { return "is <"; }
+ static const char* NegatedDesc() { return "isn't <"; }
+};
+template <typename Rhs>
+class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
+ public:
+ explicit GtMatcher(const Rhs& rhs)
+ : ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { }
+ static const char* Desc() { return "is >"; }
+ static const char* NegatedDesc() { return "isn't >"; }
+};
+template <typename Rhs>
+class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
+ public:
+ explicit LeMatcher(const Rhs& rhs)
+ : ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { }
+ static const char* Desc() { return "is <="; }
+ static const char* NegatedDesc() { return "isn't <="; }
+};
+template <typename Rhs>
+class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
+ public:
+ explicit GeMatcher(const Rhs& rhs)
+ : ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { }
+ static const char* Desc() { return "is >="; }
+ static const char* NegatedDesc() { return "isn't >="; }
+};
+
+// Implements the polymorphic IsNull() matcher, which matches any raw or smart
+// pointer that is NULL.
+class IsNullMatcher {
+ public:
+ template <typename Pointer>
+ bool MatchAndExplain(const Pointer& p,
+ MatchResultListener* /* listener */) const {
+#if GTEST_LANG_CXX11
+ return p == nullptr;
+#else // GTEST_LANG_CXX11
+ return GetRawPointer(p) == NULL;
+#endif // GTEST_LANG_CXX11
+ }
+
+ void DescribeTo(::std::ostream* os) const { *os << "is NULL"; }
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "isn't NULL";
+ }
+};
+
+// Implements the polymorphic NotNull() matcher, which matches any raw or smart
+// pointer that is not NULL.
+class NotNullMatcher {
+ public:
+ template <typename Pointer>
+ bool MatchAndExplain(const Pointer& p,
+ MatchResultListener* /* listener */) const {
+#if GTEST_LANG_CXX11
+ return p != nullptr;
+#else // GTEST_LANG_CXX11
+ return GetRawPointer(p) != NULL;
+#endif // GTEST_LANG_CXX11
+ }
+
+ void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; }
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "is NULL";
+ }
+};
+
+// Ref(variable) matches any argument that is a reference to
+// 'variable'. This matcher is polymorphic as it can match any
+// super type of the type of 'variable'.
+//
+// The RefMatcher template class implements Ref(variable). It can
+// only be instantiated with a reference type. This prevents a user
+// from mistakenly using Ref(x) to match a non-reference function
+// argument. For example, the following will righteously cause a
+// compiler error:
+//
+// int n;
+// Matcher<int> m1 = Ref(n); // This won't compile.
+// Matcher<int&> m2 = Ref(n); // This will compile.
+template <typename T>
+class RefMatcher;
+
+template <typename T>
+class RefMatcher<T&> {
+ // Google Mock is a generic framework and thus needs to support
+ // mocking any function types, including those that take non-const
+ // reference arguments. Therefore the template parameter T (and
+ // Super below) can be instantiated to either a const type or a
+ // non-const type.
+ public:
+ // RefMatcher() takes a T& instead of const T&, as we want the
+ // compiler to catch using Ref(const_value) as a matcher for a
+ // non-const reference.
+ explicit RefMatcher(T& x) : object_(x) {} // NOLINT
+
+ template <typename Super>
+ operator Matcher<Super&>() const {
+ // By passing object_ (type T&) to Impl(), which expects a Super&,
+ // we make sure that Super is a super type of T. In particular,
+ // this catches using Ref(const_value) as a matcher for a
+ // non-const reference, as you cannot implicitly convert a const
+ // reference to a non-const reference.
+ return MakeMatcher(new Impl<Super>(object_));
+ }
+
+ private:
+ template <typename Super>
+ class Impl : public MatcherInterface<Super&> {
+ public:
+ explicit Impl(Super& x) : object_(x) {} // NOLINT
+
+ // MatchAndExplain() takes a Super& (as opposed to const Super&)
+ // in order to match the interface MatcherInterface<Super&>.
+ virtual bool MatchAndExplain(
+ Super& x, MatchResultListener* listener) const {
+ *listener << "which is located @" << static_cast<const void*>(&x);
+ return &x == &object_;
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "references the variable ";
+ UniversalPrinter<Super&>::Print(object_, os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "does not reference the variable ";
+ UniversalPrinter<Super&>::Print(object_, os);
+ }
+
+ private:
+ const Super& object_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ T& object_;
+
+ GTEST_DISALLOW_ASSIGN_(RefMatcher);
+};
+
+// Polymorphic helper functions for narrow and wide string matchers.
+inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) {
+ return String::CaseInsensitiveCStringEquals(lhs, rhs);
+}
+
+inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs,
+ const wchar_t* rhs) {
+ return String::CaseInsensitiveWideCStringEquals(lhs, rhs);
+}
+
+// String comparison for narrow or wide strings that can have embedded NUL
+// characters.
+template <typename StringType>
+bool CaseInsensitiveStringEquals(const StringType& s1,
+ const StringType& s2) {
+ // Are the heads equal?
+ if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) {
+ return false;
+ }
+
+ // Skip the equal heads.
+ const typename StringType::value_type nul = 0;
+ const size_t i1 = s1.find(nul), i2 = s2.find(nul);
+
+ // Are we at the end of either s1 or s2?
+ if (i1 == StringType::npos || i2 == StringType::npos) {
+ return i1 == i2;
+ }
+
+ // Are the tails equal?
+ return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1));
+}
+
+// String matchers.
+
+// Implements equality-based string matchers like StrEq, StrCaseNe, and etc.
+template <typename StringType>
+class StrEqualityMatcher {
+ public:
+ StrEqualityMatcher(const StringType& str, bool expect_eq,
+ bool case_sensitive)
+ : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {}
+
+ // Accepts pointer types, particularly:
+ // const char*
+ // char*
+ // const wchar_t*
+ // wchar_t*
+ template <typename CharType>
+ bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+ if (s == NULL) {
+ return !expect_eq_;
+ }
+ return MatchAndExplain(StringType(s), listener);
+ }
+
+ // Matches anything that can convert to StringType.
+ //
+ // This is a template, not just a plain function with const StringType&,
+ // because StringPiece has some interfering non-explicit constructors.
+ template <typename MatcheeStringType>
+ bool MatchAndExplain(const MatcheeStringType& s,
+ MatchResultListener* /* listener */) const {
+ const StringType& s2(s);
+ const bool eq = case_sensitive_ ? s2 == string_ :
+ CaseInsensitiveStringEquals(s2, string_);
+ return expect_eq_ == eq;
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ DescribeToHelper(expect_eq_, os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ DescribeToHelper(!expect_eq_, os);
+ }
+
+ private:
+ void DescribeToHelper(bool expect_eq, ::std::ostream* os) const {
+ *os << (expect_eq ? "is " : "isn't ");
+ *os << "equal to ";
+ if (!case_sensitive_) {
+ *os << "(ignoring case) ";
+ }
+ UniversalPrint(string_, os);
+ }
+
+ const StringType string_;
+ const bool expect_eq_;
+ const bool case_sensitive_;
+
+ GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher);
+};
+
+// Implements the polymorphic HasSubstr(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class HasSubstrMatcher {
+ public:
+ explicit HasSubstrMatcher(const StringType& substring)
+ : substring_(substring) {}
+
+ // Accepts pointer types, particularly:
+ // const char*
+ // char*
+ // const wchar_t*
+ // wchar_t*
+ template <typename CharType>
+ bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+ return s != NULL && MatchAndExplain(StringType(s), listener);
+ }
+
+ // Matches anything that can convert to StringType.
+ //
+ // This is a template, not just a plain function with const StringType&,
+ // because StringPiece has some interfering non-explicit constructors.
+ template <typename MatcheeStringType>
+ bool MatchAndExplain(const MatcheeStringType& s,
+ MatchResultListener* /* listener */) const {
+ const StringType& s2(s);
+ return s2.find(substring_) != StringType::npos;
+ }
+
+ // Describes what this matcher matches.
+ void DescribeTo(::std::ostream* os) const {
+ *os << "has substring ";
+ UniversalPrint(substring_, os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "has no substring ";
+ UniversalPrint(substring_, os);
+ }
+
+ private:
+ const StringType substring_;
+
+ GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher);
+};
+
+// Implements the polymorphic StartsWith(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class StartsWithMatcher {
+ public:
+ explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) {
+ }
+
+ // Accepts pointer types, particularly:
+ // const char*
+ // char*
+ // const wchar_t*
+ // wchar_t*
+ template <typename CharType>
+ bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+ return s != NULL && MatchAndExplain(StringType(s), listener);
+ }
+
+ // Matches anything that can convert to StringType.
+ //
+ // This is a template, not just a plain function with const StringType&,
+ // because StringPiece has some interfering non-explicit constructors.
+ template <typename MatcheeStringType>
+ bool MatchAndExplain(const MatcheeStringType& s,
+ MatchResultListener* /* listener */) const {
+ const StringType& s2(s);
+ return s2.length() >= prefix_.length() &&
+ s2.substr(0, prefix_.length()) == prefix_;
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << "starts with ";
+ UniversalPrint(prefix_, os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't start with ";
+ UniversalPrint(prefix_, os);
+ }
+
+ private:
+ const StringType prefix_;
+
+ GTEST_DISALLOW_ASSIGN_(StartsWithMatcher);
+};
+
+// Implements the polymorphic EndsWith(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class EndsWithMatcher {
+ public:
+ explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {}
+
+ // Accepts pointer types, particularly:
+ // const char*
+ // char*
+ // const wchar_t*
+ // wchar_t*
+ template <typename CharType>
+ bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+ return s != NULL && MatchAndExplain(StringType(s), listener);
+ }
+
+ // Matches anything that can convert to StringType.
+ //
+ // This is a template, not just a plain function with const StringType&,
+ // because StringPiece has some interfering non-explicit constructors.
+ template <typename MatcheeStringType>
+ bool MatchAndExplain(const MatcheeStringType& s,
+ MatchResultListener* /* listener */) const {
+ const StringType& s2(s);
+ return s2.length() >= suffix_.length() &&
+ s2.substr(s2.length() - suffix_.length()) == suffix_;
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << "ends with ";
+ UniversalPrint(suffix_, os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't end with ";
+ UniversalPrint(suffix_, os);
+ }
+
+ private:
+ const StringType suffix_;
+
+ GTEST_DISALLOW_ASSIGN_(EndsWithMatcher);
+};
+
+// Implements polymorphic matchers MatchesRegex(regex) and
+// ContainsRegex(regex), which can be used as a Matcher<T> as long as
+// T can be converted to a string.
+class MatchesRegexMatcher {
+ public:
+ MatchesRegexMatcher(const RE* regex, bool full_match)
+ : regex_(regex), full_match_(full_match) {}
+
+ // Accepts pointer types, particularly:
+ // const char*
+ // char*
+ // const wchar_t*
+ // wchar_t*
+ template <typename CharType>
+ bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+ return s != NULL && MatchAndExplain(internal::string(s), listener);
+ }
+
+ // Matches anything that can convert to internal::string.
+ //
+ // This is a template, not just a plain function with const internal::string&,
+ // because StringPiece has some interfering non-explicit constructors.
+ template <class MatcheeStringType>
+ bool MatchAndExplain(const MatcheeStringType& s,
+ MatchResultListener* /* listener */) const {
+ const internal::string& s2(s);
+ return full_match_ ? RE::FullMatch(s2, *regex_) :
+ RE::PartialMatch(s2, *regex_);
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << (full_match_ ? "matches" : "contains")
+ << " regular expression ";
+ UniversalPrinter<internal::string>::Print(regex_->pattern(), os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't " << (full_match_ ? "match" : "contain")
+ << " regular expression ";
+ UniversalPrinter<internal::string>::Print(regex_->pattern(), os);
+ }
+
+ private:
+ const internal::linked_ptr<const RE> regex_;
+ const bool full_match_;
+
+ GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher);
+};
+
+// Implements a matcher that compares the two fields of a 2-tuple
+// using one of the ==, <=, <, etc, operators. The two fields being
+// compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq() can be
+// used to match a tuple<int, short>, a tuple<const long&, double>,
+// etc). Therefore we use a template type conversion operator in the
+// implementation.
+template <typename D, typename Op>
+class PairMatchBase {
+ public:
+ template <typename T1, typename T2>
+ operator Matcher< ::testing::tuple<T1, T2> >() const {
+ return MakeMatcher(new Impl< ::testing::tuple<T1, T2> >);
+ }
+ template <typename T1, typename T2>
+ operator Matcher<const ::testing::tuple<T1, T2>&>() const {
+ return MakeMatcher(new Impl<const ::testing::tuple<T1, T2>&>);
+ }
+
+ private:
+ static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT
+ return os << D::Desc();
+ }
+
+ template <typename Tuple>
+ class Impl : public MatcherInterface<Tuple> {
+ public:
+ virtual bool MatchAndExplain(
+ Tuple args,
+ MatchResultListener* /* listener */) const {
+ return Op()(::testing::get<0>(args), ::testing::get<1>(args));
+ }
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "are " << GetDesc;
+ }
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "aren't " << GetDesc;
+ }
+ };
+};
+
+class Eq2Matcher : public PairMatchBase<Eq2Matcher, AnyEq> {
+ public:
+ static const char* Desc() { return "an equal pair"; }
+};
+class Ne2Matcher : public PairMatchBase<Ne2Matcher, AnyNe> {
+ public:
+ static const char* Desc() { return "an unequal pair"; }
+};
+class Lt2Matcher : public PairMatchBase<Lt2Matcher, AnyLt> {
+ public:
+ static const char* Desc() { return "a pair where the first < the second"; }
+};
+class Gt2Matcher : public PairMatchBase<Gt2Matcher, AnyGt> {
+ public:
+ static const char* Desc() { return "a pair where the first > the second"; }
+};
+class Le2Matcher : public PairMatchBase<Le2Matcher, AnyLe> {
+ public:
+ static const char* Desc() { return "a pair where the first <= the second"; }
+};
+class Ge2Matcher : public PairMatchBase<Ge2Matcher, AnyGe> {
+ public:
+ static const char* Desc() { return "a pair where the first >= the second"; }
+};
+
+// Implements the Not(...) matcher for a particular argument type T.
+// We do not nest it inside the NotMatcher class template, as that
+// will prevent different instantiations of NotMatcher from sharing
+// the same NotMatcherImpl<T> class.
+template <typename T>
+class NotMatcherImpl : public MatcherInterface<T> {
+ public:
+ explicit NotMatcherImpl(const Matcher<T>& matcher)
+ : matcher_(matcher) {}
+
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ return !matcher_.MatchAndExplain(x, listener);
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ matcher_.DescribeNegationTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ matcher_.DescribeTo(os);
+ }
+
+ private:
+ const Matcher<T> matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(NotMatcherImpl);
+};
+
+// Implements the Not(m) matcher, which matches a value that doesn't
+// match matcher m.
+template <typename InnerMatcher>
+class NotMatcher {
+ public:
+ explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {}
+
+ // This template type conversion operator allows Not(m) to be used
+ // to match any type m can match.
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new NotMatcherImpl<T>(SafeMatcherCast<T>(matcher_)));
+ }
+
+ private:
+ InnerMatcher matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(NotMatcher);
+};
+
+// Implements the AllOf(m1, m2) matcher for a particular argument type
+// T. We do not nest it inside the BothOfMatcher class template, as
+// that will prevent different instantiations of BothOfMatcher from
+// sharing the same BothOfMatcherImpl<T> class.
+template <typename T>
+class BothOfMatcherImpl : public MatcherInterface<T> {
+ public:
+ BothOfMatcherImpl(const Matcher<T>& matcher1, const Matcher<T>& matcher2)
+ : matcher1_(matcher1), matcher2_(matcher2) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "(";
+ matcher1_.DescribeTo(os);
+ *os << ") and (";
+ matcher2_.DescribeTo(os);
+ *os << ")";
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "(";
+ matcher1_.DescribeNegationTo(os);
+ *os << ") or (";
+ matcher2_.DescribeNegationTo(os);
+ *os << ")";
+ }
+
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ // If either matcher1_ or matcher2_ doesn't match x, we only need
+ // to explain why one of them fails.
+ StringMatchResultListener listener1;
+ if (!matcher1_.MatchAndExplain(x, &listener1)) {
+ *listener << listener1.str();
+ return false;
+ }
+
+ StringMatchResultListener listener2;
+ if (!matcher2_.MatchAndExplain(x, &listener2)) {
+ *listener << listener2.str();
+ return false;
+ }
+
+ // Otherwise we need to explain why *both* of them match.
+ const internal::string s1 = listener1.str();
+ const internal::string s2 = listener2.str();
+
+ if (s1 == "") {
+ *listener << s2;
+ } else {
+ *listener << s1;
+ if (s2 != "") {
+ *listener << ", and " << s2;
+ }
+ }
+ return true;
+ }
+
+ private:
+ const Matcher<T> matcher1_;
+ const Matcher<T> matcher2_;
+
+ GTEST_DISALLOW_ASSIGN_(BothOfMatcherImpl);
+};
+
+#if GTEST_LANG_CXX11
+// MatcherList provides mechanisms for storing a variable number of matchers in
+// a list structure (ListType) and creating a combining matcher from such a
+// list.
+// The template is defined recursively using the following template paramters:
+// * kSize is the length of the MatcherList.
+// * Head is the type of the first matcher of the list.
+// * Tail denotes the types of the remaining matchers of the list.
+template <int kSize, typename Head, typename... Tail>
+struct MatcherList {
+ typedef MatcherList<kSize - 1, Tail...> MatcherListTail;
+ typedef ::std::pair<Head, typename MatcherListTail::ListType> ListType;
+
+ // BuildList stores variadic type values in a nested pair structure.
+ // Example:
+ // MatcherList<3, int, string, float>::BuildList(5, "foo", 2.0) will return
+ // the corresponding result of type pair<int, pair<string, float>>.
+ static ListType BuildList(const Head& matcher, const Tail&... tail) {
+ return ListType(matcher, MatcherListTail::BuildList(tail...));
+ }
+
+ // CreateMatcher<T> creates a Matcher<T> from a given list of matchers (built
+ // by BuildList()). CombiningMatcher<T> is used to combine the matchers of the
+ // list. CombiningMatcher<T> must implement MatcherInterface<T> and have a
+ // constructor taking two Matcher<T>s as input.
+ template <typename T, template <typename /* T */> class CombiningMatcher>
+ static Matcher<T> CreateMatcher(const ListType& matchers) {
+ return Matcher<T>(new CombiningMatcher<T>(
+ SafeMatcherCast<T>(matchers.first),
+ MatcherListTail::template CreateMatcher<T, CombiningMatcher>(
+ matchers.second)));
+ }
+};
+
+// The following defines the base case for the recursive definition of
+// MatcherList.
+template <typename Matcher1, typename Matcher2>
+struct MatcherList<2, Matcher1, Matcher2> {
+ typedef ::std::pair<Matcher1, Matcher2> ListType;
+
+ static ListType BuildList(const Matcher1& matcher1,
+ const Matcher2& matcher2) {
+ return ::std::pair<Matcher1, Matcher2>(matcher1, matcher2);
+ }
+
+ template <typename T, template <typename /* T */> class CombiningMatcher>
+ static Matcher<T> CreateMatcher(const ListType& matchers) {
+ return Matcher<T>(new CombiningMatcher<T>(
+ SafeMatcherCast<T>(matchers.first),
+ SafeMatcherCast<T>(matchers.second)));
+ }
+};
+
+// VariadicMatcher is used for the variadic implementation of
+// AllOf(m_1, m_2, ...) and AnyOf(m_1, m_2, ...).
+// CombiningMatcher<T> is used to recursively combine the provided matchers
+// (of type Args...).
+template <template <typename T> class CombiningMatcher, typename... Args>
+class VariadicMatcher {
+ public:
+ VariadicMatcher(const Args&... matchers) // NOLINT
+ : matchers_(MatcherListType::BuildList(matchers...)) {}
+
+ // This template type conversion operator allows an
+ // VariadicMatcher<Matcher1, Matcher2...> object to match any type that
+ // all of the provided matchers (Matcher1, Matcher2, ...) can match.
+ template <typename T>
+ operator Matcher<T>() const {
+ return MatcherListType::template CreateMatcher<T, CombiningMatcher>(
+ matchers_);
+ }
+
+ private:
+ typedef MatcherList<sizeof...(Args), Args...> MatcherListType;
+
+ const typename MatcherListType::ListType matchers_;
+
+ GTEST_DISALLOW_ASSIGN_(VariadicMatcher);
+};
+
+template <typename... Args>
+using AllOfMatcher = VariadicMatcher<BothOfMatcherImpl, Args...>;
+
+#endif // GTEST_LANG_CXX11
+
+// Used for implementing the AllOf(m_1, ..., m_n) matcher, which
+// matches a value that matches all of the matchers m_1, ..., and m_n.
+template <typename Matcher1, typename Matcher2>
+class BothOfMatcher {
+ public:
+ BothOfMatcher(Matcher1 matcher1, Matcher2 matcher2)
+ : matcher1_(matcher1), matcher2_(matcher2) {}
+
+ // This template type conversion operator allows a
+ // BothOfMatcher<Matcher1, Matcher2> object to match any type that
+ // both Matcher1 and Matcher2 can match.
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new BothOfMatcherImpl<T>(SafeMatcherCast<T>(matcher1_),
+ SafeMatcherCast<T>(matcher2_)));
+ }
+
+ private:
+ Matcher1 matcher1_;
+ Matcher2 matcher2_;
+
+ GTEST_DISALLOW_ASSIGN_(BothOfMatcher);
+};
+
+// Implements the AnyOf(m1, m2) matcher for a particular argument type
+// T. We do not nest it inside the AnyOfMatcher class template, as
+// that will prevent different instantiations of AnyOfMatcher from
+// sharing the same EitherOfMatcherImpl<T> class.
+template <typename T>
+class EitherOfMatcherImpl : public MatcherInterface<T> {
+ public:
+ EitherOfMatcherImpl(const Matcher<T>& matcher1, const Matcher<T>& matcher2)
+ : matcher1_(matcher1), matcher2_(matcher2) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "(";
+ matcher1_.DescribeTo(os);
+ *os << ") or (";
+ matcher2_.DescribeTo(os);
+ *os << ")";
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "(";
+ matcher1_.DescribeNegationTo(os);
+ *os << ") and (";
+ matcher2_.DescribeNegationTo(os);
+ *os << ")";
+ }
+
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ // If either matcher1_ or matcher2_ matches x, we just need to
+ // explain why *one* of them matches.
+ StringMatchResultListener listener1;
+ if (matcher1_.MatchAndExplain(x, &listener1)) {
+ *listener << listener1.str();
+ return true;
+ }
+
+ StringMatchResultListener listener2;
+ if (matcher2_.MatchAndExplain(x, &listener2)) {
+ *listener << listener2.str();
+ return true;
+ }
+
+ // Otherwise we need to explain why *both* of them fail.
+ const internal::string s1 = listener1.str();
+ const internal::string s2 = listener2.str();
+
+ if (s1 == "") {
+ *listener << s2;
+ } else {
+ *listener << s1;
+ if (s2 != "") {
+ *listener << ", and " << s2;
+ }
+ }
+ return false;
+ }
+
+ private:
+ const Matcher<T> matcher1_;
+ const Matcher<T> matcher2_;
+
+ GTEST_DISALLOW_ASSIGN_(EitherOfMatcherImpl);
+};
+
+#if GTEST_LANG_CXX11
+// AnyOfMatcher is used for the variadic implementation of AnyOf(m_1, m_2, ...).
+template <typename... Args>
+using AnyOfMatcher = VariadicMatcher<EitherOfMatcherImpl, Args...>;
+
+#endif // GTEST_LANG_CXX11
+
+// Used for implementing the AnyOf(m_1, ..., m_n) matcher, which
+// matches a value that matches at least one of the matchers m_1, ...,
+// and m_n.
+template <typename Matcher1, typename Matcher2>
+class EitherOfMatcher {
+ public:
+ EitherOfMatcher(Matcher1 matcher1, Matcher2 matcher2)
+ : matcher1_(matcher1), matcher2_(matcher2) {}
+
+ // This template type conversion operator allows a
+ // EitherOfMatcher<Matcher1, Matcher2> object to match any type that
+ // both Matcher1 and Matcher2 can match.
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new EitherOfMatcherImpl<T>(
+ SafeMatcherCast<T>(matcher1_), SafeMatcherCast<T>(matcher2_)));
+ }
+
+ private:
+ Matcher1 matcher1_;
+ Matcher2 matcher2_;
+
+ GTEST_DISALLOW_ASSIGN_(EitherOfMatcher);
+};
+
+// Used for implementing Truly(pred), which turns a predicate into a
+// matcher.
+template <typename Predicate>
+class TrulyMatcher {
+ public:
+ explicit TrulyMatcher(Predicate pred) : predicate_(pred) {}
+
+ // This method template allows Truly(pred) to be used as a matcher
+ // for type T where T is the argument type of predicate 'pred'. The
+ // argument is passed by reference as the predicate may be
+ // interested in the address of the argument.
+ template <typename T>
+ bool MatchAndExplain(T& x, // NOLINT
+ MatchResultListener* /* listener */) const {
+ // Without the if-statement, MSVC sometimes warns about converting
+ // a value to bool (warning 4800).
+ //
+ // We cannot write 'return !!predicate_(x);' as that doesn't work
+ // when predicate_(x) returns a class convertible to bool but
+ // having no operator!().
+ if (predicate_(x))
+ return true;
+ return false;
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << "satisfies the given predicate";
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't satisfy the given predicate";
+ }
+
+ private:
+ Predicate predicate_;
+
+ GTEST_DISALLOW_ASSIGN_(TrulyMatcher);
+};
+
+// Used for implementing Matches(matcher), which turns a matcher into
+// a predicate.
+template <typename M>
+class MatcherAsPredicate {
+ public:
+ explicit MatcherAsPredicate(M matcher) : matcher_(matcher) {}
+
+ // This template operator() allows Matches(m) to be used as a
+ // predicate on type T where m is a matcher on type T.
+ //
+ // The argument x is passed by reference instead of by value, as
+ // some matcher may be interested in its address (e.g. as in
+ // Matches(Ref(n))(x)).
+ template <typename T>
+ bool operator()(const T& x) const {
+ // We let matcher_ commit to a particular type here instead of
+ // when the MatcherAsPredicate object was constructed. This
+ // allows us to write Matches(m) where m is a polymorphic matcher
+ // (e.g. Eq(5)).
+ //
+ // If we write Matcher<T>(matcher_).Matches(x) here, it won't
+ // compile when matcher_ has type Matcher<const T&>; if we write
+ // Matcher<const T&>(matcher_).Matches(x) here, it won't compile
+ // when matcher_ has type Matcher<T>; if we just write
+ // matcher_.Matches(x), it won't compile when matcher_ is
+ // polymorphic, e.g. Eq(5).
+ //
+ // MatcherCast<const T&>() is necessary for making the code work
+ // in all of the above situations.
+ return MatcherCast<const T&>(matcher_).Matches(x);
+ }
+
+ private:
+ M matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(MatcherAsPredicate);
+};
+
+// For implementing ASSERT_THAT() and EXPECT_THAT(). The template
+// argument M must be a type that can be converted to a matcher.
+template <typename M>
+class PredicateFormatterFromMatcher {
+ public:
+ explicit PredicateFormatterFromMatcher(M m) : matcher_(internal::move(m)) {}
+
+ // This template () operator allows a PredicateFormatterFromMatcher
+ // object to act as a predicate-formatter suitable for using with
+ // Google Test's EXPECT_PRED_FORMAT1() macro.
+ template <typename T>
+ AssertionResult operator()(const char* value_text, const T& x) const {
+ // We convert matcher_ to a Matcher<const T&> *now* instead of
+ // when the PredicateFormatterFromMatcher object was constructed,
+ // as matcher_ may be polymorphic (e.g. NotNull()) and we won't
+ // know which type to instantiate it to until we actually see the
+ // type of x here.
+ //
+ // We write SafeMatcherCast<const T&>(matcher_) instead of
+ // Matcher<const T&>(matcher_), as the latter won't compile when
+ // matcher_ has type Matcher<T> (e.g. An<int>()).
+ // We don't write MatcherCast<const T&> either, as that allows
+ // potentially unsafe downcasting of the matcher argument.
+ const Matcher<const T&> matcher = SafeMatcherCast<const T&>(matcher_);
+ StringMatchResultListener listener;
+ if (MatchPrintAndExplain(x, matcher, &listener))
+ return AssertionSuccess();
+
+ ::std::stringstream ss;
+ ss << "Value of: " << value_text << "\n"
+ << "Expected: ";
+ matcher.DescribeTo(&ss);
+ ss << "\n Actual: " << listener.str();
+ return AssertionFailure() << ss.str();
+ }
+
+ private:
+ const M matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(PredicateFormatterFromMatcher);
+};
+
+// A helper function for converting a matcher to a predicate-formatter
+// without the user needing to explicitly write the type. This is
+// used for implementing ASSERT_THAT() and EXPECT_THAT().
+// Implementation detail: 'matcher' is received by-value to force decaying.
+template <typename M>
+inline PredicateFormatterFromMatcher<M>
+MakePredicateFormatterFromMatcher(M matcher) {
+ return PredicateFormatterFromMatcher<M>(internal::move(matcher));
+}
+
+// Implements the polymorphic floating point equality matcher, which matches
+// two float values using ULP-based approximation or, optionally, a
+// user-specified epsilon. The template is meant to be instantiated with
+// FloatType being either float or double.
+template <typename FloatType>
+class FloatingEqMatcher {
+ public:
+ // Constructor for FloatingEqMatcher.
+ // The matcher's input will be compared with expected. The matcher treats two
+ // NANs as equal if nan_eq_nan is true. Otherwise, under IEEE standards,
+ // equality comparisons between NANs will always return false. We specify a
+ // negative max_abs_error_ term to indicate that ULP-based approximation will
+ // be used for comparison.
+ FloatingEqMatcher(FloatType expected, bool nan_eq_nan) :
+ expected_(expected), nan_eq_nan_(nan_eq_nan), max_abs_error_(-1) {
+ }
+
+ // Constructor that supports a user-specified max_abs_error that will be used
+ // for comparison instead of ULP-based approximation. The max absolute
+ // should be non-negative.
+ FloatingEqMatcher(FloatType expected, bool nan_eq_nan,
+ FloatType max_abs_error)
+ : expected_(expected),
+ nan_eq_nan_(nan_eq_nan),
+ max_abs_error_(max_abs_error) {
+ GTEST_CHECK_(max_abs_error >= 0)
+ << ", where max_abs_error is" << max_abs_error;
+ }
+
+ // Implements floating point equality matcher as a Matcher<T>.
+ template <typename T>
+ class Impl : public MatcherInterface<T> {
+ public:
+ Impl(FloatType expected, bool nan_eq_nan, FloatType max_abs_error)
+ : expected_(expected),
+ nan_eq_nan_(nan_eq_nan),
+ max_abs_error_(max_abs_error) {}
+
+ virtual bool MatchAndExplain(T value,
+ MatchResultListener* listener) const {
+ const FloatingPoint<FloatType> actual(value), expected(expected_);
+
+ // Compares NaNs first, if nan_eq_nan_ is true.
+ if (actual.is_nan() || expected.is_nan()) {
+ if (actual.is_nan() && expected.is_nan()) {
+ return nan_eq_nan_;
+ }
+ // One is nan; the other is not nan.
+ return false;
+ }
+ if (HasMaxAbsError()) {
+ // We perform an equality check so that inf will match inf, regardless
+ // of error bounds. If the result of value - expected_ would result in
+ // overflow or if either value is inf, the default result is infinity,
+ // which should only match if max_abs_error_ is also infinity.
+ if (value == expected_) {
+ return true;
+ }
+
+ const FloatType diff = value - expected_;
+ if (fabs(diff) <= max_abs_error_) {
+ return true;
+ }
+
+ if (listener->IsInterested()) {
+ *listener << "which is " << diff << " from " << expected_;
+ }
+ return false;
+ } else {
+ return actual.AlmostEquals(expected);
+ }
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ // os->precision() returns the previously set precision, which we
+ // store to restore the ostream to its original configuration
+ // after outputting.
+ const ::std::streamsize old_precision = os->precision(
+ ::std::numeric_limits<FloatType>::digits10 + 2);
+ if (FloatingPoint<FloatType>(expected_).is_nan()) {
+ if (nan_eq_nan_) {
+ *os << "is NaN";
+ } else {
+ *os << "never matches";
+ }
+ } else {
+ *os << "is approximately " << expected_;
+ if (HasMaxAbsError()) {
+ *os << " (absolute error <= " << max_abs_error_ << ")";
+ }
+ }
+ os->precision(old_precision);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ // As before, get original precision.
+ const ::std::streamsize old_precision = os->precision(
+ ::std::numeric_limits<FloatType>::digits10 + 2);
+ if (FloatingPoint<FloatType>(expected_).is_nan()) {
+ if (nan_eq_nan_) {
+ *os << "isn't NaN";
+ } else {
+ *os << "is anything";
+ }
+ } else {
+ *os << "isn't approximately " << expected_;
+ if (HasMaxAbsError()) {
+ *os << " (absolute error > " << max_abs_error_ << ")";
+ }
+ }
+ // Restore original precision.
+ os->precision(old_precision);
+ }
+
+ private:
+ bool HasMaxAbsError() const {
+ return max_abs_error_ >= 0;
+ }
+
+ const FloatType expected_;
+ const bool nan_eq_nan_;
+ // max_abs_error will be used for value comparison when >= 0.
+ const FloatType max_abs_error_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ // The following 3 type conversion operators allow FloatEq(expected) and
+ // NanSensitiveFloatEq(expected) to be used as a Matcher<float>, a
+ // Matcher<const float&>, or a Matcher<float&>, but nothing else.
+ // (While Google's C++ coding style doesn't allow arguments passed
+ // by non-const reference, we may see them in code not conforming to
+ // the style. Therefore Google Mock needs to support them.)
+ operator Matcher<FloatType>() const {
+ return MakeMatcher(
+ new Impl<FloatType>(expected_, nan_eq_nan_, max_abs_error_));
+ }
+
+ operator Matcher<const FloatType&>() const {
+ return MakeMatcher(
+ new Impl<const FloatType&>(expected_, nan_eq_nan_, max_abs_error_));
+ }
+
+ operator Matcher<FloatType&>() const {
+ return MakeMatcher(
+ new Impl<FloatType&>(expected_, nan_eq_nan_, max_abs_error_));
+ }
+
+ private:
+ const FloatType expected_;
+ const bool nan_eq_nan_;
+ // max_abs_error will be used for value comparison when >= 0.
+ const FloatType max_abs_error_;
+
+ GTEST_DISALLOW_ASSIGN_(FloatingEqMatcher);
+};
+
+// Implements the Pointee(m) matcher for matching a pointer whose
+// pointee matches matcher m. The pointer can be either raw or smart.
+template <typename InnerMatcher>
+class PointeeMatcher {
+ public:
+ explicit PointeeMatcher(const InnerMatcher& matcher) : matcher_(matcher) {}
+
+ // This type conversion operator template allows Pointee(m) to be
+ // used as a matcher for any pointer type whose pointee type is
+ // compatible with the inner matcher, where type Pointer can be
+ // either a raw pointer or a smart pointer.
+ //
+ // The reason we do this instead of relying on
+ // MakePolymorphicMatcher() is that the latter is not flexible
+ // enough for implementing the DescribeTo() method of Pointee().
+ template <typename Pointer>
+ operator Matcher<Pointer>() const {
+ return MakeMatcher(new Impl<Pointer>(matcher_));
+ }
+
+ private:
+ // The monomorphic implementation that works for a particular pointer type.
+ template <typename Pointer>
+ class Impl : public MatcherInterface<Pointer> {
+ public:
+ typedef typename PointeeOf<GTEST_REMOVE_CONST_( // NOLINT
+ GTEST_REMOVE_REFERENCE_(Pointer))>::type Pointee;
+
+ explicit Impl(const InnerMatcher& matcher)
+ : matcher_(MatcherCast<const Pointee&>(matcher)) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "points to a value that ";
+ matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "does not point to a value that ";
+ matcher_.DescribeTo(os);
+ }
+
+ virtual bool MatchAndExplain(Pointer pointer,
+ MatchResultListener* listener) const {
+ if (GetRawPointer(pointer) == NULL)
+ return false;
+
+ *listener << "which points to ";
+ return MatchPrintAndExplain(*pointer, matcher_, listener);
+ }
+
+ private:
+ const Matcher<const Pointee&> matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ const InnerMatcher matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(PointeeMatcher);
+};
+
+// Implements the WhenDynamicCastTo<T>(m) matcher that matches a pointer or
+// reference that matches inner_matcher when dynamic_cast<T> is applied.
+// The result of dynamic_cast<To> is forwarded to the inner matcher.
+// If To is a pointer and the cast fails, the inner matcher will receive NULL.
+// If To is a reference and the cast fails, this matcher returns false
+// immediately.
+template <typename To>
+class WhenDynamicCastToMatcherBase {
+ public:
+ explicit WhenDynamicCastToMatcherBase(const Matcher<To>& matcher)
+ : matcher_(matcher) {}
+
+ void DescribeTo(::std::ostream* os) const {
+ GetCastTypeDescription(os);
+ matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ GetCastTypeDescription(os);
+ matcher_.DescribeNegationTo(os);
+ }
+
+ protected:
+ const Matcher<To> matcher_;
+
+ static string GetToName() {
+#if GTEST_HAS_RTTI
+ return GetTypeName<To>();
+#else // GTEST_HAS_RTTI
+ return "the target type";
+#endif // GTEST_HAS_RTTI
+ }
+
+ private:
+ static void GetCastTypeDescription(::std::ostream* os) {
+ *os << "when dynamic_cast to " << GetToName() << ", ";
+ }
+
+ GTEST_DISALLOW_ASSIGN_(WhenDynamicCastToMatcherBase);
+};
+
+// Primary template.
+// To is a pointer. Cast and forward the result.
+template <typename To>
+class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase<To> {
+ public:
+ explicit WhenDynamicCastToMatcher(const Matcher<To>& matcher)
+ : WhenDynamicCastToMatcherBase<To>(matcher) {}
+
+ template <typename From>
+ bool MatchAndExplain(From from, MatchResultListener* listener) const {
+ // TODO(sbenza): Add more detail on failures. ie did the dyn_cast fail?
+ To to = dynamic_cast<To>(from);
+ return MatchPrintAndExplain(to, this->matcher_, listener);
+ }
+};
+
+// Specialize for references.
+// In this case we return false if the dynamic_cast fails.
+template <typename To>
+class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
+ public:
+ explicit WhenDynamicCastToMatcher(const Matcher<To&>& matcher)
+ : WhenDynamicCastToMatcherBase<To&>(matcher) {}
+
+ template <typename From>
+ bool MatchAndExplain(From& from, MatchResultListener* listener) const {
+ // We don't want an std::bad_cast here, so do the cast with pointers.
+ To* to = dynamic_cast<To*>(&from);
+ if (to == NULL) {
+ *listener << "which cannot be dynamic_cast to " << this->GetToName();
+ return false;
+ }
+ return MatchPrintAndExplain(*to, this->matcher_, listener);
+ }
+};
+
+// Implements the Field() matcher for matching a field (i.e. member
+// variable) of an object.
+template <typename Class, typename FieldType>
+class FieldMatcher {
+ public:
+ FieldMatcher(FieldType Class::*field,
+ const Matcher<const FieldType&>& matcher)
+ : field_(field), matcher_(matcher) {}
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << "is an object whose given field ";
+ matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "is an object whose given field ";
+ matcher_.DescribeNegationTo(os);
+ }
+
+ template <typename T>
+ bool MatchAndExplain(const T& value, MatchResultListener* listener) const {
+ return MatchAndExplainImpl(
+ typename ::testing::internal::
+ is_pointer<GTEST_REMOVE_CONST_(T)>::type(),
+ value, listener);
+ }
+
+ private:
+ // The first argument of MatchAndExplainImpl() is needed to help
+ // Symbian's C++ compiler choose which overload to use. Its type is
+ // true_type iff the Field() matcher is used to match a pointer.
+ bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj,
+ MatchResultListener* listener) const {
+ *listener << "whose given field is ";
+ return MatchPrintAndExplain(obj.*field_, matcher_, listener);
+ }
+
+ bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p,
+ MatchResultListener* listener) const {
+ if (p == NULL)
+ return false;
+
+ *listener << "which points to an object ";
+ // Since *p has a field, it must be a class/struct/union type and
+ // thus cannot be a pointer. Therefore we pass false_type() as
+ // the first argument.
+ return MatchAndExplainImpl(false_type(), *p, listener);
+ }
+
+ const FieldType Class::*field_;
+ const Matcher<const FieldType&> matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(FieldMatcher);
+};
+
+// Implements the Property() matcher for matching a property
+// (i.e. return value of a getter method) of an object.
+template <typename Class, typename PropertyType>
+class PropertyMatcher {
+ public:
+ // The property may have a reference type, so 'const PropertyType&'
+ // may cause double references and fail to compile. That's why we
+ // need GTEST_REFERENCE_TO_CONST, which works regardless of
+ // PropertyType being a reference or not.
+ typedef GTEST_REFERENCE_TO_CONST_(PropertyType) RefToConstProperty;
+
+ PropertyMatcher(PropertyType (Class::*property)() const,
+ const Matcher<RefToConstProperty>& matcher)
+ : property_(property), matcher_(matcher) {}
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << "is an object whose given property ";
+ matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "is an object whose given property ";
+ matcher_.DescribeNegationTo(os);
+ }
+
+ template <typename T>
+ bool MatchAndExplain(const T&value, MatchResultListener* listener) const {
+ return MatchAndExplainImpl(
+ typename ::testing::internal::
+ is_pointer<GTEST_REMOVE_CONST_(T)>::type(),
+ value, listener);
+ }
+
+ private:
+ // The first argument of MatchAndExplainImpl() is needed to help
+ // Symbian's C++ compiler choose which overload to use. Its type is
+ // true_type iff the Property() matcher is used to match a pointer.
+ bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj,
+ MatchResultListener* listener) const {
+ *listener << "whose given property is ";
+ // Cannot pass the return value (for example, int) to MatchPrintAndExplain,
+ // which takes a non-const reference as argument.
+#if defined(_PREFAST_ ) && _MSC_VER == 1800
+ // Workaround bug in VC++ 2013's /analyze parser.
+ // https://connect.microsoft.com/VisualStudio/feedback/details/1106363/internal-compiler-error-with-analyze-due-to-failure-to-infer-move
+ posix::Abort(); // To make sure it is never run.
+ return false;
+#else
+ RefToConstProperty result = (obj.*property_)();
+ return MatchPrintAndExplain(result, matcher_, listener);
+#endif
+ }
+
+ bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p,
+ MatchResultListener* listener) const {
+ if (p == NULL)
+ return false;
+
+ *listener << "which points to an object ";
+ // Since *p has a property method, it must be a class/struct/union
+ // type and thus cannot be a pointer. Therefore we pass
+ // false_type() as the first argument.
+ return MatchAndExplainImpl(false_type(), *p, listener);
+ }
+
+ PropertyType (Class::*property_)() const;
+ const Matcher<RefToConstProperty> matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(PropertyMatcher);
+};
+
+// Type traits specifying various features of different functors for ResultOf.
+// The default template specifies features for functor objects.
+// Functor classes have to typedef argument_type and result_type
+// to be compatible with ResultOf.
+template <typename Functor>
+struct CallableTraits {
+ typedef typename Functor::result_type ResultType;
+ typedef Functor StorageType;
+
+ static void CheckIsValid(Functor /* functor */) {}
+ template <typename T>
+ static ResultType Invoke(Functor f, T arg) { return f(arg); }
+};
+
+// Specialization for function pointers.
+template <typename ArgType, typename ResType>
+struct CallableTraits<ResType(*)(ArgType)> {
+ typedef ResType ResultType;
+ typedef ResType(*StorageType)(ArgType);
+
+ static void CheckIsValid(ResType(*f)(ArgType)) {
+ GTEST_CHECK_(f != NULL)
+ << "NULL function pointer is passed into ResultOf().";
+ }
+ template <typename T>
+ static ResType Invoke(ResType(*f)(ArgType), T arg) {
+ return (*f)(arg);
+ }
+};
+
+// Implements the ResultOf() matcher for matching a return value of a
+// unary function of an object.
+template <typename Callable>
+class ResultOfMatcher {
+ public:
+ typedef typename CallableTraits<Callable>::ResultType ResultType;
+
+ ResultOfMatcher(Callable callable, const Matcher<ResultType>& matcher)
+ : callable_(callable), matcher_(matcher) {
+ CallableTraits<Callable>::CheckIsValid(callable_);
+ }
+
+ template <typename T>
+ operator Matcher<T>() const {
+ return Matcher<T>(new Impl<T>(callable_, matcher_));
+ }
+
+ private:
+ typedef typename CallableTraits<Callable>::StorageType CallableStorageType;
+
+ template <typename T>
+ class Impl : public MatcherInterface<T> {
+ public:
+ Impl(CallableStorageType callable, const Matcher<ResultType>& matcher)
+ : callable_(callable), matcher_(matcher) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "is mapped by the given callable to a value that ";
+ matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "is mapped by the given callable to a value that ";
+ matcher_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(T obj, MatchResultListener* listener) const {
+ *listener << "which is mapped by the given callable to ";
+ // Cannot pass the return value (for example, int) to
+ // MatchPrintAndExplain, which takes a non-const reference as argument.
+ ResultType result =
+ CallableTraits<Callable>::template Invoke<T>(callable_, obj);
+ return MatchPrintAndExplain(result, matcher_, listener);
+ }
+
+ private:
+ // Functors often define operator() as non-const method even though
+ // they are actualy stateless. But we need to use them even when
+ // 'this' is a const pointer. It's the user's responsibility not to
+ // use stateful callables with ResultOf(), which does't guarantee
+ // how many times the callable will be invoked.
+ mutable CallableStorageType callable_;
+ const Matcher<ResultType> matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ }; // class Impl
+
+ const CallableStorageType callable_;
+ const Matcher<ResultType> matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(ResultOfMatcher);
+};
+
+// Implements a matcher that checks the size of an STL-style container.
+template <typename SizeMatcher>
+class SizeIsMatcher {
+ public:
+ explicit SizeIsMatcher(const SizeMatcher& size_matcher)
+ : size_matcher_(size_matcher) {
+ }
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ return MakeMatcher(new Impl<Container>(size_matcher_));
+ }
+
+ template <typename Container>
+ class Impl : public MatcherInterface<Container> {
+ public:
+ typedef internal::StlContainerView<
+ GTEST_REMOVE_REFERENCE_AND_CONST_(Container)> ContainerView;
+ typedef typename ContainerView::type::size_type SizeType;
+ explicit Impl(const SizeMatcher& size_matcher)
+ : size_matcher_(MatcherCast<SizeType>(size_matcher)) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "size ";
+ size_matcher_.DescribeTo(os);
+ }
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "size ";
+ size_matcher_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const {
+ SizeType size = container.size();
+ StringMatchResultListener size_listener;
+ const bool result = size_matcher_.MatchAndExplain(size, &size_listener);
+ *listener
+ << "whose size " << size << (result ? " matches" : " doesn't match");
+ PrintIfNotEmpty(size_listener.str(), listener->stream());
+ return result;
+ }
+
+ private:
+ const Matcher<SizeType> size_matcher_;
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ private:
+ const SizeMatcher size_matcher_;
+ GTEST_DISALLOW_ASSIGN_(SizeIsMatcher);
+};
+
+// Implements a matcher that checks the begin()..end() distance of an STL-style
+// container.
+template <typename DistanceMatcher>
+class BeginEndDistanceIsMatcher {
+ public:
+ explicit BeginEndDistanceIsMatcher(const DistanceMatcher& distance_matcher)
+ : distance_matcher_(distance_matcher) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ return MakeMatcher(new Impl<Container>(distance_matcher_));
+ }
+
+ template <typename Container>
+ class Impl : public MatcherInterface<Container> {
+ public:
+ typedef internal::StlContainerView<
+ GTEST_REMOVE_REFERENCE_AND_CONST_(Container)> ContainerView;
+ typedef typename std::iterator_traits<
+ typename ContainerView::type::const_iterator>::difference_type
+ DistanceType;
+ explicit Impl(const DistanceMatcher& distance_matcher)
+ : distance_matcher_(MatcherCast<DistanceType>(distance_matcher)) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "distance between begin() and end() ";
+ distance_matcher_.DescribeTo(os);
+ }
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "distance between begin() and end() ";
+ distance_matcher_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const {
+#if GTEST_HAS_STD_BEGIN_AND_END_
+ using std::begin;
+ using std::end;
+ DistanceType distance = std::distance(begin(container), end(container));
+#else
+ DistanceType distance = std::distance(container.begin(), container.end());
+#endif
+ StringMatchResultListener distance_listener;
+ const bool result =
+ distance_matcher_.MatchAndExplain(distance, &distance_listener);
+ *listener << "whose distance between begin() and end() " << distance
+ << (result ? " matches" : " doesn't match");
+ PrintIfNotEmpty(distance_listener.str(), listener->stream());
+ return result;
+ }
+
+ private:
+ const Matcher<DistanceType> distance_matcher_;
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ private:
+ const DistanceMatcher distance_matcher_;
+ GTEST_DISALLOW_ASSIGN_(BeginEndDistanceIsMatcher);
+};
+
+// Implements an equality matcher for any STL-style container whose elements
+// support ==. This matcher is like Eq(), but its failure explanations provide
+// more detailed information that is useful when the container is used as a set.
+// The failure message reports elements that are in one of the operands but not
+// the other. The failure messages do not report duplicate or out-of-order
+// elements in the containers (which don't properly matter to sets, but can
+// occur if the containers are vectors or lists, for example).
+//
+// Uses the container's const_iterator, value_type, operator ==,
+// begin(), and end().
+template <typename Container>
+class ContainerEqMatcher {
+ public:
+ typedef internal::StlContainerView<Container> View;
+ typedef typename View::type StlContainer;
+ typedef typename View::const_reference StlContainerReference;
+
+ // We make a copy of expected in case the elements in it are modified
+ // after this matcher is created.
+ explicit ContainerEqMatcher(const Container& expected)
+ : expected_(View::Copy(expected)) {
+ // Makes sure the user doesn't instantiate this class template
+ // with a const or reference type.
+ (void)testing::StaticAssertTypeEq<Container,
+ GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>();
+ }
+
+ void DescribeTo(::std::ostream* os) const {
+ *os << "equals ";
+ UniversalPrint(expected_, os);
+ }
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "does not equal ";
+ UniversalPrint(expected_, os);
+ }
+
+ template <typename LhsContainer>
+ bool MatchAndExplain(const LhsContainer& lhs,
+ MatchResultListener* listener) const {
+ // GTEST_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug
+ // that causes LhsContainer to be a const type sometimes.
+ typedef internal::StlContainerView<GTEST_REMOVE_CONST_(LhsContainer)>
+ LhsView;
+ typedef typename LhsView::type LhsStlContainer;
+ StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+ if (lhs_stl_container == expected_)
+ return true;
+
+ ::std::ostream* const os = listener->stream();
+ if (os != NULL) {
+ // Something is different. Check for extra values first.
+ bool printed_header = false;
+ for (typename LhsStlContainer::const_iterator it =
+ lhs_stl_container.begin();
+ it != lhs_stl_container.end(); ++it) {
+ if (internal::ArrayAwareFind(expected_.begin(), expected_.end(), *it) ==
+ expected_.end()) {
+ if (printed_header) {
+ *os << ", ";
+ } else {
+ *os << "which has these unexpected elements: ";
+ printed_header = true;
+ }
+ UniversalPrint(*it, os);
+ }
+ }
+
+ // Now check for missing values.
+ bool printed_header2 = false;
+ for (typename StlContainer::const_iterator it = expected_.begin();
+ it != expected_.end(); ++it) {
+ if (internal::ArrayAwareFind(
+ lhs_stl_container.begin(), lhs_stl_container.end(), *it) ==
+ lhs_stl_container.end()) {
+ if (printed_header2) {
+ *os << ", ";
+ } else {
+ *os << (printed_header ? ",\nand" : "which")
+ << " doesn't have these expected elements: ";
+ printed_header2 = true;
+ }
+ UniversalPrint(*it, os);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private:
+ const StlContainer expected_;
+
+ GTEST_DISALLOW_ASSIGN_(ContainerEqMatcher);
+};
+
+// A comparator functor that uses the < operator to compare two values.
+struct LessComparator {
+ template <typename T, typename U>
+ bool operator()(const T& lhs, const U& rhs) const { return lhs < rhs; }
+};
+
+// Implements WhenSortedBy(comparator, container_matcher).
+template <typename Comparator, typename ContainerMatcher>
+class WhenSortedByMatcher {
+ public:
+ WhenSortedByMatcher(const Comparator& comparator,
+ const ContainerMatcher& matcher)
+ : comparator_(comparator), matcher_(matcher) {}
+
+ template <typename LhsContainer>
+ operator Matcher<LhsContainer>() const {
+ return MakeMatcher(new Impl<LhsContainer>(comparator_, matcher_));
+ }
+
+ template <typename LhsContainer>
+ class Impl : public MatcherInterface<LhsContainer> {
+ public:
+ typedef internal::StlContainerView<
+ GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView;
+ typedef typename LhsView::type LhsStlContainer;
+ typedef typename LhsView::const_reference LhsStlContainerReference;
+ // Transforms std::pair<const Key, Value> into std::pair<Key, Value>
+ // so that we can match associative containers.
+ typedef typename RemoveConstFromKey<
+ typename LhsStlContainer::value_type>::type LhsValue;
+
+ Impl(const Comparator& comparator, const ContainerMatcher& matcher)
+ : comparator_(comparator), matcher_(matcher) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "(when sorted) ";
+ matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "(when sorted) ";
+ matcher_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(LhsContainer lhs,
+ MatchResultListener* listener) const {
+ LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+ ::std::vector<LhsValue> sorted_container(lhs_stl_container.begin(),
+ lhs_stl_container.end());
+ ::std::sort(
+ sorted_container.begin(), sorted_container.end(), comparator_);
+
+ if (!listener->IsInterested()) {
+ // If the listener is not interested, we do not need to
+ // construct the inner explanation.
+ return matcher_.Matches(sorted_container);
+ }
+
+ *listener << "which is ";
+ UniversalPrint(sorted_container, listener->stream());
+ *listener << " when sorted";
+
+ StringMatchResultListener inner_listener;
+ const bool match = matcher_.MatchAndExplain(sorted_container,
+ &inner_listener);
+ PrintIfNotEmpty(inner_listener.str(), listener->stream());
+ return match;
+ }
+
+ private:
+ const Comparator comparator_;
+ const Matcher<const ::std::vector<LhsValue>&> matcher_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl);
+ };
+
+ private:
+ const Comparator comparator_;
+ const ContainerMatcher matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(WhenSortedByMatcher);
+};
+
+// Implements Pointwise(tuple_matcher, rhs_container). tuple_matcher
+// must be able to be safely cast to Matcher<tuple<const T1&, const
+// T2&> >, where T1 and T2 are the types of elements in the LHS
+// container and the RHS container respectively.
+template <typename TupleMatcher, typename RhsContainer>
+class PointwiseMatcher {
+ public:
+ typedef internal::StlContainerView<RhsContainer> RhsView;
+ typedef typename RhsView::type RhsStlContainer;
+ typedef typename RhsStlContainer::value_type RhsValue;
+
+ // Like ContainerEq, we make a copy of rhs in case the elements in
+ // it are modified after this matcher is created.
+ PointwiseMatcher(const TupleMatcher& tuple_matcher, const RhsContainer& rhs)
+ : tuple_matcher_(tuple_matcher), rhs_(RhsView::Copy(rhs)) {
+ // Makes sure the user doesn't instantiate this class template
+ // with a const or reference type.
+ (void)testing::StaticAssertTypeEq<RhsContainer,
+ GTEST_REMOVE_REFERENCE_AND_CONST_(RhsContainer)>();
+ }
+
+ template <typename LhsContainer>
+ operator Matcher<LhsContainer>() const {
+ return MakeMatcher(new Impl<LhsContainer>(tuple_matcher_, rhs_));
+ }
+
+ template <typename LhsContainer>
+ class Impl : public MatcherInterface<LhsContainer> {
+ public:
+ typedef internal::StlContainerView<
+ GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView;
+ typedef typename LhsView::type LhsStlContainer;
+ typedef typename LhsView::const_reference LhsStlContainerReference;
+ typedef typename LhsStlContainer::value_type LhsValue;
+ // We pass the LHS value and the RHS value to the inner matcher by
+ // reference, as they may be expensive to copy. We must use tuple
+ // instead of pair here, as a pair cannot hold references (C++ 98,
+ // 20.2.2 [lib.pairs]).
+ typedef ::testing::tuple<const LhsValue&, const RhsValue&> InnerMatcherArg;
+
+ Impl(const TupleMatcher& tuple_matcher, const RhsStlContainer& rhs)
+ // mono_tuple_matcher_ holds a monomorphic version of the tuple matcher.
+ : mono_tuple_matcher_(SafeMatcherCast<InnerMatcherArg>(tuple_matcher)),
+ rhs_(rhs) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "contains " << rhs_.size()
+ << " values, where each value and its corresponding value in ";
+ UniversalPrinter<RhsStlContainer>::Print(rhs_, os);
+ *os << " ";
+ mono_tuple_matcher_.DescribeTo(os);
+ }
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't contain exactly " << rhs_.size()
+ << " values, or contains a value x at some index i"
+ << " where x and the i-th value of ";
+ UniversalPrint(rhs_, os);
+ *os << " ";
+ mono_tuple_matcher_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(LhsContainer lhs,
+ MatchResultListener* listener) const {
+ LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+ const size_t actual_size = lhs_stl_container.size();
+ if (actual_size != rhs_.size()) {
+ *listener << "which contains " << actual_size << " values";
+ return false;
+ }
+
+ typename LhsStlContainer::const_iterator left = lhs_stl_container.begin();
+ typename RhsStlContainer::const_iterator right = rhs_.begin();
+ for (size_t i = 0; i != actual_size; ++i, ++left, ++right) {
+ const InnerMatcherArg value_pair(*left, *right);
+
+ if (listener->IsInterested()) {
+ StringMatchResultListener inner_listener;
+ if (!mono_tuple_matcher_.MatchAndExplain(
+ value_pair, &inner_listener)) {
+ *listener << "where the value pair (";
+ UniversalPrint(*left, listener->stream());
+ *listener << ", ";
+ UniversalPrint(*right, listener->stream());
+ *listener << ") at index #" << i << " don't match";
+ PrintIfNotEmpty(inner_listener.str(), listener->stream());
+ return false;
+ }
+ } else {
+ if (!mono_tuple_matcher_.Matches(value_pair))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ const Matcher<InnerMatcherArg> mono_tuple_matcher_;
+ const RhsStlContainer rhs_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ private:
+ const TupleMatcher tuple_matcher_;
+ const RhsStlContainer rhs_;
+
+ GTEST_DISALLOW_ASSIGN_(PointwiseMatcher);
+};
+
+// Holds the logic common to ContainsMatcherImpl and EachMatcherImpl.
+template <typename Container>
+class QuantifierMatcherImpl : public MatcherInterface<Container> {
+ public:
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+ typedef StlContainerView<RawContainer> View;
+ typedef typename View::type StlContainer;
+ typedef typename View::const_reference StlContainerReference;
+ typedef typename StlContainer::value_type Element;
+
+ template <typename InnerMatcher>
+ explicit QuantifierMatcherImpl(InnerMatcher inner_matcher)
+ : inner_matcher_(
+ testing::SafeMatcherCast<const Element&>(inner_matcher)) {}
+
+ // Checks whether:
+ // * All elements in the container match, if all_elements_should_match.
+ // * Any element in the container matches, if !all_elements_should_match.
+ bool MatchAndExplainImpl(bool all_elements_should_match,
+ Container container,
+ MatchResultListener* listener) const {
+ StlContainerReference stl_container = View::ConstReference(container);
+ size_t i = 0;
+ for (typename StlContainer::const_iterator it = stl_container.begin();
+ it != stl_container.end(); ++it, ++i) {
+ StringMatchResultListener inner_listener;
+ const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener);
+
+ if (matches != all_elements_should_match) {
+ *listener << "whose element #" << i
+ << (matches ? " matches" : " doesn't match");
+ PrintIfNotEmpty(inner_listener.str(), listener->stream());
+ return !all_elements_should_match;
+ }
+ }
+ return all_elements_should_match;
+ }
+
+ protected:
+ const Matcher<const Element&> inner_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(QuantifierMatcherImpl);
+};
+
+// Implements Contains(element_matcher) for the given argument type Container.
+// Symmetric to EachMatcherImpl.
+template <typename Container>
+class ContainsMatcherImpl : public QuantifierMatcherImpl<Container> {
+ public:
+ template <typename InnerMatcher>
+ explicit ContainsMatcherImpl(InnerMatcher inner_matcher)
+ : QuantifierMatcherImpl<Container>(inner_matcher) {}
+
+ // Describes what this matcher does.
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "contains at least one element that ";
+ this->inner_matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't contain any element that ";
+ this->inner_matcher_.DescribeTo(os);
+ }
+
+ virtual bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const {
+ return this->MatchAndExplainImpl(false, container, listener);
+ }
+
+ private:
+ GTEST_DISALLOW_ASSIGN_(ContainsMatcherImpl);
+};
+
+// Implements Each(element_matcher) for the given argument type Container.
+// Symmetric to ContainsMatcherImpl.
+template <typename Container>
+class EachMatcherImpl : public QuantifierMatcherImpl<Container> {
+ public:
+ template <typename InnerMatcher>
+ explicit EachMatcherImpl(InnerMatcher inner_matcher)
+ : QuantifierMatcherImpl<Container>(inner_matcher) {}
+
+ // Describes what this matcher does.
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "only contains elements that ";
+ this->inner_matcher_.DescribeTo(os);
+ }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "contains some element that ";
+ this->inner_matcher_.DescribeNegationTo(os);
+ }
+
+ virtual bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const {
+ return this->MatchAndExplainImpl(true, container, listener);
+ }
+
+ private:
+ GTEST_DISALLOW_ASSIGN_(EachMatcherImpl);
+};
+
+// Implements polymorphic Contains(element_matcher).
+template <typename M>
+class ContainsMatcher {
+ public:
+ explicit ContainsMatcher(M m) : inner_matcher_(m) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ return MakeMatcher(new ContainsMatcherImpl<Container>(inner_matcher_));
+ }
+
+ private:
+ const M inner_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(ContainsMatcher);
+};
+
+// Implements polymorphic Each(element_matcher).
+template <typename M>
+class EachMatcher {
+ public:
+ explicit EachMatcher(M m) : inner_matcher_(m) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ return MakeMatcher(new EachMatcherImpl<Container>(inner_matcher_));
+ }
+
+ private:
+ const M inner_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(EachMatcher);
+};
+
+// Implements Key(inner_matcher) for the given argument pair type.
+// Key(inner_matcher) matches an std::pair whose 'first' field matches
+// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an
+// std::map that contains at least one element whose key is >= 5.
+template <typename PairType>
+class KeyMatcherImpl : public MatcherInterface<PairType> {
+ public:
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType;
+ typedef typename RawPairType::first_type KeyType;
+
+ template <typename InnerMatcher>
+ explicit KeyMatcherImpl(InnerMatcher inner_matcher)
+ : inner_matcher_(
+ testing::SafeMatcherCast<const KeyType&>(inner_matcher)) {
+ }
+
+ // Returns true iff 'key_value.first' (the key) matches the inner matcher.
+ virtual bool MatchAndExplain(PairType key_value,
+ MatchResultListener* listener) const {
+ StringMatchResultListener inner_listener;
+ const bool match = inner_matcher_.MatchAndExplain(key_value.first,
+ &inner_listener);
+ const internal::string explanation = inner_listener.str();
+ if (explanation != "") {
+ *listener << "whose first field is a value " << explanation;
+ }
+ return match;
+ }
+
+ // Describes what this matcher does.
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "has a key that ";
+ inner_matcher_.DescribeTo(os);
+ }
+
+ // Describes what the negation of this matcher does.
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "doesn't have a key that ";
+ inner_matcher_.DescribeTo(os);
+ }
+
+ private:
+ const Matcher<const KeyType&> inner_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(KeyMatcherImpl);
+};
+
+// Implements polymorphic Key(matcher_for_key).
+template <typename M>
+class KeyMatcher {
+ public:
+ explicit KeyMatcher(M m) : matcher_for_key_(m) {}
+
+ template <typename PairType>
+ operator Matcher<PairType>() const {
+ return MakeMatcher(new KeyMatcherImpl<PairType>(matcher_for_key_));
+ }
+
+ private:
+ const M matcher_for_key_;
+
+ GTEST_DISALLOW_ASSIGN_(KeyMatcher);
+};
+
+// Implements Pair(first_matcher, second_matcher) for the given argument pair
+// type with its two matchers. See Pair() function below.
+template <typename PairType>
+class PairMatcherImpl : public MatcherInterface<PairType> {
+ public:
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType;
+ typedef typename RawPairType::first_type FirstType;
+ typedef typename RawPairType::second_type SecondType;
+
+ template <typename FirstMatcher, typename SecondMatcher>
+ PairMatcherImpl(FirstMatcher first_matcher, SecondMatcher second_matcher)
+ : first_matcher_(
+ testing::SafeMatcherCast<const FirstType&>(first_matcher)),
+ second_matcher_(
+ testing::SafeMatcherCast<const SecondType&>(second_matcher)) {
+ }
+
+ // Describes what this matcher does.
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "has a first field that ";
+ first_matcher_.DescribeTo(os);
+ *os << ", and has a second field that ";
+ second_matcher_.DescribeTo(os);
+ }
+
+ // Describes what the negation of this matcher does.
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ *os << "has a first field that ";
+ first_matcher_.DescribeNegationTo(os);
+ *os << ", or has a second field that ";
+ second_matcher_.DescribeNegationTo(os);
+ }
+
+ // Returns true iff 'a_pair.first' matches first_matcher and 'a_pair.second'
+ // matches second_matcher.
+ virtual bool MatchAndExplain(PairType a_pair,
+ MatchResultListener* listener) const {
+ if (!listener->IsInterested()) {
+ // If the listener is not interested, we don't need to construct the
+ // explanation.
+ return first_matcher_.Matches(a_pair.first) &&
+ second_matcher_.Matches(a_pair.second);
+ }
+ StringMatchResultListener first_inner_listener;
+ if (!first_matcher_.MatchAndExplain(a_pair.first,
+ &first_inner_listener)) {
+ *listener << "whose first field does not match";
+ PrintIfNotEmpty(first_inner_listener.str(), listener->stream());
+ return false;
+ }
+ StringMatchResultListener second_inner_listener;
+ if (!second_matcher_.MatchAndExplain(a_pair.second,
+ &second_inner_listener)) {
+ *listener << "whose second field does not match";
+ PrintIfNotEmpty(second_inner_listener.str(), listener->stream());
+ return false;
+ }
+ ExplainSuccess(first_inner_listener.str(), second_inner_listener.str(),
+ listener);
+ return true;
+ }
+
+ private:
+ void ExplainSuccess(const internal::string& first_explanation,
+ const internal::string& second_explanation,
+ MatchResultListener* listener) const {
+ *listener << "whose both fields match";
+ if (first_explanation != "") {
+ *listener << ", where the first field is a value " << first_explanation;
+ }
+ if (second_explanation != "") {
+ *listener << ", ";
+ if (first_explanation != "") {
+ *listener << "and ";
+ } else {
+ *listener << "where ";
+ }
+ *listener << "the second field is a value " << second_explanation;
+ }
+ }
+
+ const Matcher<const FirstType&> first_matcher_;
+ const Matcher<const SecondType&> second_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(PairMatcherImpl);
+};
+
+// Implements polymorphic Pair(first_matcher, second_matcher).
+template <typename FirstMatcher, typename SecondMatcher>
+class PairMatcher {
+ public:
+ PairMatcher(FirstMatcher first_matcher, SecondMatcher second_matcher)
+ : first_matcher_(first_matcher), second_matcher_(second_matcher) {}
+
+ template <typename PairType>
+ operator Matcher<PairType> () const {
+ return MakeMatcher(
+ new PairMatcherImpl<PairType>(
+ first_matcher_, second_matcher_));
+ }
+
+ private:
+ const FirstMatcher first_matcher_;
+ const SecondMatcher second_matcher_;
+
+ GTEST_DISALLOW_ASSIGN_(PairMatcher);
+};
+
+// Implements ElementsAre() and ElementsAreArray().
+template <typename Container>
+class ElementsAreMatcherImpl : public MatcherInterface<Container> {
+ public:
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+ typedef internal::StlContainerView<RawContainer> View;
+ typedef typename View::type StlContainer;
+ typedef typename View::const_reference StlContainerReference;
+ typedef typename StlContainer::value_type Element;
+
+ // Constructs the matcher from a sequence of element values or
+ // element matchers.
+ template <typename InputIter>
+ ElementsAreMatcherImpl(InputIter first, InputIter last) {
+ while (first != last) {
+ matchers_.push_back(MatcherCast<const Element&>(*first++));
+ }
+ }
+
+ // Describes what this matcher does.
+ virtual void DescribeTo(::std::ostream* os) const {
+ if (count() == 0) {
+ *os << "is empty";
+ } else if (count() == 1) {
+ *os << "has 1 element that ";
+ matchers_[0].DescribeTo(os);
+ } else {
+ *os << "has " << Elements(count()) << " where\n";
+ for (size_t i = 0; i != count(); ++i) {
+ *os << "element #" << i << " ";
+ matchers_[i].DescribeTo(os);
+ if (i + 1 < count()) {
+ *os << ",\n";
+ }
+ }
+ }
+ }
+
+ // Describes what the negation of this matcher does.
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ if (count() == 0) {
+ *os << "isn't empty";
+ return;
+ }
+
+ *os << "doesn't have " << Elements(count()) << ", or\n";
+ for (size_t i = 0; i != count(); ++i) {
+ *os << "element #" << i << " ";
+ matchers_[i].DescribeNegationTo(os);
+ if (i + 1 < count()) {
+ *os << ", or\n";
+ }
+ }
+ }
+
+ virtual bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const {
+ // To work with stream-like "containers", we must only walk
+ // through the elements in one pass.
+
+ const bool listener_interested = listener->IsInterested();
+
+ // explanations[i] is the explanation of the element at index i.
+ ::std::vector<internal::string> explanations(count());
+ StlContainerReference stl_container = View::ConstReference(container);
+ typename StlContainer::const_iterator it = stl_container.begin();
+ size_t exam_pos = 0;
+ bool mismatch_found = false; // Have we found a mismatched element yet?
+
+ // Go through the elements and matchers in pairs, until we reach
+ // the end of either the elements or the matchers, or until we find a
+ // mismatch.
+ for (; it != stl_container.end() && exam_pos != count(); ++it, ++exam_pos) {
+ bool match; // Does the current element match the current matcher?
+ if (listener_interested) {
+ StringMatchResultListener s;
+ match = matchers_[exam_pos].MatchAndExplain(*it, &s);
+ explanations[exam_pos] = s.str();
+ } else {
+ match = matchers_[exam_pos].Matches(*it);
+ }
+
+ if (!match) {
+ mismatch_found = true;
+ break;
+ }
+ }
+ // If mismatch_found is true, 'exam_pos' is the index of the mismatch.
+
+ // Find how many elements the actual container has. We avoid
+ // calling size() s.t. this code works for stream-like "containers"
+ // that don't define size().
+ size_t actual_count = exam_pos;
+ for (; it != stl_container.end(); ++it) {
+ ++actual_count;
+ }
+
+ if (actual_count != count()) {
+ // The element count doesn't match. If the container is empty,
+ // there's no need to explain anything as Google Mock already
+ // prints the empty container. Otherwise we just need to show
+ // how many elements there actually are.
+ if (listener_interested && (actual_count != 0)) {
+ *listener << "which has " << Elements(actual_count);
+ }
+ return false;
+ }
+
+ if (mismatch_found) {
+ // The element count matches, but the exam_pos-th element doesn't match.
+ if (listener_interested) {
+ *listener << "whose element #" << exam_pos << " doesn't match";
+ PrintIfNotEmpty(explanations[exam_pos], listener->stream());
+ }
+ return false;
+ }
+
+ // Every element matches its expectation. We need to explain why
+ // (the obvious ones can be skipped).
+ if (listener_interested) {
+ bool reason_printed = false;
+ for (size_t i = 0; i != count(); ++i) {
+ const internal::string& s = explanations[i];
+ if (!s.empty()) {
+ if (reason_printed) {
+ *listener << ",\nand ";
+ }
+ *listener << "whose element #" << i << " matches, " << s;
+ reason_printed = true;
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+ static Message Elements(size_t count) {
+ return Message() << count << (count == 1 ? " element" : " elements");
+ }
+
+ size_t count() const { return matchers_.size(); }
+
+ ::std::vector<Matcher<const Element&> > matchers_;
+
+ GTEST_DISALLOW_ASSIGN_(ElementsAreMatcherImpl);
+};
+
+// Connectivity matrix of (elements X matchers), in element-major order.
+// Initially, there are no edges.
+// Use NextGraph() to iterate over all possible edge configurations.
+// Use Randomize() to generate a random edge configuration.
+class GTEST_API_ MatchMatrix {
+ public:
+ MatchMatrix(size_t num_elements, size_t num_matchers)
+ : num_elements_(num_elements),
+ num_matchers_(num_matchers),
+ matched_(num_elements_* num_matchers_, 0) {
+ }
+
+ size_t LhsSize() const { return num_elements_; }
+ size_t RhsSize() const { return num_matchers_; }
+ bool HasEdge(size_t ilhs, size_t irhs) const {
+ return matched_[SpaceIndex(ilhs, irhs)] == 1;
+ }
+ void SetEdge(size_t ilhs, size_t irhs, bool b) {
+ matched_[SpaceIndex(ilhs, irhs)] = b ? 1 : 0;
+ }
+
+ // Treating the connectivity matrix as a (LhsSize()*RhsSize())-bit number,
+ // adds 1 to that number; returns false if incrementing the graph left it
+ // empty.
+ bool NextGraph();
+
+ void Randomize();
+
+ string DebugString() const;
+
+ private:
+ size_t SpaceIndex(size_t ilhs, size_t irhs) const {
+ return ilhs * num_matchers_ + irhs;
+ }
+
+ size_t num_elements_;
+ size_t num_matchers_;
+
+ // Each element is a char interpreted as bool. They are stored as a
+ // flattened array in lhs-major order, use 'SpaceIndex()' to translate
+ // a (ilhs, irhs) matrix coordinate into an offset.
+ ::std::vector<char> matched_;
+};
+
+typedef ::std::pair<size_t, size_t> ElementMatcherPair;
+typedef ::std::vector<ElementMatcherPair> ElementMatcherPairs;
+
+// Returns a maximum bipartite matching for the specified graph 'g'.
+// The matching is represented as a vector of {element, matcher} pairs.
+GTEST_API_ ElementMatcherPairs
+FindMaxBipartiteMatching(const MatchMatrix& g);
+
+GTEST_API_ bool FindPairing(const MatchMatrix& matrix,
+ MatchResultListener* listener);
+
+// Untyped base class for implementing UnorderedElementsAre. By
+// putting logic that's not specific to the element type here, we
+// reduce binary bloat and increase compilation speed.
+class GTEST_API_ UnorderedElementsAreMatcherImplBase {
+ protected:
+ // A vector of matcher describers, one for each element matcher.
+ // Does not own the describers (and thus can be used only when the
+ // element matchers are alive).
+ typedef ::std::vector<const MatcherDescriberInterface*> MatcherDescriberVec;
+
+ // Describes this UnorderedElementsAre matcher.
+ void DescribeToImpl(::std::ostream* os) const;
+
+ // Describes the negation of this UnorderedElementsAre matcher.
+ void DescribeNegationToImpl(::std::ostream* os) const;
+
+ bool VerifyAllElementsAndMatchersAreMatched(
+ const ::std::vector<string>& element_printouts,
+ const MatchMatrix& matrix,
+ MatchResultListener* listener) const;
+
+ MatcherDescriberVec& matcher_describers() {
+ return matcher_describers_;
+ }
+
+ static Message Elements(size_t n) {
+ return Message() << n << " element" << (n == 1 ? "" : "s");
+ }
+
+ private:
+ MatcherDescriberVec matcher_describers_;
+
+ GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImplBase);
+};
+
+// Implements unordered ElementsAre and unordered ElementsAreArray.
+template <typename Container>
+class UnorderedElementsAreMatcherImpl
+ : public MatcherInterface<Container>,
+ public UnorderedElementsAreMatcherImplBase {
+ public:
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+ typedef internal::StlContainerView<RawContainer> View;
+ typedef typename View::type StlContainer;
+ typedef typename View::const_reference StlContainerReference;
+ typedef typename StlContainer::const_iterator StlContainerConstIterator;
+ typedef typename StlContainer::value_type Element;
+
+ // Constructs the matcher from a sequence of element values or
+ // element matchers.
+ template <typename InputIter>
+ UnorderedElementsAreMatcherImpl(InputIter first, InputIter last) {
+ for (; first != last; ++first) {
+ matchers_.push_back(MatcherCast<const Element&>(*first));
+ matcher_describers().push_back(matchers_.back().GetDescriber());
+ }
+ }
+
+ // Describes what this matcher does.
+ virtual void DescribeTo(::std::ostream* os) const {
+ return UnorderedElementsAreMatcherImplBase::DescribeToImpl(os);
+ }
+
+ // Describes what the negation of this matcher does.
+ virtual void DescribeNegationTo(::std::ostream* os) const {
+ return UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(os);
+ }
+
+ virtual bool MatchAndExplain(Container container,
+ MatchResultListener* listener) const {
+ StlContainerReference stl_container = View::ConstReference(container);
+ ::std::vector<string> element_printouts;
+ MatchMatrix matrix = AnalyzeElements(stl_container.begin(),
+ stl_container.end(),
+ &element_printouts,
+ listener);
+
+ const size_t actual_count = matrix.LhsSize();
+ if (actual_count == 0 && matchers_.empty()) {
+ return true;
+ }
+ if (actual_count != matchers_.size()) {
+ // The element count doesn't match. If the container is empty,
+ // there's no need to explain anything as Google Mock already
+ // prints the empty container. Otherwise we just need to show
+ // how many elements there actually are.
+ if (actual_count != 0 && listener->IsInterested()) {
+ *listener << "which has " << Elements(actual_count);
+ }
+ return false;
+ }
+
+ return VerifyAllElementsAndMatchersAreMatched(element_printouts,
+ matrix, listener) &&
+ FindPairing(matrix, listener);
+ }
+
+ private:
+ typedef ::std::vector<Matcher<const Element&> > MatcherVec;
+
+ template <typename ElementIter>
+ MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last,
+ ::std::vector<string>* element_printouts,
+ MatchResultListener* listener) const {
+ element_printouts->clear();
+ ::std::vector<char> did_match;
+ size_t num_elements = 0;
+ for (; elem_first != elem_last; ++num_elements, ++elem_first) {
+ if (listener->IsInterested()) {
+ element_printouts->push_back(PrintToString(*elem_first));
+ }
+ for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) {
+ did_match.push_back(Matches(matchers_[irhs])(*elem_first));
+ }
+ }
+
+ MatchMatrix matrix(num_elements, matchers_.size());
+ ::std::vector<char>::const_iterator did_match_iter = did_match.begin();
+ for (size_t ilhs = 0; ilhs != num_elements; ++ilhs) {
+ for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) {
+ matrix.SetEdge(ilhs, irhs, *did_match_iter++ != 0);
+ }
+ }
+ return matrix;
+ }
+
+ MatcherVec matchers_;
+
+ GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImpl);
+};
+
+// Functor for use in TransformTuple.
+// Performs MatcherCast<Target> on an input argument of any type.
+template <typename Target>
+struct CastAndAppendTransform {
+ template <typename Arg>
+ Matcher<Target> operator()(const Arg& a) const {
+ return MatcherCast<Target>(a);
+ }
+};
+
+// Implements UnorderedElementsAre.
+template <typename MatcherTuple>
+class UnorderedElementsAreMatcher {
+ public:
+ explicit UnorderedElementsAreMatcher(const MatcherTuple& args)
+ : matchers_(args) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+ typedef typename internal::StlContainerView<RawContainer>::type View;
+ typedef typename View::value_type Element;
+ typedef ::std::vector<Matcher<const Element&> > MatcherVec;
+ MatcherVec matchers;
+ matchers.reserve(::testing::tuple_size<MatcherTuple>::value);
+ TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
+ ::std::back_inserter(matchers));
+ return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>(
+ matchers.begin(), matchers.end()));
+ }
+
+ private:
+ const MatcherTuple matchers_;
+ GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcher);
+};
+
+// Implements ElementsAre.
+template <typename MatcherTuple>
+class ElementsAreMatcher {
+ public:
+ explicit ElementsAreMatcher(const MatcherTuple& args) : matchers_(args) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+ typedef typename internal::StlContainerView<RawContainer>::type View;
+ typedef typename View::value_type Element;
+ typedef ::std::vector<Matcher<const Element&> > MatcherVec;
+ MatcherVec matchers;
+ matchers.reserve(::testing::tuple_size<MatcherTuple>::value);
+ TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
+ ::std::back_inserter(matchers));
+ return MakeMatcher(new ElementsAreMatcherImpl<Container>(
+ matchers.begin(), matchers.end()));
+ }
+
+ private:
+ const MatcherTuple matchers_;
+ GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher);
+};
+
+// Implements UnorderedElementsAreArray().
+template <typename T>
+class UnorderedElementsAreArrayMatcher {
+ public:
+ UnorderedElementsAreArrayMatcher() {}
+
+ template <typename Iter>
+ UnorderedElementsAreArrayMatcher(Iter first, Iter last)
+ : matchers_(first, last) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ return MakeMatcher(
+ new UnorderedElementsAreMatcherImpl<Container>(matchers_.begin(),
+ matchers_.end()));
+ }
+
+ private:
+ ::std::vector<T> matchers_;
+
+ GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreArrayMatcher);
+};
+
+// Implements ElementsAreArray().
+template <typename T>
+class ElementsAreArrayMatcher {
+ public:
+ template <typename Iter>
+ ElementsAreArrayMatcher(Iter first, Iter last) : matchers_(first, last) {}
+
+ template <typename Container>
+ operator Matcher<Container>() const {
+ return MakeMatcher(new ElementsAreMatcherImpl<Container>(
+ matchers_.begin(), matchers_.end()));
+ }
+
+ private:
+ const ::std::vector<T> matchers_;
+
+ GTEST_DISALLOW_ASSIGN_(ElementsAreArrayMatcher);
+};
+
+// Given a 2-tuple matcher tm of type Tuple2Matcher and a value second
+// of type Second, BoundSecondMatcher<Tuple2Matcher, Second>(tm,
+// second) is a polymorphic matcher that matches a value x iff tm
+// matches tuple (x, second). Useful for implementing
+// UnorderedPointwise() in terms of UnorderedElementsAreArray().
+//
+// BoundSecondMatcher is copyable and assignable, as we need to put
+// instances of this class in a vector when implementing
+// UnorderedPointwise().
+template <typename Tuple2Matcher, typename Second>
+class BoundSecondMatcher {
+ public:
+ BoundSecondMatcher(const Tuple2Matcher& tm, const Second& second)
+ : tuple2_matcher_(tm), second_value_(second) {}
+
+ template <typename T>
+ operator Matcher<T>() const {
+ return MakeMatcher(new Impl<T>(tuple2_matcher_, second_value_));
+ }
+
+ // We have to define this for UnorderedPointwise() to compile in
+ // C++98 mode, as it puts BoundSecondMatcher instances in a vector,
+ // which requires the elements to be assignable in C++98. The
+ // compiler cannot generate the operator= for us, as Tuple2Matcher
+ // and Second may not be assignable.
+ //
+ // However, this should never be called, so the implementation just
+ // need to assert.
+ void operator=(const BoundSecondMatcher& /*rhs*/) {
+ GTEST_LOG_(FATAL) << "BoundSecondMatcher should never be assigned.";
+ }
+
+ private:
+ template <typename T>
+ class Impl : public MatcherInterface<T> {
+ public:
+ typedef ::testing::tuple<T, Second> ArgTuple;
+
+ Impl(const Tuple2Matcher& tm, const Second& second)
+ : mono_tuple2_matcher_(SafeMatcherCast<const ArgTuple&>(tm)),
+ second_value_(second) {}
+
+ virtual void DescribeTo(::std::ostream* os) const {
+ *os << "and ";
+ UniversalPrint(second_value_, os);
+ *os << " ";
+ mono_tuple2_matcher_.DescribeTo(os);
+ }
+
+ virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+ return mono_tuple2_matcher_.MatchAndExplain(ArgTuple(x, second_value_),
+ listener);
+ }
+
+ private:
+ const Matcher<const ArgTuple&> mono_tuple2_matcher_;
+ const Second second_value_;
+
+ GTEST_DISALLOW_ASSIGN_(Impl);
+ };
+
+ const Tuple2Matcher tuple2_matcher_;
+ const Second second_value_;
+};
+
+// Given a 2-tuple matcher tm and a value second,
+// MatcherBindSecond(tm, second) returns a matcher that matches a
+// value x iff tm matches tuple (x, second). Useful for implementing
+// UnorderedPointwise() in terms of UnorderedElementsAreArray().
+template <typename Tuple2Matcher, typename Second>
+BoundSecondMatcher<Tuple2Matcher, Second> MatcherBindSecond(
+ const Tuple2Matcher& tm, const Second& second) {
+ return BoundSecondMatcher<Tuple2Matcher, Second>(tm, second);
+}
+
+// Returns the description for a matcher defined using the MATCHER*()
+// macro where the user-supplied description string is "", if
+// 'negation' is false; otherwise returns the description of the
+// negation of the matcher. 'param_values' contains a list of strings
+// that are the print-out of the matcher's parameters.
+GTEST_API_ string FormatMatcherDescription(bool negation,
+ const char* matcher_name,
+ const Strings& param_values);
+
+} // namespace internal
+
+// ElementsAreArray(first, last)
+// ElementsAreArray(pointer, count)
+// ElementsAreArray(array)
+// ElementsAreArray(container)
+// ElementsAreArray({ e1, e2, ..., en })
+//
+// The ElementsAreArray() functions are like ElementsAre(...), except
+// that they are given a homogeneous sequence rather than taking each
+// element as a function argument. The sequence can be specified as an
+// array, a pointer and count, a vector, an initializer list, or an
+// STL iterator range. In each of these cases, the underlying sequence
+// can be either a sequence of values or a sequence of matchers.
+//
+// All forms of ElementsAreArray() make a copy of the input matcher sequence.
+
+template <typename Iter>
+inline internal::ElementsAreArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>
+ElementsAreArray(Iter first, Iter last) {
+ typedef typename ::std::iterator_traits<Iter>::value_type T;
+ return internal::ElementsAreArrayMatcher<T>(first, last);
+}
+
+template <typename T>
+inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
+ const T* pointer, size_t count) {
+ return ElementsAreArray(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
+ const T (&array)[N]) {
+ return ElementsAreArray(array, N);
+}
+
+template <typename Container>
+inline internal::ElementsAreArrayMatcher<typename Container::value_type>
+ElementsAreArray(const Container& container) {
+ return ElementsAreArray(container.begin(), container.end());
+}
+
+#if GTEST_HAS_STD_INITIALIZER_LIST_
+template <typename T>
+inline internal::ElementsAreArrayMatcher<T>
+ElementsAreArray(::std::initializer_list<T> xs) {
+ return ElementsAreArray(xs.begin(), xs.end());
+}
+#endif
+
+// UnorderedElementsAreArray(first, last)
+// UnorderedElementsAreArray(pointer, count)
+// UnorderedElementsAreArray(array)
+// UnorderedElementsAreArray(container)
+// UnorderedElementsAreArray({ e1, e2, ..., en })
+//
+// The UnorderedElementsAreArray() functions are like
+// ElementsAreArray(...), but allow matching the elements in any order.
+template <typename Iter>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename ::std::iterator_traits<Iter>::value_type>
+UnorderedElementsAreArray(Iter first, Iter last) {
+ typedef typename ::std::iterator_traits<Iter>::value_type T;
+ return internal::UnorderedElementsAreArrayMatcher<T>(first, last);
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T>
+UnorderedElementsAreArray(const T* pointer, size_t count) {
+ return UnorderedElementsAreArray(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::UnorderedElementsAreArrayMatcher<T>
+UnorderedElementsAreArray(const T (&array)[N]) {
+ return UnorderedElementsAreArray(array, N);
+}
+
+template <typename Container>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename Container::value_type>
+UnorderedElementsAreArray(const Container& container) {
+ return UnorderedElementsAreArray(container.begin(), container.end());
+}
+
+#if GTEST_HAS_STD_INITIALIZER_LIST_
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T>
+UnorderedElementsAreArray(::std::initializer_list<T> xs) {
+ return UnorderedElementsAreArray(xs.begin(), xs.end());
+}
+#endif
+
+// _ is a matcher that matches anything of any type.
+//
+// This definition is fine as:
+//
+// 1. The C++ standard permits using the name _ in a namespace that
+// is not the global namespace or ::std.
+// 2. The AnythingMatcher class has no data member or constructor,
+// so it's OK to create global variables of this type.
+// 3. c-style has approved of using _ in this case.
+const internal::AnythingMatcher _ = {};
+// Creates a matcher that matches any value of the given type T.
+template <typename T>
+inline Matcher<T> A() { return MakeMatcher(new internal::AnyMatcherImpl<T>()); }
+
+// Creates a matcher that matches any value of the given type T.
+template <typename T>
+inline Matcher<T> An() { return A<T>(); }
+
+// Creates a polymorphic matcher that matches anything equal to x.
+// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
+// wouldn't compile.
+template <typename T>
+inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); }
+
+// Constructs a Matcher<T> from a 'value' of type T. The constructed
+// matcher matches any value that's equal to 'value'.
+template <typename T>
+Matcher<T>::Matcher(T value) { *this = Eq(value); }
+
+// Creates a monomorphic matcher that matches anything with type Lhs
+// and equal to rhs. A user may need to use this instead of Eq(...)
+// in order to resolve an overloading ambiguity.
+//
+// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
+// or Matcher<T>(x), but more readable than the latter.
+//
+// We could define similar monomorphic matchers for other comparison
+// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
+// it yet as those are used much less than Eq() in practice. A user
+// can always write Matcher<T>(Lt(5)) to be explicit about the type,
+// for example.
+template <typename Lhs, typename Rhs>
+inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); }
+
+// Creates a polymorphic matcher that matches anything >= x.
+template <typename Rhs>
+inline internal::GeMatcher<Rhs> Ge(Rhs x) {
+ return internal::GeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything > x.
+template <typename Rhs>
+inline internal::GtMatcher<Rhs> Gt(Rhs x) {
+ return internal::GtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything <= x.
+template <typename Rhs>
+inline internal::LeMatcher<Rhs> Le(Rhs x) {
+ return internal::LeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything < x.
+template <typename Rhs>
+inline internal::LtMatcher<Rhs> Lt(Rhs x) {
+ return internal::LtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything != x.
+template <typename Rhs>
+inline internal::NeMatcher<Rhs> Ne(Rhs x) {
+ return internal::NeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches any NULL pointer.
+inline PolymorphicMatcher<internal::IsNullMatcher > IsNull() {
+ return MakePolymorphicMatcher(internal::IsNullMatcher());
+}
+
+// Creates a polymorphic matcher that matches any non-NULL pointer.
+// This is convenient as Not(NULL) doesn't compile (the compiler
+// thinks that that expression is comparing a pointer with an integer).
+inline PolymorphicMatcher<internal::NotNullMatcher > NotNull() {
+ return MakePolymorphicMatcher(internal::NotNullMatcher());
+}
+
+// Creates a polymorphic matcher that matches any argument that
+// references variable x.
+template <typename T>
+inline internal::RefMatcher<T&> Ref(T& x) { // NOLINT
+ return internal::RefMatcher<T&>(x);
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, where two NANs are considered unequal.
+inline internal::FloatingEqMatcher<double> DoubleEq(double rhs) {
+ return internal::FloatingEqMatcher<double>(rhs, false);
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, including NaN values when rhs is NaN.
+inline internal::FloatingEqMatcher<double> NanSensitiveDoubleEq(double rhs) {
+ return internal::FloatingEqMatcher<double>(rhs, true);
+}
+
+// Creates a matcher that matches any double argument approximately equal to
+// rhs, up to the specified max absolute error bound, where two NANs are
+// considered unequal. The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<double> DoubleNear(
+ double rhs, double max_abs_error) {
+ return internal::FloatingEqMatcher<double>(rhs, false, max_abs_error);
+}
+
+// Creates a matcher that matches any double argument approximately equal to
+// rhs, up to the specified max absolute error bound, including NaN values when
+// rhs is NaN. The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<double> NanSensitiveDoubleNear(
+ double rhs, double max_abs_error) {
+ return internal::FloatingEqMatcher<double>(rhs, true, max_abs_error);
+}
+
+// Creates a matcher that matches any float argument approximately
+// equal to rhs, where two NANs are considered unequal.
+inline internal::FloatingEqMatcher<float> FloatEq(float rhs) {
+ return internal::FloatingEqMatcher<float>(rhs, false);
+}
+
+// Creates a matcher that matches any float argument approximately
+// equal to rhs, including NaN values when rhs is NaN.
+inline internal::FloatingEqMatcher<float> NanSensitiveFloatEq(float rhs) {
+ return internal::FloatingEqMatcher<float>(rhs, true);
+}
+
+// Creates a matcher that matches any float argument approximately equal to
+// rhs, up to the specified max absolute error bound, where two NANs are
+// considered unequal. The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<float> FloatNear(
+ float rhs, float max_abs_error) {
+ return internal::FloatingEqMatcher<float>(rhs, false, max_abs_error);
+}
+
+// Creates a matcher that matches any float argument approximately equal to
+// rhs, up to the specified max absolute error bound, including NaN values when
+// rhs is NaN. The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<float> NanSensitiveFloatNear(
+ float rhs, float max_abs_error) {
+ return internal::FloatingEqMatcher<float>(rhs, true, max_abs_error);
+}
+
+// Creates a matcher that matches a pointer (raw or smart) that points
+// to a value that matches inner_matcher.
+template <typename InnerMatcher>
+inline internal::PointeeMatcher<InnerMatcher> Pointee(
+ const InnerMatcher& inner_matcher) {
+ return internal::PointeeMatcher<InnerMatcher>(inner_matcher);
+}
+
+// Creates a matcher that matches a pointer or reference that matches
+// inner_matcher when dynamic_cast<To> is applied.
+// The result of dynamic_cast<To> is forwarded to the inner matcher.
+// If To is a pointer and the cast fails, the inner matcher will receive NULL.
+// If To is a reference and the cast fails, this matcher returns false
+// immediately.
+template <typename To>
+inline PolymorphicMatcher<internal::WhenDynamicCastToMatcher<To> >
+WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
+ return MakePolymorphicMatcher(
+ internal::WhenDynamicCastToMatcher<To>(inner_matcher));
+}
+
+// Creates a matcher that matches an object whose given field matches
+// 'matcher'. For example,
+// Field(&Foo::number, Ge(5))
+// matches a Foo object x iff x.number >= 5.
+template <typename Class, typename FieldType, typename FieldMatcher>
+inline PolymorphicMatcher<
+ internal::FieldMatcher<Class, FieldType> > Field(
+ FieldType Class::*field, const FieldMatcher& matcher) {
+ return MakePolymorphicMatcher(
+ internal::FieldMatcher<Class, FieldType>(
+ field, MatcherCast<const FieldType&>(matcher)));
+ // The call to MatcherCast() is required for supporting inner
+ // matchers of compatible types. For example, it allows
+ // Field(&Foo::bar, m)
+ // to compile where bar is an int32 and m is a matcher for int64.
+}
+
+// Creates a matcher that matches an object whose given property
+// matches 'matcher'. For example,
+// Property(&Foo::str, StartsWith("hi"))
+// matches a Foo object x iff x.str() starts with "hi".
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<
+ internal::PropertyMatcher<Class, PropertyType> > Property(
+ PropertyType (Class::*property)() const, const PropertyMatcher& matcher) {
+ return MakePolymorphicMatcher(
+ internal::PropertyMatcher<Class, PropertyType>(
+ property,
+ MatcherCast<GTEST_REFERENCE_TO_CONST_(PropertyType)>(matcher)));
+ // The call to MatcherCast() is required for supporting inner
+ // matchers of compatible types. For example, it allows
+ // Property(&Foo::bar, m)
+ // to compile where bar() returns an int32 and m is a matcher for int64.
+}
+
+// Creates a matcher that matches an object iff the result of applying
+// a callable to x matches 'matcher'.
+// For example,
+// ResultOf(f, StartsWith("hi"))
+// matches a Foo object x iff f(x) starts with "hi".
+// callable parameter can be a function, function pointer, or a functor.
+// Callable has to satisfy the following conditions:
+// * It is required to keep no state affecting the results of
+// the calls on it and make no assumptions about how many calls
+// will be made. Any state it keeps must be protected from the
+// concurrent access.
+// * If it is a function object, it has to define type result_type.
+// We recommend deriving your functor classes from std::unary_function.
+template <typename Callable, typename ResultOfMatcher>
+internal::ResultOfMatcher<Callable> ResultOf(
+ Callable callable, const ResultOfMatcher& matcher) {
+ return internal::ResultOfMatcher<Callable>(
+ callable,
+ MatcherCast<typename internal::CallableTraits<Callable>::ResultType>(
+ matcher));
+ // The call to MatcherCast() is required for supporting inner
+ // matchers of compatible types. For example, it allows
+ // ResultOf(Function, m)
+ // to compile where Function() returns an int32 and m is a matcher for int64.
+}
+
+// String matchers.
+
+// Matches a string equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+ StrEq(const internal::string& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+ str, true, true));
+}
+
+// Matches a string not equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+ StrNe(const internal::string& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+ str, false, true));
+}
+
+// Matches a string equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+ StrCaseEq(const internal::string& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+ str, true, false));
+}
+
+// Matches a string not equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+ StrCaseNe(const internal::string& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+ str, false, false));
+}
+
+// Creates a matcher that matches any string, std::string, or C string
+// that contains the given substring.
+inline PolymorphicMatcher<internal::HasSubstrMatcher<internal::string> >
+ HasSubstr(const internal::string& substring) {
+ return MakePolymorphicMatcher(internal::HasSubstrMatcher<internal::string>(
+ substring));
+}
+
+// Matches a string that starts with 'prefix' (case-sensitive).
+inline PolymorphicMatcher<internal::StartsWithMatcher<internal::string> >
+ StartsWith(const internal::string& prefix) {
+ return MakePolymorphicMatcher(internal::StartsWithMatcher<internal::string>(
+ prefix));
+}
+
+// Matches a string that ends with 'suffix' (case-sensitive).
+inline PolymorphicMatcher<internal::EndsWithMatcher<internal::string> >
+ EndsWith(const internal::string& suffix) {
+ return MakePolymorphicMatcher(internal::EndsWithMatcher<internal::string>(
+ suffix));
+}
+
+// Matches a string that fully matches regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+ const internal::RE* regex) {
+ return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
+}
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+ const internal::string& regex) {
+ return MatchesRegex(new internal::RE(regex));
+}
+
+// Matches a string that contains regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+ const internal::RE* regex) {
+ return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
+}
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+ const internal::string& regex) {
+ return ContainsRegex(new internal::RE(regex));
+}
+
+#if GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING
+// Wide string matchers.
+
+// Matches a string equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+ StrEq(const internal::wstring& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+ str, true, true));
+}
+
+// Matches a string not equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+ StrNe(const internal::wstring& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+ str, false, true));
+}
+
+// Matches a string equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+ StrCaseEq(const internal::wstring& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+ str, true, false));
+}
+
+// Matches a string not equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+ StrCaseNe(const internal::wstring& str) {
+ return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+ str, false, false));
+}
+
+// Creates a matcher that matches any wstring, std::wstring, or C wide string
+// that contains the given substring.
+inline PolymorphicMatcher<internal::HasSubstrMatcher<internal::wstring> >
+ HasSubstr(const internal::wstring& substring) {
+ return MakePolymorphicMatcher(internal::HasSubstrMatcher<internal::wstring>(
+ substring));
+}
+
+// Matches a string that starts with 'prefix' (case-sensitive).
+inline PolymorphicMatcher<internal::StartsWithMatcher<internal::wstring> >
+ StartsWith(const internal::wstring& prefix) {
+ return MakePolymorphicMatcher(internal::StartsWithMatcher<internal::wstring>(
+ prefix));
+}
+
+// Matches a string that ends with 'suffix' (case-sensitive).
+inline PolymorphicMatcher<internal::EndsWithMatcher<internal::wstring> >
+ EndsWith(const internal::wstring& suffix) {
+ return MakePolymorphicMatcher(internal::EndsWithMatcher<internal::wstring>(
+ suffix));
+}
+
+#endif // GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field == the second field.
+inline internal::Eq2Matcher Eq() { return internal::Eq2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field >= the second field.
+inline internal::Ge2Matcher Ge() { return internal::Ge2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field > the second field.
+inline internal::Gt2Matcher Gt() { return internal::Gt2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field <= the second field.
+inline internal::Le2Matcher Le() { return internal::Le2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field < the second field.
+inline internal::Lt2Matcher Lt() { return internal::Lt2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field != the second field.
+inline internal::Ne2Matcher Ne() { return internal::Ne2Matcher(); }
+
+// Creates a matcher that matches any value of type T that m doesn't
+// match.
+template <typename InnerMatcher>
+inline internal::NotMatcher<InnerMatcher> Not(InnerMatcher m) {
+ return internal::NotMatcher<InnerMatcher>(m);
+}
+
+// Returns a matcher that matches anything that satisfies the given
+// predicate. The predicate can be any unary function or functor
+// whose return type can be implicitly converted to bool.
+template <typename Predicate>
+inline PolymorphicMatcher<internal::TrulyMatcher<Predicate> >
+Truly(Predicate pred) {
+ return MakePolymorphicMatcher(internal::TrulyMatcher<Predicate>(pred));
+}
+
+// Returns a matcher that matches the container size. The container must
+// support both size() and size_type which all STL-like containers provide.
+// Note that the parameter 'size' can be a value of type size_type as well as
+// matcher. For instance:
+// EXPECT_THAT(container, SizeIs(2)); // Checks container has 2 elements.
+// EXPECT_THAT(container, SizeIs(Le(2)); // Checks container has at most 2.
+template <typename SizeMatcher>
+inline internal::SizeIsMatcher<SizeMatcher>
+SizeIs(const SizeMatcher& size_matcher) {
+ return internal::SizeIsMatcher<SizeMatcher>(size_matcher);
+}
+
+// Returns a matcher that matches the distance between the container's begin()
+// iterator and its end() iterator, i.e. the size of the container. This matcher
+// can be used instead of SizeIs with containers such as std::forward_list which
+// do not implement size(). The container must provide const_iterator (with
+// valid iterator_traits), begin() and end().
+template <typename DistanceMatcher>
+inline internal::BeginEndDistanceIsMatcher<DistanceMatcher>
+BeginEndDistanceIs(const DistanceMatcher& distance_matcher) {
+ return internal::BeginEndDistanceIsMatcher<DistanceMatcher>(distance_matcher);
+}
+
+// Returns a matcher that matches an equal container.
+// This matcher behaves like Eq(), but in the event of mismatch lists the
+// values that are included in one container but not the other. (Duplicate
+// values and order differences are not explained.)
+template <typename Container>
+inline PolymorphicMatcher<internal::ContainerEqMatcher< // NOLINT
+ GTEST_REMOVE_CONST_(Container)> >
+ ContainerEq(const Container& rhs) {
+ // This following line is for working around a bug in MSVC 8.0,
+ // which causes Container to be a const type sometimes.
+ typedef GTEST_REMOVE_CONST_(Container) RawContainer;
+ return MakePolymorphicMatcher(
+ internal::ContainerEqMatcher<RawContainer>(rhs));
+}
+
+// Returns a matcher that matches a container that, when sorted using
+// the given comparator, matches container_matcher.
+template <typename Comparator, typename ContainerMatcher>
+inline internal::WhenSortedByMatcher<Comparator, ContainerMatcher>
+WhenSortedBy(const Comparator& comparator,
+ const ContainerMatcher& container_matcher) {
+ return internal::WhenSortedByMatcher<Comparator, ContainerMatcher>(
+ comparator, container_matcher);
+}
+
+// Returns a matcher that matches a container that, when sorted using
+// the < operator, matches container_matcher.
+template <typename ContainerMatcher>
+inline internal::WhenSortedByMatcher<internal::LessComparator, ContainerMatcher>
+WhenSorted(const ContainerMatcher& container_matcher) {
+ return
+ internal::WhenSortedByMatcher<internal::LessComparator, ContainerMatcher>(
+ internal::LessComparator(), container_matcher);
+}
+
+// Matches an STL-style container or a native array that contains the
+// same number of elements as in rhs, where its i-th element and rhs's
+// i-th element (as a pair) satisfy the given pair matcher, for all i.
+// TupleMatcher must be able to be safely cast to Matcher<tuple<const
+// T1&, const T2&> >, where T1 and T2 are the types of elements in the
+// LHS container and the RHS container respectively.
+template <typename TupleMatcher, typename Container>
+inline internal::PointwiseMatcher<TupleMatcher,
+ GTEST_REMOVE_CONST_(Container)>
+Pointwise(const TupleMatcher& tuple_matcher, const Container& rhs) {
+ // This following line is for working around a bug in MSVC 8.0,
+ // which causes Container to be a const type sometimes (e.g. when
+ // rhs is a const int[])..
+ typedef GTEST_REMOVE_CONST_(Container) RawContainer;
+ return internal::PointwiseMatcher<TupleMatcher, RawContainer>(
+ tuple_matcher, rhs);
+}
+
+#if GTEST_HAS_STD_INITIALIZER_LIST_
+
+// Supports the Pointwise(m, {a, b, c}) syntax.
+template <typename TupleMatcher, typename T>
+inline internal::PointwiseMatcher<TupleMatcher, std::vector<T> > Pointwise(
+ const TupleMatcher& tuple_matcher, std::initializer_list<T> rhs) {
+ return Pointwise(tuple_matcher, std::vector<T>(rhs));
+}
+
+#endif // GTEST_HAS_STD_INITIALIZER_LIST_
+
+// UnorderedPointwise(pair_matcher, rhs) matches an STL-style
+// container or a native array that contains the same number of
+// elements as in rhs, where in some permutation of the container, its
+// i-th element and rhs's i-th element (as a pair) satisfy the given
+// pair matcher, for all i. Tuple2Matcher must be able to be safely
+// cast to Matcher<tuple<const T1&, const T2&> >, where T1 and T2 are
+// the types of elements in the LHS container and the RHS container
+// respectively.
+//
+// This is like Pointwise(pair_matcher, rhs), except that the element
+// order doesn't matter.
+template <typename Tuple2Matcher, typename RhsContainer>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename internal::BoundSecondMatcher<
+ Tuple2Matcher, typename internal::StlContainerView<GTEST_REMOVE_CONST_(
+ RhsContainer)>::type::value_type> >
+UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
+ const RhsContainer& rhs_container) {
+ // This following line is for working around a bug in MSVC 8.0,
+ // which causes RhsContainer to be a const type sometimes (e.g. when
+ // rhs_container is a const int[]).
+ typedef GTEST_REMOVE_CONST_(RhsContainer) RawRhsContainer;
+
+ // RhsView allows the same code to handle RhsContainer being a
+ // STL-style container and it being a native C-style array.
+ typedef typename internal::StlContainerView<RawRhsContainer> RhsView;
+ typedef typename RhsView::type RhsStlContainer;
+ typedef typename RhsStlContainer::value_type Second;
+ const RhsStlContainer& rhs_stl_container =
+ RhsView::ConstReference(rhs_container);
+
+ // Create a matcher for each element in rhs_container.
+ ::std::vector<internal::BoundSecondMatcher<Tuple2Matcher, Second> > matchers;
+ for (typename RhsStlContainer::const_iterator it = rhs_stl_container.begin();
+ it != rhs_stl_container.end(); ++it) {
+ matchers.push_back(
+ internal::MatcherBindSecond(tuple2_matcher, *it));
+ }
+
+ // Delegate the work to UnorderedElementsAreArray().
+ return UnorderedElementsAreArray(matchers);
+}
+
+#if GTEST_HAS_STD_INITIALIZER_LIST_
+
+// Supports the UnorderedPointwise(m, {a, b, c}) syntax.
+template <typename Tuple2Matcher, typename T>
+inline internal::UnorderedElementsAreArrayMatcher<
+ typename internal::BoundSecondMatcher<Tuple2Matcher, T> >
+UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
+ std::initializer_list<T> rhs) {
+ return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs));
+}
+
+#endif // GTEST_HAS_STD_INITIALIZER_LIST_
+
+// Matches an STL-style container or a native array that contains at
+// least one element matching the given value or matcher.
+//
+// Examples:
+// ::std::set<int> page_ids;
+// page_ids.insert(3);
+// page_ids.insert(1);
+// EXPECT_THAT(page_ids, Contains(1));
+// EXPECT_THAT(page_ids, Contains(Gt(2)));
+// EXPECT_THAT(page_ids, Not(Contains(4)));
+//
+// ::std::map<int, size_t> page_lengths;
+// page_lengths[1] = 100;
+// EXPECT_THAT(page_lengths,
+// Contains(::std::pair<const int, size_t>(1, 100)));
+//
+// const char* user_ids[] = { "joe", "mike", "tom" };
+// EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom"))));
+template <typename M>
+inline internal::ContainsMatcher<M> Contains(M matcher) {
+ return internal::ContainsMatcher<M>(matcher);
+}
+
+// Matches an STL-style container or a native array that contains only
+// elements matching the given value or matcher.
+//
+// Each(m) is semantically equivalent to Not(Contains(Not(m))). Only
+// the messages are different.
+//
+// Examples:
+// ::std::set<int> page_ids;
+// // Each(m) matches an empty container, regardless of what m is.
+// EXPECT_THAT(page_ids, Each(Eq(1)));
+// EXPECT_THAT(page_ids, Each(Eq(77)));
+//
+// page_ids.insert(3);
+// EXPECT_THAT(page_ids, Each(Gt(0)));
+// EXPECT_THAT(page_ids, Not(Each(Gt(4))));
+// page_ids.insert(1);
+// EXPECT_THAT(page_ids, Not(Each(Lt(2))));
+//
+// ::std::map<int, size_t> page_lengths;
+// page_lengths[1] = 100;
+// page_lengths[2] = 200;
+// page_lengths[3] = 300;
+// EXPECT_THAT(page_lengths, Not(Each(Pair(1, 100))));
+// EXPECT_THAT(page_lengths, Each(Key(Le(3))));
+//
+// const char* user_ids[] = { "joe", "mike", "tom" };
+// EXPECT_THAT(user_ids, Not(Each(Eq(::std::string("tom")))));
+template <typename M>
+inline internal::EachMatcher<M> Each(M matcher) {
+ return internal::EachMatcher<M>(matcher);
+}
+
+// Key(inner_matcher) matches an std::pair whose 'first' field matches
+// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an
+// std::map that contains at least one element whose key is >= 5.
+template <typename M>
+inline internal::KeyMatcher<M> Key(M inner_matcher) {
+ return internal::KeyMatcher<M>(inner_matcher);
+}
+
+// Pair(first_matcher, second_matcher) matches a std::pair whose 'first' field
+// matches first_matcher and whose 'second' field matches second_matcher. For
+// example, EXPECT_THAT(map_type, ElementsAre(Pair(Ge(5), "foo"))) can be used
+// to match a std::map<int, string> that contains exactly one element whose key
+// is >= 5 and whose value equals "foo".
+template <typename FirstMatcher, typename SecondMatcher>
+inline internal::PairMatcher<FirstMatcher, SecondMatcher>
+Pair(FirstMatcher first_matcher, SecondMatcher second_matcher) {
+ return internal::PairMatcher<FirstMatcher, SecondMatcher>(
+ first_matcher, second_matcher);
+}
+
+// Returns a predicate that is satisfied by anything that matches the
+// given matcher.
+template <typename M>
+inline internal::MatcherAsPredicate<M> Matches(M matcher) {
+ return internal::MatcherAsPredicate<M>(matcher);
+}
+
+// Returns true iff the value matches the matcher.
+template <typename T, typename M>
+inline bool Value(const T& value, M matcher) {
+ return testing::Matches(matcher)(value);
+}
+
+// Matches the value against the given matcher and explains the match
+// result to listener.
+template <typename T, typename M>
+inline bool ExplainMatchResult(
+ M matcher, const T& value, MatchResultListener* listener) {
+ return SafeMatcherCast<const T&>(matcher).MatchAndExplain(value, listener);
+}
+
+#if GTEST_LANG_CXX11
+// Define variadic matcher versions. They are overloaded in
+// gmock-generated-matchers.h for the cases supported by pre C++11 compilers.
+template <typename... Args>
+inline internal::AllOfMatcher<Args...> AllOf(const Args&... matchers) {
+ return internal::AllOfMatcher<Args...>(matchers...);
+}
+
+template <typename... Args>
+inline internal::AnyOfMatcher<Args...> AnyOf(const Args&... matchers) {
+ return internal::AnyOfMatcher<Args...>(matchers...);
+}
+
+#endif // GTEST_LANG_CXX11
+
+// AllArgs(m) is a synonym of m. This is useful in
+//
+// EXPECT_CALL(foo, Bar(_, _)).With(AllArgs(Eq()));
+//
+// which is easier to read than
+//
+// EXPECT_CALL(foo, Bar(_, _)).With(Eq());
+template <typename InnerMatcher>
+inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; }
+
+// These macros allow using matchers to check values in Google Test
+// tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
+// succeed iff the value matches the matcher. If the assertion fails,
+// the value and the description of the matcher will be printed.
+#define ASSERT_THAT(value, matcher) ASSERT_PRED_FORMAT1(\
+ ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
+#define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\
+ ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
+
+} // namespace testing
+
+// Include any custom callback matchers added by the local installation.
+// We must include this header at the end to make sure it can use the
+// declarations from this file.
+#include "gmock/internal/custom/gmock-matchers.h"
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/gmock-more-actions.h b/extern/gmock/include/gmock/gmock-more-actions.h
new file mode 100644
index 00000000000..3d387b6b7d7
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-more-actions.h
@@ -0,0 +1,246 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some actions that depend on gmock-generated-actions.h.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+
+#include <algorithm>
+
+#include "gmock/gmock-generated-actions.h"
+
+namespace testing {
+namespace internal {
+
+// Implements the Invoke(f) action. The template argument
+// FunctionImpl is the implementation type of f, which can be either a
+// function pointer or a functor. Invoke(f) can be used as an
+// Action<F> as long as f's type is compatible with F (i.e. f can be
+// assigned to a tr1::function<F>).
+template <typename FunctionImpl>
+class InvokeAction {
+ public:
+ // The c'tor makes a copy of function_impl (either a function
+ // pointer or a functor).
+ explicit InvokeAction(FunctionImpl function_impl)
+ : function_impl_(function_impl) {}
+
+ template <typename Result, typename ArgumentTuple>
+ Result Perform(const ArgumentTuple& args) {
+ return InvokeHelper<Result, ArgumentTuple>::Invoke(function_impl_, args);
+ }
+
+ private:
+ FunctionImpl function_impl_;
+
+ GTEST_DISALLOW_ASSIGN_(InvokeAction);
+};
+
+// Implements the Invoke(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+class InvokeMethodAction {
+ public:
+ InvokeMethodAction(Class* obj_ptr, MethodPtr method_ptr)
+ : method_ptr_(method_ptr), obj_ptr_(obj_ptr) {}
+
+ template <typename Result, typename ArgumentTuple>
+ Result Perform(const ArgumentTuple& args) const {
+ return InvokeHelper<Result, ArgumentTuple>::InvokeMethod(
+ obj_ptr_, method_ptr_, args);
+ }
+
+ private:
+ // The order of these members matters. Reversing the order can trigger
+ // warning C4121 in MSVC (see
+ // http://computer-programming-forum.com/7-vc.net/6fbc30265f860ad1.htm ).
+ const MethodPtr method_ptr_;
+ Class* const obj_ptr_;
+
+ GTEST_DISALLOW_ASSIGN_(InvokeMethodAction);
+};
+
+// An internal replacement for std::copy which mimics its behavior. This is
+// necessary because Visual Studio deprecates ::std::copy, issuing warning 4996.
+// However Visual Studio 2010 and later do not honor #pragmas which disable that
+// warning.
+template<typename InputIterator, typename OutputIterator>
+inline OutputIterator CopyElements(InputIterator first,
+ InputIterator last,
+ OutputIterator output) {
+ for (; first != last; ++first, ++output) {
+ *output = *first;
+ }
+ return output;
+}
+
+} // namespace internal
+
+// Various overloads for Invoke().
+
+// Creates an action that invokes 'function_impl' with the mock
+// function's arguments.
+template <typename FunctionImpl>
+PolymorphicAction<internal::InvokeAction<FunctionImpl> > Invoke(
+ FunctionImpl function_impl) {
+ return MakePolymorphicAction(
+ internal::InvokeAction<FunctionImpl>(function_impl));
+}
+
+// Creates an action that invokes the given method on the given object
+// with the mock function's arguments.
+template <class Class, typename MethodPtr>
+PolymorphicAction<internal::InvokeMethodAction<Class, MethodPtr> > Invoke(
+ Class* obj_ptr, MethodPtr method_ptr) {
+ return MakePolymorphicAction(
+ internal::InvokeMethodAction<Class, MethodPtr>(obj_ptr, method_ptr));
+}
+
+// WithoutArgs(inner_action) can be used in a mock function with a
+// non-empty argument list to perform inner_action, which takes no
+// argument. In other words, it adapts an action accepting no
+// argument to one that accepts (and ignores) arguments.
+template <typename InnerAction>
+inline internal::WithArgsAction<InnerAction>
+WithoutArgs(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction>(action);
+}
+
+// WithArg<k>(an_action) creates an action that passes the k-th
+// (0-based) argument of the mock function to an_action and performs
+// it. It adapts an action accepting one argument to one that accepts
+// multiple arguments. For convenience, we also provide
+// WithArgs<k>(an_action) (defined below) as a synonym.
+template <int k, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k>
+WithArg(const InnerAction& action) {
+ return internal::WithArgsAction<InnerAction, k>(action);
+}
+
+// The ACTION*() macros trigger warning C4100 (unreferenced formal
+// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in
+// the macro definition, as the warnings are generated when the macro
+// is expanded and macro expansion cannot contain #pragma. Therefore
+// we suppress them here.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#endif
+
+// Action ReturnArg<k>() returns the k-th argument of the mock function.
+ACTION_TEMPLATE(ReturnArg,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_0_VALUE_PARAMS()) {
+ return ::testing::get<k>(args);
+}
+
+// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the
+// mock function to *pointer.
+ACTION_TEMPLATE(SaveArg,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(pointer)) {
+ *pointer = ::testing::get<k>(args);
+}
+
+// Action SaveArgPointee<k>(pointer) saves the value pointed to
+// by the k-th (0-based) argument of the mock function to *pointer.
+ACTION_TEMPLATE(SaveArgPointee,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(pointer)) {
+ *pointer = *::testing::get<k>(args);
+}
+
+// Action SetArgReferee<k>(value) assigns 'value' to the variable
+// referenced by the k-th (0-based) argument of the mock function.
+ACTION_TEMPLATE(SetArgReferee,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(value)) {
+ typedef typename ::testing::tuple_element<k, args_type>::type argk_type;
+ // Ensures that argument #k is a reference. If you get a compiler
+ // error on the next line, you are using SetArgReferee<k>(value) in
+ // a mock function whose k-th (0-based) argument is not a reference.
+ GTEST_COMPILE_ASSERT_(internal::is_reference<argk_type>::value,
+ SetArgReferee_must_be_used_with_a_reference_argument);
+ ::testing::get<k>(args) = value;
+}
+
+// Action SetArrayArgument<k>(first, last) copies the elements in
+// source range [first, last) to the array pointed to by the k-th
+// (0-based) argument, which can be either a pointer or an
+// iterator. The action does not take ownership of the elements in the
+// source range.
+ACTION_TEMPLATE(SetArrayArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_2_VALUE_PARAMS(first, last)) {
+ // Visual Studio deprecates ::std::copy, so we use our own copy in that case.
+#ifdef _MSC_VER
+ internal::CopyElements(first, last, ::testing::get<k>(args));
+#else
+ ::std::copy(first, last, ::testing::get<k>(args));
+#endif
+}
+
+// Action DeleteArg<k>() deletes the k-th (0-based) argument of the mock
+// function.
+ACTION_TEMPLATE(DeleteArg,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_0_VALUE_PARAMS()) {
+ delete ::testing::get<k>(args);
+}
+
+// This action returns the value pointed to by 'pointer'.
+ACTION_P(ReturnPointee, pointer) { return *pointer; }
+
+// Action Throw(exception) can be used in a mock function of any type
+// to throw the given exception. Any copyable value can be thrown.
+#if GTEST_HAS_EXCEPTIONS
+
+// Suppresses the 'unreachable code' warning that VC generates in opt modes.
+# ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4702) // Temporarily disables warning 4702.
+# endif
+ACTION_P(Throw, exception) { throw exception; }
+# ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+# endif
+
+#endif // GTEST_HAS_EXCEPTIONS
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
diff --git a/extern/gmock/include/gmock/gmock-more-matchers.h b/extern/gmock/include/gmock/gmock-more-matchers.h
new file mode 100644
index 00000000000..3db899f4297
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-more-matchers.h
@@ -0,0 +1,58 @@
+// Copyright 2013, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: marcus.boerger@google.com (Marcus Boerger)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some matchers that depend on gmock-generated-matchers.h.
+//
+// Note that tests are implemented in gmock-matchers_test.cc rather than
+// gmock-more-matchers-test.cc.
+
+#ifndef GMOCK_GMOCK_MORE_MATCHERS_H_
+#define GMOCK_GMOCK_MORE_MATCHERS_H_
+
+#include "gmock/gmock-generated-matchers.h"
+
+namespace testing {
+
+// Defines a matcher that matches an empty container. The container must
+// support both size() and empty(), which all STL-like containers provide.
+MATCHER(IsEmpty, negation ? "isn't empty" : "is empty") {
+ if (arg.empty()) {
+ return true;
+ }
+ *result_listener << "whose size is " << arg.size();
+ return false;
+}
+
+} // namespace testing
+
+#endif // GMOCK_GMOCK_MORE_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/gmock-spec-builders.h b/extern/gmock/include/gmock/gmock-spec-builders.h
new file mode 100644
index 00000000000..fed7de66bc4
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock-spec-builders.h
@@ -0,0 +1,1847 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements the ON_CALL() and EXPECT_CALL() macros.
+//
+// A user can use the ON_CALL() macro to specify the default action of
+// a mock method. The syntax is:
+//
+// ON_CALL(mock_object, Method(argument-matchers))
+// .With(multi-argument-matcher)
+// .WillByDefault(action);
+//
+// where the .With() clause is optional.
+//
+// A user can use the EXPECT_CALL() macro to specify an expectation on
+// a mock method. The syntax is:
+//
+// EXPECT_CALL(mock_object, Method(argument-matchers))
+// .With(multi-argument-matchers)
+// .Times(cardinality)
+// .InSequence(sequences)
+// .After(expectations)
+// .WillOnce(action)
+// .WillRepeatedly(action)
+// .RetiresOnSaturation();
+//
+// where all clauses are optional, and .InSequence()/.After()/
+// .WillOnce() can appear any number of times.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept> // NOLINT
+#endif
+
+#include "gmock/gmock-actions.h"
+#include "gmock/gmock-cardinalities.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// An abstract handle of an expectation.
+class Expectation;
+
+// A set of expectation handles.
+class ExpectationSet;
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// Implements a mock function.
+template <typename F> class FunctionMocker;
+
+// Base class for expectations.
+class ExpectationBase;
+
+// Implements an expectation.
+template <typename F> class TypedExpectation;
+
+// Helper class for testing the Expectation class template.
+class ExpectationTester;
+
+// Base class for function mockers.
+template <typename F> class FunctionMockerBase;
+
+// Protects the mock object registry (in class Mock), all function
+// mockers, and all expectations.
+//
+// The reason we don't use more fine-grained protection is: when a
+// mock function Foo() is called, it needs to consult its expectations
+// to see which one should be picked. If another thread is allowed to
+// call a mock function (either Foo() or a different one) at the same
+// time, it could affect the "retired" attributes of Foo()'s
+// expectations when InSequence() is used, and thus affect which
+// expectation gets picked. Therefore, we sequence all mock function
+// calls to ensure the integrity of the mock objects' states.
+GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_gmock_mutex);
+
+// Untyped base class for ActionResultHolder<R>.
+class UntypedActionResultHolderBase;
+
+// Abstract base class of FunctionMockerBase. This is the
+// type-agnostic part of the function mocker interface. Its pure
+// virtual methods are implemented by FunctionMockerBase.
+class GTEST_API_ UntypedFunctionMockerBase {
+ public:
+ UntypedFunctionMockerBase();
+ virtual ~UntypedFunctionMockerBase();
+
+ // Verifies that all expectations on this mock function have been
+ // satisfied. Reports one or more Google Test non-fatal failures
+ // and returns false if not.
+ bool VerifyAndClearExpectationsLocked()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+ // Clears the ON_CALL()s set on this mock function.
+ virtual void ClearDefaultActionsLocked()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) = 0;
+
+ // In all of the following Untyped* functions, it's the caller's
+ // responsibility to guarantee the correctness of the arguments'
+ // types.
+
+ // Performs the default action with the given arguments and returns
+ // the action's result. The call description string will be used in
+ // the error message to describe the call in the case the default
+ // action fails.
+ // L = *
+ virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction(
+ const void* untyped_args,
+ const string& call_description) const = 0;
+
+ // Performs the given action with the given arguments and returns
+ // the action's result.
+ // L = *
+ virtual UntypedActionResultHolderBase* UntypedPerformAction(
+ const void* untyped_action,
+ const void* untyped_args) const = 0;
+
+ // Writes a message that the call is uninteresting (i.e. neither
+ // explicitly expected nor explicitly unexpected) to the given
+ // ostream.
+ virtual void UntypedDescribeUninterestingCall(
+ const void* untyped_args,
+ ::std::ostream* os) const
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0;
+
+ // Returns the expectation that matches the given function arguments
+ // (or NULL is there's no match); when a match is found,
+ // untyped_action is set to point to the action that should be
+ // performed (or NULL if the action is "do default"), and
+ // is_excessive is modified to indicate whether the call exceeds the
+ // expected number.
+ virtual const ExpectationBase* UntypedFindMatchingExpectation(
+ const void* untyped_args,
+ const void** untyped_action, bool* is_excessive,
+ ::std::ostream* what, ::std::ostream* why)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0;
+
+ // Prints the given function arguments to the ostream.
+ virtual void UntypedPrintArgs(const void* untyped_args,
+ ::std::ostream* os) const = 0;
+
+ // Sets the mock object this mock method belongs to, and registers
+ // this information in the global mock registry. Will be called
+ // whenever an EXPECT_CALL() or ON_CALL() is executed on this mock
+ // method.
+ // TODO(wan@google.com): rename to SetAndRegisterOwner().
+ void RegisterOwner(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+ // Sets the mock object this mock method belongs to, and sets the
+ // name of the mock function. Will be called upon each invocation
+ // of this mock function.
+ void SetOwnerAndName(const void* mock_obj, const char* name)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+ // Returns the mock object this mock method belongs to. Must be
+ // called after RegisterOwner() or SetOwnerAndName() has been
+ // called.
+ const void* MockObject() const
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+ // Returns the name of this mock method. Must be called after
+ // SetOwnerAndName() has been called.
+ const char* Name() const
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+ // Returns the result of invoking this mock function with the given
+ // arguments. This function can be safely called from multiple
+ // threads concurrently. The caller is responsible for deleting the
+ // result.
+ UntypedActionResultHolderBase* UntypedInvokeWith(
+ const void* untyped_args)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+ protected:
+ typedef std::vector<const void*> UntypedOnCallSpecs;
+
+ typedef std::vector<internal::linked_ptr<ExpectationBase> >
+ UntypedExpectations;
+
+ // Returns an Expectation object that references and co-owns exp,
+ // which must be an expectation on this mock function.
+ Expectation GetHandleOf(ExpectationBase* exp);
+
+ // Address of the mock object this mock method belongs to. Only
+ // valid after this mock method has been called or
+ // ON_CALL/EXPECT_CALL has been invoked on it.
+ const void* mock_obj_; // Protected by g_gmock_mutex.
+
+ // Name of the function being mocked. Only valid after this mock
+ // method has been called.
+ const char* name_; // Protected by g_gmock_mutex.
+
+ // All default action specs for this function mocker.
+ UntypedOnCallSpecs untyped_on_call_specs_;
+
+ // All expectations for this function mocker.
+ UntypedExpectations untyped_expectations_;
+}; // class UntypedFunctionMockerBase
+
+// Untyped base class for OnCallSpec<F>.
+class UntypedOnCallSpecBase {
+ public:
+ // The arguments are the location of the ON_CALL() statement.
+ UntypedOnCallSpecBase(const char* a_file, int a_line)
+ : file_(a_file), line_(a_line), last_clause_(kNone) {}
+
+ // Where in the source file was the default action spec defined?
+ const char* file() const { return file_; }
+ int line() const { return line_; }
+
+ protected:
+ // Gives each clause in the ON_CALL() statement a name.
+ enum Clause {
+ // Do not change the order of the enum members! The run-time
+ // syntax checking relies on it.
+ kNone,
+ kWith,
+ kWillByDefault
+ };
+
+ // Asserts that the ON_CALL() statement has a certain property.
+ void AssertSpecProperty(bool property, const string& failure_message) const {
+ Assert(property, file_, line_, failure_message);
+ }
+
+ // Expects that the ON_CALL() statement has a certain property.
+ void ExpectSpecProperty(bool property, const string& failure_message) const {
+ Expect(property, file_, line_, failure_message);
+ }
+
+ const char* file_;
+ int line_;
+
+ // The last clause in the ON_CALL() statement as seen so far.
+ // Initially kNone and changes as the statement is parsed.
+ Clause last_clause_;
+}; // class UntypedOnCallSpecBase
+
+// This template class implements an ON_CALL spec.
+template <typename F>
+class OnCallSpec : public UntypedOnCallSpecBase {
+ public:
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+ typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+
+ // Constructs an OnCallSpec object from the information inside
+ // the parenthesis of an ON_CALL() statement.
+ OnCallSpec(const char* a_file, int a_line,
+ const ArgumentMatcherTuple& matchers)
+ : UntypedOnCallSpecBase(a_file, a_line),
+ matchers_(matchers),
+ // By default, extra_matcher_ should match anything. However,
+ // we cannot initialize it with _ as that triggers a compiler
+ // bug in Symbian's C++ compiler (cannot decide between two
+ // overloaded constructors of Matcher<const ArgumentTuple&>).
+ extra_matcher_(A<const ArgumentTuple&>()) {
+ }
+
+ // Implements the .With() clause.
+ OnCallSpec& With(const Matcher<const ArgumentTuple&>& m) {
+ // Makes sure this is called at most once.
+ ExpectSpecProperty(last_clause_ < kWith,
+ ".With() cannot appear "
+ "more than once in an ON_CALL().");
+ last_clause_ = kWith;
+
+ extra_matcher_ = m;
+ return *this;
+ }
+
+ // Implements the .WillByDefault() clause.
+ OnCallSpec& WillByDefault(const Action<F>& action) {
+ ExpectSpecProperty(last_clause_ < kWillByDefault,
+ ".WillByDefault() must appear "
+ "exactly once in an ON_CALL().");
+ last_clause_ = kWillByDefault;
+
+ ExpectSpecProperty(!action.IsDoDefault(),
+ "DoDefault() cannot be used in ON_CALL().");
+ action_ = action;
+ return *this;
+ }
+
+ // Returns true iff the given arguments match the matchers.
+ bool Matches(const ArgumentTuple& args) const {
+ return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
+ }
+
+ // Returns the action specified by the user.
+ const Action<F>& GetAction() const {
+ AssertSpecProperty(last_clause_ == kWillByDefault,
+ ".WillByDefault() must appear exactly "
+ "once in an ON_CALL().");
+ return action_;
+ }
+
+ private:
+ // The information in statement
+ //
+ // ON_CALL(mock_object, Method(matchers))
+ // .With(multi-argument-matcher)
+ // .WillByDefault(action);
+ //
+ // is recorded in the data members like this:
+ //
+ // source file that contains the statement => file_
+ // line number of the statement => line_
+ // matchers => matchers_
+ // multi-argument-matcher => extra_matcher_
+ // action => action_
+ ArgumentMatcherTuple matchers_;
+ Matcher<const ArgumentTuple&> extra_matcher_;
+ Action<F> action_;
+}; // class OnCallSpec
+
+// Possible reactions on uninteresting calls.
+enum CallReaction {
+ kAllow,
+ kWarn,
+ kFail,
+ kDefault = kWarn // By default, warn about uninteresting calls.
+};
+
+} // namespace internal
+
+// Utilities for manipulating mock objects.
+class GTEST_API_ Mock {
+ public:
+ // The following public methods can be called concurrently.
+
+ // Tells Google Mock to ignore mock_obj when checking for leaked
+ // mock objects.
+ static void AllowLeak(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Verifies and clears all expectations on the given mock object.
+ // If the expectations aren't satisfied, generates one or more
+ // Google Test non-fatal failures and returns false.
+ static bool VerifyAndClearExpectations(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Verifies all expectations on the given mock object and clears its
+ // default actions and expectations. Returns true iff the
+ // verification was successful.
+ static bool VerifyAndClear(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ private:
+ friend class internal::UntypedFunctionMockerBase;
+
+ // Needed for a function mocker to register itself (so that we know
+ // how to clear a mock object).
+ template <typename F>
+ friend class internal::FunctionMockerBase;
+
+ template <typename M>
+ friend class NiceMock;
+
+ template <typename M>
+ friend class NaggyMock;
+
+ template <typename M>
+ friend class StrictMock;
+
+ // Tells Google Mock to allow uninteresting calls on the given mock
+ // object.
+ static void AllowUninterestingCalls(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Tells Google Mock to warn the user about uninteresting calls on
+ // the given mock object.
+ static void WarnUninterestingCalls(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Tells Google Mock to fail uninteresting calls on the given mock
+ // object.
+ static void FailUninterestingCalls(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Tells Google Mock the given mock object is being destroyed and
+ // its entry in the call-reaction table should be removed.
+ static void UnregisterCallReaction(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Returns the reaction Google Mock will have on uninteresting calls
+ // made on the given mock object.
+ static internal::CallReaction GetReactionOnUninterestingCalls(
+ const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Verifies that all expectations on the given mock object have been
+ // satisfied. Reports one or more Google Test non-fatal failures
+ // and returns false if not.
+ static bool VerifyAndClearExpectationsLocked(void* mock_obj)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
+
+ // Clears all ON_CALL()s set on the given mock object.
+ static void ClearDefaultActionsLocked(void* mock_obj)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
+
+ // Registers a mock object and a mock method it owns.
+ static void Register(
+ const void* mock_obj,
+ internal::UntypedFunctionMockerBase* mocker)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Tells Google Mock where in the source code mock_obj is used in an
+ // ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this
+ // information helps the user identify which object it is.
+ static void RegisterUseByOnCallOrExpectCall(
+ const void* mock_obj, const char* file, int line)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ // Unregisters a mock method; removes the owning mock object from
+ // the registry when the last mock method associated with it has
+ // been unregistered. This is called only in the destructor of
+ // FunctionMockerBase.
+ static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
+}; // class Mock
+
+// An abstract handle of an expectation. Useful in the .After()
+// clause of EXPECT_CALL() for setting the (partial) order of
+// expectations. The syntax:
+//
+// Expectation e1 = EXPECT_CALL(...)...;
+// EXPECT_CALL(...).After(e1)...;
+//
+// sets two expectations where the latter can only be matched after
+// the former has been satisfied.
+//
+// Notes:
+// - This class is copyable and has value semantics.
+// - Constness is shallow: a const Expectation object itself cannot
+// be modified, but the mutable methods of the ExpectationBase
+// object it references can be called via expectation_base().
+// - The constructors and destructor are defined out-of-line because
+// the Symbian WINSCW compiler wants to otherwise instantiate them
+// when it sees this class definition, at which point it doesn't have
+// ExpectationBase available yet, leading to incorrect destruction
+// in the linked_ptr (or compilation errors if using a checking
+// linked_ptr).
+class GTEST_API_ Expectation {
+ public:
+ // Constructs a null object that doesn't reference any expectation.
+ Expectation();
+
+ ~Expectation();
+
+ // This single-argument ctor must not be explicit, in order to support the
+ // Expectation e = EXPECT_CALL(...);
+ // syntax.
+ //
+ // A TypedExpectation object stores its pre-requisites as
+ // Expectation objects, and needs to call the non-const Retire()
+ // method on the ExpectationBase objects they reference. Therefore
+ // Expectation must receive a *non-const* reference to the
+ // ExpectationBase object.
+ Expectation(internal::ExpectationBase& exp); // NOLINT
+
+ // The compiler-generated copy ctor and operator= work exactly as
+ // intended, so we don't need to define our own.
+
+ // Returns true iff rhs references the same expectation as this object does.
+ bool operator==(const Expectation& rhs) const {
+ return expectation_base_ == rhs.expectation_base_;
+ }
+
+ bool operator!=(const Expectation& rhs) const { return !(*this == rhs); }
+
+ private:
+ friend class ExpectationSet;
+ friend class Sequence;
+ friend class ::testing::internal::ExpectationBase;
+ friend class ::testing::internal::UntypedFunctionMockerBase;
+
+ template <typename F>
+ friend class ::testing::internal::FunctionMockerBase;
+
+ template <typename F>
+ friend class ::testing::internal::TypedExpectation;
+
+ // This comparator is needed for putting Expectation objects into a set.
+ class Less {
+ public:
+ bool operator()(const Expectation& lhs, const Expectation& rhs) const {
+ return lhs.expectation_base_.get() < rhs.expectation_base_.get();
+ }
+ };
+
+ typedef ::std::set<Expectation, Less> Set;
+
+ Expectation(
+ const internal::linked_ptr<internal::ExpectationBase>& expectation_base);
+
+ // Returns the expectation this object references.
+ const internal::linked_ptr<internal::ExpectationBase>&
+ expectation_base() const {
+ return expectation_base_;
+ }
+
+ // A linked_ptr that co-owns the expectation this handle references.
+ internal::linked_ptr<internal::ExpectationBase> expectation_base_;
+};
+
+// A set of expectation handles. Useful in the .After() clause of
+// EXPECT_CALL() for setting the (partial) order of expectations. The
+// syntax:
+//
+// ExpectationSet es;
+// es += EXPECT_CALL(...)...;
+// es += EXPECT_CALL(...)...;
+// EXPECT_CALL(...).After(es)...;
+//
+// sets three expectations where the last one can only be matched
+// after the first two have both been satisfied.
+//
+// This class is copyable and has value semantics.
+class ExpectationSet {
+ public:
+ // A bidirectional iterator that can read a const element in the set.
+ typedef Expectation::Set::const_iterator const_iterator;
+
+ // An object stored in the set. This is an alias of Expectation.
+ typedef Expectation::Set::value_type value_type;
+
+ // Constructs an empty set.
+ ExpectationSet() {}
+
+ // This single-argument ctor must not be explicit, in order to support the
+ // ExpectationSet es = EXPECT_CALL(...);
+ // syntax.
+ ExpectationSet(internal::ExpectationBase& exp) { // NOLINT
+ *this += Expectation(exp);
+ }
+
+ // This single-argument ctor implements implicit conversion from
+ // Expectation and thus must not be explicit. This allows either an
+ // Expectation or an ExpectationSet to be used in .After().
+ ExpectationSet(const Expectation& e) { // NOLINT
+ *this += e;
+ }
+
+ // The compiler-generator ctor and operator= works exactly as
+ // intended, so we don't need to define our own.
+
+ // Returns true iff rhs contains the same set of Expectation objects
+ // as this does.
+ bool operator==(const ExpectationSet& rhs) const {
+ return expectations_ == rhs.expectations_;
+ }
+
+ bool operator!=(const ExpectationSet& rhs) const { return !(*this == rhs); }
+
+ // Implements the syntax
+ // expectation_set += EXPECT_CALL(...);
+ ExpectationSet& operator+=(const Expectation& e) {
+ expectations_.insert(e);
+ return *this;
+ }
+
+ int size() const { return static_cast<int>(expectations_.size()); }
+
+ const_iterator begin() const { return expectations_.begin(); }
+ const_iterator end() const { return expectations_.end(); }
+
+ private:
+ Expectation::Set expectations_;
+};
+
+
+// Sequence objects are used by a user to specify the relative order
+// in which the expectations should match. They are copyable (we rely
+// on the compiler-defined copy constructor and assignment operator).
+class GTEST_API_ Sequence {
+ public:
+ // Constructs an empty sequence.
+ Sequence() : last_expectation_(new Expectation) {}
+
+ // Adds an expectation to this sequence. The caller must ensure
+ // that no other thread is accessing this Sequence object.
+ void AddExpectation(const Expectation& expectation) const;
+
+ private:
+ // The last expectation in this sequence. We use a linked_ptr here
+ // because Sequence objects are copyable and we want the copies to
+ // be aliases. The linked_ptr allows the copies to co-own and share
+ // the same Expectation object.
+ internal::linked_ptr<Expectation> last_expectation_;
+}; // class Sequence
+
+// An object of this type causes all EXPECT_CALL() statements
+// encountered in its scope to be put in an anonymous sequence. The
+// work is done in the constructor and destructor. You should only
+// create an InSequence object on the stack.
+//
+// The sole purpose for this class is to support easy definition of
+// sequential expectations, e.g.
+//
+// {
+// InSequence dummy; // The name of the object doesn't matter.
+//
+// // The following expectations must match in the order they appear.
+// EXPECT_CALL(a, Bar())...;
+// EXPECT_CALL(a, Baz())...;
+// ...
+// EXPECT_CALL(b, Xyz())...;
+// }
+//
+// You can create InSequence objects in multiple threads, as long as
+// they are used to affect different mock objects. The idea is that
+// each thread can create and set up its own mocks as if it's the only
+// thread. However, for clarity of your tests we recommend you to set
+// up mocks in the main thread unless you have a good reason not to do
+// so.
+class GTEST_API_ InSequence {
+ public:
+ InSequence();
+ ~InSequence();
+ private:
+ bool sequence_created_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(InSequence); // NOLINT
+} GTEST_ATTRIBUTE_UNUSED_;
+
+namespace internal {
+
+// Points to the implicit sequence introduced by a living InSequence
+// object (if any) in the current thread or NULL.
+GTEST_API_ extern ThreadLocal<Sequence*> g_gmock_implicit_sequence;
+
+// Base class for implementing expectations.
+//
+// There are two reasons for having a type-agnostic base class for
+// Expectation:
+//
+// 1. We need to store collections of expectations of different
+// types (e.g. all pre-requisites of a particular expectation, all
+// expectations in a sequence). Therefore these expectation objects
+// must share a common base class.
+//
+// 2. We can avoid binary code bloat by moving methods not depending
+// on the template argument of Expectation to the base class.
+//
+// This class is internal and mustn't be used by user code directly.
+class GTEST_API_ ExpectationBase {
+ public:
+ // source_text is the EXPECT_CALL(...) source that created this Expectation.
+ ExpectationBase(const char* file, int line, const string& source_text);
+
+ virtual ~ExpectationBase();
+
+ // Where in the source file was the expectation spec defined?
+ const char* file() const { return file_; }
+ int line() const { return line_; }
+ const char* source_text() const { return source_text_.c_str(); }
+ // Returns the cardinality specified in the expectation spec.
+ const Cardinality& cardinality() const { return cardinality_; }
+
+ // Describes the source file location of this expectation.
+ void DescribeLocationTo(::std::ostream* os) const {
+ *os << FormatFileLocation(file(), line()) << " ";
+ }
+
+ // Describes how many times a function call matching this
+ // expectation has occurred.
+ void DescribeCallCountTo(::std::ostream* os) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+ // If this mock method has an extra matcher (i.e. .With(matcher)),
+ // describes it to the ostream.
+ virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) = 0;
+
+ protected:
+ friend class ::testing::Expectation;
+ friend class UntypedFunctionMockerBase;
+
+ enum Clause {
+ // Don't change the order of the enum members!
+ kNone,
+ kWith,
+ kTimes,
+ kInSequence,
+ kAfter,
+ kWillOnce,
+ kWillRepeatedly,
+ kRetiresOnSaturation
+ };
+
+ typedef std::vector<const void*> UntypedActions;
+
+ // Returns an Expectation object that references and co-owns this
+ // expectation.
+ virtual Expectation GetHandle() = 0;
+
+ // Asserts that the EXPECT_CALL() statement has the given property.
+ void AssertSpecProperty(bool property, const string& failure_message) const {
+ Assert(property, file_, line_, failure_message);
+ }
+
+ // Expects that the EXPECT_CALL() statement has the given property.
+ void ExpectSpecProperty(bool property, const string& failure_message) const {
+ Expect(property, file_, line_, failure_message);
+ }
+
+ // Explicitly specifies the cardinality of this expectation. Used
+ // by the subclasses to implement the .Times() clause.
+ void SpecifyCardinality(const Cardinality& cardinality);
+
+ // Returns true iff the user specified the cardinality explicitly
+ // using a .Times().
+ bool cardinality_specified() const { return cardinality_specified_; }
+
+ // Sets the cardinality of this expectation spec.
+ void set_cardinality(const Cardinality& a_cardinality) {
+ cardinality_ = a_cardinality;
+ }
+
+ // The following group of methods should only be called after the
+ // EXPECT_CALL() statement, and only when g_gmock_mutex is held by
+ // the current thread.
+
+ // Retires all pre-requisites of this expectation.
+ void RetireAllPreRequisites()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+ // Returns true iff this expectation is retired.
+ bool is_retired() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ return retired_;
+ }
+
+ // Retires this expectation.
+ void Retire()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ retired_ = true;
+ }
+
+ // Returns true iff this expectation is satisfied.
+ bool IsSatisfied() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ return cardinality().IsSatisfiedByCallCount(call_count_);
+ }
+
+ // Returns true iff this expectation is saturated.
+ bool IsSaturated() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ return cardinality().IsSaturatedByCallCount(call_count_);
+ }
+
+ // Returns true iff this expectation is over-saturated.
+ bool IsOverSaturated() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ return cardinality().IsOverSaturatedByCallCount(call_count_);
+ }
+
+ // Returns true iff all pre-requisites of this expectation are satisfied.
+ bool AllPrerequisitesAreSatisfied() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+ // Adds unsatisfied pre-requisites of this expectation to 'result'.
+ void FindUnsatisfiedPrerequisites(ExpectationSet* result) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+ // Returns the number this expectation has been invoked.
+ int call_count() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ return call_count_;
+ }
+
+ // Increments the number this expectation has been invoked.
+ void IncrementCallCount()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ call_count_++;
+ }
+
+ // Checks the action count (i.e. the number of WillOnce() and
+ // WillRepeatedly() clauses) against the cardinality if this hasn't
+ // been done before. Prints a warning if there are too many or too
+ // few actions.
+ void CheckActionCountIfNotDone() const
+ GTEST_LOCK_EXCLUDED_(mutex_);
+
+ friend class ::testing::Sequence;
+ friend class ::testing::internal::ExpectationTester;
+
+ template <typename Function>
+ friend class TypedExpectation;
+
+ // Implements the .Times() clause.
+ void UntypedTimes(const Cardinality& a_cardinality);
+
+ // This group of fields are part of the spec and won't change after
+ // an EXPECT_CALL() statement finishes.
+ const char* file_; // The file that contains the expectation.
+ int line_; // The line number of the expectation.
+ const string source_text_; // The EXPECT_CALL(...) source text.
+ // True iff the cardinality is specified explicitly.
+ bool cardinality_specified_;
+ Cardinality cardinality_; // The cardinality of the expectation.
+ // The immediate pre-requisites (i.e. expectations that must be
+ // satisfied before this expectation can be matched) of this
+ // expectation. We use linked_ptr in the set because we want an
+ // Expectation object to be co-owned by its FunctionMocker and its
+ // successors. This allows multiple mock objects to be deleted at
+ // different times.
+ ExpectationSet immediate_prerequisites_;
+
+ // This group of fields are the current state of the expectation,
+ // and can change as the mock function is called.
+ int call_count_; // How many times this expectation has been invoked.
+ bool retired_; // True iff this expectation has retired.
+ UntypedActions untyped_actions_;
+ bool extra_matcher_specified_;
+ bool repeated_action_specified_; // True if a WillRepeatedly() was specified.
+ bool retires_on_saturation_;
+ Clause last_clause_;
+ mutable bool action_count_checked_; // Under mutex_.
+ mutable Mutex mutex_; // Protects action_count_checked_.
+
+ GTEST_DISALLOW_ASSIGN_(ExpectationBase);
+}; // class ExpectationBase
+
+// Impements an expectation for the given function type.
+template <typename F>
+class TypedExpectation : public ExpectationBase {
+ public:
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+ typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+ typedef typename Function<F>::Result Result;
+
+ TypedExpectation(FunctionMockerBase<F>* owner,
+ const char* a_file, int a_line, const string& a_source_text,
+ const ArgumentMatcherTuple& m)
+ : ExpectationBase(a_file, a_line, a_source_text),
+ owner_(owner),
+ matchers_(m),
+ // By default, extra_matcher_ should match anything. However,
+ // we cannot initialize it with _ as that triggers a compiler
+ // bug in Symbian's C++ compiler (cannot decide between two
+ // overloaded constructors of Matcher<const ArgumentTuple&>).
+ extra_matcher_(A<const ArgumentTuple&>()),
+ repeated_action_(DoDefault()) {}
+
+ virtual ~TypedExpectation() {
+ // Check the validity of the action count if it hasn't been done
+ // yet (for example, if the expectation was never used).
+ CheckActionCountIfNotDone();
+ for (UntypedActions::const_iterator it = untyped_actions_.begin();
+ it != untyped_actions_.end(); ++it) {
+ delete static_cast<const Action<F>*>(*it);
+ }
+ }
+
+ // Implements the .With() clause.
+ TypedExpectation& With(const Matcher<const ArgumentTuple&>& m) {
+ if (last_clause_ == kWith) {
+ ExpectSpecProperty(false,
+ ".With() cannot appear "
+ "more than once in an EXPECT_CALL().");
+ } else {
+ ExpectSpecProperty(last_clause_ < kWith,
+ ".With() must be the first "
+ "clause in an EXPECT_CALL().");
+ }
+ last_clause_ = kWith;
+
+ extra_matcher_ = m;
+ extra_matcher_specified_ = true;
+ return *this;
+ }
+
+ // Implements the .Times() clause.
+ TypedExpectation& Times(const Cardinality& a_cardinality) {
+ ExpectationBase::UntypedTimes(a_cardinality);
+ return *this;
+ }
+
+ // Implements the .Times() clause.
+ TypedExpectation& Times(int n) {
+ return Times(Exactly(n));
+ }
+
+ // Implements the .InSequence() clause.
+ TypedExpectation& InSequence(const Sequence& s) {
+ ExpectSpecProperty(last_clause_ <= kInSequence,
+ ".InSequence() cannot appear after .After(),"
+ " .WillOnce(), .WillRepeatedly(), or "
+ ".RetiresOnSaturation().");
+ last_clause_ = kInSequence;
+
+ s.AddExpectation(GetHandle());
+ return *this;
+ }
+ TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2) {
+ return InSequence(s1).InSequence(s2);
+ }
+ TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+ const Sequence& s3) {
+ return InSequence(s1, s2).InSequence(s3);
+ }
+ TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+ const Sequence& s3, const Sequence& s4) {
+ return InSequence(s1, s2, s3).InSequence(s4);
+ }
+ TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+ const Sequence& s3, const Sequence& s4,
+ const Sequence& s5) {
+ return InSequence(s1, s2, s3, s4).InSequence(s5);
+ }
+
+ // Implements that .After() clause.
+ TypedExpectation& After(const ExpectationSet& s) {
+ ExpectSpecProperty(last_clause_ <= kAfter,
+ ".After() cannot appear after .WillOnce(),"
+ " .WillRepeatedly(), or "
+ ".RetiresOnSaturation().");
+ last_clause_ = kAfter;
+
+ for (ExpectationSet::const_iterator it = s.begin(); it != s.end(); ++it) {
+ immediate_prerequisites_ += *it;
+ }
+ return *this;
+ }
+ TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2) {
+ return After(s1).After(s2);
+ }
+ TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+ const ExpectationSet& s3) {
+ return After(s1, s2).After(s3);
+ }
+ TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+ const ExpectationSet& s3, const ExpectationSet& s4) {
+ return After(s1, s2, s3).After(s4);
+ }
+ TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+ const ExpectationSet& s3, const ExpectationSet& s4,
+ const ExpectationSet& s5) {
+ return After(s1, s2, s3, s4).After(s5);
+ }
+
+ // Implements the .WillOnce() clause.
+ TypedExpectation& WillOnce(const Action<F>& action) {
+ ExpectSpecProperty(last_clause_ <= kWillOnce,
+ ".WillOnce() cannot appear after "
+ ".WillRepeatedly() or .RetiresOnSaturation().");
+ last_clause_ = kWillOnce;
+
+ untyped_actions_.push_back(new Action<F>(action));
+ if (!cardinality_specified()) {
+ set_cardinality(Exactly(static_cast<int>(untyped_actions_.size())));
+ }
+ return *this;
+ }
+
+ // Implements the .WillRepeatedly() clause.
+ TypedExpectation& WillRepeatedly(const Action<F>& action) {
+ if (last_clause_ == kWillRepeatedly) {
+ ExpectSpecProperty(false,
+ ".WillRepeatedly() cannot appear "
+ "more than once in an EXPECT_CALL().");
+ } else {
+ ExpectSpecProperty(last_clause_ < kWillRepeatedly,
+ ".WillRepeatedly() cannot appear "
+ "after .RetiresOnSaturation().");
+ }
+ last_clause_ = kWillRepeatedly;
+ repeated_action_specified_ = true;
+
+ repeated_action_ = action;
+ if (!cardinality_specified()) {
+ set_cardinality(AtLeast(static_cast<int>(untyped_actions_.size())));
+ }
+
+ // Now that no more action clauses can be specified, we check
+ // whether their count makes sense.
+ CheckActionCountIfNotDone();
+ return *this;
+ }
+
+ // Implements the .RetiresOnSaturation() clause.
+ TypedExpectation& RetiresOnSaturation() {
+ ExpectSpecProperty(last_clause_ < kRetiresOnSaturation,
+ ".RetiresOnSaturation() cannot appear "
+ "more than once.");
+ last_clause_ = kRetiresOnSaturation;
+ retires_on_saturation_ = true;
+
+ // Now that no more action clauses can be specified, we check
+ // whether their count makes sense.
+ CheckActionCountIfNotDone();
+ return *this;
+ }
+
+ // Returns the matchers for the arguments as specified inside the
+ // EXPECT_CALL() macro.
+ const ArgumentMatcherTuple& matchers() const {
+ return matchers_;
+ }
+
+ // Returns the matcher specified by the .With() clause.
+ const Matcher<const ArgumentTuple&>& extra_matcher() const {
+ return extra_matcher_;
+ }
+
+ // Returns the action specified by the .WillRepeatedly() clause.
+ const Action<F>& repeated_action() const { return repeated_action_; }
+
+ // If this mock method has an extra matcher (i.e. .With(matcher)),
+ // describes it to the ostream.
+ virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) {
+ if (extra_matcher_specified_) {
+ *os << " Expected args: ";
+ extra_matcher_.DescribeTo(os);
+ *os << "\n";
+ }
+ }
+
+ private:
+ template <typename Function>
+ friend class FunctionMockerBase;
+
+ // Returns an Expectation object that references and co-owns this
+ // expectation.
+ virtual Expectation GetHandle() {
+ return owner_->GetHandleOf(this);
+ }
+
+ // The following methods will be called only after the EXPECT_CALL()
+ // statement finishes and when the current thread holds
+ // g_gmock_mutex.
+
+ // Returns true iff this expectation matches the given arguments.
+ bool Matches(const ArgumentTuple& args) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
+ }
+
+ // Returns true iff this expectation should handle the given arguments.
+ bool ShouldHandleArguments(const ArgumentTuple& args) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+
+ // In case the action count wasn't checked when the expectation
+ // was defined (e.g. if this expectation has no WillRepeatedly()
+ // or RetiresOnSaturation() clause), we check it when the
+ // expectation is used for the first time.
+ CheckActionCountIfNotDone();
+ return !is_retired() && AllPrerequisitesAreSatisfied() && Matches(args);
+ }
+
+ // Describes the result of matching the arguments against this
+ // expectation to the given ostream.
+ void ExplainMatchResultTo(
+ const ArgumentTuple& args,
+ ::std::ostream* os) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+
+ if (is_retired()) {
+ *os << " Expected: the expectation is active\n"
+ << " Actual: it is retired\n";
+ } else if (!Matches(args)) {
+ if (!TupleMatches(matchers_, args)) {
+ ExplainMatchFailureTupleTo(matchers_, args, os);
+ }
+ StringMatchResultListener listener;
+ if (!extra_matcher_.MatchAndExplain(args, &listener)) {
+ *os << " Expected args: ";
+ extra_matcher_.DescribeTo(os);
+ *os << "\n Actual: don't match";
+
+ internal::PrintIfNotEmpty(listener.str(), os);
+ *os << "\n";
+ }
+ } else if (!AllPrerequisitesAreSatisfied()) {
+ *os << " Expected: all pre-requisites are satisfied\n"
+ << " Actual: the following immediate pre-requisites "
+ << "are not satisfied:\n";
+ ExpectationSet unsatisfied_prereqs;
+ FindUnsatisfiedPrerequisites(&unsatisfied_prereqs);
+ int i = 0;
+ for (ExpectationSet::const_iterator it = unsatisfied_prereqs.begin();
+ it != unsatisfied_prereqs.end(); ++it) {
+ it->expectation_base()->DescribeLocationTo(os);
+ *os << "pre-requisite #" << i++ << "\n";
+ }
+ *os << " (end of pre-requisites)\n";
+ } else {
+ // This line is here just for completeness' sake. It will never
+ // be executed as currently the ExplainMatchResultTo() function
+ // is called only when the mock function call does NOT match the
+ // expectation.
+ *os << "The call matches the expectation.\n";
+ }
+ }
+
+ // Returns the action that should be taken for the current invocation.
+ const Action<F>& GetCurrentAction(
+ const FunctionMockerBase<F>* mocker,
+ const ArgumentTuple& args) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ const int count = call_count();
+ Assert(count >= 1, __FILE__, __LINE__,
+ "call_count() is <= 0 when GetCurrentAction() is "
+ "called - this should never happen.");
+
+ const int action_count = static_cast<int>(untyped_actions_.size());
+ if (action_count > 0 && !repeated_action_specified_ &&
+ count > action_count) {
+ // If there is at least one WillOnce() and no WillRepeatedly(),
+ // we warn the user when the WillOnce() clauses ran out.
+ ::std::stringstream ss;
+ DescribeLocationTo(&ss);
+ ss << "Actions ran out in " << source_text() << "...\n"
+ << "Called " << count << " times, but only "
+ << action_count << " WillOnce()"
+ << (action_count == 1 ? " is" : "s are") << " specified - ";
+ mocker->DescribeDefaultActionTo(args, &ss);
+ Log(kWarning, ss.str(), 1);
+ }
+
+ return count <= action_count ?
+ *static_cast<const Action<F>*>(untyped_actions_[count - 1]) :
+ repeated_action();
+ }
+
+ // Given the arguments of a mock function call, if the call will
+ // over-saturate this expectation, returns the default action;
+ // otherwise, returns the next action in this expectation. Also
+ // describes *what* happened to 'what', and explains *why* Google
+ // Mock does it to 'why'. This method is not const as it calls
+ // IncrementCallCount(). A return value of NULL means the default
+ // action.
+ const Action<F>* GetActionForArguments(
+ const FunctionMockerBase<F>* mocker,
+ const ArgumentTuple& args,
+ ::std::ostream* what,
+ ::std::ostream* why)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ if (IsSaturated()) {
+ // We have an excessive call.
+ IncrementCallCount();
+ *what << "Mock function called more times than expected - ";
+ mocker->DescribeDefaultActionTo(args, what);
+ DescribeCallCountTo(why);
+
+ // TODO(wan@google.com): allow the user to control whether
+ // unexpected calls should fail immediately or continue using a
+ // flag --gmock_unexpected_calls_are_fatal.
+ return NULL;
+ }
+
+ IncrementCallCount();
+ RetireAllPreRequisites();
+
+ if (retires_on_saturation_ && IsSaturated()) {
+ Retire();
+ }
+
+ // Must be done after IncrementCount()!
+ *what << "Mock function call matches " << source_text() <<"...\n";
+ return &(GetCurrentAction(mocker, args));
+ }
+
+ // All the fields below won't change once the EXPECT_CALL()
+ // statement finishes.
+ FunctionMockerBase<F>* const owner_;
+ ArgumentMatcherTuple matchers_;
+ Matcher<const ArgumentTuple&> extra_matcher_;
+ Action<F> repeated_action_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(TypedExpectation);
+}; // class TypedExpectation
+
+// A MockSpec object is used by ON_CALL() or EXPECT_CALL() for
+// specifying the default behavior of, or expectation on, a mock
+// function.
+
+// Note: class MockSpec really belongs to the ::testing namespace.
+// However if we define it in ::testing, MSVC will complain when
+// classes in ::testing::internal declare it as a friend class
+// template. To workaround this compiler bug, we define MockSpec in
+// ::testing::internal and import it into ::testing.
+
+// Logs a message including file and line number information.
+GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity,
+ const char* file, int line,
+ const string& message);
+
+template <typename F>
+class MockSpec {
+ public:
+ typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+ typedef typename internal::Function<F>::ArgumentMatcherTuple
+ ArgumentMatcherTuple;
+
+ // Constructs a MockSpec object, given the function mocker object
+ // that the spec is associated with.
+ explicit MockSpec(internal::FunctionMockerBase<F>* function_mocker)
+ : function_mocker_(function_mocker) {}
+
+ // Adds a new default action spec to the function mocker and returns
+ // the newly created spec.
+ internal::OnCallSpec<F>& InternalDefaultActionSetAt(
+ const char* file, int line, const char* obj, const char* call) {
+ LogWithLocation(internal::kInfo, file, line,
+ string("ON_CALL(") + obj + ", " + call + ") invoked");
+ return function_mocker_->AddNewOnCallSpec(file, line, matchers_);
+ }
+
+ // Adds a new expectation spec to the function mocker and returns
+ // the newly created spec.
+ internal::TypedExpectation<F>& InternalExpectedAt(
+ const char* file, int line, const char* obj, const char* call) {
+ const string source_text(string("EXPECT_CALL(") + obj + ", " + call + ")");
+ LogWithLocation(internal::kInfo, file, line, source_text + " invoked");
+ return function_mocker_->AddNewExpectation(
+ file, line, source_text, matchers_);
+ }
+
+ private:
+ template <typename Function>
+ friend class internal::FunctionMocker;
+
+ void SetMatchers(const ArgumentMatcherTuple& matchers) {
+ matchers_ = matchers;
+ }
+
+ // The function mocker that owns this spec.
+ internal::FunctionMockerBase<F>* const function_mocker_;
+ // The argument matchers specified in the spec.
+ ArgumentMatcherTuple matchers_;
+
+ GTEST_DISALLOW_ASSIGN_(MockSpec);
+}; // class MockSpec
+
+// Wrapper type for generically holding an ordinary value or lvalue reference.
+// If T is not a reference type, it must be copyable or movable.
+// ReferenceOrValueWrapper<T> is movable, and will also be copyable unless
+// T is a move-only value type (which means that it will always be copyable
+// if the current platform does not support move semantics).
+//
+// The primary template defines handling for values, but function header
+// comments describe the contract for the whole template (including
+// specializations).
+template <typename T>
+class ReferenceOrValueWrapper {
+ public:
+ // Constructs a wrapper from the given value/reference.
+ explicit ReferenceOrValueWrapper(T value)
+ : value_(::testing::internal::move(value)) {
+ }
+
+ // Unwraps and returns the underlying value/reference, exactly as
+ // originally passed. The behavior of calling this more than once on
+ // the same object is unspecified.
+ T Unwrap() { return ::testing::internal::move(value_); }
+
+ // Provides nondestructive access to the underlying value/reference.
+ // Always returns a const reference (more precisely,
+ // const RemoveReference<T>&). The behavior of calling this after
+ // calling Unwrap on the same object is unspecified.
+ const T& Peek() const {
+ return value_;
+ }
+
+ private:
+ T value_;
+};
+
+// Specialization for lvalue reference types. See primary template
+// for documentation.
+template <typename T>
+class ReferenceOrValueWrapper<T&> {
+ public:
+ // Workaround for debatable pass-by-reference lint warning (c-library-team
+ // policy precludes NOLINT in this context)
+ typedef T& reference;
+ explicit ReferenceOrValueWrapper(reference ref)
+ : value_ptr_(&ref) {}
+ T& Unwrap() { return *value_ptr_; }
+ const T& Peek() const { return *value_ptr_; }
+
+ private:
+ T* value_ptr_;
+};
+
+// MSVC warns about using 'this' in base member initializer list, so
+// we need to temporarily disable the warning. We have to do it for
+// the entire class to suppress the warning, even though it's about
+// the constructor only.
+
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4355) // Temporarily disables warning 4355.
+#endif // _MSV_VER
+
+// C++ treats the void type specially. For example, you cannot define
+// a void-typed variable or pass a void value to a function.
+// ActionResultHolder<T> holds a value of type T, where T must be a
+// copyable type or void (T doesn't need to be default-constructable).
+// It hides the syntactic difference between void and other types, and
+// is used to unify the code for invoking both void-returning and
+// non-void-returning mock functions.
+
+// Untyped base class for ActionResultHolder<T>.
+class UntypedActionResultHolderBase {
+ public:
+ virtual ~UntypedActionResultHolderBase() {}
+
+ // Prints the held value as an action's result to os.
+ virtual void PrintAsActionResult(::std::ostream* os) const = 0;
+};
+
+// This generic definition is used when T is not void.
+template <typename T>
+class ActionResultHolder : public UntypedActionResultHolderBase {
+ public:
+ // Returns the held value. Must not be called more than once.
+ T Unwrap() {
+ return result_.Unwrap();
+ }
+
+ // Prints the held value as an action's result to os.
+ virtual void PrintAsActionResult(::std::ostream* os) const {
+ *os << "\n Returns: ";
+ // T may be a reference type, so we don't use UniversalPrint().
+ UniversalPrinter<T>::Print(result_.Peek(), os);
+ }
+
+ // Performs the given mock function's default action and returns the
+ // result in a new-ed ActionResultHolder.
+ template <typename F>
+ static ActionResultHolder* PerformDefaultAction(
+ const FunctionMockerBase<F>* func_mocker,
+ const typename Function<F>::ArgumentTuple& args,
+ const string& call_description) {
+ return new ActionResultHolder(Wrapper(
+ func_mocker->PerformDefaultAction(args, call_description)));
+ }
+
+ // Performs the given action and returns the result in a new-ed
+ // ActionResultHolder.
+ template <typename F>
+ static ActionResultHolder*
+ PerformAction(const Action<F>& action,
+ const typename Function<F>::ArgumentTuple& args) {
+ return new ActionResultHolder(Wrapper(action.Perform(args)));
+ }
+
+ private:
+ typedef ReferenceOrValueWrapper<T> Wrapper;
+
+ explicit ActionResultHolder(Wrapper result)
+ : result_(::testing::internal::move(result)) {
+ }
+
+ Wrapper result_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
+};
+
+// Specialization for T = void.
+template <>
+class ActionResultHolder<void> : public UntypedActionResultHolderBase {
+ public:
+ void Unwrap() { }
+
+ virtual void PrintAsActionResult(::std::ostream* /* os */) const {}
+
+ // Performs the given mock function's default action and returns ownership
+ // of an empty ActionResultHolder*.
+ template <typename F>
+ static ActionResultHolder* PerformDefaultAction(
+ const FunctionMockerBase<F>* func_mocker,
+ const typename Function<F>::ArgumentTuple& args,
+ const string& call_description) {
+ func_mocker->PerformDefaultAction(args, call_description);
+ return new ActionResultHolder;
+ }
+
+ // Performs the given action and returns ownership of an empty
+ // ActionResultHolder*.
+ template <typename F>
+ static ActionResultHolder* PerformAction(
+ const Action<F>& action,
+ const typename Function<F>::ArgumentTuple& args) {
+ action.Perform(args);
+ return new ActionResultHolder;
+ }
+
+ private:
+ ActionResultHolder() {}
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
+};
+
+// The base of the function mocker class for the given function type.
+// We put the methods in this class instead of its child to avoid code
+// bloat.
+template <typename F>
+class FunctionMockerBase : public UntypedFunctionMockerBase {
+ public:
+ typedef typename Function<F>::Result Result;
+ typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+ typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+
+ FunctionMockerBase() : current_spec_(this) {}
+
+ // The destructor verifies that all expectations on this mock
+ // function have been satisfied. If not, it will report Google Test
+ // non-fatal failures for the violations.
+ virtual ~FunctionMockerBase()
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ MutexLock l(&g_gmock_mutex);
+ VerifyAndClearExpectationsLocked();
+ Mock::UnregisterLocked(this);
+ ClearDefaultActionsLocked();
+ }
+
+ // Returns the ON_CALL spec that matches this mock function with the
+ // given arguments; returns NULL if no matching ON_CALL is found.
+ // L = *
+ const OnCallSpec<F>* FindOnCallSpec(
+ const ArgumentTuple& args) const {
+ for (UntypedOnCallSpecs::const_reverse_iterator it
+ = untyped_on_call_specs_.rbegin();
+ it != untyped_on_call_specs_.rend(); ++it) {
+ const OnCallSpec<F>* spec = static_cast<const OnCallSpec<F>*>(*it);
+ if (spec->Matches(args))
+ return spec;
+ }
+
+ return NULL;
+ }
+
+ // Performs the default action of this mock function on the given
+ // arguments and returns the result. Asserts (or throws if
+ // exceptions are enabled) with a helpful call descrption if there
+ // is no valid return value. This method doesn't depend on the
+ // mutable state of this object, and thus can be called concurrently
+ // without locking.
+ // L = *
+ Result PerformDefaultAction(const ArgumentTuple& args,
+ const string& call_description) const {
+ const OnCallSpec<F>* const spec =
+ this->FindOnCallSpec(args);
+ if (spec != NULL) {
+ return spec->GetAction().Perform(args);
+ }
+ const string message = call_description +
+ "\n The mock function has no default action "
+ "set, and its return type has no default value set.";
+#if GTEST_HAS_EXCEPTIONS
+ if (!DefaultValue<Result>::Exists()) {
+ throw std::runtime_error(message);
+ }
+#else
+ Assert(DefaultValue<Result>::Exists(), "", -1, message);
+#endif
+ return DefaultValue<Result>::Get();
+ }
+
+ // Performs the default action with the given arguments and returns
+ // the action's result. The call description string will be used in
+ // the error message to describe the call in the case the default
+ // action fails. The caller is responsible for deleting the result.
+ // L = *
+ virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction(
+ const void* untyped_args, // must point to an ArgumentTuple
+ const string& call_description) const {
+ const ArgumentTuple& args =
+ *static_cast<const ArgumentTuple*>(untyped_args);
+ return ResultHolder::PerformDefaultAction(this, args, call_description);
+ }
+
+ // Performs the given action with the given arguments and returns
+ // the action's result. The caller is responsible for deleting the
+ // result.
+ // L = *
+ virtual UntypedActionResultHolderBase* UntypedPerformAction(
+ const void* untyped_action, const void* untyped_args) const {
+ // Make a copy of the action before performing it, in case the
+ // action deletes the mock object (and thus deletes itself).
+ const Action<F> action = *static_cast<const Action<F>*>(untyped_action);
+ const ArgumentTuple& args =
+ *static_cast<const ArgumentTuple*>(untyped_args);
+ return ResultHolder::PerformAction(action, args);
+ }
+
+ // Implements UntypedFunctionMockerBase::ClearDefaultActionsLocked():
+ // clears the ON_CALL()s set on this mock function.
+ virtual void ClearDefaultActionsLocked()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+
+ // Deleting our default actions may trigger other mock objects to be
+ // deleted, for example if an action contains a reference counted smart
+ // pointer to that mock object, and that is the last reference. So if we
+ // delete our actions within the context of the global mutex we may deadlock
+ // when this method is called again. Instead, make a copy of the set of
+ // actions to delete, clear our set within the mutex, and then delete the
+ // actions outside of the mutex.
+ UntypedOnCallSpecs specs_to_delete;
+ untyped_on_call_specs_.swap(specs_to_delete);
+
+ g_gmock_mutex.Unlock();
+ for (UntypedOnCallSpecs::const_iterator it =
+ specs_to_delete.begin();
+ it != specs_to_delete.end(); ++it) {
+ delete static_cast<const OnCallSpec<F>*>(*it);
+ }
+
+ // Lock the mutex again, since the caller expects it to be locked when we
+ // return.
+ g_gmock_mutex.Lock();
+ }
+
+ protected:
+ template <typename Function>
+ friend class MockSpec;
+
+ typedef ActionResultHolder<Result> ResultHolder;
+
+ // Returns the result of invoking this mock function with the given
+ // arguments. This function can be safely called from multiple
+ // threads concurrently.
+ Result InvokeWith(const ArgumentTuple& args)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ scoped_ptr<ResultHolder> holder(
+ DownCast_<ResultHolder*>(this->UntypedInvokeWith(&args)));
+ return holder->Unwrap();
+ }
+
+ // Adds and returns a default action spec for this mock function.
+ OnCallSpec<F>& AddNewOnCallSpec(
+ const char* file, int line,
+ const ArgumentMatcherTuple& m)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
+ OnCallSpec<F>* const on_call_spec = new OnCallSpec<F>(file, line, m);
+ untyped_on_call_specs_.push_back(on_call_spec);
+ return *on_call_spec;
+ }
+
+ // Adds and returns an expectation spec for this mock function.
+ TypedExpectation<F>& AddNewExpectation(
+ const char* file,
+ int line,
+ const string& source_text,
+ const ArgumentMatcherTuple& m)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
+ TypedExpectation<F>* const expectation =
+ new TypedExpectation<F>(this, file, line, source_text, m);
+ const linked_ptr<ExpectationBase> untyped_expectation(expectation);
+ untyped_expectations_.push_back(untyped_expectation);
+
+ // Adds this expectation into the implicit sequence if there is one.
+ Sequence* const implicit_sequence = g_gmock_implicit_sequence.get();
+ if (implicit_sequence != NULL) {
+ implicit_sequence->AddExpectation(Expectation(untyped_expectation));
+ }
+
+ return *expectation;
+ }
+
+ // The current spec (either default action spec or expectation spec)
+ // being described on this function mocker.
+ MockSpec<F>& current_spec() { return current_spec_; }
+
+ private:
+ template <typename Func> friend class TypedExpectation;
+
+ // Some utilities needed for implementing UntypedInvokeWith().
+
+ // Describes what default action will be performed for the given
+ // arguments.
+ // L = *
+ void DescribeDefaultActionTo(const ArgumentTuple& args,
+ ::std::ostream* os) const {
+ const OnCallSpec<F>* const spec = FindOnCallSpec(args);
+
+ if (spec == NULL) {
+ *os << (internal::type_equals<Result, void>::value ?
+ "returning directly.\n" :
+ "returning default value.\n");
+ } else {
+ *os << "taking default action specified at:\n"
+ << FormatFileLocation(spec->file(), spec->line()) << "\n";
+ }
+ }
+
+ // Writes a message that the call is uninteresting (i.e. neither
+ // explicitly expected nor explicitly unexpected) to the given
+ // ostream.
+ virtual void UntypedDescribeUninterestingCall(
+ const void* untyped_args,
+ ::std::ostream* os) const
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ const ArgumentTuple& args =
+ *static_cast<const ArgumentTuple*>(untyped_args);
+ *os << "Uninteresting mock function call - ";
+ DescribeDefaultActionTo(args, os);
+ *os << " Function call: " << Name();
+ UniversalPrint(args, os);
+ }
+
+ // Returns the expectation that matches the given function arguments
+ // (or NULL is there's no match); when a match is found,
+ // untyped_action is set to point to the action that should be
+ // performed (or NULL if the action is "do default"), and
+ // is_excessive is modified to indicate whether the call exceeds the
+ // expected number.
+ //
+ // Critical section: We must find the matching expectation and the
+ // corresponding action that needs to be taken in an ATOMIC
+ // transaction. Otherwise another thread may call this mock
+ // method in the middle and mess up the state.
+ //
+ // However, performing the action has to be left out of the critical
+ // section. The reason is that we have no control on what the
+ // action does (it can invoke an arbitrary user function or even a
+ // mock function) and excessive locking could cause a dead lock.
+ virtual const ExpectationBase* UntypedFindMatchingExpectation(
+ const void* untyped_args,
+ const void** untyped_action, bool* is_excessive,
+ ::std::ostream* what, ::std::ostream* why)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ const ArgumentTuple& args =
+ *static_cast<const ArgumentTuple*>(untyped_args);
+ MutexLock l(&g_gmock_mutex);
+ TypedExpectation<F>* exp = this->FindMatchingExpectationLocked(args);
+ if (exp == NULL) { // A match wasn't found.
+ this->FormatUnexpectedCallMessageLocked(args, what, why);
+ return NULL;
+ }
+
+ // This line must be done before calling GetActionForArguments(),
+ // which will increment the call count for *exp and thus affect
+ // its saturation status.
+ *is_excessive = exp->IsSaturated();
+ const Action<F>* action = exp->GetActionForArguments(this, args, what, why);
+ if (action != NULL && action->IsDoDefault())
+ action = NULL; // Normalize "do default" to NULL.
+ *untyped_action = action;
+ return exp;
+ }
+
+ // Prints the given function arguments to the ostream.
+ virtual void UntypedPrintArgs(const void* untyped_args,
+ ::std::ostream* os) const {
+ const ArgumentTuple& args =
+ *static_cast<const ArgumentTuple*>(untyped_args);
+ UniversalPrint(args, os);
+ }
+
+ // Returns the expectation that matches the arguments, or NULL if no
+ // expectation matches them.
+ TypedExpectation<F>* FindMatchingExpectationLocked(
+ const ArgumentTuple& args) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ for (typename UntypedExpectations::const_reverse_iterator it =
+ untyped_expectations_.rbegin();
+ it != untyped_expectations_.rend(); ++it) {
+ TypedExpectation<F>* const exp =
+ static_cast<TypedExpectation<F>*>(it->get());
+ if (exp->ShouldHandleArguments(args)) {
+ return exp;
+ }
+ }
+ return NULL;
+ }
+
+ // Returns a message that the arguments don't match any expectation.
+ void FormatUnexpectedCallMessageLocked(
+ const ArgumentTuple& args,
+ ::std::ostream* os,
+ ::std::ostream* why) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ *os << "\nUnexpected mock function call - ";
+ DescribeDefaultActionTo(args, os);
+ PrintTriedExpectationsLocked(args, why);
+ }
+
+ // Prints a list of expectations that have been tried against the
+ // current mock function call.
+ void PrintTriedExpectationsLocked(
+ const ArgumentTuple& args,
+ ::std::ostream* why) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ const int count = static_cast<int>(untyped_expectations_.size());
+ *why << "Google Mock tried the following " << count << " "
+ << (count == 1 ? "expectation, but it didn't match" :
+ "expectations, but none matched")
+ << ":\n";
+ for (int i = 0; i < count; i++) {
+ TypedExpectation<F>* const expectation =
+ static_cast<TypedExpectation<F>*>(untyped_expectations_[i].get());
+ *why << "\n";
+ expectation->DescribeLocationTo(why);
+ if (count > 1) {
+ *why << "tried expectation #" << i << ": ";
+ }
+ *why << expectation->source_text() << "...\n";
+ expectation->ExplainMatchResultTo(args, why);
+ expectation->DescribeCallCountTo(why);
+ }
+ }
+
+ // The current spec (either default action spec or expectation spec)
+ // being described on this function mocker.
+ MockSpec<F> current_spec_;
+
+ // There is no generally useful and implementable semantics of
+ // copying a mock object, so copying a mock is usually a user error.
+ // Thus we disallow copying function mockers. If the user really
+ // wants to copy a mock object, he should implement his own copy
+ // operation, for example:
+ //
+ // class MockFoo : public Foo {
+ // public:
+ // // Defines a copy constructor explicitly.
+ // MockFoo(const MockFoo& src) {}
+ // ...
+ // };
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(FunctionMockerBase);
+}; // class FunctionMockerBase
+
+#ifdef _MSC_VER
+# pragma warning(pop) // Restores the warning state.
+#endif // _MSV_VER
+
+// Implements methods of FunctionMockerBase.
+
+// Verifies that all expectations on this mock function have been
+// satisfied. Reports one or more Google Test non-fatal failures and
+// returns false if not.
+
+// Reports an uninteresting call (whose description is in msg) in the
+// manner specified by 'reaction'.
+void ReportUninterestingCall(CallReaction reaction, const string& msg);
+
+} // namespace internal
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file. However, the MockSpec class template is
+// meant to be defined in the ::testing namespace. The following line
+// is just a trick for working around a bug in MSVC 8.0, which cannot
+// handle it if we define MockSpec in ::testing.
+using internal::MockSpec;
+
+// Const(x) is a convenient function for obtaining a const reference
+// to x. This is useful for setting expectations on an overloaded
+// const mock method, e.g.
+//
+// class MockFoo : public FooInterface {
+// public:
+// MOCK_METHOD0(Bar, int());
+// MOCK_CONST_METHOD0(Bar, int&());
+// };
+//
+// MockFoo foo;
+// // Expects a call to non-const MockFoo::Bar().
+// EXPECT_CALL(foo, Bar());
+// // Expects a call to const MockFoo::Bar().
+// EXPECT_CALL(Const(foo), Bar());
+template <typename T>
+inline const T& Const(const T& x) { return x; }
+
+// Constructs an Expectation object that references and co-owns exp.
+inline Expectation::Expectation(internal::ExpectationBase& exp) // NOLINT
+ : expectation_base_(exp.GetHandle().expectation_base()) {}
+
+} // namespace testing
+
+// A separate macro is required to avoid compile errors when the name
+// of the method used in call is a result of macro expansion.
+// See CompilesWithMethodNameExpandedFromMacro tests in
+// internal/gmock-spec-builders_test.cc for more details.
+#define GMOCK_ON_CALL_IMPL_(obj, call) \
+ ((obj).gmock_##call).InternalDefaultActionSetAt(__FILE__, __LINE__, \
+ #obj, #call)
+#define ON_CALL(obj, call) GMOCK_ON_CALL_IMPL_(obj, call)
+
+#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
+ ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
+#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
diff --git a/extern/gmock/include/gmock/gmock.h b/extern/gmock/include/gmock/gmock.h
new file mode 100644
index 00000000000..6735c71bf8a
--- /dev/null
+++ b/extern/gmock/include/gmock/gmock.h
@@ -0,0 +1,94 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This is the main header file a user should include.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_H_
+
+// This file implements the following syntax:
+//
+// ON_CALL(mock_object.Method(...))
+// .With(...) ?
+// .WillByDefault(...);
+//
+// where With() is optional and WillByDefault() must appear exactly
+// once.
+//
+// EXPECT_CALL(mock_object.Method(...))
+// .With(...) ?
+// .Times(...) ?
+// .InSequence(...) *
+// .WillOnce(...) *
+// .WillRepeatedly(...) ?
+// .RetiresOnSaturation() ? ;
+//
+// where all clauses are optional and WillOnce() can be repeated.
+
+#include "gmock/gmock-actions.h"
+#include "gmock/gmock-cardinalities.h"
+#include "gmock/gmock-generated-actions.h"
+#include "gmock/gmock-generated-function-mockers.h"
+#include "gmock/gmock-generated-nice-strict.h"
+#include "gmock/gmock-generated-matchers.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-actions.h"
+#include "gmock/gmock-more-matchers.h"
+#include "gmock/internal/gmock-internal-utils.h"
+
+namespace testing {
+
+// Declares Google Mock flags that we want a user to use programmatically.
+GMOCK_DECLARE_bool_(catch_leaked_mocks);
+GMOCK_DECLARE_string_(verbose);
+
+// Initializes Google Mock. This must be called before running the
+// tests. In particular, it parses the command line for the flags
+// that Google Mock recognizes. Whenever a Google Mock flag is seen,
+// it is removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Mock flag variables are
+// updated.
+//
+// Since Google Test is needed for Google Mock to work, this function
+// also initializes Google Test and parses its flags, if that hasn't
+// been done.
+GTEST_API_ void InitGoogleMock(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv);
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_GMOCK_H_
diff --git a/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h b/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h
new file mode 100644
index 00000000000..7dc3b1ad541
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/custom/gmock-generated-actions.h
@@ -0,0 +1,8 @@
+// This file was GENERATED by command:
+// pump.py gmock-generated-actions.h.pump
+// DO NOT EDIT BY HAND!!!
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
diff --git a/extern/gmock/include/gmock/internal/custom/gmock-matchers.h b/extern/gmock/include/gmock/internal/custom/gmock-matchers.h
new file mode 100644
index 00000000000..f2efef91dbe
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/custom/gmock-matchers.h
@@ -0,0 +1,39 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ============================================================
+// An installation-specific extension point for gmock-matchers.h.
+// ============================================================
+//
+// Adds google3 callback support to CallableTraits.
+//
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_CALLBACK_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_CALLBACK_MATCHERS_H_
+
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_CALLBACK_MATCHERS_H_
diff --git a/extern/gmock/include/gmock/internal/custom/gmock-port.h b/extern/gmock/include/gmock/internal/custom/gmock-port.h
new file mode 100644
index 00000000000..9ce8bfe06bf
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/custom/gmock-port.h
@@ -0,0 +1,46 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations.
+// The following macros can be defined:
+//
+// Flag related macros:
+// GMOCK_DECLARE_bool_(name)
+// GMOCK_DECLARE_int32_(name)
+// GMOCK_DECLARE_string_(name)
+// GMOCK_DEFINE_bool_(name, default_val, doc)
+// GMOCK_DEFINE_int32_(name, default_val, doc)
+// GMOCK_DEFINE_string_(name, default_val, doc)
+//
+// ** Custom implementation starts here **
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
diff --git a/extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h b/extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h
new file mode 100644
index 00000000000..7811e43f87c
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/gmock-generated-internal-utils.h
@@ -0,0 +1,279 @@
+// This file was GENERATED by command:
+// pump.py gmock-generated-internal-utils.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file contains template meta-programming utility classes needed
+// for implementing Google Mock.
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
+
+#include "gmock/internal/gmock-port.h"
+
+namespace testing {
+
+template <typename T>
+class Matcher;
+
+namespace internal {
+
+// An IgnoredValue object can be implicitly constructed from ANY value.
+// This is used in implementing the IgnoreResult(a) action.
+class IgnoredValue {
+ public:
+ // This constructor template allows any value to be implicitly
+ // converted to IgnoredValue. The object has no data member and
+ // doesn't try to remember anything about the argument. We
+ // deliberately omit the 'explicit' keyword in order to allow the
+ // conversion to be implicit.
+ template <typename T>
+ IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit)
+};
+
+// MatcherTuple<T>::type is a tuple type where each field is a Matcher
+// for the corresponding field in tuple type T.
+template <typename Tuple>
+struct MatcherTuple;
+
+template <>
+struct MatcherTuple< ::testing::tuple<> > {
+ typedef ::testing::tuple< > type;
+};
+
+template <typename A1>
+struct MatcherTuple< ::testing::tuple<A1> > {
+ typedef ::testing::tuple<Matcher<A1> > type;
+};
+
+template <typename A1, typename A2>
+struct MatcherTuple< ::testing::tuple<A1, A2> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2> > type;
+};
+
+template <typename A1, typename A2, typename A3>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>,
+ Matcher<A4> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+ Matcher<A5> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+ Matcher<A5>, Matcher<A6> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+ Matcher<A5>, Matcher<A6>, Matcher<A7> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+ Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+ Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8>, Matcher<A9> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8, typename A9, typename A10>
+struct MatcherTuple< ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10> > {
+ typedef ::testing::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+ Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8>, Matcher<A9>,
+ Matcher<A10> > type;
+};
+
+// Template struct Function<F>, where F must be a function type, contains
+// the following typedefs:
+//
+// Result: the function's return type.
+// ArgumentN: the type of the N-th argument, where N starts with 1.
+// ArgumentTuple: the tuple type consisting of all parameters of F.
+// ArgumentMatcherTuple: the tuple type consisting of Matchers for all
+// parameters of F.
+// MakeResultVoid: the function type obtained by substituting void
+// for the return type of F.
+// MakeResultIgnoredValue:
+// the function type obtained by substituting Something
+// for the return type of F.
+template <typename F>
+struct Function;
+
+template <typename R>
+struct Function<R()> {
+ typedef R Result;
+ typedef ::testing::tuple<> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid();
+ typedef IgnoredValue MakeResultIgnoredValue();
+};
+
+template <typename R, typename A1>
+struct Function<R(A1)>
+ : Function<R()> {
+ typedef A1 Argument1;
+ typedef ::testing::tuple<A1> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1);
+ typedef IgnoredValue MakeResultIgnoredValue(A1);
+};
+
+template <typename R, typename A1, typename A2>
+struct Function<R(A1, A2)>
+ : Function<R(A1)> {
+ typedef A2 Argument2;
+ typedef ::testing::tuple<A1, A2> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+struct Function<R(A1, A2, A3)>
+ : Function<R(A1, A2)> {
+ typedef A3 Argument3;
+ typedef ::testing::tuple<A1, A2, A3> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+struct Function<R(A1, A2, A3, A4)>
+ : Function<R(A1, A2, A3)> {
+ typedef A4 Argument4;
+ typedef ::testing::tuple<A1, A2, A3, A4> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+struct Function<R(A1, A2, A3, A4, A5)>
+ : Function<R(A1, A2, A3, A4)> {
+ typedef A5 Argument5;
+ typedef ::testing::tuple<A1, A2, A3, A4, A5> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4, A5);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+struct Function<R(A1, A2, A3, A4, A5, A6)>
+ : Function<R(A1, A2, A3, A4, A5)> {
+ typedef A6 Argument6;
+ typedef ::testing::tuple<A1, A2, A3, A4, A5, A6> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7)>
+ : Function<R(A1, A2, A3, A4, A5, A6)> {
+ typedef A7 Argument7;
+ typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8)>
+ : Function<R(A1, A2, A3, A4, A5, A6, A7)> {
+ typedef A8 Argument8;
+ typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>
+ : Function<R(A1, A2, A3, A4, A5, A6, A7, A8)> {
+ typedef A9 Argument9;
+ typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8,
+ A9);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7, typename A8, typename A9,
+ typename A10>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>
+ : Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
+ typedef A10 Argument10;
+ typedef ::testing::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
+ A10> ArgumentTuple;
+ typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+ typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
+ typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8,
+ A9, A10);
+};
+
+} // namespace internal
+
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
diff --git a/extern/gmock/include/gmock/internal/gmock-internal-utils.h b/extern/gmock/include/gmock/internal/gmock-internal-utils.h
new file mode 100644
index 00000000000..e2ddb05c91d
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/gmock-internal-utils.h
@@ -0,0 +1,511 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file defines some utilities useful for implementing Google
+// Mock. They are subject to change without notice, so please DO NOT
+// USE THEM IN USER CODE.
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+
+#include <stdio.h>
+#include <ostream> // NOLINT
+#include <string>
+
+#include "gmock/internal/gmock-generated-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+namespace internal {
+
+// Converts an identifier name to a space-separated list of lower-case
+// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
+// treated as one word. For example, both "FooBar123" and
+// "foo_bar_123" are converted to "foo bar 123".
+GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name);
+
+// PointeeOf<Pointer>::type is the type of a value pointed to by a
+// Pointer, which can be either a smart pointer or a raw pointer. The
+// following default implementation is for the case where Pointer is a
+// smart pointer.
+template <typename Pointer>
+struct PointeeOf {
+ // Smart pointer classes define type element_type as the type of
+ // their pointees.
+ typedef typename Pointer::element_type type;
+};
+// This specialization is for the raw pointer case.
+template <typename T>
+struct PointeeOf<T*> { typedef T type; }; // NOLINT
+
+// GetRawPointer(p) returns the raw pointer underlying p when p is a
+// smart pointer, or returns p itself when p is already a raw pointer.
+// The following default implementation is for the smart pointer case.
+template <typename Pointer>
+inline const typename Pointer::element_type* GetRawPointer(const Pointer& p) {
+ return p.get();
+}
+// This overloaded version is for the raw pointer case.
+template <typename Element>
+inline Element* GetRawPointer(Element* p) { return p; }
+
+// This comparator allows linked_ptr to be stored in sets.
+template <typename T>
+struct LinkedPtrLessThan {
+ bool operator()(const ::testing::internal::linked_ptr<T>& lhs,
+ const ::testing::internal::linked_ptr<T>& rhs) const {
+ return lhs.get() < rhs.get();
+ }
+};
+
+// Symbian compilation can be done with wchar_t being either a native
+// type or a typedef. Using Google Mock with OpenC without wchar_t
+// should require the definition of _STLP_NO_WCHAR_T.
+//
+// MSVC treats wchar_t as a native type usually, but treats it as the
+// same as unsigned short when the compiler option /Zc:wchar_t- is
+// specified. It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t
+// is a native type.
+#if (GTEST_OS_SYMBIAN && defined(_STLP_NO_WCHAR_T)) || \
+ (defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED))
+// wchar_t is a typedef.
+#else
+# define GMOCK_WCHAR_T_IS_NATIVE_ 1
+#endif
+
+// signed wchar_t and unsigned wchar_t are NOT in the C++ standard.
+// Using them is a bad practice and not portable. So DON'T use them.
+//
+// Still, Google Mock is designed to work even if the user uses signed
+// wchar_t or unsigned wchar_t (obviously, assuming the compiler
+// supports them).
+//
+// To gcc,
+// wchar_t == signed wchar_t != unsigned wchar_t == unsigned int
+#ifdef __GNUC__
+// signed/unsigned wchar_t are valid types.
+# define GMOCK_HAS_SIGNED_WCHAR_T_ 1
+#endif
+
+// In what follows, we use the term "kind" to indicate whether a type
+// is bool, an integer type (excluding bool), a floating-point type,
+// or none of them. This categorization is useful for determining
+// when a matcher argument type can be safely converted to another
+// type in the implementation of SafeMatcherCast.
+enum TypeKind {
+ kBool, kInteger, kFloatingPoint, kOther
+};
+
+// KindOf<T>::value is the kind of type T.
+template <typename T> struct KindOf {
+ enum { value = kOther }; // The default kind.
+};
+
+// This macro declares that the kind of 'type' is 'kind'.
+#define GMOCK_DECLARE_KIND_(type, kind) \
+ template <> struct KindOf<type> { enum { value = kind }; }
+
+GMOCK_DECLARE_KIND_(bool, kBool);
+
+// All standard integer types.
+GMOCK_DECLARE_KIND_(char, kInteger);
+GMOCK_DECLARE_KIND_(signed char, kInteger);
+GMOCK_DECLARE_KIND_(unsigned char, kInteger);
+GMOCK_DECLARE_KIND_(short, kInteger); // NOLINT
+GMOCK_DECLARE_KIND_(unsigned short, kInteger); // NOLINT
+GMOCK_DECLARE_KIND_(int, kInteger);
+GMOCK_DECLARE_KIND_(unsigned int, kInteger);
+GMOCK_DECLARE_KIND_(long, kInteger); // NOLINT
+GMOCK_DECLARE_KIND_(unsigned long, kInteger); // NOLINT
+
+#if GMOCK_WCHAR_T_IS_NATIVE_
+GMOCK_DECLARE_KIND_(wchar_t, kInteger);
+#endif
+
+// Non-standard integer types.
+GMOCK_DECLARE_KIND_(Int64, kInteger);
+GMOCK_DECLARE_KIND_(UInt64, kInteger);
+
+// All standard floating-point types.
+GMOCK_DECLARE_KIND_(float, kFloatingPoint);
+GMOCK_DECLARE_KIND_(double, kFloatingPoint);
+GMOCK_DECLARE_KIND_(long double, kFloatingPoint);
+
+#undef GMOCK_DECLARE_KIND_
+
+// Evaluates to the kind of 'type'.
+#define GMOCK_KIND_OF_(type) \
+ static_cast< ::testing::internal::TypeKind>( \
+ ::testing::internal::KindOf<type>::value)
+
+// Evaluates to true iff integer type T is signed.
+#define GMOCK_IS_SIGNED_(T) (static_cast<T>(-1) < 0)
+
+// LosslessArithmeticConvertibleImpl<kFromKind, From, kToKind, To>::value
+// is true iff arithmetic type From can be losslessly converted to
+// arithmetic type To.
+//
+// It's the user's responsibility to ensure that both From and To are
+// raw (i.e. has no CV modifier, is not a pointer, and is not a
+// reference) built-in arithmetic types, kFromKind is the kind of
+// From, and kToKind is the kind of To; the value is
+// implementation-defined when the above pre-condition is violated.
+template <TypeKind kFromKind, typename From, TypeKind kToKind, typename To>
+struct LosslessArithmeticConvertibleImpl : public false_type {};
+
+// Converting bool to bool is lossless.
+template <>
+struct LosslessArithmeticConvertibleImpl<kBool, bool, kBool, bool>
+ : public true_type {}; // NOLINT
+
+// Converting bool to any integer type is lossless.
+template <typename To>
+struct LosslessArithmeticConvertibleImpl<kBool, bool, kInteger, To>
+ : public true_type {}; // NOLINT
+
+// Converting bool to any floating-point type is lossless.
+template <typename To>
+struct LosslessArithmeticConvertibleImpl<kBool, bool, kFloatingPoint, To>
+ : public true_type {}; // NOLINT
+
+// Converting an integer to bool is lossy.
+template <typename From>
+struct LosslessArithmeticConvertibleImpl<kInteger, From, kBool, bool>
+ : public false_type {}; // NOLINT
+
+// Converting an integer to another non-bool integer is lossless iff
+// the target type's range encloses the source type's range.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<kInteger, From, kInteger, To>
+ : public bool_constant<
+ // When converting from a smaller size to a larger size, we are
+ // fine as long as we are not converting from signed to unsigned.
+ ((sizeof(From) < sizeof(To)) &&
+ (!GMOCK_IS_SIGNED_(From) || GMOCK_IS_SIGNED_(To))) ||
+ // When converting between the same size, the signedness must match.
+ ((sizeof(From) == sizeof(To)) &&
+ (GMOCK_IS_SIGNED_(From) == GMOCK_IS_SIGNED_(To)))> {}; // NOLINT
+
+#undef GMOCK_IS_SIGNED_
+
+// Converting an integer to a floating-point type may be lossy, since
+// the format of a floating-point number is implementation-defined.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<kInteger, From, kFloatingPoint, To>
+ : public false_type {}; // NOLINT
+
+// Converting a floating-point to bool is lossy.
+template <typename From>
+struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kBool, bool>
+ : public false_type {}; // NOLINT
+
+// Converting a floating-point to an integer is lossy.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kInteger, To>
+ : public false_type {}; // NOLINT
+
+// Converting a floating-point to another floating-point is lossless
+// iff the target type is at least as big as the source type.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<
+ kFloatingPoint, From, kFloatingPoint, To>
+ : public bool_constant<sizeof(From) <= sizeof(To)> {}; // NOLINT
+
+// LosslessArithmeticConvertible<From, To>::value is true iff arithmetic
+// type From can be losslessly converted to arithmetic type To.
+//
+// It's the user's responsibility to ensure that both From and To are
+// raw (i.e. has no CV modifier, is not a pointer, and is not a
+// reference) built-in arithmetic types; the value is
+// implementation-defined when the above pre-condition is violated.
+template <typename From, typename To>
+struct LosslessArithmeticConvertible
+ : public LosslessArithmeticConvertibleImpl<
+ GMOCK_KIND_OF_(From), From, GMOCK_KIND_OF_(To), To> {}; // NOLINT
+
+// This interface knows how to report a Google Mock failure (either
+// non-fatal or fatal).
+class FailureReporterInterface {
+ public:
+ // The type of a failure (either non-fatal or fatal).
+ enum FailureType {
+ kNonfatal, kFatal
+ };
+
+ virtual ~FailureReporterInterface() {}
+
+ // Reports a failure that occurred at the given source file location.
+ virtual void ReportFailure(FailureType type, const char* file, int line,
+ const string& message) = 0;
+};
+
+// Returns the failure reporter used by Google Mock.
+GTEST_API_ FailureReporterInterface* GetFailureReporter();
+
+// Asserts that condition is true; aborts the process with the given
+// message if condition is false. We cannot use LOG(FATAL) or CHECK()
+// as Google Mock might be used to mock the log sink itself. We
+// inline this function to prevent it from showing up in the stack
+// trace.
+inline void Assert(bool condition, const char* file, int line,
+ const string& msg) {
+ if (!condition) {
+ GetFailureReporter()->ReportFailure(FailureReporterInterface::kFatal,
+ file, line, msg);
+ }
+}
+inline void Assert(bool condition, const char* file, int line) {
+ Assert(condition, file, line, "Assertion failed.");
+}
+
+// Verifies that condition is true; generates a non-fatal failure if
+// condition is false.
+inline void Expect(bool condition, const char* file, int line,
+ const string& msg) {
+ if (!condition) {
+ GetFailureReporter()->ReportFailure(FailureReporterInterface::kNonfatal,
+ file, line, msg);
+ }
+}
+inline void Expect(bool condition, const char* file, int line) {
+ Expect(condition, file, line, "Expectation failed.");
+}
+
+// Severity level of a log.
+enum LogSeverity {
+ kInfo = 0,
+ kWarning = 1
+};
+
+// Valid values for the --gmock_verbose flag.
+
+// All logs (informational and warnings) are printed.
+const char kInfoVerbosity[] = "info";
+// Only warnings are printed.
+const char kWarningVerbosity[] = "warning";
+// No logs are printed.
+const char kErrorVerbosity[] = "error";
+
+// Returns true iff a log with the given severity is visible according
+// to the --gmock_verbose flag.
+GTEST_API_ bool LogIsVisible(LogSeverity severity);
+
+// Prints the given message to stdout iff 'severity' >= the level
+// specified by the --gmock_verbose flag. If stack_frames_to_skip >=
+// 0, also prints the stack trace excluding the top
+// stack_frames_to_skip frames. In opt mode, any positive
+// stack_frames_to_skip is treated as 0, since we don't know which
+// function calls will be inlined by the compiler and need to be
+// conservative.
+GTEST_API_ void Log(LogSeverity severity,
+ const string& message,
+ int stack_frames_to_skip);
+
+// TODO(wan@google.com): group all type utilities together.
+
+// Type traits.
+
+// is_reference<T>::value is non-zero iff T is a reference type.
+template <typename T> struct is_reference : public false_type {};
+template <typename T> struct is_reference<T&> : public true_type {};
+
+// type_equals<T1, T2>::value is non-zero iff T1 and T2 are the same type.
+template <typename T1, typename T2> struct type_equals : public false_type {};
+template <typename T> struct type_equals<T, T> : public true_type {};
+
+// remove_reference<T>::type removes the reference from type T, if any.
+template <typename T> struct remove_reference { typedef T type; }; // NOLINT
+template <typename T> struct remove_reference<T&> { typedef T type; }; // NOLINT
+
+// DecayArray<T>::type turns an array type U[N] to const U* and preserves
+// other types. Useful for saving a copy of a function argument.
+template <typename T> struct DecayArray { typedef T type; }; // NOLINT
+template <typename T, size_t N> struct DecayArray<T[N]> {
+ typedef const T* type;
+};
+// Sometimes people use arrays whose size is not available at the use site
+// (e.g. extern const char kNamePrefix[]). This specialization covers that
+// case.
+template <typename T> struct DecayArray<T[]> {
+ typedef const T* type;
+};
+
+// Disable MSVC warnings for infinite recursion, since in this case the
+// the recursion is unreachable.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4717)
+#endif
+
+// Invalid<T>() is usable as an expression of type T, but will terminate
+// the program with an assertion failure if actually run. This is useful
+// when a value of type T is needed for compilation, but the statement
+// will not really be executed (or we don't care if the statement
+// crashes).
+template <typename T>
+inline T Invalid() {
+ Assert(false, "", -1, "Internal error: attempt to return invalid value");
+ // This statement is unreachable, and would never terminate even if it
+ // could be reached. It is provided only to placate compiler warnings
+ // about missing return statements.
+ return Invalid<T>();
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+// Given a raw type (i.e. having no top-level reference or const
+// modifier) RawContainer that's either an STL-style container or a
+// native array, class StlContainerView<RawContainer> has the
+// following members:
+//
+// - type is a type that provides an STL-style container view to
+// (i.e. implements the STL container concept for) RawContainer;
+// - const_reference is a type that provides a reference to a const
+// RawContainer;
+// - ConstReference(raw_container) returns a const reference to an STL-style
+// container view to raw_container, which is a RawContainer.
+// - Copy(raw_container) returns an STL-style container view of a
+// copy of raw_container, which is a RawContainer.
+//
+// This generic version is used when RawContainer itself is already an
+// STL-style container.
+template <class RawContainer>
+class StlContainerView {
+ public:
+ typedef RawContainer type;
+ typedef const type& const_reference;
+
+ static const_reference ConstReference(const RawContainer& container) {
+ // Ensures that RawContainer is not a const type.
+ testing::StaticAssertTypeEq<RawContainer,
+ GTEST_REMOVE_CONST_(RawContainer)>();
+ return container;
+ }
+ static type Copy(const RawContainer& container) { return container; }
+};
+
+// This specialization is used when RawContainer is a native array type.
+template <typename Element, size_t N>
+class StlContainerView<Element[N]> {
+ public:
+ typedef GTEST_REMOVE_CONST_(Element) RawElement;
+ typedef internal::NativeArray<RawElement> type;
+ // NativeArray<T> can represent a native array either by value or by
+ // reference (selected by a constructor argument), so 'const type'
+ // can be used to reference a const native array. We cannot
+ // 'typedef const type& const_reference' here, as that would mean
+ // ConstReference() has to return a reference to a local variable.
+ typedef const type const_reference;
+
+ static const_reference ConstReference(const Element (&array)[N]) {
+ // Ensures that Element is not a const type.
+ testing::StaticAssertTypeEq<Element, RawElement>();
+#if GTEST_OS_SYMBIAN
+ // The Nokia Symbian compiler confuses itself in template instantiation
+ // for this call without the cast to Element*:
+ // function call '[testing::internal::NativeArray<char *>].NativeArray(
+ // {lval} const char *[4], long, testing::internal::RelationToSource)'
+ // does not match
+ // 'testing::internal::NativeArray<char *>::NativeArray(
+ // char *const *, unsigned int, testing::internal::RelationToSource)'
+ // (instantiating: 'testing::internal::ContainsMatcherImpl
+ // <const char * (&)[4]>::Matches(const char * (&)[4]) const')
+ // (instantiating: 'testing::internal::StlContainerView<char *[4]>::
+ // ConstReference(const char * (&)[4])')
+ // (and though the N parameter type is mismatched in the above explicit
+ // conversion of it doesn't help - only the conversion of the array).
+ return type(const_cast<Element*>(&array[0]), N,
+ RelationToSourceReference());
+#else
+ return type(array, N, RelationToSourceReference());
+#endif // GTEST_OS_SYMBIAN
+ }
+ static type Copy(const Element (&array)[N]) {
+#if GTEST_OS_SYMBIAN
+ return type(const_cast<Element*>(&array[0]), N, RelationToSourceCopy());
+#else
+ return type(array, N, RelationToSourceCopy());
+#endif // GTEST_OS_SYMBIAN
+ }
+};
+
+// This specialization is used when RawContainer is a native array
+// represented as a (pointer, size) tuple.
+template <typename ElementPointer, typename Size>
+class StlContainerView< ::testing::tuple<ElementPointer, Size> > {
+ public:
+ typedef GTEST_REMOVE_CONST_(
+ typename internal::PointeeOf<ElementPointer>::type) RawElement;
+ typedef internal::NativeArray<RawElement> type;
+ typedef const type const_reference;
+
+ static const_reference ConstReference(
+ const ::testing::tuple<ElementPointer, Size>& array) {
+ return type(get<0>(array), get<1>(array), RelationToSourceReference());
+ }
+ static type Copy(const ::testing::tuple<ElementPointer, Size>& array) {
+ return type(get<0>(array), get<1>(array), RelationToSourceCopy());
+ }
+};
+
+// The following specialization prevents the user from instantiating
+// StlContainer with a reference type.
+template <typename T> class StlContainerView<T&>;
+
+// A type transform to remove constness from the first part of a pair.
+// Pairs like that are used as the value_type of associative containers,
+// and this transform produces a similar but assignable pair.
+template <typename T>
+struct RemoveConstFromKey {
+ typedef T type;
+};
+
+// Partially specialized to remove constness from std::pair<const K, V>.
+template <typename K, typename V>
+struct RemoveConstFromKey<std::pair<const K, V> > {
+ typedef std::pair<K, V> type;
+};
+
+// Mapping from booleans to types. Similar to boost::bool_<kValue> and
+// std::integral_constant<bool, kValue>.
+template <bool kValue>
+struct BooleanConstant {};
+
+} // namespace internal
+} // namespace testing
+
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+
diff --git a/extern/gmock/include/gmock/internal/gmock-port.h b/extern/gmock/include/gmock/internal/gmock-port.h
new file mode 100644
index 00000000000..63f4a6802e8
--- /dev/null
+++ b/extern/gmock/include/gmock/internal/gmock-port.h
@@ -0,0 +1,91 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vadimb@google.com (Vadim Berman)
+//
+// Low-level types and utilities for porting Google Mock to various
+// platforms. All macros ending with _ and symbols defined in an
+// internal namespace are subject to change without notice. Code
+// outside Google Mock MUST NOT USE THEM DIRECTLY. Macros that don't
+// end with _ are part of Google Mock's public API and can be used by
+// code outside Google Mock.
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+
+#include <assert.h>
+#include <stdlib.h>
+#include <iostream>
+
+// Most of the utilities needed for porting Google Mock are also
+// required for Google Test and are defined in gtest-port.h.
+//
+// Note to maintainers: to reduce code duplication, prefer adding
+// portability utilities to Google Test's gtest-port.h instead of
+// here, as Google Mock depends on Google Test. Only add a utility
+// here if it's truly specific to Google Mock.
+#include "gtest/internal/gtest-linked_ptr.h"
+#include "gtest/internal/gtest-port.h"
+#include "gmock/internal/custom/gmock-port.h"
+
+// To avoid conditional compilation everywhere, we make it
+// gmock-port.h's responsibility to #include the header implementing
+// tr1/tuple. gmock-port.h does this via gtest-port.h, which is
+// guaranteed to pull in the tuple header.
+
+// For MS Visual C++, check the compiler version. At least VS 2003 is
+// required to compile Google Mock.
+#if defined(_MSC_VER) && _MSC_VER < 1310
+# error "At least Visual C++ 2003 (7.1) is required to compile Google Mock."
+#endif
+
+// Macro for referencing flags. This is public as we want the user to
+// use this syntax to reference Google Mock flags.
+#define GMOCK_FLAG(name) FLAGS_gmock_##name
+
+#if !defined(GMOCK_DECLARE_bool_)
+
+// Macros for declaring flags.
+#define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name)
+#define GMOCK_DECLARE_int32_(name) \
+ extern GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name)
+#define GMOCK_DECLARE_string_(name) \
+ extern GTEST_API_ ::std::string GMOCK_FLAG(name)
+
+// Macros for defining flags.
+#define GMOCK_DEFINE_bool_(name, default_val, doc) \
+ GTEST_API_ bool GMOCK_FLAG(name) = (default_val)
+#define GMOCK_DEFINE_int32_(name, default_val, doc) \
+ GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name) = (default_val)
+#define GMOCK_DEFINE_string_(name, default_val, doc) \
+ GTEST_API_ ::std::string GMOCK_FLAG(name) = (default_val)
+
+#endif // !defined(GMOCK_DECLARE_bool_)
+
+#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
diff --git a/extern/gmock/src/gmock-all.cc b/extern/gmock/src/gmock-all.cc
new file mode 100644
index 00000000000..7aebce7afef
--- /dev/null
+++ b/extern/gmock/src/gmock-all.cc
@@ -0,0 +1,47 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// Google C++ Mocking Framework (Google Mock)
+//
+// This file #includes all Google Mock implementation .cc files. The
+// purpose is to allow a user to build Google Mock by compiling this
+// file alone.
+
+// This line ensures that gmock.h can be compiled on its own, even
+// when it's fused.
+#include "gmock/gmock.h"
+
+// The following lines pull in the real gmock *.cc files.
+#include "src/gmock-cardinalities.cc"
+#include "src/gmock-internal-utils.cc"
+#include "src/gmock-matchers.cc"
+#include "src/gmock-spec-builders.cc"
+#include "src/gmock.cc"
diff --git a/extern/gmock/src/gmock-cardinalities.cc b/extern/gmock/src/gmock-cardinalities.cc
new file mode 100644
index 00000000000..50ec7286eeb
--- /dev/null
+++ b/extern/gmock/src/gmock-cardinalities.cc
@@ -0,0 +1,156 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements cardinalities.
+
+#include "gmock/gmock-cardinalities.h"
+
+#include <limits.h>
+#include <ostream> // NOLINT
+#include <sstream>
+#include <string>
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+
+namespace {
+
+// Implements the Between(m, n) cardinality.
+class BetweenCardinalityImpl : public CardinalityInterface {
+ public:
+ BetweenCardinalityImpl(int min, int max)
+ : min_(min >= 0 ? min : 0),
+ max_(max >= min_ ? max : min_) {
+ std::stringstream ss;
+ if (min < 0) {
+ ss << "The invocation lower bound must be >= 0, "
+ << "but is actually " << min << ".";
+ internal::Expect(false, __FILE__, __LINE__, ss.str());
+ } else if (max < 0) {
+ ss << "The invocation upper bound must be >= 0, "
+ << "but is actually " << max << ".";
+ internal::Expect(false, __FILE__, __LINE__, ss.str());
+ } else if (min > max) {
+ ss << "The invocation upper bound (" << max
+ << ") must be >= the invocation lower bound (" << min
+ << ").";
+ internal::Expect(false, __FILE__, __LINE__, ss.str());
+ }
+ }
+
+ // Conservative estimate on the lower/upper bound of the number of
+ // calls allowed.
+ virtual int ConservativeLowerBound() const { return min_; }
+ virtual int ConservativeUpperBound() const { return max_; }
+
+ virtual bool IsSatisfiedByCallCount(int call_count) const {
+ return min_ <= call_count && call_count <= max_;
+ }
+
+ virtual bool IsSaturatedByCallCount(int call_count) const {
+ return call_count >= max_;
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const;
+
+ private:
+ const int min_;
+ const int max_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(BetweenCardinalityImpl);
+};
+
+// Formats "n times" in a human-friendly way.
+inline internal::string FormatTimes(int n) {
+ if (n == 1) {
+ return "once";
+ } else if (n == 2) {
+ return "twice";
+ } else {
+ std::stringstream ss;
+ ss << n << " times";
+ return ss.str();
+ }
+}
+
+// Describes the Between(m, n) cardinality in human-friendly text.
+void BetweenCardinalityImpl::DescribeTo(::std::ostream* os) const {
+ if (min_ == 0) {
+ if (max_ == 0) {
+ *os << "never called";
+ } else if (max_ == INT_MAX) {
+ *os << "called any number of times";
+ } else {
+ *os << "called at most " << FormatTimes(max_);
+ }
+ } else if (min_ == max_) {
+ *os << "called " << FormatTimes(min_);
+ } else if (max_ == INT_MAX) {
+ *os << "called at least " << FormatTimes(min_);
+ } else {
+ // 0 < min_ < max_ < INT_MAX
+ *os << "called between " << min_ << " and " << max_ << " times";
+ }
+}
+
+} // Unnamed namespace
+
+// Describes the given call count to an ostream.
+void Cardinality::DescribeActualCallCountTo(int actual_call_count,
+ ::std::ostream* os) {
+ if (actual_call_count > 0) {
+ *os << "called " << FormatTimes(actual_call_count);
+ } else {
+ *os << "never called";
+ }
+}
+
+// Creates a cardinality that allows at least n calls.
+GTEST_API_ Cardinality AtLeast(int n) { return Between(n, INT_MAX); }
+
+// Creates a cardinality that allows at most n calls.
+GTEST_API_ Cardinality AtMost(int n) { return Between(0, n); }
+
+// Creates a cardinality that allows any number of calls.
+GTEST_API_ Cardinality AnyNumber() { return AtLeast(0); }
+
+// Creates a cardinality that allows between min and max calls.
+GTEST_API_ Cardinality Between(int min, int max) {
+ return Cardinality(new BetweenCardinalityImpl(min, max));
+}
+
+// Creates a cardinality that allows exactly n calls.
+GTEST_API_ Cardinality Exactly(int n) { return Between(n, n); }
+
+} // namespace testing
diff --git a/extern/gmock/src/gmock-internal-utils.cc b/extern/gmock/src/gmock-internal-utils.cc
new file mode 100644
index 00000000000..fb5308018a7
--- /dev/null
+++ b/extern/gmock/src/gmock-internal-utils.cc
@@ -0,0 +1,174 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file defines some utilities useful for implementing Google
+// Mock. They are subject to change without notice, so please DO NOT
+// USE THEM IN USER CODE.
+
+#include "gmock/internal/gmock-internal-utils.h"
+
+#include <ctype.h>
+#include <ostream> // NOLINT
+#include <string>
+#include "gmock/gmock.h"
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+namespace internal {
+
+// Converts an identifier name to a space-separated list of lower-case
+// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
+// treated as one word. For example, both "FooBar123" and
+// "foo_bar_123" are converted to "foo bar 123".
+GTEST_API_ string ConvertIdentifierNameToWords(const char* id_name) {
+ string result;
+ char prev_char = '\0';
+ for (const char* p = id_name; *p != '\0'; prev_char = *(p++)) {
+ // We don't care about the current locale as the input is
+ // guaranteed to be a valid C++ identifier name.
+ const bool starts_new_word = IsUpper(*p) ||
+ (!IsAlpha(prev_char) && IsLower(*p)) ||
+ (!IsDigit(prev_char) && IsDigit(*p));
+
+ if (IsAlNum(*p)) {
+ if (starts_new_word && result != "")
+ result += ' ';
+ result += ToLower(*p);
+ }
+ }
+ return result;
+}
+
+// This class reports Google Mock failures as Google Test failures. A
+// user can define another class in a similar fashion if he intends to
+// use Google Mock with a testing framework other than Google Test.
+class GoogleTestFailureReporter : public FailureReporterInterface {
+ public:
+ virtual void ReportFailure(FailureType type, const char* file, int line,
+ const string& message) {
+ AssertHelper(type == kFatal ?
+ TestPartResult::kFatalFailure :
+ TestPartResult::kNonFatalFailure,
+ file,
+ line,
+ message.c_str()) = Message();
+ if (type == kFatal) {
+ posix::Abort();
+ }
+ }
+};
+
+// Returns the global failure reporter. Will create a
+// GoogleTestFailureReporter and return it the first time called.
+GTEST_API_ FailureReporterInterface* GetFailureReporter() {
+ // Points to the global failure reporter used by Google Mock. gcc
+ // guarantees that the following use of failure_reporter is
+ // thread-safe. We may need to add additional synchronization to
+ // protect failure_reporter if we port Google Mock to other
+ // compilers.
+ static FailureReporterInterface* const failure_reporter =
+ new GoogleTestFailureReporter();
+ return failure_reporter;
+}
+
+// Protects global resources (stdout in particular) used by Log().
+static GTEST_DEFINE_STATIC_MUTEX_(g_log_mutex);
+
+// Returns true iff a log with the given severity is visible according
+// to the --gmock_verbose flag.
+GTEST_API_ bool LogIsVisible(LogSeverity severity) {
+ if (GMOCK_FLAG(verbose) == kInfoVerbosity) {
+ // Always show the log if --gmock_verbose=info.
+ return true;
+ } else if (GMOCK_FLAG(verbose) == kErrorVerbosity) {
+ // Always hide it if --gmock_verbose=error.
+ return false;
+ } else {
+ // If --gmock_verbose is neither "info" nor "error", we treat it
+ // as "warning" (its default value).
+ return severity == kWarning;
+ }
+}
+
+// Prints the given message to stdout iff 'severity' >= the level
+// specified by the --gmock_verbose flag. If stack_frames_to_skip >=
+// 0, also prints the stack trace excluding the top
+// stack_frames_to_skip frames. In opt mode, any positive
+// stack_frames_to_skip is treated as 0, since we don't know which
+// function calls will be inlined by the compiler and need to be
+// conservative.
+GTEST_API_ void Log(LogSeverity severity,
+ const string& message,
+ int stack_frames_to_skip) {
+ if (!LogIsVisible(severity))
+ return;
+
+ // Ensures that logs from different threads don't interleave.
+ MutexLock l(&g_log_mutex);
+
+ // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is a
+ // macro.
+
+ if (severity == kWarning) {
+ // Prints a GMOCK WARNING marker to make the warnings easily searchable.
+ std::cout << "\nGMOCK WARNING:";
+ }
+ // Pre-pends a new-line to message if it doesn't start with one.
+ if (message.empty() || message[0] != '\n') {
+ std::cout << "\n";
+ }
+ std::cout << message;
+ if (stack_frames_to_skip >= 0) {
+#ifdef NDEBUG
+ // In opt mode, we have to be conservative and skip no stack frame.
+ const int actual_to_skip = 0;
+#else
+ // In dbg mode, we can do what the caller tell us to do (plus one
+ // for skipping this function's stack frame).
+ const int actual_to_skip = stack_frames_to_skip + 1;
+#endif // NDEBUG
+
+ // Appends a new-line to message if it doesn't end with one.
+ if (!message.empty() && *message.rbegin() != '\n') {
+ std::cout << "\n";
+ }
+ std::cout << "Stack trace:\n"
+ << ::testing::internal::GetCurrentOsStackTraceExceptTop(
+ ::testing::UnitTest::GetInstance(), actual_to_skip);
+ }
+ std::cout << ::std::flush;
+}
+
+} // namespace internal
+} // namespace testing
diff --git a/extern/gmock/src/gmock-matchers.cc b/extern/gmock/src/gmock-matchers.cc
new file mode 100644
index 00000000000..e7424510fca
--- /dev/null
+++ b/extern/gmock/src/gmock-matchers.cc
@@ -0,0 +1,498 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements Matcher<const string&>, Matcher<string>, and
+// utilities for defining matchers.
+
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-generated-matchers.h"
+
+#include <string.h>
+#include <sstream>
+#include <string>
+
+namespace testing {
+
+// Constructs a matcher that matches a const string& whose value is
+// equal to s.
+Matcher<const internal::string&>::Matcher(const internal::string& s) {
+ *this = Eq(s);
+}
+
+// Constructs a matcher that matches a const string& whose value is
+// equal to s.
+Matcher<const internal::string&>::Matcher(const char* s) {
+ *this = Eq(internal::string(s));
+}
+
+// Constructs a matcher that matches a string whose value is equal to s.
+Matcher<internal::string>::Matcher(const internal::string& s) { *this = Eq(s); }
+
+// Constructs a matcher that matches a string whose value is equal to s.
+Matcher<internal::string>::Matcher(const char* s) {
+ *this = Eq(internal::string(s));
+}
+
+#if GTEST_HAS_STRING_PIECE_
+// Constructs a matcher that matches a const StringPiece& whose value is
+// equal to s.
+Matcher<const StringPiece&>::Matcher(const internal::string& s) {
+ *this = Eq(s);
+}
+
+// Constructs a matcher that matches a const StringPiece& whose value is
+// equal to s.
+Matcher<const StringPiece&>::Matcher(const char* s) {
+ *this = Eq(internal::string(s));
+}
+
+// Constructs a matcher that matches a const StringPiece& whose value is
+// equal to s.
+Matcher<const StringPiece&>::Matcher(StringPiece s) {
+ *this = Eq(s.ToString());
+}
+
+// Constructs a matcher that matches a StringPiece whose value is equal to s.
+Matcher<StringPiece>::Matcher(const internal::string& s) {
+ *this = Eq(s);
+}
+
+// Constructs a matcher that matches a StringPiece whose value is equal to s.
+Matcher<StringPiece>::Matcher(const char* s) {
+ *this = Eq(internal::string(s));
+}
+
+// Constructs a matcher that matches a StringPiece whose value is equal to s.
+Matcher<StringPiece>::Matcher(StringPiece s) {
+ *this = Eq(s.ToString());
+}
+#endif // GTEST_HAS_STRING_PIECE_
+
+namespace internal {
+
+// Joins a vector of strings as if they are fields of a tuple; returns
+// the joined string.
+GTEST_API_ string JoinAsTuple(const Strings& fields) {
+ switch (fields.size()) {
+ case 0:
+ return "";
+ case 1:
+ return fields[0];
+ default:
+ string result = "(" + fields[0];
+ for (size_t i = 1; i < fields.size(); i++) {
+ result += ", ";
+ result += fields[i];
+ }
+ result += ")";
+ return result;
+ }
+}
+
+// Returns the description for a matcher defined using the MATCHER*()
+// macro where the user-supplied description string is "", if
+// 'negation' is false; otherwise returns the description of the
+// negation of the matcher. 'param_values' contains a list of strings
+// that are the print-out of the matcher's parameters.
+GTEST_API_ string FormatMatcherDescription(bool negation,
+ const char* matcher_name,
+ const Strings& param_values) {
+ string result = ConvertIdentifierNameToWords(matcher_name);
+ if (param_values.size() >= 1)
+ result += " " + JoinAsTuple(param_values);
+ return negation ? "not (" + result + ")" : result;
+}
+
+// FindMaxBipartiteMatching and its helper class.
+//
+// Uses the well-known Ford-Fulkerson max flow method to find a maximum
+// bipartite matching. Flow is considered to be from left to right.
+// There is an implicit source node that is connected to all of the left
+// nodes, and an implicit sink node that is connected to all of the
+// right nodes. All edges have unit capacity.
+//
+// Neither the flow graph nor the residual flow graph are represented
+// explicitly. Instead, they are implied by the information in 'graph' and
+// a vector<int> called 'left_' whose elements are initialized to the
+// value kUnused. This represents the initial state of the algorithm,
+// where the flow graph is empty, and the residual flow graph has the
+// following edges:
+// - An edge from source to each left_ node
+// - An edge from each right_ node to sink
+// - An edge from each left_ node to each right_ node, if the
+// corresponding edge exists in 'graph'.
+//
+// When the TryAugment() method adds a flow, it sets left_[l] = r for some
+// nodes l and r. This induces the following changes:
+// - The edges (source, l), (l, r), and (r, sink) are added to the
+// flow graph.
+// - The same three edges are removed from the residual flow graph.
+// - The reverse edges (l, source), (r, l), and (sink, r) are added
+// to the residual flow graph, which is a directional graph
+// representing unused flow capacity.
+//
+// When the method augments a flow (moving left_[l] from some r1 to some
+// other r2), this can be thought of as "undoing" the above steps with
+// respect to r1 and "redoing" them with respect to r2.
+//
+// It bears repeating that the flow graph and residual flow graph are
+// never represented explicitly, but can be derived by looking at the
+// information in 'graph' and in left_.
+//
+// As an optimization, there is a second vector<int> called right_ which
+// does not provide any new information. Instead, it enables more
+// efficient queries about edges entering or leaving the right-side nodes
+// of the flow or residual flow graphs. The following invariants are
+// maintained:
+//
+// left[l] == kUnused or right[left[l]] == l
+// right[r] == kUnused or left[right[r]] == r
+//
+// . [ source ] .
+// . ||| .
+// . ||| .
+// . ||\--> left[0]=1 ---\ right[0]=-1 ----\ .
+// . || | | .
+// . |\---> left[1]=-1 \--> right[1]=0 ---\| .
+// . | || .
+// . \----> left[2]=2 ------> right[2]=2 --\|| .
+// . ||| .
+// . elements matchers vvv .
+// . [ sink ] .
+//
+// See Also:
+// [1] Cormen, et al (2001). "Section 26.2: The Ford-Fulkerson method".
+// "Introduction to Algorithms (Second ed.)", pp. 651-664.
+// [2] "Ford-Fulkerson algorithm", Wikipedia,
+// 'http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm'
+class MaxBipartiteMatchState {
+ public:
+ explicit MaxBipartiteMatchState(const MatchMatrix& graph)
+ : graph_(&graph),
+ left_(graph_->LhsSize(), kUnused),
+ right_(graph_->RhsSize(), kUnused) {
+ }
+
+ // Returns the edges of a maximal match, each in the form {left, right}.
+ ElementMatcherPairs Compute() {
+ // 'seen' is used for path finding { 0: unseen, 1: seen }.
+ ::std::vector<char> seen;
+ // Searches the residual flow graph for a path from each left node to
+ // the sink in the residual flow graph, and if one is found, add flow
+ // to the graph. It's okay to search through the left nodes once. The
+ // edge from the implicit source node to each previously-visited left
+ // node will have flow if that left node has any path to the sink
+ // whatsoever. Subsequent augmentations can only add flow to the
+ // network, and cannot take away that previous flow unit from the source.
+ // Since the source-to-left edge can only carry one flow unit (or,
+ // each element can be matched to only one matcher), there is no need
+ // to visit the left nodes more than once looking for augmented paths.
+ // The flow is known to be possible or impossible by looking at the
+ // node once.
+ for (size_t ilhs = 0; ilhs < graph_->LhsSize(); ++ilhs) {
+ // Reset the path-marking vector and try to find a path from
+ // source to sink starting at the left_[ilhs] node.
+ GTEST_CHECK_(left_[ilhs] == kUnused)
+ << "ilhs: " << ilhs << ", left_[ilhs]: " << left_[ilhs];
+ // 'seen' initialized to 'graph_->RhsSize()' copies of 0.
+ seen.assign(graph_->RhsSize(), 0);
+ TryAugment(ilhs, &seen);
+ }
+ ElementMatcherPairs result;
+ for (size_t ilhs = 0; ilhs < left_.size(); ++ilhs) {
+ size_t irhs = left_[ilhs];
+ if (irhs == kUnused) continue;
+ result.push_back(ElementMatcherPair(ilhs, irhs));
+ }
+ return result;
+ }
+
+ private:
+ static const size_t kUnused = static_cast<size_t>(-1);
+
+ // Perform a depth-first search from left node ilhs to the sink. If a
+ // path is found, flow is added to the network by linking the left and
+ // right vector elements corresponding each segment of the path.
+ // Returns true if a path to sink was found, which means that a unit of
+ // flow was added to the network. The 'seen' vector elements correspond
+ // to right nodes and are marked to eliminate cycles from the search.
+ //
+ // Left nodes will only be explored at most once because they
+ // are accessible from at most one right node in the residual flow
+ // graph.
+ //
+ // Note that left_[ilhs] is the only element of left_ that TryAugment will
+ // potentially transition from kUnused to another value. Any other
+ // left_ element holding kUnused before TryAugment will be holding it
+ // when TryAugment returns.
+ //
+ bool TryAugment(size_t ilhs, ::std::vector<char>* seen) {
+ for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) {
+ if ((*seen)[irhs])
+ continue;
+ if (!graph_->HasEdge(ilhs, irhs))
+ continue;
+ // There's an available edge from ilhs to irhs.
+ (*seen)[irhs] = 1;
+ // Next a search is performed to determine whether
+ // this edge is a dead end or leads to the sink.
+ //
+ // right_[irhs] == kUnused means that there is residual flow from
+ // right node irhs to the sink, so we can use that to finish this
+ // flow path and return success.
+ //
+ // Otherwise there is residual flow to some ilhs. We push flow
+ // along that path and call ourselves recursively to see if this
+ // ultimately leads to sink.
+ if (right_[irhs] == kUnused || TryAugment(right_[irhs], seen)) {
+ // Add flow from left_[ilhs] to right_[irhs].
+ left_[ilhs] = irhs;
+ right_[irhs] = ilhs;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const MatchMatrix* graph_; // not owned
+ // Each element of the left_ vector represents a left hand side node
+ // (i.e. an element) and each element of right_ is a right hand side
+ // node (i.e. a matcher). The values in the left_ vector indicate
+ // outflow from that node to a node on the the right_ side. The values
+ // in the right_ indicate inflow, and specify which left_ node is
+ // feeding that right_ node, if any. For example, left_[3] == 1 means
+ // there's a flow from element #3 to matcher #1. Such a flow would also
+ // be redundantly represented in the right_ vector as right_[1] == 3.
+ // Elements of left_ and right_ are either kUnused or mutually
+ // referent. Mutually referent means that left_[right_[i]] = i and
+ // right_[left_[i]] = i.
+ ::std::vector<size_t> left_;
+ ::std::vector<size_t> right_;
+
+ GTEST_DISALLOW_ASSIGN_(MaxBipartiteMatchState);
+};
+
+const size_t MaxBipartiteMatchState::kUnused;
+
+GTEST_API_ ElementMatcherPairs
+FindMaxBipartiteMatching(const MatchMatrix& g) {
+ return MaxBipartiteMatchState(g).Compute();
+}
+
+static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
+ ::std::ostream* stream) {
+ typedef ElementMatcherPairs::const_iterator Iter;
+ ::std::ostream& os = *stream;
+ os << "{";
+ const char *sep = "";
+ for (Iter it = pairs.begin(); it != pairs.end(); ++it) {
+ os << sep << "\n ("
+ << "element #" << it->first << ", "
+ << "matcher #" << it->second << ")";
+ sep = ",";
+ }
+ os << "\n}";
+}
+
+// Tries to find a pairing, and explains the result.
+GTEST_API_ bool FindPairing(const MatchMatrix& matrix,
+ MatchResultListener* listener) {
+ ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix);
+
+ size_t max_flow = matches.size();
+ bool result = (max_flow == matrix.RhsSize());
+
+ if (!result) {
+ if (listener->IsInterested()) {
+ *listener << "where no permutation of the elements can "
+ "satisfy all matchers, and the closest match is "
+ << max_flow << " of " << matrix.RhsSize()
+ << " matchers with the pairings:\n";
+ LogElementMatcherPairVec(matches, listener->stream());
+ }
+ return false;
+ }
+
+ if (matches.size() > 1) {
+ if (listener->IsInterested()) {
+ const char *sep = "where:\n";
+ for (size_t mi = 0; mi < matches.size(); ++mi) {
+ *listener << sep << " - element #" << matches[mi].first
+ << " is matched by matcher #" << matches[mi].second;
+ sep = ",\n";
+ }
+ }
+ }
+ return true;
+}
+
+bool MatchMatrix::NextGraph() {
+ for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {
+ for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {
+ char& b = matched_[SpaceIndex(ilhs, irhs)];
+ if (!b) {
+ b = 1;
+ return true;
+ }
+ b = 0;
+ }
+ }
+ return false;
+}
+
+void MatchMatrix::Randomize() {
+ for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {
+ for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {
+ char& b = matched_[SpaceIndex(ilhs, irhs)];
+ b = static_cast<char>(rand() & 1); // NOLINT
+ }
+ }
+}
+
+string MatchMatrix::DebugString() const {
+ ::std::stringstream ss;
+ const char *sep = "";
+ for (size_t i = 0; i < LhsSize(); ++i) {
+ ss << sep;
+ for (size_t j = 0; j < RhsSize(); ++j) {
+ ss << HasEdge(i, j);
+ }
+ sep = ";";
+ }
+ return ss.str();
+}
+
+void UnorderedElementsAreMatcherImplBase::DescribeToImpl(
+ ::std::ostream* os) const {
+ if (matcher_describers_.empty()) {
+ *os << "is empty";
+ return;
+ }
+ if (matcher_describers_.size() == 1) {
+ *os << "has " << Elements(1) << " and that element ";
+ matcher_describers_[0]->DescribeTo(os);
+ return;
+ }
+ *os << "has " << Elements(matcher_describers_.size())
+ << " and there exists some permutation of elements such that:\n";
+ const char* sep = "";
+ for (size_t i = 0; i != matcher_describers_.size(); ++i) {
+ *os << sep << " - element #" << i << " ";
+ matcher_describers_[i]->DescribeTo(os);
+ sep = ", and\n";
+ }
+}
+
+void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(
+ ::std::ostream* os) const {
+ if (matcher_describers_.empty()) {
+ *os << "isn't empty";
+ return;
+ }
+ if (matcher_describers_.size() == 1) {
+ *os << "doesn't have " << Elements(1)
+ << ", or has " << Elements(1) << " that ";
+ matcher_describers_[0]->DescribeNegationTo(os);
+ return;
+ }
+ *os << "doesn't have " << Elements(matcher_describers_.size())
+ << ", or there exists no permutation of elements such that:\n";
+ const char* sep = "";
+ for (size_t i = 0; i != matcher_describers_.size(); ++i) {
+ *os << sep << " - element #" << i << " ";
+ matcher_describers_[i]->DescribeTo(os);
+ sep = ", and\n";
+ }
+}
+
+// Checks that all matchers match at least one element, and that all
+// elements match at least one matcher. This enables faster matching
+// and better error reporting.
+// Returns false, writing an explanation to 'listener', if and only
+// if the success criteria are not met.
+bool UnorderedElementsAreMatcherImplBase::
+VerifyAllElementsAndMatchersAreMatched(
+ const ::std::vector<string>& element_printouts,
+ const MatchMatrix& matrix,
+ MatchResultListener* listener) const {
+ bool result = true;
+ ::std::vector<char> element_matched(matrix.LhsSize(), 0);
+ ::std::vector<char> matcher_matched(matrix.RhsSize(), 0);
+
+ for (size_t ilhs = 0; ilhs < matrix.LhsSize(); ilhs++) {
+ for (size_t irhs = 0; irhs < matrix.RhsSize(); irhs++) {
+ char matched = matrix.HasEdge(ilhs, irhs);
+ element_matched[ilhs] |= matched;
+ matcher_matched[irhs] |= matched;
+ }
+ }
+
+ {
+ const char* sep =
+ "where the following matchers don't match any elements:\n";
+ for (size_t mi = 0; mi < matcher_matched.size(); ++mi) {
+ if (matcher_matched[mi])
+ continue;
+ result = false;
+ if (listener->IsInterested()) {
+ *listener << sep << "matcher #" << mi << ": ";
+ matcher_describers_[mi]->DescribeTo(listener->stream());
+ sep = ",\n";
+ }
+ }
+ }
+
+ {
+ const char* sep =
+ "where the following elements don't match any matchers:\n";
+ const char* outer_sep = "";
+ if (!result) {
+ outer_sep = "\nand ";
+ }
+ for (size_t ei = 0; ei < element_matched.size(); ++ei) {
+ if (element_matched[ei])
+ continue;
+ result = false;
+ if (listener->IsInterested()) {
+ *listener << outer_sep << sep << "element #" << ei << ": "
+ << element_printouts[ei];
+ sep = ",\n";
+ outer_sep = "";
+ }
+ }
+ }
+ return result;
+}
+
+} // namespace internal
+} // namespace testing
diff --git a/extern/gmock/src/gmock-spec-builders.cc b/extern/gmock/src/gmock-spec-builders.cc
new file mode 100644
index 00000000000..95513420707
--- /dev/null
+++ b/extern/gmock/src/gmock-spec-builders.cc
@@ -0,0 +1,823 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements the spec builder syntax (ON_CALL and
+// EXPECT_CALL).
+
+#include "gmock/gmock-spec-builders.h"
+
+#include <stdlib.h>
+#include <iostream> // NOLINT
+#include <map>
+#include <set>
+#include <string>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC
+# include <unistd.h> // NOLINT
+#endif
+
+namespace testing {
+namespace internal {
+
+// Protects the mock object registry (in class Mock), all function
+// mockers, and all expectations.
+GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex);
+
+// Logs a message including file and line number information.
+GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity,
+ const char* file, int line,
+ const string& message) {
+ ::std::ostringstream s;
+ s << file << ":" << line << ": " << message << ::std::endl;
+ Log(severity, s.str(), 0);
+}
+
+// Constructs an ExpectationBase object.
+ExpectationBase::ExpectationBase(const char* a_file,
+ int a_line,
+ const string& a_source_text)
+ : file_(a_file),
+ line_(a_line),
+ source_text_(a_source_text),
+ cardinality_specified_(false),
+ cardinality_(Exactly(1)),
+ call_count_(0),
+ retired_(false),
+ extra_matcher_specified_(false),
+ repeated_action_specified_(false),
+ retires_on_saturation_(false),
+ last_clause_(kNone),
+ action_count_checked_(false) {}
+
+// Destructs an ExpectationBase object.
+ExpectationBase::~ExpectationBase() {}
+
+// Explicitly specifies the cardinality of this expectation. Used by
+// the subclasses to implement the .Times() clause.
+void ExpectationBase::SpecifyCardinality(const Cardinality& a_cardinality) {
+ cardinality_specified_ = true;
+ cardinality_ = a_cardinality;
+}
+
+// Retires all pre-requisites of this expectation.
+void ExpectationBase::RetireAllPreRequisites()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ if (is_retired()) {
+ // We can take this short-cut as we never retire an expectation
+ // until we have retired all its pre-requisites.
+ return;
+ }
+
+ for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
+ it != immediate_prerequisites_.end(); ++it) {
+ ExpectationBase* const prerequisite = it->expectation_base().get();
+ if (!prerequisite->is_retired()) {
+ prerequisite->RetireAllPreRequisites();
+ prerequisite->Retire();
+ }
+ }
+}
+
+// Returns true iff all pre-requisites of this expectation have been
+// satisfied.
+bool ExpectationBase::AllPrerequisitesAreSatisfied() const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
+ it != immediate_prerequisites_.end(); ++it) {
+ if (!(it->expectation_base()->IsSatisfied()) ||
+ !(it->expectation_base()->AllPrerequisitesAreSatisfied()))
+ return false;
+ }
+ return true;
+}
+
+// Adds unsatisfied pre-requisites of this expectation to 'result'.
+void ExpectationBase::FindUnsatisfiedPrerequisites(ExpectationSet* result) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
+ it != immediate_prerequisites_.end(); ++it) {
+ if (it->expectation_base()->IsSatisfied()) {
+ // If *it is satisfied and has a call count of 0, some of its
+ // pre-requisites may not be satisfied yet.
+ if (it->expectation_base()->call_count_ == 0) {
+ it->expectation_base()->FindUnsatisfiedPrerequisites(result);
+ }
+ } else {
+ // Now that we know *it is unsatisfied, we are not so interested
+ // in whether its pre-requisites are satisfied. Therefore we
+ // don't recursively call FindUnsatisfiedPrerequisites() here.
+ *result += *it;
+ }
+ }
+}
+
+// Describes how many times a function call matching this
+// expectation has occurred.
+void ExpectationBase::DescribeCallCountTo(::std::ostream* os) const
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+
+ // Describes how many times the function is expected to be called.
+ *os << " Expected: to be ";
+ cardinality().DescribeTo(os);
+ *os << "\n Actual: ";
+ Cardinality::DescribeActualCallCountTo(call_count(), os);
+
+ // Describes the state of the expectation (e.g. is it satisfied?
+ // is it active?).
+ *os << " - " << (IsOverSaturated() ? "over-saturated" :
+ IsSaturated() ? "saturated" :
+ IsSatisfied() ? "satisfied" : "unsatisfied")
+ << " and "
+ << (is_retired() ? "retired" : "active");
+}
+
+// Checks the action count (i.e. the number of WillOnce() and
+// WillRepeatedly() clauses) against the cardinality if this hasn't
+// been done before. Prints a warning if there are too many or too
+// few actions.
+void ExpectationBase::CheckActionCountIfNotDone() const
+ GTEST_LOCK_EXCLUDED_(mutex_) {
+ bool should_check = false;
+ {
+ MutexLock l(&mutex_);
+ if (!action_count_checked_) {
+ action_count_checked_ = true;
+ should_check = true;
+ }
+ }
+
+ if (should_check) {
+ if (!cardinality_specified_) {
+ // The cardinality was inferred - no need to check the action
+ // count against it.
+ return;
+ }
+
+ // The cardinality was explicitly specified.
+ const int action_count = static_cast<int>(untyped_actions_.size());
+ const int upper_bound = cardinality().ConservativeUpperBound();
+ const int lower_bound = cardinality().ConservativeLowerBound();
+ bool too_many; // True if there are too many actions, or false
+ // if there are too few.
+ if (action_count > upper_bound ||
+ (action_count == upper_bound && repeated_action_specified_)) {
+ too_many = true;
+ } else if (0 < action_count && action_count < lower_bound &&
+ !repeated_action_specified_) {
+ too_many = false;
+ } else {
+ return;
+ }
+
+ ::std::stringstream ss;
+ DescribeLocationTo(&ss);
+ ss << "Too " << (too_many ? "many" : "few")
+ << " actions specified in " << source_text() << "...\n"
+ << "Expected to be ";
+ cardinality().DescribeTo(&ss);
+ ss << ", but has " << (too_many ? "" : "only ")
+ << action_count << " WillOnce()"
+ << (action_count == 1 ? "" : "s");
+ if (repeated_action_specified_) {
+ ss << " and a WillRepeatedly()";
+ }
+ ss << ".";
+ Log(kWarning, ss.str(), -1); // -1 means "don't print stack trace".
+ }
+}
+
+// Implements the .Times() clause.
+void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) {
+ if (last_clause_ == kTimes) {
+ ExpectSpecProperty(false,
+ ".Times() cannot appear "
+ "more than once in an EXPECT_CALL().");
+ } else {
+ ExpectSpecProperty(last_clause_ < kTimes,
+ ".Times() cannot appear after "
+ ".InSequence(), .WillOnce(), .WillRepeatedly(), "
+ "or .RetiresOnSaturation().");
+ }
+ last_clause_ = kTimes;
+
+ SpecifyCardinality(a_cardinality);
+}
+
+// Points to the implicit sequence introduced by a living InSequence
+// object (if any) in the current thread or NULL.
+GTEST_API_ ThreadLocal<Sequence*> g_gmock_implicit_sequence;
+
+// Reports an uninteresting call (whose description is in msg) in the
+// manner specified by 'reaction'.
+void ReportUninterestingCall(CallReaction reaction, const string& msg) {
+ // Include a stack trace only if --gmock_verbose=info is specified.
+ const int stack_frames_to_skip =
+ GMOCK_FLAG(verbose) == kInfoVerbosity ? 3 : -1;
+ switch (reaction) {
+ case kAllow:
+ Log(kInfo, msg, stack_frames_to_skip);
+ break;
+ case kWarn:
+ Log(kWarning,
+ msg +
+ "\nNOTE: You can safely ignore the above warning unless this "
+ "call should not happen. Do not suppress it by blindly adding "
+ "an EXPECT_CALL() if you don't mean to enforce the call. "
+ "See https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#"
+ "knowing-when-to-expect for details.\n",
+ stack_frames_to_skip);
+ break;
+ default: // FAIL
+ Expect(false, NULL, -1, msg);
+ }
+}
+
+UntypedFunctionMockerBase::UntypedFunctionMockerBase()
+ : mock_obj_(NULL), name_("") {}
+
+UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {}
+
+// Sets the mock object this mock method belongs to, and registers
+// this information in the global mock registry. Will be called
+// whenever an EXPECT_CALL() or ON_CALL() is executed on this mock
+// method.
+void UntypedFunctionMockerBase::RegisterOwner(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ {
+ MutexLock l(&g_gmock_mutex);
+ mock_obj_ = mock_obj;
+ }
+ Mock::Register(mock_obj, this);
+}
+
+// Sets the mock object this mock method belongs to, and sets the name
+// of the mock function. Will be called upon each invocation of this
+// mock function.
+void UntypedFunctionMockerBase::SetOwnerAndName(const void* mock_obj,
+ const char* name)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ // We protect name_ under g_gmock_mutex in case this mock function
+ // is called from two threads concurrently.
+ MutexLock l(&g_gmock_mutex);
+ mock_obj_ = mock_obj;
+ name_ = name;
+}
+
+// Returns the name of the function being mocked. Must be called
+// after RegisterOwner() or SetOwnerAndName() has been called.
+const void* UntypedFunctionMockerBase::MockObject() const
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ const void* mock_obj;
+ {
+ // We protect mock_obj_ under g_gmock_mutex in case this mock
+ // function is called from two threads concurrently.
+ MutexLock l(&g_gmock_mutex);
+ Assert(mock_obj_ != NULL, __FILE__, __LINE__,
+ "MockObject() must not be called before RegisterOwner() or "
+ "SetOwnerAndName() has been called.");
+ mock_obj = mock_obj_;
+ }
+ return mock_obj;
+}
+
+// Returns the name of this mock method. Must be called after
+// SetOwnerAndName() has been called.
+const char* UntypedFunctionMockerBase::Name() const
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ const char* name;
+ {
+ // We protect name_ under g_gmock_mutex in case this mock
+ // function is called from two threads concurrently.
+ MutexLock l(&g_gmock_mutex);
+ Assert(name_ != NULL, __FILE__, __LINE__,
+ "Name() must not be called before SetOwnerAndName() has "
+ "been called.");
+ name = name_;
+ }
+ return name;
+}
+
+// Calculates the result of invoking this mock function with the given
+// arguments, prints it, and returns it. The caller is responsible
+// for deleting the result.
+UntypedActionResultHolderBase*
+UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
+ GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+ if (untyped_expectations_.size() == 0) {
+ // No expectation is set on this mock method - we have an
+ // uninteresting call.
+
+ // We must get Google Mock's reaction on uninteresting calls
+ // made on this mock object BEFORE performing the action,
+ // because the action may DELETE the mock object and make the
+ // following expression meaningless.
+ const CallReaction reaction =
+ Mock::GetReactionOnUninterestingCalls(MockObject());
+
+ // True iff we need to print this call's arguments and return
+ // value. This definition must be kept in sync with
+ // the behavior of ReportUninterestingCall().
+ const bool need_to_report_uninteresting_call =
+ // If the user allows this uninteresting call, we print it
+ // only when he wants informational messages.
+ reaction == kAllow ? LogIsVisible(kInfo) :
+ // If the user wants this to be a warning, we print it only
+ // when he wants to see warnings.
+ reaction == kWarn ? LogIsVisible(kWarning) :
+ // Otherwise, the user wants this to be an error, and we
+ // should always print detailed information in the error.
+ true;
+
+ if (!need_to_report_uninteresting_call) {
+ // Perform the action without printing the call information.
+ return this->UntypedPerformDefaultAction(untyped_args, "");
+ }
+
+ // Warns about the uninteresting call.
+ ::std::stringstream ss;
+ this->UntypedDescribeUninterestingCall(untyped_args, &ss);
+
+ // Calculates the function result.
+ UntypedActionResultHolderBase* const result =
+ this->UntypedPerformDefaultAction(untyped_args, ss.str());
+
+ // Prints the function result.
+ if (result != NULL)
+ result->PrintAsActionResult(&ss);
+
+ ReportUninterestingCall(reaction, ss.str());
+ return result;
+ }
+
+ bool is_excessive = false;
+ ::std::stringstream ss;
+ ::std::stringstream why;
+ ::std::stringstream loc;
+ const void* untyped_action = NULL;
+
+ // The UntypedFindMatchingExpectation() function acquires and
+ // releases g_gmock_mutex.
+ const ExpectationBase* const untyped_expectation =
+ this->UntypedFindMatchingExpectation(
+ untyped_args, &untyped_action, &is_excessive,
+ &ss, &why);
+ const bool found = untyped_expectation != NULL;
+
+ // True iff we need to print the call's arguments and return value.
+ // This definition must be kept in sync with the uses of Expect()
+ // and Log() in this function.
+ const bool need_to_report_call =
+ !found || is_excessive || LogIsVisible(kInfo);
+ if (!need_to_report_call) {
+ // Perform the action without printing the call information.
+ return
+ untyped_action == NULL ?
+ this->UntypedPerformDefaultAction(untyped_args, "") :
+ this->UntypedPerformAction(untyped_action, untyped_args);
+ }
+
+ ss << " Function call: " << Name();
+ this->UntypedPrintArgs(untyped_args, &ss);
+
+ // In case the action deletes a piece of the expectation, we
+ // generate the message beforehand.
+ if (found && !is_excessive) {
+ untyped_expectation->DescribeLocationTo(&loc);
+ }
+
+ UntypedActionResultHolderBase* const result =
+ untyped_action == NULL ?
+ this->UntypedPerformDefaultAction(untyped_args, ss.str()) :
+ this->UntypedPerformAction(untyped_action, untyped_args);
+ if (result != NULL)
+ result->PrintAsActionResult(&ss);
+ ss << "\n" << why.str();
+
+ if (!found) {
+ // No expectation matches this call - reports a failure.
+ Expect(false, NULL, -1, ss.str());
+ } else if (is_excessive) {
+ // We had an upper-bound violation and the failure message is in ss.
+ Expect(false, untyped_expectation->file(),
+ untyped_expectation->line(), ss.str());
+ } else {
+ // We had an expected call and the matching expectation is
+ // described in ss.
+ Log(kInfo, loc.str() + ss.str(), 2);
+ }
+
+ return result;
+}
+
+// Returns an Expectation object that references and co-owns exp,
+// which must be an expectation on this mock function.
+Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) {
+ for (UntypedExpectations::const_iterator it =
+ untyped_expectations_.begin();
+ it != untyped_expectations_.end(); ++it) {
+ if (it->get() == exp) {
+ return Expectation(*it);
+ }
+ }
+
+ Assert(false, __FILE__, __LINE__, "Cannot find expectation.");
+ return Expectation();
+ // The above statement is just to make the code compile, and will
+ // never be executed.
+}
+
+// Verifies that all expectations on this mock function have been
+// satisfied. Reports one or more Google Test non-fatal failures
+// and returns false if not.
+bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked()
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+ g_gmock_mutex.AssertHeld();
+ bool expectations_met = true;
+ for (UntypedExpectations::const_iterator it =
+ untyped_expectations_.begin();
+ it != untyped_expectations_.end(); ++it) {
+ ExpectationBase* const untyped_expectation = it->get();
+ if (untyped_expectation->IsOverSaturated()) {
+ // There was an upper-bound violation. Since the error was
+ // already reported when it occurred, there is no need to do
+ // anything here.
+ expectations_met = false;
+ } else if (!untyped_expectation->IsSatisfied()) {
+ expectations_met = false;
+ ::std::stringstream ss;
+ ss << "Actual function call count doesn't match "
+ << untyped_expectation->source_text() << "...\n";
+ // No need to show the source file location of the expectation
+ // in the description, as the Expect() call that follows already
+ // takes care of it.
+ untyped_expectation->MaybeDescribeExtraMatcherTo(&ss);
+ untyped_expectation->DescribeCallCountTo(&ss);
+ Expect(false, untyped_expectation->file(),
+ untyped_expectation->line(), ss.str());
+ }
+ }
+
+ // Deleting our expectations may trigger other mock objects to be deleted, for
+ // example if an action contains a reference counted smart pointer to that
+ // mock object, and that is the last reference. So if we delete our
+ // expectations within the context of the global mutex we may deadlock when
+ // this method is called again. Instead, make a copy of the set of
+ // expectations to delete, clear our set within the mutex, and then clear the
+ // copied set outside of it.
+ UntypedExpectations expectations_to_delete;
+ untyped_expectations_.swap(expectations_to_delete);
+
+ g_gmock_mutex.Unlock();
+ expectations_to_delete.clear();
+ g_gmock_mutex.Lock();
+
+ return expectations_met;
+}
+
+} // namespace internal
+
+// Class Mock.
+
+namespace {
+
+typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers;
+
+// The current state of a mock object. Such information is needed for
+// detecting leaked mock objects and explicitly verifying a mock's
+// expectations.
+struct MockObjectState {
+ MockObjectState()
+ : first_used_file(NULL), first_used_line(-1), leakable(false) {}
+
+ // Where in the source file an ON_CALL or EXPECT_CALL is first
+ // invoked on this mock object.
+ const char* first_used_file;
+ int first_used_line;
+ ::std::string first_used_test_case;
+ ::std::string first_used_test;
+ bool leakable; // true iff it's OK to leak the object.
+ FunctionMockers function_mockers; // All registered methods of the object.
+};
+
+// A global registry holding the state of all mock objects that are
+// alive. A mock object is added to this registry the first time
+// Mock::AllowLeak(), ON_CALL(), or EXPECT_CALL() is called on it. It
+// is removed from the registry in the mock object's destructor.
+class MockObjectRegistry {
+ public:
+ // Maps a mock object (identified by its address) to its state.
+ typedef std::map<const void*, MockObjectState> StateMap;
+
+ // This destructor will be called when a program exits, after all
+ // tests in it have been run. By then, there should be no mock
+ // object alive. Therefore we report any living object as test
+ // failure, unless the user explicitly asked us to ignore it.
+ ~MockObjectRegistry() {
+ // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is
+ // a macro.
+
+ if (!GMOCK_FLAG(catch_leaked_mocks))
+ return;
+
+ int leaked_count = 0;
+ for (StateMap::const_iterator it = states_.begin(); it != states_.end();
+ ++it) {
+ if (it->second.leakable) // The user said it's fine to leak this object.
+ continue;
+
+ // TODO(wan@google.com): Print the type of the leaked object.
+ // This can help the user identify the leaked object.
+ std::cout << "\n";
+ const MockObjectState& state = it->second;
+ std::cout << internal::FormatFileLocation(state.first_used_file,
+ state.first_used_line);
+ std::cout << " ERROR: this mock object";
+ if (state.first_used_test != "") {
+ std::cout << " (used in test " << state.first_used_test_case << "."
+ << state.first_used_test << ")";
+ }
+ std::cout << " should be deleted but never is. Its address is @"
+ << it->first << ".";
+ leaked_count++;
+ }
+ if (leaked_count > 0) {
+ std::cout << "\nERROR: " << leaked_count
+ << " leaked mock " << (leaked_count == 1 ? "object" : "objects")
+ << " found at program exit.\n";
+ std::cout.flush();
+ ::std::cerr.flush();
+ // RUN_ALL_TESTS() has already returned when this destructor is
+ // called. Therefore we cannot use the normal Google Test
+ // failure reporting mechanism.
+ _exit(1); // We cannot call exit() as it is not reentrant and
+ // may already have been called.
+ }
+ }
+
+ StateMap& states() { return states_; }
+
+ private:
+ StateMap states_;
+};
+
+// Protected by g_gmock_mutex.
+MockObjectRegistry g_mock_object_registry;
+
+// Maps a mock object to the reaction Google Mock should have when an
+// uninteresting method is called. Protected by g_gmock_mutex.
+std::map<const void*, internal::CallReaction> g_uninteresting_call_reaction;
+
+// Sets the reaction Google Mock should have when an uninteresting
+// method of the given mock object is called.
+void SetReactionOnUninterestingCalls(const void* mock_obj,
+ internal::CallReaction reaction)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ g_uninteresting_call_reaction[mock_obj] = reaction;
+}
+
+} // namespace
+
+// Tells Google Mock to allow uninteresting calls on the given mock
+// object.
+void Mock::AllowUninterestingCalls(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ SetReactionOnUninterestingCalls(mock_obj, internal::kAllow);
+}
+
+// Tells Google Mock to warn the user about uninteresting calls on the
+// given mock object.
+void Mock::WarnUninterestingCalls(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ SetReactionOnUninterestingCalls(mock_obj, internal::kWarn);
+}
+
+// Tells Google Mock to fail uninteresting calls on the given mock
+// object.
+void Mock::FailUninterestingCalls(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ SetReactionOnUninterestingCalls(mock_obj, internal::kFail);
+}
+
+// Tells Google Mock the given mock object is being destroyed and its
+// entry in the call-reaction table should be removed.
+void Mock::UnregisterCallReaction(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ g_uninteresting_call_reaction.erase(mock_obj);
+}
+
+// Returns the reaction Google Mock will have on uninteresting calls
+// made on the given mock object.
+internal::CallReaction Mock::GetReactionOnUninterestingCalls(
+ const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ return (g_uninteresting_call_reaction.count(mock_obj) == 0) ?
+ internal::kDefault : g_uninteresting_call_reaction[mock_obj];
+}
+
+// Tells Google Mock to ignore mock_obj when checking for leaked mock
+// objects.
+void Mock::AllowLeak(const void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ g_mock_object_registry.states()[mock_obj].leakable = true;
+}
+
+// Verifies and clears all expectations on the given mock object. If
+// the expectations aren't satisfied, generates one or more Google
+// Test non-fatal failures and returns false.
+bool Mock::VerifyAndClearExpectations(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ return VerifyAndClearExpectationsLocked(mock_obj);
+}
+
+// Verifies all expectations on the given mock object and clears its
+// default actions and expectations. Returns true iff the
+// verification was successful.
+bool Mock::VerifyAndClear(void* mock_obj)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ ClearDefaultActionsLocked(mock_obj);
+ return VerifyAndClearExpectationsLocked(mock_obj);
+}
+
+// Verifies and clears all expectations on the given mock object. If
+// the expectations aren't satisfied, generates one or more Google
+// Test non-fatal failures and returns false.
+bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) {
+ internal::g_gmock_mutex.AssertHeld();
+ if (g_mock_object_registry.states().count(mock_obj) == 0) {
+ // No EXPECT_CALL() was set on the given mock object.
+ return true;
+ }
+
+ // Verifies and clears the expectations on each mock method in the
+ // given mock object.
+ bool expectations_met = true;
+ FunctionMockers& mockers =
+ g_mock_object_registry.states()[mock_obj].function_mockers;
+ for (FunctionMockers::const_iterator it = mockers.begin();
+ it != mockers.end(); ++it) {
+ if (!(*it)->VerifyAndClearExpectationsLocked()) {
+ expectations_met = false;
+ }
+ }
+
+ // We don't clear the content of mockers, as they may still be
+ // needed by ClearDefaultActionsLocked().
+ return expectations_met;
+}
+
+// Registers a mock object and a mock method it owns.
+void Mock::Register(const void* mock_obj,
+ internal::UntypedFunctionMockerBase* mocker)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker);
+}
+
+// Tells Google Mock where in the source code mock_obj is used in an
+// ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this
+// information helps the user identify which object it is.
+void Mock::RegisterUseByOnCallOrExpectCall(const void* mock_obj,
+ const char* file, int line)
+ GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
+ internal::MutexLock l(&internal::g_gmock_mutex);
+ MockObjectState& state = g_mock_object_registry.states()[mock_obj];
+ if (state.first_used_file == NULL) {
+ state.first_used_file = file;
+ state.first_used_line = line;
+ const TestInfo* const test_info =
+ UnitTest::GetInstance()->current_test_info();
+ if (test_info != NULL) {
+ // TODO(wan@google.com): record the test case name when the
+ // ON_CALL or EXPECT_CALL is invoked from SetUpTestCase() or
+ // TearDownTestCase().
+ state.first_used_test_case = test_info->test_case_name();
+ state.first_used_test = test_info->name();
+ }
+ }
+}
+
+// Unregisters a mock method; removes the owning mock object from the
+// registry when the last mock method associated with it has been
+// unregistered. This is called only in the destructor of
+// FunctionMockerBase.
+void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) {
+ internal::g_gmock_mutex.AssertHeld();
+ for (MockObjectRegistry::StateMap::iterator it =
+ g_mock_object_registry.states().begin();
+ it != g_mock_object_registry.states().end(); ++it) {
+ FunctionMockers& mockers = it->second.function_mockers;
+ if (mockers.erase(mocker) > 0) {
+ // mocker was in mockers and has been just removed.
+ if (mockers.empty()) {
+ g_mock_object_registry.states().erase(it);
+ }
+ return;
+ }
+ }
+}
+
+// Clears all ON_CALL()s set on the given mock object.
+void Mock::ClearDefaultActionsLocked(void* mock_obj)
+ GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) {
+ internal::g_gmock_mutex.AssertHeld();
+
+ if (g_mock_object_registry.states().count(mock_obj) == 0) {
+ // No ON_CALL() was set on the given mock object.
+ return;
+ }
+
+ // Clears the default actions for each mock method in the given mock
+ // object.
+ FunctionMockers& mockers =
+ g_mock_object_registry.states()[mock_obj].function_mockers;
+ for (FunctionMockers::const_iterator it = mockers.begin();
+ it != mockers.end(); ++it) {
+ (*it)->ClearDefaultActionsLocked();
+ }
+
+ // We don't clear the content of mockers, as they may still be
+ // needed by VerifyAndClearExpectationsLocked().
+}
+
+Expectation::Expectation() {}
+
+Expectation::Expectation(
+ const internal::linked_ptr<internal::ExpectationBase>& an_expectation_base)
+ : expectation_base_(an_expectation_base) {}
+
+Expectation::~Expectation() {}
+
+// Adds an expectation to a sequence.
+void Sequence::AddExpectation(const Expectation& expectation) const {
+ if (*last_expectation_ != expectation) {
+ if (last_expectation_->expectation_base() != NULL) {
+ expectation.expectation_base()->immediate_prerequisites_
+ += *last_expectation_;
+ }
+ *last_expectation_ = expectation;
+ }
+}
+
+// Creates the implicit sequence if there isn't one.
+InSequence::InSequence() {
+ if (internal::g_gmock_implicit_sequence.get() == NULL) {
+ internal::g_gmock_implicit_sequence.set(new Sequence);
+ sequence_created_ = true;
+ } else {
+ sequence_created_ = false;
+ }
+}
+
+// Deletes the implicit sequence if it was created by the constructor
+// of this object.
+InSequence::~InSequence() {
+ if (sequence_created_) {
+ delete internal::g_gmock_implicit_sequence.get();
+ internal::g_gmock_implicit_sequence.set(NULL);
+ }
+}
+
+} // namespace testing
diff --git a/extern/gmock/src/gmock.cc b/extern/gmock/src/gmock.cc
new file mode 100644
index 00000000000..eac3d842ba0
--- /dev/null
+++ b/extern/gmock/src/gmock.cc
@@ -0,0 +1,183 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#include "gmock/gmock.h"
+#include "gmock/internal/gmock-port.h"
+
+namespace testing {
+
+// TODO(wan@google.com): support using environment variables to
+// control the flag values, like what Google Test does.
+
+GMOCK_DEFINE_bool_(catch_leaked_mocks, true,
+ "true iff Google Mock should report leaked mock objects "
+ "as failures.");
+
+GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity,
+ "Controls how verbose Google Mock's output is."
+ " Valid values:\n"
+ " info - prints all messages.\n"
+ " warning - prints warnings and errors.\n"
+ " error - prints errors only.");
+
+namespace internal {
+
+// Parses a string as a command line flag. The string should have the
+// format "--gmock_flag=value". When def_optional is true, the
+// "=value" part can be omitted.
+//
+// Returns the value of the flag, or NULL if the parsing failed.
+static const char* ParseGoogleMockFlagValue(const char* str,
+ const char* flag,
+ bool def_optional) {
+ // str and flag must not be NULL.
+ if (str == NULL || flag == NULL) return NULL;
+
+ // The flag must start with "--gmock_".
+ const std::string flag_str = std::string("--gmock_") + flag;
+ const size_t flag_len = flag_str.length();
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+
+ // Skips the flag name.
+ const char* flag_end = str + flag_len;
+
+ // When def_optional is true, it's OK to not have a "=value" part.
+ if (def_optional && (flag_end[0] == '\0')) {
+ return flag_end;
+ }
+
+ // If def_optional is true and there are more characters after the
+ // flag name, or if def_optional is false, there must be a '=' after
+ // the flag name.
+ if (flag_end[0] != '=') return NULL;
+
+ // Returns the string after "=".
+ return flag_end + 1;
+}
+
+// Parses a string for a Google Mock bool flag, in the form of
+// "--gmock_flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+static bool ParseGoogleMockBoolFlag(const char* str, const char* flag,
+ bool* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseGoogleMockFlagValue(str, flag, true);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Converts the string value to a bool.
+ *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
+ return true;
+}
+
+// Parses a string for a Google Mock string flag, in the form of
+// "--gmock_flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+template <typename String>
+static bool ParseGoogleMockStringFlag(const char* str, const char* flag,
+ String* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseGoogleMockFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == NULL) return false;
+
+ // Sets *value to the value of the flag.
+ *value = value_str;
+ return true;
+}
+
+// The internal implementation of InitGoogleMock().
+//
+// The type parameter CharType can be instantiated to either char or
+// wchar_t.
+template <typename CharType>
+void InitGoogleMockImpl(int* argc, CharType** argv) {
+ // Makes sure Google Test is initialized. InitGoogleTest() is
+ // idempotent, so it's fine if the user has already called it.
+ InitGoogleTest(argc, argv);
+ if (*argc <= 0) return;
+
+ for (int i = 1; i != *argc; i++) {
+ const std::string arg_string = StreamableToString(argv[i]);
+ const char* const arg = arg_string.c_str();
+
+ // Do we see a Google Mock flag?
+ if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks",
+ &GMOCK_FLAG(catch_leaked_mocks)) ||
+ ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) {
+ // Yes. Shift the remainder of the argv list left by one. Note
+ // that argv has (*argc + 1) elements, the last one always being
+ // NULL. The following loop moves the trailing NULL element as
+ // well.
+ for (int j = i; j != *argc; j++) {
+ argv[j] = argv[j + 1];
+ }
+
+ // Decrements the argument count.
+ (*argc)--;
+
+ // We also need to decrement the iterator as we just removed
+ // an element.
+ i--;
+ }
+ }
+}
+
+} // namespace internal
+
+// Initializes Google Mock. This must be called before running the
+// tests. In particular, it parses a command line for the flags that
+// Google Mock recognizes. Whenever a Google Mock flag is seen, it is
+// removed from argv, and *argc is decremented.
+//
+// No value is returned. Instead, the Google Mock flag variables are
+// updated.
+//
+// Since Google Test is needed for Google Mock to work, this function
+// also initializes Google Test and parses its flags, if that hasn't
+// been done.
+GTEST_API_ void InitGoogleMock(int* argc, char** argv) {
+ internal::InitGoogleMockImpl(argc, argv);
+}
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv) {
+ internal::InitGoogleMockImpl(argc, argv);
+}
+
+} // namespace testing
diff --git a/extern/gmock/src/gmock_main.cc b/extern/gmock/src/gmock_main.cc
new file mode 100644
index 00000000000..bd5be03be22
--- /dev/null
+++ b/extern/gmock/src/gmock_main.cc
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#include <iostream>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+// MS C++ compiler/linker has a bug on Windows (not on Windows CE), which
+// causes a link error when _tmain is defined in a static library and UNICODE
+// is enabled. For this reason instead of _tmain, main function is used on
+// Windows. See the following link to track the current status of this bug:
+// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=394464 // NOLINT
+#if GTEST_OS_WINDOWS_MOBILE
+# include <tchar.h> // NOLINT
+
+GTEST_API_ int _tmain(int argc, TCHAR** argv) {
+#else
+GTEST_API_ int main(int argc, char** argv) {
+#endif // GTEST_OS_WINDOWS_MOBILE
+ std::cout << "Running main() from gmock_main.cc\n";
+ // Since Google Mock depends on Google Test, InitGoogleMock() is
+ // also responsible for initializing Google Test. Therefore there's
+ // no need for calling testing::InitGoogleTest() separately.
+ testing::InitGoogleMock(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/extern/gtest/README.blender b/extern/gtest/README.blender
index 81c04ad3f3c..41dda92c19c 100644
--- a/extern/gtest/README.blender
+++ b/extern/gtest/README.blender
@@ -1,5 +1,7 @@
Project: Google C++ Testing Framework
URL: https://github.com/google/googletest
License: New BSD
-Upstream version: 1.7.0
-Local modifications:None
+Upstream version: 1.7.0 (ec44c6c)
+Local modifications:
+
+None.
diff --git a/extern/gtest/include/gtest/gtest-param-test.h b/extern/gtest/include/gtest/gtest-param-test.h
index d6702c8f162..038f9ba79eb 100644
--- a/extern/gtest/include/gtest/gtest-param-test.h
+++ b/extern/gtest/include/gtest/gtest-param-test.h
@@ -1387,14 +1387,17 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
static int AddToRegistry() { \
::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
GetTestCasePatternHolder<test_case_name>(\
- #test_case_name, __FILE__, __LINE__)->AddTestPattern(\
- #test_case_name, \
- #test_name, \
- new ::testing::internal::TestMetaFactory< \
- GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \
+ #test_case_name, \
+ ::testing::internal::CodeLocation(\
+ __FILE__, __LINE__))->AddTestPattern(\
+ #test_case_name, \
+ #test_name, \
+ new ::testing::internal::TestMetaFactory< \
+ GTEST_TEST_CLASS_NAME_(\
+ test_case_name, test_name)>()); \
return 0; \
} \
- static int gtest_registering_dummy_; \
+ static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
GTEST_DISALLOW_COPY_AND_ASSIGN_(\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
}; \
@@ -1403,16 +1406,36 @@ internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
-# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+// The optional last argument to INSTANTIATE_TEST_CASE_P allows the user
+// to specify a function or functor that generates custom test name suffixes
+// based on the test parameters. The function should accept one argument of
+// type testing::TestParamInfo<class ParamType>, and return std::string.
+//
+// testing::PrintToStringParamName is a builtin test suffix generator that
+// returns the value of testing::PrintToString(GetParam()). It does not work
+// for std::string or C strings.
+//
+// Note: test names must be non-empty, unique, and may only contain ASCII
+// alphanumeric characters or underscore.
+
+# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \
::testing::internal::ParamGenerator<test_case_name::ParamType> \
gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
- int gtest_##prefix##test_case_name##_dummy_ = \
+ ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \
+ const ::testing::TestParamInfo<test_case_name::ParamType>& info) { \
+ return ::testing::internal::GetParamNameGen<test_case_name::ParamType> \
+ (__VA_ARGS__)(info); \
+ } \
+ int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \
::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
GetTestCasePatternHolder<test_case_name>(\
- #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\
- #prefix, \
- &gtest_##prefix##test_case_name##_EvalGenerator_, \
- __FILE__, __LINE__)
+ #test_case_name, \
+ ::testing::internal::CodeLocation(\
+ __FILE__, __LINE__))->AddTestCaseInstantiation(\
+ #prefix, \
+ &gtest_##prefix##test_case_name##_EvalGenerator_, \
+ &gtest_##prefix##test_case_name##_EvalGenerateName_, \
+ __FILE__, __LINE__)
} // namespace testing
diff --git a/extern/gtest/include/gtest/gtest-printers.h b/extern/gtest/include/gtest/gtest-printers.h
index 0639d9f5869..27a1edc3728 100644
--- a/extern/gtest/include/gtest/gtest-printers.h
+++ b/extern/gtest/include/gtest/gtest-printers.h
@@ -103,6 +103,10 @@
#include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-internal.h"
+#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+# include <tuple>
+#endif
+
namespace testing {
// Definitions in the 'internal' and 'internal2' name spaces are
@@ -250,6 +254,103 @@ void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
namespace testing {
namespace internal {
+// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
+// value of type ToPrint that is an operand of a comparison assertion
+// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in
+// the comparison, and is used to help determine the best way to
+// format the value. In particular, when the value is a C string
+// (char pointer) and the other operand is an STL string object, we
+// want to format the C string as a string, since we know it is
+// compared by value with the string object. If the value is a char
+// pointer but the other operand is not an STL string object, we don't
+// know whether the pointer is supposed to point to a NUL-terminated
+// string, and thus want to print it as a pointer to be safe.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// The default case.
+template <typename ToPrint, typename OtherOperand>
+class FormatForComparison {
+ public:
+ static ::std::string Format(const ToPrint& value) {
+ return ::testing::PrintToString(value);
+ }
+};
+
+// Array.
+template <typename ToPrint, size_t N, typename OtherOperand>
+class FormatForComparison<ToPrint[N], OtherOperand> {
+ public:
+ static ::std::string Format(const ToPrint* value) {
+ return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
+ }
+};
+
+// By default, print C string as pointers to be safe, as we don't know
+// whether they actually point to a NUL-terminated string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \
+ template <typename OtherOperand> \
+ class FormatForComparison<CharType*, OtherOperand> { \
+ public: \
+ static ::std::string Format(CharType* value) { \
+ return ::testing::PrintToString(static_cast<const void*>(value)); \
+ } \
+ }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
+
+// If a C string is compared with an STL string object, we know it's meant
+// to point to a NUL-terminated string, and thus can print it as a string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
+ template <> \
+ class FormatForComparison<CharType*, OtherStringType> { \
+ public: \
+ static ::std::string Format(CharType* value) { \
+ return ::testing::PrintToString(value); \
+ } \
+ }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
+
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
+#endif
+
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
+#endif
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
+#endif
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
+
+// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
+// operand to be used in a failure message. The type (but not value)
+// of the other operand may affect the format. This allows us to
+// print a char* as a raw pointer when it is compared against another
+// char* or void*, and print it as a C string when it is compared
+// against an std::string object, for example.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename T1, typename T2>
+std::string FormatForComparisonFailureMessage(
+ const T1& value, const T2& /* other_operand */) {
+ return FormatForComparison<T1, T2>::Format(value);
+}
+
// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
// value to the given ostream. The caller must ensure that
// 'ostream_ptr' is not NULL, or the behavior is undefined.
@@ -480,14 +581,16 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
}
#endif // GTEST_HAS_STD_WSTRING
-#if GTEST_HAS_TR1_TUPLE
-// Overload for ::std::tr1::tuple. Needed for printing function arguments,
-// which are packed as tuples.
-
+#if GTEST_HAS_TR1_TUPLE || (defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_)
// Helper function for printing a tuple. T must be instantiated with
// a tuple type.
template <typename T>
void PrintTupleTo(const T& t, ::std::ostream* os);
+#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
+
+#if GTEST_HAS_TR1_TUPLE
+// Overload for ::std::tr1::tuple. Needed for printing function arguments,
+// which are packed as tuples.
// Overloaded PrintTo() for tuples of various arities. We support
// tuples of up-to 10 fields. The following implementation works
@@ -561,6 +664,13 @@ void PrintTo(
}
#endif // GTEST_HAS_TR1_TUPLE
+#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+template <typename... Types>
+void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) {
+ PrintTupleTo(t, os);
+}
+#endif // GTEST_HAS_STD_TUPLE_
+
// Overload for std::pair.
template <typename T1, typename T2>
void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
@@ -580,10 +690,7 @@ class UniversalPrinter {
public:
// MSVC warns about adding const to a function type, so we want to
// disable the warning.
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4180) // Temporarily disables warning 4180.
-#endif // _MSC_VER
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
// Note: we deliberately don't call this PrintTo(), as that name
// conflicts with ::testing::internal::PrintTo in the body of the
@@ -600,9 +707,7 @@ class UniversalPrinter {
PrintTo(value, os);
}
-#ifdef _MSC_VER
-# pragma warning(pop) // Restores the warning state.
-#endif // _MSC_VER
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
};
// UniversalPrintArray(begin, len, os) prints an array of 'len'
@@ -654,10 +759,7 @@ class UniversalPrinter<T&> {
public:
// MSVC warns about adding const to a function type, so we want to
// disable the warning.
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4180) // Temporarily disables warning 4180.
-#endif // _MSC_VER
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
static void Print(const T& value, ::std::ostream* os) {
// Prints the address of the value. We use reinterpret_cast here
@@ -668,9 +770,7 @@ class UniversalPrinter<T&> {
UniversalPrint(value, os);
}
-#ifdef _MSC_VER
-# pragma warning(pop) // Restores the warning state.
-#endif // _MSC_VER
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
};
// Prints a value tersely: for a reference type, the referenced value
@@ -756,16 +856,65 @@ void UniversalPrint(const T& value, ::std::ostream* os) {
UniversalPrinter<T1>::Print(value, os);
}
-#if GTEST_HAS_TR1_TUPLE
typedef ::std::vector<string> Strings;
+// TuplePolicy<TupleT> must provide:
+// - tuple_size
+// size of tuple TupleT.
+// - get<size_t I>(const TupleT& t)
+// static function extracting element I of tuple TupleT.
+// - tuple_element<size_t I>::type
+// type of element I of tuple TupleT.
+template <typename TupleT>
+struct TuplePolicy;
+
+#if GTEST_HAS_TR1_TUPLE
+template <typename TupleT>
+struct TuplePolicy {
+ typedef TupleT Tuple;
+ static const size_t tuple_size = ::std::tr1::tuple_size<Tuple>::value;
+
+ template <size_t I>
+ struct tuple_element : ::std::tr1::tuple_element<I, Tuple> {};
+
+ template <size_t I>
+ static typename AddReference<
+ const typename ::std::tr1::tuple_element<I, Tuple>::type>::type get(
+ const Tuple& tuple) {
+ return ::std::tr1::get<I>(tuple);
+ }
+};
+template <typename TupleT>
+const size_t TuplePolicy<TupleT>::tuple_size;
+#endif // GTEST_HAS_TR1_TUPLE
+
+#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+template <typename... Types>
+struct TuplePolicy< ::std::tuple<Types...> > {
+ typedef ::std::tuple<Types...> Tuple;
+ static const size_t tuple_size = ::std::tuple_size<Tuple>::value;
+
+ template <size_t I>
+ struct tuple_element : ::std::tuple_element<I, Tuple> {};
+
+ template <size_t I>
+ static const typename ::std::tuple_element<I, Tuple>::type& get(
+ const Tuple& tuple) {
+ return ::std::get<I>(tuple);
+ }
+};
+template <typename... Types>
+const size_t TuplePolicy< ::std::tuple<Types...> >::tuple_size;
+#endif // GTEST_HAS_STD_TUPLE_
+
+#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
// This helper template allows PrintTo() for tuples and
// UniversalTersePrintTupleFieldsToStrings() to be defined by
// induction on the number of tuple fields. The idea is that
// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
// fields in tuple t, and can be defined in terms of
// TuplePrefixPrinter<N - 1>.
-
+//
// The inductive case.
template <size_t N>
struct TuplePrefixPrinter {
@@ -773,9 +922,14 @@ struct TuplePrefixPrinter {
template <typename Tuple>
static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
- *os << ", ";
- UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
- ::Print(::std::tr1::get<N - 1>(t), os);
+ GTEST_INTENTIONAL_CONST_COND_PUSH_()
+ if (N > 1) {
+ GTEST_INTENTIONAL_CONST_COND_POP_()
+ *os << ", ";
+ }
+ UniversalPrinter<
+ typename TuplePolicy<Tuple>::template tuple_element<N - 1>::type>
+ ::Print(TuplePolicy<Tuple>::template get<N - 1>(t), os);
}
// Tersely prints the first N fields of a tuple to a string vector,
@@ -784,12 +938,12 @@ struct TuplePrefixPrinter {
static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
::std::stringstream ss;
- UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
+ UniversalTersePrint(TuplePolicy<Tuple>::template get<N - 1>(t), &ss);
strings->push_back(ss.str());
}
};
-// Base cases.
+// Base case.
template <>
struct TuplePrefixPrinter<0> {
template <typename Tuple>
@@ -798,34 +952,13 @@ struct TuplePrefixPrinter<0> {
template <typename Tuple>
static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
};
-// We have to specialize the entire TuplePrefixPrinter<> class
-// template here, even though the definition of
-// TersePrintPrefixToStrings() is the same as the generic version, as
-// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't
-// support specializing a method template of a class template.
-template <>
-struct TuplePrefixPrinter<1> {
- template <typename Tuple>
- static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
- UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
- Print(::std::tr1::get<0>(t), os);
- }
- template <typename Tuple>
- static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
- ::std::stringstream ss;
- UniversalTersePrint(::std::tr1::get<0>(t), &ss);
- strings->push_back(ss.str());
- }
-};
-
-// Helper function for printing a tuple. T must be instantiated with
-// a tuple type.
-template <typename T>
-void PrintTupleTo(const T& t, ::std::ostream* os) {
+// Helper function for printing a tuple.
+// Tuple must be either std::tr1::tuple or std::tuple type.
+template <typename Tuple>
+void PrintTupleTo(const Tuple& t, ::std::ostream* os) {
*os << "(";
- TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>::
- PrintPrefixTo(t, os);
+ TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::PrintPrefixTo(t, os);
*os << ")";
}
@@ -835,11 +968,11 @@ void PrintTupleTo(const T& t, ::std::ostream* os) {
template <typename Tuple>
Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
Strings result;
- TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
+ TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::
TersePrintPrefixToStrings(value, &result);
return result;
}
-#endif // GTEST_HAS_TR1_TUPLE
+#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
} // namespace internal
@@ -852,4 +985,9 @@ template <typename T>
} // namespace testing
+// Include any custom printer added by the local installation.
+// We must include this header at the end to make sure it can use the
+// declarations from this file.
+#include "gtest/internal/custom/gtest-printers.h"
+
#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
diff --git a/extern/gtest/include/gtest/gtest-typed-test.h b/extern/gtest/include/gtest/gtest-typed-test.h
index fe1e83b274b..5f69d5678ea 100644
--- a/extern/gtest/include/gtest/gtest-typed-test.h
+++ b/extern/gtest/include/gtest/gtest-typed-test.h
@@ -181,7 +181,8 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
::testing::internal::TemplateSel< \
GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
GTEST_TYPE_PARAMS_(CaseName)>::Register(\
- "", #CaseName, #TestName, 0); \
+ "", ::testing::internal::CodeLocation(__FILE__, __LINE__), \
+ #CaseName, #TestName, 0); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
@@ -252,7 +253,10 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
::testing::internal::TypeParameterizedTestCase<CaseName, \
GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
::testing::internal::TypeList< Types >::type>::Register(\
- #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
+ #Prefix, \
+ ::testing::internal::CodeLocation(__FILE__, __LINE__), \
+ &GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), \
+ #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
#endif // GTEST_HAS_TYPED_TEST_P
diff --git a/extern/gtest/include/gtest/gtest.h b/extern/gtest/include/gtest/gtest.h
index 69a8e72da5f..18d3ed4b469 100644
--- a/extern/gtest/include/gtest/gtest.h
+++ b/extern/gtest/include/gtest/gtest.h
@@ -70,14 +70,14 @@
// class ::string, which has the same interface as ::std::string, but
// has a different implementation.
//
-// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that
+// You can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that
// ::string is available AND is a distinct type to ::std::string, or
// define it to 0 to indicate otherwise.
//
-// If the user's ::std::string and ::string are the same class due to
-// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0.
+// If ::std::string and ::string are the same class on your platform
+// due to aliasing, you should define GTEST_HAS_GLOBAL_STRING to 0.
//
-// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined
+// If you do not define GTEST_HAS_GLOBAL_STRING, it is defined
// heuristically.
namespace testing {
@@ -258,8 +258,31 @@ class GTEST_API_ AssertionResult {
// Copy constructor.
// Used in EXPECT_TRUE/FALSE(assertion_result).
AssertionResult(const AssertionResult& other);
+
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
+
// Used in the EXPECT_TRUE/FALSE(bool_expression).
- explicit AssertionResult(bool success) : success_(success) {}
+ //
+ // T must be contextually convertible to bool.
+ //
+ // The second parameter prevents this overload from being considered if
+ // the argument is implicitly convertible to AssertionResult. In that case
+ // we want AssertionResult's copy constructor to be used.
+ template <typename T>
+ explicit AssertionResult(
+ const T& success,
+ typename internal::EnableIf<
+ !internal::ImplicitlyConvertible<T, AssertionResult>::value>::type*
+ /*enabler*/ = NULL)
+ : success_(success) {}
+
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
+
+ // Assignment operator.
+ AssertionResult& operator=(AssertionResult other) {
+ swap(other);
+ return *this;
+ }
// Returns true iff the assertion succeeded.
operator bool() const { return success_; } // NOLINT
@@ -300,6 +323,9 @@ class GTEST_API_ AssertionResult {
message_->append(a_message.GetString().c_str());
}
+ // Swap the contents of this AssertionResult with other.
+ void swap(AssertionResult& other);
+
// Stores result of the assertion predicate.
bool success_;
// Stores the message describing the condition in case the expectation
@@ -307,8 +333,6 @@ class GTEST_API_ AssertionResult {
// Referenced via a pointer to avoid taking too much stack frame space
// with test assertions.
internal::scoped_ptr< ::std::string> message_;
-
- GTEST_DISALLOW_ASSIGN_(AssertionResult);
};
// Makes a successful assertion result.
@@ -335,8 +359,8 @@ GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
//
// class FooTest : public testing::Test {
// protected:
-// virtual void SetUp() { ... }
-// virtual void TearDown() { ... }
+// void SetUp() override { ... }
+// void TearDown() override { ... }
// ...
// };
//
@@ -428,20 +452,19 @@ class GTEST_API_ Test {
// internal method to avoid clashing with names used in user TESTs.
void DeleteSelf_() { delete this; }
- // Uses a GTestFlagSaver to save and restore all Google Test flags.
- const internal::GTestFlagSaver* const gtest_flag_saver_;
+ const internal::scoped_ptr< GTEST_FLAG_SAVER_ > gtest_flag_saver_;
- // Often a user mis-spells SetUp() as Setup() and spends a long time
+ // Often a user misspells SetUp() as Setup() and spends a long time
// wondering why it is never called by Google Test. The declaration of
// the following method is solely for catching such an error at
// compile time:
//
// - The return type is deliberately chosen to be not void, so it
- // will be a conflict if a user declares void Setup() in his test
- // fixture.
+ // will be a conflict if void Setup() is declared in the user's
+ // test fixture.
//
// - This method is private, so it will be another compiler error
- // if a user calls it from his test fixture.
+ // if the method is called from the user's test fixture.
//
// DO NOT OVERRIDE THIS FUNCTION.
//
@@ -646,6 +669,12 @@ class GTEST_API_ TestInfo {
return NULL;
}
+ // Returns the file name where this test is defined.
+ const char* file() const { return location_.file.c_str(); }
+
+ // Returns the line where this test is defined.
+ int line() const { return location_.line; }
+
// Returns true if this test should run, that is if the test is not
// disabled (or it is disabled but the also_run_disabled_tests flag has
// been specified) and its full name matches the user-specified filter.
@@ -688,6 +717,7 @@ class GTEST_API_ TestInfo {
const char* name,
const char* type_param,
const char* value_param,
+ internal::CodeLocation code_location,
internal::TypeId fixture_class_id,
Test::SetUpTestCaseFunc set_up_tc,
Test::TearDownTestCaseFunc tear_down_tc,
@@ -699,6 +729,7 @@ class GTEST_API_ TestInfo {
const std::string& name,
const char* a_type_param, // NULL if not a type-parameterized test
const char* a_value_param, // NULL if not a value-parameterized test
+ internal::CodeLocation a_code_location,
internal::TypeId fixture_class_id,
internal::TestFactoryBase* factory);
@@ -725,6 +756,7 @@ class GTEST_API_ TestInfo {
// Text representation of the value parameter, or NULL if this is not a
// value-parameterized test.
const internal::scoped_ptr<const ::std::string> value_param_;
+ internal::CodeLocation location_;
const internal::TypeId fixture_class_id_; // ID of the test fixture class
bool should_run_; // True iff this test should run
bool is_disabled_; // True iff this test is disabled
@@ -924,7 +956,7 @@ class GTEST_API_ TestCase {
};
// An Environment object is capable of setting up and tearing down an
-// environment. The user should subclass this to define his own
+// environment. You should subclass this to define your own
// environment(s).
//
// An Environment object does the set-up and tear-down in virtual
@@ -1336,137 +1368,42 @@ GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv);
namespace internal {
-// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
-// value of type ToPrint that is an operand of a comparison assertion
-// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in
-// the comparison, and is used to help determine the best way to
-// format the value. In particular, when the value is a C string
-// (char pointer) and the other operand is an STL string object, we
-// want to format the C string as a string, since we know it is
-// compared by value with the string object. If the value is a char
-// pointer but the other operand is not an STL string object, we don't
-// know whether the pointer is supposed to point to a NUL-terminated
-// string, and thus want to print it as a pointer to be safe.
-//
-// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
-
-// The default case.
-template <typename ToPrint, typename OtherOperand>
-class FormatForComparison {
- public:
- static ::std::string Format(const ToPrint& value) {
- return ::testing::PrintToString(value);
- }
-};
-
-// Array.
-template <typename ToPrint, size_t N, typename OtherOperand>
-class FormatForComparison<ToPrint[N], OtherOperand> {
- public:
- static ::std::string Format(const ToPrint* value) {
- return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
- }
-};
-
-// By default, print C string as pointers to be safe, as we don't know
-// whether they actually point to a NUL-terminated string.
-
-#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \
- template <typename OtherOperand> \
- class FormatForComparison<CharType*, OtherOperand> { \
- public: \
- static ::std::string Format(CharType* value) { \
- return ::testing::PrintToString(static_cast<const void*>(value)); \
- } \
- }
-
-GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
-GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
-GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
-GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
-
-#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
-
-// If a C string is compared with an STL string object, we know it's meant
-// to point to a NUL-terminated string, and thus can print it as a string.
-
-#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
- template <> \
- class FormatForComparison<CharType*, OtherStringType> { \
- public: \
- static ::std::string Format(CharType* value) { \
- return ::testing::PrintToString(value); \
- } \
- }
-
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
-
-#if GTEST_HAS_GLOBAL_STRING
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
-#endif
-
-#if GTEST_HAS_GLOBAL_WSTRING
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
-#endif
-
-#if GTEST_HAS_STD_WSTRING
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
-GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
-#endif
-
-#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
-
-// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
-// operand to be used in a failure message. The type (but not value)
-// of the other operand may affect the format. This allows us to
-// print a char* as a raw pointer when it is compared against another
-// char* or void*, and print it as a C string when it is compared
-// against an std::string object, for example.
-//
-// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+// Separate the error generating code from the code path to reduce the stack
+// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers
+// when calling EXPECT_* in a tight loop.
template <typename T1, typename T2>
-std::string FormatForComparisonFailureMessage(
- const T1& value, const T2& /* other_operand */) {
- return FormatForComparison<T1, T2>::Format(value);
+AssertionResult CmpHelperEQFailure(const char* lhs_expression,
+ const char* rhs_expression,
+ const T1& lhs, const T2& rhs) {
+ return EqFailure(lhs_expression,
+ rhs_expression,
+ FormatForComparisonFailureMessage(lhs, rhs),
+ FormatForComparisonFailureMessage(rhs, lhs),
+ false);
}
// The helper function for {ASSERT|EXPECT}_EQ.
template <typename T1, typename T2>
-AssertionResult CmpHelperEQ(const char* expected_expression,
- const char* actual_expression,
- const T1& expected,
- const T2& actual) {
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4389) // Temporarily disables warning on
- // signed/unsigned mismatch.
-#endif
-
- if (expected == actual) {
+AssertionResult CmpHelperEQ(const char* lhs_expression,
+ const char* rhs_expression,
+ const T1& lhs,
+ const T2& rhs) {
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4389 /* signed/unsigned mismatch */)
+ if (lhs == rhs) {
return AssertionSuccess();
}
+GTEST_DISABLE_MSC_WARNINGS_POP_()
-#ifdef _MSC_VER
-# pragma warning(pop) // Restores the warning state.
-#endif
-
- return EqFailure(expected_expression,
- actual_expression,
- FormatForComparisonFailureMessage(expected, actual),
- FormatForComparisonFailureMessage(actual, expected),
- false);
+ return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs);
}
// With this overloaded version, we allow anonymous enums to be used
// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums
// can be implicitly cast to BiggestInt.
-GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression,
- const char* actual_expression,
- BiggestInt expected,
- BiggestInt actual);
+GTEST_API_ AssertionResult CmpHelperEQ(const char* lhs_expression,
+ const char* rhs_expression,
+ BiggestInt lhs,
+ BiggestInt rhs);
// The helper class for {ASSERT|EXPECT}_EQ. The template argument
// lhs_is_null_literal is true iff the first argument to ASSERT_EQ()
@@ -1477,12 +1414,11 @@ class EqHelper {
public:
// This templatized version is for the general case.
template <typename T1, typename T2>
- static AssertionResult Compare(const char* expected_expression,
- const char* actual_expression,
- const T1& expected,
- const T2& actual) {
- return CmpHelperEQ(expected_expression, actual_expression, expected,
- actual);
+ static AssertionResult Compare(const char* lhs_expression,
+ const char* rhs_expression,
+ const T1& lhs,
+ const T2& rhs) {
+ return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
}
// With this overloaded version, we allow anonymous enums to be used
@@ -1491,12 +1427,11 @@ class EqHelper {
//
// Even though its body looks the same as the above version, we
// cannot merge the two, as it will make anonymous enums unhappy.
- static AssertionResult Compare(const char* expected_expression,
- const char* actual_expression,
- BiggestInt expected,
- BiggestInt actual) {
- return CmpHelperEQ(expected_expression, actual_expression, expected,
- actual);
+ static AssertionResult Compare(const char* lhs_expression,
+ const char* rhs_expression,
+ BiggestInt lhs,
+ BiggestInt rhs) {
+ return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
}
};
@@ -1511,40 +1446,52 @@ class EqHelper<true> {
// EXPECT_EQ(false, a_bool).
template <typename T1, typename T2>
static AssertionResult Compare(
- const char* expected_expression,
- const char* actual_expression,
- const T1& expected,
- const T2& actual,
+ const char* lhs_expression,
+ const char* rhs_expression,
+ const T1& lhs,
+ const T2& rhs,
// The following line prevents this overload from being considered if T2
// is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr)
// expands to Compare("", "", NULL, my_ptr), which requires a conversion
// to match the Secret* in the other overload, which would otherwise make
// this template match better.
typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
- return CmpHelperEQ(expected_expression, actual_expression, expected,
- actual);
+ return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
}
// This version will be picked when the second argument to ASSERT_EQ() is a
// pointer, e.g. ASSERT_EQ(NULL, a_pointer).
template <typename T>
static AssertionResult Compare(
- const char* expected_expression,
- const char* actual_expression,
+ const char* lhs_expression,
+ const char* rhs_expression,
// We used to have a second template parameter instead of Secret*. That
// template parameter would deduce to 'long', making this a better match
// than the first overload even without the first overload's EnableIf.
// Unfortunately, gcc with -Wconversion-null warns when "passing NULL to
// non-pointer argument" (even a deduced integral argument), so the old
// implementation caused warnings in user code.
- Secret* /* expected (NULL) */,
- T* actual) {
- // We already know that 'expected' is a null pointer.
- return CmpHelperEQ(expected_expression, actual_expression,
- static_cast<T*>(NULL), actual);
+ Secret* /* lhs (NULL) */,
+ T* rhs) {
+ // We already know that 'lhs' is a null pointer.
+ return CmpHelperEQ(lhs_expression, rhs_expression,
+ static_cast<T*>(NULL), rhs);
}
};
+// Separate the error generating code from the code path to reduce the stack
+// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers
+// when calling EXPECT_OP in a tight loop.
+template <typename T1, typename T2>
+AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2,
+ const T1& val1, const T2& val2,
+ const char* op) {
+ return AssertionFailure()
+ << "Expected: (" << expr1 << ") " << op << " (" << expr2
+ << "), actual: " << FormatForComparisonFailureMessage(val1, val2)
+ << " vs " << FormatForComparisonFailureMessage(val2, val1);
+}
+
// A macro for implementing the helper functions needed to implement
// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste
// of similar code.
@@ -1555,6 +1502,7 @@ class EqHelper<true> {
// with gcc 4.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
template <typename T1, typename T2>\
AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
@@ -1562,10 +1510,7 @@ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
if (val1 op val2) {\
return AssertionSuccess();\
} else {\
- return AssertionFailure() \
- << "Expected: (" << expr1 << ") " #op " (" << expr2\
- << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
- << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+ return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\
}\
}\
GTEST_API_ AssertionResult CmpHelper##op_name(\
@@ -1589,18 +1534,18 @@ GTEST_IMPL_CMP_HELPER_(GT, >);
// The helper function for {ASSERT|EXPECT}_STREQ.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
-GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
- const char* actual_expression,
- const char* expected,
- const char* actual);
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2);
// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
-GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
- const char* actual_expression,
- const char* expected,
- const char* actual);
+GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression,
+ const char* s2_expression,
+ const char* s1,
+ const char* s2);
// The helper function for {ASSERT|EXPECT}_STRNE.
//
@@ -1622,10 +1567,10 @@ GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
// Helper function for *_STREQ on wide strings.
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
-GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
- const char* actual_expression,
- const wchar_t* expected,
- const wchar_t* actual);
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression,
+ const char* s2_expression,
+ const wchar_t* s1,
+ const wchar_t* s2);
// Helper function for *_STRNE on wide strings.
//
@@ -1683,28 +1628,28 @@ namespace internal {
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
template <typename RawType>
-AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression,
- const char* actual_expression,
- RawType expected,
- RawType actual) {
- const FloatingPoint<RawType> lhs(expected), rhs(actual);
+AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression,
+ const char* rhs_expression,
+ RawType lhs_value,
+ RawType rhs_value) {
+ const FloatingPoint<RawType> lhs(lhs_value), rhs(rhs_value);
if (lhs.AlmostEquals(rhs)) {
return AssertionSuccess();
}
- ::std::stringstream expected_ss;
- expected_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
- << expected;
+ ::std::stringstream lhs_ss;
+ lhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << lhs_value;
- ::std::stringstream actual_ss;
- actual_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
- << actual;
+ ::std::stringstream rhs_ss;
+ rhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+ << rhs_value;
- return EqFailure(expected_expression,
- actual_expression,
- StringStreamToString(&expected_ss),
- StringStreamToString(&actual_ss),
+ return EqFailure(lhs_expression,
+ rhs_expression,
+ StringStreamToString(&lhs_ss),
+ StringStreamToString(&rhs_ss),
false);
}
@@ -1912,13 +1857,13 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// AssertionResult. For more information on how to use AssertionResult with
// these macros see comments on that class.
#define EXPECT_TRUE(condition) \
- GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+ GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \
GTEST_NONFATAL_FAILURE_)
#define EXPECT_FALSE(condition) \
GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
GTEST_NONFATAL_FAILURE_)
#define ASSERT_TRUE(condition) \
- GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+ GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \
GTEST_FATAL_FAILURE_)
#define ASSERT_FALSE(condition) \
GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
@@ -1930,12 +1875,12 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// Macros for testing equalities and inequalities.
//
-// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual
-// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2
-// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2
-// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2
-// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2
-// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2
+// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2
+// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2
+// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2
+// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2
+// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2
+// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2
//
// When they are not, Google Test prints both the tested expressions and
// their actual values. The values must be compatible built-in types,
@@ -1957,8 +1902,8 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// are related, not how their content is related. To compare two C
// strings by content, use {ASSERT|EXPECT}_STR*().
//
-// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to
-// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you
+// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to
+// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you
// what the actual value is when it fails, and similarly for the
// other comparisons.
//
@@ -1974,12 +1919,12 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// ASSERT_LT(i, array_size);
// ASSERT_GT(records.size(), 0) << "There is no record left.";
-#define EXPECT_EQ(expected, actual) \
+#define EXPECT_EQ(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal:: \
- EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
- expected, actual)
-#define EXPECT_NE(expected, actual) \
- EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual)
+ EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
+ val1, val2)
+#define EXPECT_NE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
#define EXPECT_LE(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
#define EXPECT_LT(val1, val2) \
@@ -1989,10 +1934,10 @@ class TestWithParam : public Test, public WithParamInterface<T> {
#define EXPECT_GT(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
-#define GTEST_ASSERT_EQ(expected, actual) \
+#define GTEST_ASSERT_EQ(val1, val2) \
ASSERT_PRED_FORMAT2(::testing::internal:: \
- EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
- expected, actual)
+ EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
+ val1, val2)
#define GTEST_ASSERT_NE(val1, val2) \
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
#define GTEST_ASSERT_LE(val1, val2) \
@@ -2047,29 +1992,29 @@ class TestWithParam : public Test, public WithParamInterface<T> {
//
// These macros evaluate their arguments exactly once.
-#define EXPECT_STREQ(expected, actual) \
- EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define EXPECT_STREQ(s1, s2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2)
#define EXPECT_STRNE(s1, s2) \
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
-#define EXPECT_STRCASEEQ(expected, actual) \
- EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define EXPECT_STRCASEEQ(s1, s2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2)
#define EXPECT_STRCASENE(s1, s2)\
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
-#define ASSERT_STREQ(expected, actual) \
- ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define ASSERT_STREQ(s1, s2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2)
#define ASSERT_STRNE(s1, s2) \
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
-#define ASSERT_STRCASEEQ(expected, actual) \
- ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define ASSERT_STRCASEEQ(s1, s2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2)
#define ASSERT_STRCASENE(s1, s2)\
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
// Macros for comparing floating-point numbers.
//
-// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual):
+// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2):
// Tests that two float values are almost equal.
-// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual):
+// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2):
// Tests that two double values are almost equal.
// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error):
// Tests that v1 and v2 are within the given distance to each other.
@@ -2079,21 +2024,21 @@ class TestWithParam : public Test, public WithParamInterface<T> {
// FloatingPoint template class in gtest-internal.h if you are
// interested in the implementation details.
-#define EXPECT_FLOAT_EQ(expected, actual)\
+#define EXPECT_FLOAT_EQ(val1, val2)\
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
- expected, actual)
+ val1, val2)
-#define EXPECT_DOUBLE_EQ(expected, actual)\
+#define EXPECT_DOUBLE_EQ(val1, val2)\
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
- expected, actual)
+ val1, val2)
-#define ASSERT_FLOAT_EQ(expected, actual)\
+#define ASSERT_FLOAT_EQ(val1, val2)\
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
- expected, actual)
+ val1, val2)
-#define ASSERT_DOUBLE_EQ(expected, actual)\
+#define ASSERT_DOUBLE_EQ(val1, val2)\
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
- expected, actual)
+ val1, val2)
#define EXPECT_NEAR(val1, val2, abs_error)\
EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
@@ -2215,8 +2160,8 @@ bool StaticAssertTypeEq() {
// The convention is to end the test case name with "Test". For
// example, a test case for the Foo class can be named FooTest.
//
-// The user should put his test code between braces after using this
-// macro. Example:
+// Test code should appear between braces after an invocation of
+// this macro. Example:
//
// TEST(FooTest, InitializesCorrectly) {
// Foo foo;
diff --git a/extern/gtest/include/gtest/internal/custom/gtest-port.h b/extern/gtest/include/gtest/internal/custom/gtest-port.h
new file mode 100644
index 00000000000..7e744bd3bb3
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/custom/gtest-port.h
@@ -0,0 +1,69 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations.
+// The following macros can be defined:
+//
+// Flag related macros:
+// GTEST_FLAG(flag_name)
+// GTEST_USE_OWN_FLAGFILE_FLAG_ - Define to 0 when the system provides its
+// own flagfile flag parsing.
+// GTEST_DECLARE_bool_(name)
+// GTEST_DECLARE_int32_(name)
+// GTEST_DECLARE_string_(name)
+// GTEST_DEFINE_bool_(name, default_val, doc)
+// GTEST_DEFINE_int32_(name, default_val, doc)
+// GTEST_DEFINE_string_(name, default_val, doc)
+//
+// Test filtering:
+// GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that
+// will be used if --GTEST_FLAG(test_filter)
+// is not provided.
+//
+// Logging:
+// GTEST_LOG_(severity)
+// GTEST_CHECK_(condition)
+// Functions LogToStderr() and FlushInfoLog() have to be provided too.
+//
+// Threading:
+// GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided.
+// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are
+// already provided.
+// Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and
+// GTEST_DEFINE_STATIC_MUTEX_(mutex)
+//
+// GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
+// GTEST_LOCK_EXCLUDED_(locks)
+//
+// ** Custom implementation starts here **
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
diff --git a/extern/gtest/include/gtest/internal/custom/gtest-printers.h b/extern/gtest/include/gtest/internal/custom/gtest-printers.h
new file mode 100644
index 00000000000..60c1ea050b6
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/custom/gtest-printers.h
@@ -0,0 +1,42 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file provides an injection point for custom printers in a local
+// installation of gTest.
+// It will be included from gtest-printers.h and the overrides in this file
+// will be visible to everyone.
+// See documentation at gtest/gtest-printers.h for details on how to define a
+// custom printer.
+//
+// ** Custom implementation starts here **
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
diff --git a/extern/gtest/include/gtest/internal/custom/gtest.h b/extern/gtest/include/gtest/internal/custom/gtest.h
new file mode 100644
index 00000000000..c27412a8981
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/custom/gtest.h
@@ -0,0 +1,41 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations.
+// The following macros can be defined:
+//
+// GTEST_OS_STACK_TRACE_GETTER_ - The name of an implementation of
+// OsStackTraceGetterInterface.
+//
+// ** Custom implementation starts here **
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-internal.h b/extern/gtest/include/gtest/internal/gtest-internal.h
index 0dcc3a3194f..ebd1cf615de 100644
--- a/extern/gtest/include/gtest/internal/gtest-internal.h
+++ b/extern/gtest/include/gtest/internal/gtest-internal.h
@@ -55,7 +55,10 @@
#include <string.h>
#include <iomanip>
#include <limits>
+#include <map>
#include <set>
+#include <string>
+#include <vector>
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-string.h"
@@ -97,9 +100,6 @@ class ScopedTrace; // Implements scoped trace.
class TestInfoImpl; // Opaque implementation of TestInfo
class UnitTestImpl; // Opaque implementation of UnitTest
-// How many times InitGoogleTest() has been called.
-GTEST_API_ extern int g_init_gtest_count;
-
// The text used in failure messages to indicate the start of the
// stack trace.
GTEST_API_ extern const char kStackTraceMarker[];
@@ -171,6 +171,36 @@ class GTEST_API_ ScopedTrace {
// c'tor and d'tor. Therefore it doesn't
// need to be used otherwise.
+namespace edit_distance {
+// Returns the optimal edits to go from 'left' to 'right'.
+// All edits cost the same, with replace having lower priority than
+// add/remove.
+// Simple implementation of the Wagner–Fischer algorithm.
+// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
+enum EditType { kMatch, kAdd, kRemove, kReplace };
+GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
+ const std::vector<size_t>& left, const std::vector<size_t>& right);
+
+// Same as above, but the input is represented as strings.
+GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
+ const std::vector<std::string>& left,
+ const std::vector<std::string>& right);
+
+// Create a diff of the input strings in Unified diff format.
+GTEST_API_ std::string CreateUnifiedDiff(const std::vector<std::string>& left,
+ const std::vector<std::string>& right,
+ size_t context = 2);
+
+} // namespace edit_distance
+
+// Calculate the diff between 'left' and 'right' and return it in unified diff
+// format.
+// If not null, stores in 'total_line_count' the total number of lines found
+// in left + right.
+GTEST_API_ std::string DiffStrings(const std::string& left,
+ const std::string& right,
+ size_t* total_line_count);
+
// Constructs and returns the message for an equality assertion
// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
//
@@ -471,6 +501,13 @@ GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr,
typedef void (*SetUpTestCaseFunc)();
typedef void (*TearDownTestCaseFunc)();
+struct CodeLocation {
+ CodeLocation(const string& a_file, int a_line) : file(a_file), line(a_line) {}
+
+ string file;
+ int line;
+};
+
// Creates a new TestInfo object and registers it with Google Test;
// returns the created object.
//
@@ -482,6 +519,7 @@ typedef void (*TearDownTestCaseFunc)();
// this is not a typed or a type-parameterized test.
// value_param text representation of the test's value parameter,
// or NULL if this is not a type-parameterized test.
+// code_location: code location where the test is defined
// fixture_class_id: ID of the test fixture class
// set_up_tc: pointer to the function that sets up the test case
// tear_down_tc: pointer to the function that tears down the test case
@@ -493,6 +531,7 @@ GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
const char* name,
const char* type_param,
const char* value_param,
+ CodeLocation code_location,
TypeId fixture_class_id,
SetUpTestCaseFunc set_up_tc,
TearDownTestCaseFunc tear_down_tc,
@@ -522,10 +561,21 @@ class GTEST_API_ TypedTestCasePState {
fflush(stderr);
posix::Abort();
}
- defined_test_names_.insert(test_name);
+ registered_tests_.insert(
+ ::std::make_pair(test_name, CodeLocation(file, line)));
return true;
}
+ bool TestExists(const std::string& test_name) const {
+ return registered_tests_.count(test_name) > 0;
+ }
+
+ const CodeLocation& GetCodeLocation(const std::string& test_name) const {
+ RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name);
+ GTEST_CHECK_(it != registered_tests_.end());
+ return it->second;
+ }
+
// Verifies that registered_tests match the test names in
// defined_test_names_; returns registered_tests if successful, or
// aborts the program otherwise.
@@ -533,8 +583,10 @@ class GTEST_API_ TypedTestCasePState {
const char* file, int line, const char* registered_tests);
private:
+ typedef ::std::map<std::string, CodeLocation> RegisteredTestsMap;
+
bool registered_;
- ::std::set<const char*> defined_test_names_;
+ RegisteredTestsMap registered_tests_;
};
// Skips to the first non-space char after the first comma in 'str';
@@ -555,6 +607,11 @@ inline std::string GetPrefixUntilComma(const char* str) {
return comma == NULL ? str : std::string(str, comma);
}
+// Splits a given string on a given delimiter, populating a given
+// vector with the fields.
+void SplitString(const ::std::string& str, char delimiter,
+ ::std::vector< ::std::string>* dest);
+
// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
// registers a list of type-parameterized tests with Google Test. The
// return value is insignificant - we just need to return something
@@ -569,8 +626,10 @@ class TypeParameterizedTest {
// specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
// Types). Valid values for 'index' are [0, N - 1] where N is the
// length of Types.
- static bool Register(const char* prefix, const char* case_name,
- const char* test_names, int index) {
+ static bool Register(const char* prefix,
+ CodeLocation code_location,
+ const char* case_name, const char* test_names,
+ int index) {
typedef typename Types::Head Type;
typedef Fixture<Type> FixtureClass;
typedef typename GTEST_BIND_(TestSel, Type) TestClass;
@@ -580,9 +639,10 @@ class TypeParameterizedTest {
MakeAndRegisterTestInfo(
(std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"
+ StreamableToString(index)).c_str(),
- GetPrefixUntilComma(test_names).c_str(),
+ StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),
GetTypeName<Type>().c_str(),
NULL, // No value parameter.
+ code_location,
GetTypeId<FixtureClass>(),
TestClass::SetUpTestCase,
TestClass::TearDownTestCase,
@@ -590,7 +650,7 @@ class TypeParameterizedTest {
// Next, recurses (at compile time) with the tail of the type list.
return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>
- ::Register(prefix, case_name, test_names, index + 1);
+ ::Register(prefix, code_location, case_name, test_names, index + 1);
}
};
@@ -598,8 +658,9 @@ class TypeParameterizedTest {
template <GTEST_TEMPLATE_ Fixture, class TestSel>
class TypeParameterizedTest<Fixture, TestSel, Types0> {
public:
- static bool Register(const char* /*prefix*/, const char* /*case_name*/,
- const char* /*test_names*/, int /*index*/) {
+ static bool Register(const char* /*prefix*/, CodeLocation,
+ const char* /*case_name*/, const char* /*test_names*/,
+ int /*index*/) {
return true;
}
};
@@ -611,17 +672,31 @@ class TypeParameterizedTest<Fixture, TestSel, Types0> {
template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
class TypeParameterizedTestCase {
public:
- static bool Register(const char* prefix, const char* case_name,
- const char* test_names) {
+ static bool Register(const char* prefix, CodeLocation code_location,
+ const TypedTestCasePState* state,
+ const char* case_name, const char* test_names) {
+ std::string test_name = StripTrailingSpaces(
+ GetPrefixUntilComma(test_names));
+ if (!state->TestExists(test_name)) {
+ fprintf(stderr, "Failed to get code location for test %s.%s at %s.",
+ case_name, test_name.c_str(),
+ FormatFileLocation(code_location.file.c_str(),
+ code_location.line).c_str());
+ fflush(stderr);
+ posix::Abort();
+ }
+ const CodeLocation& test_location = state->GetCodeLocation(test_name);
+
typedef typename Tests::Head Head;
// First, register the first test in 'Test' for each type in 'Types'.
TypeParameterizedTest<Fixture, Head, Types>::Register(
- prefix, case_name, test_names, 0);
+ prefix, test_location, case_name, test_names, 0);
// Next, recurses (at compile time) with the tail of the test list.
return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>
- ::Register(prefix, case_name, SkipComma(test_names));
+ ::Register(prefix, code_location, state,
+ case_name, SkipComma(test_names));
}
};
@@ -629,8 +704,9 @@ class TypeParameterizedTestCase {
template <GTEST_TEMPLATE_ Fixture, typename Types>
class TypeParameterizedTestCase<Fixture, Templates0, Types> {
public:
- static bool Register(const char* /*prefix*/, const char* /*case_name*/,
- const char* /*test_names*/) {
+ static bool Register(const char* /*prefix*/, CodeLocation,
+ const TypedTestCasePState* /*state*/,
+ const char* /*case_name*/, const char* /*test_names*/) {
return true;
}
};
@@ -784,7 +860,7 @@ class ImplicitlyConvertible {
// MakeFrom() is an expression whose type is From. We cannot simply
// use From(), as the type From may not have a public default
// constructor.
- static From MakeFrom();
+ static typename AddReference<From>::type MakeFrom();
// These two functions are overloaded. Given an expression
// Helper(x), the compiler will pick the first version if x can be
@@ -802,25 +878,20 @@ class ImplicitlyConvertible {
// We have to put the 'public' section after the 'private' section,
// or MSVC refuses to compile the code.
public:
- // MSVC warns about implicitly converting from double to int for
- // possible loss of data, so we need to temporarily disable the
- // warning.
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4244) // Temporarily disables warning 4244.
-
- static const bool value =
- sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
-# pragma warning(pop) // Restores the warning state.
-#elif defined(__BORLANDC__)
+#if defined(__BORLANDC__)
// C++Builder cannot use member overload resolution during template
// instantiation. The simplest workaround is to use its C++0x type traits
// functions (C++Builder 2009 and above only).
static const bool value = __is_convertible(From, To);
#else
+ // MSVC warns about implicitly converting from double to int for
+ // possible loss of data, so we need to temporarily disable the
+ // warning.
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4244)
static const bool value =
sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
-#endif // _MSV_VER
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif // __BORLANDC__
};
template <typename From, typename To>
const bool ImplicitlyConvertible<From, To>::value;
@@ -946,11 +1017,10 @@ void CopyArray(const T* from, size_t size, U* to) {
// The relation between an NativeArray object (see below) and the
// native array it represents.
-enum RelationToSource {
- kReference, // The NativeArray references the native array.
- kCopy // The NativeArray makes a copy of the native array and
- // owns the copy.
-};
+// We use 2 different structs to allow non-copyable types to be used, as long
+// as RelationToSourceReference() is passed.
+struct RelationToSourceReference {};
+struct RelationToSourceCopy {};
// Adapts a native array to a read-only STL-style container. Instead
// of the complete STL container concept, this adaptor only implements
@@ -968,22 +1038,23 @@ class NativeArray {
typedef Element* iterator;
typedef const Element* const_iterator;
- // Constructs from a native array.
- NativeArray(const Element* array, size_t count, RelationToSource relation) {
- Init(array, count, relation);
+ // Constructs from a native array. References the source.
+ NativeArray(const Element* array, size_t count, RelationToSourceReference) {
+ InitRef(array, count);
+ }
+
+ // Constructs from a native array. Copies the source.
+ NativeArray(const Element* array, size_t count, RelationToSourceCopy) {
+ InitCopy(array, count);
}
// Copy constructor.
NativeArray(const NativeArray& rhs) {
- Init(rhs.array_, rhs.size_, rhs.relation_to_source_);
+ (this->*rhs.clone_)(rhs.array_, rhs.size_);
}
~NativeArray() {
- // Ensures that the user doesn't instantiate NativeArray with a
- // const or reference type.
- static_cast<void>(StaticAssertTypeEqHelper<Element,
- GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>());
- if (relation_to_source_ == kCopy)
+ if (clone_ != &NativeArray::InitRef)
delete[] array_;
}
@@ -997,23 +1068,30 @@ class NativeArray {
}
private:
- // Initializes this object; makes a copy of the input array if
- // 'relation' is kCopy.
- void Init(const Element* array, size_t a_size, RelationToSource relation) {
- if (relation == kReference) {
- array_ = array;
- } else {
- Element* const copy = new Element[a_size];
- CopyArray(array, a_size, copy);
- array_ = copy;
- }
+ enum {
+ kCheckTypeIsNotConstOrAReference = StaticAssertTypeEqHelper<
+ Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value,
+ };
+
+ // Initializes this object with a copy of the input.
+ void InitCopy(const Element* array, size_t a_size) {
+ Element* const copy = new Element[a_size];
+ CopyArray(array, a_size, copy);
+ array_ = copy;
size_ = a_size;
- relation_to_source_ = relation;
+ clone_ = &NativeArray::InitCopy;
+ }
+
+ // Initializes this object with a reference of the input.
+ void InitRef(const Element* array, size_t a_size) {
+ array_ = array;
+ size_ = a_size;
+ clone_ = &NativeArray::InitRef;
}
const Element* array_;
size_t size_;
- RelationToSource relation_to_source_;
+ void (NativeArray::*clone_)(const Element*, size_t);
GTEST_DISALLOW_ASSIGN_(NativeArray);
};
@@ -1148,6 +1226,7 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
::test_info_ =\
::testing::internal::MakeAndRegisterTestInfo(\
#test_case_name, #test_name, NULL, NULL, \
+ ::testing::internal::CodeLocation(__FILE__, __LINE__), \
(parent_id), \
parent_class::SetUpTestCase, \
parent_class::TearDownTestCase, \
@@ -1156,3 +1235,4 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+
diff --git a/extern/gtest/include/gtest/internal/gtest-linked_ptr.h b/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
index b1362cd002c..36029422174 100644
--- a/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
+++ b/extern/gtest/include/gtest/internal/gtest-linked_ptr.h
@@ -110,7 +110,12 @@ class linked_ptr_internal {
MutexLock lock(&g_linked_ptr_mutex);
linked_ptr_internal const* p = ptr;
- while (p->next_ != ptr) p = p->next_;
+ while (p->next_ != ptr) {
+ assert(p->next_ != this &&
+ "Trying to join() a linked ring we are already in. "
+ "Is GMock thread safety enabled?");
+ p = p->next_;
+ }
p->next_ = this;
next_ = ptr;
}
@@ -123,7 +128,12 @@ class linked_ptr_internal {
if (next_ == this) return true;
linked_ptr_internal const* p = next_;
- while (p->next_ != this) p = p->next_;
+ while (p->next_ != this) {
+ assert(p->next_ != next_ &&
+ "Trying to depart() a linked ring we are not in. "
+ "Is GMock thread safety enabled?");
+ p = p->next_;
+ }
p->next_ = next_;
return false;
}
diff --git a/extern/gtest/include/gtest/internal/gtest-param-util-generated.h b/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
index e80548592c7..4d1d81d20ff 100644
--- a/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
+++ b/extern/gtest/include/gtest/internal/gtest-param-util-generated.h
@@ -40,7 +40,7 @@
// and at most 10 arguments in Combine. Please contact
// googletestframework@googlegroups.com if you need more.
// Please note that the number of arguments to Combine is limited
-// by the maximum arity of the implementation of tr1::tuple which is
+// by the maximum arity of the implementation of tuple which is
// currently set at 10.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
@@ -79,7 +79,10 @@ class ValueArray1 {
explicit ValueArray1(T1 v1) : v1_(v1) {}
template <typename T>
- operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); }
+ operator ParamGenerator<T>() const {
+ const T array[] = {static_cast<T>(v1_)};
+ return ValuesIn(array);
+ }
private:
// No implementation - assignment is unsupported.
@@ -3157,9 +3160,9 @@ class ValueArray50 {
//
template <typename T1, typename T2>
class CartesianProductGenerator2
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2> > {
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2> > {
public:
- typedef ::std::tr1::tuple<T1, T2> ParamType;
+ typedef ::testing::tuple<T1, T2> ParamType;
CartesianProductGenerator2(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2)
@@ -3272,9 +3275,9 @@ class CartesianProductGenerator2
template <typename T1, typename T2, typename T3>
class CartesianProductGenerator3
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3> > {
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3> ParamType;
+ typedef ::testing::tuple<T1, T2, T3> ParamType;
CartesianProductGenerator3(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3)
@@ -3404,9 +3407,9 @@ class CartesianProductGenerator3
template <typename T1, typename T2, typename T3, typename T4>
class CartesianProductGenerator4
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4> > {
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4> ParamType;
CartesianProductGenerator4(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -3555,9 +3558,9 @@ class CartesianProductGenerator4
template <typename T1, typename T2, typename T3, typename T4, typename T5>
class CartesianProductGenerator5
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5> > {
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4, T5> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4, T5> ParamType;
CartesianProductGenerator5(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -3723,10 +3726,10 @@ class CartesianProductGenerator5
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6>
class CartesianProductGenerator6
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5,
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5,
T6> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4, T5, T6> ParamType;
CartesianProductGenerator6(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -3909,10 +3912,10 @@ class CartesianProductGenerator6
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7>
class CartesianProductGenerator7
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
T7> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType;
CartesianProductGenerator7(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -4112,10 +4115,10 @@ class CartesianProductGenerator7
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8>
class CartesianProductGenerator8
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
T7, T8> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType;
CartesianProductGenerator8(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -4334,10 +4337,10 @@ class CartesianProductGenerator8
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9>
class CartesianProductGenerator9
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
T7, T8, T9> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType;
CartesianProductGenerator9(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -4573,10 +4576,10 @@ class CartesianProductGenerator9
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10>
class CartesianProductGenerator10
- : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ : public ParamGeneratorInterface< ::testing::tuple<T1, T2, T3, T4, T5, T6,
T7, T8, T9, T10> > {
public:
- typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType;
+ typedef ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType;
CartesianProductGenerator10(const ParamGenerator<T1>& g1,
const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
@@ -4838,8 +4841,8 @@ class CartesianProductHolder2 {
CartesianProductHolder2(const Generator1& g1, const Generator2& g2)
: g1_(g1), g2_(g2) {}
template <typename T1, typename T2>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2> >(
+ operator ParamGenerator< ::testing::tuple<T1, T2> >() const {
+ return ParamGenerator< ::testing::tuple<T1, T2> >(
new CartesianProductGenerator2<T1, T2>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_)));
@@ -4860,8 +4863,8 @@ CartesianProductHolder3(const Generator1& g1, const Generator2& g2,
const Generator3& g3)
: g1_(g1), g2_(g2), g3_(g3) {}
template <typename T1, typename T2, typename T3>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >(
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3> >() const {
+ return ParamGenerator< ::testing::tuple<T1, T2, T3> >(
new CartesianProductGenerator3<T1, T2, T3>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_),
@@ -4885,8 +4888,8 @@ CartesianProductHolder4(const Generator1& g1, const Generator2& g2,
const Generator3& g3, const Generator4& g4)
: g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
template <typename T1, typename T2, typename T3, typename T4>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >(
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4> >() const {
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4> >(
new CartesianProductGenerator4<T1, T2, T3, T4>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_),
@@ -4912,8 +4915,8 @@ CartesianProductHolder5(const Generator1& g1, const Generator2& g2,
const Generator3& g3, const Generator4& g4, const Generator5& g5)
: g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
template <typename T1, typename T2, typename T3, typename T4, typename T5>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >(
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5> >() const {
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5> >(
new CartesianProductGenerator5<T1, T2, T3, T4, T5>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_),
@@ -4943,8 +4946,8 @@ CartesianProductHolder6(const Generator1& g1, const Generator2& g2,
: g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >(
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6> >() const {
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6> >(
new CartesianProductGenerator6<T1, T2, T3, T4, T5, T6>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_),
@@ -4976,9 +4979,9 @@ CartesianProductHolder7(const Generator1& g1, const Generator2& g2,
: g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6,
T7> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> >(
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7> >(
new CartesianProductGenerator7<T1, T2, T3, T4, T5, T6, T7>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_),
@@ -5014,9 +5017,9 @@ CartesianProductHolder8(const Generator1& g1, const Generator2& g2,
g8_(g8) {}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7,
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7,
T8> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >(
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >(
new CartesianProductGenerator8<T1, T2, T3, T4, T5, T6, T7, T8>(
static_cast<ParamGenerator<T1> >(g1_),
static_cast<ParamGenerator<T2> >(g2_),
@@ -5055,9 +5058,9 @@ CartesianProductHolder9(const Generator1& g1, const Generator2& g2,
g9_(g9) {}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
T9> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
T9> >(
new CartesianProductGenerator9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
static_cast<ParamGenerator<T1> >(g1_),
@@ -5099,10 +5102,10 @@ CartesianProductHolder10(const Generator1& g1, const Generator2& g2,
g9_(g9), g10_(g10) {}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10>
- operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
- T9, T10> >() const {
- return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
- T9, T10> >(
+ operator ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+ T10> >() const {
+ return ParamGenerator< ::testing::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+ T10> >(
new CartesianProductGenerator10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
T10>(
static_cast<ParamGenerator<T1> >(g1_),
diff --git a/extern/gtest/include/gtest/internal/gtest-param-util.h b/extern/gtest/include/gtest/internal/gtest-param-util.h
index d5e1028b0c1..82cab9b0201 100644
--- a/extern/gtest/include/gtest/internal/gtest-param-util.h
+++ b/extern/gtest/include/gtest/internal/gtest-param-util.h
@@ -34,7 +34,10 @@
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#include <ctype.h>
+
#include <iterator>
+#include <set>
#include <utility>
#include <vector>
@@ -49,6 +52,27 @@
#if GTEST_HAS_PARAM_TEST
namespace testing {
+
+// Input to a parameterized test name generator, describing a test parameter.
+// Consists of the parameter value and the integer parameter index.
+template <class ParamType>
+struct TestParamInfo {
+ TestParamInfo(const ParamType& a_param, size_t an_index) :
+ param(a_param),
+ index(an_index) {}
+ ParamType param;
+ size_t index;
+};
+
+// A builtin parameterized test name generator which returns the result of
+// testing::PrintToString.
+struct PrintToStringParamName {
+ template <class ParamType>
+ std::string operator()(const TestParamInfo<ParamType>& info) const {
+ return PrintToString(info.param);
+ }
+};
+
namespace internal {
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
@@ -58,7 +82,7 @@ namespace internal {
// TEST_P macro is used to define two tests with the same name
// but in different namespaces.
GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
- const char* file, int line);
+ CodeLocation code_location);
template <typename> class ParamGeneratorInterface;
template <typename> class ParamGenerator;
@@ -206,7 +230,7 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
return base_;
}
virtual void Advance() {
- value_ = value_ + step_;
+ value_ = static_cast<T>(value_ + step_);
index_++;
}
virtual ParamIteratorInterface<T>* Clone() const {
@@ -243,7 +267,7 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
const T& end,
const IncrementT& step) {
int end_index = 0;
- for (T i = begin; i < end; i = i + step)
+ for (T i = begin; i < end; i = static_cast<T>(i + step))
end_index++;
return end_index;
}
@@ -347,6 +371,37 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
+// Default parameterized test name generator, returns a string containing the
+// integer test parameter index.
+template <class ParamType>
+std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
+ Message name_stream;
+ name_stream << info.index;
+ return name_stream.GetString();
+}
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Parameterized test name overload helpers, which help the
+// INSTANTIATE_TEST_CASE_P macro choose between the default parameterized
+// test name generator and user param name generator.
+template <class ParamType, class ParamNameGenFunctor>
+ParamNameGenFunctor GetParamNameGen(ParamNameGenFunctor func) {
+ return func;
+}
+
+template <class ParamType>
+struct ParamNameGenFunc {
+ typedef std::string Type(const TestParamInfo<ParamType>&);
+};
+
+template <class ParamType>
+typename ParamNameGenFunc<ParamType>::Type *GetParamNameGen() {
+ return DefaultParamName;
+}
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
// Stores a parameter value and later creates tests parameterized with that
// value.
template <class TestClass>
@@ -449,9 +504,11 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
typedef typename TestCase::ParamType ParamType;
// A function that returns an instance of appropriate generator type.
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
+ typedef typename ParamNameGenFunc<ParamType>::Type ParamNameGeneratorFunc;
- explicit ParameterizedTestCaseInfo(const char* name)
- : test_case_name_(name) {}
+ explicit ParameterizedTestCaseInfo(
+ const char* name, CodeLocation code_location)
+ : test_case_name_(name), code_location_(code_location) {}
// Test case base name for display purposes.
virtual const string& GetTestCaseName() const { return test_case_name_; }
@@ -474,9 +531,11 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
// about a generator.
int AddTestCaseInstantiation(const string& instantiation_name,
GeneratorCreationFunc* func,
- const char* /* file */,
- int /* line */) {
- instantiations_.push_back(::std::make_pair(instantiation_name, func));
+ ParamNameGeneratorFunc* name_func,
+ const char* file,
+ int line) {
+ instantiations_.push_back(
+ InstantiationInfo(instantiation_name, func, name_func, file, line));
return 0; // Return value used only to run this method in namespace scope.
}
// UnitTest class invokes this method to register tests in this test case
@@ -491,25 +550,45 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
for (typename InstantiationContainer::iterator gen_it =
instantiations_.begin(); gen_it != instantiations_.end();
++gen_it) {
- const string& instantiation_name = gen_it->first;
- ParamGenerator<ParamType> generator((*gen_it->second)());
+ const string& instantiation_name = gen_it->name;
+ ParamGenerator<ParamType> generator((*gen_it->generator)());
+ ParamNameGeneratorFunc* name_func = gen_it->name_func;
+ const char* file = gen_it->file;
+ int line = gen_it->line;
string test_case_name;
if ( !instantiation_name.empty() )
test_case_name = instantiation_name + "/";
test_case_name += test_info->test_case_base_name;
- int i = 0;
+ size_t i = 0;
+ std::set<std::string> test_param_names;
for (typename ParamGenerator<ParamType>::iterator param_it =
generator.begin();
param_it != generator.end(); ++param_it, ++i) {
Message test_name_stream;
- test_name_stream << test_info->test_base_name << "/" << i;
+
+ std::string param_name = name_func(
+ TestParamInfo<ParamType>(*param_it, i));
+
+ GTEST_CHECK_(IsValidParamName(param_name))
+ << "Parameterized test name '" << param_name
+ << "' is invalid, in " << file
+ << " line " << line << std::endl;
+
+ GTEST_CHECK_(test_param_names.count(param_name) == 0)
+ << "Duplicate parameterized test name '" << param_name
+ << "', in " << file << " line " << line << std::endl;
+
+ test_param_names.insert(param_name);
+
+ test_name_stream << test_info->test_base_name << "/" << param_name;
MakeAndRegisterTestInfo(
test_case_name.c_str(),
test_name_stream.GetString().c_str(),
NULL, // No type parameter.
PrintToString(*param_it).c_str(),
+ code_location_,
GetTestCaseTypeId(),
TestCase::SetUpTestCase,
TestCase::TearDownTestCase,
@@ -535,12 +614,45 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
};
typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
- // Keeps pairs of <Instantiation name, Sequence generator creation function>
- // received from INSTANTIATE_TEST_CASE_P macros.
- typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> >
- InstantiationContainer;
+ // Records data received from INSTANTIATE_TEST_CASE_P macros:
+ // <Instantiation name, Sequence generator creation function,
+ // Name generator function, Source file, Source line>
+ struct InstantiationInfo {
+ InstantiationInfo(const std::string &name_in,
+ GeneratorCreationFunc* generator_in,
+ ParamNameGeneratorFunc* name_func_in,
+ const char* file_in,
+ int line_in)
+ : name(name_in),
+ generator(generator_in),
+ name_func(name_func_in),
+ file(file_in),
+ line(line_in) {}
+
+ std::string name;
+ GeneratorCreationFunc* generator;
+ ParamNameGeneratorFunc* name_func;
+ const char* file;
+ int line;
+ };
+ typedef ::std::vector<InstantiationInfo> InstantiationContainer;
+
+ static bool IsValidParamName(const std::string& name) {
+ // Check for empty string
+ if (name.empty())
+ return false;
+
+ // Check for invalid characters
+ for (std::string::size_type index = 0; index < name.size(); ++index) {
+ if (!isalnum(name[index]) && name[index] != '_')
+ return false;
+ }
+
+ return true;
+ }
const string test_case_name_;
+ CodeLocation code_location_;
TestInfoContainer tests_;
InstantiationContainer instantiations_;
@@ -568,8 +680,7 @@ class ParameterizedTestCaseRegistry {
template <class TestCase>
ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
const char* test_case_name,
- const char* file,
- int line) {
+ CodeLocation code_location) {
ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
it != test_case_infos_.end(); ++it) {
@@ -578,7 +689,7 @@ class ParameterizedTestCaseRegistry {
// Complain about incorrect usage of Google Test facilities
// and terminate the program since we cannot guaranty correct
// test case setup and tear-down in this case.
- ReportInvalidTestCaseType(test_case_name, file, line);
+ ReportInvalidTestCaseType(test_case_name, code_location);
posix::Abort();
} else {
// At this point we are sure that the object we found is of the same
@@ -591,7 +702,8 @@ class ParameterizedTestCaseRegistry {
}
}
if (typed_test_info == NULL) {
- typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name);
+ typed_test_info = new ParameterizedTestCaseInfo<TestCase>(
+ test_case_name, code_location);
test_case_infos_.push_back(typed_test_info);
}
return typed_test_info;
diff --git a/extern/gtest/include/gtest/internal/gtest-port-arch.h b/extern/gtest/include/gtest/internal/gtest-port-arch.h
new file mode 100644
index 00000000000..74ab949057c
--- /dev/null
+++ b/extern/gtest/include/gtest/internal/gtest-port-arch.h
@@ -0,0 +1,93 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the GTEST_OS_* macro.
+// It is separate from gtest-port.h so that custom/gtest-port.h can include it.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+
+// Determines the platform on which Google Test is compiled.
+#ifdef __CYGWIN__
+# define GTEST_OS_CYGWIN 1
+#elif defined __SYMBIAN32__
+# define GTEST_OS_SYMBIAN 1
+#elif defined _WIN32
+# define GTEST_OS_WINDOWS 1
+# ifdef _WIN32_WCE
+# define GTEST_OS_WINDOWS_MOBILE 1
+# elif defined(__MINGW__) || defined(__MINGW32__)
+# define GTEST_OS_WINDOWS_MINGW 1
+# elif defined(WINAPI_FAMILY)
+# include <winapifamily.h>
+# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define GTEST_OS_WINDOWS_DESKTOP 1
+# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
+# define GTEST_OS_WINDOWS_PHONE 1
+# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+# define GTEST_OS_WINDOWS_RT 1
+# else
+ // WINAPI_FAMILY defined but no known partition matched.
+ // Default to desktop.
+# define GTEST_OS_WINDOWS_DESKTOP 1
+# endif
+# else
+# define GTEST_OS_WINDOWS_DESKTOP 1
+# endif // _WIN32_WCE
+#elif defined __APPLE__
+# define GTEST_OS_MAC 1
+# if TARGET_OS_IPHONE
+# define GTEST_OS_IOS 1
+# endif
+#elif defined __FreeBSD__
+# define GTEST_OS_FREEBSD 1
+#elif defined __linux__
+# define GTEST_OS_LINUX 1
+# if defined __ANDROID__
+# define GTEST_OS_LINUX_ANDROID 1
+# endif
+#elif defined __MVS__
+# define GTEST_OS_ZOS 1
+#elif defined(__sun) && defined(__SVR4)
+# define GTEST_OS_SOLARIS 1
+#elif defined(_AIX)
+# define GTEST_OS_AIX 1
+#elif defined(__hpux)
+# define GTEST_OS_HPUX 1
+#elif defined __native_client__
+# define GTEST_OS_NACL 1
+#elif defined __OpenBSD__
+# define GTEST_OS_OPENBSD 1
+#elif defined __QNX__
+# define GTEST_OS_QNX 1
+#endif // __CYGWIN__
+
+#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
diff --git a/extern/gtest/include/gtest/internal/gtest-port.h b/extern/gtest/include/gtest/internal/gtest-port.h
index 5ff2c29ad43..ad2a43df17d 100644
--- a/extern/gtest/include/gtest/internal/gtest-port.h
+++ b/extern/gtest/include/gtest/internal/gtest-port.h
@@ -30,8 +30,11 @@
// Authors: wan@google.com (Zhanyong Wan)
//
// Low-level types and utilities for porting Google Test to various
-// platforms. They are subject to change without notice. DO NOT USE
-// THEM IN USER CODE.
+// platforms. All macros ending with _ and symbols defined in an
+// internal namespace are subject to change without notice. Code
+// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't
+// end with _ are part of Google Test's public API and can be used by
+// code outside Google Test.
//
// This file is fundamental to Google Test. All other Google Test source
// files are expected to #include this. Therefore, it cannot #include
@@ -40,9 +43,30 @@
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
-// The user can define the following macros in the build script to
-// control Google Test's behavior. If the user doesn't define a macro
-// in this list, Google Test will define it.
+// Environment-describing macros
+// -----------------------------
+//
+// Google Test can be used in many different environments. Macros in
+// this section tell Google Test what kind of environment it is being
+// used in, such that Google Test can provide environment-specific
+// features and implementations.
+//
+// Google Test tries to automatically detect the properties of its
+// environment, so users usually don't need to worry about these
+// macros. However, the automatic detection is not perfect.
+// Sometimes it's necessary for a user to define some of the following
+// macros in the build script to override Google Test's decisions.
+//
+// If the user doesn't define a macro in the list, Google Test will
+// provide a default definition. After this header is #included, all
+// macros in this list will be defined to either 1 or 0.
+//
+// Notes to maintainers:
+// - Each macro here is a user-tweakable knob; do not grow the list
+// lightly.
+// - Use #if to key off these macros. Don't use #ifdef or "#if
+// defined(...)", which will not work as these macros are ALWAYS
+// defined.
//
// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2)
// is/isn't available.
@@ -86,18 +110,23 @@
// - Define to 1 when compiling Google Test itself
// as a shared library.
-// This header defines the following utilities:
+// Platform-indicating macros
+// --------------------------
+//
+// Macros indicating the platform on which Google Test is being used
+// (a macro is defined to 1 if compiled on the given platform;
+// otherwise UNDEFINED -- it's never defined to 0.). Google Test
+// defines these macros automatically. Code outside Google Test MUST
+// NOT define them.
//
-// Macros indicating the current platform (defined to 1 if compiled on
-// the given platform; otherwise undefined):
// GTEST_OS_AIX - IBM AIX
// GTEST_OS_CYGWIN - Cygwin
+// GTEST_OS_FREEBSD - FreeBSD
// GTEST_OS_HPUX - HP-UX
// GTEST_OS_LINUX - Linux
// GTEST_OS_LINUX_ANDROID - Google Android
// GTEST_OS_MAC - Mac OS X
// GTEST_OS_IOS - iOS
-// GTEST_OS_IOS_SIMULATOR - iOS simulator
// GTEST_OS_NACL - Google Native Client (NaCl)
// GTEST_OS_OPENBSD - OpenBSD
// GTEST_OS_QNX - QNX
@@ -107,6 +136,8 @@
// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop
// GTEST_OS_WINDOWS_MINGW - MinGW
// GTEST_OS_WINDOWS_MOBILE - Windows Mobile
+// GTEST_OS_WINDOWS_PHONE - Windows Phone
+// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT
// GTEST_OS_ZOS - z/OS
//
// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the
@@ -116,22 +147,50 @@
// googletestframework@googlegroups.com (patches for fixing them are
// even more welcome!).
//
-// Note that it is possible that none of the GTEST_OS_* macros are defined.
+// It is possible that none of the GTEST_OS_* macros are defined.
+
+// Feature-indicating macros
+// -------------------------
+//
+// Macros indicating which Google Test features are available (a macro
+// is defined to 1 if the corresponding feature is supported;
+// otherwise UNDEFINED -- it's never defined to 0.). Google Test
+// defines these macros automatically. Code outside Google Test MUST
+// NOT define them.
+//
+// These macros are public so that portable tests can be written.
+// Such tests typically surround code using a feature with an #if
+// which controls that code. For example:
+//
+// #if GTEST_HAS_DEATH_TEST
+// EXPECT_DEATH(DoSomethingDeadly());
+// #endif
//
-// Macros indicating available Google Test features (defined to 1 if
-// the corresponding feature is supported; otherwise undefined):
// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized
// tests)
// GTEST_HAS_DEATH_TEST - death tests
// GTEST_HAS_PARAM_TEST - value-parameterized tests
// GTEST_HAS_TYPED_TEST - typed tests
// GTEST_HAS_TYPED_TEST_P - type-parameterized tests
+// GTEST_IS_THREADSAFE - Google Test is thread-safe.
// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with
// GTEST_HAS_POSIX_RE (see above) which users can
// define themselves.
// GTEST_USES_SIMPLE_RE - our own simple regex is used;
// the above two are mutually exclusive.
// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
+
+// Misc public macros
+// ------------------
+//
+// GTEST_FLAG(flag_name) - references the variable corresponding to
+// the given Google Test flag.
+
+// Internal utilities
+// ------------------
+//
+// The following macros and utilities are for Google Test's INTERNAL
+// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY.
//
// Macros for basic C++ coding:
// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
@@ -140,13 +199,18 @@
// GTEST_DISALLOW_ASSIGN_ - disables operator=.
// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used.
+// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is
+// suppressed (constant conditional).
+// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127
+// is suppressed.
+//
+// C++11 feature wrappers:
+//
+// testing::internal::move - portability wrapper for std::move.
//
// Synchronization:
// Mutex, MutexLock, ThreadLocal, GetThreadCount()
-// - synchronization primitives.
-// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above
-// synchronization primitives have real implementations
-// and Google Test is thread-safe; or 0 otherwise.
+// - synchronization primitives.
//
// Template meta programming:
// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only.
@@ -182,7 +246,6 @@
// BiggestInt - the biggest signed integer type.
//
// Command-line utilities:
-// GTEST_FLAG() - references a flag.
// GTEST_DECLARE_*() - declares a flag.
// GTEST_DEFINE_*() - defines a flag.
// GetInjectableArgvs() - returns the command line as a vector of strings.
@@ -208,16 +271,28 @@
# include <TargetConditionals.h>
#endif
+#include <algorithm> // NOLINT
#include <iostream> // NOLINT
#include <sstream> // NOLINT
#include <string> // NOLINT
+#include <utility>
+#include <vector> // NOLINT
-#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
-#define GTEST_FLAG_PREFIX_ "gtest_"
-#define GTEST_FLAG_PREFIX_DASH_ "gtest-"
-#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
-#define GTEST_NAME_ "Google Test"
-#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/"
+#include "gtest/internal/gtest-port-arch.h"
+#include "gtest/internal/custom/gtest-port.h"
+
+#if !defined(GTEST_DEV_EMAIL_)
+# define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
+# define GTEST_FLAG_PREFIX_ "gtest_"
+# define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+# define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
+# define GTEST_NAME_ "Google Test"
+# define GTEST_PROJECT_URL_ "https://github.com/google/googletest/"
+#endif // !defined(GTEST_DEV_EMAIL_)
+
+#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_)
+# define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest"
+#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_)
// Determines the version of gcc that is used to compile this.
#ifdef __GNUC__
@@ -226,48 +301,22 @@
(__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
#endif // __GNUC__
-// Determines the platform on which Google Test is compiled.
-#ifdef __CYGWIN__
-# define GTEST_OS_CYGWIN 1
-#elif defined __SYMBIAN32__
-# define GTEST_OS_SYMBIAN 1
-#elif defined _WIN32
-# define GTEST_OS_WINDOWS 1
-# ifdef _WIN32_WCE
-# define GTEST_OS_WINDOWS_MOBILE 1
-# elif defined(__MINGW__) || defined(__MINGW32__)
-# define GTEST_OS_WINDOWS_MINGW 1
-# else
-# define GTEST_OS_WINDOWS_DESKTOP 1
-# endif // _WIN32_WCE
-#elif defined __APPLE__
-# define GTEST_OS_MAC 1
-# if TARGET_OS_IPHONE
-# define GTEST_OS_IOS 1
-# if TARGET_IPHONE_SIMULATOR
-# define GTEST_OS_IOS_SIMULATOR 1
-# endif
-# endif
-#elif defined __linux__
-# define GTEST_OS_LINUX 1
-# if defined __ANDROID__
-# define GTEST_OS_LINUX_ANDROID 1
-# endif
-#elif defined __MVS__
-# define GTEST_OS_ZOS 1
-#elif defined(__sun) && defined(__SVR4)
-# define GTEST_OS_SOLARIS 1
-#elif defined(_AIX)
-# define GTEST_OS_AIX 1
-#elif defined(__hpux)
-# define GTEST_OS_HPUX 1
-#elif defined __native_client__
-# define GTEST_OS_NACL 1
-#elif defined __OpenBSD__
-# define GTEST_OS_OPENBSD 1
-#elif defined __QNX__
-# define GTEST_OS_QNX 1
-#endif // __CYGWIN__
+// Macros for disabling Microsoft Visual C++ warnings.
+//
+// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385)
+// /* code that triggers warnings C4800 and C4385 */
+// GTEST_DISABLE_MSC_WARNINGS_POP_()
+#if defined(_MSC_VER) && _MSC_VER >= 1500
+# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable: warnings))
+# define GTEST_DISABLE_MSC_WARNINGS_POP_() \
+ __pragma(warning(pop))
+#else
+// Older versions of MSVC don't have __pragma.
+# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings)
+# define GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
#ifndef GTEST_LANG_CXX11
// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when
@@ -282,12 +331,77 @@
# endif
#endif
+// Distinct from C++11 language support, some environments don't provide
+// proper C++11 library support. Notably, it's possible to build in
+// C++11 mode when targeting Mac OS X 10.6, which has an old libstdc++
+// with no C++11 support.
+//
+// libstdc++ has sufficient C++11 support as of GCC 4.6.0, __GLIBCXX__
+// 20110325, but maintenance releases in the 4.4 and 4.5 series followed
+// this date, so check for those versions by their date stamps.
+// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning
+#if GTEST_LANG_CXX11 && \
+ (!defined(__GLIBCXX__) || ( \
+ __GLIBCXX__ >= 20110325ul && /* GCC >= 4.6.0 */ \
+ /* Blacklist of patch releases of older branches: */ \
+ __GLIBCXX__ != 20110416ul && /* GCC 4.4.6 */ \
+ __GLIBCXX__ != 20120313ul && /* GCC 4.4.7 */ \
+ __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \
+ __GLIBCXX__ != 20120702ul)) /* GCC 4.5.4 */
+# define GTEST_STDLIB_CXX11 1
+#endif
+
+// Only use C++11 library features if the library provides them.
+#if defined(GTEST_STDLIB_CXX11) && GTEST_STDLIB_CXX11
+# define GTEST_HAS_STD_BEGIN_AND_END_ 1
+# define GTEST_HAS_STD_FORWARD_LIST_ 1
+# define GTEST_HAS_STD_FUNCTION_ 1
+# define GTEST_HAS_STD_INITIALIZER_LIST_ 1
+# define GTEST_HAS_STD_MOVE_ 1
+# define GTEST_HAS_STD_SHARED_PTR_ 1
+# define GTEST_HAS_STD_TYPE_TRAITS_ 1
+# define GTEST_HAS_STD_UNIQUE_PTR_ 1
+#endif
+
+// C++11 specifies that <tuple> provides std::tuple.
+// Some platforms still might not have it, however.
+#if GTEST_LANG_CXX11
+# define GTEST_HAS_STD_TUPLE_ 1
+# if defined(__clang__)
+// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include
+# if defined(__has_include) && !__has_include(<tuple>)
+# undef GTEST_HAS_STD_TUPLE_
+# endif
+# elif defined(_MSC_VER)
+// Inspired by boost/config/stdlib/dinkumware.hpp
+# if defined(_CPPLIB_VER) && _CPPLIB_VER < 520
+# undef GTEST_HAS_STD_TUPLE_
+# endif
+# elif defined(__GLIBCXX__)
+// Inspired by boost/config/stdlib/libstdcpp3.hpp,
+// http://gcc.gnu.org/gcc-4.2/changes.html and
+// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
+# undef GTEST_HAS_STD_TUPLE_
+# endif
+# endif
+#endif
+
#ifndef GTEST_OS_WINDOWS
# define GTEST_OS_WINDOWS 0
#endif
+#ifndef GTEST_OS_WINDOWS_MINGW
+# define GTEST_OS_WINDOWS_MINGW 0
+#endif
+#ifndef GTEST_OS_WINDOWS_PHONE
+# define GTEST_OS_WINDOWS_PHONE 0
+#endif
#ifndef GTEST_OS_WINDOWS_MOBILE
# define GTEST_OS_WINDOWS_MOBILE 0
#endif
+#ifndef GTEST_OS_WINDOWS_RT
+# define GTEST_OS_WINDOWS_RT 0
+#endif
#ifndef GTEST_OS_LINUX_ANDROID
# define GTEST_OS_LINUX_ANDROID 0
#endif
@@ -307,16 +421,23 @@
// Brings in definitions for functions used in the testing::internal::posix
// namespace (read, write, close, chdir, isatty, stat). We do not currently
// use them on Windows Mobile.
-#if !GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS_MOBILE
+# include <direct.h>
+# include <io.h>
+# endif
+// In order to avoid having to include <windows.h>, use forward declaration
+// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
+// This assumption is verified by
+// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION.
+struct _RTL_CRITICAL_SECTION;
+#else
// This assumes that non-Windows OSes provide unistd.h. For OSes where this
// is not the case, we need to include headers that provide the functions
// mentioned above.
# include <unistd.h>
# include <strings.h>
-#elif !GTEST_OS_WINDOWS_MOBILE
-# include <direct.h>
-# include <io.h>
-#endif
+#endif // GTEST_OS_WINDOWS
#if GTEST_OS_LINUX_ANDROID
// Used to define __ANDROID_API__ matching the target NDK API level.
@@ -333,7 +454,10 @@
# endif
#endif
-#if GTEST_HAS_POSIX_RE
+#if defined(GTEST_USES_PCRE) && GTEST_USES_PCRE
+// The appropriate headers have already been included.
+
+#elif GTEST_HAS_POSIX_RE
// On some platforms, <regex.h> needs someone to define size_t, and
// won't compile otherwise. We can #include it here as we already
@@ -355,7 +479,7 @@
// simple regex implementation instead.
# define GTEST_USES_SIMPLE_RE 1
-#endif // GTEST_HAS_POSIX_RE
+#endif // GTEST_USES_PCRE
#ifndef GTEST_HAS_EXCEPTIONS
// The user didn't tell us whether exceptions are enabled, so we need
@@ -368,6 +492,15 @@
# define _HAS_EXCEPTIONS 1
# endif // _HAS_EXCEPTIONS
# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
+# elif defined(__clang__)
+// clang defines __EXCEPTIONS iff exceptions are enabled before clang 220714,
+// but iff cleanups are enabled after that. In Obj-C++ files, there can be
+// cleanups for ObjC exceptions which also need cleanups, even if C++ exceptions
+// are disabled. clang has __has_feature(cxx_exceptions) which checks for C++
+// exceptions starting at clang r206352, but which checked for cleanups prior to
+// that. To reliably check for C++ exception availability with clang, check for
+// __EXCEPTIONS && __has_feature(cxx_exceptions).
+# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions))
# elif defined(__GNUC__) && __EXCEPTIONS
// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
# define GTEST_HAS_EXCEPTIONS 1
@@ -493,13 +626,13 @@
// Determines whether Google Test can use the pthreads library.
#ifndef GTEST_HAS_PTHREAD
-// The user didn't tell us explicitly, so we assume pthreads support is
-// available on Linux and Mac.
+// The user didn't tell us explicitly, so we make reasonable assumptions about
+// which platforms have pthreads support.
//
// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
// to your compiler flags.
# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \
- || GTEST_OS_QNX)
+ || GTEST_OS_QNX || GTEST_OS_FREEBSD || GTEST_OS_NACL)
#endif // GTEST_HAS_PTHREAD
#if GTEST_HAS_PTHREAD
@@ -511,6 +644,15 @@
# include <time.h> // NOLINT
#endif
+// Determines if hash_map/hash_set are available.
+// Only used for testing against those containers.
+#if !defined(GTEST_HAS_HASH_MAP_)
+# if defined(_MSC_VER)
+# define GTEST_HAS_HASH_MAP_ 1 // Indicates that hash_map is available.
+# define GTEST_HAS_HASH_SET_ 1 // Indicates that hash_set is available.
+# endif // _MSC_VER
+#endif // !defined(GTEST_HAS_HASH_MAP_)
+
// Determines whether Google Test can use tr1/tuple. You can define
// this macro to 0 to prevent Google Test from using tuple (any
// feature depending on tuple with be disabled in this mode).
@@ -563,11 +705,21 @@
// To avoid conditional compilation everywhere, we make it
// gtest-port.h's responsibility to #include the header implementing
-// tr1/tuple.
+// tuple.
+#if defined(GTEST_HAS_STD_TUPLE_) && GTEST_HAS_STD_TUPLE_
+# include <tuple> // IWYU pragma: export
+# define GTEST_TUPLE_NAMESPACE_ ::std
+#endif // GTEST_HAS_STD_TUPLE_
+
+// We include tr1::tuple even if std::tuple is available to define printers for
+// them.
#if GTEST_HAS_TR1_TUPLE
+# ifndef GTEST_TUPLE_NAMESPACE_
+# define GTEST_TUPLE_NAMESPACE_ ::std::tr1
+# endif // GTEST_TUPLE_NAMESPACE_
# if GTEST_USE_OWN_TR1_TUPLE
-# include "gtest/internal/gtest-tuple.h"
+# include "gtest/internal/gtest-tuple.h" // IWYU pragma: export // NOLINT
# elif GTEST_ENV_HAS_STD_TUPLE_
# include <tuple>
// C++11 puts its tuple into the ::std namespace rather than
@@ -598,7 +750,7 @@ using ::std::tuple_size;
// This prevents <boost/tr1/detail/config.hpp>, which defines
// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>.
# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED
-# include <tuple>
+# include <tuple> // IWYU pragma: export // NOLINT
# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)
// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does
@@ -621,7 +773,7 @@ using ::std::tuple_size;
# else
// If the compiler is not GCC 4.0+, we assume the user is using a
// spec-conforming TR1 implementation.
-# include <tuple> // NOLINT
+# include <tuple> // IWYU pragma: export // NOLINT
# endif // GTEST_USE_OWN_TR1_TUPLE
#endif // GTEST_HAS_TR1_TUPLE
@@ -655,7 +807,8 @@ using ::std::tuple_size;
#ifndef GTEST_HAS_STREAM_REDIRECTION
// By default, we assume that stream redirection is supported on all
// platforms except known mobile ones.
-# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || \
+ GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
# define GTEST_HAS_STREAM_REDIRECTION 0
# else
# define GTEST_HAS_STREAM_REDIRECTION 1
@@ -667,12 +820,11 @@ using ::std::tuple_size;
// abort() in a VC 7.1 application compiled as GUI in debug config
// pops up a dialog window that cannot be suppressed programmatically.
#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
- (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \
+ (GTEST_OS_MAC && !GTEST_OS_IOS) || \
(GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \
- GTEST_OS_OPENBSD || GTEST_OS_QNX)
+ GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD)
# define GTEST_HAS_DEATH_TEST 1
-# include <vector> // NOLINT
#endif
// We don't support MSVC 7.1 with exceptions disabled now. Therefore
@@ -736,7 +888,12 @@ using ::std::tuple_size;
// compiler the variable/parameter does not have to be used.
#if defined(__GNUC__) && !defined(COMPILER_ICC)
# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
-#else
+#elif defined(__clang__)
+# if __has_attribute(unused)
+# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+# endif
+#endif
+#ifndef GTEST_ATTRIBUTE_UNUSED_
# define GTEST_ATTRIBUTE_UNUSED_
#endif
@@ -762,6 +919,19 @@ using ::std::tuple_size;
# define GTEST_MUST_USE_RESULT_
#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
+// MS C++ compiler emits warning when a conditional expression is compile time
+// constant. In some contexts this warning is false positive and needs to be
+// suppressed. Use the following two macros in such cases:
+//
+// GTEST_INTENTIONAL_CONST_COND_PUSH_()
+// while (true) {
+// GTEST_INTENTIONAL_CONST_COND_POP_()
+// }
+# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127)
+# define GTEST_INTENTIONAL_CONST_COND_POP_() \
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
+
// Determine whether the compiler supports Microsoft's Structured Exception
// Handling. This is supported by several Windows compilers but generally
// does not exist on any other system.
@@ -776,17 +946,22 @@ using ::std::tuple_size;
# define GTEST_HAS_SEH 0
# endif
+#define GTEST_IS_THREADSAFE \
+ ((defined(GTEST_HAS_MUTEX_AND_THREAD_LOCAL_) && GTEST_HAS_MUTEX_AND_THREAD_LOCAL_) \
+ || (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) \
+ || GTEST_HAS_PTHREAD)
+
#endif // GTEST_HAS_SEH
#ifdef _MSC_VER
-
# if GTEST_LINKED_AS_SHARED_LIBRARY
# define GTEST_API_ __declspec(dllimport)
# elif GTEST_CREATE_SHARED_LIBRARY
# define GTEST_API_ __declspec(dllexport)
# endif
-
-#endif // _MSC_VER
+#elif __GNUC__ >= 4 || defined(__clang__)
+# define GTEST_API_ __attribute__((visibility ("default")))
+#endif // _MSC_VER
#ifndef GTEST_API_
# define GTEST_API_
@@ -806,10 +981,58 @@ using ::std::tuple_size;
# define GTEST_HAS_CXXABI_H_ 0
#endif
+// A function level attribute to disable checking for use of uninitialized
+// memory when built with MemorySanitizer.
+#if defined(__clang__)
+# if __has_feature(memory_sanitizer)
+# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \
+ __attribute__((no_sanitize_memory))
+# else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+# endif // __has_feature(memory_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+#endif // __clang__
+
+// A function level attribute to disable AddressSanitizer instrumentation.
+#if defined(__clang__)
+# if __has_feature(address_sanitizer)
+# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \
+ __attribute__((no_sanitize_address))
+# else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+# endif // __has_feature(address_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+#endif // __clang__
+
+// A function level attribute to disable ThreadSanitizer instrumentation.
+#if defined(__clang__)
+# if __has_feature(thread_sanitizer)
+# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \
+ __attribute__((no_sanitize_thread))
+# else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
+# endif // __has_feature(thread_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
+#endif // __clang__
+
namespace testing {
class Message;
+#if defined(GTEST_TUPLE_NAMESPACE_)
+// Import tuple and friends into the ::testing namespace.
+// It is part of our interface, having them in ::testing allows us to change
+// their types as needed.
+using GTEST_TUPLE_NAMESPACE_::get;
+using GTEST_TUPLE_NAMESPACE_::make_tuple;
+using GTEST_TUPLE_NAMESPACE_::tuple;
+using GTEST_TUPLE_NAMESPACE_::tuple_size;
+using GTEST_TUPLE_NAMESPACE_::tuple_element;
+#endif // defined(GTEST_TUPLE_NAMESPACE_)
+
namespace internal {
// A secret type that Google Test users don't know about. It has no
@@ -821,8 +1044,8 @@ class Secret;
// expression is true. For example, you could use it to verify the
// size of a static array:
//
-// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
-// content_type_names_incorrect_size);
+// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES,
+// names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
@@ -832,16 +1055,22 @@ class Secret;
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
+#if GTEST_LANG_CXX11
+# define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg)
+#else // !GTEST_LANG_CXX11
template <bool>
-struct CompileAssert {
+ struct CompileAssert {
};
-#define GTEST_COMPILE_ASSERT_(expr, msg) \
+# define GTEST_COMPILE_ASSERT_(expr, msg) \
typedef ::testing::internal::CompileAssert<(static_cast<bool>(expr))> \
msg[static_cast<bool>(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_
+#endif // !GTEST_LANG_CXX11
// Implementation details of GTEST_COMPILE_ASSERT_:
//
+// (In C++11, we simply use static_assert instead of the following)
+//
// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
@@ -888,7 +1117,12 @@ template <typename T1, typename T2>
struct StaticAssertTypeEqHelper;
template <typename T>
-struct StaticAssertTypeEqHelper<T, T> {};
+struct StaticAssertTypeEqHelper<T, T> {
+ enum { value = true };
+};
+
+// Evaluates to the number of elements in 'array'.
+#define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0]))
#if GTEST_HAS_GLOBAL_STRING
typedef ::string string;
@@ -937,6 +1171,11 @@ class scoped_ptr {
}
}
+ friend void swap(scoped_ptr& a, scoped_ptr& b) {
+ using std::swap;
+ swap(a.ptr_, b.ptr_);
+ }
+
private:
T* ptr_;
@@ -1060,13 +1299,18 @@ class GTEST_API_ GTestLog {
GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog);
};
-#define GTEST_LOG_(severity) \
+#if !defined(GTEST_LOG_)
+
+# define GTEST_LOG_(severity) \
::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \
__FILE__, __LINE__).GetStream()
inline void LogToStderr() {}
inline void FlushInfoLog() { fflush(NULL); }
+#endif // !defined(GTEST_LOG_)
+
+#if !defined(GTEST_CHECK_)
// INTERNAL IMPLEMENTATION - DO NOT USE.
//
// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition
@@ -1081,12 +1325,13 @@ inline void FlushInfoLog() { fflush(NULL); }
// condition itself, plus additional message streamed into it, if any,
// and then it aborts the program. It aborts the program irrespective of
// whether it is built in the debug mode or not.
-#define GTEST_CHECK_(condition) \
+# define GTEST_CHECK_(condition) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::IsTrue(condition)) \
; \
else \
GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+#endif // !defined(GTEST_CHECK_)
// An all-mode assert to verify that the given POSIX-style function
// call returns 0 (indicating success). Known limitation: this
@@ -1098,6 +1343,15 @@ inline void FlushInfoLog() { fflush(NULL); }
GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
<< gtest_error
+#if defined(GTEST_HAS_STD_MOVE_) && GTEST_HAS_STD_MOVE_
+using std::move;
+#else // GTEST_HAS_STD_MOVE_
+template <typename T>
+const T& move(const T& t) {
+ return t;
+}
+#endif // GTEST_HAS_STD_MOVE_
+
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Use ImplicitCast_ as a safe version of static_cast for upcasting in
@@ -1148,7 +1402,9 @@ inline To DownCast_(From* f) { // so we only accept pointers
// for compile-time type checking, and has no overhead in an
// optimized build at run-time, as it will be optimized away
// completely.
+ GTEST_INTENTIONAL_CONST_COND_PUSH_()
if (false) {
+ GTEST_INTENTIONAL_CONST_COND_POP_()
const To to = NULL;
::testing::internal::ImplicitCast_<From*>(to);
}
@@ -1169,6 +1425,11 @@ template <class Derived, class Base>
Derived* CheckedDowncastToActualType(Base* base) {
#if GTEST_HAS_RTTI
GTEST_CHECK_(typeid(*base) == typeid(Derived));
+#endif
+
+#if defined(GTEST_HAS_DOWNCAST_) && GTEST_HAS_DOWNCAST_
+ return ::down_cast<Derived*>(base);
+#elif GTEST_HAS_RTTI
return dynamic_cast<Derived*>(base); // NOLINT
#else
return static_cast<Derived*>(base); // Poor man's downcast.
@@ -1190,6 +1451,17 @@ GTEST_API_ std::string GetCapturedStderr();
#endif // GTEST_HAS_STREAM_REDIRECTION
+// Returns a path to temporary directory.
+GTEST_API_ std::string TempDir();
+
+// Returns the size (in bytes) of a file.
+GTEST_API_ size_t GetFileSize(FILE* file);
+
+// Reads the entire content of a file as a string.
+GTEST_API_ std::string ReadEntireFile(FILE* file);
+
+// All command line arguments.
+GTEST_API_ const ::std::vector<testing::internal::string>& GetArgvs();
#if GTEST_HAS_DEATH_TEST
@@ -1197,18 +1469,15 @@ const ::std::vector<testing::internal::string>& GetInjectableArgvs();
void SetInjectableArgvs(const ::std::vector<testing::internal::string>*
new_argvs);
-// A copy of all command line arguments. Set by InitGoogleTest().
-extern ::std::vector<testing::internal::string> g_argvs;
#endif // GTEST_HAS_DEATH_TEST
// Defines synchronization primitives.
-
-#if GTEST_HAS_PTHREAD
-
-// Sleeps for (roughly) n milli-seconds. This function is only for
-// testing Google Test's own constructs. Don't use it in user tests,
-// either directly or indirectly.
+#if GTEST_IS_THREADSAFE
+# if GTEST_HAS_PTHREAD
+// Sleeps for (roughly) n milliseconds. This function is only for testing
+// Google Test's own constructs. Don't use it in user tests, either
+// directly or indirectly.
inline void SleepMilliseconds(int n) {
const timespec time = {
0, // 0 seconds.
@@ -1216,7 +1485,13 @@ inline void SleepMilliseconds(int n) {
};
nanosleep(&time, NULL);
}
+# endif // GTEST_HAS_PTHREAD
+
+# if defined(GTEST_HAS_NOTIFICATION_) && GTEST_HAS_NOTIFICATION_
+// Notification has already been imported into the namespace.
+// Nothing to do here.
+# elif GTEST_HAS_PTHREAD
// Allows a controller thread to pause execution of newly created
// threads until notified. Instances of this class must be created
// and destroyed in the controller thread.
@@ -1260,6 +1535,62 @@ class Notification {
GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
};
+# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+
+GTEST_API_ void SleepMilliseconds(int n);
+
+// Provides leak-safe Windows kernel handle ownership.
+// Used in death tests and in threading support.
+class GTEST_API_ AutoHandle {
+ public:
+ // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to
+ // avoid including <windows.h> in this header file. Including <windows.h> is
+ // undesirable because it defines a lot of symbols and macros that tend to
+ // conflict with client code. This assumption is verified by
+ // WindowsTypesTest.HANDLEIsVoidStar.
+ typedef void* Handle;
+ AutoHandle();
+ explicit AutoHandle(Handle handle);
+
+ ~AutoHandle();
+
+ Handle Get() const;
+ void Reset();
+ void Reset(Handle handle);
+
+ private:
+ // Returns true iff the handle is a valid handle object that can be closed.
+ bool IsCloseable() const;
+
+ Handle handle_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
+};
+
+// Allows a controller thread to pause execution of newly created
+// threads until notified. Instances of this class must be created
+// and destroyed in the controller thread.
+//
+// This class is only for testing Google Test's own constructs. Do not
+// use it in user tests, either directly or indirectly.
+class GTEST_API_ Notification {
+ public:
+ Notification();
+ void Notify();
+ void WaitForNotification();
+
+ private:
+ AutoHandle event_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
+};
+# endif // GTEST_HAS_NOTIFICATION_
+
+// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD
+// defined, but we don't want to use MinGW's pthreads implementation, which
+// has conformance problems with some versions of the POSIX standard.
+# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW
+
// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
// Consequently, it cannot select a correct instantiation of ThreadWithParam
// in order to call its Run(). Introducing ThreadWithParamBase as a
@@ -1297,10 +1628,9 @@ extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
template <typename T>
class ThreadWithParam : public ThreadWithParamBase {
public:
- typedef void (*UserThreadFunc)(T);
+ typedef void UserThreadFunc(T);
- ThreadWithParam(
- UserThreadFunc func, T param, Notification* thread_can_start)
+ ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start)
: func_(func),
param_(param),
thread_can_start_(thread_can_start),
@@ -1327,7 +1657,7 @@ class ThreadWithParam : public ThreadWithParamBase {
}
private:
- const UserThreadFunc func_; // User-supplied thread function.
+ UserThreadFunc* const func_; // User-supplied thread function.
const T param_; // User-supplied parameter to the thread function.
// When non-NULL, used to block execution until the controller thread
// notifies.
@@ -1337,26 +1667,293 @@ class ThreadWithParam : public ThreadWithParamBase {
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
};
+# endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD ||
+ // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
+
+# if defined(GTEST_HAS_MUTEX_AND_THREAD_LOCAL_) && GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
+// Mutex and ThreadLocal have already been imported into the namespace.
+// Nothing to do here.
+
+# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
-// MutexBase and Mutex implement mutex on pthreads-based platforms. They
-// are used in conjunction with class MutexLock:
+// Mutex implements mutex on Windows platforms. It is used in conjunction
+// with class MutexLock:
//
// Mutex mutex;
// ...
-// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end
-// // of the current scope.
-//
-// MutexBase implements behavior for both statically and dynamically
-// allocated mutexes. Do not use MutexBase directly. Instead, write
-// the following to define a static mutex:
+// MutexLock lock(&mutex); // Acquires the mutex and releases it at the
+// // end of the current scope.
//
+// A static Mutex *must* be defined or declared using one of the following
+// macros:
// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
+// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
//
-// You can forward declare a static mutex like this:
+// (A non-static Mutex is defined/declared in the usual way).
+class GTEST_API_ Mutex {
+ public:
+ enum MutexType { kStatic = 0, kDynamic = 1 };
+ // We rely on kStaticMutex being 0 as it is to what the linker initializes
+ // type_ in static mutexes. critical_section_ will be initialized lazily
+ // in ThreadSafeLazyInit().
+ enum StaticConstructorSelector { kStaticMutex = 0 };
+
+ // This constructor intentionally does nothing. It relies on type_ being
+ // statically initialized to 0 (effectively setting it to kStatic) and on
+ // ThreadSafeLazyInit() to lazily initialize the rest of the members.
+ explicit Mutex(StaticConstructorSelector /*dummy*/) {}
+
+ Mutex();
+ ~Mutex();
+
+ void Lock();
+
+ void Unlock();
+
+ // Does nothing if the current thread holds the mutex. Otherwise, crashes
+ // with high probability.
+ void AssertHeld();
+
+ private:
+ // Initializes owner_thread_id_ and critical_section_ in static mutexes.
+ void ThreadSafeLazyInit();
+
+ // Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx,
+ // we assume that 0 is an invalid value for thread IDs.
+ unsigned int owner_thread_id_;
+
+ // For static mutexes, we rely on these members being initialized to zeros
+ // by the linker.
+ MutexType type_;
+ long critical_section_init_phase_; // NOLINT
+ _RTL_CRITICAL_SECTION* critical_section_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
+};
+
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+ extern ::testing::internal::Mutex mutex
+
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+ ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex)
+
+// We cannot name this class MutexLock because the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. That macro is used as a defensive measure to prevent against
+// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
+// "MutexLock l(&mu)". Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+ explicit GTestMutexLock(Mutex* mutex)
+ : mutex_(mutex) { mutex_->Lock(); }
+
+ ~GTestMutexLock() { mutex_->Unlock(); }
+
+ private:
+ Mutex* const mutex_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
+};
+
+typedef GTestMutexLock MutexLock;
+
+// Base class for ValueHolder<T>. Allows a caller to hold and delete a value
+// without knowing its type.
+class ThreadLocalValueHolderBase {
+ public:
+ virtual ~ThreadLocalValueHolderBase() {}
+};
+
+// Provides a way for a thread to send notifications to a ThreadLocal
+// regardless of its parameter type.
+class ThreadLocalBase {
+ public:
+ // Creates a new ValueHolder<T> object holding a default value passed to
+ // this ThreadLocal<T>'s constructor and returns it. It is the caller's
+ // responsibility not to call this when the ThreadLocal<T> instance already
+ // has a value on the current thread.
+ virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0;
+
+ protected:
+ ThreadLocalBase() {}
+ virtual ~ThreadLocalBase() {}
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase);
+};
+
+// Maps a thread to a set of ThreadLocals that have values instantiated on that
+// thread and notifies them when the thread exits. A ThreadLocal instance is
+// expected to persist until all threads it has values on have terminated.
+class GTEST_API_ ThreadLocalRegistry {
+ public:
+ // Registers thread_local_instance as having value on the current thread.
+ // Returns a value that can be used to identify the thread from other threads.
+ static ThreadLocalValueHolderBase* GetValueOnCurrentThread(
+ const ThreadLocalBase* thread_local_instance);
+
+ // Invoked when a ThreadLocal instance is destroyed.
+ static void OnThreadLocalDestroyed(
+ const ThreadLocalBase* thread_local_instance);
+};
+
+class GTEST_API_ ThreadWithParamBase {
+ public:
+ void Join();
+
+ protected:
+ class Runnable {
+ public:
+ virtual ~Runnable() {}
+ virtual void Run() = 0;
+ };
+
+ ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start);
+ virtual ~ThreadWithParamBase();
+
+ private:
+ AutoHandle thread_;
+};
+
+// Helper class for testing Google Test's multi-threading constructs.
+template <typename T>
+class ThreadWithParam : public ThreadWithParamBase {
+ public:
+ typedef void UserThreadFunc(T);
+
+ ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start)
+ : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) {
+ }
+ virtual ~ThreadWithParam() {}
+
+ private:
+ class RunnableImpl : public Runnable {
+ public:
+ RunnableImpl(UserThreadFunc* func, T param)
+ : func_(func),
+ param_(param) {
+ }
+ virtual ~RunnableImpl() {}
+ virtual void Run() {
+ func_(param_);
+ }
+
+ private:
+ UserThreadFunc* const func_;
+ const T param_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl);
+ };
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
+};
+
+// Implements thread-local storage on Windows systems.
//
-// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
+// // Thread 1
+// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
+//
+// // Thread 2
+// tl.set(150); // Changes the value for thread 2 only.
+// EXPECT_EQ(150, tl.get());
+//
+// // Thread 1
+// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value.
+// tl.set(200);
+// EXPECT_EQ(200, tl.get());
+//
+// The template type argument T must have a public copy constructor.
+// In addition, the default ThreadLocal constructor requires T to have
+// a public default constructor.
+//
+// The users of a TheadLocal instance have to make sure that all but one
+// threads (including the main one) using that instance have exited before
+// destroying it. Otherwise, the per-thread objects managed for them by the
+// ThreadLocal instance are not guaranteed to be destroyed on all platforms.
//
-// To create a dynamic mutex, just define an object of type Mutex.
+// Google Test only uses global ThreadLocal objects. That means they
+// will die after main() has returned. Therefore, no per-thread
+// object managed by Google Test will be leaked as long as all threads
+// using Google Test have exited when main() returns.
+template <typename T>
+class ThreadLocal : public ThreadLocalBase {
+ public:
+ ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {}
+ explicit ThreadLocal(const T& value)
+ : default_factory_(new InstanceValueHolderFactory(value)) {}
+
+ ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); }
+
+ T* pointer() { return GetOrCreateValue(); }
+ const T* pointer() const { return GetOrCreateValue(); }
+ const T& get() const { return *pointer(); }
+ void set(const T& value) { *pointer() = value; }
+
+ private:
+ // Holds a value of T. Can be deleted via its base class without the caller
+ // knowing the type of T.
+ class ValueHolder : public ThreadLocalValueHolderBase {
+ public:
+ ValueHolder() : value_() {}
+ explicit ValueHolder(const T& value) : value_(value) {}
+
+ T* pointer() { return &value_; }
+
+ private:
+ T value_;
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
+ };
+
+
+ T* GetOrCreateValue() const {
+ return static_cast<ValueHolder*>(
+ ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer();
+ }
+
+ virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const {
+ return default_factory_->MakeNewHolder();
+ }
+
+ class ValueHolderFactory {
+ public:
+ ValueHolderFactory() {}
+ virtual ~ValueHolderFactory() {}
+ virtual ValueHolder* MakeNewHolder() const = 0;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory);
+ };
+
+ class DefaultValueHolderFactory : public ValueHolderFactory {
+ public:
+ DefaultValueHolderFactory() {}
+ virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
+ };
+
+ class InstanceValueHolderFactory : public ValueHolderFactory {
+ public:
+ explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
+ virtual ValueHolder* MakeNewHolder() const {
+ return new ValueHolder(value_);
+ }
+
+ private:
+ const T value_; // The value for each thread.
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
+ };
+
+ scoped_ptr<ValueHolderFactory> default_factory_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
+};
+
+# elif GTEST_HAS_PTHREAD
+
+// MutexBase and Mutex implement mutex on pthreads-based platforms.
class MutexBase {
public:
// Acquires this mutex.
@@ -1401,17 +1998,12 @@ class MutexBase {
};
// Forward-declares a static mutex.
-# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
- extern ::testing::internal::MutexBase mutex
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+ extern ::testing::internal::MutexBase mutex
// Defines and statically (i.e. at link time) initializes a static mutex.
-// The initialization list here does not explicitly initialize each field,
-// instead relying on default initialization for the unspecified fields. In
-// particular, the owner_ field (a pthread_t) is not explicitly initialized.
-// This allows initialization to work whether pthread_t is a scalar or struct.
-// The flag -Wmissing-field-initializers must not be specified for this to work.
-# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
- ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+ ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false, pthread_t() }
// The Mutex class can only be used for mutexes created at runtime. It
// shares its API with MutexBase otherwise.
@@ -1429,9 +2021,11 @@ class Mutex : public MutexBase {
GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
};
-// We cannot name this class MutexLock as the ctor declaration would
+// We cannot name this class MutexLock because the ctor declaration would
// conflict with a macro named MutexLock, which is defined on some
-// platforms. Hence the typedef trick below.
+// platforms. That macro is used as a defensive measure to prevent against
+// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
+// "MutexLock l(&mu)". Hence the typedef trick below.
class GTestMutexLock {
public:
explicit GTestMutexLock(MutexBase* mutex)
@@ -1465,41 +2059,14 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
}
// Implements thread-local storage on pthreads-based systems.
-//
-// // Thread 1
-// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
-//
-// // Thread 2
-// tl.set(150); // Changes the value for thread 2 only.
-// EXPECT_EQ(150, tl.get());
-//
-// // Thread 1
-// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value.
-// tl.set(200);
-// EXPECT_EQ(200, tl.get());
-//
-// The template type argument T must have a public copy constructor.
-// In addition, the default ThreadLocal constructor requires T to have
-// a public default constructor.
-//
-// An object managed for a thread by a ThreadLocal instance is deleted
-// when the thread exits. Or, if the ThreadLocal instance dies in
-// that thread, when the ThreadLocal dies. It's the user's
-// responsibility to ensure that all other threads using a ThreadLocal
-// have exited when it dies, or the per-thread objects for those
-// threads will not be deleted.
-//
-// Google Test only uses global ThreadLocal objects. That means they
-// will die after main() has returned. Therefore, no per-thread
-// object managed by Google Test will be leaked as long as all threads
-// using Google Test have exited when main() returns.
template <typename T>
class ThreadLocal {
public:
- ThreadLocal() : key_(CreateKey()),
- default_() {}
- explicit ThreadLocal(const T& value) : key_(CreateKey()),
- default_(value) {}
+ ThreadLocal()
+ : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {}
+ explicit ThreadLocal(const T& value)
+ : key_(CreateKey()),
+ default_factory_(new InstanceValueHolderFactory(value)) {}
~ThreadLocal() {
// Destroys the managed object for the current thread, if any.
@@ -1519,6 +2086,7 @@ class ThreadLocal {
// Holds a value of type T.
class ValueHolder : public ThreadLocalValueHolderBase {
public:
+ ValueHolder() : value_() {}
explicit ValueHolder(const T& value) : value_(value) {}
T* pointer() { return &value_; }
@@ -1544,22 +2112,54 @@ class ThreadLocal {
return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
}
- ValueHolder* const new_holder = new ValueHolder(default_);
+ ValueHolder* const new_holder = default_factory_->MakeNewHolder();
ThreadLocalValueHolderBase* const holder_base = new_holder;
GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base));
return new_holder->pointer();
}
+ class ValueHolderFactory {
+ public:
+ ValueHolderFactory() {}
+ virtual ~ValueHolderFactory() {}
+ virtual ValueHolder* MakeNewHolder() const = 0;
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory);
+ };
+
+ class DefaultValueHolderFactory : public ValueHolderFactory {
+ public:
+ DefaultValueHolderFactory() {}
+ virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); }
+
+ private:
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
+ };
+
+ class InstanceValueHolderFactory : public ValueHolderFactory {
+ public:
+ explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
+ virtual ValueHolder* MakeNewHolder() const {
+ return new ValueHolder(value_);
+ }
+
+ private:
+ const T value_; // The value for each thread.
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
+ };
+
// A key pthreads uses for looking up per-thread values.
const pthread_key_t key_;
- const T default_; // The default value for each thread.
+ scoped_ptr<ValueHolderFactory> default_factory_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
};
-# define GTEST_IS_THREADSAFE 1
+# endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
-#else // GTEST_HAS_PTHREAD
+#else // GTEST_IS_THREADSAFE
// A dummy implementation of synchronization primitives (mutex, lock,
// and thread-local variable). Necessary for compiling Google Test where
@@ -1579,6 +2179,11 @@ class Mutex {
# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
+// We cannot name this class MutexLock because the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. That macro is used as a defensive measure to prevent against
+// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
+// "MutexLock l(&mu)". Hence the typedef trick below.
class GTestMutexLock {
public:
explicit GTestMutexLock(Mutex*) {} // NOLINT
@@ -1599,11 +2204,7 @@ class ThreadLocal {
T value_;
};
-// The above synchronization primitives have dummy implementations.
-// Therefore Google Test is not thread-safe.
-# define GTEST_IS_THREADSAFE 0
-
-#endif // GTEST_HAS_PTHREAD
+#endif // GTEST_IS_THREADSAFE
// Returns the number of threads running in the process, or 0 to indicate that
// we cannot detect it.
@@ -1713,6 +2314,13 @@ inline char ToUpper(char ch) {
return static_cast<char>(toupper(static_cast<unsigned char>(ch)));
}
+inline std::string StripTrailingSpaces(std::string str) {
+ std::string::iterator it = str.end();
+ while (it != str.begin() && IsSpace(*--it))
+ it = str.erase(it);
+ return str;
+}
+
// The testing::internal::posix namespace holds wrappers for common
// POSIX functions. These wrappers hide the differences between
// Windows/MSVC and POSIX systems. Since some compilers define these
@@ -1776,11 +2384,7 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
// Functions deprecated by MSVC 8.0.
-#ifdef _MSC_VER
-// Temporarily disable warning 4996 (deprecated function).
-# pragma warning(push)
-# pragma warning(disable:4996)
-#endif
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996 /* deprecated function */)
inline const char* StrNCpy(char* dest, const char* src, size_t n) {
return strncpy(dest, src, n);
@@ -1790,7 +2394,7 @@ inline const char* StrNCpy(char* dest, const char* src, size_t n) {
// StrError() aren't needed on Windows CE at this time and thus not
// defined there.
-#if !GTEST_OS_WINDOWS_MOBILE
+#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
inline int ChDir(const char* dir) { return chdir(dir); }
#endif
inline FILE* FOpen(const char* path, const char* mode) {
@@ -1814,8 +2418,9 @@ inline int Close(int fd) { return close(fd); }
inline const char* StrError(int errnum) { return strerror(errnum); }
#endif
inline const char* GetEnv(const char* name) {
-#if GTEST_OS_WINDOWS_MOBILE
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE | GTEST_OS_WINDOWS_RT
// We are on Windows CE, which has no environment variables.
+ static_cast<void>(name); // To prevent 'unused argument' warning.
return NULL;
#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
// Environment variables which we programmatically clear will be set to the
@@ -1827,9 +2432,7 @@ inline const char* GetEnv(const char* name) {
#endif
}
-#ifdef _MSC_VER
-# pragma warning(pop) // Restores the warning state.
-#endif
+GTEST_DISABLE_MSC_WARNINGS_POP_()
#if GTEST_OS_WINDOWS_MOBILE
// Windows CE has no C library. The abort() function is used in
@@ -1930,11 +2533,20 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds.
// Utilities for command line flags and environment variables.
// Macro for referencing flags.
-#define GTEST_FLAG(name) FLAGS_gtest_##name
+#if !defined(GTEST_FLAG)
+# define GTEST_FLAG(name) FLAGS_gtest_##name
+#endif // !defined(GTEST_FLAG)
+
+#if !defined(GTEST_USE_OWN_FLAGFILE_FLAG_)
+# define GTEST_USE_OWN_FLAGFILE_FLAG_ 1
+#endif // !defined(GTEST_USE_OWN_FLAGFILE_FLAG_)
+
+#if !defined(GTEST_DECLARE_bool_)
+# define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver
// Macros for declaring flags.
-#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
-#define GTEST_DECLARE_int32_(name) \
+# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
+# define GTEST_DECLARE_int32_(name) \
GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
#define GTEST_DECLARE_string_(name) \
GTEST_API_ extern ::std::string GTEST_FLAG(name)
@@ -1947,9 +2559,13 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds.
#define GTEST_DEFINE_string_(name, default_val, doc) \
GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
+#endif // !defined(GTEST_DECLARE_bool_)
+
// Thread annotations
-#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
-#define GTEST_LOCK_EXCLUDED_(locks)
+#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_)
+# define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
+# define GTEST_LOCK_EXCLUDED_(locks)
+#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_)
// Parses 'str' for a 32-bit signed integer. If successful, writes the result
// to *value and returns true; otherwise leaves *value unchanged and returns
@@ -1963,7 +2579,7 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value);
// corresponding to the given Google Test flag.
bool BoolFromGTestEnv(const char* flag, bool default_val);
GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
-const char* StringFromGTestEnv(const char* flag, const char* default_val);
+std::string StringFromGTestEnv(const char* flag, const char* default_val);
} // namespace internal
} // namespace testing
diff --git a/extern/gtest/include/gtest/internal/gtest-tuple.h b/extern/gtest/include/gtest/internal/gtest-tuple.h
index 7b3dfc312dc..e9b405340a8 100644
--- a/extern/gtest/include/gtest/internal/gtest-tuple.h
+++ b/extern/gtest/include/gtest/internal/gtest-tuple.h
@@ -53,6 +53,14 @@
private:
#endif
+// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict
+// with our own definitions. Therefore using our own tuple does not work on
+// those compilers.
+#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */
+# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \
+GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers."
+#endif
+
// GTEST_n_TUPLE_(T) is the type of an n-tuple.
#define GTEST_0_TUPLE_(T) tuple<>
#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \
diff --git a/extern/gtest/src/gtest-death-test.cc b/extern/gtest/src/gtest-death-test.cc
index a6023fce4fa..a01a3698308 100644
--- a/extern/gtest/src/gtest-death-test.cc
+++ b/extern/gtest/src/gtest-death-test.cc
@@ -33,6 +33,7 @@
#include "gtest/gtest-death-test.h"
#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/custom/gtest.h"
#if GTEST_HAS_DEATH_TEST
@@ -68,9 +69,9 @@
// Indicates that this translation unit is part of Google Test's
// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick is to
-// prevent a user from accidentally including gtest-internal-inl.h in
-// his code.
+// included, or there will be a compiler error. This trick exists to
+// prevent the accidental inclusion of gtest-internal-inl.h in the
+// user's code.
#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
#undef GTEST_IMPLEMENTATION_
@@ -120,7 +121,9 @@ namespace internal {
// Valid only for fast death tests. Indicates the code is running in the
// child process of a fast style death test.
+# if !GTEST_OS_WINDOWS
static bool g_in_fast_death_test_child = false;
+# endif
// Returns a Boolean value indicating whether the caller is currently
// executing in the context of the death test child process. Tools such as
@@ -169,6 +172,14 @@ KilledBySignal::KilledBySignal(int signum) : signum_(signum) {
// KilledBySignal function-call operator.
bool KilledBySignal::operator()(int exit_status) const {
+# if defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_)
+ {
+ bool result;
+ if (GTEST_KILLED_BY_SIGNAL_OVERRIDE_(signum_, exit_status, &result)) {
+ return result;
+ }
+ }
+# endif // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_)
return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_;
}
# endif // !GTEST_OS_WINDOWS
@@ -875,6 +886,11 @@ class ExecDeathTest : public ForkingDeathTest {
static ::std::vector<testing::internal::string>
GetArgvsForDeathTestChildProcess() {
::std::vector<testing::internal::string> args = GetInjectableArgvs();
+# if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_)
+ ::std::vector<testing::internal::string> extra_args =
+ GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_();
+ args.insert(args.end(), extra_args.begin(), extra_args.end());
+# endif // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_)
return args;
}
// The name of the file in which the death test is located.
@@ -985,6 +1001,8 @@ void StackLowerThanAddress(const void* ptr, bool* result) {
*result = (&dummy < ptr);
}
+// Make sure AddressSanitizer does not tamper with the stack here.
+GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
bool StackGrowsDown() {
int dummy;
bool result;
@@ -1202,26 +1220,6 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex,
return true;
}
-// Splits a given string on a given delimiter, populating a given
-// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have
-// ::std::string, so we can use it here.
-static void SplitString(const ::std::string& str, char delimiter,
- ::std::vector< ::std::string>* dest) {
- ::std::vector< ::std::string> parsed;
- ::std::string::size_type pos = 0;
- while (::testing::internal::AlwaysTrue()) {
- const ::std::string::size_type colon = str.find(delimiter, pos);
- if (colon == ::std::string::npos) {
- parsed.push_back(str.substr(pos));
- break;
- } else {
- parsed.push_back(str.substr(pos, colon - pos));
- pos = colon + 1;
- }
- }
- dest->swap(parsed);
-}
-
# if GTEST_OS_WINDOWS
// Recreates the pipe and event handles from the provided parameters,
// signals the event, and returns a file descriptor wrapped around the pipe
diff --git a/extern/gtest/src/gtest-filepath.cc b/extern/gtest/src/gtest-filepath.cc
index 6be58b6fca2..0292dc11957 100644
--- a/extern/gtest/src/gtest-filepath.cc
+++ b/extern/gtest/src/gtest-filepath.cc
@@ -70,7 +70,6 @@ namespace internal {
// of them.
const char kPathSeparator = '\\';
const char kAlternatePathSeparator = '/';
-const char kPathSeparatorString[] = "\\";
const char kAlternatePathSeparatorString[] = "/";
# if GTEST_OS_WINDOWS_MOBILE
// Windows CE doesn't have a current directory. You should not use
@@ -84,7 +83,6 @@ const char kCurrentDirectoryString[] = ".\\";
# endif // GTEST_OS_WINDOWS_MOBILE
#else
const char kPathSeparator = '/';
-const char kPathSeparatorString[] = "/";
const char kCurrentDirectoryString[] = "./";
#endif // GTEST_OS_WINDOWS
@@ -99,7 +97,7 @@ static bool IsPathSeparator(char c) {
// Returns the current working directory, or "" if unsuccessful.
FilePath FilePath::GetCurrentDir() {
-#if GTEST_OS_WINDOWS_MOBILE
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
// Windows CE doesn't have a current directory, so we just return
// something reasonable.
return FilePath(kCurrentDirectoryString);
@@ -108,7 +106,14 @@ FilePath FilePath::GetCurrentDir() {
return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
#else
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
- return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+ char* result = getcwd(cwd, sizeof(cwd));
+# if GTEST_OS_NACL
+ // getcwd will likely fail in NaCl due to the sandbox, so return something
+ // reasonable. The user may have provided a shim implementation for getcwd,
+ // however, so fallback only when failure is detected.
+ return FilePath(result == NULL ? kCurrentDirectoryString : cwd);
+# endif // GTEST_OS_NACL
+ return FilePath(result == NULL ? "" : cwd);
#endif // GTEST_OS_WINDOWS_MOBILE
}
diff --git a/extern/gtest/src/gtest-internal-inl.h b/extern/gtest/src/gtest-internal-inl.h
index 35df303cca6..ed8a682a964 100644
--- a/extern/gtest/src/gtest-internal-inl.h
+++ b/extern/gtest/src/gtest-internal-inl.h
@@ -40,7 +40,7 @@
// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is
// part of Google Test's implementation; otherwise it's undefined.
#if !GTEST_IMPLEMENTATION_
-// A user is trying to include this from his code - just say no.
+// If this file is included from the user's code, just say no.
# error "gtest-internal-inl.h is part of Google Test's internal implementation."
# error "It must not be included except by Google Test itself."
#endif // GTEST_IMPLEMENTATION_
@@ -100,6 +100,7 @@ const char kShuffleFlag[] = "shuffle";
const char kStackTraceDepthFlag[] = "stack_trace_depth";
const char kStreamResultToFlag[] = "stream_result_to";
const char kThrowOnFailureFlag[] = "throw_on_failure";
+const char kFlagfileFlag[] = "flagfile";
// A valid random seed must be in [1, kMaxRandomSeed].
const int kMaxRandomSeed = 99999;
@@ -432,6 +433,10 @@ class OsStackTraceGetterInterface {
// CurrentStackTrace() will use to find and hide Google Test stack frames.
virtual void UponLeavingGTest() = 0;
+ // This string is inserted in place of stack frames that are part of
+ // Google Test's implementation.
+ static const char* const kElidedFramesMarker;
+
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface);
};
@@ -439,26 +444,12 @@ class OsStackTraceGetterInterface {
// A working implementation of the OsStackTraceGetterInterface interface.
class OsStackTraceGetter : public OsStackTraceGetterInterface {
public:
- OsStackTraceGetter() : caller_frame_(NULL) {}
-
- virtual string CurrentStackTrace(int max_depth, int skip_count)
- GTEST_LOCK_EXCLUDED_(mutex_);
+ OsStackTraceGetter() {}
- virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_);
-
- // This string is inserted in place of stack frames that are part of
- // Google Test's implementation.
- static const char* const kElidedFramesMarker;
+ virtual string CurrentStackTrace(int max_depth, int skip_count);
+ virtual void UponLeavingGTest();
private:
- Mutex mutex_; // protects all internal state
-
- // We save the stack frame below the frame that calls user code.
- // We do this because the address of the frame immediately below
- // the user code changes between the call to UponLeavingGTest()
- // and any calls to CurrentStackTrace() from within the user code.
- void* caller_frame_;
-
GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
};
@@ -968,32 +959,6 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
// platform.
GTEST_API_ std::string GetLastErrnoDescription();
-# if GTEST_OS_WINDOWS
-// Provides leak-safe Windows kernel handle ownership.
-class AutoHandle {
- public:
- AutoHandle() : handle_(INVALID_HANDLE_VALUE) {}
- explicit AutoHandle(HANDLE handle) : handle_(handle) {}
-
- ~AutoHandle() { Reset(); }
-
- HANDLE Get() const { return handle_; }
- void Reset() { Reset(INVALID_HANDLE_VALUE); }
- void Reset(HANDLE handle) {
- if (handle != handle_) {
- if (handle_ != INVALID_HANDLE_VALUE)
- ::CloseHandle(handle_);
- handle_ = handle;
- }
- }
-
- private:
- HANDLE handle_;
-
- GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
-};
-# endif // GTEST_OS_WINDOWS
-
// Attempts to parse a string into a positive integer pointed to by the
// number parameter. Returns true if that is possible.
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
@@ -1067,7 +1032,7 @@ class TestResultAccessor {
#if GTEST_CAN_STREAM_RESULTS_
// Streams test results to the given port on the given host machine.
-class StreamingListener : public EmptyTestEventListener {
+class GTEST_API_ StreamingListener : public EmptyTestEventListener {
public:
// Abstract base class for writing strings to a socket.
class AbstractSocketWriter {
diff --git a/extern/gtest/src/gtest-port.cc b/extern/gtest/src/gtest-port.cc
index 0c4df5f29a7..e5bf3dd2be4 100644
--- a/extern/gtest/src/gtest-port.cc
+++ b/extern/gtest/src/gtest-port.cc
@@ -35,15 +35,16 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <fstream>
-#if GTEST_OS_WINDOWS_MOBILE
-# include <windows.h> // For TerminateProcess()
-#elif GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS
+# include <windows.h>
# include <io.h>
# include <sys/stat.h>
+# include <map> // Used in ThreadLocal.
#else
# include <unistd.h>
-#endif // GTEST_OS_WINDOWS_MOBILE
+#endif // GTEST_OS_WINDOWS
#if GTEST_OS_MAC
# include <mach/mach_init.h>
@@ -53,9 +54,15 @@
#if GTEST_OS_QNX
# include <devctl.h>
+# include <fcntl.h>
# include <sys/procfs.h>
#endif // GTEST_OS_QNX
+#if GTEST_OS_AIX
+# include <procinfo.h>
+# include <sys/types.h>
+#endif // GTEST_OS_AIX
+
#include "gtest/gtest-spi.h"
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-internal.h"
@@ -63,9 +70,9 @@
// Indicates that this translation unit is part of Google Test's
// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick is to
-// prevent a user from accidentally including gtest-internal-inl.h in
-// his code.
+// included, or there will be a compiler error. This trick exists to
+// prevent the accidental inclusion of gtest-internal-inl.h in the
+// user's code.
#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
#undef GTEST_IMPLEMENTATION_
@@ -82,10 +89,31 @@ const int kStdOutFileno = STDOUT_FILENO;
const int kStdErrFileno = STDERR_FILENO;
#endif // _MSC_VER
-#if GTEST_OS_MAC
+#if GTEST_OS_LINUX
+
+namespace {
+template <typename T>
+T ReadProcFileField(const string& filename, int field) {
+ std::string dummy;
+ std::ifstream file(filename.c_str());
+ while (field-- > 0) {
+ file >> dummy;
+ }
+ T output = 0;
+ file >> output;
+ return output;
+}
+} // namespace
+
+// Returns the number of active threads, or 0 when there is an error.
+size_t GetThreadCount() {
+ const string filename =
+ (Message() << "/proc/" << getpid() << "/stat").GetString();
+ return ReadProcFileField<int>(filename, 19);
+}
+
+#elif GTEST_OS_MAC
-// Returns the number of threads running in the process, or 0 to indicate that
-// we cannot detect it.
size_t GetThreadCount() {
const task_t task = mach_task_self();
mach_msg_type_number_t thread_count;
@@ -123,6 +151,19 @@ size_t GetThreadCount() {
}
}
+#elif GTEST_OS_AIX
+
+size_t GetThreadCount() {
+ struct procentry64 entry;
+ pid_t pid = getpid();
+ int status = getprocs64(&entry, sizeof(entry), NULL, 0, &pid, 1);
+ if (status == 1) {
+ return entry.pi_thcount;
+ } else {
+ return 0;
+ }
+}
+
#else
size_t GetThreadCount() {
@@ -131,7 +172,390 @@ size_t GetThreadCount() {
return 0;
}
-#endif // GTEST_OS_MAC
+#endif // GTEST_OS_LINUX
+
+#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS
+
+void SleepMilliseconds(int n) {
+ ::Sleep(n);
+}
+
+AutoHandle::AutoHandle()
+ : handle_(INVALID_HANDLE_VALUE) {}
+
+AutoHandle::AutoHandle(Handle handle)
+ : handle_(handle) {}
+
+AutoHandle::~AutoHandle() {
+ Reset();
+}
+
+AutoHandle::Handle AutoHandle::Get() const {
+ return handle_;
+}
+
+void AutoHandle::Reset() {
+ Reset(INVALID_HANDLE_VALUE);
+}
+
+void AutoHandle::Reset(HANDLE handle) {
+ // Resetting with the same handle we already own is invalid.
+ if (handle_ != handle) {
+ if (IsCloseable()) {
+ ::CloseHandle(handle_);
+ }
+ handle_ = handle;
+ } else {
+ GTEST_CHECK_(!IsCloseable())
+ << "Resetting a valid handle to itself is likely a programmer error "
+ "and thus not allowed.";
+ }
+}
+
+bool AutoHandle::IsCloseable() const {
+ // Different Windows APIs may use either of these values to represent an
+ // invalid handle.
+ return handle_ != NULL && handle_ != INVALID_HANDLE_VALUE;
+}
+
+Notification::Notification()
+ : event_(::CreateEvent(NULL, // Default security attributes.
+ TRUE, // Do not reset automatically.
+ FALSE, // Initially unset.
+ NULL)) { // Anonymous event.
+ GTEST_CHECK_(event_.Get() != NULL);
+}
+
+void Notification::Notify() {
+ GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE);
+}
+
+void Notification::WaitForNotification() {
+ GTEST_CHECK_(
+ ::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0);
+}
+
+Mutex::Mutex()
+ : owner_thread_id_(0),
+ type_(kDynamic),
+ critical_section_init_phase_(0),
+ critical_section_(new CRITICAL_SECTION) {
+ ::InitializeCriticalSection(critical_section_);
+}
+
+Mutex::~Mutex() {
+ // Static mutexes are leaked intentionally. It is not thread-safe to try
+ // to clean them up.
+ // TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires
+ // nothing to clean it up but is available only on Vista and later.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx
+ if (type_ == kDynamic) {
+ ::DeleteCriticalSection(critical_section_);
+ delete critical_section_;
+ critical_section_ = NULL;
+ }
+}
+
+void Mutex::Lock() {
+ ThreadSafeLazyInit();
+ ::EnterCriticalSection(critical_section_);
+ owner_thread_id_ = ::GetCurrentThreadId();
+}
+
+void Mutex::Unlock() {
+ ThreadSafeLazyInit();
+ // We don't protect writing to owner_thread_id_ here, as it's the
+ // caller's responsibility to ensure that the current thread holds the
+ // mutex when this is called.
+ owner_thread_id_ = 0;
+ ::LeaveCriticalSection(critical_section_);
+}
+
+// Does nothing if the current thread holds the mutex. Otherwise, crashes
+// with high probability.
+void Mutex::AssertHeld() {
+ ThreadSafeLazyInit();
+ GTEST_CHECK_(owner_thread_id_ == ::GetCurrentThreadId())
+ << "The current thread is not holding the mutex @" << this;
+}
+
+// Initializes owner_thread_id_ and critical_section_ in static mutexes.
+void Mutex::ThreadSafeLazyInit() {
+ // Dynamic mutexes are initialized in the constructor.
+ if (type_ == kStatic) {
+ switch (
+ ::InterlockedCompareExchange(&critical_section_init_phase_, 1L, 0L)) {
+ case 0:
+ // If critical_section_init_phase_ was 0 before the exchange, we
+ // are the first to test it and need to perform the initialization.
+ owner_thread_id_ = 0;
+ critical_section_ = new CRITICAL_SECTION;
+ ::InitializeCriticalSection(critical_section_);
+ // Updates the critical_section_init_phase_ to 2 to signal
+ // initialization complete.
+ GTEST_CHECK_(::InterlockedCompareExchange(
+ &critical_section_init_phase_, 2L, 1L) ==
+ 1L);
+ break;
+ case 1:
+ // Somebody else is already initializing the mutex; spin until they
+ // are done.
+ while (::InterlockedCompareExchange(&critical_section_init_phase_,
+ 2L,
+ 2L) != 2L) {
+ // Possibly yields the rest of the thread's time slice to other
+ // threads.
+ ::Sleep(0);
+ }
+ break;
+
+ case 2:
+ break; // The mutex is already initialized and ready for use.
+
+ default:
+ GTEST_CHECK_(false)
+ << "Unexpected value of critical_section_init_phase_ "
+ << "while initializing a static mutex.";
+ }
+ }
+}
+
+namespace {
+
+class ThreadWithParamSupport : public ThreadWithParamBase {
+ public:
+ static HANDLE CreateThread(Runnable* runnable,
+ Notification* thread_can_start) {
+ ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start);
+ DWORD thread_id;
+ // TODO(yukawa): Consider to use _beginthreadex instead.
+ HANDLE thread_handle = ::CreateThread(
+ NULL, // Default security.
+ 0, // Default stack size.
+ &ThreadWithParamSupport::ThreadMain,
+ param, // Parameter to ThreadMainStatic
+ 0x0, // Default creation flags.
+ &thread_id); // Need a valid pointer for the call to work under Win98.
+ GTEST_CHECK_(thread_handle != NULL) << "CreateThread failed with error "
+ << ::GetLastError() << ".";
+ if (thread_handle == NULL) {
+ delete param;
+ }
+ return thread_handle;
+ }
+
+ private:
+ struct ThreadMainParam {
+ ThreadMainParam(Runnable* runnable, Notification* thread_can_start)
+ : runnable_(runnable),
+ thread_can_start_(thread_can_start) {
+ }
+ scoped_ptr<Runnable> runnable_;
+ // Does not own.
+ Notification* thread_can_start_;
+ };
+
+ static DWORD WINAPI ThreadMain(void* ptr) {
+ // Transfers ownership.
+ scoped_ptr<ThreadMainParam> param(static_cast<ThreadMainParam*>(ptr));
+ if (param->thread_can_start_ != NULL)
+ param->thread_can_start_->WaitForNotification();
+ param->runnable_->Run();
+ return 0;
+ }
+
+ // Prohibit instantiation.
+ ThreadWithParamSupport();
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParamSupport);
+};
+
+} // namespace
+
+ThreadWithParamBase::ThreadWithParamBase(Runnable *runnable,
+ Notification* thread_can_start)
+ : thread_(ThreadWithParamSupport::CreateThread(runnable,
+ thread_can_start)) {
+}
+
+ThreadWithParamBase::~ThreadWithParamBase() {
+ Join();
+}
+
+void ThreadWithParamBase::Join() {
+ GTEST_CHECK_(::WaitForSingleObject(thread_.Get(), INFINITE) == WAIT_OBJECT_0)
+ << "Failed to join the thread with error " << ::GetLastError() << ".";
+}
+
+// Maps a thread to a set of ThreadIdToThreadLocals that have values
+// instantiated on that thread and notifies them when the thread exits. A
+// ThreadLocal instance is expected to persist until all threads it has
+// values on have terminated.
+class ThreadLocalRegistryImpl {
+ public:
+ // Registers thread_local_instance as having value on the current thread.
+ // Returns a value that can be used to identify the thread from other threads.
+ static ThreadLocalValueHolderBase* GetValueOnCurrentThread(
+ const ThreadLocalBase* thread_local_instance) {
+ DWORD current_thread = ::GetCurrentThreadId();
+ MutexLock lock(&mutex_);
+ ThreadIdToThreadLocals* const thread_to_thread_locals =
+ GetThreadLocalsMapLocked();
+ ThreadIdToThreadLocals::iterator thread_local_pos =
+ thread_to_thread_locals->find(current_thread);
+ if (thread_local_pos == thread_to_thread_locals->end()) {
+ thread_local_pos = thread_to_thread_locals->insert(
+ std::make_pair(current_thread, ThreadLocalValues())).first;
+ StartWatcherThreadFor(current_thread);
+ }
+ ThreadLocalValues& thread_local_values = thread_local_pos->second;
+ ThreadLocalValues::iterator value_pos =
+ thread_local_values.find(thread_local_instance);
+ if (value_pos == thread_local_values.end()) {
+ value_pos =
+ thread_local_values
+ .insert(std::make_pair(
+ thread_local_instance,
+ linked_ptr<ThreadLocalValueHolderBase>(
+ thread_local_instance->NewValueForCurrentThread())))
+ .first;
+ }
+ return value_pos->second.get();
+ }
+
+ static void OnThreadLocalDestroyed(
+ const ThreadLocalBase* thread_local_instance) {
+ std::vector<linked_ptr<ThreadLocalValueHolderBase> > value_holders;
+ // Clean up the ThreadLocalValues data structure while holding the lock, but
+ // defer the destruction of the ThreadLocalValueHolderBases.
+ {
+ MutexLock lock(&mutex_);
+ ThreadIdToThreadLocals* const thread_to_thread_locals =
+ GetThreadLocalsMapLocked();
+ for (ThreadIdToThreadLocals::iterator it =
+ thread_to_thread_locals->begin();
+ it != thread_to_thread_locals->end();
+ ++it) {
+ ThreadLocalValues& thread_local_values = it->second;
+ ThreadLocalValues::iterator value_pos =
+ thread_local_values.find(thread_local_instance);
+ if (value_pos != thread_local_values.end()) {
+ value_holders.push_back(value_pos->second);
+ thread_local_values.erase(value_pos);
+ // This 'if' can only be successful at most once, so theoretically we
+ // could break out of the loop here, but we don't bother doing so.
+ }
+ }
+ }
+ // Outside the lock, let the destructor for 'value_holders' deallocate the
+ // ThreadLocalValueHolderBases.
+ }
+
+ static void OnThreadExit(DWORD thread_id) {
+ GTEST_CHECK_(thread_id != 0) << ::GetLastError();
+ std::vector<linked_ptr<ThreadLocalValueHolderBase> > value_holders;
+ // Clean up the ThreadIdToThreadLocals data structure while holding the
+ // lock, but defer the destruction of the ThreadLocalValueHolderBases.
+ {
+ MutexLock lock(&mutex_);
+ ThreadIdToThreadLocals* const thread_to_thread_locals =
+ GetThreadLocalsMapLocked();
+ ThreadIdToThreadLocals::iterator thread_local_pos =
+ thread_to_thread_locals->find(thread_id);
+ if (thread_local_pos != thread_to_thread_locals->end()) {
+ ThreadLocalValues& thread_local_values = thread_local_pos->second;
+ for (ThreadLocalValues::iterator value_pos =
+ thread_local_values.begin();
+ value_pos != thread_local_values.end();
+ ++value_pos) {
+ value_holders.push_back(value_pos->second);
+ }
+ thread_to_thread_locals->erase(thread_local_pos);
+ }
+ }
+ // Outside the lock, let the destructor for 'value_holders' deallocate the
+ // ThreadLocalValueHolderBases.
+ }
+
+ private:
+ // In a particular thread, maps a ThreadLocal object to its value.
+ typedef std::map<const ThreadLocalBase*,
+ linked_ptr<ThreadLocalValueHolderBase> > ThreadLocalValues;
+ // Stores all ThreadIdToThreadLocals having values in a thread, indexed by
+ // thread's ID.
+ typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals;
+
+ // Holds the thread id and thread handle that we pass from
+ // StartWatcherThreadFor to WatcherThreadFunc.
+ typedef std::pair<DWORD, HANDLE> ThreadIdAndHandle;
+
+ static void StartWatcherThreadFor(DWORD thread_id) {
+ // The returned handle will be kept in thread_map and closed by
+ // watcher_thread in WatcherThreadFunc.
+ HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION,
+ FALSE,
+ thread_id);
+ GTEST_CHECK_(thread != NULL);
+ // We need to to pass a valid thread ID pointer into CreateThread for it
+ // to work correctly under Win98.
+ DWORD watcher_thread_id;
+ HANDLE watcher_thread = ::CreateThread(
+ NULL, // Default security.
+ 0, // Default stack size
+ &ThreadLocalRegistryImpl::WatcherThreadFunc,
+ reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)),
+ CREATE_SUSPENDED,
+ &watcher_thread_id);
+ GTEST_CHECK_(watcher_thread != NULL);
+ // Give the watcher thread the same priority as ours to avoid being
+ // blocked by it.
+ ::SetThreadPriority(watcher_thread,
+ ::GetThreadPriority(::GetCurrentThread()));
+ ::ResumeThread(watcher_thread);
+ ::CloseHandle(watcher_thread);
+ }
+
+ // Monitors exit from a given thread and notifies those
+ // ThreadIdToThreadLocals about thread termination.
+ static DWORD WINAPI WatcherThreadFunc(LPVOID param) {
+ const ThreadIdAndHandle* tah =
+ reinterpret_cast<const ThreadIdAndHandle*>(param);
+ GTEST_CHECK_(
+ ::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0);
+ OnThreadExit(tah->first);
+ ::CloseHandle(tah->second);
+ delete tah;
+ return 0;
+ }
+
+ // Returns map of thread local instances.
+ static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() {
+ mutex_.AssertHeld();
+ static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals;
+ return map;
+ }
+
+ // Protects access to GetThreadLocalsMapLocked() and its return value.
+ static Mutex mutex_;
+ // Protects access to GetThreadMapLocked() and its return value.
+ static Mutex thread_map_mutex_;
+};
+
+Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex);
+Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex);
+
+ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread(
+ const ThreadLocalBase* thread_local_instance) {
+ return ThreadLocalRegistryImpl::GetValueOnCurrentThread(
+ thread_local_instance);
+}
+
+void ThreadLocalRegistry::OnThreadLocalDestroyed(
+ const ThreadLocalBase* thread_local_instance) {
+ ThreadLocalRegistryImpl::OnThreadLocalDestroyed(thread_local_instance);
+}
+
+#endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS
#if GTEST_USES_POSIX_RE
@@ -481,7 +905,6 @@ GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(
return file_name + ":" + StreamableToString(line);
}
-
GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line)
: severity_(severity) {
const char* const marker =
@@ -502,10 +925,7 @@ GTestLog::~GTestLog() {
}
// Disable Microsoft deprecation warnings for POSIX functions called from
// this class (creat, dup, dup2, and close)
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable: 4996)
-#endif // _MSC_VER
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
#if GTEST_HAS_STREAM_REDIRECTION
@@ -581,12 +1001,6 @@ class CapturedStream {
}
private:
- // Reads the entire content of a file as an std::string.
- static std::string ReadEntireFile(FILE* file);
-
- // Returns the size (in bytes) of a file.
- static size_t GetFileSize(FILE* file);
-
const int fd_; // A stream to capture.
int uncaptured_fd_;
// Name of the temporary file holding the stderr output.
@@ -595,38 +1009,7 @@ class CapturedStream {
GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
};
-// Returns the size (in bytes) of a file.
-size_t CapturedStream::GetFileSize(FILE* file) {
- fseek(file, 0, SEEK_END);
- return static_cast<size_t>(ftell(file));
-}
-
-// Reads the entire content of a file as a string.
-std::string CapturedStream::ReadEntireFile(FILE* file) {
- const size_t file_size = GetFileSize(file);
- char* const buffer = new char[file_size];
-
- size_t bytes_last_read = 0; // # of bytes read in the last fread()
- size_t bytes_read = 0; // # of bytes read so far
-
- fseek(file, 0, SEEK_SET);
-
- // Keeps reading the file until we cannot read further or the
- // pre-determined file size is reached.
- do {
- bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
- bytes_read += bytes_last_read;
- } while (bytes_last_read > 0 && bytes_read < file_size);
-
- const std::string content(buffer, bytes_read);
- delete[] buffer;
-
- return content;
-}
-
-# ifdef _MSC_VER
-# pragma warning(pop)
-# endif // _MSC_VER
+GTEST_DISABLE_MSC_WARNINGS_POP_()
static CapturedStream* g_captured_stderr = NULL;
static CapturedStream* g_captured_stdout = NULL;
@@ -672,10 +1055,52 @@ std::string GetCapturedStderr() {
#endif // GTEST_HAS_STREAM_REDIRECTION
-#if GTEST_HAS_DEATH_TEST
+std::string TempDir() {
+#if GTEST_OS_WINDOWS_MOBILE
+ return "\\temp\\";
+#elif GTEST_OS_WINDOWS
+ const char* temp_dir = posix::GetEnv("TEMP");
+ if (temp_dir == NULL || temp_dir[0] == '\0')
+ return "\\temp\\";
+ else if (temp_dir[strlen(temp_dir) - 1] == '\\')
+ return temp_dir;
+ else
+ return std::string(temp_dir) + "\\";
+#elif GTEST_OS_LINUX_ANDROID
+ return "/sdcard/";
+#else
+ return "/tmp/";
+#endif // GTEST_OS_WINDOWS_MOBILE
+}
+
+size_t GetFileSize(FILE* file) {
+ fseek(file, 0, SEEK_END);
+ return static_cast<size_t>(ftell(file));
+}
+
+std::string ReadEntireFile(FILE* file) {
+ const size_t file_size = GetFileSize(file);
+ char* const buffer = new char[file_size];
+
+ size_t bytes_last_read = 0; // # of bytes read in the last fread()
+ size_t bytes_read = 0; // # of bytes read so far
+
+ fseek(file, 0, SEEK_SET);
+
+ // Keeps reading the file until we cannot read further or the
+ // pre-determined file size is reached.
+ do {
+ bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
+ bytes_read += bytes_last_read;
+ } while (bytes_last_read > 0 && bytes_read < file_size);
+
+ const std::string content(buffer, bytes_read);
+ delete[] buffer;
+
+ return content;
+}
-// A copy of all command line arguments. Set by InitGoogleTest().
-::std::vector<testing::internal::string> g_argvs;
+#if GTEST_HAS_DEATH_TEST
static const ::std::vector<testing::internal::string>* g_injected_test_argvs =
NULL; // Owned.
@@ -690,7 +1115,7 @@ const ::std::vector<testing::internal::string>& GetInjectableArgvs() {
if (g_injected_test_argvs != NULL) {
return *g_injected_test_argvs;
}
- return g_argvs;
+ return GetArgvs();
}
#endif // GTEST_HAS_DEATH_TEST
@@ -764,6 +1189,9 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
//
// The value is considered true iff it's not "0".
bool BoolFromGTestEnv(const char* flag, bool default_value) {
+#if defined(GTEST_GET_BOOL_FROM_ENV_)
+ return GTEST_GET_BOOL_FROM_ENV_(flag, default_value);
+#endif // defined(GTEST_GET_BOOL_FROM_ENV_)
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = posix::GetEnv(env_var.c_str());
return string_value == NULL ?
@@ -774,6 +1202,9 @@ bool BoolFromGTestEnv(const char* flag, bool default_value) {
// variable corresponding to the given flag; if it isn't set or
// doesn't represent a valid 32-bit integer, returns default_value.
Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
+#if defined(GTEST_GET_INT32_FROM_ENV_)
+ return GTEST_GET_INT32_FROM_ENV_(flag, default_value);
+#endif // defined(GTEST_GET_INT32_FROM_ENV_)
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = posix::GetEnv(env_var.c_str());
if (string_value == NULL) {
@@ -795,10 +1226,33 @@ Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
// Reads and returns the string environment variable corresponding to
// the given flag; if it's not set, returns default_value.
-const char* StringFromGTestEnv(const char* flag, const char* default_value) {
+std::string StringFromGTestEnv(const char* flag, const char* default_value) {
+#if defined(GTEST_GET_STRING_FROM_ENV_)
+ return GTEST_GET_STRING_FROM_ENV_(flag, default_value);
+#endif // defined(GTEST_GET_STRING_FROM_ENV_)
const std::string env_var = FlagToEnvVar(flag);
- const char* const value = posix::GetEnv(env_var.c_str());
- return value == NULL ? default_value : value;
+ const char* value = posix::GetEnv(env_var.c_str());
+ if (value != NULL) {
+ return value;
+ }
+
+ // As a special case for the 'output' flag, if GTEST_OUTPUT is not
+ // set, we look for XML_OUTPUT_FILE, which is set by the Bazel build
+ // system. The value of XML_OUTPUT_FILE is a filename without the
+ // "xml:" prefix of GTEST_OUTPUT.
+ //
+ // The net priority order after flag processing is thus:
+ // --gtest_output command line flag
+ // GTEST_OUTPUT environment variable
+ // XML_OUTPUT_FILE environment variable
+ // 'default_value'
+ if (strcmp(flag, "output") == 0) {
+ value = posix::GetEnv("XML_OUTPUT_FILE");
+ if (value != NULL) {
+ return std::string("xml:") + value;
+ }
+ }
+ return default_value;
}
} // namespace internal
diff --git a/extern/gtest/src/gtest-printers.cc b/extern/gtest/src/gtest-printers.cc
index 75fa4081009..a2df412f8a2 100644
--- a/extern/gtest/src/gtest-printers.cc
+++ b/extern/gtest/src/gtest-printers.cc
@@ -45,6 +45,7 @@
#include "gtest/gtest-printers.h"
#include <ctype.h>
#include <stdio.h>
+#include <cwchar>
#include <ostream> // NOLINT
#include <string>
#include "gtest/internal/gtest-port.h"
@@ -56,6 +57,9 @@ namespace {
using ::std::ostream;
// Prints a segment of bytes in the given object.
+GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
size_t count, ostream* os) {
char text[5] = "";
@@ -252,6 +256,9 @@ void PrintTo(wchar_t wc, ostream* os) {
// The array starts at begin, the length is len, it may include '\0' characters
// and may not be NUL-terminated.
template <typename CharType>
+GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
static void PrintCharsAsStringTo(
const CharType* begin, size_t len, ostream* os) {
const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
@@ -273,6 +280,9 @@ static void PrintCharsAsStringTo(
// Prints a (const) char/wchar_t array of 'len' elements, starting at address
// 'begin'. CharType must be either char or wchar_t.
template <typename CharType>
+GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
static void UniversalPrintCharArray(
const CharType* begin, size_t len, ostream* os) {
// The code
@@ -329,7 +339,7 @@ void PrintTo(const wchar_t* s, ostream* os) {
*os << "NULL";
} else {
*os << ImplicitCast_<const void*>(s) << " pointing to ";
- PrintCharsAsStringTo(s, wcslen(s), os);
+ PrintCharsAsStringTo(s, std::wcslen(s), os);
}
}
#endif // wchar_t is native
diff --git a/extern/gtest/src/gtest-test-part.cc b/extern/gtest/src/gtest-test-part.cc
index c60eef3ab35..fb0e35425e1 100644
--- a/extern/gtest/src/gtest-test-part.cc
+++ b/extern/gtest/src/gtest-test-part.cc
@@ -35,9 +35,9 @@
// Indicates that this translation unit is part of Google Test's
// implementation. It must come before gtest-internal-inl.h is
-// included, or there will be a compiler error. This trick is to
-// prevent a user from accidentally including gtest-internal-inl.h in
-// his code.
+// included, or there will be a compiler error. This trick exists to
+// prevent the accidental inclusion of gtest-internal-inl.h in the
+// user's code.
#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
#undef GTEST_IMPLEMENTATION_
diff --git a/extern/gtest/src/gtest-typed-test.cc b/extern/gtest/src/gtest-typed-test.cc
index f0079f407c5..df1eef4754e 100644
--- a/extern/gtest/src/gtest-typed-test.cc
+++ b/extern/gtest/src/gtest-typed-test.cc
@@ -45,33 +45,41 @@ static const char* SkipSpaces(const char* str) {
return str;
}
+static std::vector<std::string> SplitIntoTestNames(const char* src) {
+ std::vector<std::string> name_vec;
+ src = SkipSpaces(src);
+ for (; src != NULL; src = SkipComma(src)) {
+ name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src)));
+ }
+ return name_vec;
+}
+
// Verifies that registered_tests match the test names in
-// defined_test_names_; returns registered_tests if successful, or
+// registered_tests_; returns registered_tests if successful, or
// aborts the program otherwise.
const char* TypedTestCasePState::VerifyRegisteredTestNames(
const char* file, int line, const char* registered_tests) {
- typedef ::std::set<const char*>::const_iterator DefinedTestIter;
+ typedef RegisteredTestsMap::const_iterator RegisteredTestIter;
registered_ = true;
- // Skip initial whitespace in registered_tests since some
- // preprocessors prefix stringizied literals with whitespace.
- registered_tests = SkipSpaces(registered_tests);
+ std::vector<std::string> name_vec = SplitIntoTestNames(registered_tests);
Message errors;
- ::std::set<std::string> tests;
- for (const char* names = registered_tests; names != NULL;
- names = SkipComma(names)) {
- const std::string name = GetPrefixUntilComma(names);
+
+ std::set<std::string> tests;
+ for (std::vector<std::string>::const_iterator name_it = name_vec.begin();
+ name_it != name_vec.end(); ++name_it) {
+ const std::string& name = *name_it;
if (tests.count(name) != 0) {
errors << "Test " << name << " is listed more than once.\n";
continue;
}
bool found = false;
- for (DefinedTestIter it = defined_test_names_.begin();
- it != defined_test_names_.end();
+ for (RegisteredTestIter it = registered_tests_.begin();
+ it != registered_tests_.end();
++it) {
- if (name == *it) {
+ if (name == it->first) {
found = true;
break;
}
@@ -85,11 +93,11 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
}
}
- for (DefinedTestIter it = defined_test_names_.begin();
- it != defined_test_names_.end();
+ for (RegisteredTestIter it = registered_tests_.begin();
+ it != registered_tests_.end();
++it) {
- if (tests.count(*it) == 0) {
- errors << "You forgot to list test " << *it << ".\n";
+ if (tests.count(it->first) == 0) {
+ errors << "You forgot to list test " << it->first << ".\n";
}
}
diff --git a/extern/gtest/src/gtest.cc b/extern/gtest/src/gtest.cc
index 6de53dd0198..d882ab2e36a 100644
--- a/extern/gtest/src/gtest.cc
+++ b/extern/gtest/src/gtest.cc
@@ -32,6 +32,7 @@
// The Google C++ Testing Framework (Google Test)
#include "gtest/gtest.h"
+#include "gtest/internal/custom/gtest.h"
#include "gtest/gtest-spi.h"
#include <ctype.h>
@@ -46,6 +47,8 @@
#include <algorithm>
#include <iomanip>
#include <limits>
+#include <list>
+#include <map>
#include <ostream> // NOLINT
#include <sstream>
#include <vector>
@@ -80,6 +83,7 @@
#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE.
# include <windows.h> // NOLINT
+# undef min
#elif GTEST_OS_WINDOWS // We are on Windows proper.
@@ -102,6 +106,7 @@
// cpplint thinks that the header is already included, so we want to
// silence it.
# include <windows.h> // NOLINT
+# undef min
#else
@@ -124,6 +129,8 @@
#if GTEST_CAN_STREAM_RESULTS_
# include <arpa/inet.h> // NOLINT
# include <netdb.h> // NOLINT
+# include <sys/socket.h> // NOLINT
+# include <sys/types.h> // NOLINT
#endif
// Indicates that this translation unit is part of Google Test's
@@ -183,6 +190,12 @@ bool g_help_flag = false;
} // namespace internal
static const char* GetDefaultFilter() {
+#ifdef GTEST_TEST_FILTER_ENV_VAR_
+ const char* const testbridge_test_only = getenv(GTEST_TEST_FILTER_ENV_VAR_);
+ if (testbridge_test_only != NULL) {
+ return testbridge_test_only;
+ }
+#endif // GTEST_TEST_FILTER_ENV_VAR_
return kUniversalFilter;
}
@@ -283,6 +296,13 @@ GTEST_DEFINE_bool_(
"if exceptions are enabled or exit the program with a non-zero code "
"otherwise.");
+#if GTEST_USE_OWN_FLAGFILE_FLAG_
+GTEST_DEFINE_string_(
+ flagfile,
+ internal::StringFromGTestEnv("flagfile", ""),
+ "This flag specifies the flagfile to read command-line flags from.");
+#endif // GTEST_USE_OWN_FLAGFILE_FLAG_
+
namespace internal {
// Generates a random number from [0, range), using a Linear
@@ -307,13 +327,7 @@ UInt32 Random::Generate(UInt32 range) {
// GTestIsInitialized() returns true iff the user has initialized
// Google Test. Useful for catching the user mistake of not initializing
// Google Test before calling RUN_ALL_TESTS().
-//
-// A user must call testing::InitGoogleTest() to initialize Google
-// Test. g_init_gtest_count is set to the number of times
-// InitGoogleTest() has been called. We don't protect this variable
-// under a mutex as it is only accessed in the main thread.
-GTEST_API_ int g_init_gtest_count = 0;
-static bool GTestIsInitialized() { return g_init_gtest_count != 0; }
+static bool GTestIsInitialized() { return GetArgvs().size() > 0; }
// Iterates over a vector of TestCases, keeping a running sum of the
// results of calling a given int-returning method on each.
@@ -369,8 +383,16 @@ void AssertHelper::operator=(const Message& message) const {
// Mutex for linked pointers.
GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
-// Application pathname gotten in InitGoogleTest.
-std::string g_executable_path;
+// A copy of all command line arguments. Set by InitGoogleTest().
+::std::vector<testing::internal::string> g_argvs;
+
+const ::std::vector<testing::internal::string>& GetArgvs() {
+#if defined(GTEST_CUSTOM_GET_ARGVS_)
+ return GTEST_CUSTOM_GET_ARGVS_();
+#else // defined(GTEST_CUSTOM_GET_ARGVS_)
+ return g_argvs;
+#endif // defined(GTEST_CUSTOM_GET_ARGVS_)
+}
// Returns the current application's name, removing directory path if that
// is present.
@@ -378,9 +400,9 @@ FilePath GetCurrentExecutableName() {
FilePath result;
#if GTEST_OS_WINDOWS
- result.Set(FilePath(g_executable_path).RemoveExtension("exe"));
+ result.Set(FilePath(GetArgvs()[0]).RemoveExtension("exe"));
#else
- result.Set(FilePath(g_executable_path));
+ result.Set(FilePath(GetArgvs()[0]));
#endif // GTEST_OS_WINDOWS
return result.RemoveDirectoryName();
@@ -772,8 +794,12 @@ int UnitTestImpl::test_to_run_count() const {
// CurrentOsStackTraceExceptTop(1), Foo() will be included in the
// trace but Bar() and CurrentOsStackTraceExceptTop() won't.
std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) {
- (void)skip_count;
- return "";
+ return os_stack_trace_getter()->CurrentStackTrace(
+ static_cast<int>(GTEST_FLAG(stack_trace_depth)),
+ skip_count + 1
+ // Skips the user-specified number of frames plus this function
+ // itself.
+ ); // NOLINT
}
// Returns the current time in milliseconds.
@@ -802,21 +828,13 @@ TimeInMillis GetTimeInMillis() {
#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_
__timeb64 now;
-# ifdef _MSC_VER
-
// MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
// (deprecated function) there.
// TODO(kenton@google.com): Use GetTickCount()? Or use
// SystemTimeToFileTime()
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4996) // Temporarily disables warning 4996.
- _ftime64(&now);
-# pragma warning(pop) // Restores the warning state.
-# else
-
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
_ftime64(&now);
-
-# endif // _MSC_VER
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
#elif GTEST_HAS_GETTIMEOFDAY_
@@ -901,6 +919,23 @@ static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length,
#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING
+void SplitString(const ::std::string& str, char delimiter,
+ ::std::vector< ::std::string>* dest) {
+ ::std::vector< ::std::string> parsed;
+ ::std::string::size_type pos = 0;
+ while (::testing::internal::AlwaysTrue()) {
+ const ::std::string::size_type colon = str.find(delimiter, pos);
+ if (colon == ::std::string::npos) {
+ parsed.push_back(str.substr(pos));
+ break;
+ } else {
+ parsed.push_back(str.substr(pos, colon - pos));
+ pos = colon + 1;
+ }
+ }
+ dest->swap(parsed);
+}
+
} // namespace internal
// Constructs an empty Message.
@@ -956,6 +991,13 @@ AssertionResult::AssertionResult(const AssertionResult& other)
static_cast< ::std::string*>(NULL)) {
}
+// Swaps two AssertionResults.
+void AssertionResult::swap(AssertionResult& other) {
+ using std::swap;
+ swap(success_, other.success_);
+ swap(message_, other.message_);
+}
+
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
AssertionResult AssertionResult::operator!() const {
AssertionResult negation(!success_);
@@ -982,6 +1024,276 @@ AssertionResult AssertionFailure(const Message& message) {
namespace internal {
+namespace edit_distance {
+std::vector<EditType> CalculateOptimalEdits(const std::vector<size_t>& left,
+ const std::vector<size_t>& right) {
+ std::vector<std::vector<double> > costs(
+ left.size() + 1, std::vector<double>(right.size() + 1));
+ std::vector<std::vector<EditType> > best_move(
+ left.size() + 1, std::vector<EditType>(right.size() + 1));
+
+ // Populate for empty right.
+ for (size_t l_i = 0; l_i < costs.size(); ++l_i) {
+ costs[l_i][0] = static_cast<double>(l_i);
+ best_move[l_i][0] = kRemove;
+ }
+ // Populate for empty left.
+ for (size_t r_i = 1; r_i < costs[0].size(); ++r_i) {
+ costs[0][r_i] = static_cast<double>(r_i);
+ best_move[0][r_i] = kAdd;
+ }
+
+ for (size_t l_i = 0; l_i < left.size(); ++l_i) {
+ for (size_t r_i = 0; r_i < right.size(); ++r_i) {
+ if (left[l_i] == right[r_i]) {
+ // Found a match. Consume it.
+ costs[l_i + 1][r_i + 1] = costs[l_i][r_i];
+ best_move[l_i + 1][r_i + 1] = kMatch;
+ continue;
+ }
+
+ const double add = costs[l_i + 1][r_i];
+ const double remove = costs[l_i][r_i + 1];
+ const double replace = costs[l_i][r_i];
+ if (add < remove && add < replace) {
+ costs[l_i + 1][r_i + 1] = add + 1;
+ best_move[l_i + 1][r_i + 1] = kAdd;
+ } else if (remove < add && remove < replace) {
+ costs[l_i + 1][r_i + 1] = remove + 1;
+ best_move[l_i + 1][r_i + 1] = kRemove;
+ } else {
+ // We make replace a little more expensive than add/remove to lower
+ // their priority.
+ costs[l_i + 1][r_i + 1] = replace + 1.00001;
+ best_move[l_i + 1][r_i + 1] = kReplace;
+ }
+ }
+ }
+
+ // Reconstruct the best path. We do it in reverse order.
+ std::vector<EditType> best_path;
+ for (size_t l_i = left.size(), r_i = right.size(); l_i > 0 || r_i > 0;) {
+ EditType move = best_move[l_i][r_i];
+ best_path.push_back(move);
+ l_i -= move != kAdd;
+ r_i -= move != kRemove;
+ }
+ std::reverse(best_path.begin(), best_path.end());
+ return best_path;
+}
+
+namespace {
+
+// Helper class to convert string into ids with deduplication.
+class InternalStrings {
+ public:
+ size_t GetId(const std::string& str) {
+ IdMap::iterator it = ids_.find(str);
+ if (it != ids_.end()) return it->second;
+ size_t id = ids_.size();
+ return ids_[str] = id;
+ }
+
+ private:
+ typedef std::map<std::string, size_t> IdMap;
+ IdMap ids_;
+};
+
+} // namespace
+
+std::vector<EditType> CalculateOptimalEdits(
+ const std::vector<std::string>& left,
+ const std::vector<std::string>& right) {
+ std::vector<size_t> left_ids, right_ids;
+ {
+ InternalStrings intern_table;
+ for (size_t i = 0; i < left.size(); ++i) {
+ left_ids.push_back(intern_table.GetId(left[i]));
+ }
+ for (size_t i = 0; i < right.size(); ++i) {
+ right_ids.push_back(intern_table.GetId(right[i]));
+ }
+ }
+ return CalculateOptimalEdits(left_ids, right_ids);
+}
+
+namespace {
+
+// Helper class that holds the state for one hunk and prints it out to the
+// stream.
+// It reorders adds/removes when possible to group all removes before all
+// adds. It also adds the hunk header before printint into the stream.
+class Hunk {
+ public:
+ Hunk(size_t left_start, size_t right_start)
+ : left_start_(left_start),
+ right_start_(right_start),
+ adds_(),
+ removes_(),
+ common_() {}
+
+ void PushLine(char edit, const char* line) {
+ switch (edit) {
+ case ' ':
+ ++common_;
+ FlushEdits();
+ hunk_.push_back(std::make_pair(' ', line));
+ break;
+ case '-':
+ ++removes_;
+ hunk_removes_.push_back(std::make_pair('-', line));
+ break;
+ case '+':
+ ++adds_;
+ hunk_adds_.push_back(std::make_pair('+', line));
+ break;
+ }
+ }
+
+ void PrintTo(std::ostream* os) {
+ PrintHeader(os);
+ FlushEdits();
+ for (std::list<std::pair<char, const char*> >::const_iterator it =
+ hunk_.begin();
+ it != hunk_.end(); ++it) {
+ *os << it->first << it->second << "\n";
+ }
+ }
+
+ bool has_edits() const { return adds_ || removes_; }
+
+ private:
+ void FlushEdits() {
+ hunk_.splice(hunk_.end(), hunk_removes_);
+ hunk_.splice(hunk_.end(), hunk_adds_);
+ }
+
+ // Print a unified diff header for one hunk.
+ // The format is
+ // "@@ -<left_start>,<left_length> +<right_start>,<right_length> @@"
+ // where the left/right parts are ommitted if unnecessary.
+ void PrintHeader(std::ostream* ss) const {
+ *ss << "@@ ";
+ if (removes_) {
+ *ss << "-" << left_start_ << "," << (removes_ + common_);
+ }
+ if (removes_ && adds_) {
+ *ss << " ";
+ }
+ if (adds_) {
+ *ss << "+" << right_start_ << "," << (adds_ + common_);
+ }
+ *ss << " @@\n";
+ }
+
+ size_t left_start_, right_start_;
+ size_t adds_, removes_, common_;
+ std::list<std::pair<char, const char*> > hunk_, hunk_adds_, hunk_removes_;
+};
+
+} // namespace
+
+// Create a list of diff hunks in Unified diff format.
+// Each hunk has a header generated by PrintHeader above plus a body with
+// lines prefixed with ' ' for no change, '-' for deletion and '+' for
+// addition.
+// 'context' represents the desired unchanged prefix/suffix around the diff.
+// If two hunks are close enough that their contexts overlap, then they are
+// joined into one hunk.
+std::string CreateUnifiedDiff(const std::vector<std::string>& left,
+ const std::vector<std::string>& right,
+ size_t context) {
+ const std::vector<EditType> edits = CalculateOptimalEdits(left, right);
+
+ size_t l_i = 0, r_i = 0, edit_i = 0;
+ std::stringstream ss;
+ while (edit_i < edits.size()) {
+ // Find first edit.
+ while (edit_i < edits.size() && edits[edit_i] == kMatch) {
+ ++l_i;
+ ++r_i;
+ ++edit_i;
+ }
+
+ // Find the first line to include in the hunk.
+ const size_t prefix_context = std::min(l_i, context);
+ Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1);
+ for (size_t i = prefix_context; i > 0; --i) {
+ hunk.PushLine(' ', left[l_i - i].c_str());
+ }
+
+ // Iterate the edits until we found enough suffix for the hunk or the input
+ // is over.
+ size_t n_suffix = 0;
+ for (; edit_i < edits.size(); ++edit_i) {
+ if (n_suffix >= context) {
+ // Continue only if the next hunk is very close.
+ std::vector<EditType>::const_iterator it = edits.begin() + edit_i;
+ while (it != edits.end() && *it == kMatch) ++it;
+ if (it == edits.end() || (it - edits.begin()) - edit_i >= context) {
+ // There is no next edit or it is too far away.
+ break;
+ }
+ }
+
+ EditType edit = edits[edit_i];
+ // Reset count when a non match is found.
+ n_suffix = edit == kMatch ? n_suffix + 1 : 0;
+
+ if (edit == kMatch || edit == kRemove || edit == kReplace) {
+ hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str());
+ }
+ if (edit == kAdd || edit == kReplace) {
+ hunk.PushLine('+', right[r_i].c_str());
+ }
+
+ // Advance indices, depending on edit type.
+ l_i += edit != kAdd;
+ r_i += edit != kRemove;
+ }
+
+ if (!hunk.has_edits()) {
+ // We are done. We don't want this hunk.
+ break;
+ }
+
+ hunk.PrintTo(&ss);
+ }
+ return ss.str();
+}
+
+} // namespace edit_distance
+
+namespace {
+
+// The string representation of the values received in EqFailure() are already
+// escaped. Split them on escaped '\n' boundaries. Leave all other escaped
+// characters the same.
+std::vector<std::string> SplitEscapedString(const std::string& str) {
+ std::vector<std::string> lines;
+ size_t start = 0, end = str.size();
+ if (end > 2 && str[0] == '"' && str[end - 1] == '"') {
+ ++start;
+ --end;
+ }
+ bool escaped = false;
+ for (size_t i = start; i + 1 < end; ++i) {
+ if (escaped) {
+ escaped = false;
+ if (str[i] == 'n') {
+ lines.push_back(str.substr(start, i - start - 1));
+ start = i + 1;
+ }
+ } else {
+ escaped = str[i] == '\\';
+ }
+ }
+ lines.push_back(str.substr(start, end - start));
+ return lines;
+}
+
+} // namespace
+
// Constructs and returns the message for an equality assertion
// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
//
@@ -989,31 +1301,42 @@ namespace internal {
// and their values, as strings. For example, for ASSERT_EQ(foo, bar)
// where foo is 5 and bar is 6, we have:
//
-// expected_expression: "foo"
-// actual_expression: "bar"
-// expected_value: "5"
-// actual_value: "6"
+// lhs_expression: "foo"
+// rhs_expression: "bar"
+// lhs_value: "5"
+// rhs_value: "6"
//
// The ignoring_case parameter is true iff the assertion is a
-// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will
+// *_STRCASEEQ*. When it's true, the string "Ignoring case" will
// be inserted into the message.
-AssertionResult EqFailure(const char* expected_expression,
- const char* actual_expression,
- const std::string& expected_value,
- const std::string& actual_value,
+AssertionResult EqFailure(const char* lhs_expression,
+ const char* rhs_expression,
+ const std::string& lhs_value,
+ const std::string& rhs_value,
bool ignoring_case) {
Message msg;
- msg << "Value of: " << actual_expression;
- if (actual_value != actual_expression) {
- msg << "\n Actual: " << actual_value;
+ msg << " Expected: " << lhs_expression;
+ if (lhs_value != lhs_expression) {
+ msg << "\n Which is: " << lhs_value;
+ }
+ msg << "\nTo be equal to: " << rhs_expression;
+ if (rhs_value != rhs_expression) {
+ msg << "\n Which is: " << rhs_value;
}
- msg << "\nExpected: " << expected_expression;
if (ignoring_case) {
- msg << " (ignoring case)";
+ msg << "\nIgnoring case";
}
- if (expected_value != expected_expression) {
- msg << "\nWhich is: " << expected_value;
+
+ if (!lhs_value.empty() && !rhs_value.empty()) {
+ const std::vector<std::string> lhs_lines =
+ SplitEscapedString(lhs_value);
+ const std::vector<std::string> rhs_lines =
+ SplitEscapedString(rhs_value);
+ if (lhs_lines.size() > 1 || rhs_lines.size() > 1) {
+ msg << "\nWith diff:\n"
+ << edit_distance::CreateUnifiedDiff(lhs_lines, rhs_lines);
+ }
}
return AssertionFailure() << msg;
@@ -1111,18 +1434,18 @@ namespace internal {
// The helper function for {ASSERT|EXPECT}_EQ with int or enum
// arguments.
-AssertionResult CmpHelperEQ(const char* expected_expression,
- const char* actual_expression,
- BiggestInt expected,
- BiggestInt actual) {
- if (expected == actual) {
+AssertionResult CmpHelperEQ(const char* lhs_expression,
+ const char* rhs_expression,
+ BiggestInt lhs,
+ BiggestInt rhs) {
+ if (lhs == rhs) {
return AssertionSuccess();
}
- return EqFailure(expected_expression,
- actual_expression,
- FormatForComparisonFailureMessage(expected, actual),
- FormatForComparisonFailureMessage(actual, expected),
+ return EqFailure(lhs_expression,
+ rhs_expression,
+ FormatForComparisonFailureMessage(lhs, rhs),
+ FormatForComparisonFailureMessage(rhs, lhs),
false);
}
@@ -1161,34 +1484,34 @@ GTEST_IMPL_CMP_HELPER_(GT, > )
#undef GTEST_IMPL_CMP_HELPER_
// The helper function for {ASSERT|EXPECT}_STREQ.
-AssertionResult CmpHelperSTREQ(const char* expected_expression,
- const char* actual_expression,
- const char* expected,
- const char* actual) {
- if (String::CStringEquals(expected, actual)) {
+AssertionResult CmpHelperSTREQ(const char* lhs_expression,
+ const char* rhs_expression,
+ const char* lhs,
+ const char* rhs) {
+ if (String::CStringEquals(lhs, rhs)) {
return AssertionSuccess();
}
- return EqFailure(expected_expression,
- actual_expression,
- PrintToString(expected),
- PrintToString(actual),
+ return EqFailure(lhs_expression,
+ rhs_expression,
+ PrintToString(lhs),
+ PrintToString(rhs),
false);
}
// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
-AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
- const char* actual_expression,
- const char* expected,
- const char* actual) {
- if (String::CaseInsensitiveCStringEquals(expected, actual)) {
+AssertionResult CmpHelperSTRCASEEQ(const char* lhs_expression,
+ const char* rhs_expression,
+ const char* lhs,
+ const char* rhs) {
+ if (String::CaseInsensitiveCStringEquals(lhs, rhs)) {
return AssertionSuccess();
}
- return EqFailure(expected_expression,
- actual_expression,
- PrintToString(expected),
- PrintToString(actual),
+ return EqFailure(lhs_expression,
+ rhs_expression,
+ PrintToString(lhs),
+ PrintToString(rhs),
true);
}
@@ -1543,18 +1866,18 @@ bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) {
}
// Helper function for *_STREQ on wide strings.
-AssertionResult CmpHelperSTREQ(const char* expected_expression,
- const char* actual_expression,
- const wchar_t* expected,
- const wchar_t* actual) {
- if (String::WideCStringEquals(expected, actual)) {
+AssertionResult CmpHelperSTREQ(const char* lhs_expression,
+ const char* rhs_expression,
+ const wchar_t* lhs,
+ const wchar_t* rhs) {
+ if (String::WideCStringEquals(lhs, rhs)) {
return AssertionSuccess();
}
- return EqFailure(expected_expression,
- actual_expression,
- PrintToString(expected),
- PrintToString(actual),
+ return EqFailure(lhs_expression,
+ rhs_expression,
+ PrintToString(lhs),
+ PrintToString(rhs),
false);
}
@@ -1887,14 +2210,15 @@ int TestResult::test_property_count() const {
// Creates a Test object.
-// The c'tor saves the values of all Google Test flags.
+// The c'tor saves the states of all flags.
Test::Test()
- : gtest_flag_saver_(new internal::GTestFlagSaver) {
+ : gtest_flag_saver_(new GTEST_FLAG_SAVER_) {
}
-// The d'tor restores the values of all Google Test flags.
+// The d'tor restores the states of all flags. The actual work is
+// done by the d'tor of the gtest_flag_saver_ field, and thus not
+// visible here.
Test::~Test() {
- delete gtest_flag_saver_;
}
// Sets up the test fixture.
@@ -1963,8 +2287,8 @@ bool Test::HasSameFixtureClass() {
const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId();
if (first_is_TEST || this_is_TEST) {
- // The user mixed TEST and TEST_F in this test case - we'll tell
- // him/her how to fix it.
+ // Both TEST and TEST_F appear in same test case, which is incorrect.
+ // Tell the user how to fix this.
// Gets the name of the TEST and the name of the TEST_F. Note
// that first_is_TEST and this_is_TEST cannot both be true, as
@@ -1984,8 +2308,8 @@ bool Test::HasSameFixtureClass() {
<< "want to change the TEST to TEST_F or move it to another test\n"
<< "case.";
} else {
- // The user defined two fixture classes with the same name in
- // two namespaces - we'll tell him/her how to fix it.
+ // Two fixture classes with the same name appear in two different
+ // namespaces, which is not allowed. Tell the user how to fix this.
ADD_FAILURE()
<< "All tests in the same test case must use the same test fixture\n"
<< "class. However, in test case "
@@ -2178,12 +2502,14 @@ TestInfo::TestInfo(const std::string& a_test_case_name,
const std::string& a_name,
const char* a_type_param,
const char* a_value_param,
+ internal::CodeLocation a_code_location,
internal::TypeId fixture_class_id,
internal::TestFactoryBase* factory)
: test_case_name_(a_test_case_name),
name_(a_name),
type_param_(a_type_param ? new std::string(a_type_param) : NULL),
value_param_(a_value_param ? new std::string(a_value_param) : NULL),
+ location_(a_code_location),
fixture_class_id_(fixture_class_id),
should_run_(false),
is_disabled_(false),
@@ -2207,6 +2533,7 @@ namespace internal {
// this is not a typed or a type-parameterized test.
// value_param: text representation of the test's value parameter,
// or NULL if this is not a value-parameterized test.
+// code_location: code location where the test is defined
// fixture_class_id: ID of the test fixture class
// set_up_tc: pointer to the function that sets up the test case
// tear_down_tc: pointer to the function that tears down the test case
@@ -2218,20 +2545,21 @@ TestInfo* MakeAndRegisterTestInfo(
const char* name,
const char* type_param,
const char* value_param,
+ CodeLocation code_location,
TypeId fixture_class_id,
SetUpTestCaseFunc set_up_tc,
TearDownTestCaseFunc tear_down_tc,
TestFactoryBase* factory) {
TestInfo* const test_info =
new TestInfo(test_case_name, name, type_param, value_param,
- fixture_class_id, factory);
+ code_location, fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
#if GTEST_HAS_PARAM_TEST
void ReportInvalidTestCaseType(const char* test_case_name,
- const char* file, int line) {
+ CodeLocation code_location) {
Message errors;
errors
<< "Attempted redefinition of test case " << test_case_name << ".\n"
@@ -2243,7 +2571,9 @@ void ReportInvalidTestCaseType(const char* test_case_name,
<< "probably rename one of the classes to put the tests into different\n"
<< "test cases.";
- fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
+ fprintf(stderr, "%s %s",
+ FormatFileLocation(code_location.file.c_str(),
+ code_location.line).c_str(),
errors.GetString().c_str());
}
#endif // GTEST_HAS_PARAM_TEST
@@ -2554,7 +2884,8 @@ enum GTestColor {
COLOR_YELLOW
};
-#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \
+ !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
// Returns the character attribute for the given color.
WORD GetColorAttribute(GTestColor color) {
@@ -2599,6 +2930,10 @@ bool ShouldUseColor(bool stdout_is_tty) {
String::CStringEquals(term, "xterm-256color") ||
String::CStringEquals(term, "screen") ||
String::CStringEquals(term, "screen-256color") ||
+ String::CStringEquals(term, "tmux") ||
+ String::CStringEquals(term, "tmux-256color") ||
+ String::CStringEquals(term, "rxvt-unicode") ||
+ String::CStringEquals(term, "rxvt-unicode-256color") ||
String::CStringEquals(term, "linux") ||
String::CStringEquals(term, "cygwin");
return stdout_is_tty && term_supports_color;
@@ -2622,8 +2957,9 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS
- const bool use_color = false;
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || \
+ GTEST_OS_IOS || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
+ const bool use_color = AlwaysFalse();
#else
static const bool in_color_mode =
ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0);
@@ -2637,7 +2973,8 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) {
return;
}
-#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \
+ !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
// Gets the current text color.
@@ -3212,34 +3549,39 @@ std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(
// Formats the given time in milliseconds as seconds.
std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
::std::stringstream ss;
- ss << ms/1000.0;
+ ss << (static_cast<double>(ms) * 1e-3);
return ss.str();
}
-// Converts the given epoch time in milliseconds to a date string in the ISO
-// 8601 format, without the timezone information.
-std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
- // Using non-reentrant version as localtime_r is not portable.
- time_t seconds = static_cast<time_t>(ms / 1000);
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4996) // Temporarily disables warning 4996
- // (function or variable may be unsafe).
- const struct tm* const time_struct = localtime(&seconds); // NOLINT
-# pragma warning(pop) // Restores the warning state again.
+static bool PortableLocaltime(time_t seconds, struct tm* out) {
+#if defined(_MSC_VER)
+ return localtime_s(out, &seconds) == 0;
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+ // MINGW <time.h> provides neither localtime_r nor localtime_s, but uses
+ // Windows' localtime(), which has a thread-local tm buffer.
+ struct tm* tm_ptr = localtime(&seconds); // NOLINT
+ if (tm_ptr == NULL)
+ return false;
+ *out = *tm_ptr;
+ return true;
#else
- const struct tm* const time_struct = localtime(&seconds); // NOLINT
+ return localtime_r(&seconds, out) != NULL;
#endif
- if (time_struct == NULL)
- return ""; // Invalid ms value
+}
+// Converts the given epoch time in milliseconds to a date string in the ISO
+// 8601 format, without the timezone information.
+std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
+ struct tm time_struct;
+ if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct))
+ return "";
// YYYY-MM-DDThh:mm:ss
- return StreamableToString(time_struct->tm_year + 1900) + "-" +
- String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" +
- String::FormatIntWidth2(time_struct->tm_mday) + "T" +
- String::FormatIntWidth2(time_struct->tm_hour) + ":" +
- String::FormatIntWidth2(time_struct->tm_min) + ":" +
- String::FormatIntWidth2(time_struct->tm_sec);
+ return StreamableToString(time_struct.tm_year + 1900) + "-" +
+ String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" +
+ String::FormatIntWidth2(time_struct.tm_mday) + "T" +
+ String::FormatIntWidth2(time_struct.tm_hour) + ":" +
+ String::FormatIntWidth2(time_struct.tm_min) + ":" +
+ String::FormatIntWidth2(time_struct.tm_sec);
}
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
@@ -3502,26 +3844,15 @@ ScopedTrace::~ScopedTrace()
// class OsStackTraceGetter
-// Returns the current OS stack trace as an std::string. Parameters:
-//
-// max_depth - the maximum number of stack frames to be included
-// in the trace.
-// skip_count - the number of top frames to be skipped; doesn't count
-// against max_depth.
-//
-string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */,
- int /* skip_count */)
- GTEST_LOCK_EXCLUDED_(mutex_) {
- return "";
-}
+const char* const OsStackTraceGetterInterface::kElidedFramesMarker =
+ "... " GTEST_NAME_ " internal frames ...";
-void OsStackTraceGetter::UponLeavingGTest()
- GTEST_LOCK_EXCLUDED_(mutex_) {
+string OsStackTraceGetter::CurrentStackTrace(int /*max_depth*/,
+ int /*skip_count*/) {
+ return "";
}
-const char* const
-OsStackTraceGetter::kElidedFramesMarker =
- "... " GTEST_NAME_ " internal frames ...";
+void OsStackTraceGetter::UponLeavingGTest() {}
// A helper class that creates the premature-exit file in its
// constructor and deletes the file in its destructor.
@@ -3812,7 +4143,7 @@ void UnitTest::AddTestPartResult(
// with another testing framework) and specify the former on the
// command line for debugging.
if (GTEST_FLAG(break_on_failure)) {
-#if GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
// Using DebugBreak on Windows allows gtest to still break into a debugger
// when a failure happens and both the --gtest_break_on_failure and
// the --gtest_catch_exceptions flags are specified.
@@ -3890,7 +4221,7 @@ int UnitTest::Run() {
// process. In either case the user does not want to see pop-up dialogs
// about crashes - they are expected.
if (impl()->catch_exceptions() || in_death_test_child_process) {
-# if !GTEST_OS_WINDOWS_MOBILE
+# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
// SetErrorMode doesn't exist on CE.
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
@@ -3993,17 +4324,10 @@ namespace internal {
UnitTestImpl::UnitTestImpl(UnitTest* parent)
: parent_(parent),
-#ifdef _MSC_VER
-# pragma warning(push) // Saves the current warning state.
-# pragma warning(disable:4355) // Temporarily disables warning 4355
- // (using this in initializer).
+ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */)
default_global_test_part_result_reporter_(this),
default_per_thread_test_part_result_reporter_(this),
-# pragma warning(pop) // Restores the warning state again.
-#else
- default_global_test_part_result_reporter_(this),
- default_per_thread_test_part_result_reporter_(this),
-#endif // _MSC_VER
+ GTEST_DISABLE_MSC_WARNINGS_POP_()
global_test_part_result_repoter_(
&default_global_test_part_result_reporter_),
per_thread_test_part_result_reporter_(
@@ -4114,6 +4438,11 @@ void UnitTestImpl::PostFlagParsingInit() {
if (!post_flag_parse_init_performed_) {
post_flag_parse_init_performed_ = true;
+#if defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_)
+ // Register to send notifications about key process state changes.
+ listeners()->Append(new GTEST_CUSTOM_TEST_EVENT_LISTENER_());
+#endif // defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_)
+
#if GTEST_HAS_DEATH_TEST
InitDeathTestSubprocessControlInfo();
SuppressTestEventsIfInSubprocess();
@@ -4247,6 +4576,11 @@ bool UnitTestImpl::RunAllTests() {
#if GTEST_HAS_DEATH_TEST
in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL);
+# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
+ if (in_subprocess_for_death_test) {
+ GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_();
+ }
+# endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
#endif // GTEST_HAS_DEATH_TEST
const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
@@ -4583,7 +4917,11 @@ void UnitTestImpl::set_os_stack_trace_getter(
// getter, and returns it.
OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() {
if (os_stack_trace_getter_ == NULL) {
+#ifdef GTEST_OS_STACK_TRACE_GETTER_
+ os_stack_trace_getter_ = new GTEST_OS_STACK_TRACE_GETTER_;
+#else
os_stack_trace_getter_ = new OsStackTraceGetter;
+#endif // GTEST_OS_STACK_TRACE_GETTER_
}
return os_stack_trace_getter_;
@@ -4882,6 +5220,58 @@ static const char kColorEncodedHelpMessage[] =
"(not one in your own code or tests), please report it to\n"
"@G<" GTEST_DEV_EMAIL_ ">@D.\n";
+bool ParseGoogleTestFlag(const char* const arg) {
+ return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
+ &GTEST_FLAG(also_run_disabled_tests)) ||
+ ParseBoolFlag(arg, kBreakOnFailureFlag,
+ &GTEST_FLAG(break_on_failure)) ||
+ ParseBoolFlag(arg, kCatchExceptionsFlag,
+ &GTEST_FLAG(catch_exceptions)) ||
+ ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
+ ParseStringFlag(arg, kDeathTestStyleFlag,
+ &GTEST_FLAG(death_test_style)) ||
+ ParseBoolFlag(arg, kDeathTestUseFork,
+ &GTEST_FLAG(death_test_use_fork)) ||
+ ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
+ ParseStringFlag(arg, kInternalRunDeathTestFlag,
+ &GTEST_FLAG(internal_run_death_test)) ||
+ ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
+ ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
+ ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+ ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
+ ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
+ ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
+ ParseInt32Flag(arg, kStackTraceDepthFlag,
+ &GTEST_FLAG(stack_trace_depth)) ||
+ ParseStringFlag(arg, kStreamResultToFlag,
+ &GTEST_FLAG(stream_result_to)) ||
+ ParseBoolFlag(arg, kThrowOnFailureFlag,
+ &GTEST_FLAG(throw_on_failure));
+}
+
+#if GTEST_USE_OWN_FLAGFILE_FLAG_
+void LoadFlagsFromFile(const std::string& path) {
+ FILE* flagfile = posix::FOpen(path.c_str(), "r");
+ if (!flagfile) {
+ fprintf(stderr,
+ "Unable to open file \"%s\"\n",
+ GTEST_FLAG(flagfile).c_str());
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+ std::string contents(ReadEntireFile(flagfile));
+ posix::FClose(flagfile);
+ std::vector<std::string> lines;
+ SplitString(contents, '\n', &lines);
+ for (size_t i = 0; i < lines.size(); ++i) {
+ if (lines[i].empty())
+ continue;
+ if (!ParseGoogleTestFlag(lines[i].c_str()))
+ g_help_flag = true;
+ }
+}
+#endif // GTEST_USE_OWN_FLAGFILE_FLAG_
+
// Parses the command line for Google Test flags, without initializing
// other parts of Google Test. The type parameter CharType can be
// instantiated to either char or wchar_t.
@@ -4895,35 +5285,24 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
using internal::ParseInt32Flag;
using internal::ParseStringFlag;
- // Do we see a Google Test flag?
- if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
- &GTEST_FLAG(also_run_disabled_tests)) ||
- ParseBoolFlag(arg, kBreakOnFailureFlag,
- &GTEST_FLAG(break_on_failure)) ||
- ParseBoolFlag(arg, kCatchExceptionsFlag,
- &GTEST_FLAG(catch_exceptions)) ||
- ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
- ParseStringFlag(arg, kDeathTestStyleFlag,
- &GTEST_FLAG(death_test_style)) ||
- ParseBoolFlag(arg, kDeathTestUseFork,
- &GTEST_FLAG(death_test_use_fork)) ||
- ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
- ParseStringFlag(arg, kInternalRunDeathTestFlag,
- &GTEST_FLAG(internal_run_death_test)) ||
- ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
- ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
- ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
- ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
- ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
- ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
- ParseInt32Flag(arg, kStackTraceDepthFlag,
- &GTEST_FLAG(stack_trace_depth)) ||
- ParseStringFlag(arg, kStreamResultToFlag,
- &GTEST_FLAG(stream_result_to)) ||
- ParseBoolFlag(arg, kThrowOnFailureFlag,
- &GTEST_FLAG(throw_on_failure))
- ) {
- // Yes. Shift the remainder of the argv list left by one. Note
+ bool remove_flag = false;
+ if (ParseGoogleTestFlag(arg)) {
+ remove_flag = true;
+#if GTEST_USE_OWN_FLAGFILE_FLAG_
+ } else if (ParseStringFlag(arg, kFlagfileFlag, &GTEST_FLAG(flagfile))) {
+ LoadFlagsFromFile(GTEST_FLAG(flagfile));
+ remove_flag = true;
+#endif // GTEST_USE_OWN_FLAGFILE_FLAG_
+ } else if (arg_string == "--help" || arg_string == "-h" ||
+ arg_string == "-?" || arg_string == "/?" ||
+ HasGoogleTestFlagPrefix(arg)) {
+ // Both help flag and unrecognized Google Test flags (excluding
+ // internal ones) trigger help display.
+ g_help_flag = true;
+ }
+
+ if (remove_flag) {
+ // Shift the remainder of the argv list left by one. Note
// that argv has (*argc + 1) elements, the last one always being
// NULL. The following loop moves the trailing NULL element as
// well.
@@ -4937,12 +5316,6 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
// We also need to decrement the iterator as we just removed
// an element.
i--;
- } else if (arg_string == "--help" || arg_string == "-h" ||
- arg_string == "-?" || arg_string == "/?" ||
- HasGoogleTestFlagPrefix(arg)) {
- // Both help flag and unrecognized Google Test flags (excluding
- // internal ones) trigger help display.
- g_help_flag = true;
}
}
@@ -4969,24 +5342,16 @@ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) {
// wchar_t.
template <typename CharType>
void InitGoogleTestImpl(int* argc, CharType** argv) {
- g_init_gtest_count++;
-
// We don't want to run the initialization code twice.
- if (g_init_gtest_count != 1) return;
+ if (GTestIsInitialized()) return;
if (*argc <= 0) return;
- internal::g_executable_path = internal::StreamableToString(argv[0]);
-
-#if GTEST_HAS_DEATH_TEST
-
g_argvs.clear();
for (int i = 0; i != *argc; i++) {
g_argvs.push_back(StreamableToString(argv[i]));
}
-#endif // GTEST_HAS_DEATH_TEST
-
ParseGoogleTestFlagsOnly(argc, argv);
GetUnitTestImpl()->PostFlagParsingInit();
}
@@ -5003,13 +5368,21 @@ void InitGoogleTestImpl(int* argc, CharType** argv) {
//
// Calling the function for the second time has no user-visible effect.
void InitGoogleTest(int* argc, char** argv) {
+#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
+ GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv);
+#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
internal::InitGoogleTestImpl(argc, argv);
+#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
}
// This overloaded version can be used in Windows programs compiled in
// UNICODE mode.
void InitGoogleTest(int* argc, wchar_t** argv) {
+#if defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
+ GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_(argc, argv);
+#else // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
internal::InitGoogleTestImpl(argc, argv);
+#endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_)
}
} // namespace testing
diff --git a/intern/audaspace/jack/AUD_JackLibrary.cpp b/intern/audaspace/jack/AUD_JackLibrary.cpp
index 63306ee0b15..9ed6862bbb9 100644
--- a/intern/audaspace/jack/AUD_JackLibrary.cpp
+++ b/intern/audaspace/jack/AUD_JackLibrary.cpp
@@ -44,7 +44,20 @@ static bool jack_supported = false;
void AUD_jack_init(void)
{
#ifdef WITH_JACK_DYNLOAD
- jack_handle = dlopen("libjack.so", RTLD_LAZY);
+ const char *names[] = {"libjack.so",
+ "libjack.so.0",
+ "libjack.so.1",
+ "libjack.so.2",
+ NULL};
+ int index = 0;
+ while (names[index] != NULL) {
+ jack_handle = dlopen(names[index], RTLD_LAZY);
+ if (jack_handle != NULL) {
+ // Found existing library.
+ break;
+ }
+ ++index;
+ }
if (!jack_handle) {
return;
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index 3b410b2a1e1..97854a88e84 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -146,6 +146,14 @@ if(WITH_CYCLES_OSL)
)
endif()
+if(WITH_CYCLES_OPENSUBDIV)
+ add_definitions(-DWITH_OPENSUBDIV)
+ include_directories(
+ SYSTEM
+ ${OPENSUBDIV_INCLUDE_DIR}
+ )
+endif()
+
set(WITH_CYCLES_DEVICE_OPENCL TRUE)
set(WITH_CYCLES_DEVICE_CUDA TRUE)
set(WITH_CYCLES_DEVICE_MULTI TRUE)
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index 3aca46e2dc7..3d3aca33881 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -35,7 +35,6 @@
#include "shader.h"
#include "scene.h"
-#include "subd_mesh.h"
#include "subd_patch.h"
#include "subd_split.h"
@@ -417,6 +416,7 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
xml_read_int_array(verts, node, "verts");
xml_read_int_array(nverts, node, "nverts");
+#if 0
if(xml_equal_string(node, "subdivision", "catmull-clark")) {
/* create subd mesh */
SubdMesh sdmesh;
@@ -460,7 +460,9 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
DiagSplit dsplit(sdparams);
sdmesh.tessellate(&dsplit);
}
- else {
+ else
+#endif
+ {
/* create vertices */
mesh->verts = P;
@@ -568,7 +570,7 @@ static void xml_read_patch(const XMLReadState& state, pugi::xml_node node)
mesh->used_shaders.push_back(state.shader);
/* split */
- SubdParams sdparams(mesh, 0, state.smooth);
+ SubdParams sdparams(mesh);
xml_read_float(&sdparams.dicing_rate, node, "dicing_rate");
DiagSplit dsplit(sdparams);
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index d4b7535b9ee..2c5365c9189 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -82,7 +82,6 @@ def init():
import bpy
import _cycles
import os.path
- import sys
# Workaround possibly buggy legacy drivers which crashes on the OpenCL
# device enumeration.
@@ -103,10 +102,12 @@ def init():
_cycles.init(path, user_path, bpy.app.background)
_parse_command_line()
+
def exit():
import _cycles
_cycles.exit()
+
def create(engine, data, scene, region=None, v3d=None, rv3d=None, preview_osl=False):
import bpy
import _cycles
diff --git a/intern/cycles/blender/addon/osl.py b/intern/cycles/blender/addon/osl.py
index f4aaaab5eab..19f2ecc9d1a 100644
--- a/intern/cycles/blender/addon/osl.py
+++ b/intern/cycles/blender/addon/osl.py
@@ -41,6 +41,8 @@ def update_script_node(node, report):
import shutil
import tempfile
+ oso_file_remove = False
+
if node.mode == 'EXTERNAL':
# compile external script file
script_path = bpy.path.abspath(node.filepath, library=node.id_data.library)
@@ -49,7 +51,6 @@ def update_script_node(node, report):
if script_ext == ".oso":
# it's a .oso file, no need to compile
ok, oso_path = True, script_path
- oso_file_remove = False
elif script_ext == ".osl":
# compile .osl file
ok, oso_path = osl_compile(script_path, report)
@@ -65,7 +66,6 @@ def update_script_node(node, report):
elif os.path.dirname(node.filepath) == "":
# module in search path
oso_path = node.filepath
- oso_file_remove = False
ok = True
else:
# unknown
@@ -88,12 +88,10 @@ def update_script_node(node, report):
osl_file.close()
ok, oso_path = osl_compile(osl_file.name, report)
- oso_file_remove = False
os.remove(osl_file.name)
else:
# compile text datablock from disk directly
ok, oso_path = osl_compile(osl_path, report)
- oso_file_remove = False
if ok:
# read bytecode
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 0b3dd552f62..8e82eac2b59 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -46,12 +46,6 @@ enum_displacement_methods = (
('BOTH', "Both", "Combination of displacement and bump mapping"),
)
-enum_subdivision_types = (
- ('NONE', "None", "No subdivision"),
- ('LINEAR', "Linear", "Use linear subdivision"),
- ('CATMULL_CLARK', "Catmull–Clark", "Use Catmull-Clark subdivision"),
- )
-
enum_bvh_types = (
('DYNAMIC_BVH', "Dynamic BVH", "Objects can be individually updated, at the cost of slower render time"),
('STATIC_BVH', "Static BVH", "Any object modification requires a complete BVH rebuild, but renders faster"),
@@ -781,6 +775,13 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup):
default='LINEAR',
)
+ cls.displacement_method = EnumProperty(
+ name="Displacement Method",
+ description="Method to use for the displacement",
+ items=enum_displacement_methods,
+ default='BUMP',
+ )
+
@classmethod
def unregister(cls):
del bpy.types.Material.cycles
@@ -958,25 +959,6 @@ class CyclesMeshSettings(bpy.types.PropertyGroup):
type=cls,
)
- cls.displacement_method = EnumProperty(
- name="Displacement Method",
- description="Method to use for the displacement",
- items=enum_displacement_methods,
- default='BUMP',
- )
- cls.subdivision_type = EnumProperty(
- name="Subdivision Type",
- description="Type of subdivision to use",
- items=enum_subdivision_types,
- default='NONE',
- )
- cls.dicing_rate = FloatProperty(
- name="Dicing Rate",
- description="Multiplier for scene dicing rate",
- min=0.1, max=1000.0,
- default=1.0,
- )
-
@classmethod
def unregister(cls):
del bpy.types.Mesh.cycles
@@ -984,11 +966,9 @@ class CyclesMeshSettings(bpy.types.PropertyGroup):
del bpy.types.MetaBall.cycles
-class CyclesObjectBlurSettings(bpy.types.PropertyGroup):
-
+class CyclesObjectSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
-
bpy.types.Object.cycles = PointerProperty(
name="Cycles Object Settings",
description="Cycles object settings",
@@ -1020,6 +1000,19 @@ class CyclesObjectBlurSettings(bpy.types.PropertyGroup):
default=False,
)
+ cls.use_adaptive_subdivision = BoolProperty(
+ name="Use Adaptive Subdivision",
+ description="Use adaptive render time subdivision",
+ default=False,
+ )
+
+ cls.dicing_rate = FloatProperty(
+ name="Dicing Rate",
+ description="Multiplier for scene dicing rate",
+ min=0.1, max=1000.0,
+ default=1.0,
+ )
+
@classmethod
def unregister(cls):
del bpy.types.Object.cycles
@@ -1136,6 +1129,7 @@ def register():
bpy.utils.register_class(CyclesWorldSettings)
bpy.utils.register_class(CyclesVisibilitySettings)
bpy.utils.register_class(CyclesMeshSettings)
+ bpy.utils.register_class(CyclesObjectSettings)
bpy.utils.register_class(CyclesCurveRenderSettings)
bpy.utils.register_class(CyclesCurveSettings)
@@ -1147,6 +1141,7 @@ def unregister():
bpy.utils.unregister_class(CyclesLampSettings)
bpy.utils.unregister_class(CyclesWorldSettings)
bpy.utils.unregister_class(CyclesMeshSettings)
+ bpy.utils.unregister_class(CyclesObjectSettings)
bpy.utils.unregister_class(CyclesVisibilitySettings)
bpy.utils.unregister_class(CyclesCurveRenderSettings)
bpy.utils.unregister_class(CyclesCurveSettings)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 6656beb4478..52872d2b83f 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -674,48 +674,6 @@ class Cycles_PT_context_material(CyclesButtonsPanel, Panel):
split.separator()
-class Cycles_PT_mesh_displacement(CyclesButtonsPanel, Panel):
- bl_label = "Displacement"
- bl_context = "data"
-
- @classmethod
- def poll(cls, context):
- if CyclesButtonsPanel.poll(context):
- if context.mesh or context.curve or context.meta_ball:
- if context.scene.cycles.feature_set == 'EXPERIMENTAL':
- return True
-
- return False
-
- def draw(self, context):
- layout = self.layout
-
- mesh = context.mesh
- curve = context.curve
- mball = context.meta_ball
-
- if mesh:
- cdata = mesh.cycles
- elif curve:
- cdata = curve.cycles
- elif mball:
- cdata = mball.cycles
-
- split = layout.split()
-
- col = split.column()
- sub = col.column(align=True)
- sub.label(text="Displacement:")
- sub.prop(cdata, "displacement_method", text="")
-
- col = split.column()
- sub = col.column(align=True)
- sub.label(text="Subdivision:")
- sub.prop(cdata, "subdivision_type", text="")
-
- if cdata.subdivision_type != 'NONE':
- sub.prop(cdata, "dicing_rate")
-
class CyclesObject_PT_motion_blur(CyclesButtonsPanel, Panel):
bl_label = "Motion Blur"
bl_context = "object"
@@ -895,7 +853,7 @@ class CyclesLamp_PT_lamp(CyclesButtonsPanel, Panel):
lamp = context.lamp
clamp = lamp.cycles
- cscene = context.scene.cycles
+ # cscene = context.scene.cycles
layout.prop(lamp, "type", expand=True)
@@ -1115,7 +1073,7 @@ class CyclesWorld_PT_settings(CyclesButtonsPanel, Panel):
world = context.world
cworld = world.cycles
- cscene = context.scene.cycles
+ # cscene = context.scene.cycles
split = layout.split()
@@ -1227,6 +1185,11 @@ class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel):
col.prop(cmat, "sample_as_light", text="Multiple Importance")
col.prop(cmat, "use_transparent_shadow")
+ if context.scene.cycles.feature_set == 'EXPERIMENTAL':
+ col.separator()
+ col.label(text="Displacement:")
+ col.prop(cmat, "displacement_method", text="")
+
col = split.column()
col.label(text="Volume:")
sub = col.column()
diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py
index 221b1437128..830723d6149 100644
--- a/intern/cycles/blender/addon/version_update.py
+++ b/intern/cycles/blender/addon/version_update.py
@@ -104,7 +104,6 @@ def vector_curve_node_remap(node):
"""
Remap values of vector curve node from normalized to absolute values
"""
- from mathutils import Vector
if node.bl_idname == 'ShaderNodeVectorCurve':
node.mapping.use_clip = False
for curve in node.mapping.curves:
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index ec11a893b5a..5d63aaa1415 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -24,7 +24,6 @@
#include "blender_session.h"
#include "blender_util.h"
-#include "subd_mesh.h"
#include "subd_patch.h"
#include "subd_split.h"
@@ -335,44 +334,71 @@ static void attr_create_vertex_color(Scene *scene,
Mesh *mesh,
BL::Mesh& b_mesh,
const vector<int>& nverts,
- const vector<int>& face_flags)
+ const vector<int>& face_flags,
+ bool subdivision)
{
- BL::Mesh::tessface_vertex_colors_iterator l;
- for(b_mesh.tessface_vertex_colors.begin(l); l != b_mesh.tessface_vertex_colors.end(); ++l) {
- if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
- continue;
+ if(subdivision) {
+ BL::Mesh::vertex_colors_iterator l;
- Attribute *attr = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
+ for(b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
+ if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
+ continue;
- BL::MeshColorLayer::data_iterator c;
- uchar4 *cdata = attr->data_uchar4();
- size_t i = 0;
+ Attribute *attr = mesh->subd_attributes.add(ustring(l->name().c_str()),
+ TypeDesc::TypeColor,
+ ATTR_ELEMENT_CORNER_BYTE);
- for(l->data.begin(c); c != l->data.end(); ++c, ++i) {
- int tri_a[3], tri_b[3];
- face_split_tri_indices(nverts[i], face_flags[i], tri_a, tri_b);
+ BL::Mesh::polygons_iterator p;
+ uchar4 *cdata = attr->data_uchar4();
- uchar4 colors[4];
- colors[0] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color1())));
- colors[1] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color2())));
- colors[2] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color3())));
- if(nverts[i] == 4) {
- colors[3] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color4())));
+ for(b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
+ int n = p->loop_total();
+ for(int i = 0; i < n; i++) {
+ float3 color = get_float3(l->data[p->loop_start() + i].color());
+ *(cdata++) = color_float_to_byte(color_srgb_to_scene_linear(color));
+ }
}
+ }
+ }
+ else {
+ BL::Mesh::tessface_vertex_colors_iterator l;
+ for(b_mesh.tessface_vertex_colors.begin(l); l != b_mesh.tessface_vertex_colors.end(); ++l) {
+ if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
+ continue;
+
+ Attribute *attr = mesh->attributes.add(ustring(l->name().c_str()),
+ TypeDesc::TypeColor,
+ ATTR_ELEMENT_CORNER_BYTE);
+
+ BL::MeshColorLayer::data_iterator c;
+ uchar4 *cdata = attr->data_uchar4();
+ size_t i = 0;
+
+ for(l->data.begin(c); c != l->data.end(); ++c, ++i) {
+ int tri_a[3], tri_b[3];
+ face_split_tri_indices(nverts[i], face_flags[i], tri_a, tri_b);
+
+ uchar4 colors[4];
+ colors[0] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color1())));
+ colors[1] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color2())));
+ colors[2] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color3())));
+ if(nverts[i] == 4) {
+ colors[3] = color_float_to_byte(color_srgb_to_scene_linear(get_float3(c->color4())));
+ }
- cdata[0] = colors[tri_a[0]];
- cdata[1] = colors[tri_a[1]];
- cdata[2] = colors[tri_a[2]];
+ cdata[0] = colors[tri_a[0]];
+ cdata[1] = colors[tri_a[1]];
+ cdata[2] = colors[tri_a[2]];
- if(nverts[i] == 4) {
- cdata[3] = colors[tri_b[0]];
- cdata[4] = colors[tri_b[1]];
- cdata[5] = colors[tri_b[2]];
- cdata += 6;
+ if(nverts[i] == 4) {
+ cdata[3] = colors[tri_b[0]];
+ cdata[4] = colors[tri_b[1]];
+ cdata[5] = colors[tri_b[2]];
+ cdata += 6;
+ }
+ else
+ cdata += 3;
}
- else
- cdata += 3;
}
}
}
@@ -382,9 +408,45 @@ static void attr_create_uv_map(Scene *scene,
Mesh *mesh,
BL::Mesh& b_mesh,
const vector<int>& nverts,
- const vector<int>& face_flags)
+ const vector<int>& face_flags,
+ bool subdivision,
+ bool subdivide_uvs)
{
- if(b_mesh.tessface_uv_textures.length() != 0) {
+ if(subdivision) {
+ BL::Mesh::uv_layers_iterator l;
+ int i = 0;
+
+ for(b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l, ++i) {
+ bool active_render = b_mesh.uv_textures[i].active_render();
+ AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE;
+ ustring name = ustring(l->name().c_str());
+
+ /* UV map */
+ if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
+ Attribute *attr;
+
+ if(active_render)
+ attr = mesh->subd_attributes.add(std, name);
+ else
+ attr = mesh->subd_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER);
+
+ if(subdivide_uvs) {
+ attr->flags |= ATTR_SUBDIVIDED;
+ }
+
+ BL::Mesh::polygons_iterator p;
+ float3 *fdata = attr->data_float3();
+
+ for(b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
+ int n = p->loop_total();
+ for(int j = 0; j < n; j++) {
+ *(fdata++) = get_float3(l->data[p->loop_start() + j].uv());
+ }
+ }
+ }
+ }
+ }
+ else if(b_mesh.tessface_uv_textures.length() != 0) {
BL::Mesh::tessface_uv_textures_iterator l;
for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l) {
@@ -465,11 +527,13 @@ static void attr_create_uv_map(Scene *scene,
/* Create vertex pointiness attributes. */
static void attr_create_pointiness(Scene *scene,
Mesh *mesh,
- BL::Mesh& b_mesh)
+ BL::Mesh& b_mesh,
+ bool subdivision)
{
if(mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
const int numverts = b_mesh.vertices.length();
- Attribute *attr = mesh->attributes.add(ATTR_STD_POINTINESS);
+ AttributeSet& attributes = (subdivision)? mesh->subd_attributes: mesh->attributes;
+ Attribute *attr = attributes.add(ATTR_STD_POINTINESS);
float *data = attr->data_float();
int *counter = new int[numverts];
float *raw_data = new float[numverts];
@@ -532,30 +596,45 @@ static void attr_create_pointiness(Scene *scene,
static void create_mesh(Scene *scene,
Mesh *mesh,
BL::Mesh& b_mesh,
- const vector<Shader*>& used_shaders)
+ const vector<Shader*>& used_shaders,
+ bool subdivision=false,
+ bool subdivide_uvs=true)
{
/* count vertices and faces */
int numverts = b_mesh.vertices.length();
- int numfaces = b_mesh.tessfaces.length();
+ int numfaces = (!subdivision) ? b_mesh.tessfaces.length() : b_mesh.polygons.length();
int numtris = 0;
+ int numcorners = 0;
+ int numngons = 0;
bool use_loop_normals = b_mesh.use_auto_smooth();
BL::Mesh::vertices_iterator v;
BL::Mesh::tessfaces_iterator f;
+ BL::Mesh::polygons_iterator p;
- for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f) {
- int4 vi = get_int4(f->vertices_raw());
- numtris += (vi[3] == 0)? 1: 2;
+ if(!subdivision) {
+ for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f) {
+ int4 vi = get_int4(f->vertices_raw());
+ numtris += (vi[3] == 0)? 1: 2;
+ }
+ }
+ else {
+ for(b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
+ numngons += (p->loop_total() == 4)? 0: 1;
+ numcorners += p->loop_total();
+ }
}
/* allocate memory */
mesh->reserve_mesh(numverts, numtris);
+ mesh->reserve_subd_faces(numfaces, numngons, numcorners);
/* create vertex coordinates and normals */
for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v)
mesh->add_vertex(get_float3(v->co()));
- Attribute *attr_N = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
+ AttributeSet& attributes = (subdivision)? mesh->subd_attributes: mesh->attributes;
+ Attribute *attr_N = attributes.add(ATTR_STD_VERTEX_NORMAL);
float3 *N = attr_N->data_float3();
for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v, ++N)
@@ -564,7 +643,8 @@ static void create_mesh(Scene *scene,
/* create generated coordinates from undeformed coordinates */
if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
- Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
+ Attribute *attr = attributes.add(ATTR_STD_GENERATED);
+ attr->flags |= ATTR_SUBDIVIDED;
float3 loc, size;
mesh_texture_space(b_mesh, loc, size);
@@ -577,67 +657,103 @@ static void create_mesh(Scene *scene,
}
/* Create needed vertex attributes. */
- attr_create_pointiness(scene, mesh, b_mesh);
+ attr_create_pointiness(scene, mesh, b_mesh, subdivision);
/* create faces */
vector<int> nverts(numfaces);
vector<int> face_flags(numfaces, FACE_FLAG_NONE);
int fi = 0;
- for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f, ++fi) {
- int4 vi = get_int4(f->vertices_raw());
- int n = (vi[3] == 0)? 3: 4;
- int shader = clamp(f->material_index(), 0, used_shaders.size()-1);
- bool smooth = f->use_smooth() || use_loop_normals;
-
- /* split vertices if normal is different
- *
- * note all vertex attributes must have been set here so we can split
- * and copy attributes in split_vertex without remapping later */
- if(use_loop_normals) {
- BL::Array<float, 12> loop_normals = f->split_normals();
-
- for(int i = 0; i < n; i++) {
- float3 loop_N = make_float3(loop_normals[i * 3], loop_normals[i * 3 + 1], loop_normals[i * 3 + 2]);
-
- if(N[vi[i]] != loop_N) {
- int new_vi = mesh->split_vertex(vi[i]);
-
- /* set new normal and vertex index */
- N = attr_N->data_float3();
- N[new_vi] = loop_N;
- vi[i] = new_vi;
+ if(!subdivision) {
+ for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f, ++fi) {
+ int4 vi = get_int4(f->vertices_raw());
+ int n = (vi[3] == 0)? 3: 4;
+ int shader = clamp(f->material_index(), 0, used_shaders.size()-1);
+ bool smooth = f->use_smooth() || use_loop_normals;
+
+ /* split vertices if normal is different
+ *
+ * note all vertex attributes must have been set here so we can split
+ * and copy attributes in split_vertex without remapping later */
+ if(use_loop_normals) {
+ BL::Array<float, 12> loop_normals = f->split_normals();
+
+ for(int i = 0; i < n; i++) {
+ float3 loop_N = make_float3(loop_normals[i * 3], loop_normals[i * 3 + 1], loop_normals[i * 3 + 2]);
+
+ if(N[vi[i]] != loop_N) {
+ int new_vi = mesh->split_vertex(vi[i]);
+
+ /* set new normal and vertex index */
+ N = attr_N->data_float3();
+ N[new_vi] = loop_N;
+ vi[i] = new_vi;
+ }
}
}
- }
- /* create triangles */
- if(n == 4) {
- if(is_zero(cross(mesh->verts[vi[1]] - mesh->verts[vi[0]], mesh->verts[vi[2]] - mesh->verts[vi[0]])) ||
- is_zero(cross(mesh->verts[vi[2]] - mesh->verts[vi[0]], mesh->verts[vi[3]] - mesh->verts[vi[0]])))
- {
- // TODO(mai): order here is probably wrong
- mesh->add_triangle(vi[0], vi[1], vi[3], shader, smooth, true);
- mesh->add_triangle(vi[2], vi[3], vi[1], shader, smooth, true);
- face_flags[fi] |= FACE_FLAG_DIVIDE_24;
+ /* create triangles */
+ if(n == 4) {
+ if(is_zero(cross(mesh->verts[vi[1]] - mesh->verts[vi[0]], mesh->verts[vi[2]] - mesh->verts[vi[0]])) ||
+ is_zero(cross(mesh->verts[vi[2]] - mesh->verts[vi[0]], mesh->verts[vi[3]] - mesh->verts[vi[0]])))
+ {
+ mesh->add_triangle(vi[0], vi[1], vi[3], shader, smooth);
+ mesh->add_triangle(vi[2], vi[3], vi[1], shader, smooth);
+ face_flags[fi] |= FACE_FLAG_DIVIDE_24;
+ }
+ else {
+ mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
+ mesh->add_triangle(vi[0], vi[2], vi[3], shader, smooth);
+ face_flags[fi] |= FACE_FLAG_DIVIDE_13;
+ }
}
else {
- mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth, true);
- mesh->add_triangle(vi[0], vi[2], vi[3], shader, smooth, true);
- face_flags[fi] |= FACE_FLAG_DIVIDE_13;
+ mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
}
+
+ nverts[fi] = n;
}
- else
- mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth, false);
+ }
+ else {
+ vector<int> vi;
+
+ for(b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
+ int n = p->loop_total();
+ int shader = clamp(p->material_index(), 0, used_shaders.size()-1);
+ bool smooth = p->use_smooth() || use_loop_normals;
- nverts[fi] = n;
+ vi.reserve(n);
+ for(int i = 0; i < n; i++) {
+ vi[i] = b_mesh.loops[p->loop_start() + i].vertex_index();
+
+ /* split vertices if normal is different
+ *
+ * note all vertex attributes must have been set here so we can split
+ * and copy attributes in split_vertex without remapping later */
+ if(use_loop_normals) {
+ float3 loop_N = get_float3(b_mesh.loops[p->loop_start() + i].normal());
+
+ if(N[vi[i]] != loop_N) {
+ int new_vi = mesh->split_vertex(vi[i]);
+
+ /* set new normal and vertex index */
+ N = attr_N->data_float3();
+ N[new_vi] = loop_N;
+ vi[i] = new_vi;
+ }
+ }
+ }
+
+ /* create subd faces */
+ mesh->add_subd_face(&vi[0], n, shader, smooth);
+ }
}
/* Create all needed attributes.
* The calculate functions will check whether they're needed or not.
*/
- attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags);
- attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags);
+ attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags, subdivision);
+ attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags, subdivision, subdivide_uvs);
/* for volume objects, create a matrix to transform from object space to
* mesh texture space. this does not work with deformations but that can
@@ -657,16 +773,44 @@ static void create_subd_mesh(Scene *scene,
Mesh *mesh,
BL::Object& b_ob,
BL::Mesh& b_mesh,
- PointerRNA *cmesh,
const vector<Shader*>& used_shaders,
float dicing_rate,
int max_subdivisions)
{
- Mesh basemesh;
- create_mesh(scene, &basemesh, b_mesh, used_shaders);
+ BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length()-1]);
+ bool subdivide_uvs = subsurf_mod.use_subsurf_uv();
+
+ create_mesh(scene, mesh, b_mesh, used_shaders, true, subdivide_uvs);
+
+ /* export creases */
+ size_t num_creases = 0;
+ BL::Mesh::edges_iterator e;
+
+ for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
+ if(e->crease() != 0.0f) {
+ num_creases++;
+ }
+ }
+
+ mesh->subd_creases.resize(num_creases);
- SubdParams sdparams(mesh, 0, true, false);
- sdparams.dicing_rate = max(0.1f, RNA_float_get(cmesh, "dicing_rate") * dicing_rate);
+ Mesh::SubdEdgeCrease* crease = mesh->subd_creases.data();
+ for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
+ if(e->crease() != 0.0f) {
+ crease->v[0] = e->vertices()[0];
+ crease->v[1] = e->vertices()[1];
+ crease->crease = e->crease();
+
+ crease++;
+ }
+ }
+
+ /* set subd params */
+ SubdParams sdparams(mesh);
+
+ PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
+
+ sdparams.dicing_rate = max(0.1f, RNA_float_get(&cobj, "dicing_rate") * dicing_rate);
sdparams.max_level = max_subdivisions;
scene->camera->update();
@@ -675,7 +819,7 @@ static void create_subd_mesh(Scene *scene,
/* tesselate */
DiagSplit dsplit(sdparams);
- basemesh.tessellate(&dsplit);
+ mesh->tessellate(&dsplit);
}
/* Sync */
@@ -793,8 +937,6 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
mesh_synced.insert(mesh);
/* create derived mesh */
- PointerRNA cmesh = RNA_pointer_get(&b_ob_data.ptr, "cycles");
-
array<int> oldtriangle = mesh->triangles;
/* compares curve_keys rather than strands in order to handle quick hair
@@ -817,20 +959,41 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
b_ob.update_from_editmode();
bool need_undeformed = mesh->need_attribute(scene, ATTR_STD_GENERATED);
- BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed);
+
+ mesh->subdivision_type = Mesh::SUBDIVISION_NONE;
+
+ PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
+
+ if(cobj.data && b_ob.modifiers.length() > 0 && experimental) {
+ BL::Modifier mod = b_ob.modifiers[b_ob.modifiers.length()-1];
+ bool enabled = preview ? mod.show_viewport() : mod.show_render();
+
+ if(enabled && mod.type() == BL::Modifier::type_SUBSURF && RNA_int_get(&cobj, "use_adaptive_subdivision")) {
+ BL::SubsurfModifier subsurf(mod);
+
+ if(subsurf.subdivision_type() == BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) {
+ mesh->subdivision_type = Mesh::SUBDIVISION_CATMULL_CLARK;
+ }
+ else {
+ mesh->subdivision_type = Mesh::SUBDIVISION_LINEAR;
+ }
+ }
+ }
+
+ BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed, mesh->subdivision_type);
if(b_mesh) {
if(render_layer.use_surfaces && !hide_tris) {
- if(cmesh.data && experimental && RNA_enum_get(&cmesh, "subdivision_type"))
- create_subd_mesh(scene, mesh, b_ob, b_mesh, &cmesh, used_shaders,
+ if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE)
+ create_subd_mesh(scene, mesh, b_ob, b_mesh, used_shaders,
dicing_rate, max_subdivisions);
else
- create_mesh(scene, mesh, b_mesh, used_shaders);
+ create_mesh(scene, mesh, b_mesh, used_shaders, false);
create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
}
- if(render_layer.use_hair)
+ if(render_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
sync_curves(mesh, b_mesh, b_ob, false);
if(can_free_caches) {
@@ -843,21 +1006,6 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
}
mesh->geometry_flags = requested_geometry_flags;
- /* displacement method */
- if(cmesh.data) {
- const int method = get_enum(cmesh,
- "displacement_method",
- Mesh::DISPLACE_NUM_METHODS,
- Mesh::DISPLACE_BUMP);
-
- if(method == 0 || !experimental)
- mesh->displacement_method = Mesh::DISPLACE_BUMP;
- else if(method == 1)
- mesh->displacement_method = Mesh::DISPLACE_TRUE;
- else
- mesh->displacement_method = Mesh::DISPLACE_BOTH;
- }
-
/* fluid motion */
sync_mesh_fluid_motion(b_ob, scene, mesh);
@@ -957,7 +1105,7 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
if(ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
/* get derived mesh */
- b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, false);
+ b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, false, false);
}
if(!b_mesh) {
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 22a0b3988c8..f305e8e17cc 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -253,8 +253,18 @@ static bool object_boundbox_clip(Scene *scene,
boundbox[3 * i + 1],
boundbox[3 * i + 2]);
p = transform_point(&tfm, p);
- p = transform_perspective(&worldtondc, p);
- if(p.z >= -margin) {
+
+ float4 b = make_float4(p.x, p.y, p.z, 1.0f);
+ float4 c = make_float4(dot(worldtondc.x, b),
+ dot(worldtondc.y, b),
+ dot(worldtondc.z, b),
+ dot(worldtondc.w, b));
+ p = float4_to_float3(c / c.w);
+ if(c.z < 0.0f) {
+ p.x = 1.0f - p.x;
+ p.y = 1.0f - p.y;
+ }
+ if(c.z >= -margin) {
all_behind = false;
}
bb_min = min(bb_min, p);
@@ -319,16 +329,18 @@ Object *BlenderSync::sync_object(BL::Object& b_parent,
/* object transformation */
if(tfm != object->tfm) {
VLOG(1) << "Object " << b_ob.name() << " motion detected.";
- if(motion_time == -1.0f) {
- object->motion.pre = tfm;
- object->use_motion = true;
- }
- else if(motion_time == 1.0f) {
- object->motion.post = tfm;
+ if(motion_time == -1.0f || motion_time == 1.0f) {
object->use_motion = true;
}
}
+ if(motion_time == -1.0f) {
+ object->motion.pre = tfm;
+ }
+ else if(motion_time == 1.0f) {
+ object->motion.post = tfm;
+ }
+
/* mesh deformation */
if(object->mesh)
sync_mesh_motion(b_ob, object, motion_time);
@@ -385,8 +397,8 @@ Object *BlenderSync::sync_object(BL::Object& b_parent,
object->name = b_ob.name().c_str();
object->pass_id = b_ob.pass_index();
object->tfm = tfm;
- object->motion.pre = tfm;
- object->motion.post = tfm;
+ object->motion.pre = transform_empty();
+ object->motion.post = transform_empty();
object->use_motion = false;
/* motion blur */
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 64559804ccb..534bc6cc897 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -64,6 +64,14 @@ static VolumeInterpolation get_volume_interpolation(PointerRNA& ptr)
VOLUME_INTERPOLATION_LINEAR);
}
+static DisplacementMethod get_displacement_method(PointerRNA& ptr)
+{
+ return (DisplacementMethod)get_enum(ptr,
+ "displacement_method",
+ DISPLACE_NUM_METHODS,
+ DISPLACE_BUMP);
+}
+
static int validate_enum_value(int value, int num_values, int default_value)
{
if(value >= num_values) {
@@ -837,8 +845,10 @@ static ShaderNode *add_node(Scene *scene,
}
}
- if(node)
+ if(node) {
+ node->name = b_node.name();
graph->add(node);
+ }
return node;
}
@@ -1180,6 +1190,7 @@ void BlenderSync::sync_materials(bool update_all)
shader->heterogeneous_volume = !get_boolean(cmat, "homogeneous_volume");
shader->volume_sampling_method = get_volume_sampling(cmat);
shader->volume_interpolation_method = get_volume_interpolation(cmat);
+ shader->displacement_method = (experimental) ? get_displacement_method(cmat) : DISPLACE_BUMP;
shader->set_graph(graph);
shader->tag_update(scene);
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 188d23d0c59..d5dbaba094b 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -45,14 +45,39 @@ static inline BL::Mesh object_to_mesh(BL::BlendData& data,
BL::Scene& scene,
bool apply_modifiers,
bool render,
- bool calc_undeformed)
+ bool calc_undeformed,
+ bool subdivision)
{
+ bool subsurf_mod_show_render;
+ bool subsurf_mod_show_viewport;
+
+ if(subdivision) {
+ BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
+
+ subsurf_mod_show_render = subsurf_mod.show_render();
+ subsurf_mod_show_viewport = subsurf_mod.show_render();
+
+ subsurf_mod.show_render(false);
+ subsurf_mod.show_viewport(false);
+
+ }
+
BL::Mesh me = data.meshes.new_from_object(scene, object, apply_modifiers, (render)? 2: 1, false, calc_undeformed);
+
+ if(subdivision) {
+ BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
+
+ subsurf_mod.show_render(subsurf_mod_show_render);
+ subsurf_mod.show_viewport(subsurf_mod_show_viewport);
+ }
+
if((bool)me) {
if(me.use_auto_smooth()) {
me.calc_normals_split();
}
- me.calc_tessface(true);
+ if(!subdivision) {
+ me.calc_tessface(true);
+ }
}
return me;
}
diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp
index 2d404918a38..a85f34082db 100644
--- a/intern/cycles/device/device_cuda.cpp
+++ b/intern/cycles/device/device_cuda.cpp
@@ -249,121 +249,161 @@ public:
return DebugFlags().cuda.adaptive_compile;
}
+ /* Common NVCC flags which stays the same regardless of shading model,
+ * kernel sources md5 and only depends on compiler or compilation settings.
+ */
+ string compile_kernel_get_common_cflags(
+ const DeviceRequestedFeatures& requested_features)
+ {
+ const int cuda_version = cuewCompilerVersion();
+ const int machine = system_cpu_bits();
+ const string kernel_path = path_get("kernel");
+ const string include = kernel_path;
+ string cflags = string_printf("-m%d "
+ "--ptxas-options=\"-v\" "
+ "--use_fast_math "
+ "-DNVCC "
+ "-D__KERNEL_CUDA_VERSION__=%d "
+ "-I\"%s\"",
+ machine,
+ cuda_version,
+ include.c_str());
+ if(use_adaptive_compilation()) {
+ cflags += " " + requested_features.get_build_options();
+ }
+ const char *extra_cflags = getenv("CYCLES_CUDA_EXTRA_CFLAGS");
+ if(extra_cflags) {
+ cflags += string(" ") + string(extra_cflags);
+ }
+#ifdef WITH_CYCLES_DEBUG
+ cflags += " -D__KERNEL_DEBUG__";
+#endif
+ return cflags;
+ }
+
+ bool compile_check_compiler() {
+ const char *nvcc = cuewCompilerPath();
+ if(nvcc == NULL) {
+ cuda_error_message("CUDA nvcc compiler not found. "
+ "Install CUDA toolkit in default location.");
+ return false;
+ }
+ const int cuda_version = cuewCompilerVersion();
+ VLOG(1) << "Found nvcc " << nvcc
+ << ", CUDA version " << cuda_version
+ << ".";
+ const int major = cuda_version / 10, minor = cuda_version & 10;
+ if(cuda_version == 0) {
+ cuda_error_message("CUDA nvcc compiler version could not be parsed.");
+ return false;
+ }
+ if(cuda_version < 75) {
+ printf("Unsupported CUDA version %d.%d detected, "
+ "you need CUDA 7.5 or newer.\n",
+ major, minor);
+ return false;
+ }
+ else if(cuda_version != 75 && cuda_version != 80) {
+ printf("CUDA version %d.%d detected, build may succeed but only "
+ "CUDA 7.5 and 8.0 are officially supported.\n",
+ major, minor);
+ }
+ return true;
+ }
+
string compile_kernel(const DeviceRequestedFeatures& requested_features)
{
/* Compute cubin name. */
int major, minor;
cuDeviceComputeCapability(&major, &minor, cuDevId);
- string cubin;
-
- /* Adaptive Compile.
- * If enabled, always use that */
- bool use_adaptive_compile = use_adaptive_compilation();
/* Attempt to use kernel provided with Blender. */
- if(!use_adaptive_compile) {
- cubin = path_get(string_printf("lib/kernel_sm_%d%d.cubin", major, minor));
- VLOG(1) << "Testing for pre-compiled kernel " << cubin;
+ if(!use_adaptive_compilation()) {
+ const string cubin = path_get(string_printf("lib/kernel_sm_%d%d.cubin",
+ major, minor));
+ VLOG(1) << "Testing for pre-compiled kernel " << cubin << ".";
if(path_exists(cubin)) {
- VLOG(1) << "Using precompiled kernel";
+ VLOG(1) << "Using precompiled kernel.";
return cubin;
}
}
+ const string common_cflags =
+ compile_kernel_get_common_cflags(requested_features);
+
/* Try to use locally compiled kernel. */
- string kernel_path = path_get("kernel");
- string md5 = path_files_md5_hash(kernel_path);
-
- string feature_build_options;
- if(use_adaptive_compile) {
- feature_build_options = requested_features.get_build_options();
- string device_md5 = util_md5_string(feature_build_options);
- cubin = string_printf("cycles_kernel_%s_sm%d%d_%s.cubin",
- device_md5.c_str(),
- major, minor,
- md5.c_str());
- }
- else {
- cubin = string_printf("cycles_kernel_sm%d%d_%s.cubin", major, minor, md5.c_str());
- }
+ const string kernel_path = path_get("kernel");
+ const string kernel_md5 = path_files_md5_hash(kernel_path);
+
+ /* We include cflags into md5 so changing cuda toolkit or changing other
+ * compiler command line arguments makes sure cubin gets re-built.
+ */
+ const string cubin_md5 = util_md5_string(kernel_md5 + common_cflags);
- cubin = path_user_get(path_join("cache", cubin));
- VLOG(1) << "Testing for locally compiled kernel " << cubin;
- /* If exists already, use it. */
+ const string cubin_file = string_printf("cycles_kernel_sm%d%d_%s.cubin",
+ major, minor,
+ cubin_md5.c_str());
+ const string cubin = path_user_get(path_join("cache", cubin_file));
+ VLOG(1) << "Testing for locally compiled kernel " << cubin << ".";
if(path_exists(cubin)) {
- VLOG(1) << "Using locally compiled kernel";
+ VLOG(1) << "Using locally compiled kernel.";
return cubin;
}
#ifdef _WIN32
if(have_precompiled_kernels()) {
- if(major < 2)
- cuda_error_message(string_printf("CUDA device requires compute capability 2.0 or up, found %d.%d. Your GPU is not supported.", major, minor));
- else
- cuda_error_message(string_printf("CUDA binary kernel for this graphics card compute capability (%d.%d) not found.", major, minor));
+ if(major < 2) {
+ cuda_error_message(string_printf(
+ "CUDA device requires compute capability 2.0 or up, "
+ "found %d.%d. Your GPU is not supported.",
+ major, minor));
+ }
+ else {
+ cuda_error_message(string_printf(
+ "CUDA binary kernel for this graphics card compute "
+ "capability (%d.%d) not found.",
+ major, minor));
+ }
return "";
}
#endif
- /* If not, find CUDA compiler. */
- const char *nvcc = cuewCompilerPath();
-
- if(nvcc == NULL) {
- cuda_error_message("CUDA nvcc compiler not found. Install CUDA toolkit in default location.");
- return "";
- }
-
- int cuda_version = cuewCompilerVersion();
- VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << cuda_version;
-
- if(cuda_version == 0) {
- cuda_error_message("CUDA nvcc compiler version could not be parsed.");
- return "";
- }
- if(cuda_version < 60) {
- printf("Unsupported CUDA version %d.%d detected, you need CUDA 7.5.\n", cuda_version/10, cuda_version%10);
+ /* Compile. */
+ if(!compile_check_compiler()) {
return "";
}
- else if(cuda_version != 75)
- printf("CUDA version %d.%d detected, build may succeed but only CUDA 7.5 is officially supported.\n", cuda_version/10, cuda_version%10);
-
- /* Compile. */
- string kernel = path_join(kernel_path, path_join("kernels", path_join("cuda", "kernel.cu")));
- string include = kernel_path;
- const int machine = system_cpu_bits();
-
+ const char *nvcc = cuewCompilerPath();
+ const string kernel = path_join(kernel_path,
+ path_join("kernels",
+ path_join("cuda", "kernel.cu")));
double starttime = time_dt();
printf("Compiling CUDA kernel ...\n");
path_create_directories(cubin);
- string command = string_printf("\"%s\" -arch=sm_%d%d -m%d --cubin \"%s\" "
- "-o \"%s\" --ptxas-options=\"-v\" --use_fast_math -I\"%s\" "
- "-DNVCC -D__KERNEL_CUDA_VERSION__=%d",
- nvcc, major, minor, machine, kernel.c_str(), cubin.c_str(), include.c_str(), cuda_version);
-
- if(use_adaptive_compile)
- command += " " + feature_build_options;
-
- const char* extra_cflags = getenv("CYCLES_CUDA_EXTRA_CFLAGS");
- if(extra_cflags) {
- command += string(" ") + string(extra_cflags);
- }
-
-#ifdef WITH_CYCLES_DEBUG
- command += " -D__KERNEL_DEBUG__";
-#endif
+ string command = string_printf("\"%s\" "
+ "-arch=sm_%d%d "
+ "--cubin \"%s\" "
+ "-o \"%s\" "
+ "%s ",
+ nvcc,
+ major, minor,
+ kernel.c_str(),
+ cubin.c_str(),
+ common_cflags.c_str());
printf("%s\n", command.c_str());
if(system(command.c_str()) == -1) {
- cuda_error_message("Failed to execute compilation command, see console for details.");
+ cuda_error_message("Failed to execute compilation command, "
+ "see console for details.");
return "";
}
/* Verify if compilation succeeded */
if(!path_exists(cubin)) {
- cuda_error_message("CUDA kernel compilation failed, see console for details.");
+ cuda_error_message("CUDA kernel compilation failed, "
+ "see console for details.");
return "";
}
@@ -964,11 +1004,11 @@ public:
if(!background) {
PixelMem pmem = pixel_mem_map[mem];
CUdeviceptr buffer;
-
+
size_t bytes;
cuda_assert(cuGraphicsMapResources(1, &pmem.cuPBOresource, 0));
cuda_assert(cuGraphicsResourceGetMappedPointer(&buffer, &bytes, pmem.cuPBOresource));
-
+
return buffer;
}
@@ -1000,9 +1040,9 @@ public:
glBufferData(GL_PIXEL_UNPACK_BUFFER, pmem.w*pmem.h*sizeof(GLhalf)*4, NULL, GL_DYNAMIC_DRAW);
else
glBufferData(GL_PIXEL_UNPACK_BUFFER, pmem.w*pmem.h*sizeof(uint8_t)*4, NULL, GL_DYNAMIC_DRAW);
-
+
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-
+
glGenTextures(1, &pmem.cuTexId);
glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
if(mem.data_type == TYPE_HALF)
@@ -1012,7 +1052,7 @@ public:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
-
+
CUresult result = cuGraphicsGLRegisterBuffer(&pmem.cuPBOresource, pmem.cuPBO, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE);
if(result == CUDA_SUCCESS) {
@@ -1114,9 +1154,9 @@ public:
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)offset);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-
+
glEnable(GL_TEXTURE_2D);
-
+
if(transparent) {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
@@ -1181,7 +1221,7 @@ public:
if(transparent)
glDisable(GL_BLEND);
-
+
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
@@ -1197,12 +1237,12 @@ public:
{
if(task->type == DeviceTask::PATH_TRACE) {
RenderTile tile;
-
+
bool branched = task->integrator_branched;
/* Upload Bindless Mapping */
load_bindless_mapping();
-
+
/* keep rendering tiles until done */
while(task->acquire_tile(this, tile)) {
int start_sample = tile.start_sample;
@@ -1339,7 +1379,7 @@ void device_cuda_info(vector<DeviceInfo>& devices)
fprintf(stderr, "CUDA cuDeviceGetCount: %s\n", cuewErrorString(result));
return;
}
-
+
vector<DeviceInfo> display_devices;
for(int num = 0; num < count; num++) {
diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp
index 50490f3a20e..5c05aeb5569 100644
--- a/intern/cycles/device/device_opencl.cpp
+++ b/intern/cycles/device/device_opencl.cpp
@@ -875,6 +875,7 @@ public:
if(ciErr != CL_SUCCESS) {
opencl_error("OpenCL build failed: errors in console");
+ fprintf(stderr, "Build error: %s\n", clewErrorString(ciErr));
return false;
}
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index bd3969b2889..7bef247d3bd 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -87,6 +87,7 @@ set(SRC_KERNELS_CPU_HEADERS
)
set(SRC_CLOSURE_HEADERS
+ closure/alloc.h
closure/bsdf.h
closure/bsdf_ashikhmin_velvet.h
closure/bsdf_diffuse.h
@@ -140,6 +141,7 @@ set(SRC_SVM_HEADERS
svm/svm_noisetex.h
svm/svm_normal.h
svm/svm_ramp.h
+ svm/svm_ramp_util.h
svm/svm_sepcomb_hsv.h
svm/svm_sepcomb_vector.h
svm/svm_sky.h
@@ -160,7 +162,9 @@ set(SRC_GEOM_HEADERS
geom/geom_motion_curve.h
geom/geom_motion_triangle.h
geom/geom_object.h
+ geom/geom_patch.h
geom/geom_primitive.h
+ geom/geom_subd_triangle.h
geom/geom_triangle.h
geom/geom_triangle_intersect.h
geom/geom_volume.h
@@ -241,12 +245,20 @@ if(WITH_CYCLES_CUDA_BINARIES)
set(cuda_debug_flags "")
endif()
- set(cuda_version_flags "-D__KERNEL_CUDA_VERSION__=${CUDA_VERSION}")
+ set(cuda_nvcc_command ${CUDA_NVCC_EXECUTABLE})
+ set(cuda_nvcc_version ${CUDA_VERSION})
+
+ if(DEFINED CUDA_NVCC8_EXECUTABLE AND ((${arch} STREQUAL "sm_60") OR (${arch} STREQUAL "sm_61")))
+ set(cuda_nvcc_command ${CUDA_NVCC8_EXECUTABLE})
+ set(cuda_nvcc_version "80")
+ endif()
+
+ set(cuda_version_flags "-D__KERNEL_CUDA_VERSION__=${cuda_nvcc_version}")
set(cuda_math_flags "--use_fast_math")
add_custom_command(
OUTPUT ${cuda_cubin}
- COMMAND ${CUDA_NVCC_EXECUTABLE}
+ COMMAND ${cuda_nvcc_command}
-arch=${arch}
${CUDA_NVCC_FLAGS}
-m${CUDA_BITS}
@@ -263,7 +275,6 @@ if(WITH_CYCLES_CUDA_BINARIES)
-DCCL_NAMESPACE_BEGIN=
-DCCL_NAMESPACE_END=
-DNVCC
-
DEPENDS ${cuda_sources})
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${cuda_cubin}" ${CYCLES_INSTALL_PATH}/lib)
@@ -271,6 +282,9 @@ if(WITH_CYCLES_CUDA_BINARIES)
unset(cuda_extra_flags)
unset(cuda_debug_flags)
+
+ unset(cuda_nvcc_command)
+ unset(cuda_nvcc_version)
endmacro()
foreach(arch ${CYCLES_CUDA_BINARIES_ARCH})
diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h
index b27afaa9869..e9eeff31ecc 100644
--- a/intern/cycles/kernel/bvh/bvh_shadow_all.h
+++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h
@@ -37,11 +37,16 @@
*
*/
-ccl_device bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect_array,
- const uint max_hits,
- uint *num_hits)
+#ifndef __KERNEL_GPU__
+ccl_device
+#else
+ccl_device_inline
+#endif
+bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
+ const Ray *ray,
+ Intersection *isect_array,
+ const uint max_hits,
+ uint *num_hits)
{
/* todo:
* - likely and unlikely for if() statements
@@ -254,9 +259,6 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
/* shadow ray early termination */
if(hit) {
- /* Update number of hits now, so we do proper check on max bounces. */
- (*num_hits)++;
-
/* detect if this surface has a shader with transparent shadows */
/* todo: optimize so primitive visibility flag indicates if
@@ -283,15 +285,18 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
return true;
}
/* if maximum number of hits reached, block all light */
- else if(*num_hits >= max_hits) {
+ else if(*num_hits == max_hits) {
return true;
}
+ /* move on to next entry in intersections array */
+ isect_array++;
+ (*num_hits)++;
#if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
#endif
- /* Move on to next entry in intersections array */
- isect_array++;
+
+ isect_array->t = isect_t;
}
prim_addr++;
diff --git a/intern/cycles/kernel/bvh/bvh_subsurface.h b/intern/cycles/kernel/bvh/bvh_subsurface.h
index 18978efcfa3..d9623c94b2e 100644
--- a/intern/cycles/kernel/bvh/bvh_subsurface.h
+++ b/intern/cycles/kernel/bvh/bvh_subsurface.h
@@ -35,12 +35,17 @@
*
*/
-ccl_device void BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
- const Ray *ray,
- SubsurfaceIntersection *ss_isect,
- int subsurface_object,
- uint *lcg_state,
- int max_hits)
+#ifndef __KERNEL_GPU__
+ccl_device
+#else
+ccl_device_inline
+#endif
+void BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
+ const Ray *ray,
+ SubsurfaceIntersection *ss_isect,
+ int subsurface_object,
+ uint *lcg_state,
+ int max_hits)
{
/* todo:
* - test if pushing distance on the stack helps (for non shadow rays)
diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h
index 68a11b65ad7..b1a52968a26 100644
--- a/intern/cycles/kernel/bvh/bvh_traversal.h
+++ b/intern/cycles/kernel/bvh/bvh_traversal.h
@@ -40,16 +40,21 @@
*
*/
-ccl_device bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility
+#ifndef __KERNEL_GPU__
+ccl_device
+#else
+ccl_device_inline
+#endif
+bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
+ const Ray *ray,
+ Intersection *isect,
+ const uint visibility
#if BVH_FEATURE(BVH_HAIR_MINIMUM_WIDTH)
- , uint *lcg_state,
- float difl,
- float extmax
+ , uint *lcg_state,
+ float difl,
+ float extmax
#endif
- )
+ )
{
/* todo:
* - test if pushing distance on the stack helps (for non shadow rays)
diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h
index 03499e94347..107373c17dc 100644
--- a/intern/cycles/kernel/bvh/bvh_volume.h
+++ b/intern/cycles/kernel/bvh/bvh_volume.h
@@ -36,10 +36,15 @@
*
*/
-ccl_device bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility)
+#ifndef __KERNEL_GPU__
+ccl_device
+#else
+ccl_device_inline
+#endif
+bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
+ const Ray *ray,
+ Intersection *isect,
+ const uint visibility)
{
/* todo:
* - test if pushing distance on the stack helps (for non shadow rays)
diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h
index d7f6bf86c71..1f6515c9862 100644
--- a/intern/cycles/kernel/bvh/bvh_volume_all.h
+++ b/intern/cycles/kernel/bvh/bvh_volume_all.h
@@ -36,11 +36,16 @@
*
*/
-ccl_device uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect_array,
- const uint max_hits,
- const uint visibility)
+#ifndef __KERNEL_GPU__
+ccl_device
+#else
+ccl_device_inline
+#endif
+uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
+ const Ray *ray,
+ Intersection *isect_array,
+ const uint max_hits,
+ const uint visibility)
{
/* todo:
* - test if pushing distance on the stack helps (for non shadow rays)
@@ -201,12 +206,14 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
object,
prim_addr);
if(hit) {
- /* Update number of hits now, so we do proper check on max bounces. */
+ /* Move on to next entry in intersections array. */
+ isect_array++;
num_hits++;
#if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
#endif
- if(num_hits >= max_hits) {
+ isect_array->t = isect_t;
+ if(num_hits == max_hits) {
#if BVH_FEATURE(BVH_INSTANCING)
# if BVH_FEATURE(BVH_MOTION)
float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
@@ -220,9 +227,6 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
#endif /* BVH_FEATURE(BVH_INSTANCING) */
return num_hits;
}
- /* Move on to next entry in intersections array */
- isect_array++;
- isect_array->t = isect_t;
}
}
break;
@@ -247,12 +251,14 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
object,
prim_addr);
if(hit) {
- /* Update number of hits now, so we do proper check on max bounces. */
+ /* Move on to next entry in intersections array. */
+ isect_array++;
num_hits++;
# if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
# endif
- if(num_hits >= max_hits) {
+ isect_array->t = isect_t;
+ if(num_hits == max_hits) {
# if BVH_FEATURE(BVH_INSTANCING)
# if BVH_FEATURE(BVH_MOTION)
float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
@@ -266,9 +272,6 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
# endif /* BVH_FEATURE(BVH_INSTANCING) */
return num_hits;
}
- /* Move on to next entry in intersections array */
- isect_array++;
- isect_array->t = isect_t;
}
}
break;
diff --git a/intern/cycles/kernel/bvh/qbvh_shadow_all.h b/intern/cycles/kernel/bvh/qbvh_shadow_all.h
index eb98eaf7455..3a728b388eb 100644
--- a/intern/cycles/kernel/bvh/qbvh_shadow_all.h
+++ b/intern/cycles/kernel/bvh/qbvh_shadow_all.h
@@ -337,9 +337,6 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
/* Shadow ray early termination. */
if(hit) {
- /* Update number of hits now, so we do proper check on max bounces. */
- (*num_hits)++;
-
/* detect if this surface has a shader with transparent shadows */
/* todo: optimize so primitive visibility flag indicates if
@@ -366,15 +363,17 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
return true;
}
/* if maximum number of hits reached, block all light */
- else if(*num_hits >= max_hits) {
+ else if(*num_hits == max_hits) {
return true;
}
+ /* move on to next entry in intersections array */
+ isect_array++;
+ (*num_hits)++;
#if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
#endif
- /* Move on to next entry in intersections array */
- isect_array++;
+
isect_array->t = isect_t;
}
diff --git a/intern/cycles/kernel/bvh/qbvh_volume_all.h b/intern/cycles/kernel/bvh/qbvh_volume_all.h
index 90cad9d91c0..4d3028b37bf 100644
--- a/intern/cycles/kernel/bvh/qbvh_volume_all.h
+++ b/intern/cycles/kernel/bvh/qbvh_volume_all.h
@@ -268,12 +268,14 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
/* Intersect ray against primitive. */
hit = triangle_intersect(kg, &isect_precalc, isect_array, P, visibility, object, prim_addr);
if(hit) {
- /* Update number of hits now, so we do proper check on max bounces. */
+ /* Move on to next entry in intersections array. */
+ isect_array++;
num_hits++;
#if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
#endif
- if(num_hits >= max_hits) {
+ isect_array->t = isect_t;
+ if(num_hits == max_hits) {
#if BVH_FEATURE(BVH_INSTANCING)
# if BVH_FEATURE(BVH_MOTION)
float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
@@ -287,9 +289,6 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
#endif /* BVH_FEATURE(BVH_INSTANCING) */
return num_hits;
}
- /* Move on to next entry in intersections array */
- isect_array++;
- isect_array->t = isect_t;
}
}
break;
@@ -307,12 +306,14 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
/* Intersect ray against primitive. */
hit = motion_triangle_intersect(kg, isect_array, P, dir, ray->time, visibility, object, prim_addr);
if(hit) {
- /* Update number of hits now, so we do proper check on max bounces. */
+ /* Move on to next entry in intersections array. */
+ isect_array++;
num_hits++;
# if BVH_FEATURE(BVH_INSTANCING)
num_hits_in_instance++;
# endif
- if(num_hits >= max_hits) {
+ isect_array->t = isect_t;
+ if(num_hits == max_hits) {
# if BVH_FEATURE(BVH_INSTANCING)
# if BVH_FEATURE(BVH_MOTION)
float t_fac = 1.0f / len(transform_direction(&ob_itfm, dir));
@@ -326,9 +327,6 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
# endif /* BVH_FEATURE(BVH_INSTANCING) */
return num_hits;
}
- /* Move on to next entry in intersections array */
- isect_array++;
- isect_array->t = isect_t;
}
}
break;
diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h
new file mode 100644
index 00000000000..b7abc1ec507
--- /dev/null
+++ b/intern/cycles/kernel/closure/alloc.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+ccl_device ShaderClosure *closure_alloc(ShaderData *sd, int size, ClosureType type, float3 weight)
+{
+ kernel_assert(size <= sizeof(ShaderClosure));
+
+ int num_closure = ccl_fetch(sd, num_closure);
+ int num_closure_extra = ccl_fetch(sd, num_closure_extra);
+ if(num_closure + num_closure_extra >= MAX_CLOSURE)
+ return NULL;
+
+ ShaderClosure *sc = &ccl_fetch(sd, closure)[num_closure];
+
+ sc->type = type;
+ sc->weight = weight;
+
+ ccl_fetch(sd, num_closure)++;
+
+ return sc;
+}
+
+ccl_device ccl_addr_space void *closure_alloc_extra(ShaderData *sd, int size)
+{
+ /* Allocate extra space for closure that need more parameters. We allocate
+ * in chunks of sizeof(ShaderClosure) starting from the end of the closure
+ * array.
+ *
+ * This lets us keep the same fast array iteration over closures, as we
+ * found linked list iteration and iteration with skipping to be slower. */
+ int num_extra = ((size + sizeof(ShaderClosure) - 1) / sizeof(ShaderClosure));
+ int num_closure = ccl_fetch(sd, num_closure);
+ int num_closure_extra = ccl_fetch(sd, num_closure_extra) + num_extra;
+
+ if(num_closure + num_closure_extra > MAX_CLOSURE) {
+ /* Remove previous closure. */
+ ccl_fetch(sd, num_closure)--;
+ ccl_fetch(sd, num_closure_extra)++;
+ return NULL;
+ }
+
+ ccl_fetch(sd, num_closure_extra) = num_closure_extra;
+ return (ccl_addr_space void*)(ccl_fetch(sd, closure) + MAX_CLOSURE - num_closure_extra);
+}
+
+ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 weight)
+{
+ ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
+
+ if(!sc)
+ return NULL;
+
+ float sample_weight = fabsf(average(weight));
+ sc->sample_weight = sample_weight;
+ return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? sc : NULL;
+}
+
+#ifdef __OSL__
+ccl_device_inline ShaderClosure *bsdf_alloc_osl(ShaderData *sd, int size, float3 weight, void *data)
+{
+ ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
+
+ if(!sc)
+ return NULL;
+
+ memcpy(sc, data, size);
+
+ float sample_weight = fabsf(average(weight));
+ sc->weight = weight;
+ sc->sample_weight = sample_weight;
+ return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? sc : NULL;
+}
+#endif
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index f318a61f3a3..1e7fbdb5450 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -36,15 +36,18 @@
CCL_NAMESPACE_BEGIN
-ccl_device int bsdf_sample(KernelGlobals *kg, ShaderData *sd, const ShaderClosure *sc, float randu, float randv, float3 *eval, float3 *omega_in, differential3 *domega_in, float *pdf)
+ccl_device_inline int bsdf_sample(KernelGlobals *kg,
+ ShaderData *sd,
+ const ShaderClosure *sc,
+ float randu,
+ float randv,
+ float3 *eval,
+ float3 *omega_in,
+ differential3 *domega_in,
+ float *pdf)
{
int label;
-#ifdef __OSL__
- if(kg->osl && sc->prim)
- return OSLShader::bsdf_sample(sd, sc, randu, randv, *eval, *omega_in, *domega_in, *pdf);
-#endif
-
switch(sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
case CLOSURE_BSDF_BSSRDF_ID:
@@ -56,14 +59,16 @@ ccl_device int bsdf_sample(KernelGlobals *kg, ShaderData *sd, const ShaderClosur
label = bsdf_oren_nayar_sample(sc, ccl_fetch(sd, Ng), ccl_fetch(sd, I), ccl_fetch(sd, dI).dx, ccl_fetch(sd, dI).dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
break;
- /*case CLOSURE_BSDF_PHONG_RAMP_ID:
+#ifdef __OSL__
+ case CLOSURE_BSDF_PHONG_RAMP_ID:
label = bsdf_phong_ramp_sample(sc, ccl_fetch(sd, Ng), ccl_fetch(sd, I), ccl_fetch(sd, dI).dx, ccl_fetch(sd, dI).dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
label = bsdf_diffuse_ramp_sample(sc, ccl_fetch(sd, Ng), ccl_fetch(sd, I), ccl_fetch(sd, dI).dx, ccl_fetch(sd, dI).dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
- break;*/
+ break;
+#endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
label = bsdf_translucent_sample(sc, ccl_fetch(sd, Ng), ccl_fetch(sd, I), ccl_fetch(sd, dI).dx, ccl_fetch(sd, dI).dy, randu, randv,
eval, omega_in, &domega_in->dx, &domega_in->dy, pdf);
@@ -139,15 +144,19 @@ ccl_device int bsdf_sample(KernelGlobals *kg, ShaderData *sd, const ShaderClosur
return label;
}
-ccl_device float3 bsdf_eval(KernelGlobals *kg, ShaderData *sd, const ShaderClosure *sc, const float3 omega_in, float *pdf)
+#ifndef __KERNEL_CUDA__
+ccl_device
+#else
+ccl_device_inline
+#endif
+float3 bsdf_eval(KernelGlobals *kg,
+ ShaderData *sd,
+ const ShaderClosure *sc,
+ const float3 omega_in,
+ float *pdf)
{
float3 eval;
-#ifdef __OSL__
- if(kg->osl && sc->prim)
- return OSLShader::bsdf_eval(sd, sc, omega_in, *pdf);
-#endif
-
if(dot(ccl_fetch(sd, Ng), omega_in) >= 0.0f) {
switch(sc->type) {
case CLOSURE_BSDF_DIFFUSE_ID:
@@ -158,12 +167,14 @@ ccl_device float3 bsdf_eval(KernelGlobals *kg, ShaderData *sd, const ShaderClosu
case CLOSURE_BSDF_OREN_NAYAR_ID:
eval = bsdf_oren_nayar_eval_reflect(sc, ccl_fetch(sd, I), omega_in, pdf);
break;
- /*case CLOSURE_BSDF_PHONG_RAMP_ID:
+#ifdef __OSL__
+ case CLOSURE_BSDF_PHONG_RAMP_ID:
eval = bsdf_phong_ramp_eval_reflect(sc, ccl_fetch(sd, I), omega_in, pdf);
break;
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
eval = bsdf_diffuse_ramp_eval_reflect(sc, ccl_fetch(sd, I), omega_in, pdf);
- break;*/
+ break;
+#endif
case CLOSURE_BSDF_TRANSLUCENT_ID:
eval = bsdf_translucent_eval_reflect(sc, ccl_fetch(sd, I), omega_in, pdf);
break;
@@ -296,15 +307,7 @@ ccl_device float3 bsdf_eval(KernelGlobals *kg, ShaderData *sd, const ShaderClosu
ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness)
{
-/* ToDo: do we want to blur volume closures? */
-
-#ifdef __OSL__
- if(kg->osl && sc->prim) {
- OSLShader::bsdf_blur(sc, roughness);
- return;
- }
-#endif
-
+ /* ToDo: do we want to blur volume closures? */
#ifdef __SVM__
switch(sc->type) {
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
@@ -331,5 +334,48 @@ ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness)
#endif
}
+ccl_device bool bsdf_merge(ShaderClosure *a, ShaderClosure *b)
+{
+#ifdef __SVM__
+ switch(a->type) {
+ case CLOSURE_BSDF_TRANSPARENT_ID:
+ return true;
+ case CLOSURE_BSDF_DIFFUSE_ID:
+ case CLOSURE_BSDF_BSSRDF_ID:
+ case CLOSURE_BSDF_TRANSLUCENT_ID:
+ return bsdf_diffuse_merge(a, b);
+ case CLOSURE_BSDF_OREN_NAYAR_ID:
+ return bsdf_oren_nayar_merge(a, b);
+ case CLOSURE_BSDF_REFLECTION_ID:
+ case CLOSURE_BSDF_REFRACTION_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID:
+ case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
+ case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
+ case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID:
+ case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
+ case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID:
+ return bsdf_microfacet_merge(a, b);
+ case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
+ return bsdf_ashikhmin_velvet_merge(a, b);
+ case CLOSURE_BSDF_DIFFUSE_TOON_ID:
+ case CLOSURE_BSDF_GLOSSY_TOON_ID:
+ return bsdf_toon_merge(a, b);
+ case CLOSURE_BSDF_HAIR_REFLECTION_ID:
+ case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
+ return bsdf_hair_merge(a, b);
+#ifdef __VOLUME__
+ case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
+ return volume_henyey_greenstein_merge(a, b);
+#endif
+ default:
+ return false;
+ }
+#endif
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h
index 8d7d533d6f8..9929246ae5c 100644
--- a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h
+++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h
@@ -31,28 +31,30 @@ Other than that, the implementation directly follows the paper.
CCL_NAMESPACE_BEGIN
-ccl_device int bsdf_ashikhmin_shirley_setup(ShaderClosure *sc)
+ccl_device int bsdf_ashikhmin_shirley_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = clamp(sc->data0, 1e-4f, 1.0f);
- sc->data1 = sc->data0;
+ bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
+ bsdf->alpha_y = bsdf->alpha_x;
- sc->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID;
+ bsdf->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device int bsdf_ashikhmin_shirley_aniso_setup(ShaderClosure *sc)
+ccl_device int bsdf_ashikhmin_shirley_aniso_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = clamp(sc->data0, 1e-4f, 1.0f);
- sc->data1 = clamp(sc->data1, 1e-4f, 1.0f);
+ bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
+ bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f);
- sc->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID;
+ bsdf->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
ccl_device void bsdf_ashikhmin_shirley_blur(ShaderClosure *sc, float roughness)
{
- sc->data0 = fmaxf(roughness, sc->data0); /* clamp roughness */
- sc->data1 = fmaxf(roughness, sc->data1);
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)sc;
+
+ bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x);
+ bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float roughness)
@@ -60,16 +62,21 @@ ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float rough
return 2.0f / (roughness*roughness) - 2.0f;
}
-ccl_device float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
+ccl_device_inline float3 bsdf_ashikhmin_shirley_eval_reflect(
+ const ShaderClosure *sc,
+ const float3 I,
+ const float3 omega_in,
+ float *pdf)
{
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float3 N = bsdf->N;
float NdotI = dot(N, I); /* in Cycles/OSL convention I is omega_out */
float NdotO = dot(N, omega_in); /* and consequently we use for O omaga_in ;) */
float out = 0.0f;
- if(fmaxf(sc->data0, sc->data1) <= 1e-4f)
+ if(fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f)
return make_float3(0.0f, 0.0f, 0.0f);
if(NdotI > 0.0f && NdotO > 0.0f) {
@@ -82,8 +89,8 @@ ccl_device float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderClosure *sc, c
float pump = 1.0f / fmaxf(1e-6f, (HdotI*fmaxf(NdotO, NdotI))); /* pump from original paper (first derivative disc., but cancels the HdotI in the pdf nicely) */
/*float pump = 1.0f / fmaxf(1e-4f, ((NdotO + NdotI) * (NdotO*NdotI))); */ /* pump from d-brdf paper */
- float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data0);
- float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data1);
+ float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
+ float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
if(n_x == n_y) {
/* isotropic */
@@ -97,12 +104,18 @@ ccl_device float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderClosure *sc, c
else {
/* anisotropic */
float3 X, Y;
- make_orthonormals_tangent(N, sc->T, &X, &Y);
+ make_orthonormals_tangent(N, bsdf->T, &X, &Y);
float HdotX = dot(H, X);
float HdotY = dot(H, Y);
- float e = (n_x * HdotX*HdotX + n_y * HdotY*HdotY) / (1.0f - HdotN*HdotN);
- float lobe = powf(HdotN, e);
+ float lobe;
+ if(HdotN < 1.0f) {
+ float e = (n_x * HdotX*HdotX + n_y * HdotY*HdotY) / (1.0f - HdotN*HdotN);
+ lobe = powf(HdotN, e);
+ }
+ else {
+ lobe = 1.0f;
+ }
float norm = sqrtf((n_x + 1.0f)*(n_y + 1.0f)) / (8.0f * M_PI_F);
out = NdotO * norm * lobe * pump;
@@ -128,13 +141,14 @@ ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, f
ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float3 N = bsdf->N;
float NdotI = dot(N, I);
if(NdotI > 0.0f) {
- float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data0);
- float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(sc->data1);
+ float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
+ float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
/* get x,y basis on the surface for anisotropy */
float3 X, Y;
@@ -142,7 +156,7 @@ ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, float3 Ng,
if(n_x == n_y)
make_orthonormals(N, &X, &Y);
else
- make_orthonormals_tangent(N, sc->T, &X, &Y);
+ make_orthonormals_tangent(N, bsdf->T, &X, &Y);
/* sample spherical coords for h in tangent space */
float phi;
@@ -193,7 +207,7 @@ ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, float3 Ng,
/* reflect I on H to get omega_in */
*omega_in = -I + (2.0f * HdotI) * H;
- if(fmaxf(sc->data0, sc->data1) <= 1e-4f) {
+ if(fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) {
/* Some high number for MIS. */
*pdf = 1e6f;
*eval = make_float3(1e6f, 1e6f, 1e6f);
diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h
index f1a26650078..7e0f5a7ec75 100644
--- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h
+++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h
@@ -35,20 +35,38 @@
CCL_NAMESPACE_BEGIN
-ccl_device int bsdf_ashikhmin_velvet_setup(ShaderClosure *sc)
+typedef ccl_addr_space struct VelvetBsdf {
+ SHADER_CLOSURE_BASE;
+
+ float sigma;
+ float invsigma2;
+ float3 N;
+} VelvetBsdf;
+
+ccl_device int bsdf_ashikhmin_velvet_setup(VelvetBsdf *bsdf)
{
- float sigma = fmaxf(sc->data0, 0.01f);
- sc->data0 = 1.0f/(sigma * sigma); /* m_invsigma2 */
+ float sigma = fmaxf(bsdf->sigma, 0.01f);
+ bsdf->invsigma2 = 1.0f/(sigma * sigma);
- sc->type = CLOSURE_BSDF_ASHIKHMIN_VELVET_ID;
+ bsdf->type = CLOSURE_BSDF_ASHIKHMIN_VELVET_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
+ccl_device bool bsdf_ashikhmin_velvet_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const VelvetBsdf *bsdf_a = (const VelvetBsdf*)a;
+ const VelvetBsdf *bsdf_b = (const VelvetBsdf*)b;
+
+ return (isequal_float3(bsdf_a->N, bsdf_b->N)) &&
+ (bsdf_a->sigma == bsdf_b->sigma);
+}
+
ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float m_invsigma2 = sc->data0;
- float3 N = sc->N;
+ const VelvetBsdf *bsdf = (const VelvetBsdf*)sc;
+ float m_invsigma2 = bsdf->invsigma2;
+ float3 N = bsdf->N;
float cosNO = dot(N, I);
float cosNI = dot(N, omega_in);
@@ -90,8 +108,9 @@ ccl_device float3 bsdf_ashikhmin_velvet_eval_transmit(const ShaderClosure *sc, c
ccl_device int bsdf_ashikhmin_velvet_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float m_invsigma2 = sc->data0;
- float3 N = sc->N;
+ const VelvetBsdf *bsdf = (const VelvetBsdf*)sc;
+ float m_invsigma2 = bsdf->invsigma2;
+ float3 N = bsdf->N;
// we are viewing the surface from above - send a ray out with uniform
// distribution over the hemisphere
diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h
index 4b29bb096d1..dcd187f9305 100644
--- a/intern/cycles/kernel/closure/bsdf_diffuse.h
+++ b/intern/cycles/kernel/closure/bsdf_diffuse.h
@@ -35,17 +35,31 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct DiffuseBsdf {
+ SHADER_CLOSURE_BASE;
+ float3 N;
+} DiffuseBsdf;
+
/* DIFFUSE */
-ccl_device int bsdf_diffuse_setup(ShaderClosure *sc)
+ccl_device int bsdf_diffuse_setup(DiffuseBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_DIFFUSE_ID;
+ bsdf->type = CLOSURE_BSDF_DIFFUSE_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
+ccl_device bool bsdf_diffuse_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const DiffuseBsdf *bsdf_a = (const DiffuseBsdf*)a;
+ const DiffuseBsdf *bsdf_b = (const DiffuseBsdf*)b;
+
+ return (isequal_float3(bsdf_a->N, bsdf_b->N));
+}
+
ccl_device float3 bsdf_diffuse_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float3 N = sc->N;
+ const DiffuseBsdf *bsdf = (const DiffuseBsdf*)sc;
+ float3 N = bsdf->N;
float cos_pi = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F;
*pdf = cos_pi;
@@ -59,7 +73,8 @@ ccl_device float3 bsdf_diffuse_eval_transmit(const ShaderClosure *sc, const floa
ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float3 N = sc->N;
+ const DiffuseBsdf *bsdf = (const DiffuseBsdf*)sc;
+ float3 N = bsdf->N;
// distribution over the hemisphere
sample_cos_hemisphere(N, randu, randv, omega_in, pdf);
@@ -80,9 +95,9 @@ ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc, float3 Ng, float3 I,
/* TRANSLUCENT */
-ccl_device int bsdf_translucent_setup(ShaderClosure *sc)
+ccl_device int bsdf_translucent_setup(DiffuseBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_TRANSLUCENT_ID;
+ bsdf->type = CLOSURE_BSDF_TRANSLUCENT_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
@@ -93,7 +108,8 @@ ccl_device float3 bsdf_translucent_eval_reflect(const ShaderClosure *sc, const f
ccl_device float3 bsdf_translucent_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float3 N = sc->N;
+ const DiffuseBsdf *bsdf = (const DiffuseBsdf*)sc;
+ float3 N = bsdf->N;
float cos_pi = fmaxf(-dot(N, omega_in), 0.0f) * M_1_PI_F;
*pdf = cos_pi;
@@ -102,7 +118,8 @@ ccl_device float3 bsdf_translucent_eval_transmit(const ShaderClosure *sc, const
ccl_device int bsdf_translucent_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float3 N = sc->N;
+ const DiffuseBsdf *bsdf = (const DiffuseBsdf*)sc;
+ float3 N = bsdf->N;
// we are viewing the surface from the right side - send a ray out with cosine
// distribution over the hemisphere
diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h
index e0287e7655a..2d982a95fe4 100644
--- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h
+++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h
@@ -35,7 +35,16 @@
CCL_NAMESPACE_BEGIN
-ccl_device float3 bsdf_diffuse_ramp_get_color(const ShaderClosure *sc, const float3 colors[8], float pos)
+#ifdef __OSL__
+
+typedef ccl_addr_space struct DiffuseRampBsdf {
+ SHADER_CLOSURE_BASE;
+
+ float3 N;
+ float3 *colors;
+} DiffuseRampBsdf;
+
+ccl_device float3 bsdf_diffuse_ramp_get_color(const float3 colors[8], float pos)
{
int MAXCOLORS = 8;
@@ -49,11 +58,9 @@ ccl_device float3 bsdf_diffuse_ramp_get_color(const ShaderClosure *sc, const flo
return colors[ipos] * (1.0f - offset) + colors[ipos+1] * offset;
}
-ccl_device int bsdf_diffuse_ramp_setup(ShaderClosure *sc)
+ccl_device int bsdf_diffuse_ramp_setup(DiffuseRampBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_DIFFUSE_RAMP_ID;
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
+ bsdf->type = CLOSURE_BSDF_DIFFUSE_RAMP_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
@@ -61,29 +68,31 @@ ccl_device void bsdf_diffuse_ramp_blur(ShaderClosure *sc, float roughness)
{
}
-ccl_device float3 bsdf_diffuse_ramp_eval_reflect(const ShaderClosure *sc, const float3 colors[8], const float3 I, const float3 omega_in, float *pdf)
+ccl_device float3 bsdf_diffuse_ramp_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float3 N = sc->N;
+ const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf*)sc;
+ float3 N = bsdf->N;
float cos_pi = fmaxf(dot(N, omega_in), 0.0f);
*pdf = cos_pi * M_1_PI_F;
- return bsdf_diffuse_ramp_get_color(sc, colors, cos_pi) * M_1_PI_F;
+ return bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F;
}
-ccl_device float3 bsdf_diffuse_ramp_eval_transmit(const ShaderClosure *sc, const float3 colors[8], const float3 I, const float3 omega_in, float *pdf)
+ccl_device float3 bsdf_diffuse_ramp_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
return make_float3(0.0f, 0.0f, 0.0f);
}
-ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, const float3 colors[8], float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float3 N = sc->N;
+ const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf*)sc;
+ float3 N = bsdf->N;
// distribution over the hemisphere
sample_cos_hemisphere(N, randu, randv, omega_in, pdf);
if(dot(Ng, *omega_in) > 0.0f) {
- *eval = bsdf_diffuse_ramp_get_color(sc, colors, *pdf * M_PI_F) * M_1_PI_F;
+ *eval = bsdf_diffuse_ramp_get_color(bsdf->colors, *pdf * M_PI_F) * M_1_PI_F;
#ifdef __RAY_DIFFERENTIALS__
*domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
*domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
@@ -95,6 +104,8 @@ ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, const float3 co
return LABEL_REFLECT|LABEL_DIFFUSE;
}
+#endif /* __OSL__ */
+
CCL_NAMESPACE_END
#endif /* __BSDF_DIFFUSE_RAMP_H__ */
diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h
index 1e81617a7d3..bede5f45e7e 100644
--- a/intern/cycles/kernel/closure/bsdf_hair.h
+++ b/intern/cycles/kernel/closure/bsdf_hair.h
@@ -35,29 +35,49 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct HairBsdf {
+ SHADER_CLOSURE_BASE;
-ccl_device int bsdf_hair_reflection_setup(ShaderClosure *sc)
+ float3 T;
+ float roughness1;
+ float roughness2;
+ float offset;
+} HairBsdf;
+
+ccl_device int bsdf_hair_reflection_setup(HairBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_HAIR_REFLECTION_ID;
- sc->data0 = clamp(sc->data0, 0.001f, 1.0f);
- sc->data1 = clamp(sc->data1, 0.001f, 1.0f);
+ bsdf->type = CLOSURE_BSDF_HAIR_REFLECTION_ID;
+ bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f);
+ bsdf->roughness2 = clamp(bsdf->roughness2, 0.001f, 1.0f);
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device int bsdf_hair_transmission_setup(ShaderClosure *sc)
+ccl_device int bsdf_hair_transmission_setup(HairBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID;
- sc->data0 = clamp(sc->data0, 0.001f, 1.0f);
- sc->data1 = clamp(sc->data1, 0.001f, 1.0f);
+ bsdf->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID;
+ bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f);
+ bsdf->roughness2 = clamp(bsdf->roughness2, 0.001f, 1.0f);
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
+ccl_device bool bsdf_hair_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const HairBsdf *bsdf_a = (const HairBsdf*)a;
+ const HairBsdf *bsdf_b = (const HairBsdf*)b;
+
+ return (isequal_float3(bsdf_a->T, bsdf_b->T)) &&
+ (bsdf_a->roughness1 == bsdf_b->roughness1) &&
+ (bsdf_a->roughness2 == bsdf_b->roughness2) &&
+ (bsdf_a->offset == bsdf_b->offset);
+}
+
ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float offset = sc->data2;
- float3 Tg = sc->T;
- float roughness1 = sc->data0;
- float roughness2 = sc->data1;
+ const HairBsdf *bsdf = (const HairBsdf*)sc;
+ float offset = bsdf->offset;
+ float3 Tg = bsdf->T;
+ float roughness1 = bsdf->roughness1;
+ float roughness2 = bsdf->roughness2;
float Iz = dot(Tg, I);
float3 locy = normalize(I - Tg * Iz);
@@ -107,10 +127,11 @@ ccl_device float3 bsdf_hair_reflection_eval_transmit(const ShaderClosure *sc, co
ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float offset = sc->data2;
- float3 Tg = sc->T;
- float roughness1 = sc->data0;
- float roughness2 = sc->data1;
+ const HairBsdf *bsdf = (const HairBsdf*)sc;
+ float offset = bsdf->offset;
+ float3 Tg = bsdf->T;
+ float roughness1 = bsdf->roughness1;
+ float roughness2 = bsdf->roughness2;
float Iz = dot(Tg, I);
float3 locy = normalize(I - Tg * Iz);
@@ -148,10 +169,11 @@ ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc,
ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float offset = sc->data2;
- float3 Tg = sc->T;
- float roughness1 = sc->data0;
- float roughness2 = sc->data1;
+ const HairBsdf *bsdf = (const HairBsdf*)sc;
+ float offset = bsdf->offset;
+ float3 Tg = bsdf->T;
+ float roughness1 = bsdf->roughness1;
+ float roughness2 = bsdf->roughness2;
float Iz = dot(Tg, I);
float3 locy = normalize(I - Tg * Iz);
float3 locx = cross(locy, Tg);
@@ -198,10 +220,11 @@ ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, float3 Ng, f
ccl_device int bsdf_hair_transmission_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float offset = sc->data2;
- float3 Tg = sc->T;
- float roughness1 = sc->data0;
- float roughness2 = sc->data1;
+ const HairBsdf *bsdf = (const HairBsdf*)sc;
+ float offset = bsdf->offset;
+ float3 Tg = bsdf->T;
+ float roughness1 = bsdf->roughness1;
+ float roughness2 = bsdf->roughness2;
float Iz = dot(Tg, I);
float3 locy = normalize(I - Tg * Iz);
float3 locx = cross(locy, Tg);
diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h
index 7bf7c2806d4..9da73f66da0 100644
--- a/intern/cycles/kernel/closure/bsdf_microfacet.h
+++ b/intern/cycles/kernel/closure/bsdf_microfacet.h
@@ -35,6 +35,19 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct MicrofacetExtra {
+ float3 color;
+} MicrofacetExtra;
+
+typedef ccl_addr_space struct MicrofacetBsdf {
+ SHADER_CLOSURE_BASE;
+
+ float alpha_x, alpha_y, ior;
+ MicrofacetExtra *extra;
+ float3 T;
+ float3 N;
+} MicrofacetBsdf;
+
/* Beckmann and GGX microfacet importance sampling. */
ccl_device_inline void microfacet_beckmann_sample_slopes(
@@ -233,48 +246,66 @@ ccl_device_inline float3 microfacet_sample_stretched(
* Anisotropy is only supported for reflection currently, but adding it for
* transmission is just a matter of copying code from reflection if needed. */
-ccl_device int bsdf_microfacet_ggx_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_ggx_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = saturate(sc->data0); /* alpha_x */
- sc->data1 = sc->data0; /* alpha_y */
+ bsdf->alpha_x = saturate(bsdf->alpha_x);
+ bsdf->alpha_y = bsdf->alpha_x;
- sc->type = CLOSURE_BSDF_MICROFACET_GGX_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device int bsdf_microfacet_ggx_aniso_setup(ShaderClosure *sc)
+ccl_device bool bsdf_microfacet_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const MicrofacetBsdf *bsdf_a = (const MicrofacetBsdf*)a;
+ const MicrofacetBsdf *bsdf_b = (const MicrofacetBsdf*)b;
+
+ return (isequal_float3(bsdf_a->N, bsdf_b->N)) &&
+ (bsdf_a->alpha_x == bsdf_b->alpha_x) &&
+ (bsdf_a->alpha_y == bsdf_b->alpha_y) &&
+ (isequal_float3(bsdf_a->T, bsdf_b->T)) &&
+ (bsdf_a->ior == bsdf_b->ior) &&
+ ((!bsdf_a->extra && !bsdf_b->extra) ||
+ ((bsdf_a->extra && bsdf_b->extra) &&
+ (isequal_float3(bsdf_a->extra->color, bsdf_b->extra->color))));
+}
+
+ccl_device int bsdf_microfacet_ggx_aniso_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = saturate(sc->data0); /* alpha_x */
- sc->data1 = saturate(sc->data1); /* alpha_y */
+ bsdf->alpha_x = saturate(bsdf->alpha_x);
+ bsdf->alpha_y = saturate(bsdf->alpha_y);
- sc->type = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device int bsdf_microfacet_ggx_refraction_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = saturate(sc->data0); /* alpha_x */
- sc->data1 = sc->data0; /* alpha_y */
+ bsdf->alpha_x = saturate(bsdf->alpha_x);
+ bsdf->alpha_y = bsdf->alpha_x;
- sc->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
ccl_device void bsdf_microfacet_ggx_blur(ShaderClosure *sc, float roughness)
{
- sc->data0 = fmaxf(roughness, sc->data0); /* alpha_x */
- sc->data1 = fmaxf(roughness, sc->data1); /* alpha_y */
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)sc;
+
+ bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x);
+ bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float alpha_x = sc->data0;
- float alpha_y = sc->data1;
- bool m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float alpha_x = bsdf->alpha_x;
+ float alpha_y = bsdf->alpha_y;
+ bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
+ float3 N = bsdf->N;
if(m_refractive || alpha_x*alpha_y <= 1e-7f)
return make_float3(0.0f, 0.0f, 0.0f);
@@ -305,7 +336,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, cons
else {
/* anisotropic */
float3 X, Y, Z = N;
- make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* distribution */
float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
@@ -361,11 +392,12 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, cons
ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float alpha_x = sc->data0;
- float alpha_y = sc->data1;
- float m_eta = sc->data2;
- bool m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float alpha_x = bsdf->alpha_x;
+ float alpha_y = bsdf->alpha_y;
+ float m_eta = bsdf->ior;
+ bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
+ float3 N = bsdf->N;
if(!m_refractive || alpha_x*alpha_y <= 1e-7f)
return make_float3(0.0f, 0.0f, 0.0f);
@@ -415,10 +447,11 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, con
ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float alpha_x = sc->data0;
- float alpha_y = sc->data1;
- bool m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float alpha_x = bsdf->alpha_x;
+ float alpha_y = bsdf->alpha_y;
+ bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
+ float3 N = bsdf->N;
float cosNO = dot(N, I);
if(cosNO > 0) {
@@ -427,7 +460,7 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure
if(alpha_x == alpha_y)
make_orthonormals(Z, &X, &Y);
else
- make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* importance sampling with distribution of visible normals. vectors are
* transformed to local space before and after */
@@ -522,7 +555,7 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
- float m_eta = sc->data2, fresnel;
+ float m_eta = bsdf->ior, fresnel;
bool inside;
fresnel = fresnel_dielectric(m_eta, m, I, &R, &T,
@@ -582,37 +615,39 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure
* Microfacet Models for Refraction through Rough Surfaces
* B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007 */
-ccl_device int bsdf_microfacet_beckmann_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_beckmann_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = saturate(sc->data0); /* alpha_x */
- sc->data1 = sc->data0; /* alpha_y */
+ bsdf->alpha_x = saturate(bsdf->alpha_x);
+ bsdf->alpha_y = bsdf->alpha_x;
- sc->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device int bsdf_microfacet_beckmann_aniso_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_beckmann_aniso_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = saturate(sc->data0); /* alpha_x */
- sc->data1 = saturate(sc->data1); /* alpha_y */
+ bsdf->alpha_x = saturate(bsdf->alpha_x);
+ bsdf->alpha_y = saturate(bsdf->alpha_y);
- sc->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device int bsdf_microfacet_beckmann_refraction_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_beckmann_refraction_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = saturate(sc->data0); /* alpha_x */
- sc->data1 = sc->data0; /* alpha_y */
+ bsdf->alpha_x = saturate(bsdf->alpha_x);
+ bsdf->alpha_y = bsdf->alpha_x;
- sc->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
ccl_device void bsdf_microfacet_beckmann_blur(ShaderClosure *sc, float roughness)
{
- sc->data0 = fmaxf(roughness, sc->data0); /* alpha_x */
- sc->data1 = fmaxf(roughness, sc->data1); /* alpha_y */
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)sc;
+
+ bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x);
+ bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
ccl_device_inline float bsdf_beckmann_G1(float alpha, float cos_n)
@@ -647,10 +682,11 @@ ccl_device_inline float bsdf_beckmann_aniso_G1(float alpha_x, float alpha_y, flo
ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float alpha_x = sc->data0;
- float alpha_y = sc->data1;
- bool m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float alpha_x = bsdf->alpha_x;
+ float alpha_y = bsdf->alpha_y;
+ bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
+ float3 N = bsdf->N;
if(m_refractive || alpha_x*alpha_y <= 1e-7f)
return make_float3(0.0f, 0.0f, 0.0f);
@@ -682,7 +718,7 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc,
else {
/* anisotropic */
float3 X, Y, Z = N;
- make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* distribution */
float3 local_m = make_float3(dot(X, m), dot(Y, m), dot(Z, m));
@@ -722,11 +758,12 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc,
ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float alpha_x = sc->data0;
- float alpha_y = sc->data1;
- float m_eta = sc->data2;
- bool m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float alpha_x = bsdf->alpha_x;
+ float alpha_y = bsdf->alpha_y;
+ float m_eta = bsdf->ior;
+ bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
+ float3 N = bsdf->N;
if(!m_refractive || alpha_x*alpha_y <= 1e-7f)
return make_float3(0.0f, 0.0f, 0.0f);
@@ -773,10 +810,11 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc
ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float alpha_x = sc->data0;
- float alpha_y = sc->data1;
- bool m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float alpha_x = bsdf->alpha_x;
+ float alpha_y = bsdf->alpha_y;
+ bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
+ float3 N = bsdf->N;
float cosNO = dot(N, I);
if(cosNO > 0) {
@@ -785,7 +823,7 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, const ShaderCl
if(alpha_x == alpha_y)
make_orthonormals(Z, &X, &Y);
else
- make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
/* importance sampling with distribution of visible normals. vectors are
* transformed to local space before and after */
@@ -872,7 +910,7 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, const ShaderCl
#ifdef __RAY_DIFFERENTIALS__
float3 dRdx, dRdy, dTdx, dTdy;
#endif
- float m_eta = sc->data2, fresnel;
+ float m_eta = bsdf->ior, fresnel;
bool inside;
fresnel = fresnel_dielectric(m_eta, m, I, &R, &T,
diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h
index 51b12fe4e45..df848c3d179 100644
--- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h
+++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h
@@ -42,7 +42,7 @@ ccl_device_inline float D_ggx_aniso(const float3 wm, const float2 alpha)
/* Sample slope distribution (based on page 14 of the supplemental implementation). */
ccl_device_inline float2 mf_sampleP22_11(const float cosI, const float2 randU)
{
- if(cosI > 0.9999f) {
+ if(cosI > 0.9999f || cosI < 1e-6f) {
const float r = sqrtf(randU.x / (1.0f - randU.x));
const float phi = M_2PI_F * randU.y;
return make_float2(r*cosf(phi), r*sinf(phi));
@@ -117,7 +117,7 @@ ccl_device_inline float3 mf_eval_phase_glossy(const float3 w, const float lambda
if(dotW_WH < 0.0f)
return make_float3(0.0f, 0.0f, 0.0f);
- float phase = max(0.0f, dotW_WH) * 0.25f / (pArea * dotW_WH);
+ float phase = max(0.0f, dotW_WH) * 0.25f / max(pArea * dotW_WH, 1e-7f);
if(alpha.x == alpha.y)
phase *= D_ggx(wh, alpha.x);
else
@@ -200,9 +200,9 @@ ccl_device_inline float mf_lambda(const float3 w, const float2 alpha)
if(w.z > 0.9999f)
return 0.0f;
else if(w.z < -0.9999f)
- return -1.0f;
+ return -0.9999f;
- const float inv_wz2 = 1.0f / (w.z*w.z);
+ const float inv_wz2 = 1.0f / max(w.z*w.z, 1e-7f);
const float2 wa = make_float2(w.x, w.y)*alpha;
float v = sqrtf(1.0f + dot(wa, wa) * inv_wz2);
if(w.z <= 0.0f)
@@ -271,7 +271,10 @@ ccl_device_inline float mf_ggx_albedo(float r)
ccl_device_inline float mf_ggx_pdf(const float3 wi, const float3 wo, const float alpha)
{
- return 0.25f * D_ggx(normalize(wi+wo), alpha) / ((1.0f + mf_lambda(wi, make_float2(alpha, alpha))) * wi.z) + (1.0f - mf_ggx_albedo(alpha)) * wo.z;
+ float D = D_ggx(normalize(wi+wo), alpha);
+ float lambda = mf_lambda(wi, make_float2(alpha, alpha));
+ float albedo = mf_ggx_albedo(alpha);
+ return 0.25f * D / max((1.0f + lambda) * wi.z, 1e-7f) + (1.0f - albedo) * wo.z;
}
ccl_device_inline float mf_ggx_aniso_pdf(const float3 wi, const float3 wo, const float2 alpha)
@@ -325,40 +328,42 @@ ccl_device_inline float mf_glass_pdf(const float3 wi, const float3 wo, const flo
ccl_device void bsdf_microfacet_multi_ggx_blur(ShaderClosure *sc, float roughness)
{
- sc->data0 = fmaxf(roughness, sc->data0); /* alpha_x */
- sc->data1 = fmaxf(roughness, sc->data1); /* alpha_y */
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)sc;
+
+ bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x);
+ bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
}
/* === Closure implementations === */
/* Multiscattering GGX Glossy closure */
-ccl_device int bsdf_microfacet_multi_ggx_common_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_multi_ggx_common_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = clamp(sc->data0, 1e-4f, 1.0f); /* alpha */
- sc->data1 = clamp(sc->data1, 1e-4f, 1.0f);
- sc->custom1 = saturate(sc->custom1); /* color */
- sc->custom2 = saturate(sc->custom2);
- sc->custom3 = saturate(sc->custom3);
+ bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
+ bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f);
+ bsdf->extra->color.x = saturate(bsdf->extra->color.x);
+ bsdf->extra->color.y = saturate(bsdf->extra->color.y);
+ bsdf->extra->color.z = saturate(bsdf->extra->color.z);
- sc->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
- return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG|SD_BSDF_HAS_CUSTOM;
+ return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG;
}
-ccl_device int bsdf_microfacet_multi_ggx_aniso_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_multi_ggx_aniso_setup(MicrofacetBsdf *bsdf)
{
- if(is_zero(sc->T))
- sc->T = make_float3(1.0f, 0.0f, 0.0f);
+ if(is_zero(bsdf->T))
+ bsdf->T = make_float3(1.0f, 0.0f, 0.0f);
- return bsdf_microfacet_multi_ggx_common_setup(sc);
+ return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
-ccl_device int bsdf_microfacet_multi_ggx_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_multi_ggx_setup(MicrofacetBsdf *bsdf)
{
- sc->data1 = sc->data0;
+ bsdf->alpha_y = bsdf->alpha_x;
- return bsdf_microfacet_multi_ggx_common_setup(sc);
+ return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, ccl_addr_space uint *lcg_state) {
@@ -367,11 +372,12 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(const ShaderClosure *s
}
ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, ccl_addr_space uint *lcg_state) {
- bool is_aniso = (sc->data0 != sc->data1);
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y);
float3 X, Y, Z;
- Z = sc->N;
+ Z = bsdf->N;
if(is_aniso)
- make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
else
make_orthonormals(Z, &X, &Y);
@@ -379,94 +385,115 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc
float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
if(is_aniso)
- *pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(sc->data0, sc->data1));
+ *pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(bsdf->alpha_x, bsdf->alpha_y));
else
- *pdf = mf_ggx_pdf(localI, localO, sc->data0);
- return mf_eval_glossy(localI, localO, true, make_float3(sc->custom1, sc->custom2, sc->custom3), sc->data0, sc->data1, lcg_state, NULL, NULL);
+ *pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x);
+ return mf_eval_glossy(localI, localO, true, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, NULL, NULL);
}
ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf, ccl_addr_space uint *lcg_state)
{
- bool is_aniso = (sc->data0 != sc->data1);
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y);
float3 X, Y, Z;
- Z = sc->N;
+ Z = bsdf->N;
if(is_aniso)
- make_orthonormals_tangent(Z, sc->T, &X, &Y);
+ make_orthonormals_tangent(Z, bsdf->T, &X, &Y);
else
make_orthonormals(Z, &X, &Y);
float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z));
float3 localO;
- *eval = mf_sample_glossy(localI, &localO, make_float3(sc->custom1, sc->custom2, sc->custom3), sc->data0, sc->data1, lcg_state, NULL, NULL);
+ *eval = mf_sample_glossy(localI, &localO, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, NULL, NULL);
if(is_aniso)
- *pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(sc->data0, sc->data1));
+ *pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(bsdf->alpha_x, bsdf->alpha_y));
else
- *pdf = mf_ggx_pdf(localI, localO, sc->data0);
+ *pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x);
*eval *= *pdf;
*omega_in = X*localO.x + Y*localO.y + Z*localO.z;
+#ifdef __RAY_DIFFERENTIALS__
+ *domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
+ *domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
+#endif
return LABEL_REFLECT|LABEL_GLOSSY;
}
/* Multiscattering GGX Glass closure */
-ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ShaderClosure *sc)
+ccl_device int bsdf_microfacet_multi_ggx_glass_setup(MicrofacetBsdf *bsdf)
{
- sc->data0 = clamp(sc->data0, 1e-4f, 1.0f); /* alpha */
- sc->data1 = sc->data0;
- sc->data2 = max(0.0f, sc->data2); /* ior */
- sc->custom1 = saturate(sc->custom1); /* color */
- sc->custom2 = saturate(sc->custom2);
- sc->custom3 = saturate(sc->custom3);
+ bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
+ bsdf->alpha_y = bsdf->alpha_x;
+ bsdf->ior = max(0.0f, bsdf->ior);
+ bsdf->extra->color.x = saturate(bsdf->extra->color.x);
+ bsdf->extra->color.y = saturate(bsdf->extra->color.y);
+ bsdf->extra->color.z = saturate(bsdf->extra->color.z);
- sc->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID;
+ bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID;
- return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG|SD_BSDF_HAS_CUSTOM;
+ return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG;
}
ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, ccl_addr_space uint *lcg_state) {
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
float3 X, Y, Z;
- Z = sc->N;
+ Z = bsdf->N;
make_orthonormals(Z, &X, &Y);
float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z));
float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
- *pdf = mf_glass_pdf(localI, localO, sc->data0, sc->data2);
- return mf_eval_glass(localI, localO, false, make_float3(sc->custom1, sc->custom2, sc->custom3), sc->data0, sc->data1, lcg_state, sc->data2);
+ *pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior);
+ return mf_eval_glass(localI, localO, false, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior);
}
ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, ccl_addr_space uint *lcg_state) {
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
float3 X, Y, Z;
- Z = sc->N;
+ Z = bsdf->N;
make_orthonormals(Z, &X, &Y);
float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z));
float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z));
- *pdf = mf_glass_pdf(localI, localO, sc->data0, sc->data2);
- return mf_eval_glass(localI, localO, true, make_float3(sc->custom1, sc->custom2, sc->custom3), sc->data0, sc->data1, lcg_state, sc->data2);
+ *pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior);
+ return mf_eval_glass(localI, localO, true, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior);
}
ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf, ccl_addr_space uint *lcg_state)
{
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
float3 X, Y, Z;
- Z = sc->N;
+ Z = bsdf->N;
make_orthonormals(Z, &X, &Y);
float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z));
float3 localO;
- *eval = mf_sample_glass(localI, &localO, make_float3(sc->custom1, sc->custom2, sc->custom3), sc->data0, sc->data1, lcg_state, sc->data2);
- *pdf = mf_glass_pdf(localI, localO, sc->data0, sc->data2);
+ *eval = mf_sample_glass(localI, &localO, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior);
+ *pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior);
*eval *= *pdf;
*omega_in = X*localO.x + Y*localO.y + Z*localO.z;
- if(localO.z*localI.z > 0.0f)
+ if(localO.z*localI.z > 0.0f) {
+#ifdef __RAY_DIFFERENTIALS__
+ *domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx;
+ *domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy;
+#endif
return LABEL_REFLECT|LABEL_GLOSSY;
- else
+ }
+ else {
+#ifdef __RAY_DIFFERENTIALS__
+ float cosI = dot(Z, I);
+ float dnp = max(sqrtf(1.0f - (bsdf->ior * bsdf->ior * (1.0f - cosI*cosI))), 1e-7f);
+ *domega_in_dx = -(bsdf->ior * dIdx) + ((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdx, Z)) * Z;
+ *domega_in_dy = -(bsdf->ior * dIdy) + ((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdy, Z)) * Z;
+#endif
+
return LABEL_TRANSMIT|LABEL_GLOSSY;
+ }
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h
index afd4a8da62a..6ebe2f6a751 100644
--- a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h
+++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h
@@ -25,11 +25,18 @@
* energy is used. In combination with MIS, that is enough to produce an unbiased result, although
* the balance heuristic isn't necessarily optimal anymore.
*/
-ccl_device float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, float3 wo, const bool wo_outside, const float3 color, const float alpha_x, const float alpha_y, ccl_addr_space uint* lcg_state
+ccl_device_inline float3 MF_FUNCTION_FULL_NAME(mf_eval)(
+ float3 wi,
+ float3 wo,
+ const bool wo_outside,
+ const float3 color,
+ const float alpha_x,
+ const float alpha_y,
+ ccl_addr_space uint *lcg_state
#ifdef MF_MULTI_GLASS
- , const float eta
+ , const float eta
#elif defined(MF_MULTI_GLOSSY)
- , float3 *n, float3 *k
+ , float3 *n, float3 *k
#endif
)
{
diff --git a/intern/cycles/kernel/closure/bsdf_oren_nayar.h b/intern/cycles/kernel/closure/bsdf_oren_nayar.h
index 61b7cb11b02..cb342a026ef 100644
--- a/intern/cycles/kernel/closure/bsdf_oren_nayar.h
+++ b/intern/cycles/kernel/closure/bsdf_oren_nayar.h
@@ -19,39 +19,59 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct OrenNayarBsdf {
+ SHADER_CLOSURE_BASE;
+
+ float3 N;
+ float roughness;
+ float a;
+ float b;
+} OrenNayarBsdf;
+
ccl_device float3 bsdf_oren_nayar_get_intensity(const ShaderClosure *sc, float3 n, float3 v, float3 l)
{
+ const OrenNayarBsdf *bsdf = (const OrenNayarBsdf*)sc;
float nl = max(dot(n, l), 0.0f);
float nv = max(dot(n, v), 0.0f);
float t = dot(l, v) - nl * nv;
if(t > 0.0f)
t /= max(nl, nv) + FLT_MIN;
- float is = nl * (sc->data0 + sc->data1 * t);
+ float is = nl * (bsdf->a + bsdf->b * t);
return make_float3(is, is, is);
}
-ccl_device int bsdf_oren_nayar_setup(ShaderClosure *sc)
+ccl_device int bsdf_oren_nayar_setup(OrenNayarBsdf *bsdf)
{
- float sigma = sc->data0;
+ float sigma = bsdf->roughness;
- sc->type = CLOSURE_BSDF_OREN_NAYAR_ID;
+ bsdf->type = CLOSURE_BSDF_OREN_NAYAR_ID;
sigma = saturate(sigma);
float div = 1.0f / (M_PI_F + ((3.0f * M_PI_F - 4.0f) / 6.0f) * sigma);
- sc->data0 = 1.0f * div;
- sc->data1 = sigma * div;
+ bsdf->a = 1.0f * div;
+ bsdf->b = sigma * div;
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
+ccl_device bool bsdf_oren_nayar_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const OrenNayarBsdf *bsdf_a = (const OrenNayarBsdf*)a;
+ const OrenNayarBsdf *bsdf_b = (const OrenNayarBsdf*)b;
+
+ return (isequal_float3(bsdf_a->N, bsdf_b->N)) &&
+ (bsdf_a->roughness == bsdf_b->roughness);
+}
+
ccl_device float3 bsdf_oren_nayar_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- if(dot(sc->N, omega_in) > 0.0f) {
+ const OrenNayarBsdf *bsdf = (const OrenNayarBsdf*)sc;
+ if(dot(bsdf->N, omega_in) > 0.0f) {
*pdf = 0.5f * M_1_PI_F;
- return bsdf_oren_nayar_get_intensity(sc, sc->N, I, omega_in);
+ return bsdf_oren_nayar_get_intensity(sc, bsdf->N, I, omega_in);
}
else {
*pdf = 0.0f;
@@ -66,15 +86,16 @@ ccl_device float3 bsdf_oren_nayar_eval_transmit(const ShaderClosure *sc, const f
ccl_device int bsdf_oren_nayar_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- sample_uniform_hemisphere(sc->N, randu, randv, omega_in, pdf);
+ const OrenNayarBsdf *bsdf = (const OrenNayarBsdf*)sc;
+ sample_uniform_hemisphere(bsdf->N, randu, randv, omega_in, pdf);
if(dot(Ng, *omega_in) > 0.0f) {
- *eval = bsdf_oren_nayar_get_intensity(sc, sc->N, I, *omega_in);
+ *eval = bsdf_oren_nayar_get_intensity(sc, bsdf->N, I, *omega_in);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the bounce
- *domega_in_dx = (2.0f * dot(sc->N, dIdx)) * sc->N - dIdx;
- *domega_in_dy = (2.0f * dot(sc->N, dIdy)) * sc->N - dIdy;
+ *domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
+ *domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
}
else {
diff --git a/intern/cycles/kernel/closure/bsdf_phong_ramp.h b/intern/cycles/kernel/closure/bsdf_phong_ramp.h
index 1ab15eee954..e152a8780db 100644
--- a/intern/cycles/kernel/closure/bsdf_phong_ramp.h
+++ b/intern/cycles/kernel/closure/bsdf_phong_ramp.h
@@ -35,7 +35,17 @@
CCL_NAMESPACE_BEGIN
-ccl_device float3 bsdf_phong_ramp_get_color(const ShaderClosure *sc, const float3 colors[8], float pos)
+#ifdef __OSL__
+
+typedef ccl_addr_space struct PhongRampBsdf {
+ SHADER_CLOSURE_BASE;
+
+ float3 N;
+ float exponent;
+ float3 *colors;
+} PhongRampBsdf;
+
+ccl_device float3 bsdf_phong_ramp_get_color(const float3 colors[8], float pos)
{
int MAXCOLORS = 8;
@@ -49,57 +59,54 @@ ccl_device float3 bsdf_phong_ramp_get_color(const ShaderClosure *sc, const float
return colors[ipos] * (1.0f - offset) + colors[ipos+1] * offset;
}
-ccl_device int bsdf_phong_ramp_setup(ShaderClosure *sc)
+ccl_device int bsdf_phong_ramp_setup(PhongRampBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_PHONG_RAMP_ID;
- sc->data0 = max(sc->data0, 0.0f);
- sc->data1 = 0.0f;
+ bsdf->type = CLOSURE_BSDF_PHONG_RAMP_ID;
+ bsdf->exponent = max(bsdf->exponent, 0.0f);
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
-ccl_device void bsdf_phong_ramp_blur(ShaderClosure *sc, float roughness)
-{
-}
-
-ccl_device float3 bsdf_phong_ramp_eval_reflect(const ShaderClosure *sc, const float3 colors[8], const float3 I, const float3 omega_in, float *pdf)
+ccl_device float3 bsdf_phong_ramp_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float m_exponent = sc->data0;
- float cosNI = dot(sc->N, omega_in);
- float cosNO = dot(sc->N, I);
+ const PhongRampBsdf *bsdf = (const PhongRampBsdf*)sc;
+ float m_exponent = bsdf->exponent;
+ float cosNI = dot(bsdf->N, omega_in);
+ float cosNO = dot(bsdf->N, I);
if(cosNI > 0 && cosNO > 0) {
// reflect the view vector
- float3 R = (2 * cosNO) * sc->N - I;
+ float3 R = (2 * cosNO) * bsdf->N - I;
float cosRI = dot(R, omega_in);
if(cosRI > 0) {
float cosp = powf(cosRI, m_exponent);
float common = 0.5f * M_1_PI_F * cosp;
float out = cosNI * (m_exponent + 2) * common;
*pdf = (m_exponent + 1) * common;
- return bsdf_phong_ramp_get_color(sc, colors, cosp) * out;
+ return bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out;
}
}
return make_float3(0.0f, 0.0f, 0.0f);
}
-ccl_device float3 bsdf_phong_ramp_eval_transmit(const ShaderClosure *sc, const float3 colors[8], const float3 I, const float3 omega_in, float *pdf)
+ccl_device float3 bsdf_phong_ramp_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
return make_float3(0.0f, 0.0f, 0.0f);
}
-ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, const float3 colors[8], float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float cosNO = dot(sc->N, I);
- float m_exponent = sc->data0;
+ const PhongRampBsdf *bsdf = (const PhongRampBsdf*)sc;
+ float cosNO = dot(bsdf->N, I);
+ float m_exponent = bsdf->exponent;
if(cosNO > 0) {
// reflect the view vector
- float3 R = (2 * cosNO) * sc->N - I;
+ float3 R = (2 * cosNO) * bsdf->N - I;
#ifdef __RAY_DIFFERENTIALS__
- *domega_in_dx = (2 * dot(sc->N, dIdx)) * sc->N - dIdx;
- *domega_in_dy = (2 * dot(sc->N, dIdy)) * sc->N - dIdy;
+ *domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
+ *domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
float3 T, B;
@@ -114,7 +121,7 @@ ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, const float3 colo
if(dot(Ng, *omega_in) > 0.0f)
{
// common terms for pdf and eval
- float cosNI = dot(sc->N, *omega_in);
+ float cosNI = dot(bsdf->N, *omega_in);
// make sure the direction we chose is still in the right hemisphere
if(cosNI > 0)
{
@@ -122,13 +129,14 @@ ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, const float3 colo
float common = 0.5f * M_1_PI_F * cosp;
*pdf = (m_exponent + 1) * common;
float out = cosNI * (m_exponent + 2) * common;
- *eval = bsdf_phong_ramp_get_color(sc, colors, cosp) * out;
+ *eval = bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out;
}
}
}
return LABEL_REFLECT|LABEL_GLOSSY;
}
+#endif /* __OSL__ */
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/closure/bsdf_reflection.h b/intern/cycles/kernel/closure/bsdf_reflection.h
index 303f4c9ce34..1d21614ecee 100644
--- a/intern/cycles/kernel/closure/bsdf_reflection.h
+++ b/intern/cycles/kernel/closure/bsdf_reflection.h
@@ -37,9 +37,9 @@ CCL_NAMESPACE_BEGIN
/* REFLECTION */
-ccl_device int bsdf_reflection_setup(ShaderClosure *sc)
+ccl_device int bsdf_reflection_setup(MicrofacetBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_REFLECTION_ID;
+ bsdf->type = CLOSURE_BSDF_REFLECTION_ID;
return SD_BSDF;
}
@@ -55,7 +55,8 @@ ccl_device float3 bsdf_reflection_eval_transmit(const ShaderClosure *sc, const f
ccl_device int bsdf_reflection_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float3 N = bsdf->N;
// only one direction is possible
float cosNO = dot(N, I);
diff --git a/intern/cycles/kernel/closure/bsdf_refraction.h b/intern/cycles/kernel/closure/bsdf_refraction.h
index c78a4b67134..050a4e76fa9 100644
--- a/intern/cycles/kernel/closure/bsdf_refraction.h
+++ b/intern/cycles/kernel/closure/bsdf_refraction.h
@@ -37,9 +37,9 @@ CCL_NAMESPACE_BEGIN
/* REFRACTION */
-ccl_device int bsdf_refraction_setup(ShaderClosure *sc)
+ccl_device int bsdf_refraction_setup(MicrofacetBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_REFRACTION_ID;
+ bsdf->type = CLOSURE_BSDF_REFRACTION_ID;
return SD_BSDF;
}
@@ -55,8 +55,9 @@ ccl_device float3 bsdf_refraction_eval_transmit(const ShaderClosure *sc, const f
ccl_device int bsdf_refraction_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float m_eta = sc->data0;
- float3 N = sc->N;
+ const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc;
+ float m_eta = bsdf->ior;
+ float3 N = bsdf->N;
float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
diff --git a/intern/cycles/kernel/closure/bsdf_toon.h b/intern/cycles/kernel/closure/bsdf_toon.h
index e5b6ab93a64..28e775bcbc8 100644
--- a/intern/cycles/kernel/closure/bsdf_toon.h
+++ b/intern/cycles/kernel/closure/bsdf_toon.h
@@ -35,17 +35,35 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct ToonBsdf {
+ SHADER_CLOSURE_BASE;
+
+ float3 N;
+ float size;
+ float smooth;
+} ToonBsdf;
+
/* DIFFUSE TOON */
-ccl_device int bsdf_diffuse_toon_setup(ShaderClosure *sc)
+ccl_device int bsdf_diffuse_toon_setup(ToonBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_DIFFUSE_TOON_ID;
- sc->data0 = saturate(sc->data0);
- sc->data1 = saturate(sc->data1);
+ bsdf->type = CLOSURE_BSDF_DIFFUSE_TOON_ID;
+ bsdf->size = saturate(bsdf->size);
+ bsdf->smooth = saturate(bsdf->smooth);
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
+ccl_device bool bsdf_toon_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const ToonBsdf *bsdf_a = (const ToonBsdf*)a;
+ const ToonBsdf *bsdf_b = (const ToonBsdf*)b;
+
+ return (isequal_float3(bsdf_a->N, bsdf_b->N)) &&
+ (bsdf_a->size == bsdf_b->size) &&
+ (bsdf_a->smooth == bsdf_b->smooth);
+}
+
ccl_device float3 bsdf_toon_get_intensity(float max_angle, float smooth, float angle)
{
float is;
@@ -67,9 +85,10 @@ ccl_device float bsdf_toon_get_sample_angle(float max_angle, float smooth)
ccl_device float3 bsdf_diffuse_toon_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float max_angle = sc->data0*M_PI_2_F;
- float smooth = sc->data1*M_PI_2_F;
- float angle = safe_acosf(fmaxf(dot(sc->N, omega_in), 0.0f));
+ const ToonBsdf *bsdf = (const ToonBsdf*)sc;
+ float max_angle = bsdf->size*M_PI_2_F;
+ float smooth = bsdf->smooth*M_PI_2_F;
+ float angle = safe_acosf(fmaxf(dot(bsdf->N, omega_in), 0.0f));
float3 eval = bsdf_toon_get_intensity(max_angle, smooth, angle);
@@ -90,21 +109,22 @@ ccl_device float3 bsdf_diffuse_toon_eval_transmit(const ShaderClosure *sc, const
ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float max_angle = sc->data0*M_PI_2_F;
- float smooth = sc->data1*M_PI_2_F;
+ const ToonBsdf *bsdf = (const ToonBsdf*)sc;
+ float max_angle = bsdf->size*M_PI_2_F;
+ float smooth = bsdf->smooth*M_PI_2_F;
float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
float angle = sample_angle*randu;
if(sample_angle > 0.0f) {
- sample_uniform_cone(sc->N, sample_angle, randu, randv, omega_in, pdf);
+ sample_uniform_cone(bsdf->N, sample_angle, randu, randv, omega_in, pdf);
if(dot(Ng, *omega_in) > 0.0f) {
*eval = *pdf * bsdf_toon_get_intensity(max_angle, smooth, angle);
#ifdef __RAY_DIFFERENTIALS__
// TODO: find a better approximation for the bounce
- *domega_in_dx = (2.0f * dot(sc->N, dIdx)) * sc->N - dIdx;
- *domega_in_dy = (2.0f * dot(sc->N, dIdy)) * sc->N - dIdy;
+ *domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
+ *domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
}
else
@@ -117,25 +137,26 @@ ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc, float3 Ng, floa
/* GLOSSY TOON */
-ccl_device int bsdf_glossy_toon_setup(ShaderClosure *sc)
+ccl_device int bsdf_glossy_toon_setup(ToonBsdf *bsdf)
{
- sc->type = CLOSURE_BSDF_GLOSSY_TOON_ID;
- sc->data0 = saturate(sc->data0);
- sc->data1 = saturate(sc->data1);
+ bsdf->type = CLOSURE_BSDF_GLOSSY_TOON_ID;
+ bsdf->size = saturate(bsdf->size);
+ bsdf->smooth = saturate(bsdf->smooth);
return SD_BSDF|SD_BSDF_HAS_EVAL;
}
ccl_device float3 bsdf_glossy_toon_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf)
{
- float max_angle = sc->data0*M_PI_2_F;
- float smooth = sc->data1*M_PI_2_F;
- float cosNI = dot(sc->N, omega_in);
- float cosNO = dot(sc->N, I);
+ const ToonBsdf *bsdf = (const ToonBsdf*)sc;
+ float max_angle = bsdf->size*M_PI_2_F;
+ float smooth = bsdf->smooth*M_PI_2_F;
+ float cosNI = dot(bsdf->N, omega_in);
+ float cosNO = dot(bsdf->N, I);
if(cosNI > 0 && cosNO > 0) {
/* reflect the view vector */
- float3 R = (2 * cosNO) * sc->N - I;
+ float3 R = (2 * cosNO) * bsdf->N - I;
float cosRI = dot(R, omega_in);
float angle = safe_acosf(fmaxf(cosRI, 0.0f));
@@ -157,13 +178,14 @@ ccl_device float3 bsdf_glossy_toon_eval_transmit(const ShaderClosure *sc, const
ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float max_angle = sc->data0*M_PI_2_F;
- float smooth = sc->data1*M_PI_2_F;
- float cosNO = dot(sc->N, I);
+ const ToonBsdf *bsdf = (const ToonBsdf*)sc;
+ float max_angle = bsdf->size*M_PI_2_F;
+ float smooth = bsdf->smooth*M_PI_2_F;
+ float cosNO = dot(bsdf->N, I);
if(cosNO > 0) {
/* reflect the view vector */
- float3 R = (2 * cosNO) * sc->N - I;
+ float3 R = (2 * cosNO) * bsdf->N - I;
float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
float angle = sample_angle*randu;
@@ -171,15 +193,15 @@ ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc, float3 Ng, float
sample_uniform_cone(R, sample_angle, randu, randv, omega_in, pdf);
if(dot(Ng, *omega_in) > 0.0f) {
- float cosNI = dot(sc->N, *omega_in);
+ float cosNI = dot(bsdf->N, *omega_in);
/* make sure the direction we chose is still in the right hemisphere */
if(cosNI > 0) {
*eval = *pdf * bsdf_toon_get_intensity(max_angle, smooth, angle);
#ifdef __RAY_DIFFERENTIALS__
- *domega_in_dx = (2 * dot(sc->N, dIdx)) * sc->N - dIdx;
- *domega_in_dy = (2 * dot(sc->N, dIdy)) * sc->N - dIdy;
+ *domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx;
+ *domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy;
#endif
}
else
diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h
index c24720cefbe..35c95768b69 100644
--- a/intern/cycles/kernel/closure/bssrdf.h
+++ b/intern/cycles/kernel/closure/bssrdf.h
@@ -19,6 +19,17 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct Bssrdf {
+ SHADER_CLOSURE_BASE;
+
+ float radius;
+ float sharpness;
+ float d;
+ float texture_blur;
+ float albedo;
+ float3 N;
+} Bssrdf;
+
/* Planar Truncated Gaussian
*
* Note how this is different from the typical gaussian, this one integrates
@@ -28,11 +39,12 @@ CCL_NAMESPACE_BEGIN
/* paper suggests 1/12.46 which is much too small, suspect it's *12.46 */
#define GAUSS_TRUNCATE 12.46f
-ccl_device float bssrdf_gaussian_eval(ShaderClosure *sc, float r)
+ccl_device float bssrdf_gaussian_eval(const ShaderClosure *sc, float r)
{
/* integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) from 0 to Rm
* = 1 - exp(-Rm*Rm/(2*v)) */
- const float v = sc->data0*sc->data0*(0.25f*0.25f);
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float v = bssrdf->radius*bssrdf->radius*(0.25f*0.25f);
const float Rm = sqrtf(v*GAUSS_TRUNCATE);
if(r >= Rm)
@@ -41,7 +53,7 @@ ccl_device float bssrdf_gaussian_eval(ShaderClosure *sc, float r)
return expf(-r*r/(2.0f*v))/(2.0f*M_PI_F*v);
}
-ccl_device float bssrdf_gaussian_pdf(ShaderClosure *sc, float r)
+ccl_device float bssrdf_gaussian_pdf(const ShaderClosure *sc, float r)
{
/* 1.0 - expf(-Rm*Rm/(2*v)) simplified */
const float area_truncated = 1.0f - expf(-0.5f*GAUSS_TRUNCATE);
@@ -49,12 +61,12 @@ ccl_device float bssrdf_gaussian_pdf(ShaderClosure *sc, float r)
return bssrdf_gaussian_eval(sc, r) * (1.0f/(area_truncated));
}
-ccl_device void bssrdf_gaussian_sample(ShaderClosure *sc, float xi, float *r, float *h)
+ccl_device void bssrdf_gaussian_sample(const ShaderClosure *sc, float xi, float *r, float *h)
{
/* xi = integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) = -exp(-r^2/(2*v))
* r = sqrt(-2*v*logf(xi)) */
-
- const float v = sc->data0*sc->data0*(0.25f*0.25f);
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float v = bssrdf->radius*bssrdf->radius*(0.25f*0.25f);
const float Rm = sqrtf(v*GAUSS_TRUNCATE);
/* 1.0 - expf(-Rm*Rm/(2*v)) simplified */
@@ -75,12 +87,13 @@ ccl_device void bssrdf_gaussian_sample(ShaderClosure *sc, float xi, float *r, fl
* far as I can tell has no closed form solution. So we get an iterative solution
* instead with newton-raphson. */
-ccl_device float bssrdf_cubic_eval(ShaderClosure *sc, float r)
+ccl_device float bssrdf_cubic_eval(const ShaderClosure *sc, float r)
{
- const float sharpness = sc->T.x;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float sharpness = bssrdf->sharpness;
if(sharpness == 0.0f) {
- const float Rm = sc->data0;
+ const float Rm = bssrdf->radius;
if(r >= Rm)
return 0.0f;
@@ -94,7 +107,7 @@ ccl_device float bssrdf_cubic_eval(ShaderClosure *sc, float r)
}
else {
- float Rm = sc->data0*(1.0f + sharpness);
+ float Rm = bssrdf->radius*(1.0f + sharpness);
if(r >= Rm)
return 0.0f;
@@ -122,13 +135,13 @@ ccl_device float bssrdf_cubic_eval(ShaderClosure *sc, float r)
}
}
-ccl_device float bssrdf_cubic_pdf(ShaderClosure *sc, float r)
+ccl_device float bssrdf_cubic_pdf(const ShaderClosure *sc, float r)
{
return bssrdf_cubic_eval(sc, r);
}
/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */
-ccl_device float bssrdf_cubic_quintic_root_find(float xi)
+ccl_device_inline float bssrdf_cubic_quintic_root_find(float xi)
{
/* newton-raphson iteration, usually succeeds in 2-4 iterations, except
* outside 0.02 ... 0.98 where it can go up to 10, so overall performance
@@ -155,12 +168,13 @@ ccl_device float bssrdf_cubic_quintic_root_find(float xi)
return x;
}
-ccl_device void bssrdf_cubic_sample(ShaderClosure *sc, float xi, float *r, float *h)
+ccl_device void bssrdf_cubic_sample(const ShaderClosure *sc, float xi, float *r, float *h)
{
- float Rm = sc->data0;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float sharpness = bssrdf->sharpness;
+ float Rm = bssrdf->radius;
float r_ = bssrdf_cubic_quintic_root_find(xi);
- const float sharpness = sc->T.x;
if(sharpness != 0.0f) {
r_ = powf(r_, 1.0f + sharpness);
Rm *= (1.0f + sharpness);
@@ -198,21 +212,22 @@ ccl_device_inline float bssrdf_burley_compatible_mfp(float r)
return 0.25f * M_1_PI_F * r;
}
-ccl_device void bssrdf_burley_setup(ShaderClosure *sc)
+ccl_device void bssrdf_burley_setup(Bssrdf *bssrdf)
{
/* Mean free path length. */
- const float l = bssrdf_burley_compatible_mfp(sc->data0);
+ const float l = bssrdf_burley_compatible_mfp(bssrdf->radius);
/* Surface albedo. */
- const float A = sc->data2;
+ const float A = bssrdf->albedo;
const float s = bssrdf_burley_fitting(A);
const float d = l / s;
- sc->custom1 = d;
+ bssrdf->d = d;
}
-ccl_device float bssrdf_burley_eval(ShaderClosure *sc, float r)
+ccl_device float bssrdf_burley_eval(const ShaderClosure *sc, float r)
{
- const float d = sc->custom1;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float d = bssrdf->d;
const float Rm = BURLEY_TRUNCATE * d;
if(r >= Rm)
@@ -231,7 +246,7 @@ ccl_device float bssrdf_burley_eval(ShaderClosure *sc, float r)
return (exp_r_d + exp_r_3_d) / (4.0f*d);
}
-ccl_device float bssrdf_burley_pdf(ShaderClosure *sc, float r)
+ccl_device float bssrdf_burley_pdf(const ShaderClosure *sc, float r)
{
return bssrdf_burley_eval(sc, r) * (1.0f/BURLEY_TRUNCATE_CDF);
}
@@ -240,7 +255,7 @@ ccl_device float bssrdf_burley_pdf(ShaderClosure *sc, float r)
* Returns scaled radius, meaning the result is to be scaled up by d.
* Since there's no closed form solution we do Newton-Raphson method to find it.
*/
-ccl_device float bssrdf_burley_root_find(float xi)
+ccl_device_inline float bssrdf_burley_root_find(float xi)
{
const float tolerance = 1e-6f;
const int max_iteration_count = 10;
@@ -276,12 +291,13 @@ ccl_device float bssrdf_burley_root_find(float xi)
return r;
}
-ccl_device void bssrdf_burley_sample(ShaderClosure *sc,
+ccl_device void bssrdf_burley_sample(const ShaderClosure *sc,
float xi,
float *r,
float *h)
{
- const float d = sc->custom1;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float d = bssrdf->d;
const float Rm = BURLEY_TRUNCATE * d;
const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d;
@@ -295,26 +311,29 @@ ccl_device void bssrdf_burley_sample(ShaderClosure *sc,
*
* Samples distributed over disk with no falloff, for reference. */
-ccl_device float bssrdf_none_eval(ShaderClosure *sc, float r)
+ccl_device float bssrdf_none_eval(const ShaderClosure *sc, float r)
{
- const float Rm = sc->data0;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float Rm = bssrdf->radius;
return (r < Rm)? 1.0f: 0.0f;
}
-ccl_device float bssrdf_none_pdf(ShaderClosure *sc, float r)
+ccl_device float bssrdf_none_pdf(const ShaderClosure *sc, float r)
{
/* integrate (2*pi*r)/(pi*Rm*Rm) from 0 to Rm = 1 */
- const float Rm = sc->data0;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float Rm = bssrdf->radius;
const float area = (M_PI_F*Rm*Rm);
return bssrdf_none_eval(sc, r) / area;
}
-ccl_device void bssrdf_none_sample(ShaderClosure *sc, float xi, float *r, float *h)
+ccl_device void bssrdf_none_sample(const ShaderClosure *sc, float xi, float *r, float *h)
{
/* xi = integrate (2*pi*r)/(pi*Rm*Rm) = r^2/Rm^2
* r = sqrt(xi)*Rm */
- const float Rm = sc->data0;
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
+ const float Rm = bssrdf->radius;
const float r_ = sqrtf(xi)*Rm;
*r = r_;
@@ -325,30 +344,42 @@ ccl_device void bssrdf_none_sample(ShaderClosure *sc, float xi, float *r, float
/* Generic */
-ccl_device int bssrdf_setup(ShaderClosure *sc, ClosureType type)
+ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight)
+{
+ Bssrdf *bssrdf = (Bssrdf*)closure_alloc(sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight);
+
+ if(!bssrdf)
+ return NULL;
+
+ float sample_weight = fabsf(average(weight));
+ bssrdf->sample_weight = sample_weight;
+ return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? bssrdf : NULL;
+}
+
+ccl_device int bssrdf_setup(Bssrdf *bssrdf, ClosureType type)
{
- if(sc->data0 < BSSRDF_MIN_RADIUS) {
+ if(bssrdf->radius < BSSRDF_MIN_RADIUS) {
/* revert to diffuse BSDF if radius too small */
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- int flag = bsdf_diffuse_setup(sc);
- sc->type = CLOSURE_BSDF_BSSRDF_ID;
+ DiffuseBsdf *bsdf = (DiffuseBsdf*)bssrdf;
+ bsdf->N = bssrdf->N;
+ int flag = bsdf_diffuse_setup(bsdf);
+ bsdf->type = CLOSURE_BSDF_BSSRDF_ID;
return flag;
}
else {
- sc->data1 = saturate(sc->data1); /* texture blur */
- sc->T.x = saturate(sc->T.x); /* sharpness */
- sc->type = type;
+ bssrdf->texture_blur = saturate(bssrdf->texture_blur);
+ bssrdf->sharpness = saturate(bssrdf->sharpness);
+ bssrdf->type = type;
if(type == CLOSURE_BSSRDF_BURLEY_ID) {
- bssrdf_burley_setup(sc);
+ bssrdf_burley_setup(bssrdf);
}
return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSSRDF;
}
}
-ccl_device void bssrdf_sample(ShaderClosure *sc, float xi, float *r, float *h)
+ccl_device void bssrdf_sample(const ShaderClosure *sc, float xi, float *r, float *h)
{
if(sc->type == CLOSURE_BSSRDF_CUBIC_ID)
bssrdf_cubic_sample(sc, xi, r, h);
@@ -358,7 +389,7 @@ ccl_device void bssrdf_sample(ShaderClosure *sc, float xi, float *r, float *h)
bssrdf_burley_sample(sc, xi, r, h);
}
-ccl_device float bssrdf_pdf(ShaderClosure *sc, float r)
+ccl_device_inline float bssrdf_pdf(const ShaderClosure *sc, float r)
{
if(sc->type == CLOSURE_BSSRDF_CUBIC_ID)
return bssrdf_cubic_pdf(sc, r);
diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h
index 4d71ba50ec3..01e67c7c2fd 100644
--- a/intern/cycles/kernel/closure/volume.h
+++ b/intern/cycles/kernel/closure/volume.h
@@ -19,6 +19,12 @@
CCL_NAMESPACE_BEGIN
+typedef ccl_addr_space struct HenyeyGreensteinVolume {
+ SHADER_CLOSURE_BASE;
+
+ float g;
+} HenyeyGreensteinVolume;
+
/* HENYEY-GREENSTEIN CLOSURE */
/* Given cosine between rays, return probability density that a photon bounces
@@ -29,19 +35,28 @@ ccl_device float single_peaked_henyey_greenstein(float cos_theta, float g)
return ((1.0f - g * g) / safe_powf(1.0f + g * g - 2.0f * g * cos_theta, 1.5f)) * (M_1_PI_F * 0.25f);
};
-ccl_device int volume_henyey_greenstein_setup(ShaderClosure *sc)
+ccl_device int volume_henyey_greenstein_setup(HenyeyGreensteinVolume *volume)
{
- sc->type = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
+ volume->type = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
/* clamp anisotropy to avoid delta function */
- sc->data0 = signf(sc->data0) * min(fabsf(sc->data0), 1.0f - 1e-3f);
+ volume->g = signf(volume->g) * min(fabsf(volume->g), 1.0f - 1e-3f);
return SD_SCATTER;
}
+ccl_device bool volume_henyey_greenstein_merge(const ShaderClosure *a, const ShaderClosure *b)
+{
+ const HenyeyGreensteinVolume *volume_a = (const HenyeyGreensteinVolume*)a;
+ const HenyeyGreensteinVolume *volume_b = (const HenyeyGreensteinVolume*)b;
+
+ return (volume_a->g == volume_b->g);
+}
+
ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, const float3 I, float3 omega_in, float *pdf)
{
- float g = sc->data0;
+ const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume*)sc;
+ float g = volume->g;
/* note that I points towards the viewer */
if(fabsf(g) < 1e-3f) {
@@ -58,7 +73,8 @@ ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, c
ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv,
float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
- float g = sc->data0;
+ const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume*)sc;
+ float g = volume->g;
float cos_phi, sin_phi, cos_theta;
/* match pdf for small g */
diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h
index d2c7edb11ea..11548324e18 100644
--- a/intern/cycles/kernel/geom/geom.h
+++ b/intern/cycles/kernel/geom/geom.h
@@ -17,7 +17,9 @@
#include "geom_attribute.h"
#include "geom_object.h"
+#include "geom_patch.h"
#include "geom_triangle.h"
+#include "geom_subd_triangle.h"
#include "geom_triangle_intersect.h"
#include "geom_motion_triangle.h"
#include "geom_motion_curve.h"
diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h
index c7364e9edac..8604d30ad34 100644
--- a/intern/cycles/kernel/geom/geom_attribute.h
+++ b/intern/cycles/kernel/geom/geom_attribute.h
@@ -25,47 +25,76 @@ CCL_NAMESPACE_BEGIN
* Lookup of attributes is different between OSL and SVM, as OSL is ustring
* based while for SVM we use integer ids. */
+ccl_device_inline uint subd_triangle_patch(KernelGlobals *kg, const ShaderData *sd);
+
+ccl_device_inline uint attribute_primitive_type(KernelGlobals *kg, const ShaderData *sd)
+{
+#ifdef __HAIR__
+ if(ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE) {
+ return ATTR_PRIM_CURVE;
+ }
+ else
+#endif
+ if(subd_triangle_patch(kg, sd) != ~0) {
+ return ATTR_PRIM_SUBD;
+ }
+ else {
+ return ATTR_PRIM_TRIANGLE;
+ }
+}
+
+ccl_device_inline AttributeDescriptor attribute_not_found()
+{
+ const AttributeDescriptor desc = {ATTR_ELEMENT_NONE, (NodeAttributeType)0, 0, ATTR_STD_NOT_FOUND};
+ return desc;
+}
+
/* Find attribute based on ID */
-ccl_device_inline int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem)
+ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id)
{
- if(ccl_fetch(sd, object) == PRIM_NONE)
- return (int)ATTR_STD_NOT_FOUND;
+ if(ccl_fetch(sd, object) == PRIM_NONE) {
+ return attribute_not_found();
+ }
/* for SVM, find attribute by unique id */
uint attr_offset = ccl_fetch(sd, object)*kernel_data.bvh.attributes_map_stride;
-#ifdef __HAIR__
- attr_offset = (ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE)? attr_offset + ATTR_PRIM_CURVE: attr_offset;
-#endif
+ attr_offset += attribute_primitive_type(kg, sd);
uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset);
while(attr_map.x != id) {
if(UNLIKELY(attr_map.x == ATTR_STD_NONE)) {
- return ATTR_STD_NOT_FOUND;
+ return attribute_not_found();
}
attr_offset += ATTR_PRIM_TYPES;
attr_map = kernel_tex_fetch(__attributes_map, attr_offset);
}
- *elem = (AttributeElement)attr_map.y;
+ AttributeDescriptor desc;
+ desc.element = (AttributeElement)attr_map.y;
- if(ccl_fetch(sd, prim) == PRIM_NONE && (AttributeElement)attr_map.y != ATTR_ELEMENT_MESH)
- return ATTR_STD_NOT_FOUND;
+ if(ccl_fetch(sd, prim) == PRIM_NONE && desc.element != ATTR_ELEMENT_MESH) {
+ return attribute_not_found();
+ }
/* return result */
- return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z;
+ desc.offset = (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z;
+ desc.type = (NodeAttributeType)(attr_map.w & 0xff);
+ desc.flags = (AttributeFlag)(attr_map.w >> 8);
+
+ return desc;
}
/* Transform matrix attribute on meshes */
-ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, const ShaderData *sd, int offset)
+ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc)
{
Transform tfm;
- tfm.x = kernel_tex_fetch(__attributes_float3, offset + 0);
- tfm.y = kernel_tex_fetch(__attributes_float3, offset + 1);
- tfm.z = kernel_tex_fetch(__attributes_float3, offset + 2);
- tfm.w = kernel_tex_fetch(__attributes_float3, offset + 3);
+ tfm.x = kernel_tex_fetch(__attributes_float3, desc.offset + 0);
+ tfm.y = kernel_tex_fetch(__attributes_float3, desc.offset + 1);
+ tfm.z = kernel_tex_fetch(__attributes_float3, desc.offset + 2);
+ tfm.w = kernel_tex_fetch(__attributes_float3, desc.offset + 3);
return tfm;
}
diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h
index 292e1bfca0e..aa9cd295452 100644
--- a/intern/cycles/kernel/geom/geom_curve.h
+++ b/intern/cycles/kernel/geom/geom_curve.h
@@ -24,23 +24,23 @@ CCL_NAMESPACE_BEGIN
/* Reading attributes on various curve elements */
-ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy)
+ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
{
- if(elem == ATTR_ELEMENT_CURVE) {
+ if(desc.element == ATTR_ELEMENT_CURVE) {
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
#endif
- return kernel_tex_fetch(__attributes_float, offset + ccl_fetch(sd, prim));
+ return kernel_tex_fetch(__attributes_float, desc.offset + ccl_fetch(sd, prim));
}
- else if(elem == ATTR_ELEMENT_CURVE_KEY || elem == ATTR_ELEMENT_CURVE_KEY_MOTION) {
+ else if(desc.element == ATTR_ELEMENT_CURVE_KEY || desc.element == ATTR_ELEMENT_CURVE_KEY_MOTION) {
float4 curvedata = kernel_tex_fetch(__curves, ccl_fetch(sd, prim));
int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(ccl_fetch(sd, type));
int k1 = k0 + 1;
- float f0 = kernel_tex_fetch(__attributes_float, offset + k0);
- float f1 = kernel_tex_fetch(__attributes_float, offset + k1);
+ float f0 = kernel_tex_fetch(__attributes_float, desc.offset + k0);
+ float f1 = kernel_tex_fetch(__attributes_float, desc.offset + k1);
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = ccl_fetch(sd, du).dx*(f1 - f0);
@@ -59,9 +59,9 @@ ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd,
}
}
-ccl_device float3 curve_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy)
+ccl_device float3 curve_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
{
- if(elem == ATTR_ELEMENT_CURVE) {
+ if(desc.element == ATTR_ELEMENT_CURVE) {
/* idea: we can't derive any useful differentials here, but for tiled
* mipmap image caching it would be useful to avoid reading the highest
* detail level always. maybe a derivative based on the hair density
@@ -71,15 +71,15 @@ ccl_device float3 curve_attribute_float3(KernelGlobals *kg, const ShaderData *sd
if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
#endif
- return float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + ccl_fetch(sd, prim)));
+ return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + ccl_fetch(sd, prim)));
}
- else if(elem == ATTR_ELEMENT_CURVE_KEY || elem == ATTR_ELEMENT_CURVE_KEY_MOTION) {
+ else if(desc.element == ATTR_ELEMENT_CURVE_KEY || desc.element == ATTR_ELEMENT_CURVE_KEY_MOTION) {
float4 curvedata = kernel_tex_fetch(__curves, ccl_fetch(sd, prim));
int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(ccl_fetch(sd, type));
int k1 = k0 + 1;
- float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + k0));
- float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + k1));
+ float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + k0));
+ float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + k1));
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = ccl_fetch(sd, du).dx*(f1 - f0);
diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h
index 2fb8e219884..dabba3fb1f0 100644
--- a/intern/cycles/kernel/geom/geom_motion_triangle.h
+++ b/intern/cycles/kernel/geom/geom_motion_triangle.h
@@ -387,6 +387,12 @@ ccl_device_inline void motion_triangle_intersect_subsurface(
float t, u, v;
if(ray_triangle_intersect_uv(P, dir, tmax, verts[2], verts[0], verts[1], &u, &v, &t)) {
+ for(int i = min(max_hits, ss_isect->num_hits) - 1; i >= 0; --i) {
+ if(ss_isect->hits[i].t == t) {
+ return;
+ }
+ }
+
ss_isect->num_hits++;
int hit;
diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h
index c0d15a95954..883c5dc100d 100644
--- a/intern/cycles/kernel/geom/geom_object.h
+++ b/intern/cycles/kernel/geom/geom_object.h
@@ -292,6 +292,18 @@ ccl_device_inline void object_motion_info(KernelGlobals *kg, int object, int *nu
*numverts = __float_as_int(f.w);
}
+/* Offset to an objects patch map */
+
+ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object)
+{
+ if(object == OBJECT_NONE)
+ return 0;
+
+ int offset = object*OBJECT_SIZE + 11;
+ float4 f = kernel_tex_fetch(__objects, offset);
+ return __float_as_uint(f.x);
+}
+
/* Pass ID for shader */
ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd)
diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h
new file mode 100644
index 00000000000..6a0ff5a4a04
--- /dev/null
+++ b/intern/cycles/kernel/geom/geom_patch.h
@@ -0,0 +1,343 @@
+/*
+ * Based on code from OpenSubdiv released under this license:
+ *
+ * Copyright 2013 Pixar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "Apache License")
+ * with the following modification; you may not use this file except in
+ * compliance with the Apache License and the following modification to it:
+ * Section 6. Trademarks. is deleted and replaced with:
+ *
+ * 6. Trademarks. This License does not grant permission to use the trade
+ * names, trademarks, service marks, or product names of the Licensor
+ * and its affiliates, except as required to comply with Section 4(c) of
+ * the License and to reproduce the content of the NOTICE file.
+ *
+ * You may obtain a copy of the Apache License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Apache License with the above modification is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the Apache License for the specific
+ * language governing permissions and limitations under the Apache License.
+ *
+ */
+
+CCL_NAMESPACE_BEGIN
+
+typedef struct PatchHandle {
+ int array_index, patch_index, vert_index;
+} PatchHandle;
+
+ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float *v)
+{
+ int quadrant = -1;
+
+ if(*u < median) {
+ if(*v < median) {
+ quadrant = 0;
+ }
+ else {
+ quadrant = 1;
+ *v -= median;
+ }
+ }
+ else {
+ if(*v < median) {
+ quadrant = 3;
+ }
+ else {
+ quadrant = 2;
+ *v -= median;
+ }
+ *u -= median;
+ }
+
+ return quadrant;
+}
+
+/* retrieve PatchHandle from patch coords */
+
+ccl_device_inline PatchHandle patch_map_find_patch(KernelGlobals *kg, int object, int patch, float u, float v)
+{
+ PatchHandle handle;
+
+ kernel_assert((u >= 0.0f) && (u <= 1.0f) && (v >= 0.0f) && (v <= 1.0f));
+
+ int node = (object_patch_map_offset(kg, object) + patch)/2;
+ float median = 0.5f;
+
+ for(int depth = 0; depth < 0xff; depth++) {
+ float delta = median * 0.5f;
+
+ int quadrant = patch_map_resolve_quadrant(median, &u, &v);
+ kernel_assert(quadrant >= 0);
+
+ uint child = kernel_tex_fetch(__patches, node + quadrant);
+
+ /* is the quadrant a hole? */
+ if(!(child & PATCH_MAP_NODE_IS_SET)) {
+ handle.array_index = -1;
+ return handle;
+ }
+
+ uint index = child & PATCH_MAP_NODE_INDEX_MASK;
+
+ if(child & PATCH_MAP_NODE_IS_LEAF) {
+ handle.array_index = kernel_tex_fetch(__patches, index + 0);
+ handle.patch_index = kernel_tex_fetch(__patches, index + 1);
+ handle.vert_index = kernel_tex_fetch(__patches, index + 2);
+
+ return handle;
+ } else {
+ node = index;
+ }
+
+ median = delta;
+ }
+
+ /* no leaf found */
+ kernel_assert(0);
+
+ handle.array_index = -1;
+ return handle;
+}
+
+ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float *deriv)
+{
+ /* The four uniform cubic B-Spline basis functions evaluated at t */
+ float inv_6 = 1.0f / 6.0f;
+
+ float t2 = t * t;
+ float t3 = t * t2;
+
+ point[0] = inv_6 * (1.0f - 3.0f*(t - t2) - t3);
+ point[1] = inv_6 * (4.0f - 6.0f*t2 + 3.0f*t3);
+ point[2] = inv_6 * (1.0f + 3.0f*(t + t2 - t3));
+ point[3] = inv_6 * t3;
+
+ /* Derivatives of the above four basis functions at t */
+ deriv[0] = -0.5f*t2 + t - 0.5f;
+ deriv[1] = 1.5f*t2 - 2.0f*t;
+ deriv[2] = -1.5f*t2 + t + 0.5f;
+ deriv[3] = 0.5f*t2;
+}
+
+ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, float *s, float *t)
+{
+ int boundary = ((bits >> 8) & 0xf);
+
+ if(boundary & 1) {
+ t[2] -= t[0];
+ t[1] += 2*t[0];
+ t[0] = 0;
+ }
+
+ if(boundary & 2) {
+ s[1] -= s[3];
+ s[2] += 2*s[3];
+ s[3] = 0;
+ }
+
+ if(boundary & 4) {
+ t[1] -= t[3];
+ t[2] += 2*t[3];
+ t[3] = 0;
+ }
+
+ if(boundary & 8) {
+ s[2] -= s[0];
+ s[1] += 2*s[0];
+ s[0] = 0;
+ }
+}
+
+ccl_device_inline int patch_eval_depth(uint patch_bits)
+{
+ return (patch_bits & 0xf);
+}
+
+ccl_device_inline float patch_eval_param_fraction(uint patch_bits)
+{
+ bool non_quad_root = (patch_bits >> 4) & 0x1;
+ int depth = patch_eval_depth(patch_bits);
+
+ if(non_quad_root) {
+ return 1.0f / (float)(1 << (depth-1));
+ }
+ else {
+ return 1.0f / (float)(1 << depth);
+ }
+}
+
+ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, float *v)
+{
+ float frac = patch_eval_param_fraction(patch_bits);
+
+ int iu = (patch_bits >> 22) & 0x3ff;
+ int iv = (patch_bits >> 12) & 0x3ff;
+
+ /* top left corner */
+ float pu = (float)iu*frac;
+ float pv = (float)iv*frac;
+
+ /* normalize uv coordinates */
+ *u = (*u - pu) / frac;
+ *v = (*v - pv) / frac;
+}
+
+/* retrieve patch control indices */
+
+ccl_device_inline int patch_eval_indices(KernelGlobals *kg, const PatchHandle *handle, int channel,
+ int indices[PATCH_MAX_CONTROL_VERTS])
+{
+ int index_base = kernel_tex_fetch(__patches, handle->array_index + 2) + handle->vert_index;
+
+ /* XXX: regular patches only */
+ for(int i = 0; i < 16; i++) {
+ indices[i] = kernel_tex_fetch(__patches, index_base + i);
+ }
+
+ return 16;
+}
+
+/* evaluate patch basis functions */
+
+ccl_device_inline void patch_eval_basis(KernelGlobals *kg, const PatchHandle *handle, float u, float v,
+ float weights[PATCH_MAX_CONTROL_VERTS],
+ float weights_du[PATCH_MAX_CONTROL_VERTS],
+ float weights_dv[PATCH_MAX_CONTROL_VERTS])
+{
+ uint patch_bits = kernel_tex_fetch(__patches, handle->patch_index + 1); /* read patch param */
+ float d_scale = 1 << patch_eval_depth(patch_bits);
+
+ bool non_quad_root = (patch_bits >> 4) & 0x1;
+ if(non_quad_root) {
+ d_scale *= 0.5f;
+ }
+
+ patch_eval_normalize_coords(patch_bits, &u, &v);
+
+ /* XXX: regular patches only for now. */
+
+ float s[4], t[4], ds[4], dt[4];
+
+ patch_eval_bspline_weights(u, s, ds);
+ patch_eval_bspline_weights(v, t, dt);
+
+ patch_eval_adjust_boundary_weights(patch_bits, s, t);
+ patch_eval_adjust_boundary_weights(patch_bits, ds, dt);
+
+ for(int k = 0; k < 4; k++) {
+ for(int l = 0; l < 4; l++) {
+ weights[4*k+l] = s[l] * t[k];
+ weights_du[4*k+l] = ds[l] * t[k] * d_scale;
+ weights_dv[4*k+l] = s[l] * dt[k] * d_scale;
+ }
+ }
+}
+
+/* generic function for evaluating indices and weights from patch coords */
+
+ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, int object, int patch, float u, float v, int channel,
+ int indices[PATCH_MAX_CONTROL_VERTS],
+ float weights[PATCH_MAX_CONTROL_VERTS],
+ float weights_du[PATCH_MAX_CONTROL_VERTS],
+ float weights_dv[PATCH_MAX_CONTROL_VERTS])
+{
+ PatchHandle handle = patch_map_find_patch(kg, object, patch, u, v);
+ kernel_assert(handle.array_index >= 0);
+
+ int num_control = patch_eval_indices(kg, &handle, channel, indices);
+ patch_eval_basis(kg, &handle, u, v, weights, weights_du, weights_dv);
+
+ return num_control;
+}
+
+/* functions for evaluating attributes on patches */
+
+ccl_device float patch_eval_float(KernelGlobals *kg, const ShaderData *sd, int offset,
+ int patch, float u, float v, int channel,
+ float *du, float* dv)
+{
+ int indices[PATCH_MAX_CONTROL_VERTS];
+ float weights[PATCH_MAX_CONTROL_VERTS];
+ float weights_du[PATCH_MAX_CONTROL_VERTS];
+ float weights_dv[PATCH_MAX_CONTROL_VERTS];
+
+ int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel,
+ indices, weights, weights_du, weights_dv);
+
+ float val = 0.0f;
+ if(du) *du = 0.0f;
+ if(dv) *dv = 0.0f;
+
+ for(int i = 0; i < num_control; i++) {
+ float v = kernel_tex_fetch(__attributes_float, offset + indices[i]);
+
+ val += v * weights[i];
+ if(du) *du += v * weights_du[i];
+ if(dv) *dv += v * weights_dv[i];
+ }
+
+ return val;
+}
+
+ccl_device float3 patch_eval_float3(KernelGlobals *kg, const ShaderData *sd, int offset,
+ int patch, float u, float v, int channel,
+ float3 *du, float3 *dv)
+{
+ int indices[PATCH_MAX_CONTROL_VERTS];
+ float weights[PATCH_MAX_CONTROL_VERTS];
+ float weights_du[PATCH_MAX_CONTROL_VERTS];
+ float weights_dv[PATCH_MAX_CONTROL_VERTS];
+
+ int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel,
+ indices, weights, weights_du, weights_dv);
+
+ float3 val = make_float3(0.0f, 0.0f, 0.0f);
+ if(du) *du = make_float3(0.0f, 0.0f, 0.0f);
+ if(dv) *dv = make_float3(0.0f, 0.0f, 0.0f);
+
+ for(int i = 0; i < num_control; i++) {
+ float3 v = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + indices[i]));
+
+ val += v * weights[i];
+ if(du) *du += v * weights_du[i];
+ if(dv) *dv += v * weights_dv[i];
+ }
+
+ return val;
+}
+
+ccl_device float3 patch_eval_uchar4(KernelGlobals *kg, const ShaderData *sd, int offset,
+ int patch, float u, float v, int channel,
+ float3 *du, float3 *dv)
+{
+ int indices[PATCH_MAX_CONTROL_VERTS];
+ float weights[PATCH_MAX_CONTROL_VERTS];
+ float weights_du[PATCH_MAX_CONTROL_VERTS];
+ float weights_dv[PATCH_MAX_CONTROL_VERTS];
+
+ int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel,
+ indices, weights, weights_du, weights_dv);
+
+ float3 val = make_float3(0.0f, 0.0f, 0.0f);
+ if(du) *du = make_float3(0.0f, 0.0f, 0.0f);
+ if(dv) *dv = make_float3(0.0f, 0.0f, 0.0f);
+
+ for(int i = 0; i < num_control; i++) {
+ float3 v = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, offset + indices[i]));
+
+ val += v * weights[i];
+ if(du) *du += v * weights_du[i];
+ if(dv) *dv += v * weights_dv[i];
+ }
+
+ return val;
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h
index b1b1e919e00..4384c2093e9 100644
--- a/intern/cycles/kernel/geom/geom_primitive.h
+++ b/intern/cycles/kernel/geom/geom_primitive.h
@@ -23,19 +23,25 @@ CCL_NAMESPACE_BEGIN
/* Generic primitive attribute reading functions */
-ccl_device float primitive_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy)
+ccl_device_inline float primitive_attribute_float(KernelGlobals *kg,
+ const ShaderData *sd,
+ const AttributeDescriptor desc,
+ float *dx, float *dy)
{
if(ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE) {
- return triangle_attribute_float(kg, sd, elem, offset, dx, dy);
+ if(subd_triangle_patch(kg, sd) == ~0)
+ return triangle_attribute_float(kg, sd, desc, dx, dy);
+ else
+ return subd_triangle_attribute_float(kg, sd, desc, dx, dy);
}
#ifdef __HAIR__
else if(ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE) {
- return curve_attribute_float(kg, sd, elem, offset, dx, dy);
+ return curve_attribute_float(kg, sd, desc, dx, dy);
}
#endif
#ifdef __VOLUME__
- else if(ccl_fetch(sd, object) != OBJECT_NONE && elem == ATTR_ELEMENT_VOXEL) {
- return volume_attribute_float(kg, sd, elem, offset, dx, dy);
+ else if(ccl_fetch(sd, object) != OBJECT_NONE && desc.element == ATTR_ELEMENT_VOXEL) {
+ return volume_attribute_float(kg, sd, desc, dx, dy);
}
#endif
else {
@@ -45,19 +51,25 @@ ccl_device float primitive_attribute_float(KernelGlobals *kg, const ShaderData *
}
}
-ccl_device float3 primitive_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy)
+ccl_device_inline float3 primitive_attribute_float3(KernelGlobals *kg,
+ const ShaderData *sd,
+ const AttributeDescriptor desc,
+ float3 *dx, float3 *dy)
{
if(ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE) {
- return triangle_attribute_float3(kg, sd, elem, offset, dx, dy);
+ if(subd_triangle_patch(kg, sd) == ~0)
+ return triangle_attribute_float3(kg, sd, desc, dx, dy);
+ else
+ return subd_triangle_attribute_float3(kg, sd, desc, dx, dy);
}
#ifdef __HAIR__
else if(ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE) {
- return curve_attribute_float3(kg, sd, elem, offset, dx, dy);
+ return curve_attribute_float3(kg, sd, desc, dx, dy);
}
#endif
#ifdef __VOLUME__
- else if(ccl_fetch(sd, object) != OBJECT_NONE && elem == ATTR_ELEMENT_VOXEL) {
- return volume_attribute_float3(kg, sd, elem, offset, dx, dy);
+ else if(ccl_fetch(sd, object) != OBJECT_NONE && desc.element == ATTR_ELEMENT_VOXEL) {
+ return volume_attribute_float3(kg, sd, desc, dx, dy);
}
#endif
else {
@@ -69,15 +81,14 @@ ccl_device float3 primitive_attribute_float3(KernelGlobals *kg, const ShaderData
/* Default UV coordinate */
-ccl_device float3 primitive_uv(KernelGlobals *kg, ShaderData *sd)
+ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd)
{
- AttributeElement elem_uv;
- int offset_uv = find_attribute(kg, sd, ATTR_STD_UV, &elem_uv);
+ const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV);
- if(offset_uv == ATTR_STD_NOT_FOUND)
+ if(desc.offset == ATTR_STD_NOT_FOUND)
return make_float3(0.0f, 0.0f, 0.0f);
- float3 uv = primitive_attribute_float3(kg, sd, elem_uv, offset_uv, NULL, NULL);
+ float3 uv = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
uv.z = 1.0f;
return uv;
}
@@ -87,15 +98,14 @@ ccl_device float3 primitive_uv(KernelGlobals *kg, ShaderData *sd)
ccl_device bool primitive_ptex(KernelGlobals *kg, ShaderData *sd, float2 *uv, int *face_id)
{
/* storing ptex data as attributes is not memory efficient but simple for tests */
- AttributeElement elem_face_id, elem_uv;
- int offset_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID, &elem_face_id);
- int offset_uv = find_attribute(kg, sd, ATTR_STD_PTEX_UV, &elem_uv);
+ const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID);
+ const AttributeDescriptor desc_uv = find_attribute(kg, sd, ATTR_STD_PTEX_UV);
- if(offset_face_id == ATTR_STD_NOT_FOUND || offset_uv == ATTR_STD_NOT_FOUND)
+ if(desc_face_id.offset == ATTR_STD_NOT_FOUND || desc_uv.offset == ATTR_STD_NOT_FOUND)
return false;
- float3 uv3 = primitive_attribute_float3(kg, sd, elem_uv, offset_uv, NULL, NULL);
- float face_id_f = primitive_attribute_float(kg, sd, elem_face_id, offset_face_id, NULL, NULL);
+ float3 uv3 = primitive_attribute_float3(kg, sd, desc_uv, NULL, NULL);
+ float face_id_f = primitive_attribute_float(kg, sd, desc_face_id, NULL, NULL);
*uv = make_float2(uv3.x, uv3.y);
*face_id = (int)face_id_f;
@@ -117,11 +127,10 @@ ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd)
#endif
/* try to create spherical tangent from generated coordinates */
- AttributeElement attr_elem;
- int attr_offset = find_attribute(kg, sd, ATTR_STD_GENERATED, &attr_elem);
+ const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED);
- if(attr_offset != ATTR_STD_NOT_FOUND) {
- float3 data = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL);
+ if(desc.offset != ATTR_STD_NOT_FOUND) {
+ float3 data = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
data = make_float3(-(data.y - 0.5f), (data.x - 0.5f), 0.0f);
object_normal_transform(kg, sd, &data);
return cross(ccl_fetch(sd, N), normalize(cross(data, ccl_fetch(sd, N))));
@@ -138,7 +147,7 @@ ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd)
/* Motion vector for motion pass */
-ccl_device float4 primitive_motion_vector(KernelGlobals *kg, ShaderData *sd)
+ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData *sd)
{
/* center position */
float3 center;
@@ -158,19 +167,18 @@ ccl_device float4 primitive_motion_vector(KernelGlobals *kg, ShaderData *sd)
float3 motion_pre = center, motion_post = center;
/* deformation motion */
- AttributeElement elem;
- int offset = find_attribute(kg, sd, ATTR_STD_MOTION_VERTEX_POSITION, &elem);
+ AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_MOTION_VERTEX_POSITION);
- if(offset != ATTR_STD_NOT_FOUND) {
+ if(desc.offset != ATTR_STD_NOT_FOUND) {
/* get motion info */
int numverts, numkeys;
object_motion_info(kg, ccl_fetch(sd, object), NULL, &numverts, &numkeys);
/* lookup attributes */
- int offset_next = (ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE)? offset + numverts: offset + numkeys;
+ motion_pre = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
- motion_pre = primitive_attribute_float3(kg, sd, elem, offset, NULL, NULL);
- motion_post = primitive_attribute_float3(kg, sd, elem, offset_next, NULL, NULL);
+ desc.offset += (ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE)? numverts: numkeys;
+ motion_post = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
#ifdef __HAIR__
if(is_curve_primitive && (ccl_fetch(sd, flag) & SD_OBJECT_HAS_VERTEX_MOTION) == 0) {
diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/geom_subd_triangle.h
new file mode 100644
index 00000000000..fccacf435f9
--- /dev/null
+++ b/intern/cycles/kernel/geom/geom_subd_triangle.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Functions for retrieving attributes on triangles produced from subdivision meshes */
+
+CCL_NAMESPACE_BEGIN
+
+/* Patch index for triangle, -1 if not subdivision triangle */
+
+ccl_device_inline uint subd_triangle_patch(KernelGlobals *kg, const ShaderData *sd)
+{
+ return (ccl_fetch(sd, prim) != PRIM_NONE) ? kernel_tex_fetch(__tri_patch, ccl_fetch(sd, prim)) : ~0;
+}
+
+/* UV coords of triangle within patch */
+
+ccl_device_inline void subd_triangle_patch_uv(KernelGlobals *kg, const ShaderData *sd, float2 uv[3])
+{
+ uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim));
+
+ uv[0] = kernel_tex_fetch(__tri_patch_uv, tri_vindex.x);
+ uv[1] = kernel_tex_fetch(__tri_patch_uv, tri_vindex.y);
+ uv[2] = kernel_tex_fetch(__tri_patch_uv, tri_vindex.z);
+}
+
+/* Vertex indices of patch */
+
+ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals *kg, int patch)
+{
+ uint4 indices;
+
+ indices.x = kernel_tex_fetch(__patches, patch+0);
+ indices.y = kernel_tex_fetch(__patches, patch+1);
+ indices.z = kernel_tex_fetch(__patches, patch+2);
+ indices.w = kernel_tex_fetch(__patches, patch+3);
+
+ return indices;
+}
+
+/* Originating face for patch */
+
+ccl_device_inline uint subd_triangle_patch_face(KernelGlobals *kg, int patch)
+{
+ return kernel_tex_fetch(__patches, patch+4);
+}
+
+/* Number of corners on originating face */
+
+ccl_device_inline uint subd_triangle_patch_num_corners(KernelGlobals *kg, int patch)
+{
+ return kernel_tex_fetch(__patches, patch+5) & 0xffff;
+}
+
+/* Indices of the four corners that are used by the patch */
+
+ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch, int corners[4])
+{
+ uint4 data;
+
+ data.x = kernel_tex_fetch(__patches, patch+4);
+ data.y = kernel_tex_fetch(__patches, patch+5);
+ data.z = kernel_tex_fetch(__patches, patch+6);
+ data.w = kernel_tex_fetch(__patches, patch+7);
+
+ int num_corners = data.y & 0xffff;
+
+ if(num_corners == 4) {
+ /* quad */
+ corners[0] = data.z;
+ corners[1] = data.z+1;
+ corners[2] = data.z+2;
+ corners[3] = data.z+3;
+ }
+ else {
+ /* ngon */
+ int c = data.y >> 16;
+
+ corners[0] = data.z + c;
+ corners[1] = data.z + mod(c+1, num_corners);
+ corners[2] = data.w;
+ corners[3] = data.z + mod(c-1, num_corners);
+ }
+}
+
+/* Reading attributes on various subdivision triangle elements */
+
+ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
+{
+ int patch = subd_triangle_patch(kg, sd);
+
+ if(desc.flags & ATTR_SUBDIVIDED) {
+ float2 uv[3];
+ subd_triangle_patch_uv(kg, sd, uv);
+
+ float2 dpdu = uv[0] - uv[2];
+ float2 dpdv = uv[1] - uv[2];
+
+ /* p is [s, t] */
+ float2 p = dpdu * ccl_fetch(sd, u) + dpdv * ccl_fetch(sd, v) + uv[2];
+
+ float a, dads, dadt;
+ a = patch_eval_float(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
+
+#ifdef __RAY_DIFFERENTIALS__
+ if(dx || dy) {
+ float dsdu = dpdu.x;
+ float dtdu = dpdu.y;
+ float dsdv = dpdv.x;
+ float dtdv = dpdv.y;
+
+ if(dx) {
+ float dudx = ccl_fetch(sd, du).dx;
+ float dvdx = ccl_fetch(sd, dv).dx;
+
+ float dsdx = dsdu*dudx + dsdv*dvdx;
+ float dtdx = dtdu*dudx + dtdv*dvdx;
+
+ *dx = dads*dsdx + dadt*dtdx;
+ }
+ if(dy) {
+ float dudy = ccl_fetch(sd, du).dy;
+ float dvdy = ccl_fetch(sd, dv).dy;
+
+ float dsdy = dsdu*dudy + dsdv*dvdy;
+ float dtdy = dtdu*dudy + dtdv*dvdy;
+
+ *dy = dads*dsdy + dadt*dtdy;
+ }
+ }
+#endif
+
+ return a;
+ }
+ else if(desc.element == ATTR_ELEMENT_FACE) {
+ if(dx) *dx = 0.0f;
+ if(dy) *dy = 0.0f;
+
+ return kernel_tex_fetch(__attributes_float, desc.offset + subd_triangle_patch_face(kg, patch));
+ }
+ else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
+ float2 uv[3];
+ subd_triangle_patch_uv(kg, sd, uv);
+
+ uint4 v = subd_triangle_patch_indices(kg, patch);
+
+ float f0 = kernel_tex_fetch(__attributes_float, desc.offset + v.x);
+ float f1 = kernel_tex_fetch(__attributes_float, desc.offset + v.y);
+ float f2 = kernel_tex_fetch(__attributes_float, desc.offset + v.z);
+ float f3 = kernel_tex_fetch(__attributes_float, desc.offset + v.w);
+
+ if(subd_triangle_patch_num_corners(kg, patch) != 4) {
+ f1 = (f1+f0)*0.5f;
+ f3 = (f3+f0)*0.5f;
+ }
+
+ float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+ float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+ float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+
+#ifdef __RAY_DIFFERENTIALS__
+ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
+ if(dy) *dy = ccl_fetch(sd, du).dy*a + ccl_fetch(sd, dv).dy*b - (ccl_fetch(sd, du).dy + ccl_fetch(sd, dv).dy)*c;
+#endif
+
+ return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
+ }
+ else if(desc.element == ATTR_ELEMENT_CORNER) {
+ float2 uv[3];
+ subd_triangle_patch_uv(kg, sd, uv);
+
+ int corners[4];
+ subd_triangle_patch_corners(kg, patch, corners);
+
+ float f0 = kernel_tex_fetch(__attributes_float, corners[0] + desc.offset);
+ float f1 = kernel_tex_fetch(__attributes_float, corners[1] + desc.offset);
+ float f2 = kernel_tex_fetch(__attributes_float, corners[2] + desc.offset);
+ float f3 = kernel_tex_fetch(__attributes_float, corners[3] + desc.offset);
+
+ if(subd_triangle_patch_num_corners(kg, patch) != 4) {
+ f1 = (f1+f0)*0.5f;
+ f3 = (f3+f0)*0.5f;
+ }
+
+ float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+ float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+ float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+
+#ifdef __RAY_DIFFERENTIALS__
+ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
+ if(dy) *dy = ccl_fetch(sd, du).dy*a + ccl_fetch(sd, dv).dy*b - (ccl_fetch(sd, du).dy + ccl_fetch(sd, dv).dy)*c;
+#endif
+
+ return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
+ }
+ else {
+ if(dx) *dx = 0.0f;
+ if(dy) *dy = 0.0f;
+
+ return 0.0f;
+ }
+}
+
+ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
+{
+ int patch = subd_triangle_patch(kg, sd);
+
+ if(desc.flags & ATTR_SUBDIVIDED) {
+ float2 uv[3];
+ subd_triangle_patch_uv(kg, sd, uv);
+
+ float2 dpdu = uv[0] - uv[2];
+ float2 dpdv = uv[1] - uv[2];
+
+ /* p is [s, t] */
+ float2 p = dpdu * ccl_fetch(sd, u) + dpdv * ccl_fetch(sd, v) + uv[2];
+
+ float3 a, dads, dadt;
+
+ if(desc.element == ATTR_ELEMENT_CORNER_BYTE) {
+ a = patch_eval_uchar4(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
+ }
+ else {
+ a = patch_eval_float3(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
+ }
+
+#ifdef __RAY_DIFFERENTIALS__
+ if(dx || dy) {
+ float dsdu = dpdu.x;
+ float dtdu = dpdu.y;
+ float dsdv = dpdv.x;
+ float dtdv = dpdv.y;
+
+ if(dx) {
+ float dudx = ccl_fetch(sd, du).dx;
+ float dvdx = ccl_fetch(sd, dv).dx;
+
+ float dsdx = dsdu*dudx + dsdv*dvdx;
+ float dtdx = dtdu*dudx + dtdv*dvdx;
+
+ *dx = dads*dsdx + dadt*dtdx;
+ }
+ if(dy) {
+ float dudy = ccl_fetch(sd, du).dy;
+ float dvdy = ccl_fetch(sd, dv).dy;
+
+ float dsdy = dsdu*dudy + dsdv*dvdy;
+ float dtdy = dtdu*dudy + dtdv*dvdy;
+
+ *dy = dads*dsdy + dadt*dtdy;
+ }
+ }
+#endif
+
+ return a;
+ }
+ else if(desc.element == ATTR_ELEMENT_FACE) {
+ if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
+ if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
+
+ return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + subd_triangle_patch_face(kg, patch)));
+ }
+ else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
+ float2 uv[3];
+ subd_triangle_patch_uv(kg, sd, uv);
+
+ uint4 v = subd_triangle_patch_indices(kg, patch);
+
+ float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.x));
+ float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.y));
+ float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.z));
+ float3 f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.w));
+
+ if(subd_triangle_patch_num_corners(kg, patch) != 4) {
+ f1 = (f1+f0)*0.5f;
+ f3 = (f3+f0)*0.5f;
+ }
+
+ float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+ float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+ float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+
+#ifdef __RAY_DIFFERENTIALS__
+ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
+ if(dy) *dy = ccl_fetch(sd, du).dy*a + ccl_fetch(sd, dv).dy*b - (ccl_fetch(sd, du).dy + ccl_fetch(sd, dv).dy)*c;
+#endif
+
+ return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
+ }
+ else if(desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) {
+ float2 uv[3];
+ subd_triangle_patch_uv(kg, sd, uv);
+
+ int corners[4];
+ subd_triangle_patch_corners(kg, patch, corners);
+
+ float3 f0, f1, f2, f3;
+
+ if(desc.element == ATTR_ELEMENT_CORNER) {
+ f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[0] + desc.offset));
+ f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[1] + desc.offset));
+ f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[2] + desc.offset));
+ f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[3] + desc.offset));
+ }
+ else {
+ f0 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[0] + desc.offset));
+ f1 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[1] + desc.offset));
+ f2 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[2] + desc.offset));
+ f3 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[3] + desc.offset));
+ }
+
+ if(subd_triangle_patch_num_corners(kg, patch) != 4) {
+ f1 = (f1+f0)*0.5f;
+ f3 = (f3+f0)*0.5f;
+ }
+
+ float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
+ float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
+ float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+
+#ifdef __RAY_DIFFERENTIALS__
+ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c;
+ if(dy) *dy = ccl_fetch(sd, du).dy*a + ccl_fetch(sd, dv).dy*b - (ccl_fetch(sd, du).dy + ccl_fetch(sd, dv).dy)*c;
+#endif
+
+ return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
+ }
+ else {
+ if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
+ if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
+
+ return make_float3(0.0f, 0.0f, 0.0f);
+ }
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h
index 0c2351e1d1b..d3289d6572c 100644
--- a/intern/cycles/kernel/geom/geom_triangle.h
+++ b/intern/cycles/kernel/geom/geom_triangle.h
@@ -105,20 +105,20 @@ ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, int prim, ccl_addr_spa
/* Reading attributes on various triangle elements */
-ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy)
+ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
{
- if(elem == ATTR_ELEMENT_FACE) {
+ if(desc.element == ATTR_ELEMENT_FACE) {
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
- return kernel_tex_fetch(__attributes_float, offset + ccl_fetch(sd, prim));
+ return kernel_tex_fetch(__attributes_float, desc.offset + ccl_fetch(sd, prim));
}
- else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) {
+ else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim));
- float f0 = kernel_tex_fetch(__attributes_float, offset + tri_vindex.x);
- float f1 = kernel_tex_fetch(__attributes_float, offset + tri_vindex.y);
- float f2 = kernel_tex_fetch(__attributes_float, offset + tri_vindex.z);
+ float f0 = kernel_tex_fetch(__attributes_float, desc.offset + tri_vindex.x);
+ float f1 = kernel_tex_fetch(__attributes_float, desc.offset + tri_vindex.y);
+ float f2 = kernel_tex_fetch(__attributes_float, desc.offset + tri_vindex.z);
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = ccl_fetch(sd, du).dx*f0 + ccl_fetch(sd, dv).dx*f1 - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*f2;
@@ -127,8 +127,8 @@ ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *s
return ccl_fetch(sd, u)*f0 + ccl_fetch(sd, v)*f1 + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*f2;
}
- else if(elem == ATTR_ELEMENT_CORNER) {
- int tri = offset + ccl_fetch(sd, prim)*3;
+ else if(desc.element == ATTR_ELEMENT_CORNER) {
+ int tri = desc.offset + ccl_fetch(sd, prim)*3;
float f0 = kernel_tex_fetch(__attributes_float, tri + 0);
float f1 = kernel_tex_fetch(__attributes_float, tri + 1);
float f2 = kernel_tex_fetch(__attributes_float, tri + 2);
@@ -148,20 +148,20 @@ ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *s
}
}
-ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy)
+ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
{
- if(elem == ATTR_ELEMENT_FACE) {
+ if(desc.element == ATTR_ELEMENT_FACE) {
if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
- return float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + ccl_fetch(sd, prim)));
+ return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + ccl_fetch(sd, prim)));
}
- else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) {
+ else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim));
- float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + tri_vindex.x));
- float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + tri_vindex.y));
- float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + tri_vindex.z));
+ float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.x));
+ float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.y));
+ float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.z));
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = ccl_fetch(sd, du).dx*f0 + ccl_fetch(sd, dv).dx*f1 - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*f2;
@@ -170,11 +170,11 @@ ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData
return ccl_fetch(sd, u)*f0 + ccl_fetch(sd, v)*f1 + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*f2;
}
- else if(elem == ATTR_ELEMENT_CORNER || elem == ATTR_ELEMENT_CORNER_BYTE) {
- int tri = offset + ccl_fetch(sd, prim)*3;
+ else if(desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) {
+ int tri = desc.offset + ccl_fetch(sd, prim)*3;
float3 f0, f1, f2;
- if(elem == ATTR_ELEMENT_CORNER) {
+ if(desc.element == ATTR_ELEMENT_CORNER) {
f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 0));
f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 1));
f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 2));
diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h
index caa6c9d9a5b..dd5328220ab 100644
--- a/intern/cycles/kernel/geom/geom_triangle_intersect.h
+++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h
@@ -342,9 +342,16 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg,
float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
float3 qvec = cross(tvec, edge1);
float3 pvec = cross(D, edge2);
- float rt = dot(edge2, qvec) / dot(edge1, pvec);
-
- P = P + D*rt;
+ float det = dot(edge1, pvec);
+ if(det != 0.0f) {
+ /* If determinant is zero it means ray lies in the plane of
+ * the triangle. It is possible in theory due to watertight
+ * nature of triangle intersection. For such cases we simply
+ * don't refine intersection hoping it'll go all fine.
+ */
+ float rt = dot(edge2, qvec) / det;
+ P = P + D*rt;
+ }
if(isect->object != OBJECT_NONE) {
# ifdef __OBJECT_MOTION__
@@ -400,9 +407,16 @@ ccl_device_inline float3 triangle_refine_subsurface(KernelGlobals *kg,
float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
float3 qvec = cross(tvec, edge1);
float3 pvec = cross(D, edge2);
- float rt = dot(edge2, qvec) / dot(edge1, pvec);
-
- P = P + D*rt;
+ float det = dot(edge1, pvec);
+ if(det != 0.0f) {
+ /* If determinant is zero it means ray lies in the plane of
+ * the triangle. It is possible in theory due to watertight
+ * nature of triangle intersection. For such cases we simply
+ * don't refine intersection hoping it'll go all fine.
+ */
+ float rt = dot(edge2, qvec) / det;
+ P = P + D*rt;
+ }
#endif /* __INTERSECTION_REFINE__ */
if(isect->object != OBJECT_NONE) {
diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h
index 2044aafc877..efe540a8518 100644
--- a/intern/cycles/kernel/geom/geom_volume.h
+++ b/intern/cycles/kernel/geom/geom_volume.h
@@ -44,40 +44,41 @@ ccl_device float4 volume_image_texture_3d(int id, float x, float y, float z)
}
#endif /* __KERNEL_GPU__ */
-ccl_device float3 volume_normalized_position(KernelGlobals *kg, const ShaderData *sd, float3 P)
+ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg,
+ const ShaderData *sd,
+ float3 P)
{
/* todo: optimize this so it's just a single matrix multiplication when
* possible (not motion blur), or perhaps even just translation + scale */
- AttributeElement attr_elem;
- int attr_offset = find_attribute(kg, sd, ATTR_STD_GENERATED_TRANSFORM, &attr_elem);
+ const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED_TRANSFORM);
object_inverse_position_transform(kg, sd, &P);
- if(attr_offset != ATTR_STD_NOT_FOUND) {
- Transform tfm = primitive_attribute_matrix(kg, sd, attr_offset);
+ if(desc.offset != ATTR_STD_NOT_FOUND) {
+ Transform tfm = primitive_attribute_matrix(kg, sd, desc);
P = transform_point(&tfm, P);
}
return P;
}
-ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int id, float *dx, float *dy)
+ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
{
float3 P = volume_normalized_position(kg, sd, sd->P);
#ifdef __KERNEL_GPU__
# if __CUDA_ARCH__ >= 300
- CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id);
+ CUtexObject tex = kernel_tex_fetch(__bindless_mapping, desc.offset);
float f = kernel_tex_image_interp_3d_float(tex, P.x, P.y, P.z);
float4 r = make_float4(f, f, f, 1.0);
# else
- float4 r = volume_image_texture_3d(id, P.x, P.y, P.z);
+ float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z);
# endif
#else
float4 r;
if(sd->flag & SD_VOLUME_CUBIC)
- r = kernel_tex_image_interp_3d_ex(id, P.x, P.y, P.z, INTERPOLATION_CUBIC);
+ r = kernel_tex_image_interp_3d_ex(desc.offset, P.x, P.y, P.z, INTERPOLATION_CUBIC);
else
- r = kernel_tex_image_interp_3d(id, P.x, P.y, P.z);
+ r = kernel_tex_image_interp_3d(desc.offset, P.x, P.y, P.z);
#endif
if(dx) *dx = 0.0f;
@@ -86,22 +87,22 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd,
return average(float4_to_float3(r));
}
-ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int id, float3 *dx, float3 *dy)
+ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
{
float3 P = volume_normalized_position(kg, sd, sd->P);
#ifdef __KERNEL_GPU__
# if __CUDA_ARCH__ >= 300
- CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id);
+ CUtexObject tex = kernel_tex_fetch(__bindless_mapping, desc.offset);
float4 r = kernel_tex_image_interp_3d_float4(tex, P.x, P.y, P.z);
# else
- float4 r = volume_image_texture_3d(id, P.x, P.y, P.z);
+ float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z);
# endif
#else
float4 r;
if(sd->flag & SD_VOLUME_CUBIC)
- r = kernel_tex_image_interp_3d_ex(id, P.x, P.y, P.z, INTERPOLATION_CUBIC);
+ r = kernel_tex_image_interp_3d_ex(desc.offset, P.x, P.y, P.z, INTERPOLATION_CUBIC);
else
- r = kernel_tex_image_interp_3d(id, P.x, P.y, P.z);
+ r = kernel_tex_image_interp_3d(desc.offset, P.x, P.y, P.z);
#endif
if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h
index 9ee0b09529e..bfbf73df54f 100644
--- a/intern/cycles/kernel/kernel_bake.h
+++ b/intern/cycles/kernel/kernel_bake.h
@@ -18,8 +18,12 @@ CCL_NAMESPACE_BEGIN
#ifdef __BAKING__
-ccl_device void compute_light_pass(KernelGlobals *kg, ShaderData *sd, PathRadiance *L, RNG rng,
- int pass_filter, int sample)
+ccl_device_inline void compute_light_pass(KernelGlobals *kg,
+ ShaderData *sd,
+ PathRadiance *L,
+ RNG rng,
+ int pass_filter,
+ int sample)
{
/* initialize master radiance accumulator */
kernel_assert(kernel_data.film.use_light_pass);
diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h
index f6c103d59dd..88514de514c 100644
--- a/intern/cycles/kernel/kernel_camera.h
+++ b/intern/cycles/kernel/kernel_camera.h
@@ -211,7 +211,10 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl
/* Panorama Camera */
-ccl_device void camera_sample_panorama(KernelGlobals *kg, float raster_x, float raster_y, float lens_u, float lens_v, ccl_addr_space Ray *ray)
+ccl_device_inline void camera_sample_panorama(KernelGlobals *kg,
+ float raster_x, float raster_y,
+ float lens_u, float lens_v,
+ ccl_addr_space Ray *ray)
{
Transform rastertocamera = kernel_data.cam.rastertocamera;
float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f));
@@ -303,8 +306,12 @@ ccl_device void camera_sample_panorama(KernelGlobals *kg, float raster_x, float
/* Common */
-ccl_device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, float filter_v,
- float lens_u, float lens_v, float time, ccl_addr_space Ray *ray)
+ccl_device_inline void camera_sample(KernelGlobals *kg,
+ int x, int y,
+ float filter_u, float filter_v,
+ float lens_u, float lens_v,
+ float time,
+ ccl_addr_space Ray *ray)
{
/* pixel filter */
int filter_table_offset = kernel_data.film.filter_table_offset;
diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/kernel_compat_cuda.h
index 08f6f457805..063220b542e 100644
--- a/intern/cycles/kernel/kernel_compat_cuda.h
+++ b/intern/cycles/kernel/kernel_compat_cuda.h
@@ -36,13 +36,18 @@
/* Qualifier wrappers for different names on different devices */
#define ccl_device __device__ __inline__
-#define ccl_device_inline __device__ __inline__
+#if (__KERNEL_CUDA_VERSION__ == 80) && (__CUDA_ARCH__ < 500)
+# define ccl_device_inline __device__ __forceinline__
+#else
+# define ccl_device_inline __device__ __inline__
+#endif
#define ccl_device_noinline __device__ __noinline__
#define ccl_global
#define ccl_constant
#define ccl_may_alias
#define ccl_addr_space
#define ccl_restrict __restrict__
+#define ccl_align(n) __align__(n)
/* No assert supported for CUDA */
diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h
index 8505cb85576..2ae89dde7c4 100644
--- a/intern/cycles/kernel/kernel_compat_opencl.h
+++ b/intern/cycles/kernel/kernel_compat_opencl.h
@@ -40,6 +40,7 @@
#define ccl_local __local
#define ccl_private __private
#define ccl_restrict restrict
+#define ccl_align(n) __attribute__((aligned(n)))
#ifdef __SPLIT_KERNEL__
# define ccl_addr_space __global
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index 93c4bd3f7d5..1e829eaa1fa 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -44,11 +44,11 @@ typedef struct LightSample {
*
* Note: light_p is modified when sample_coord is true.
*/
-ccl_device float area_light_sample(float3 P,
- float3 *light_p,
- float3 axisu, float3 axisv,
- float randu, float randv,
- bool sample_coord)
+ccl_device_inline float area_light_sample(float3 P,
+ float3 *light_p,
+ float3 axisu, float3 axisv,
+ float randu, float randv,
+ bool sample_coord)
{
/* In our name system we're using P for the center,
* which is o in the paper.
@@ -268,11 +268,11 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side(KernelGlobals
return false;
}
-ccl_device float background_portal_pdf(KernelGlobals *kg,
- float3 P,
- float3 direction,
- int ignore_portal,
- bool *is_possible)
+ccl_device_inline float background_portal_pdf(KernelGlobals *kg,
+ float3 P,
+ float3 direction,
+ int ignore_portal,
+ bool *is_possible)
{
float portal_pdf = 0.0f;
@@ -367,7 +367,10 @@ ccl_device float3 background_portal_sample(KernelGlobals *kg,
return make_float3(0.0f, 0.0f, 0.0f);
}
-ccl_device float3 background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf)
+ccl_device_inline float3 background_light_sample(KernelGlobals *kg,
+ float3 P,
+ float randu, float randv,
+ float *pdf)
{
/* Probability of sampling portals instead of the map. */
float portal_sampling_pdf = kernel_data.integrator.portal_pdf;
@@ -507,8 +510,11 @@ ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3
return t*t/cos_pi;
}
-ccl_device void lamp_light_sample(KernelGlobals *kg, int lamp,
- float randu, float randv, float3 P, LightSample *ls)
+ccl_device_inline void lamp_light_sample(KernelGlobals *kg,
+ int lamp,
+ float randu, float randv,
+ float3 P,
+ LightSample *ls)
{
float4 data0 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 0);
float4 data1 = kernel_tex_fetch(__light_data, lamp*LIGHT_SIZE + 1);
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index d5b31037723..903be4f09a0 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -435,8 +435,12 @@ ccl_device_noinline void kernel_path_ao(KernelGlobals *kg,
}
#ifdef __SUBSURFACE__
-
-ccl_device bool kernel_path_subsurface_scatter(
+# ifndef __KERNEL_CUDA__
+ccl_device
+# else
+ccl_device_inline
+# endif
+bool kernel_path_subsurface_scatter(
KernelGlobals *kg,
ShaderData *sd,
ShaderData *emission_sd,
diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h
index 56516967d8f..64f1468eacf 100644
--- a/intern/cycles/kernel/kernel_path_branched.h
+++ b/intern/cycles/kernel/kernel_path_branched.h
@@ -18,13 +18,13 @@ CCL_NAMESPACE_BEGIN
#ifdef __BRANCHED_PATH__
-ccl_device void kernel_branched_path_ao(KernelGlobals *kg,
- ShaderData *sd,
- ShaderData *emission_sd,
- PathRadiance *L,
- PathState *state,
- RNG *rng,
- float3 throughput)
+ccl_device_inline void kernel_branched_path_ao(KernelGlobals *kg,
+ ShaderData *sd,
+ ShaderData *emission_sd,
+ PathRadiance *L,
+ PathState *state,
+ RNG *rng,
+ float3 throughput)
{
int num_samples = kernel_data.integrator.ao_samples;
float num_samples_inv = 1.0f/num_samples;
diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h
index 74b1ae0ca32..250b8e92a45 100644
--- a/intern/cycles/kernel/kernel_path_surface.h
+++ b/intern/cycles/kernel/kernel_path_surface.h
@@ -222,8 +222,13 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_
#endif
/* path tracing: bounce off or through surface to with new direction stored in ray */
-ccl_device_inline bool kernel_path_surface_bounce(KernelGlobals *kg, ccl_addr_space RNG *rng,
- ShaderData *sd, ccl_addr_space float3 *throughput, ccl_addr_space PathState *state, PathRadiance *L, ccl_addr_space Ray *ray)
+ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg,
+ ccl_addr_space RNG *rng,
+ ShaderData *sd,
+ ccl_addr_space float3 *throughput,
+ ccl_addr_space PathState *state,
+ PathRadiance *L,
+ ccl_addr_space Ray *ray)
{
/* no BSDF? we can stop here */
if(ccl_fetch(sd, flag) & SD_BSDF) {
diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h
index e45522a4641..5fd4f2fad4c 100644
--- a/intern/cycles/kernel/kernel_path_volume.h
+++ b/intern/cycles/kernel/kernel_path_volume.h
@@ -18,8 +18,14 @@ CCL_NAMESPACE_BEGIN
#ifdef __VOLUME_SCATTER__
-ccl_device void kernel_path_volume_connect_light(KernelGlobals *kg, RNG *rng,
- ShaderData *sd, ShaderData *emission_sd, float3 throughput, PathState *state, PathRadiance *L)
+ccl_device_inline void kernel_path_volume_connect_light(
+ KernelGlobals *kg,
+ RNG *rng,
+ ShaderData *sd,
+ ShaderData *emission_sd,
+ float3 throughput,
+ PathState *state,
+ PathRadiance *L)
{
#ifdef __EMISSION__
if(!kernel_data.integrator.use_direct_light)
diff --git a/intern/cycles/kernel/kernel_projection.h b/intern/cycles/kernel/kernel_projection.h
index 8be6742699a..3437d83ed7d 100644
--- a/intern/cycles/kernel/kernel_projection.h
+++ b/intern/cycles/kernel/kernel_projection.h
@@ -130,7 +130,10 @@ ccl_device float2 direction_to_fisheye_equisolid(float3 dir, float lens, float w
return make_float2(u, v);
}
-ccl_device float3 fisheye_equisolid_to_direction(float u, float v, float lens, float fov, float width, float height)
+ccl_device_inline float3 fisheye_equisolid_to_direction(float u, float v,
+ float lens,
+ float fov,
+ float width, float height)
{
u = (u - 0.5f) * width;
v = (v - 0.5f) * height;
@@ -189,7 +192,7 @@ ccl_device float2 direction_to_mirrorball(float3 dir)
return make_float2(u, v);
}
-ccl_device float3 panorama_to_direction(KernelGlobals *kg, float u, float v)
+ccl_device_inline float3 panorama_to_direction(KernelGlobals *kg, float u, float v)
{
switch(kernel_data.cam.panorama_type) {
case PANORAMA_EQUIRECTANGULAR:
@@ -205,7 +208,7 @@ ccl_device float3 panorama_to_direction(KernelGlobals *kg, float u, float v)
}
}
-ccl_device float2 direction_to_panorama(KernelGlobals *kg, float3 dir)
+ccl_device_inline float2 direction_to_panorama(KernelGlobals *kg, float3 dir)
{
switch(kernel_data.cam.panorama_type) {
case PANORAMA_EQUIRECTANGULAR:
@@ -221,9 +224,9 @@ ccl_device float2 direction_to_panorama(KernelGlobals *kg, float3 dir)
}
}
-ccl_device float3 spherical_stereo_position(KernelGlobals *kg,
- float3 dir,
- float3 pos)
+ccl_device_inline float3 spherical_stereo_position(KernelGlobals *kg,
+ float3 dir,
+ float3 pos)
{
float interocular_offset = kernel_data.cam.interocular_offset;
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 765baa2a5ba..079bea30bdd 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -24,6 +24,7 @@
*
*/
+#include "closure/alloc.h"
#include "closure/bsdf_util.h"
#include "closure/bsdf.h"
#include "closure/emissive.h"
@@ -148,8 +149,16 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg,
/* ShaderData setup from BSSRDF scatter */
#ifdef __SUBSURFACE__
-ccl_device_inline void shader_setup_from_subsurface(KernelGlobals *kg, ShaderData *sd,
- const Intersection *isect, const Ray *ray)
+# ifndef __KERNEL_CUDA__
+ccl_device
+# else
+ccl_device_inline
+# endif
+void shader_setup_from_subsurface(
+ KernelGlobals *kg,
+ ShaderData *sd,
+ const Intersection *isect,
+ const Ray *ray)
{
bool backfacing = sd->flag & SD_BACKFACING;
@@ -225,14 +234,14 @@ ccl_device_inline void shader_setup_from_subsurface(KernelGlobals *kg, ShaderDat
/* ShaderData setup from position sampled on mesh */
-ccl_device void shader_setup_from_sample(KernelGlobals *kg,
- ShaderData *sd,
- const float3 P,
- const float3 Ng,
- const float3 I,
- int shader, int object, int prim,
- float u, float v, float t,
- float time)
+ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg,
+ ShaderData *sd,
+ const float3 P,
+ const float3 Ng,
+ const float3 I,
+ int shader, int object, int prim,
+ float u, float v, float t,
+ float time)
{
/* vectors */
ccl_fetch(sd, P) = P;
@@ -444,7 +453,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s
/* Merging */
#if defined(__BRANCHED_PATH__) || defined(__VOLUME__)
-ccl_device void shader_merge_closures(ShaderData *sd)
+ccl_device_inline void shader_merge_closures(ShaderData *sd)
{
/* merge identical closures, better when we sample a single closure at a time */
for(int i = 0; i < sd->num_closure; i++) {
@@ -453,22 +462,9 @@ ccl_device void shader_merge_closures(ShaderData *sd)
for(int j = i + 1; j < sd->num_closure; j++) {
ShaderClosure *scj = &sd->closure[j];
-#ifdef __OSL__
- if(sci->prim || scj->prim)
+ if(sci->type != scj->type)
continue;
-#endif
-
- if(!(sci->type == scj->type && sci->data0 == scj->data0 && sci->data1 == scj->data1 && sci->data2 == scj->data2))
- continue;
-
- if(CLOSURE_IS_BSDF_OR_BSSRDF(sci->type)) {
- if(sci->N != scj->N)
- continue;
- else if(CLOSURE_IS_BSDF_ANISOTROPIC(sci->type) && sci->T != scj->T)
- continue;
- }
-
- if((sd->flag & SD_BSDF_HAS_CUSTOM) && !(sci->custom1 == scj->custom1 && sci->custom2 == scj->custom2 && sci->custom3 == scj->custom3))
+ if(!bsdf_merge(sci, scj))
continue;
sci->weight += scj->weight;
@@ -542,12 +538,18 @@ ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg,
}
#endif
-ccl_device void shader_bsdf_eval(KernelGlobals *kg,
- ShaderData *sd,
- const float3 omega_in,
- BsdfEval *eval,
- float light_pdf,
- bool use_mis)
+
+#ifndef __KERNEL_CUDA__
+ccl_device
+#else
+ccl_device_inline
+#endif
+void shader_bsdf_eval(KernelGlobals *kg,
+ ShaderData *sd,
+ const float3 omega_in,
+ BsdfEval *eval,
+ float light_pdf,
+ bool use_mis)
{
bsdf_eval_init(eval, NBUILTIN_CLOSURES, make_float3(0.0f, 0.0f, 0.0f), kernel_data.film.use_light_pass);
@@ -566,9 +568,13 @@ ccl_device void shader_bsdf_eval(KernelGlobals *kg,
}
}
-ccl_device int shader_bsdf_sample(KernelGlobals *kg, ShaderData *sd,
- float randu, float randv, BsdfEval *bsdf_eval,
- float3 *omega_in, differential3 *domega_in, float *pdf)
+ccl_device_inline int shader_bsdf_sample(KernelGlobals *kg,
+ ShaderData *sd,
+ float randu, float randv,
+ BsdfEval *bsdf_eval,
+ float3 *omega_in,
+ differential3 *domega_in,
+ float *pdf)
{
int sampled = 0;
@@ -741,8 +747,9 @@ ccl_device float3 shader_bsdf_ao(KernelGlobals *kg, ShaderData *sd, float ao_fac
ShaderClosure *sc = ccl_fetch_array(sd, closure, i);
if(CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
+ const DiffuseBsdf *bsdf = (const DiffuseBsdf*)sc;
eval += sc->weight*ao_factor;
- N += sc->N*average(sc->weight);
+ N += bsdf->N*average(sc->weight);
}
else if(CLOSURE_IS_AMBIENT_OCCLUSION(sc->type)) {
eval += sc->weight;
@@ -759,6 +766,7 @@ ccl_device float3 shader_bsdf_ao(KernelGlobals *kg, ShaderData *sd, float ao_fac
return eval;
}
+#ifdef __SUBSURFACE__
ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_blur_)
{
float3 eval = make_float3(0.0f, 0.0f, 0.0f);
@@ -769,11 +777,12 @@ ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_b
ShaderClosure *sc = ccl_fetch_array(sd, closure, i);
if(CLOSURE_IS_BSSRDF(sc->type)) {
+ const Bssrdf *bssrdf = (const Bssrdf*)sc;
float avg_weight = fabsf(average(sc->weight));
- N += sc->N*avg_weight;
+ N += bssrdf->N*avg_weight;
eval += sc->weight;
- texture_blur += sc->data1*avg_weight;
+ texture_blur += bssrdf->texture_blur*avg_weight;
weight_sum += avg_weight;
}
}
@@ -786,6 +795,7 @@ ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_b
return eval;
}
+#endif
/* Emission */
@@ -831,6 +841,7 @@ ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd, ccl_addr_
ccl_addr_space PathState *state, float randb, int path_flag, ShaderContext ctx)
{
ccl_fetch(sd, num_closure) = 0;
+ ccl_fetch(sd, num_closure_extra) = 0;
ccl_fetch(sd, randb_closure) = randb;
#ifdef __OSL__
@@ -861,33 +872,33 @@ ccl_device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd,
ccl_addr_space PathState *state, int path_flag, ShaderContext ctx)
{
ccl_fetch(sd, num_closure) = 0;
+ ccl_fetch(sd, num_closure_extra) = 0;
ccl_fetch(sd, randb_closure) = 0.0f;
+#ifdef __SVM__
#ifdef __OSL__
if(kg->osl) {
- return OSLShader::eval_background(kg, sd, state, path_flag, ctx);
+ OSLShader::eval_background(kg, sd, state, path_flag, ctx);
}
else
#endif
-
{
-#ifdef __SVM__
svm_eval_nodes(kg, sd, state, SHADER_TYPE_SURFACE, path_flag);
+ }
- float3 eval = make_float3(0.0f, 0.0f, 0.0f);
+ float3 eval = make_float3(0.0f, 0.0f, 0.0f);
- for(int i = 0; i < ccl_fetch(sd, num_closure); i++) {
- const ShaderClosure *sc = ccl_fetch_array(sd, closure, i);
+ for(int i = 0; i < ccl_fetch(sd, num_closure); i++) {
+ const ShaderClosure *sc = ccl_fetch_array(sd, closure, i);
- if(CLOSURE_IS_BACKGROUND(sc->type))
- eval += sc->weight;
- }
+ if(CLOSURE_IS_BACKGROUND(sc->type))
+ eval += sc->weight;
+ }
- return eval;
+ return eval;
#else
- return make_float3(0.8f, 0.8f, 0.8f);
+ return make_float3(0.8f, 0.8f, 0.8f);
#endif
- }
}
/* Volume */
@@ -998,12 +1009,17 @@ ccl_device int shader_phase_sample_closure(KernelGlobals *kg, const ShaderData *
/* Volume Evaluation */
-ccl_device void shader_eval_volume(KernelGlobals *kg, ShaderData *sd,
- PathState *state, VolumeStack *stack, int path_flag, ShaderContext ctx)
+ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
+ ShaderData *sd,
+ PathState *state,
+ VolumeStack *stack,
+ int path_flag,
+ ShaderContext ctx)
{
/* reset closures once at the start, we will be accumulating the closures
* for all volumes in the stack into a single array of closures */
sd->num_closure = 0;
+ sd->num_closure_extra = 0;
sd->flag = 0;
for(int i = 0; stack[i].shader != SHADER_NONE; i++) {
@@ -1051,6 +1067,7 @@ ccl_device void shader_eval_volume(KernelGlobals *kg, ShaderData *sd,
ccl_device void shader_eval_displacement(KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, ShaderContext ctx)
{
ccl_fetch(sd, num_closure) = 0;
+ ccl_fetch(sd, num_closure_extra) = 0;
ccl_fetch(sd, randb_closure) = 0.0f;
/* this will modify sd->P */
diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h
index d1576754d2e..db2fc84834a 100644
--- a/intern/cycles/kernel/kernel_shadow.h
+++ b/intern/cycles/kernel/kernel_shadow.h
@@ -75,12 +75,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg, ShaderData *shadow_sd,
}
uint num_hits;
- if(max_hits == 0) {
- blocked = true;
- num_hits = 0;
- } else {
- blocked = scene_intersect_shadow_all(kg, ray, hits, max_hits, &num_hits);
- }
+ blocked = scene_intersect_shadow_all(kg, ray, hits, max_hits, &num_hits);
/* if no opaque surface found but we did find transparent hits, shade them */
if(!blocked && num_hits > 0) {
diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h
index b048bd38fc9..ba45eea6388 100644
--- a/intern/cycles/kernel/kernel_subsurface.h
+++ b/intern/cycles/kernel/kernel_subsurface.h
@@ -85,7 +85,16 @@ ccl_device ShaderClosure *subsurface_scatter_pick_closure(KernelGlobals *kg, Sha
return NULL;
}
-ccl_device float3 subsurface_scatter_eval(ShaderData *sd, ShaderClosure *sc, float disk_r, float r, bool all)
+#ifndef __KERNEL_GPU__
+ccl_device_noinline
+#else
+ccl_device_inline
+#endif
+float3 subsurface_scatter_eval(ShaderData *sd,
+ ShaderClosure *sc,
+ float disk_r,
+ float r,
+ bool all)
{
#ifdef BSSRDF_MULTI_EVAL
/* this is the veach one-sample model with balance heuristic, some pdf
@@ -140,24 +149,21 @@ ccl_device void subsurface_scatter_setup_diffuse_bsdf(ShaderData *sd, float3 wei
{
sd->flag &= ~SD_CLOSURE_FLAGS;
sd->randb_closure = 0.0f;
+ sd->num_closure = 0;
+ sd->num_closure_extra = 0;
if(hit) {
- ShaderClosure *sc = &sd->closure[0];
- sd->num_closure = 1;
-
- sc->weight = weight;
- sc->sample_weight = 1.0f;
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->N = N;
- sd->flag |= bsdf_diffuse_setup(sc);
-
- /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes
- * can recognize it as not being a regular diffuse closure */
- sc->type = CLOSURE_BSDF_BSSRDF_ID;
+ DiffuseBsdf *bsdf = (DiffuseBsdf*)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight);
+
+ if(bsdf) {
+ bsdf->N = N;
+ sd->flag |= bsdf_diffuse_setup(bsdf);
+
+ /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes
+ * can recognize it as not being a regular diffuse closure */
+ bsdf->type = CLOSURE_BSDF_BSSRDF_ID;
+ }
}
- else
- sd->num_closure = 0;
}
/* optionally do blurring of color and/or bump mapping, at the cost of a shader evaluation */
@@ -217,7 +223,12 @@ ccl_device void subsurface_color_bump_blur(KernelGlobals *kg,
/* Subsurface scattering step, from a point on the surface to other
* nearby points on the same object.
*/
-ccl_device int subsurface_scatter_multi_intersect(
+#ifndef __KERNEL_CUDA__
+ccl_device
+#else
+ccl_device_inline
+#endif
+int subsurface_scatter_multi_intersect(
KernelGlobals *kg,
SubsurfaceIntersection* ss_isect,
ShaderData *sd,
diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h
index 5ba262c1044..7d6fec02331 100644
--- a/intern/cycles/kernel/kernel_textures.h
+++ b/intern/cycles/kernel/kernel_textures.h
@@ -41,11 +41,16 @@ KERNEL_TEX(float4, texture_float4, __objects_vector)
KERNEL_TEX(uint, texture_uint, __tri_shader)
KERNEL_TEX(float4, texture_float4, __tri_vnormal)
KERNEL_TEX(uint4, texture_uint4, __tri_vindex)
+KERNEL_TEX(uint, texture_uint, __tri_patch)
+KERNEL_TEX(float2, texture_float2, __tri_patch_uv)
/* curves */
KERNEL_TEX(float4, texture_float4, __curves)
KERNEL_TEX(float4, texture_float4, __curve_keys)
+/* patches */
+KERNEL_TEX(uint, texture_uint, __patches)
+
/* attributes */
KERNEL_TEX(uint4, texture_uint4, __attributes_map)
KERNEL_TEX(float, texture_float, __attributes_float)
@@ -173,9 +178,6 @@ KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_086)
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_087)
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_088)
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_089)
-KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_090)
-KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_091)
-KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_092)
# else
/* bindless textures */
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index a9be2ae717a..f3b10c21b9d 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -34,7 +34,7 @@
CCL_NAMESPACE_BEGIN
/* constants */
-#define OBJECT_SIZE 11
+#define OBJECT_SIZE 12
#define OBJECT_VECTOR_SIZE 6
#define LIGHT_SIZE 5
#define FILTER_TABLE_SIZE 1024
@@ -573,8 +573,13 @@ typedef enum PrimitiveType {
/* Attributes */
-#define ATTR_PRIM_TYPES 2
-#define ATTR_PRIM_CURVE 1
+typedef enum AttributePrimitive {
+ ATTR_PRIM_TRIANGLE = 0,
+ ATTR_PRIM_CURVE,
+ ATTR_PRIM_SUBD,
+
+ ATTR_PRIM_TYPES
+} AttributePrimitive;
typedef enum AttributeElement {
ATTR_ELEMENT_NONE,
@@ -619,6 +624,18 @@ typedef enum AttributeStandard {
ATTR_STD_NOT_FOUND = ~0
} AttributeStandard;
+typedef enum AttributeFlag {
+ ATTR_FINAL_SIZE = (1 << 0),
+ ATTR_SUBDIVIDED = (1 << 1),
+} AttributeFlag;
+
+typedef struct AttributeDescriptor {
+ AttributeElement element;
+ NodeAttributeType type;
+ uint flags; /* see enum AttributeFlag */
+ int offset;
+} AttributeDescriptor;
+
/* Closure data */
#ifdef __MULTI_CLOSURE__
@@ -631,33 +648,26 @@ typedef enum AttributeStandard {
# define MAX_CLOSURE 1
#endif
-/* This struct is to be 16 bytes aligned, we also keep some extra precautions:
- * - All the float3 members are in the beginning of the struct, so compiler
- * does not put own padding trying to align this members.
- * - We make sure OSL pointer is also 16 bytes aligned.
- */
-typedef ccl_addr_space struct ShaderClosure {
- float3 weight;
- float3 N;
- float3 T;
-
- ClosureType type;
- float sample_weight;
- float data0;
- float data1;
- float data2;
-
- /* Following fields could be used to store pre-calculated
- * values by various BSDF closures for more effective sampling
- * and evaluation.
- */
- float custom1;
- float custom2;
- float custom3;
+/* This struct is the base class for all closures. The common members are
+ * duplicated in all derived classes since we don't have C++ in the kernel
+ * yet, and because it lets us lay out the members to minimize padding. The
+ * weight member is located at the beginning of the struct for this reason.
+ *
+ * ShaderClosure has a fixed size, and any extra space must be allocated
+ * with closure_alloc_extra().
+ *
+ * We pad the struct to 80 bytes and ensure it is aligned to 16 bytes, which
+ * we assume to be the maximum required alignment for any struct. */
-#ifdef __OSL__
- void *prim, *pad4;
-#endif
+#define SHADER_CLOSURE_BASE \
+ float3 weight; \
+ ClosureType type; \
+ float sample_weight \
+
+typedef ccl_addr_space struct ccl_align(16) ShaderClosure {
+ SHADER_CLOSURE_BASE;
+
+ float data[14]; /* pad to 80 bytes */
} ShaderClosure;
/* Shader Context
@@ -692,11 +702,10 @@ enum ShaderDataFlag {
SD_AO = (1 << 8), /* have ao closure? */
SD_TRANSPARENT = (1 << 9), /* have transparent closure? */
SD_BSDF_NEEDS_LCG = (1 << 10),
- SD_BSDF_HAS_CUSTOM = (1 << 11), /* are the custom variables relevant? */
SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSSRDF|
SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO|
- SD_BSDF_NEEDS_LCG|SD_BSDF_HAS_CUSTOM),
+ SD_BSDF_NEEDS_LCG),
/* shader flags */
SD_USE_MIS = (1 << 12), /* direct light sample */
@@ -729,94 +738,97 @@ enum ShaderDataFlag {
SD_OBJECT_INTERSECTS_VOLUME)
};
-struct KernelGlobals;
-
#ifdef __SPLIT_KERNEL__
# define SD_THREAD (get_global_id(1) * get_global_size(0) + get_global_id(0))
# if defined(__SPLIT_KERNEL_AOS__)
/* ShaderData is stored as an Array-of-Structures */
-# define ccl_fetch(s, t) (s[SD_THREAD].t)
-# define ccl_fetch_array(s, t, index) (&s[SD_THREAD].t[index])
+# define ccl_soa_member(type, name) type soa_##name
+# define ccl_fetch(s, t) (s[SD_THREAD].soa_##t)
+# define ccl_fetch_array(s, t, index) (&s[SD_THREAD].soa_##t[index])
# else
/* ShaderData is stored as an Structure-of-Arrays */
# define SD_GLOBAL_SIZE (get_global_size(0) * get_global_size(1))
# define SD_FIELD_SIZE(t) sizeof(((struct ShaderData*)0)->t)
# define SD_OFFSETOF(t) ((char*)(&((struct ShaderData*)0)->t) - (char*)0)
-# define ccl_fetch(s, t) (((ShaderData*)((ccl_addr_space char*)s + SD_GLOBAL_SIZE * SD_OFFSETOF(t) + SD_FIELD_SIZE(t) * SD_THREAD - SD_OFFSETOF(t)))->t)
+# define ccl_soa_member(type, name) type soa_##name
+# define ccl_fetch(s, t) (((ShaderData*)((ccl_addr_space char*)s + SD_GLOBAL_SIZE * SD_OFFSETOF(soa_##t) + SD_FIELD_SIZE(soa_##t) * SD_THREAD - SD_OFFSETOF(soa_##t)))->soa_##t)
# define ccl_fetch_array(s, t, index) (&ccl_fetch(s, t)[index])
# endif
#else
+# define ccl_soa_member(type, name) type name
# define ccl_fetch(s, t) (s->t)
# define ccl_fetch_array(s, t, index) (&s->t[index])
#endif
typedef ccl_addr_space struct ShaderData {
/* position */
- float3 P;
+ ccl_soa_member(float3, P);
/* smooth normal for shading */
- float3 N;
+ ccl_soa_member(float3, N);
/* true geometric normal */
- float3 Ng;
+ ccl_soa_member(float3, Ng);
/* view/incoming direction */
- float3 I;
+ ccl_soa_member(float3, I);
/* shader id */
- int shader;
+ ccl_soa_member(int, shader);
/* booleans describing shader, see ShaderDataFlag */
- int flag;
+ ccl_soa_member(int, flag);
/* primitive id if there is one, ~0 otherwise */
- int prim;
+ ccl_soa_member(int, prim);
/* combined type and curve segment for hair */
- int type;
+ ccl_soa_member(int, type);
/* parametric coordinates
* - barycentric weights for triangles */
- float u;
- float v;
+ ccl_soa_member(float, u);
+ ccl_soa_member(float, v);
/* object id if there is one, ~0 otherwise */
- int object;
+ ccl_soa_member(int, object);
/* motion blur sample time */
- float time;
+ ccl_soa_member(float, time);
/* length of the ray being shaded */
- float ray_length;
+ ccl_soa_member(float, ray_length);
#ifdef __RAY_DIFFERENTIALS__
/* differential of P. these are orthogonal to Ng, not N */
- differential3 dP;
+ ccl_soa_member(differential3, dP);
/* differential of I */
- differential3 dI;
+ ccl_soa_member(differential3, dI);
/* differential of u, v */
- differential du;
- differential dv;
+ ccl_soa_member(differential, du);
+ ccl_soa_member(differential, dv);
#endif
#ifdef __DPDU__
/* differential of P w.r.t. parametric coordinates. note that dPdu is
* not readily suitable as a tangent for shading on triangles. */
- float3 dPdu;
- float3 dPdv;
+ ccl_soa_member(float3, dPdu);
+ ccl_soa_member(float3, dPdv);
#endif
#ifdef __OBJECT_MOTION__
/* object <-> world space transformations, cached to avoid
* re-interpolating them constantly for shading */
- Transform ob_tfm;
- Transform ob_itfm;
+ ccl_soa_member(Transform, ob_tfm);
+ ccl_soa_member(Transform, ob_itfm);
#endif
/* Closure data, we store a fixed array of closures */
- struct ShaderClosure closure[MAX_CLOSURE];
- int num_closure;
- float randb_closure;
+ ccl_soa_member(struct ShaderClosure, closure[MAX_CLOSURE]);
+ ccl_soa_member(int, num_closure);
+ ccl_soa_member(int, num_closure_extra);
+ ccl_soa_member(float, randb_closure);
+ ccl_soa_member(float3, svm_closure_weight);
/* LCG state for closures that require additional random numbers. */
- uint lcg_state;
+ ccl_soa_member(uint, lcg_state);
/* ray start position, only set for backgrounds */
- float3 ray_P;
- differential3 ray_dP;
+ ccl_soa_member(float3, ray_P);
+ ccl_soa_member(differential3, ray_dP);
#ifdef __OSL__
struct KernelGlobals * osl_globals;
@@ -1234,6 +1246,16 @@ enum RayState {
#define REMOVE_RAY_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] = (ray_state[ray_index] & (~flag)))
#define IS_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] & flag)
+/* Patches */
+
+#define PATCH_MAX_CONTROL_VERTS 16
+
+/* Patch map node flags */
+
+#define PATCH_MAP_NODE_IS_SET (1 << 30)
+#define PATCH_MAP_NODE_IS_LEAF (1 << 31)
+#define PATCH_MAP_NODE_INDEX_MASK (~(PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF))
+
CCL_NAMESPACE_END
#endif /* __KERNEL_TYPES_H__ */
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index bf8301fe5fb..9dafed9afd1 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -36,7 +36,11 @@ typedef struct VolumeShaderCoefficients {
} VolumeShaderCoefficients;
/* evaluate shader to get extinction coefficient at P */
-ccl_device bool volume_shader_extinction_sample(KernelGlobals *kg, ShaderData *sd, PathState *state, float3 P, float3 *extinction)
+ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg,
+ ShaderData *sd,
+ PathState *state,
+ float3 P,
+ float3 *extinction)
{
sd->P = P;
shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
@@ -58,7 +62,11 @@ ccl_device bool volume_shader_extinction_sample(KernelGlobals *kg, ShaderData *s
}
/* evaluate shader to get absorption, scattering and emission at P */
-ccl_device bool volume_shader_sample(KernelGlobals *kg, ShaderData *sd, PathState *state, float3 P, VolumeShaderCoefficients *coeff)
+ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
+ ShaderData *sd,
+ PathState *state,
+ float3 P,
+ VolumeShaderCoefficients *coeff)
{
sd->P = P;
shader_eval_volume(kg, sd, state, state->volume_stack, state->flag, SHADER_CONTEXT_VOLUME);
@@ -1029,7 +1037,7 @@ ccl_device void kernel_volume_stack_init(KernelGlobals *kg,
int stack_index = 0, enclosed_index = 0;
#ifdef __VOLUME_RECORD_ALL__
- Intersection hits[2*VOLUME_STACK_SIZE];
+ Intersection hits[2*VOLUME_STACK_SIZE + 1];
uint num_hits = scene_intersect_volume_all(kg,
&volume_ray,
hits,
@@ -1199,7 +1207,7 @@ ccl_device void kernel_volume_stack_update_for_subsurface(KernelGlobals *kg,
Ray volume_ray = *ray;
# ifdef __VOLUME_RECORD_ALL__
- Intersection hits[2*VOLUME_STACK_SIZE];
+ Intersection hits[2*VOLUME_STACK_SIZE + 1];
uint num_hits = scene_intersect_volume_all(kg,
&volume_ray,
hits,
diff --git a/intern/cycles/kernel/osl/CMakeLists.txt b/intern/cycles/kernel/osl/CMakeLists.txt
index 9cf4f2d759a..98de40e5a8a 100644
--- a/intern/cycles/kernel/osl/CMakeLists.txt
+++ b/intern/cycles/kernel/osl/CMakeLists.txt
@@ -25,7 +25,6 @@ set(SRC
)
set(HEADER_SRC
- osl_bssrdf.h
osl_closures.h
osl_globals.h
osl_services.h
diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp
index 85fa7b34bcc..d835f9be45c 100644
--- a/intern/cycles/kernel/osl/background.cpp
+++ b/intern/cycles/kernel/osl/background.cpp
@@ -36,6 +36,9 @@
#include "osl_closures.h"
+#include "kernel_compat_cpu.h"
+#include "closure/alloc.h"
+
CCL_NAMESPACE_BEGIN
using namespace OSL;
@@ -48,7 +51,10 @@ using namespace OSL;
///
class GenericBackgroundClosure : public CClosurePrimitive {
public:
- GenericBackgroundClosure() : CClosurePrimitive(Background) {}
+ void setup(ShaderData *sd, int /* path_flag */, float3 weight)
+ {
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_BACKGROUND_ID, weight);
+ }
};
/// Holdout closure
@@ -60,7 +66,11 @@ public:
///
class HoldoutClosure : CClosurePrimitive {
public:
- HoldoutClosure () : CClosurePrimitive(Holdout) {}
+ void setup(ShaderData *sd, int /* path_flag */, float3 weight)
+ {
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, weight);
+ sd->flag |= SD_HOLDOUT;
+ }
};
/// ambient occlusion closure
@@ -71,7 +81,11 @@ public:
///
class AmbientOcclusionClosure : public CClosurePrimitive {
public:
- AmbientOcclusionClosure () : CClosurePrimitive(AmbientOcclusion) {}
+ void setup(ShaderData *sd, int /* path_flag */, float3 weight)
+ {
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_AMBIENT_OCCLUSION_ID, weight);
+ sd->flag |= SD_AO;
+ }
};
ClosureParam *closure_background_params()
diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp
index b5c0d76cf37..bc26f42b559 100644
--- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp
+++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp
@@ -39,6 +39,7 @@
#include "kernel_types.h"
#include "kernel_montecarlo.h"
+#include "closure/alloc.h"
#include "closure/bsdf_diffuse_ramp.h"
CCL_NAMESPACE_BEGIN
@@ -47,51 +48,30 @@ using namespace OSL;
class DiffuseRampClosure : public CBSDFClosure {
public:
+ DiffuseRampBsdf params;
Color3 colors[8];
- float3 fcolors[8];
- DiffuseRampClosure() : CBSDFClosure(LABEL_DIFFUSE)
- {}
-
- void setup()
+ void setup(ShaderData *sd, int /* path_flag */, float3 weight)
{
- sc.prim = this;
- m_shaderdata_flag = bsdf_diffuse_ramp_setup(&sc);
+ DiffuseRampBsdf *bsdf = (DiffuseRampBsdf*)bsdf_alloc_osl(sd, sizeof(DiffuseRampBsdf), weight, &params);
- for(int i = 0; i < 8; i++)
- fcolors[i] = TO_FLOAT3(colors[i]);
- }
+ if(bsdf) {
+ bsdf->colors = (float3*)closure_alloc_extra(sd, sizeof(float3)*8);
- void blur(float roughness)
- {
- bsdf_diffuse_ramp_blur(&sc, roughness);
- }
+ if(bsdf->colors) {
+ for(int i = 0; i < 8; i++)
+ bsdf->colors[i] = TO_FLOAT3(colors[i]);
- float3 eval_reflect(const float3 &omega_out, const float3 &omega_in, float& pdf) const
- {
- return bsdf_diffuse_ramp_eval_reflect(&sc, fcolors, omega_out, omega_in, &pdf);
- }
-
- float3 eval_transmit(const float3 &omega_out, const float3 &omega_in, float& pdf) const
- {
- return bsdf_diffuse_ramp_eval_transmit(&sc, fcolors, omega_out, omega_in, &pdf);
- }
-
- int sample(const float3 &Ng,
- const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy,
- float randu, float randv,
- float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy,
- float &pdf, float3 &eval) const
- {
- return bsdf_diffuse_ramp_sample(&sc, fcolors, Ng, omega_out, domega_out_dx, domega_out_dy,
- randu, randv, &eval, &omega_in, &domega_in_dx, &domega_in_dy, &pdf);
+ sd->flag |= bsdf_diffuse_ramp_setup(bsdf);
+ }
+ }
}
};
ClosureParam *closure_bsdf_diffuse_ramp_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(DiffuseRampClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(DiffuseRampClosure, params.N),
CLOSURE_COLOR_ARRAY_PARAM(DiffuseRampClosure, colors, 8),
CLOSURE_STRING_KEYPARAM(DiffuseRampClosure, label, "label"),
CLOSURE_FINISH_PARAM(DiffuseRampClosure)
diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp
index bc73d80cd78..14c7644936e 100644
--- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp
+++ b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp
@@ -38,6 +38,7 @@
#include "osl_closures.h"
#include "kernel_types.h"
+#include "closure/alloc.h"
#include "closure/bsdf_phong_ramp.h"
CCL_NAMESPACE_BEGIN
@@ -46,52 +47,31 @@ using namespace OSL;
class PhongRampClosure : public CBSDFClosure {
public:
+ PhongRampBsdf params;
Color3 colors[8];
- float3 fcolors[8];
- PhongRampClosure() : CBSDFClosure(LABEL_GLOSSY)
- {}
-
- void setup()
+ void setup(ShaderData *sd, int /* path_flag */, float3 weight)
{
- sc.prim = this;
- m_shaderdata_flag = bsdf_phong_ramp_setup(&sc);
+ PhongRampBsdf *bsdf = (PhongRampBsdf*)bsdf_alloc_osl(sd, sizeof(PhongRampBsdf), weight, &params);
- for(int i = 0; i < 8; i++)
- fcolors[i] = TO_FLOAT3(colors[i]);
- }
+ if(bsdf) {
+ bsdf->colors = (float3*)closure_alloc_extra(sd, sizeof(float3)*8);
- void blur(float roughness)
- {
- bsdf_phong_ramp_blur(&sc, roughness);
- }
+ if(bsdf->colors) {
+ for(int i = 0; i < 8; i++)
+ bsdf->colors[i] = TO_FLOAT3(colors[i]);
- float3 eval_reflect(const float3 &omega_out, const float3 &omega_in, float& pdf) const
- {
- return bsdf_phong_ramp_eval_reflect(&sc, fcolors, omega_out, omega_in, &pdf);
- }
-
- float3 eval_transmit(const float3 &omega_out, const float3 &omega_in, float& pdf) const
- {
- return bsdf_phong_ramp_eval_transmit(&sc, fcolors, omega_out, omega_in, &pdf);
- }
-
- int sample(const float3 &Ng,
- const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy,
- float randu, float randv,
- float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy,
- float &pdf, float3 &eval) const
- {
- return bsdf_phong_ramp_sample(&sc, fcolors, Ng, omega_out, domega_out_dx, domega_out_dy,
- randu, randv, &eval, &omega_in, &domega_in_dx, &domega_in_dy, &pdf);
+ sd->flag |= bsdf_phong_ramp_setup(bsdf);
+ }
+ }
}
};
ClosureParam *closure_bsdf_phong_ramp_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(PhongRampClosure, sc.N),
- CLOSURE_FLOAT_PARAM(PhongRampClosure, sc.data0),
+ CLOSURE_FLOAT3_PARAM(PhongRampClosure, params.N),
+ CLOSURE_FLOAT_PARAM(PhongRampClosure, params.exponent),
CLOSURE_COLOR_ARRAY_PARAM(PhongRampClosure, colors, 8),
CLOSURE_STRING_KEYPARAM(PhongRampClosure, label, "label"),
CLOSURE_FINISH_PARAM(PhongRampClosure)
diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp
index f91fd6e015c..3f13e08b302 100644
--- a/intern/cycles/kernel/osl/emissive.cpp
+++ b/intern/cycles/kernel/osl/emissive.cpp
@@ -36,7 +36,9 @@
#include "osl_closures.h"
+#include "kernel_compat_cpu.h"
#include "kernel_types.h"
+#include "closure/alloc.h"
#include "closure/emissive.h"
CCL_NAMESPACE_BEGIN
@@ -52,25 +54,10 @@ using namespace OSL;
///
class GenericEmissiveClosure : public CClosurePrimitive {
public:
- GenericEmissiveClosure() : CClosurePrimitive(Emissive) { }
-
- Color3 eval(const Vec3 &Ng, const Vec3 &omega_out) const
- {
- float3 result = emissive_simple_eval(TO_FLOAT3(Ng), TO_FLOAT3(omega_out));
- return TO_COLOR3(result);
- }
-
- void sample(const Vec3 &Ng, float randu, float randv,
- Vec3 &omega_out, float &pdf) const
- {
- float3 omega_out_;
- emissive_sample(TO_FLOAT3(Ng), randu, randv, &omega_out_, &pdf);
- omega_out = TO_VEC3(omega_out_);
- }
-
- float pdf(const Vec3 &Ng, const Vec3 &omega_out) const
+ void setup(ShaderData *sd, int /* path_flag */, float3 weight)
{
- return emissive_pdf(TO_FLOAT3(Ng), TO_FLOAT3(omega_out));
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_EMISSION_ID, weight);
+ sd->flag |= SD_EMISSION;
}
};
diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp
index da4afb138f6..3614717e28c 100644
--- a/intern/cycles/kernel/osl/osl_bssrdf.cpp
+++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp
@@ -30,17 +30,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <OpenImageIO/fmath.h>
-
#include <OSL/genclosure.h>
#include "kernel_compat_cpu.h"
-#include "osl_bssrdf.h"
#include "osl_closures.h"
#include "kernel_types.h"
#include "kernel_montecarlo.h"
+#include "closure/alloc.h"
#include "closure/bsdf_diffuse.h"
#include "closure/bssrdf.h"
@@ -48,27 +46,83 @@ CCL_NAMESPACE_BEGIN
using namespace OSL;
+class CBSSRDFClosure : public CClosurePrimitive {
+public:
+ Bssrdf params;
+ float3 radius;
+ float3 albedo;
+
+ void alloc(ShaderData *sd, int path_flag, float3 weight, ClosureType type)
+ {
+ float sample_weight = fabsf(average(weight));
+
+ /* disable in case of diffuse ancestor, can't see it well then and
+ * adds considerably noise due to probabilities of continuing path
+ * getting lower and lower */
+ if(path_flag & PATH_RAY_DIFFUSE_ANCESTOR) {
+ radius = make_float3(0.0f, 0.0f, 0.0f);
+ }
+
+ if(sample_weight > CLOSURE_WEIGHT_CUTOFF) {
+ /* sharpness */
+ float sharpness = params.sharpness;
+ /* texture color blur */
+ float texture_blur = params.texture_blur;
+
+ /* create one closure per color channel */
+ Bssrdf *bssrdf = bssrdf_alloc(sd, make_float3(weight.x, 0.0f, 0.0f));
+ if(bssrdf) {
+ bssrdf->sample_weight = sample_weight;
+ bssrdf->radius = radius.x;
+ bssrdf->texture_blur = texture_blur;
+ bssrdf->albedo = albedo.x;
+ bssrdf->sharpness = sharpness;
+ bssrdf->N = params.N;
+ ccl_fetch(sd, flag) |= bssrdf_setup(bssrdf, (ClosureType)type);
+ }
+
+ bssrdf = bssrdf_alloc(sd, make_float3(0.0f, weight.y, 0.0f));
+ if(bssrdf) {
+ bssrdf->sample_weight = sample_weight;
+ bssrdf->radius = radius.y;
+ bssrdf->texture_blur = texture_blur;
+ bssrdf->albedo = albedo.y;
+ bssrdf->sharpness = sharpness;
+ bssrdf->N = params.N;
+ ccl_fetch(sd, flag) |= bssrdf_setup(bssrdf, (ClosureType)type);
+ }
+
+ bssrdf = bssrdf_alloc(sd, make_float3(0.0f, 0.0f, weight.z));
+ if(bssrdf) {
+ bssrdf->sample_weight = sample_weight;
+ bssrdf->radius = radius.z;
+ bssrdf->texture_blur = texture_blur;
+ bssrdf->albedo = albedo.z;
+ bssrdf->sharpness = sharpness;
+ bssrdf->N = params.N;
+ ccl_fetch(sd, flag) |= bssrdf_setup(bssrdf, (ClosureType)type);
+ }
+ }
+ }
+};
+
/* Cubic */
class CubicBSSRDFClosure : public CBSSRDFClosure {
public:
- CubicBSSRDFClosure()
- {}
-
- void setup()
+ void setup(ShaderData *sd, int path_flag, float3 weight)
{
- sc.type = CLOSURE_BSSRDF_CUBIC_ID;
- sc.data0 = fabsf(average(radius));
+ alloc(sd, path_flag, weight, CLOSURE_BSSRDF_CUBIC_ID);
}
};
ClosureParam *closure_bssrdf_cubic_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(CubicBSSRDFClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(CubicBSSRDFClosure, params.N),
CLOSURE_FLOAT3_PARAM(CubicBSSRDFClosure, radius),
- CLOSURE_FLOAT_PARAM(CubicBSSRDFClosure, sc.data1),
- CLOSURE_FLOAT_PARAM(CubicBSSRDFClosure, sc.T.x),
+ CLOSURE_FLOAT_PARAM(CubicBSSRDFClosure, params.texture_blur),
+ CLOSURE_FLOAT_PARAM(CubicBSSRDFClosure, params.sharpness),
CLOSURE_STRING_KEYPARAM(CubicBSSRDFClosure, label, "label"),
CLOSURE_FINISH_PARAM(CubicBSSRDFClosure)
};
@@ -81,22 +135,18 @@ CCLOSURE_PREPARE(closure_bssrdf_cubic_prepare, CubicBSSRDFClosure)
class GaussianBSSRDFClosure : public CBSSRDFClosure {
public:
- GaussianBSSRDFClosure()
- {}
-
- void setup()
+ void setup(ShaderData *sd, int path_flag, float3 weight)
{
- sc.type = CLOSURE_BSSRDF_GAUSSIAN_ID;
- sc.data0 = fabsf(average(radius));
+ alloc(sd, path_flag, weight, CLOSURE_BSSRDF_GAUSSIAN_ID);
}
};
ClosureParam *closure_bssrdf_gaussian_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(GaussianBSSRDFClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(GaussianBSSRDFClosure, params.N),
CLOSURE_FLOAT3_PARAM(GaussianBSSRDFClosure, radius),
- CLOSURE_FLOAT_PARAM(GaussianBSSRDFClosure, sc.data1),
+ CLOSURE_FLOAT_PARAM(GaussianBSSRDFClosure, params.texture_blur),
CLOSURE_STRING_KEYPARAM(GaussianBSSRDFClosure, label, "label"),
CLOSURE_FINISH_PARAM(GaussianBSSRDFClosure)
};
@@ -109,22 +159,18 @@ CCLOSURE_PREPARE(closure_bssrdf_gaussian_prepare, GaussianBSSRDFClosure)
class BurleyBSSRDFClosure : public CBSSRDFClosure {
public:
- BurleyBSSRDFClosure()
- {}
-
- void setup()
+ void setup(ShaderData *sd, int path_flag, float3 weight)
{
- sc.type = CLOSURE_BSSRDF_BURLEY_ID;
- sc.data0 = fabsf(average(radius));
+ alloc(sd, path_flag, weight, CLOSURE_BSSRDF_BURLEY_ID);
}
};
ClosureParam *closure_bssrdf_burley_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(BurleyBSSRDFClosure, sc.N),
+ CLOSURE_FLOAT3_PARAM(BurleyBSSRDFClosure, params.N),
CLOSURE_FLOAT3_PARAM(BurleyBSSRDFClosure, radius),
- CLOSURE_FLOAT_PARAM(BurleyBSSRDFClosure, sc.data1),
+ CLOSURE_FLOAT_PARAM(BurleyBSSRDFClosure, params.texture_blur),
CLOSURE_FLOAT3_PARAM(BurleyBSSRDFClosure, albedo),
CLOSURE_STRING_KEYPARAM(BurleyBSSRDFClosure, label, "label"),
CLOSURE_FINISH_PARAM(BurleyBSSRDFClosure)
diff --git a/intern/cycles/kernel/osl/osl_bssrdf.h b/intern/cycles/kernel/osl/osl_bssrdf.h
deleted file mode 100644
index d81ecade543..00000000000
--- a/intern/cycles/kernel/osl/osl_bssrdf.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Adapted from Open Shading Language with this license:
- *
- * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
- * All Rights Reserved.
- *
- * Modifications Copyright 2011, Blender Foundation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Sony Pictures Imageworks nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef __OSL_BSSRDF_H__
-#define __OSL_BSSRDF_H__
-
-#include <OSL/oslclosure.h>
-#include <OSL/oslexec.h>
-#include <OSL/genclosure.h>
-
-#include "osl_closures.h"
-
-#include "kernel_types.h"
-
-#include "util_types.h"
-
-CCL_NAMESPACE_BEGIN
-
-class CBSSRDFClosure : public CClosurePrimitive {
-public:
- ShaderClosure sc;
- float3 radius;
- float3 albedo;
-
- CBSSRDFClosure() : CClosurePrimitive(BSSRDF) { }
- int scattering() const { return LABEL_DIFFUSE; }
-};
-
-CCL_NAMESPACE_END
-
-#endif /* __OSL_BSSRDF_H__ */
-
diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp
index 02b1491489c..94de782dca0 100644
--- a/intern/cycles/kernel/osl/osl_closures.cpp
+++ b/intern/cycles/kernel/osl/osl_closures.cpp
@@ -46,6 +46,7 @@
#include "kernel_montecarlo.h"
#include "kernel_random.h"
+#include "closure/alloc.h"
#include "closure/bsdf_util.h"
#include "closure/bsdf_ashikhmin_velvet.h"
#include "closure/bsdf_diffuse.h"
@@ -66,112 +67,112 @@ using namespace OSL;
/* BSDF class definitions */
-BSDF_CLOSURE_CLASS_BEGIN(Diffuse, diffuse, diffuse, LABEL_DIFFUSE)
- CLOSURE_FLOAT3_PARAM(DiffuseClosure, sc.N),
+BSDF_CLOSURE_CLASS_BEGIN(Diffuse, diffuse, DiffuseBsdf, LABEL_DIFFUSE)
+ CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N),
BSDF_CLOSURE_CLASS_END(Diffuse, diffuse)
-BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, translucent, LABEL_DIFFUSE)
- CLOSURE_FLOAT3_PARAM(TranslucentClosure, sc.N),
+BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, DiffuseBsdf, LABEL_DIFFUSE)
+ CLOSURE_FLOAT3_PARAM(TranslucentClosure, params.N),
BSDF_CLOSURE_CLASS_END(Translucent, translucent)
-BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, oren_nayar, LABEL_DIFFUSE)
- CLOSURE_FLOAT3_PARAM(OrenNayarClosure, sc.N),
- CLOSURE_FLOAT_PARAM(OrenNayarClosure, sc.data0),
+BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, OrenNayarBsdf, LABEL_DIFFUSE)
+ CLOSURE_FLOAT3_PARAM(OrenNayarClosure, params.N),
+ CLOSURE_FLOAT_PARAM(OrenNayarClosure, params.roughness),
BSDF_CLOSURE_CLASS_END(OrenNayar, oren_nayar)
-BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, reflection, LABEL_SINGULAR)
- CLOSURE_FLOAT3_PARAM(ReflectionClosure, sc.N),
+BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, MicrofacetBsdf, LABEL_SINGULAR)
+ CLOSURE_FLOAT3_PARAM(ReflectionClosure, params.N),
BSDF_CLOSURE_CLASS_END(Reflection, reflection)
-BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, refraction, LABEL_SINGULAR)
- CLOSURE_FLOAT3_PARAM(RefractionClosure, sc.N),
- CLOSURE_FLOAT_PARAM(RefractionClosure, sc.data0),
+BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, MicrofacetBsdf, LABEL_SINGULAR)
+ CLOSURE_FLOAT3_PARAM(RefractionClosure, params.N),
+ CLOSURE_FLOAT_PARAM(RefractionClosure, params.ior),
BSDF_CLOSURE_CLASS_END(Refraction, refraction)
-BSDF_CLOSURE_CLASS_BEGIN(Transparent, transparent, transparent, LABEL_SINGULAR)
+BSDF_CLOSURE_CLASS_BEGIN(Transparent, transparent, ShaderClosure, LABEL_SINGULAR)
BSDF_CLOSURE_CLASS_END(Transparent, transparent)
-BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, ashikhmin_velvet, LABEL_DIFFUSE)
- CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, sc.N),
- CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, sc.data0),
+BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, VelvetBsdf, LABEL_DIFFUSE)
+ CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, params.N),
+ CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, params.sigma),
BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet)
-BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, ashikhmin_shirley_aniso, ashikhmin_shirley, LABEL_GLOSSY|LABEL_REFLECT)
- CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, sc.N),
- CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, sc.T),
- CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, sc.data1),
+BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, ashikhmin_shirley_aniso, MicrofacetBsdf, LABEL_GLOSSY|LABEL_REFLECT)
+ CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.N),
+ CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.T),
+ CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_y),
BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley_aniso)
-BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, diffuse_toon, LABEL_DIFFUSE)
- CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, sc.N),
- CLOSURE_FLOAT_PARAM(DiffuseToonClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(DiffuseToonClosure, sc.data1),
+BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, ToonBsdf, LABEL_DIFFUSE)
+ CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, params.N),
+ CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.size),
+ CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.smooth),
BSDF_CLOSURE_CLASS_END(DiffuseToon, diffuse_toon)
-BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, glossy_toon, LABEL_GLOSSY)
- CLOSURE_FLOAT3_PARAM(GlossyToonClosure, sc.N),
- CLOSURE_FLOAT_PARAM(GlossyToonClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(GlossyToonClosure, sc.data1),
+BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, ToonBsdf, LABEL_GLOSSY)
+ CLOSURE_FLOAT3_PARAM(GlossyToonClosure, params.N),
+ CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.size),
+ CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.smooth),
BSDF_CLOSURE_CLASS_END(GlossyToon, glossy_toon)
-BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, microfacet_ggx, microfacet_ggx, LABEL_GLOSSY|LABEL_REFLECT)
- CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, sc.N),
- CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, sc.data0),
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, microfacet_ggx, MicrofacetBsdf, LABEL_GLOSSY|LABEL_REFLECT)
+ CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.N),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_x),
BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx)
-BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXAniso, microfacet_ggx_aniso, microfacet_ggx, LABEL_GLOSSY|LABEL_REFLECT)
- CLOSURE_FLOAT3_PARAM(MicrofacetGGXAnisoClosure, sc.N),
- CLOSURE_FLOAT3_PARAM(MicrofacetGGXAnisoClosure, sc.T),
- CLOSURE_FLOAT_PARAM(MicrofacetGGXAnisoClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(MicrofacetGGXAnisoClosure, sc.data1),
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXAniso, microfacet_ggx_aniso, MicrofacetBsdf, LABEL_GLOSSY|LABEL_REFLECT)
+ CLOSURE_FLOAT3_PARAM(MicrofacetGGXAnisoClosure, params.N),
+ CLOSURE_FLOAT3_PARAM(MicrofacetGGXAnisoClosure, params.T),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXAnisoClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXAnisoClosure, params.alpha_y),
BSDF_CLOSURE_CLASS_END(MicrofacetGGXAniso, microfacet_ggx_aniso)
-BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, microfacet_beckmann, microfacet_beckmann, LABEL_GLOSSY|LABEL_REFLECT)
- CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, sc.N),
- CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, sc.data0),
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, microfacet_beckmann, MicrofacetBsdf, LABEL_GLOSSY|LABEL_REFLECT)
+ CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.N),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_x),
BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann)
-BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannAniso, microfacet_beckmann_aniso, microfacet_beckmann, LABEL_GLOSSY|LABEL_REFLECT)
- CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannAnisoClosure, sc.N),
- CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannAnisoClosure, sc.T),
- CLOSURE_FLOAT_PARAM(MicrofacetBeckmannAnisoClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(MicrofacetBeckmannAnisoClosure, sc.data1),
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannAniso, microfacet_beckmann_aniso, MicrofacetBsdf, LABEL_GLOSSY|LABEL_REFLECT)
+ CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannAnisoClosure, params.N),
+ CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannAnisoClosure, params.T),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannAnisoClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannAnisoClosure, params.alpha_y),
BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannAniso, microfacet_beckmann_aniso)
-BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, microfacet_ggx_refraction, microfacet_ggx, LABEL_GLOSSY|LABEL_TRANSMIT)
- CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, sc.N),
- CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, sc.data2),
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, microfacet_ggx_refraction, MicrofacetBsdf, LABEL_GLOSSY|LABEL_TRANSMIT)
+ CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, params.N),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.ior),
BSDF_CLOSURE_CLASS_END(MicrofacetGGXRefraction, microfacet_ggx_refraction)
-BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction, microfacet_beckmann, LABEL_GLOSSY|LABEL_TRANSMIT)
- CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, sc.N),
- CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, sc.data2),
+BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction, MicrofacetBsdf, LABEL_GLOSSY|LABEL_TRANSMIT)
+ CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, params.N),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.ior),
BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction)
-BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, hair_reflection, LABEL_GLOSSY)
- CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.N),
- CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data1),
- CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.T),
- CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data2),
+BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, HairBsdf, LABEL_GLOSSY)
+ CLOSURE_FLOAT3_PARAM(HairReflectionClosure, unused),
+ CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness1),
+ CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness2),
+ CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T),
+ CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset),
BSDF_CLOSURE_CLASS_END(HairReflection, hair_reflection)
-BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, hair_transmission, LABEL_GLOSSY)
- CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, sc.N),
- CLOSURE_FLOAT_PARAM(HairTransmissionClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(HairTransmissionClosure, sc.data1),
- CLOSURE_FLOAT3_PARAM(HairReflectionClosure, sc.T),
- CLOSURE_FLOAT_PARAM(HairReflectionClosure, sc.data2),
+BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, HairBsdf, LABEL_GLOSSY)
+ CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, unused),
+ CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness1),
+ CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness2),
+ CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T),
+ CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset),
BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission)
-VOLUME_CLOSURE_CLASS_BEGIN(VolumeHenyeyGreenstein, henyey_greenstein, LABEL_VOLUME_SCATTER)
- CLOSURE_FLOAT_PARAM(VolumeHenyeyGreensteinClosure, sc.data0),
+VOLUME_CLOSURE_CLASS_BEGIN(VolumeHenyeyGreenstein, henyey_greenstein, HenyeyGreensteinVolume, LABEL_VOLUME_SCATTER)
+ CLOSURE_FLOAT_PARAM(VolumeHenyeyGreensteinClosure, params.g),
VOLUME_CLOSURE_CLASS_END(VolumeHenyeyGreenstein, henyey_greenstein)
-VOLUME_CLOSURE_CLASS_BEGIN(VolumeAbsorption, absorption, LABEL_SINGULAR)
+VOLUME_CLOSURE_CLASS_BEGIN(VolumeAbsorption, absorption, ShaderClosure, LABEL_SINGULAR)
VOLUME_CLOSURE_CLASS_END(VolumeAbsorption, absorption)
/* Registration */
@@ -258,69 +259,64 @@ void OSLShader::register_closures(OSLShadingSystem *ss_)
volume_absorption_params(), volume_absorption_prepare);
}
-/* Multiscattering GGX closures */
-
-class MicrofacetMultiClosure : public CBSDFClosure {
-public:
- float3 color;
-
- /* Technically, the MultiGGX Glass closure may also transmit.
- * However, since this is set statically and only used for caustic flags, this is probably as good as it gets. */
- MicrofacetMultiClosure() : CBSDFClosure(LABEL_GLOSSY|LABEL_REFLECT)
- {
- }
+/* BSDF Closure */
- void setup()
- {
- sc.prim = NULL;
- sc.custom1 = color.x;
- sc.custom2 = color.y;
- sc.custom3 = color.z;
+bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering)
+{
+ /* caustic options */
+ if((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) {
+ KernelGlobals *kg = sd->osl_globals;
+
+ if((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) ||
+ (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT)))
+ {
+ return true;
+ }
}
- void blur(float roughness)
- {
- }
+ return false;
+}
- float3 eval_reflect(const float3 &omega_out, const float3 &omega_in, float& pdf) const
- {
- pdf = 0.0f;
- return make_float3(0.0f, 0.0f, 0.0f);
- }
+/* Multiscattering GGX closures */
- float3 eval_transmit(const float3 &omega_out, const float3 &omega_in, float& pdf) const
- {
- pdf = 0.0f;
- return make_float3(0.0f, 0.0f, 0.0f);
- }
+class MicrofacetMultiClosure : public CBSDFClosure {
+public:
+ MicrofacetBsdf params;
+ float3 color;
- int sample(const float3 &Ng,
- const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy,
- float randu, float randv,
- float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy,
- float &pdf, float3 &eval) const
+ MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight)
{
- pdf = 0;
- return LABEL_NONE;
+ /* Technically, the MultiGGX Glass closure may also transmit. However,
+ * since this is set statically and only used for caustic flags, this
+ * is probably as good as it gets. */
+ if(!skip(sd, path_flag, LABEL_GLOSSY|LABEL_REFLECT)) {
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc_osl(sd, sizeof(MicrofacetBsdf), weight, &params);
+ MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra));
+ if(bsdf && extra) {
+ bsdf->extra = extra;
+ bsdf->extra->color = color;
+ return bsdf;
+ }
+ }
+
+ return NULL;
}
};
class MicrofacetMultiGGXClosure : public MicrofacetMultiClosure {
public:
- MicrofacetMultiGGXClosure() : MicrofacetMultiClosure() {}
-
- void setup()
+ void setup(ShaderData *sd, int path_flag, float3 weight)
{
- MicrofacetMultiClosure::setup();
- m_shaderdata_flag = bsdf_microfacet_multi_ggx_setup(&sc);
+ MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight);
+ sd->flag |= (bsdf) ? bsdf_microfacet_multi_ggx_setup(bsdf) : 0;
}
};
ClosureParam *closure_bsdf_microfacet_multi_ggx_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, sc.N),
- CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, sc.data0),
+ CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.N),
+ CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_x),
CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, color),
CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXClosure, label, "label"),
CLOSURE_FINISH_PARAM(MicrofacetMultiGGXClosure)
@@ -331,22 +327,20 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_prepare, MicrofacetMultiGGXCl
class MicrofacetMultiGGXAnisoClosure : public MicrofacetMultiClosure {
public:
- MicrofacetMultiGGXAnisoClosure() : MicrofacetMultiClosure() {}
-
- void setup()
+ void setup(ShaderData *sd, int path_flag, float3 weight)
{
- MicrofacetMultiClosure::setup();
- m_shaderdata_flag = bsdf_microfacet_multi_ggx_aniso_setup(&sc);
+ MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight);
+ sd->flag |= (bsdf) ? bsdf_microfacet_multi_ggx_aniso_setup(bsdf) : 0;
}
};
ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, sc.N),
- CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, sc.T),
- CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, sc.data1),
+ CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.N),
+ CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.T),
+ CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_y),
CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, color),
CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXClosure, label, "label"),
CLOSURE_FINISH_PARAM(MicrofacetMultiGGXClosure)
@@ -359,19 +353,19 @@ class MicrofacetMultiGGXGlassClosure : public MicrofacetMultiClosure {
public:
MicrofacetMultiGGXGlassClosure() : MicrofacetMultiClosure() {}
- void setup()
+ void setup(ShaderData *sd, int path_flag, float3 weight)
{
- MicrofacetMultiClosure::setup();
- m_shaderdata_flag = bsdf_microfacet_multi_ggx_glass_setup(&sc);
+ MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight);
+ sd->flag |= (bsdf) ? bsdf_microfacet_multi_ggx_glass_setup(bsdf) : 0;
}
};
ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params()
{
static ClosureParam params[] = {
- CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, sc.N),
- CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, sc.data0),
- CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, sc.data2),
+ CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, params.N),
+ CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.alpha_x),
+ CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXClosure, params.ior),
CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXClosure, color),
CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXClosure, label, "label"),
CLOSURE_FINISH_PARAM(MicrofacetMultiGGXClosure)
diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h
index c5a1a29b6af..cd7b33703ff 100644
--- a/intern/cycles/kernel/osl/osl_closures.h
+++ b/intern/cycles/kernel/osl/osl_closures.h
@@ -90,21 +90,7 @@ void name(RendererServices *, int id, void *data) \
class CClosurePrimitive {
public:
- enum Category {
- BSDF, ///< Reflective and/or transmissive surface
- BSSRDF, ///< Sub-surface light transfer
- Emissive, ///< Light emission
- Background, ///< Background emission
- Volume, ///< Volume scattering
- Holdout, ///< Holdout from alpha
- AmbientOcclusion, ///< Ambient occlusion
- };
-
- CClosurePrimitive (Category category_) : category (category_) {}
- virtual ~CClosurePrimitive() {}
- virtual void setup() {}
-
- Category category;
+ virtual void setup(ShaderData *sd, int path_flag, float3 weight) = 0;
OSL::ustring label;
};
@@ -113,68 +99,22 @@ public:
class CBSDFClosure : public CClosurePrimitive {
public:
- ShaderClosure sc;
-
- CBSDFClosure(int scattering) : CClosurePrimitive(BSDF),
- m_scattering_label(scattering), m_shaderdata_flag(0)
- {}
-
- int scattering() const { return m_scattering_label; }
- int shaderdata_flag() const { return m_shaderdata_flag; }
-
- virtual void blur(float roughness) = 0;
- virtual float3 eval_reflect(const float3 &omega_out, const float3 &omega_in, float &pdf) const = 0;
- virtual float3 eval_transmit(const float3 &omega_out, const float3 &omega_in, float &pdf) const = 0;
-
- virtual int sample(const float3 &Ng,
- const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy,
- float randu, float randv,
- float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy,
- float &pdf, float3 &eval) const = 0;
-
-protected:
- int m_scattering_label;
- int m_shaderdata_flag;
+ bool skip(const ShaderData *sd, int path_flag, int scattering);
};
-#define BSDF_CLOSURE_CLASS_BEGIN(Upper, lower, svmlower, TYPE) \
+#define BSDF_CLOSURE_CLASS_BEGIN(Upper, lower, structname, TYPE) \
\
class Upper##Closure : public CBSDFClosure { \
public: \
- Upper##Closure() : CBSDFClosure(TYPE) \
- { \
- } \
+ structname params; \
+ float3 unused; \
\
- void setup() \
+ void setup(ShaderData *sd, int path_flag, float3 weight) \
{ \
- sc.prim = NULL; \
- m_shaderdata_flag = bsdf_##lower##_setup(&sc); \
- } \
-\
- void blur(float roughness) \
- { \
- } \
-\
- float3 eval_reflect(const float3 &omega_out, const float3 &omega_in, float& pdf) const \
- { \
- pdf = 0.0f; \
- return make_float3(0.0f, 0.0f, 0.0f); \
- } \
-\
- float3 eval_transmit(const float3 &omega_out, const float3 &omega_in, float& pdf) const \
- { \
- pdf = 0.0f; \
- return make_float3(0.0f, 0.0f, 0.0f); \
- } \
-\
- int sample(const float3 &Ng, \
- const float3 &omega_out, const float3 &domega_out_dx, const float3 &domega_out_dy, \
- float randu, float randv, \
- float3 &omega_in, float3 &domega_in_dx, float3 &domega_in_dy, \
- float &pdf, float3 &eval) const \
- { \
- pdf = 0; \
- return LABEL_NONE; \
+ if(!skip(sd, path_flag, TYPE)) { \
+ structname *bsdf = (structname*)bsdf_alloc_osl(sd, sizeof(structname), weight, &params); \
+ sd->flag |= (bsdf) ? bsdf_##lower##_setup(bsdf) : 0; \
+ } \
} \
}; \
\
@@ -193,36 +133,18 @@ static ClosureParam *bsdf_##lower##_params() \
\
CCLOSURE_PREPARE_STATIC(bsdf_##lower##_prepare, Upper##Closure)
-
/* Volume */
-class CVolumeClosure : public CClosurePrimitive {
-public:
- ShaderClosure sc;
-
- CVolumeClosure(int scattering) : CClosurePrimitive(Volume),
- m_scattering_label(scattering), m_shaderdata_flag(0)
- {}
- ~CVolumeClosure() { }
-
- int scattering() const { return m_scattering_label; }
- int shaderdata_flag() const { return m_shaderdata_flag; }
-
-protected:
- int m_scattering_label;
- int m_shaderdata_flag;
-};
-
-#define VOLUME_CLOSURE_CLASS_BEGIN(Upper, lower, TYPE) \
+#define VOLUME_CLOSURE_CLASS_BEGIN(Upper, lower, structname, TYPE) \
\
-class Upper##Closure : public CVolumeClosure { \
+class Upper##Closure : public CBSDFClosure { \
public: \
- Upper##Closure() : CVolumeClosure(TYPE) {} \
+ structname params; \
\
- void setup() \
+ void setup(ShaderData *sd, int path_flag, float3 weight) \
{ \
- sc.prim = NULL; \
- m_shaderdata_flag = volume_##lower##_setup(&sc); \
+ structname *volume = (structname*)bsdf_alloc_osl(sd, sizeof(structname), weight, &params); \
+ sd->flag |= (volume) ? volume_##lower##_setup(volume) : 0; \
} \
}; \
\
diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h
index 916542ec628..8353c4e434b 100644
--- a/intern/cycles/kernel/osl/osl_globals.h
+++ b/intern/cycles/kernel/osl/osl_globals.h
@@ -59,8 +59,7 @@ struct OSLGlobals {
/* attributes */
struct Attribute {
TypeDesc type;
- AttributeElement elem;
- int offset;
+ AttributeDescriptor desc;
ParamValue value;
};
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 2bb2be5e6b3..153ebad6cd2 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -554,13 +554,13 @@ static bool get_mesh_element_attribute(KernelGlobals *kg, const ShaderData *sd,
attr.type == TypeDesc::TypeNormal || attr.type == TypeDesc::TypeColor)
{
float3 fval[3];
- fval[0] = primitive_attribute_float3(kg, sd, attr.elem, attr.offset,
+ fval[0] = primitive_attribute_float3(kg, sd, attr.desc,
(derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
return set_attribute_float3(fval, type, derivatives, val);
}
else if(attr.type == TypeDesc::TypeFloat) {
float fval[3];
- fval[0] = primitive_attribute_float(kg, sd, attr.elem, attr.offset,
+ fval[0] = primitive_attribute_float(kg, sd, attr.desc,
(derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL);
return set_attribute_float(fval, type, derivatives, val);
}
@@ -573,7 +573,7 @@ static bool get_mesh_attribute(KernelGlobals *kg, const ShaderData *sd, const OS
const TypeDesc& type, bool derivatives, void *val)
{
if(attr.type == TypeDesc::TypeMatrix) {
- Transform tfm = primitive_attribute_matrix(kg, sd, attr.offset);
+ Transform tfm = primitive_attribute_matrix(kg, sd, attr.desc);
return set_attribute_matrix(tfm, type, val);
}
else {
@@ -787,7 +787,7 @@ bool OSLRenderServices::get_attribute(ShaderData *sd, bool derivatives, ustring
TypeDesc type, ustring name, void *val)
{
KernelGlobals *kg = sd->osl_globals;
- bool is_curve;
+ int prim_type = 0;
int object;
/* lookup of attribute on another object */
@@ -798,25 +798,24 @@ bool OSLRenderServices::get_attribute(ShaderData *sd, bool derivatives, ustring
return false;
object = it->second;
- is_curve = false;
}
else {
object = sd->object;
- is_curve = (sd->type & PRIMITIVE_ALL_CURVE) != 0;
+ prim_type = attribute_primitive_type(kg, sd);
if(object == OBJECT_NONE)
return get_background_attribute(kg, sd, name, type, derivatives, val);
}
/* find attribute on object */
- object = object*ATTR_PRIM_TYPES + (is_curve == true);
+ object = object*ATTR_PRIM_TYPES + prim_type;
OSLGlobals::AttributeMap& attribute_map = kg->osl->attribute_map[object];
OSLGlobals::AttributeMap::iterator it = attribute_map.find(name);
if(it != attribute_map.end()) {
const OSLGlobals::Attribute& attr = it->second;
- if(attr.elem != ATTR_ELEMENT_OBJECT) {
+ if(attr.desc.element != ATTR_ELEMENT_OBJECT) {
/* triangle and vertex attributes */
if(get_mesh_element_attribute(kg, sd, attr, type, derivatives, val))
return true;
diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp
index 6cde7419e10..20dd167708c 100644
--- a/intern/cycles/kernel/osl/osl_shader.cpp
+++ b/intern/cycles/kernel/osl/osl_shader.cpp
@@ -23,10 +23,6 @@
#include "geom/geom_object.h"
-#include "closure/bsdf_diffuse.h"
-#include "closure/bssrdf.h"
-
-#include "osl_bssrdf.h"
#include "osl_closures.h"
#include "osl_globals.h"
#include "osl_services.h"
@@ -141,8 +137,10 @@ static void shaderdata_to_shaderglobals(KernelGlobals *kg, ShaderData *sd, PathS
/* Surface */
-static void flatten_surface_closure_tree(ShaderData *sd, int path_flag,
- const OSL::ClosureColor *closure, float3 weight = make_float3(1.0f, 1.0f, 1.0f))
+static void flatten_surface_closure_tree(ShaderData *sd,
+ int path_flag,
+ const OSL::ClosureColor *closure,
+ float3 weight = make_float3(1.0f, 1.0f, 1.0f))
{
/* OSL gives us a closure tree, we flatten it into arrays per
* closure type, for evaluation, sampling, etc later on. */
@@ -164,164 +162,10 @@ static void flatten_surface_closure_tree(ShaderData *sd, int path_flag,
CClosurePrimitive *prim = (CClosurePrimitive *)comp->data();
if(prim) {
- ShaderClosure sc;
-
#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS
weight = weight*TO_FLOAT3(comp->w);
#endif
- sc.weight = weight;
-
- prim->setup();
-
- switch(prim->category) {
- case CClosurePrimitive::BSDF: {
- CBSDFClosure *bsdf = (CBSDFClosure *)prim;
- int scattering = bsdf->scattering();
- int shaderdata_flag = bsdf->shaderdata_flag();
-
- /* caustic options */
- if((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) {
- KernelGlobals *kg = sd->osl_globals;
-
- if((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) ||
- (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT)))
- {
- return;
- }
- }
-
- /* sample weight */
- float sample_weight = fabsf(average(weight));
-
- sc.sample_weight = sample_weight;
-
- sc.type = bsdf->sc.type;
- sc.N = bsdf->sc.N;
- sc.T = bsdf->sc.T;
- sc.data0 = bsdf->sc.data0;
- sc.data1 = bsdf->sc.data1;
- sc.data2 = bsdf->sc.data2;
- sc.prim = bsdf->sc.prim;
- if(shaderdata_flag & SD_BSDF_HAS_CUSTOM) {
- sc.custom1 = bsdf->sc.custom1;
- sc.custom2 = bsdf->sc.custom2;
- sc.custom3 = bsdf->sc.custom3;
- }
-
- /* add */
- if(sc.sample_weight > CLOSURE_WEIGHT_CUTOFF && sd->num_closure < MAX_CLOSURE) {
- sd->closure[sd->num_closure++] = sc;
- sd->flag |= shaderdata_flag;
- }
- break;
- }
- case CClosurePrimitive::Emissive: {
- /* sample weight */
- float sample_weight = fabsf(average(weight));
-
- sc.sample_weight = sample_weight;
- sc.type = CLOSURE_EMISSION_ID;
- sc.data0 = 0.0f;
- sc.data1 = 0.0f;
- sc.data2 = 0.0f;
- sc.prim = NULL;
-
- /* flag */
- if(sd->num_closure < MAX_CLOSURE) {
- sd->closure[sd->num_closure++] = sc;
- sd->flag |= SD_EMISSION;
- }
- break;
- }
- case CClosurePrimitive::AmbientOcclusion: {
- /* sample weight */
- float sample_weight = fabsf(average(weight));
-
- sc.sample_weight = sample_weight;
- sc.type = CLOSURE_AMBIENT_OCCLUSION_ID;
- sc.data0 = 0.0f;
- sc.data1 = 0.0f;
- sc.data2 = 0.0f;
- sc.prim = NULL;
-
- if(sd->num_closure < MAX_CLOSURE) {
- sd->closure[sd->num_closure++] = sc;
- sd->flag |= SD_AO;
- }
- break;
- }
- case CClosurePrimitive::Holdout: {
- sc.sample_weight = 0.0f;
- sc.type = CLOSURE_HOLDOUT_ID;
- sc.data0 = 0.0f;
- sc.data1 = 0.0f;
- sc.data2 = 0.0f;
- sc.prim = NULL;
-
- if(sd->num_closure < MAX_CLOSURE) {
- sd->closure[sd->num_closure++] = sc;
- sd->flag |= SD_HOLDOUT;
- }
- break;
- }
- case CClosurePrimitive::BSSRDF: {
- CBSSRDFClosure *bssrdf = (CBSSRDFClosure *)prim;
- float sample_weight = fabsf(average(weight));
-
- if(sample_weight > CLOSURE_WEIGHT_CUTOFF && sd->num_closure+2 < MAX_CLOSURE) {
- sc.sample_weight = sample_weight;
-
- sc.type = bssrdf->sc.type;
- sc.N = bssrdf->sc.N;
- sc.data1 = bssrdf->sc.data1;
- sc.T.x = bssrdf->sc.T.x;
- sc.prim = NULL;
-
- /* disable in case of diffuse ancestor, can't see it well then and
- * adds considerably noise due to probabilities of continuing path
- * getting lower and lower */
- if(path_flag & PATH_RAY_DIFFUSE_ANCESTOR)
- bssrdf->radius = make_float3(0.0f, 0.0f, 0.0f);
-
- float3 albedo =
- (bssrdf->sc.type == CLOSURE_BSSRDF_BURLEY_ID)
- ? bssrdf->albedo
- : make_float3(0.0f, 0.0f, 0.0f);
-
- /* create one closure for each color channel */
- if(fabsf(weight.x) > 0.0f) {
- sc.weight = make_float3(weight.x, 0.0f, 0.0f);
- sc.data0 = bssrdf->radius.x;
- sc.data1 = 0.0f;
- sc.data2 = albedo.x;
- sd->flag |= bssrdf_setup(&sc, sc.type);
- sd->closure[sd->num_closure++] = sc;
- }
-
- if(fabsf(weight.y) > 0.0f) {
- sc.weight = make_float3(0.0f, weight.y, 0.0f);
- sc.data0 = bssrdf->radius.y;
- sc.data1 = 0.0f;
- sc.data2 = albedo.y;
- sd->flag |= bssrdf_setup(&sc, sc.type);
- sd->closure[sd->num_closure++] = sc;
- }
-
- if(fabsf(weight.z) > 0.0f) {
- sc.weight = make_float3(0.0f, 0.0f, weight.z);
- sc.data0 = bssrdf->radius.z;
- sc.data1 = 0.0f;
- sc.data2 = albedo.z;
- sd->flag |= bssrdf_setup(&sc, sc.type);
- sd->closure[sd->num_closure++] = sc;
- }
- }
- break;
- }
- case CClosurePrimitive::Background:
- case CClosurePrimitive::Volume:
- break; /* not relevant */
- }
+ prim->setup(sd, path_flag, weight);
}
break;
}
@@ -351,7 +195,9 @@ void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state
/* Background */
-static float3 flatten_background_closure_tree(const OSL::ClosureColor *closure)
+static void flatten_background_closure_tree(ShaderData *sd,
+ const OSL::ClosureColor *closure,
+ float3 weight = make_float3(1.0f, 1.0f, 1.0f))
{
/* OSL gives us a closure tree, if we are shading for background there
* is only one supported closure type at the moment, which has no evaluation
@@ -360,32 +206,32 @@ static float3 flatten_background_closure_tree(const OSL::ClosureColor *closure)
switch(closure->id) {
case OSL::ClosureColor::MUL: {
OSL::ClosureMul *mul = (OSL::ClosureMul *)closure;
-
- return TO_FLOAT3(mul->weight) * flatten_background_closure_tree(mul->closure);
+ flatten_background_closure_tree(sd, mul->closure, weight * TO_FLOAT3(mul->weight));
+ break;
}
case OSL::ClosureColor::ADD: {
OSL::ClosureAdd *add = (OSL::ClosureAdd *)closure;
- return flatten_background_closure_tree(add->closureA) +
- flatten_background_closure_tree(add->closureB);
+ flatten_background_closure_tree(sd, add->closureA, weight);
+ flatten_background_closure_tree(sd, add->closureB, weight);
+ break;
}
default: {
OSL::ClosureComponent *comp = (OSL::ClosureComponent *)closure;
CClosurePrimitive *prim = (CClosurePrimitive *)comp->data();
- if(prim && prim->category == CClosurePrimitive::Background)
+ if(prim) {
#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS
- return TO_FLOAT3(comp->w);
-#else
- return make_float3(1.0f, 1.0f, 1.0f);
+ weight = weight*TO_FLOAT3(comp->w);
#endif
+ prim->setup(sd, 0, weight);
+ }
+ break;
}
}
-
- return make_float3(0.0f, 0.0f, 0.0f);
}
-float3 OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx)
+void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx)
{
/* setup shader globals from shader data */
OSLThreadData *tdata = kg->osl_tdata;
@@ -402,15 +248,14 @@ float3 OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *
/* return background color immediately */
if(globals->Ci)
- return flatten_background_closure_tree(globals->Ci);
-
- return make_float3(0.0f, 0.0f, 0.0f);
+ flatten_background_closure_tree(sd, globals->Ci);
}
/* Volume */
static void flatten_volume_closure_tree(ShaderData *sd,
- const OSL::ClosureColor *closure, float3 weight = make_float3(1.0f, 1.0f, 1.0f))
+ const OSL::ClosureColor *closure,
+ float3 weight = make_float3(1.0f, 1.0f, 1.0f))
{
/* OSL gives us a closure tree, we flatten it into arrays per
* closure type, for evaluation, sampling, etc later on. */
@@ -432,60 +277,10 @@ static void flatten_volume_closure_tree(ShaderData *sd,
CClosurePrimitive *prim = (CClosurePrimitive *)comp->data();
if(prim) {
- ShaderClosure sc;
-
#ifdef OSL_SUPPORTS_WEIGHTED_CLOSURE_COMPONENTS
weight = weight*TO_FLOAT3(comp->w);
#endif
- sc.weight = weight;
-
- prim->setup();
-
- switch(prim->category) {
- case CClosurePrimitive::Volume: {
- CVolumeClosure *volume = (CVolumeClosure *)prim;
- /* sample weight */
- float sample_weight = fabsf(average(weight));
-
- sc.sample_weight = sample_weight;
- sc.type = volume->sc.type;
- sc.data0 = volume->sc.data0;
- sc.data1 = volume->sc.data1;
-
- /* add */
- if((sc.sample_weight > CLOSURE_WEIGHT_CUTOFF) &&
- (sd->num_closure < MAX_CLOSURE))
- {
- sd->closure[sd->num_closure++] = sc;
- sd->flag |= volume->shaderdata_flag();
- }
- break;
- }
- case CClosurePrimitive::Emissive: {
- /* sample weight */
- float sample_weight = fabsf(average(weight));
-
- sc.sample_weight = sample_weight;
- sc.type = CLOSURE_EMISSION_ID;
- sc.data0 = 0.0f;
- sc.data1 = 0.0f;
- sc.prim = NULL;
-
- /* flag */
- if(sd->num_closure < MAX_CLOSURE) {
- sd->closure[sd->num_closure++] = sc;
- sd->flag |= SD_EMISSION;
- }
- break;
- }
- case CClosurePrimitive::Holdout:
- break; /* not implemented */
- case CClosurePrimitive::Background:
- case CClosurePrimitive::BSDF:
- case CClosurePrimitive::BSSRDF:
- case CClosurePrimitive::AmbientOcclusion:
- break; /* not relevant */
- }
+ prim->setup(sd, 0, weight);
}
}
}
@@ -537,43 +332,9 @@ void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderConte
sd->P = TO_FLOAT3(globals->P);
}
-/* BSDF Closure */
-
-int OSLShader::bsdf_sample(const ShaderData *sd, const ShaderClosure *sc, float randu, float randv, float3& eval, float3& omega_in, differential3& domega_in, float& pdf)
-{
- CBSDFClosure *sample_bsdf = (CBSDFClosure *)sc->prim;
-
- pdf = 0.0f;
-
- return sample_bsdf->sample(sd->Ng,
- sd->I, sd->dI.dx, sd->dI.dy,
- randu, randv,
- omega_in, domega_in.dx, domega_in.dy,
- pdf, eval);
-}
-
-float3 OSLShader::bsdf_eval(const ShaderData *sd, const ShaderClosure *sc, const float3& omega_in, float& pdf)
-{
- CBSDFClosure *bsdf = (CBSDFClosure *)sc->prim;
- float3 bsdf_eval;
-
- if(dot(sd->Ng, omega_in) >= 0.0f)
- bsdf_eval = bsdf->eval_reflect(sd->I, omega_in, pdf);
- else
- bsdf_eval = bsdf->eval_transmit(sd->I, omega_in, pdf);
-
- return bsdf_eval;
-}
-
-void OSLShader::bsdf_blur(ShaderClosure *sc, float roughness)
-{
- CBSDFClosure *bsdf = (CBSDFClosure *)sc->prim;
- bsdf->blur(roughness);
-}
-
/* Attributes */
-int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem)
+int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc)
{
/* for OSL, a hash map is used to lookup the attribute by name. */
int object = sd->object*ATTR_PRIM_TYPES;
@@ -587,16 +348,23 @@ int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id,
if(it != attr_map.end()) {
const OSLGlobals::Attribute &osl_attr = it->second;
- *elem = osl_attr.elem;
+ *desc = osl_attr.desc;
- if(sd->prim == PRIM_NONE && (AttributeElement)osl_attr.elem != ATTR_ELEMENT_MESH)
+ if(sd->prim == PRIM_NONE && (AttributeElement)osl_attr.desc.element != ATTR_ELEMENT_MESH) {
+ desc->offset = ATTR_STD_NOT_FOUND;
return ATTR_STD_NOT_FOUND;
+ }
/* return result */
- return (osl_attr.elem == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : osl_attr.offset;
+ if(osl_attr.desc.element == ATTR_ELEMENT_NONE) {
+ desc->offset = ATTR_STD_NOT_FOUND;
+ }
+ return desc->offset;
}
- else
+ else {
+ desc->offset = ATTR_STD_NOT_FOUND;
return (int)ATTR_STD_NOT_FOUND;
+ }
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/osl_shader.h
index 7d26cd40da5..ad06dd6929d 100644
--- a/intern/cycles/kernel/osl/osl_shader.h
+++ b/intern/cycles/kernel/osl/osl_shader.h
@@ -54,20 +54,12 @@ public:
/* eval */
static void eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
- static float3 eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
+ static void eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
static void eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, ShaderContext ctx);
static void eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderContext ctx);
- /* sample & eval */
- static int bsdf_sample(const ShaderData *sd, const ShaderClosure *sc,
- float randu, float randv,
- float3& eval, float3& omega_in, differential3& domega_in, float& pdf);
- static float3 bsdf_eval(const ShaderData *sd, const ShaderClosure *sc,
- const float3& omega_in, float& pdf);
- static void bsdf_blur(ShaderClosure *sc, float roughness);
-
/* attributes */
- static int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem);
+ static int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/shaders/node_hair_bsdf.osl b/intern/cycles/kernel/shaders/node_hair_bsdf.osl
index c8cb88f0c0b..ef8f2fae894 100644
--- a/intern/cycles/kernel/shaders/node_hair_bsdf.osl
+++ b/intern/cycles/kernel/shaders/node_hair_bsdf.osl
@@ -24,34 +24,35 @@ shader node_hair_bsdf(
float Offset = 0.0,
float RoughnessU = 0.1,
float RoughnessV = 1.0,
- normal Normal = Ng,
+ normal Tangent = normal(0, 0, 0),
output closure color BSDF = 0)
{
- float IsStrand;
float roughnessh = clamp(RoughnessU, 0.001, 1.0);
float roughnessv = clamp(RoughnessV, 0.001, 1.0);
- getattribute("geom:is_curve", IsStrand);
+ float offset = -Offset;
- if (!IsStrand) {
- if (backfacing()) {
- BSDF = transparent();
- }
- else {
- if (component == "reflection")
- BSDF = Color * hair_reflection(Normal, roughnessh, roughnessv, normalize(dPdv), 0.0);
- else
- BSDF = Color * hair_transmission(Normal, roughnessh, roughnessv, normalize(dPdv), 0.0);
- }
+ normal T;
+ float IsCurve = 0;
+ getattribute("geom:is_curve", IsCurve);
+
+ if (isconnected(Tangent)) {
+ T = Tangent;
+ }
+ else if(!IsCurve) {
+ T = normalize(dPdv);
+ offset = 0.0;
+ }
+ else {
+ T = normalize(dPdu);
+ }
+
+ if (backfacing() && IsCurve) {
+ BSDF = transparent();
}
else {
- if (backfacing()) {
- BSDF = transparent();
- }
- else {
- if (component == "reflection")
- BSDF = Color * hair_reflection(Normal, roughnessh, roughnessv, dPdu, -Offset);
- else
- BSDF = Color * hair_transmission(Normal, roughnessh, roughnessv, dPdu, -Offset);
- }
+ if (component == "reflection")
+ BSDF = Color * hair_reflection(Ng, roughnessh, roughnessv, T, offset);
+ else
+ BSDF = Color * hair_transmission(Ng, roughnessh, roughnessv, T, offset);
}
}
diff --git a/intern/cycles/kernel/shaders/node_ramp_util.h b/intern/cycles/kernel/shaders/node_ramp_util.h
new file mode 100644
index 00000000000..917fb65c6df
--- /dev/null
+++ b/intern/cycles/kernel/shaders/node_ramp_util.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
+
+color rgb_ramp_lookup(color ramp[], float at, int interpolate, int extrapolate)
+{
+ float f = at;
+ int table_size = arraylength(ramp);
+
+ if ((f < 0.0 || f > 1.0) && extrapolate) {
+ color t0, dy;
+ if (f < 0.0) {
+ t0 = ramp[0];
+ dy = t0 - ramp[1];
+ f = -f;
+ }
+ else {
+ t0 = ramp[table_size - 1];
+ dy = t0 - ramp[table_size - 2];
+ f = f - 1.0;
+ }
+ return t0 + dy * f * (table_size - 1);
+ }
+
+ f = clamp(at, 0.0, 1.0) * (table_size - 1);
+
+ /* clamp int as well in case of NaN */
+ int i = (int)f;
+ if (i < 0) i = 0;
+ if (i >= table_size) i = table_size - 1;
+ float t = f - (float)i;
+
+ color result = ramp[i];
+
+ if (interpolate && t > 0.0)
+ result = (1.0 - t) * result + t * ramp[i + 1];
+
+ return result;
+}
+
+float rgb_ramp_lookup(float ramp[], float at, int interpolate, int extrapolate)
+{
+ float f = at;
+ int table_size = arraylength(ramp);
+
+ if ((f < 0.0 || f > 1.0) && extrapolate) {
+ float t0, dy;
+ if (f < 0.0) {
+ t0 = ramp[0];
+ dy = t0 - ramp[1];
+ f = -f;
+ }
+ else {
+ t0 = ramp[table_size - 1];
+ dy = t0 - ramp[table_size - 2];
+ f = f - 1.0;
+ }
+ return t0 + dy * f * (table_size - 1);
+ }
+
+ f = clamp(at, 0.0, 1.0) * (table_size - 1);
+
+ /* clamp int as well in case of NaN */
+ int i = (int)f;
+ if (i < 0) i = 0;
+ if (i >= table_size) i = table_size - 1;
+ float t = f - (float)i;
+
+ float result = ramp[i];
+
+ if (interpolate && t > 0.0)
+ result = (1.0 - t) * result + t * ramp[i + 1];
+
+ return result;
+}
diff --git a/intern/cycles/kernel/shaders/node_rgb_curves.osl b/intern/cycles/kernel/shaders/node_rgb_curves.osl
index 8e208e8a8f7..c8e7e4f175b 100644
--- a/intern/cycles/kernel/shaders/node_rgb_curves.osl
+++ b/intern/cycles/kernel/shaders/node_rgb_curves.osl
@@ -14,43 +14,7 @@
* limitations under the License.
*/
-#include "stdosl.h"
-#include "oslutil.h"
-
-float ramp_lookup(color ramp[], float at, int component)
-{
- int table_size = arraylength(ramp);
-
- if (at < 0.0 || at > 1.0) {
- float t0, dy;
- if (at < 0.0) {
- t0 = ramp[0][component];
- dy = t0 - ramp[1][component];
- at = -at;
- }
- else {
- t0 = ramp[table_size - 1][component];
- dy = t0 - ramp[table_size - 2][component];
- at = at - 1.0;
- }
- return t0 + dy * at * (table_size - 1);
- }
-
- float f = clamp(at, 0.0, 1.0) * (table_size - 1);
-
- /* clamp int as well in case of NaN */
- int i = (int)f;
- if (i < 0) i = 0;
- if (i >= table_size) i = table_size - 1;
- float t = f - (float)i;
-
- float result = ramp[i][component];
-
- if (t > 0.0)
- result = (1.0 - t) * result + t * ramp[i + 1][component];
-
- return result;
-}
+#include "node_ramp_util.h"
shader node_rgb_curves(
color ramp[] = {0.0},
@@ -63,9 +27,13 @@ shader node_rgb_curves(
{
color c = (ColorIn - color(min_x, min_x, min_x)) / (max_x - min_x);
- ColorOut[0] = ramp_lookup(ramp, c[0], 0);
- ColorOut[1] = ramp_lookup(ramp, c[1], 1);
- ColorOut[2] = ramp_lookup(ramp, c[2], 2);
+ color r = rgb_ramp_lookup(ramp, c[0], 1, 1);
+ color g = rgb_ramp_lookup(ramp, c[0], 1, 1);
+ color b = rgb_ramp_lookup(ramp, c[0], 1, 1);
+
+ ColorOut[0] = r[0];
+ ColorOut[1] = g[1];
+ ColorOut[2] = b[2];
ColorOut = mix(ColorIn, ColorOut, Fac);
}
diff --git a/intern/cycles/kernel/shaders/node_rgb_ramp.osl b/intern/cycles/kernel/shaders/node_rgb_ramp.osl
index c0ae74d6b33..24b8728b999 100644
--- a/intern/cycles/kernel/shaders/node_rgb_ramp.osl
+++ b/intern/cycles/kernel/shaders/node_rgb_ramp.osl
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#include "stdosl.h"
-#include "oslutil.h"
+#include "node_ramp_util.h"
shader node_rgb_ramp(
color ramp_color[] = {0.0},
@@ -26,21 +25,7 @@ shader node_rgb_ramp(
output color Color = 0.0,
output float Alpha = 1.0)
{
- int table_size = arraylength(ramp_color);
- float f = clamp(Fac, 0.0, 1.0) * (table_size - 1);
-
- /* clamp int as well in case of NaN */
- int i = (int)f;
- if (i < 0) i = 0;
- if (i >= table_size) i = table_size - 1;
- float t = f - (float)i;
-
- Color = ramp_color[i];
- Alpha = ramp_alpha[i];
-
- if (interpolate && t > 0.0) {
- Color = (1.0 - t) * Color + t * ramp_color[i + 1];
- Alpha = (1.0 - t) * Alpha + t * ramp_alpha[i + 1];
- }
+ Color = rgb_ramp_lookup(ramp_color, Fac, interpolate, 0);
+ Alpha = rgb_ramp_lookup(ramp_alpha, Fac, interpolate, 0);
}
diff --git a/intern/cycles/kernel/shaders/node_vector_curves.osl b/intern/cycles/kernel/shaders/node_vector_curves.osl
index cff4efe1d98..d92fa11d439 100644
--- a/intern/cycles/kernel/shaders/node_vector_curves.osl
+++ b/intern/cycles/kernel/shaders/node_vector_curves.osl
@@ -14,43 +14,7 @@
* limitations under the License.
*/
-#include "stdosl.h"
-#include "oslutil.h"
-
-float ramp_lookup(color ramp[], float at, int component)
-{
- int table_size = arraylength(ramp);
-
- if (at < 0.0 || at > 1.0) {
- float t0, dy;
- if (at < 0.0) {
- t0 = ramp[0][component];
- dy = t0 - ramp[1][component];
- at = -at;
- }
- else {
- t0 = ramp[table_size - 1][component];
- dy = t0 - ramp[table_size - 2][component];
- at = at - 1.0;
- }
- return t0 + dy * at * (table_size - 1);
- }
-
- float f = clamp(at, 0.0, 1.0) * (table_size - 1);
-
- /* clamp int as well in case of NaN */
- int i = (int)f;
- if (i < 0) i = 0;
- if (i >= table_size) i = table_size - 1;
- float t = f - (float)i;
-
- float result = ramp[i][component];
-
- if (t > 0.0)
- result = (1.0 - t) * result + t * ramp[i + 1][component];
-
- return result;
-}
+#include "node_ramp_util.h"
shader node_vector_curves(
color ramp[] = {0.0},
@@ -63,9 +27,13 @@ shader node_vector_curves(
{
vector c = (VectorIn - vector(min_x, min_x, min_x)) / (max_x - min_x);
- VectorOut[0] = ramp_lookup(ramp, c[0], 0);
- VectorOut[1] = ramp_lookup(ramp, c[1], 1);
- VectorOut[2] = ramp_lookup(ramp, c[2], 2);
+ color r = rgb_ramp_lookup(ramp, c[0], 1, 1);
+ color g = rgb_ramp_lookup(ramp, c[0], 1, 1);
+ color b = rgb_ramp_lookup(ramp, c[0], 1, 1);
+
+ VectorOut[0] = r[0];
+ VectorOut[1] = g[1];
+ VectorOut[2] = b[2];
VectorOut = mix(VectorIn, VectorOut, Fac);
}
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index de7e03e5a19..502994e71f1 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -405,10 +405,8 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a
#if NODES_GROUP(NODE_GROUP_LEVEL_3)
case NODE_RGB_CURVES:
- svm_node_rgb_curves(kg, sd, stack, node, &offset);
- break;
case NODE_VECTOR_CURVES:
- svm_node_vector_curves(kg, sd, stack, node, &offset);
+ svm_node_curves(kg, sd, stack, node, &offset);
break;
case NODE_TANGENT:
svm_node_tangent(kg, sd, stack, node);
diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h
index 63bbb27d873..de978a423b4 100644
--- a/intern/cycles/kernel/svm/svm_attribute.h
+++ b/intern/cycles/kernel/svm/svm_attribute.h
@@ -18,144 +18,136 @@ CCL_NAMESPACE_BEGIN
/* Attribute Node */
-ccl_device void svm_node_attr_init(KernelGlobals *kg, ShaderData *sd,
+ccl_device AttributeDescriptor svm_node_attr_init(KernelGlobals *kg, ShaderData *sd,
uint4 node, NodeAttributeType *type,
- NodeAttributeType *mesh_type, AttributeElement *elem, int *offset, uint *out_offset)
+ uint *out_offset)
{
*out_offset = node.z;
*type = (NodeAttributeType)node.w;
+
+ AttributeDescriptor desc;
+
if(ccl_fetch(sd, object) != OBJECT_NONE) {
- /* find attribute by unique id */
- uint id = node.y;
- uint attr_offset = ccl_fetch(sd, object)*kernel_data.bvh.attributes_map_stride;
-#ifdef __HAIR__
- attr_offset = (ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE)? attr_offset + ATTR_PRIM_CURVE: attr_offset;
-#endif
- uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset);
-
- while(attr_map.x != id) {
- if(UNLIKELY(attr_map.x == ATTR_STD_NONE)) {
- *elem = ATTR_ELEMENT_NONE;
- *offset = 0;
- *mesh_type = (NodeAttributeType)node.w;
- return;
- }
- attr_offset += ATTR_PRIM_TYPES;
- attr_map = kernel_tex_fetch(__attributes_map, attr_offset);
+ desc = find_attribute(kg, sd, node.y);
+ if(desc.offset == ATTR_STD_NOT_FOUND) {
+ desc.element = ATTR_ELEMENT_NONE;
+ desc.offset = 0;
+ desc.type = (NodeAttributeType)node.w;
}
-
- /* return result */
- *elem = (AttributeElement)attr_map.y;
- *offset = as_int(attr_map.z);
- *mesh_type = (NodeAttributeType)attr_map.w;
}
else {
/* background */
- *elem = ATTR_ELEMENT_NONE;
- *offset = 0;
- *mesh_type = (NodeAttributeType)node.w;
+ desc.element = ATTR_ELEMENT_NONE;
+ desc.offset = 0;
+ desc.type = (NodeAttributeType)node.w;
}
+
+ return desc;
}
ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node)
{
- NodeAttributeType type, mesh_type;
- AttributeElement elem;
+ NodeAttributeType type;
uint out_offset;
- int offset;
-
- svm_node_attr_init(kg, sd, node, &type, &mesh_type, &elem, &offset, &out_offset);
+ AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset);
/* fetch and store attribute */
if(type == NODE_ATTR_FLOAT) {
- if(mesh_type == NODE_ATTR_FLOAT) {
- float f = primitive_attribute_float(kg, sd, elem, offset, NULL, NULL);
+ if(desc.type == NODE_ATTR_FLOAT) {
+ float f = primitive_attribute_float(kg, sd, desc, NULL, NULL);
stack_store_float(stack, out_offset, f);
}
else {
- float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, NULL);
+ float3 f = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
stack_store_float(stack, out_offset, average(f));
}
}
else {
- if(mesh_type == NODE_ATTR_FLOAT3) {
- float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, NULL);
+ if(desc.type == NODE_ATTR_FLOAT3) {
+ float3 f = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
stack_store_float3(stack, out_offset, f);
}
else {
- float f = primitive_attribute_float(kg, sd, elem, offset, NULL, NULL);
+ float f = primitive_attribute_float(kg, sd, desc, NULL, NULL);
stack_store_float3(stack, out_offset, make_float3(f, f, f));
}
}
}
-ccl_device void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node)
+#ifndef __KERNEL_CUDA__
+ccl_device
+#else
+ccl_device_noinline
+#endif
+void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node)
{
- NodeAttributeType type, mesh_type;
- AttributeElement elem;
+ NodeAttributeType type;
uint out_offset;
- int offset;
-
- svm_node_attr_init(kg, sd, node, &type, &mesh_type, &elem, &offset, &out_offset);
+ AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset);
/* fetch and store attribute */
if(type == NODE_ATTR_FLOAT) {
- if(mesh_type == NODE_ATTR_FLOAT) {
+ if(desc.type == NODE_ATTR_FLOAT) {
float dx;
- float f = primitive_attribute_float(kg, sd, elem, offset, &dx, NULL);
+ float f = primitive_attribute_float(kg, sd, desc, &dx, NULL);
stack_store_float(stack, out_offset, f+dx);
}
else {
float3 dx;
- float3 f = primitive_attribute_float3(kg, sd, elem, offset, &dx, NULL);
+ float3 f = primitive_attribute_float3(kg, sd, desc, &dx, NULL);
stack_store_float(stack, out_offset, average(f+dx));
}
}
else {
- if(mesh_type == NODE_ATTR_FLOAT3) {
+ if(desc.type == NODE_ATTR_FLOAT3) {
float3 dx;
- float3 f = primitive_attribute_float3(kg, sd, elem, offset, &dx, NULL);
+ float3 f = primitive_attribute_float3(kg, sd, desc, &dx, NULL);
stack_store_float3(stack, out_offset, f+dx);
}
else {
float dx;
- float f = primitive_attribute_float(kg, sd, elem, offset, &dx, NULL);
+ float f = primitive_attribute_float(kg, sd, desc, &dx, NULL);
stack_store_float3(stack, out_offset, make_float3(f+dx, f+dx, f+dx));
}
}
}
-ccl_device void svm_node_attr_bump_dy(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node)
+#ifndef __KERNEL_CUDA__
+ccl_device
+#else
+ccl_device_noinline
+#endif
+void svm_node_attr_bump_dy(KernelGlobals *kg,
+ ShaderData *sd,
+ float *stack,
+ uint4 node)
{
- NodeAttributeType type, mesh_type;
- AttributeElement elem;
+ NodeAttributeType type;
uint out_offset;
- int offset;
-
- svm_node_attr_init(kg, sd, node, &type, &mesh_type, &elem, &offset, &out_offset);
+ AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset);
/* fetch and store attribute */
if(type == NODE_ATTR_FLOAT) {
- if(mesh_type == NODE_ATTR_FLOAT) {
+ if(desc.type == NODE_ATTR_FLOAT) {
float dy;
- float f = primitive_attribute_float(kg, sd, elem, offset, NULL, &dy);
+ float f = primitive_attribute_float(kg, sd, desc, NULL, &dy);
stack_store_float(stack, out_offset, f+dy);
}
else {
float3 dy;
- float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, &dy);
+ float3 f = primitive_attribute_float3(kg, sd, desc, NULL, &dy);
stack_store_float(stack, out_offset, average(f+dy));
}
}
else {
- if(mesh_type == NODE_ATTR_FLOAT3) {
+ if(desc.type == NODE_ATTR_FLOAT3) {
float3 dy;
- float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, &dy);
+ float3 f = primitive_attribute_float3(kg, sd, desc, NULL, &dy);
stack_store_float3(stack, out_offset, f+dy);
}
else {
float dy;
- float f = primitive_attribute_float(kg, sd, elem, offset, NULL, &dy);
+ float f = primitive_attribute_float(kg, sd, desc, NULL, &dy);
stack_store_float3(stack, out_offset, make_float3(f+dy, f+dy, f+dy));
}
}
diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h
index fae89aade60..017d697f9f8 100644
--- a/intern/cycles/kernel/svm/svm_closure.h
+++ b/intern/cycles/kernel/svm/svm_closure.h
@@ -18,104 +18,44 @@ CCL_NAMESPACE_BEGIN
/* Closure Nodes */
-ccl_device void svm_node_glass_setup(ShaderData *sd, ShaderClosure *sc, int type, float eta, float roughness, bool refract)
+ccl_device void svm_node_glass_setup(ShaderData *sd, MicrofacetBsdf *bsdf, int type, float eta, float roughness, bool refract)
{
if(type == CLOSURE_BSDF_SHARP_GLASS_ID) {
if(refract) {
- sc->data0 = eta;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= bsdf_refraction_setup(sc);
+ bsdf->alpha_y = 0.0f;
+ bsdf->alpha_x = 0.0f;
+ bsdf->ior = eta;
+ ccl_fetch(sd, flag) |= bsdf_refraction_setup(bsdf);
}
else {
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= bsdf_reflection_setup(sc);
+ bsdf->alpha_y = 0.0f;
+ bsdf->alpha_x = 0.0f;
+ bsdf->ior = 0.0f;
+ ccl_fetch(sd, flag) |= bsdf_reflection_setup(bsdf);
}
}
else if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID) {
- sc->data0 = roughness;
- sc->data1 = roughness;
- sc->data2 = eta;
+ bsdf->alpha_x = roughness;
+ bsdf->alpha_y = roughness;
+ bsdf->ior = eta;
if(refract)
- ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_refraction_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_refraction_setup(bsdf);
else
- ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_setup(bsdf);
}
else {
- sc->data0 = roughness;
- sc->data1 = roughness;
- sc->data2 = eta;
+ bsdf->alpha_x = roughness;
+ bsdf->alpha_y = roughness;
+ bsdf->ior = eta;
if(refract)
- ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_refraction_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_refraction_setup(bsdf);
else
- ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_setup(bsdf);
}
}
-ccl_device_inline ShaderClosure *svm_node_closure_get_non_bsdf(ShaderData *sd, ClosureType type, float mix_weight)
-{
- ShaderClosure *sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure));
-
- if(ccl_fetch(sd, num_closure) < MAX_CLOSURE) {
- sc->weight *= mix_weight;
- sc->type = type;
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
-#ifdef __OSL__
- sc->prim = NULL;
-#endif
- ccl_fetch(sd, num_closure)++;
- return sc;
- }
-
- return NULL;
-}
-
-ccl_device_inline ShaderClosure *svm_node_closure_get_bsdf(ShaderData *sd, float mix_weight)
-{
- ShaderClosure *sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure));
-
- float3 weight = sc->weight * mix_weight;
- float sample_weight = fabsf(average(weight));
-
- if(sample_weight > CLOSURE_WEIGHT_CUTOFF && ccl_fetch(sd, num_closure) < MAX_CLOSURE) {
- sc->weight = weight;
- sc->sample_weight = sample_weight;
- ccl_fetch(sd, num_closure)++;
-#ifdef __OSL__
- sc->prim = NULL;
-#endif
- return sc;
- }
-
- return NULL;
-}
-
-ccl_device_inline ShaderClosure *svm_node_closure_get_absorption(ShaderData *sd, float mix_weight)
-{
- ShaderClosure *sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure));
-
- float3 weight = (make_float3(1.0f, 1.0f, 1.0f) - sc->weight) * mix_weight;
- float sample_weight = fabsf(average(weight));
-
- if(sample_weight > CLOSURE_WEIGHT_CUTOFF && ccl_fetch(sd, num_closure) < MAX_CLOSURE) {
- sc->weight = weight;
- sc->sample_weight = sample_weight;
- ccl_fetch(sd, num_closure)++;
-#ifdef __OSL__
- sc->prim = NULL;
-#endif
- return sc;
- }
-
- return NULL;
-}
-
ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int path_flag, int *offset)
{
uint type, param1_offset, param2_offset;
@@ -137,49 +77,40 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
switch(type) {
case CLOSURE_BSDF_DIFFUSE_ID: {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ OrenNayarBsdf *bsdf = (OrenNayarBsdf*)bsdf_alloc(sd, sizeof(OrenNayarBsdf), weight);
- if(sc) {
- sc->N = N;
+ if(bsdf) {
+ bsdf->N = N;
float roughness = param1;
if(roughness == 0.0f) {
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= bsdf_diffuse_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_diffuse_setup((DiffuseBsdf*)bsdf);
}
else {
- sc->data0 = roughness;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= bsdf_oren_nayar_setup(sc);
+ bsdf->roughness = roughness;
+ ccl_fetch(sd, flag) |= bsdf_oren_nayar_setup(bsdf);
}
}
break;
}
case CLOSURE_BSDF_TRANSLUCENT_ID: {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ DiffuseBsdf *bsdf = (DiffuseBsdf*)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight);
- if(sc) {
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- sc->N = N;
- ccl_fetch(sd, flag) |= bsdf_translucent_setup(sc);
+ if(bsdf) {
+ bsdf->N = N;
+ ccl_fetch(sd, flag) |= bsdf_translucent_setup(bsdf);
}
break;
}
case CLOSURE_BSDF_TRANSPARENT_ID: {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ ShaderClosure *bsdf = bsdf_alloc(sd, sizeof(ShaderClosure), weight);
- if(sc) {
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- sc->N = N;
- ccl_fetch(sd, flag) |= bsdf_transparent_setup(sc);
+ if(bsdf) {
+ ccl_fetch(sd, flag) |= bsdf_transparent_setup(bsdf);
}
break;
}
@@ -192,31 +123,33 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(!kernel_data.integrator.caustics_reflective && (path_flag & PATH_RAY_DIFFUSE))
break;
#endif
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight);
- if(sc) {
- sc->N = N;
- sc->data0 = param1;
- sc->data1 = param1;
- sc->data2 = 0.0f;
+ if(bsdf) {
+ bsdf->N = N;
+ bsdf->alpha_x = param1;
+ bsdf->alpha_y = param1;
+ bsdf->ior = 0.0f;
+ bsdf->extra = NULL;
/* setup bsdf */
if(type == CLOSURE_BSDF_REFLECTION_ID)
- ccl_fetch(sd, flag) |= bsdf_reflection_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_reflection_setup(bsdf);
else if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_ID)
- ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_setup(bsdf);
else if(type == CLOSURE_BSDF_MICROFACET_GGX_ID)
- ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_setup(bsdf);
else if(type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) {
kernel_assert(stack_valid(data_node.z));
- float3 color = stack_load_float3(stack, data_node.z);
- sc->custom1 = color.x;
- sc->custom2 = color.y;
- sc->custom3 = color.z;
- ccl_fetch(sd, flag) |= bsdf_microfacet_multi_ggx_setup(sc);
+ bsdf->extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra));
+ if(bsdf->extra) {
+ bsdf->extra->color = stack_load_float3(stack, data_node.z);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_multi_ggx_setup(bsdf);
+ }
}
else
- ccl_fetch(sd, flag) |= bsdf_ashikhmin_shirley_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_ashikhmin_shirley_setup(bsdf);
}
break;
@@ -228,31 +161,33 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(!kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE))
break;
#endif
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight);
- if(sc) {
- sc->N = N;
+ if(bsdf) {
+ bsdf->N = N;
+ bsdf->extra = NULL;
float eta = fmaxf(param2, 1e-5f);
eta = (ccl_fetch(sd, flag) & SD_BACKFACING)? 1.0f/eta: eta;
/* setup bsdf */
if(type == CLOSURE_BSDF_REFRACTION_ID) {
- sc->data0 = eta;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
+ bsdf->alpha_x = 0.0f;
+ bsdf->alpha_y = 0.0f;
+ bsdf->ior = eta;
- ccl_fetch(sd, flag) |= bsdf_refraction_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_refraction_setup(bsdf);
}
else {
- sc->data0 = param1;
- sc->data1 = param1;
- sc->data2 = eta;
+ bsdf->alpha_x = param1;
+ bsdf->alpha_y = param1;
+ bsdf->ior = eta;
if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID)
- ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_refraction_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_refraction_setup(bsdf);
else
- ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_refraction_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_refraction_setup(bsdf);
}
}
@@ -268,7 +203,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
break;
}
#endif
- int num_closure = ccl_fetch(sd, num_closure);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
/* index of refraction */
float eta = fmaxf(param2, 1e-5f);
@@ -280,37 +215,30 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
float roughness = param1;
/* reflection */
- ShaderClosure *sc = ccl_fetch_array(sd, closure, num_closure);
- float3 weight = sc->weight;
- float sample_weight = sc->sample_weight;
-
- sc = svm_node_closure_get_bsdf(sd, mix_weight*fresnel);
#ifdef __CAUSTICS_TRICKS__
if(kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0)
#endif
{
- if(sc) {
- sc->N = N;
- svm_node_glass_setup(sd, sc, type, eta, roughness, false);
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight*fresnel);
+
+ if(bsdf) {
+ bsdf->N = N;
+ bsdf->extra = NULL;
+ svm_node_glass_setup(sd, bsdf, type, eta, roughness, false);
}
}
+ /* refraction */
#ifdef __CAUSTICS_TRICKS__
- if(!kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE))
- break;
+ if(kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0)
#endif
+ {
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight*(1.0f - fresnel));
- /* refraction */
- if(num_closure + 1 < MAX_CLOSURE) {
- sc = ccl_fetch_array(sd, closure, num_closure + 1);
- sc->weight = weight;
- sc->sample_weight = sample_weight;
-
- sc = svm_node_closure_get_bsdf(sd, mix_weight*(1.0f - fresnel));
-
- if(sc) {
- sc->N = N;
- svm_node_glass_setup(sd, sc, type, eta, roughness, true);
+ if(bsdf) {
+ bsdf->N = N;
+ bsdf->extra = NULL;
+ svm_node_glass_setup(sd, bsdf, type, eta, roughness, true);
}
}
@@ -321,24 +249,25 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(!kernel_data.integrator.caustics_reflective && !kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE))
break;
#endif
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight);
+ MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra));
- if(sc) {
- sc->N = N;
+ if(bsdf && extra) {
+ bsdf->N = N;
+ bsdf->extra = extra;
+ bsdf->T = make_float3(0.0f, 0.0f, 0.0f);
- sc->data0 = param1;
- sc->data1 = param1;
+ bsdf->alpha_x = param1;
+ bsdf->alpha_y = param1;
float eta = fmaxf(param2, 1e-5f);
- sc->data2 = (ccl_fetch(sd, flag) & SD_BACKFACING)? 1.0f/eta: eta;
+ bsdf->ior = (ccl_fetch(sd, flag) & SD_BACKFACING)? 1.0f/eta: eta;
kernel_assert(stack_valid(data_node.z));
- float3 color = stack_load_float3(stack, data_node.z);
- sc->custom1 = color.x;
- sc->custom2 = color.y;
- sc->custom3 = color.z;
+ bsdf->extra->color = stack_load_float3(stack, data_node.z);
/* setup bsdf */
- ccl_fetch(sd, flag) |= bsdf_microfacet_multi_ggx_glass_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_multi_ggx_glass_setup(bsdf);
}
break;
@@ -351,62 +280,63 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(!kernel_data.integrator.caustics_reflective && (path_flag & PATH_RAY_DIFFUSE))
break;
#endif
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
-
- if(sc) {
- sc->N = N;
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight);
- sc->T = stack_load_float3(stack, data_node.y);
+ if(bsdf) {
+ bsdf->N = N;
+ bsdf->extra = NULL;
+ bsdf->T = stack_load_float3(stack, data_node.y);
/* rotate tangent */
float rotation = stack_load_float(stack, data_node.z);
if(rotation != 0.0f)
- sc->T = rotate_around_axis(sc->T, sc->N, rotation * M_2PI_F);
+ bsdf->T = rotate_around_axis(bsdf->T, bsdf->N, rotation * M_2PI_F);
/* compute roughness */
float roughness = param1;
float anisotropy = clamp(param2, -0.99f, 0.99f);
if(anisotropy < 0.0f) {
- sc->data0 = roughness/(1.0f + anisotropy);
- sc->data1 = roughness*(1.0f + anisotropy);
+ bsdf->alpha_x = roughness/(1.0f + anisotropy);
+ bsdf->alpha_y = roughness*(1.0f + anisotropy);
}
else {
- sc->data0 = roughness*(1.0f - anisotropy);
- sc->data1 = roughness/(1.0f - anisotropy);
+ bsdf->alpha_x = roughness*(1.0f - anisotropy);
+ bsdf->alpha_y = roughness/(1.0f - anisotropy);
}
- sc->data2 = 0.0f;
+ bsdf->ior = 0.0f;
- if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID)
- ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_aniso_setup(sc);
- else if(type == CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID)
- ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_aniso_setup(sc);
+ if(type == CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID) {
+ ccl_fetch(sd, flag) |= bsdf_microfacet_beckmann_aniso_setup(bsdf);
+ }
+ else if(type == CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID) {
+ ccl_fetch(sd, flag) |= bsdf_microfacet_ggx_aniso_setup(bsdf);
+ }
else if(type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID) {
kernel_assert(stack_valid(data_node.w));
- float3 color = stack_load_float3(stack, data_node.w);
- sc->custom1 = color.x;
- sc->custom2 = color.y;
- sc->custom3 = color.z;
- ccl_fetch(sd, flag) |= bsdf_microfacet_multi_ggx_aniso_setup(sc);
+ bsdf->extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra));
+ if(bsdf->extra) {
+ bsdf->extra->color = stack_load_float3(stack, data_node.w);
+ ccl_fetch(sd, flag) |= bsdf_microfacet_multi_ggx_aniso_setup(bsdf);
+ }
}
else
- ccl_fetch(sd, flag) |= bsdf_ashikhmin_shirley_aniso_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_ashikhmin_shirley_aniso_setup(bsdf);
}
break;
}
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ VelvetBsdf *bsdf = (VelvetBsdf*)bsdf_alloc(sd, sizeof(VelvetBsdf), weight);
- if(sc) {
- sc->N = N;
+ if(bsdf) {
+ bsdf->N = N;
- /* sigma */
- sc->data0 = saturate(param1);
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= bsdf_ashikhmin_velvet_setup(sc);
+ bsdf->sigma = saturate(param1);
+ ccl_fetch(sd, flag) |= bsdf_ashikhmin_velvet_setup(bsdf);
}
break;
}
@@ -416,68 +346,62 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
break;
#endif
case CLOSURE_BSDF_DIFFUSE_TOON_ID: {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
+ ToonBsdf *bsdf = (ToonBsdf*)bsdf_alloc(sd, sizeof(ToonBsdf), weight);
- if(sc) {
- /* Normal, Size and Smooth */
- sc->N = N;
- sc->data0 = param1;
- sc->data1 = param2;
- sc->data2 = 0.0f;
+ if(bsdf) {
+ bsdf->N = N;
+ bsdf->size = param1;
+ bsdf->smooth = param2;
if(type == CLOSURE_BSDF_DIFFUSE_TOON_ID)
- ccl_fetch(sd, flag) |= bsdf_diffuse_toon_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_diffuse_toon_setup(bsdf);
else
- ccl_fetch(sd, flag) |= bsdf_glossy_toon_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_glossy_toon_setup(bsdf);
}
break;
}
#ifdef __HAIR__
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: {
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
if(ccl_fetch(sd, flag) & SD_BACKFACING && ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE) {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ ShaderClosure *bsdf = bsdf_alloc(sd, sizeof(ShaderClosure), weight);
- if(sc) {
+ if(bsdf) {
/* todo: giving a fixed weight here will cause issues when
* mixing multiple BSDFS. energy will not be conserved and
* the throughput can blow up after multiple bounces. we
* better figure out a way to skip backfaces from rays
* spawned by transmission from the front */
- sc->weight = make_float3(1.0f, 1.0f, 1.0f);
- sc->N = N;
- sc->data0 = 0.0f;
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= bsdf_transparent_setup(sc);
+ bsdf->weight = make_float3(1.0f, 1.0f, 1.0f);
+ ccl_fetch(sd, flag) |= bsdf_transparent_setup(bsdf);
}
}
else {
- ShaderClosure *sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure));
- sc = svm_node_closure_get_bsdf(sd, mix_weight);
+ HairBsdf *bsdf = (HairBsdf*)bsdf_alloc(sd, sizeof(HairBsdf), weight);
- if(sc) {
- sc->N = N;
- sc->data0 = param1;
- sc->data1 = param2;
- sc->data2 = -stack_load_float(stack, data_node.z);
+ if(bsdf) {
+ bsdf->roughness1 = param1;
+ bsdf->roughness2 = param2;
+ bsdf->offset = -stack_load_float(stack, data_node.z);
if(stack_valid(data_node.y)) {
- sc->T = normalize(stack_load_float3(stack, data_node.y));
+ bsdf->T = normalize(stack_load_float3(stack, data_node.y));
}
else if(!(ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE)) {
- sc->T = normalize(ccl_fetch(sd, dPdv));
- sc->data2 = 0.0f;
+ bsdf->T = normalize(ccl_fetch(sd, dPdv));
+ bsdf->offset = 0.0f;
}
else
- sc->T = normalize(ccl_fetch(sd, dPdu));
+ bsdf->T = normalize(ccl_fetch(sd, dPdu));
if(type == CLOSURE_BSDF_HAIR_REFLECTION_ID) {
- ccl_fetch(sd, flag) |= bsdf_hair_reflection_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_hair_reflection_setup(bsdf);
}
else {
- ccl_fetch(sd, flag) |= bsdf_hair_transmission_setup(sc);
+ ccl_fetch(sd, flag) |= bsdf_hair_transmission_setup(bsdf);
}
}
}
@@ -487,17 +411,11 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
#endif
#ifdef __SUBSURFACE__
-# ifndef __SPLIT_KERNEL__
-# define sc_next(sc) sc++
-# else
-# define sc_next(sc) sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure))
-# endif
case CLOSURE_BSSRDF_CUBIC_ID:
case CLOSURE_BSSRDF_GAUSSIAN_ID:
case CLOSURE_BSSRDF_BURLEY_ID: {
- ShaderClosure *sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure));
- float3 albedo = sc->weight;
- float3 weight = sc->weight * mix_weight;
+ float3 albedo = ccl_fetch(sd, svm_closure_weight);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight;
float sample_weight = fabsf(average(weight));
/* disable in case of diffuse ancestor, can't see it well then and
@@ -506,7 +424,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
if(path_flag & PATH_RAY_DIFFUSE_ANCESTOR)
param1 = 0.0f;
- if(sample_weight > CLOSURE_WEIGHT_CUTOFF && ccl_fetch(sd, num_closure)+2 < MAX_CLOSURE) {
+ if(sample_weight > CLOSURE_WEIGHT_CUTOFF) {
/* radius * scale */
float3 radius = stack_load_float3(stack, data_node.z)*param1;
/* sharpness */
@@ -515,61 +433,42 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
float texture_blur = param2;
/* create one closure per color channel */
- if(fabsf(weight.x) > 0.0f) {
- sc->weight = make_float3(weight.x, 0.0f, 0.0f);
- sc->sample_weight = sample_weight;
- sc->data0 = radius.x;
- sc->data1 = texture_blur;
- sc->data2 = albedo.x;
- sc->T.x = sharpness;
-# ifdef __OSL__
- sc->prim = NULL;
-# endif
- sc->N = N;
- ccl_fetch(sd, flag) |= bssrdf_setup(sc, (ClosureType)type);
-
- ccl_fetch(sd, num_closure)++;
- sc_next(sc);
+ Bssrdf *bssrdf = bssrdf_alloc(sd, make_float3(weight.x, 0.0f, 0.0f));
+ if(bssrdf) {
+ bssrdf->sample_weight = sample_weight;
+ bssrdf->radius = radius.x;
+ bssrdf->texture_blur = texture_blur;
+ bssrdf->albedo = albedo.x;
+ bssrdf->sharpness = sharpness;
+ bssrdf->N = N;
+ ccl_fetch(sd, flag) |= bssrdf_setup(bssrdf, (ClosureType)type);
}
- if(fabsf(weight.y) > 0.0f) {
- sc->weight = make_float3(0.0f, weight.y, 0.0f);
- sc->sample_weight = sample_weight;
- sc->data0 = radius.y;
- sc->data1 = texture_blur;
- sc->data2 = albedo.y;
- sc->T.x = sharpness;
-# ifdef __OSL__
- sc->prim = NULL;
-# endif
- sc->N = N;
- ccl_fetch(sd, flag) |= bssrdf_setup(sc, (ClosureType)type);
-
- ccl_fetch(sd, num_closure)++;
- sc_next(sc);
+ bssrdf = bssrdf_alloc(sd, make_float3(0.0f, weight.y, 0.0f));
+ if(bssrdf) {
+ bssrdf->sample_weight = sample_weight;
+ bssrdf->radius = radius.y;
+ bssrdf->texture_blur = texture_blur;
+ bssrdf->albedo = albedo.y;
+ bssrdf->sharpness = sharpness;
+ bssrdf->N = N;
+ ccl_fetch(sd, flag) |= bssrdf_setup(bssrdf, (ClosureType)type);
}
- if(fabsf(weight.z) > 0.0f) {
- sc->weight = make_float3(0.0f, 0.0f, weight.z);
- sc->sample_weight = sample_weight;
- sc->data0 = radius.z;
- sc->data1 = texture_blur;
- sc->data2 = albedo.z;
- sc->T.x = sharpness;
-# ifdef __OSL__
- sc->prim = NULL;
-# endif
- sc->N = N;
- ccl_fetch(sd, flag) |= bssrdf_setup(sc, (ClosureType)type);
-
- ccl_fetch(sd, num_closure)++;
- sc_next(sc);
+ bssrdf = bssrdf_alloc(sd, make_float3(0.0f, 0.0f, weight.z));
+ if(bssrdf) {
+ bssrdf->sample_weight = sample_weight;
+ bssrdf->radius = radius.z;
+ bssrdf->texture_blur = texture_blur;
+ bssrdf->albedo = albedo.z;
+ bssrdf->sharpness = sharpness;
+ bssrdf->N = N;
+ ccl_fetch(sd, flag) |= bssrdf_setup(bssrdf, (ClosureType)type);
}
}
break;
}
-# undef sc_next
#endif
default:
break;
@@ -594,7 +493,8 @@ ccl_device void svm_node_closure_volume(KernelGlobals *kg, ShaderData *sd, float
switch(type) {
case CLOSURE_VOLUME_ABSORPTION_ID: {
- ShaderClosure *sc = svm_node_closure_get_absorption(sd, mix_weight * density);
+ float3 weight = (make_float3(1.0f, 1.0f, 1.0f) - ccl_fetch(sd, svm_closure_weight)) * mix_weight * density;
+ ShaderClosure *sc = closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_NONE_ID, weight);
if(sc) {
ccl_fetch(sd, flag) |= volume_absorption_setup(sc);
@@ -602,13 +502,12 @@ ccl_device void svm_node_closure_volume(KernelGlobals *kg, ShaderData *sd, float
break;
}
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: {
- ShaderClosure *sc = svm_node_closure_get_bsdf(sd, mix_weight * density);
+ float3 weight = ccl_fetch(sd, svm_closure_weight) * mix_weight * density;
+ HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume*)bsdf_alloc(sd, sizeof(HenyeyGreensteinVolume), weight);
- if(sc) {
- sc->data0 = param2; /* g */
- sc->data1 = 0.0f;
- sc->data2 = 0.0f;
- ccl_fetch(sd, flag) |= volume_henyey_greenstein_setup(sc);
+ if(volume) {
+ volume->g = param2; /* g */
+ ccl_fetch(sd, flag) |= volume_henyey_greenstein_setup(volume);
}
break;
}
@@ -628,10 +527,10 @@ ccl_device void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 no
if(mix_weight == 0.0f)
return;
- svm_node_closure_get_non_bsdf(sd, CLOSURE_EMISSION_ID, mix_weight);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_EMISSION_ID, ccl_fetch(sd, svm_closure_weight) * mix_weight);
}
else
- svm_node_closure_get_non_bsdf(sd, CLOSURE_EMISSION_ID, 1.0f);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_EMISSION_ID, ccl_fetch(sd, svm_closure_weight));
ccl_fetch(sd, flag) |= SD_EMISSION;
}
@@ -646,10 +545,10 @@ ccl_device void svm_node_closure_background(ShaderData *sd, float *stack, uint4
if(mix_weight == 0.0f)
return;
- svm_node_closure_get_non_bsdf(sd, CLOSURE_BACKGROUND_ID, mix_weight);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_BACKGROUND_ID, ccl_fetch(sd, svm_closure_weight) * mix_weight);
}
else
- svm_node_closure_get_non_bsdf(sd, CLOSURE_BACKGROUND_ID, 1.0f);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_BACKGROUND_ID, ccl_fetch(sd, svm_closure_weight));
}
ccl_device void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 node)
@@ -662,10 +561,10 @@ ccl_device void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 nod
if(mix_weight == 0.0f)
return;
- svm_node_closure_get_non_bsdf(sd, CLOSURE_HOLDOUT_ID, mix_weight);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, ccl_fetch(sd, svm_closure_weight) * mix_weight);
}
else
- svm_node_closure_get_non_bsdf(sd, CLOSURE_HOLDOUT_ID, 1.0f);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, ccl_fetch(sd, svm_closure_weight));
ccl_fetch(sd, flag) |= SD_HOLDOUT;
}
@@ -680,10 +579,10 @@ ccl_device void svm_node_closure_ambient_occlusion(ShaderData *sd, float *stack,
if(mix_weight == 0.0f)
return;
- svm_node_closure_get_non_bsdf(sd, CLOSURE_AMBIENT_OCCLUSION_ID, mix_weight);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_AMBIENT_OCCLUSION_ID, ccl_fetch(sd, svm_closure_weight) * mix_weight);
}
else
- svm_node_closure_get_non_bsdf(sd, CLOSURE_AMBIENT_OCCLUSION_ID, 1.0f);
+ closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_AMBIENT_OCCLUSION_ID, ccl_fetch(sd, svm_closure_weight));
ccl_fetch(sd, flag) |= SD_AO;
}
@@ -692,10 +591,7 @@ ccl_device void svm_node_closure_ambient_occlusion(ShaderData *sd, float *stack,
ccl_device_inline void svm_node_closure_store_weight(ShaderData *sd, float3 weight)
{
- if(ccl_fetch(sd, num_closure) < MAX_CLOSURE) {
- ShaderClosure *sc = ccl_fetch_array(sd, closure, ccl_fetch(sd, num_closure));
- sc->weight = weight;
- }
+ ccl_fetch(sd, svm_closure_weight) = weight;
}
ccl_device void svm_node_closure_set_weight(ShaderData *sd, uint r, uint g, uint b)
diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h
index bb06254c3a9..7d512f7ff4d 100644
--- a/intern/cycles/kernel/svm/svm_geometry.h
+++ b/intern/cycles/kernel/svm/svm_geometry.h
@@ -18,7 +18,11 @@ CCL_NAMESPACE_BEGIN
/* Geometry Node */
-ccl_device void svm_node_geometry(KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset)
+ccl_device_inline void svm_node_geometry(KernelGlobals *kg,
+ ShaderData *sd,
+ float *stack,
+ uint type,
+ uint out_offset)
{
float3 data;
@@ -94,7 +98,11 @@ ccl_device void svm_node_object_info(KernelGlobals *kg, ShaderData *sd, float *s
/* Particle Info */
-ccl_device void svm_node_particle_info(KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset)
+ccl_device void svm_node_particle_info(KernelGlobals *kg,
+ ShaderData *sd,
+ float *stack,
+ uint type,
+ uint out_offset)
{
switch(type) {
case NODE_INFO_PAR_INDEX: {
@@ -146,7 +154,11 @@ ccl_device void svm_node_particle_info(KernelGlobals *kg, ShaderData *sd, float
/* Hair Info */
-ccl_device void svm_node_hair_info(KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset)
+ccl_device void svm_node_hair_info(KernelGlobals *kg,
+ ShaderData *sd,
+ float *stack,
+ uint type,
+ uint out_offset)
{
float data;
float3 data3;
diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h
index 44732734c31..b6b90dfff81 100644
--- a/intern/cycles/kernel/svm/svm_image.h
+++ b/intern/cycles/kernel/svm/svm_image.h
@@ -271,9 +271,6 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
case 87: r = kernel_tex_image_interp(__tex_image_byte4_087, x, y); break;
case 88: r = kernel_tex_image_interp(__tex_image_byte4_088, x, y); break;
case 89: r = kernel_tex_image_interp(__tex_image_byte4_089, x, y); break;
- case 90: r = kernel_tex_image_interp(__tex_image_byte4_090, x, y); break;
- case 91: r = kernel_tex_image_interp(__tex_image_byte4_091, x, y); break;
- case 92: r = kernel_tex_image_interp(__tex_image_byte4_092, x, y); break;
default:
kernel_assert(0);
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h
index 3f7d18a02fe..6d13a0d8e02 100644
--- a/intern/cycles/kernel/svm/svm_math_util.h
+++ b/intern/cycles/kernel/svm/svm_math_util.h
@@ -32,21 +32,17 @@ ccl_device void svm_vector_math(float *Fac, float3 *Vector, NodeVectorMath type,
*Fac = average_fac(*Vector);
}
else if(type == NODE_VECTOR_MATH_AVERAGE) {
- *Fac = len(Vector1 + Vector2);
- *Vector = normalize(Vector1 + Vector2);
+ *Vector = safe_normalize_len(Vector1 + Vector2, Fac);
}
else if(type == NODE_VECTOR_MATH_DOT_PRODUCT) {
*Fac = dot(Vector1, Vector2);
*Vector = make_float3(0.0f, 0.0f, 0.0f);
}
else if(type == NODE_VECTOR_MATH_CROSS_PRODUCT) {
- float3 c = cross(Vector1, Vector2);
- *Fac = len(c);
- *Vector = normalize(c);
+ *Vector = safe_normalize_len(cross(Vector1, Vector2), Fac);
}
else if(type == NODE_VECTOR_MATH_NORMALIZE) {
- *Fac = len(Vector1);
- *Vector = normalize(Vector1);
+ *Vector = safe_normalize_len(Vector1, Fac);
}
else {
*Fac = 0.0f;
diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h
index 24275d05c4a..368740f64c7 100644
--- a/intern/cycles/kernel/svm/svm_ramp.h
+++ b/intern/cycles/kernel/svm/svm_ramp.h
@@ -19,12 +19,14 @@
CCL_NAMESPACE_BEGIN
-ccl_device float4 rgb_ramp_lookup(KernelGlobals *kg,
- int offset,
- float f,
- bool interpolate,
- bool extrapolate,
- int table_size)
+/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
+
+ccl_device_inline float4 rgb_ramp_lookup(KernelGlobals *kg,
+ int offset,
+ float f,
+ bool interpolate,
+ bool extrapolate,
+ int table_size)
{
if((f < 0.0f || f > 1.0f) && extrapolate) {
float4 t0, dy;
@@ -75,36 +77,7 @@ ccl_device void svm_node_rgb_ramp(KernelGlobals *kg, ShaderData *sd, float *stac
*offset += table_size;
}
-ccl_device void svm_node_rgb_curves(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
-{
- uint fac_offset, color_offset, out_offset;
- decode_node_uchar4(node.y,
- &fac_offset,
- &color_offset,
- &out_offset,
- NULL);
-
- uint table_size = read_node(kg, offset).x;
-
- float fac = stack_load_float(stack, fac_offset);
- float3 color = stack_load_float3(stack, color_offset);
-
- const float min_x = __int_as_float(node.z),
- max_x = __int_as_float(node.w);
- const float range_x = max_x - min_x;
- const float3 relpos = (color - make_float3(min_x, min_x, min_x)) / range_x;
-
- float r = rgb_ramp_lookup(kg, *offset, relpos.x, true, true, table_size).x;
- float g = rgb_ramp_lookup(kg, *offset, relpos.y, true, true, table_size).y;
- float b = rgb_ramp_lookup(kg, *offset, relpos.z, true, true, table_size).z;
-
- color = (1.0f - fac)*color + fac*make_float3(r, g, b);
- stack_store_float3(stack, out_offset, color);
-
- *offset += table_size;
-}
-
-ccl_device void svm_node_vector_curves(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
+ccl_device void svm_node_curves(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
{
uint fac_offset, color_offset, out_offset;
decode_node_uchar4(node.y,
diff --git a/intern/cycles/kernel/svm/svm_ramp_util.h b/intern/cycles/kernel/svm/svm_ramp_util.h
new file mode 100644
index 00000000000..9f2ce1276f9
--- /dev/null
+++ b/intern/cycles/kernel/svm/svm_ramp_util.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SVM_RAMP_UTIL_H__
+#define __SVM_RAMP_UTIL_H__
+
+CCL_NAMESPACE_BEGIN
+
+/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
+
+ccl_device_inline float3 rgb_ramp_lookup(const float3 *ramp,
+ float f,
+ bool interpolate,
+ bool extrapolate,
+ int table_size)
+{
+ if ((f < 0.0f || f > 1.0f) && extrapolate) {
+ float3 t0, dy;
+ if (f < 0.0f) {
+ t0 = ramp[0];
+ dy = t0 - ramp[1],
+ f = -f;
+ }
+ else {
+ t0 = ramp[table_size - 1];
+ dy = t0 - ramp[table_size - 2];
+ f = f - 1.0f;
+ }
+ return t0 + dy * f * (table_size - 1);
+ }
+
+ f = clamp(f, 0.0f, 1.0f) * (table_size - 1);
+
+ /* clamp int as well in case of NaN */
+ int i = clamp(float_to_int(f), 0, table_size-1);
+ float t = f - (float)i;
+
+ float3 result = ramp[i];
+
+ if (interpolate && t > 0.0f)
+ result = (1.0f - t) * result + t * ramp[i + 1];
+
+ return result;
+}
+
+ccl_device float float_ramp_lookup(const float *ramp,
+ float f,
+ bool interpolate,
+ bool extrapolate,
+ int table_size)
+{
+ if ((f < 0.0f || f > 1.0f) && extrapolate) {
+ float t0, dy;
+ if (f < 0.0f) {
+ t0 = ramp[0];
+ dy = t0 - ramp[1],
+ f = -f;
+ }
+ else {
+ t0 = ramp[table_size - 1];
+ dy = t0 - ramp[table_size - 2];
+ f = f - 1.0f;
+ }
+ return t0 + dy * f * (table_size - 1);
+ }
+
+ f = clamp(f, 0.0f, 1.0f) * (table_size - 1);
+
+ /* clamp int as well in case of NaN */
+ int i = clamp(float_to_int(f), 0, table_size-1);
+ float t = f - (float)i;
+
+ float result = ramp[i];
+
+ if (interpolate && t > 0.0f)
+ result = (1.0f - t) * result + t * ramp[i + 1];
+
+ return result;
+}
+
+CCL_NAMESPACE_END
+
+#endif /* __SVM_RAMP_UTIL_H__ */
+
diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h
index 276b6f26f5e..01dede3fff5 100644
--- a/intern/cycles/kernel/svm/svm_tex_coord.h
+++ b/intern/cycles/kernel/svm/svm_tex_coord.h
@@ -99,12 +99,12 @@ ccl_device void svm_node_tex_coord(KernelGlobals *kg,
stack_store_float3(stack, out_offset, data);
}
-ccl_device_inline void svm_node_tex_coord_bump_dx(KernelGlobals *kg,
- ShaderData *sd,
- int path_flag,
- float *stack,
- uint4 node,
- int *offset)
+ccl_device void svm_node_tex_coord_bump_dx(KernelGlobals *kg,
+ ShaderData *sd,
+ int path_flag,
+ float *stack,
+ uint4 node,
+ int *offset)
{
#ifdef __RAY_DIFFERENTIALS__
float3 data;
@@ -184,12 +184,12 @@ ccl_device_inline void svm_node_tex_coord_bump_dx(KernelGlobals *kg,
#endif
}
-ccl_device_inline void svm_node_tex_coord_bump_dy(KernelGlobals *kg,
- ShaderData *sd,
- int path_flag,
- float *stack,
- uint4 node,
- int *offset)
+ccl_device void svm_node_tex_coord_bump_dy(KernelGlobals *kg,
+ ShaderData *sd,
+ int path_flag,
+ float *stack,
+ uint4 node,
+ int *offset)
{
#ifdef __RAY_DIFFERENTIALS__
float3 data;
@@ -287,23 +287,22 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st
}
/* first try to get tangent attribute */
- AttributeElement attr_elem, attr_sign_elem, attr_normal_elem;
- int attr_offset = find_attribute(kg, sd, node.z, &attr_elem);
- int attr_sign_offset = find_attribute(kg, sd, node.w, &attr_sign_elem);
- int attr_normal_offset = find_attribute(kg, sd, ATTR_STD_VERTEX_NORMAL, &attr_normal_elem);
+ const AttributeDescriptor attr = find_attribute(kg, sd, node.z);
+ const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w);
+ const AttributeDescriptor attr_normal = find_attribute(kg, sd, ATTR_STD_VERTEX_NORMAL);
- if(attr_offset == ATTR_STD_NOT_FOUND || attr_sign_offset == ATTR_STD_NOT_FOUND || attr_normal_offset == ATTR_STD_NOT_FOUND) {
+ if(attr.offset == ATTR_STD_NOT_FOUND || attr_sign.offset == ATTR_STD_NOT_FOUND || attr_normal.offset == ATTR_STD_NOT_FOUND) {
stack_store_float3(stack, normal_offset, make_float3(0.0f, 0.0f, 0.0f));
return;
}
/* get _unnormalized_ interpolated normal and tangent */
- float3 tangent = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL);
- float sign = primitive_attribute_float(kg, sd, attr_sign_elem, attr_sign_offset, NULL, NULL);
+ float3 tangent = primitive_attribute_float3(kg, sd, attr, NULL, NULL);
+ float sign = primitive_attribute_float(kg, sd, attr_sign, NULL, NULL);
float3 normal;
if(ccl_fetch(sd, shader) & SHADER_SMOOTH_NORMAL) {
- normal = primitive_attribute_float3(kg, sd, attr_normal_elem, attr_normal_offset, NULL, NULL);
+ normal = primitive_attribute_float3(kg, sd, attr_normal, NULL, NULL);
}
else {
normal = ccl_fetch(sd, Ng);
@@ -356,24 +355,22 @@ ccl_device void svm_node_tangent(KernelGlobals *kg, ShaderData *sd, float *stack
if(direction_type == NODE_TANGENT_UVMAP) {
/* UV map */
- AttributeElement attr_elem;
- int attr_offset = find_attribute(kg, sd, node.z, &attr_elem);
+ const AttributeDescriptor desc = find_attribute(kg, sd, node.z);
- if(attr_offset == ATTR_STD_NOT_FOUND)
+ if(desc.offset == ATTR_STD_NOT_FOUND)
tangent = make_float3(0.0f, 0.0f, 0.0f);
else
- tangent = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL);
+ tangent = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
}
else {
/* radial */
- AttributeElement attr_elem;
- int attr_offset = find_attribute(kg, sd, node.z, &attr_elem);
+ const AttributeDescriptor desc = find_attribute(kg, sd, node.z);
float3 generated;
- if(attr_offset == ATTR_STD_NOT_FOUND)
+ if(desc.offset == ATTR_STD_NOT_FOUND)
generated = ccl_fetch(sd, P);
else
- generated = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL);
+ generated = primitive_attribute_float3(kg, sd, desc, NULL, NULL);
if(axis == NODE_TANGENT_AXIS_X)
tangent = make_float3(0.0f, -(generated.z - 0.5f), (generated.y - 0.5f));
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index e1a8ced6a34..51083c31708 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -449,6 +449,9 @@ typedef enum ClosureType {
#define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSMISSION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID)
#define CLOSURE_IS_BSDF_ANISOTROPIC(type) (type >= CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID)
+#define CLOSURE_IS_BSDF_MULTISCATTER(type) (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID ||\
+ type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID || \
+ type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_BURLEY_ID)
#define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID)
#define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/svm_wireframe.h
index 30ccd523add..6eed9bc1a99 100644
--- a/intern/cycles/kernel/svm/svm_wireframe.h
+++ b/intern/cycles/kernel/svm/svm_wireframe.h
@@ -34,11 +34,11 @@ CCL_NAMESPACE_BEGIN
/* Wireframe Node */
-ccl_device float wireframe(KernelGlobals *kg,
- ShaderData *sd,
- float size,
- int pixel_size,
- float3 *P)
+ccl_device_inline float wireframe(KernelGlobals *kg,
+ ShaderData *sd,
+ float size,
+ int pixel_size,
+ float3 *P)
{
#ifdef __HAIR__
if(ccl_fetch(sd, prim) != PRIM_NONE && ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE)
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index a632ddc0598..8eaa9de3874 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -30,6 +30,7 @@ set(SRC
light.cpp
mesh.cpp
mesh_displace.cpp
+ mesh_subdivision.cpp
nodes.cpp
object.cpp
osl.cpp
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 71a3cba6811..c0d429a583c 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -44,6 +44,7 @@ void Attribute::set(ustring name_, TypeDesc type_, AttributeElement element_)
type = type_;
element = element_;
std = ATTR_STD_NONE;
+ flags = 0;
/* string and matrix not supported! */
assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor ||
@@ -51,16 +52,21 @@ void Attribute::set(ustring name_, TypeDesc type_, AttributeElement element_)
type == TypeDesc::TypeNormal || type == TypeDesc::TypeMatrix);
}
-void Attribute::resize(int numverts, int numtris, int numsteps, int numcurves, int numkeys, bool reserve_only)
+void Attribute::resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only)
{
if(reserve_only) {
- buffer.reserve(buffer_size(numverts, numtris, numsteps, numcurves, numkeys));
+ buffer.reserve(buffer_size(mesh, prim));
}
else {
- buffer.resize(buffer_size(numverts, numtris, numsteps, numcurves, numkeys), 0);
+ buffer.resize(buffer_size(mesh, prim), 0);
}
}
+void Attribute::resize(size_t num_elements)
+{
+ buffer.resize(num_elements * data_sizeof(), 0);
+}
+
void Attribute::add(const float& f)
{
char *data = (char*)&f;
@@ -118,6 +124,8 @@ size_t Attribute::data_sizeof() const
{
if(element == ATTR_ELEMENT_VOXEL)
return sizeof(VoxelAttribute);
+ else if(element == ATTR_ELEMENT_CORNER_BYTE)
+ return sizeof(uchar4);
else if(type == TypeDesc::TypeFloat)
return sizeof(float);
else if(type == TypeDesc::TypeMatrix)
@@ -126,10 +134,14 @@ size_t Attribute::data_sizeof() const
return sizeof(float3);
}
-size_t Attribute::element_size(int numverts, int numtris, int numsteps, int numcurves, int numkeys) const
+size_t Attribute::element_size(Mesh *mesh, AttributePrimitive prim) const
{
+ if(flags & ATTR_FINAL_SIZE) {
+ return buffer.size() / data_sizeof();
+ }
+
size_t size;
-
+
switch(element) {
case ATTR_ELEMENT_OBJECT:
case ATTR_ELEMENT_MESH:
@@ -137,38 +149,54 @@ size_t Attribute::element_size(int numverts, int numtris, int numsteps, int numc
size = 1;
break;
case ATTR_ELEMENT_VERTEX:
- size = numverts;
+ size = mesh->verts.size() + mesh->num_ngons;
+ if(prim == ATTR_PRIM_SUBD) {
+ size -= mesh->num_subd_verts;
+ }
break;
case ATTR_ELEMENT_VERTEX_MOTION:
- size = numverts * (numsteps - 1);
+ size = (mesh->verts.size() + mesh->num_ngons) * (mesh->motion_steps - 1);
+ if(prim == ATTR_PRIM_SUBD) {
+ size -= mesh->num_subd_verts * (mesh->motion_steps - 1);
+ }
break;
case ATTR_ELEMENT_FACE:
- size = numtris;
+ if(prim == ATTR_PRIM_TRIANGLE) {
+ size = mesh->num_triangles();
+ }
+ else {
+ size = mesh->subd_faces.size() + mesh->num_ngons;
+ }
break;
case ATTR_ELEMENT_CORNER:
case ATTR_ELEMENT_CORNER_BYTE:
- size = numtris*3;
+ if(prim == ATTR_PRIM_TRIANGLE) {
+ size = mesh->num_triangles()*3;
+ }
+ else {
+ size = mesh->subd_face_corners.size() + mesh->num_ngons;
+ }
break;
case ATTR_ELEMENT_CURVE:
- size = numcurves;
+ size = mesh->num_curves();
break;
case ATTR_ELEMENT_CURVE_KEY:
- size = numkeys;
+ size = mesh->curve_keys.size();
break;
case ATTR_ELEMENT_CURVE_KEY_MOTION:
- size = numkeys * (numsteps - 1);
+ size = mesh->curve_keys.size() * (mesh->motion_steps - 1);
break;
default:
size = 0;
break;
}
-
+
return size;
}
-size_t Attribute::buffer_size(int numverts, int numtris, int numsteps, int numcurves, int numkeys) const
+size_t Attribute::buffer_size(Mesh *mesh, AttributePrimitive prim) const
{
- return element_size(numverts, numtris, numsteps, numcurves, numkeys)*data_sizeof();
+ return element_size(mesh, prim)*data_sizeof();
}
bool Attribute::same_storage(TypeDesc a, TypeDesc b)
@@ -188,6 +216,29 @@ bool Attribute::same_storage(TypeDesc a, TypeDesc b)
return false;
}
+void Attribute::zero_data(void* dst)
+{
+ memset(dst, 0, data_sizeof());
+}
+
+void Attribute::add_with_weight(void* dst, void* src, float weight)
+{
+ if(element == ATTR_ELEMENT_CORNER_BYTE) {
+ for(int i = 0; i < 4; i++) {
+ ((uchar*)dst)[i] += uchar(((uchar*)src)[i] * weight);
+ }
+ }
+ else if(same_storage(type, TypeDesc::TypeFloat)) {
+ *((float*)dst) += *((float*)src) * weight;
+ }
+ else if(same_storage(type, TypeDesc::TypeVector)) {
+ *((float4*)dst) += *((float4*)src) * weight;
+ }
+ else {
+ assert(!"not implemented for this type");
+ }
+}
+
const char *Attribute::standard_name(AttributeStandard std)
{
switch(std) {
@@ -257,6 +308,7 @@ AttributeSet::AttributeSet()
{
triangle_mesh = NULL;
curve_mesh = NULL;
+ subd_mesh = NULL;
}
AttributeSet::~AttributeSet()
@@ -291,10 +343,12 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme
/* this is weak .. */
if(triangle_mesh)
- attr->resize(triangle_mesh->verts.size(), triangle_mesh->num_triangles(), triangle_mesh->motion_steps, 0, 0, false);
+ attr->resize(triangle_mesh, ATTR_PRIM_TRIANGLE, false);
if(curve_mesh)
- attr->resize(0, 0, curve_mesh->motion_steps, curve_mesh->num_curves(), curve_mesh->curve_keys.size(), false);
-
+ attr->resize(curve_mesh, ATTR_PRIM_CURVE, false);
+ if(subd_mesh)
+ attr->resize(subd_mesh, ATTR_PRIM_SUBD, false);
+
return attr;
}
@@ -330,7 +384,7 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
if(name == ustring())
name = Attribute::standard_name(std);
- if(triangle_mesh) {
+ if(triangle_mesh || subd_mesh) {
switch(std) {
case ATTR_STD_VERTEX_NORMAL:
attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_VERTEX);
@@ -452,9 +506,11 @@ void AttributeSet::resize(bool reserve_only)
{
foreach(Attribute& attr, attributes) {
if(triangle_mesh)
- attr.resize(triangle_mesh->verts.size(), triangle_mesh->num_triangles(), triangle_mesh->motion_steps, 0, 0, reserve_only);
+ attr.resize(triangle_mesh, ATTR_PRIM_TRIANGLE, reserve_only);
if(curve_mesh)
- attr.resize(0, 0, 0, curve_mesh->num_curves(), curve_mesh->curve_keys.size(), reserve_only);
+ attr.resize(curve_mesh, ATTR_PRIM_CURVE, reserve_only);
+ if(subd_mesh)
+ attr.resize(subd_mesh, ATTR_PRIM_SUBD, reserve_only);
}
}
@@ -471,12 +527,19 @@ AttributeRequest::AttributeRequest(ustring name_)
std = ATTR_STD_NONE;
triangle_type = TypeDesc::TypeFloat;
- triangle_element = ATTR_ELEMENT_NONE;
- triangle_offset = 0;
+ triangle_desc.element = ATTR_ELEMENT_NONE;
+ triangle_desc.offset = 0;
+ triangle_desc.type = NODE_ATTR_FLOAT;
curve_type = TypeDesc::TypeFloat;
- curve_element = ATTR_ELEMENT_NONE;
- curve_offset = 0;
+ curve_desc.element = ATTR_ELEMENT_NONE;
+ curve_desc.offset = 0;
+ curve_desc.type = NODE_ATTR_FLOAT;
+
+ subd_type = TypeDesc::TypeFloat;
+ subd_desc.element = ATTR_ELEMENT_NONE;
+ subd_desc.offset = 0;
+ subd_desc.type = NODE_ATTR_FLOAT;
}
AttributeRequest::AttributeRequest(AttributeStandard std_)
@@ -485,12 +548,19 @@ AttributeRequest::AttributeRequest(AttributeStandard std_)
std = std_;
triangle_type = TypeDesc::TypeFloat;
- triangle_element = ATTR_ELEMENT_NONE;
- triangle_offset = 0;
+ triangle_desc.element = ATTR_ELEMENT_NONE;
+ triangle_desc.offset = 0;
+ triangle_desc.type = NODE_ATTR_FLOAT;
curve_type = TypeDesc::TypeFloat;
- curve_element = ATTR_ELEMENT_NONE;
- curve_offset = 0;
+ curve_desc.element = ATTR_ELEMENT_NONE;
+ curve_desc.offset = 0;
+ curve_desc.type = NODE_ATTR_FLOAT;
+
+ subd_type = TypeDesc::TypeFloat;
+ subd_desc.element = ATTR_ELEMENT_NONE;
+ subd_desc.offset = 0;
+ subd_desc.type = NODE_ATTR_FLOAT;
}
/* AttributeRequestSet */
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 41b3626afd3..f4538c76369 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -54,15 +54,17 @@ public:
TypeDesc type;
vector<char> buffer;
AttributeElement element;
+ uint flags; /* enum AttributeFlag */
Attribute() {}
~Attribute();
void set(ustring name, TypeDesc type, AttributeElement element);
- void resize(int numverts, int numfaces, int numsteps, int numcurves, int numkeys, bool reserve_only);
+ void resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only);
+ void resize(size_t num_elements);
size_t data_sizeof() const;
- size_t element_size(int numverts, int numfaces, int numsteps, int numcurves, int numkeys) const;
- size_t buffer_size(int numverts, int numfaces, int numsteps, int numcurves, int numkeys) const;
+ size_t element_size(Mesh *mesh, AttributePrimitive prim) const;
+ size_t buffer_size(Mesh *mesh, AttributePrimitive prim) const;
char *data() { return (buffer.size())? &buffer[0]: NULL; };
float3 *data_float3() { return (float3*)data(); }
@@ -79,6 +81,9 @@ public:
const Transform *data_transform() const { return (const Transform*)data(); }
const VoxelAttribute *data_voxel() const { return (const VoxelAttribute*)data(); }
+ void zero_data(void* dst);
+ void add_with_weight(void* dst, void* src, float weight);
+
void add(const float& f);
void add(const float3& f);
void add(const uchar4& f);
@@ -99,6 +104,7 @@ class AttributeSet {
public:
Mesh *triangle_mesh;
Mesh *curve_mesh;
+ Mesh *subd_mesh;
list<Attribute> attributes;
AttributeSet();
@@ -130,9 +136,8 @@ public:
AttributeStandard std;
/* temporary variables used by MeshManager */
- TypeDesc triangle_type, curve_type;
- AttributeElement triangle_element, curve_element;
- int triangle_offset, curve_offset;
+ TypeDesc triangle_type, curve_type, subd_type;
+ AttributeDescriptor triangle_desc, curve_desc, subd_desc;
explicit AttributeRequest(ustring name_);
explicit AttributeRequest(AttributeStandard std);
diff --git a/intern/cycles/render/constant_fold.cpp b/intern/cycles/render/constant_fold.cpp
index 1fee6b2c081..200a4c497cd 100644
--- a/intern/cycles/render/constant_fold.cpp
+++ b/intern/cycles/render/constant_fold.cpp
@@ -18,6 +18,7 @@
#include "graph.h"
#include "util_foreach.h"
+#include "util_logging.h"
CCL_NAMESPACE_BEGIN
@@ -39,6 +40,8 @@ bool ConstantFolder::all_inputs_constant() const
void ConstantFolder::make_constant(float value) const
{
+ VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant (" << value << ").";
+
foreach(ShaderInput *sock, output->links) {
sock->set(value);
}
@@ -48,6 +51,8 @@ void ConstantFolder::make_constant(float value) const
void ConstantFolder::make_constant(float3 value) const
{
+ VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant " << value << ".";
+
foreach(ShaderInput *sock, output->links) {
sock->set(value);
}
@@ -62,7 +67,7 @@ void ConstantFolder::make_constant_clamp(float value, bool clamp) const
void ConstantFolder::make_constant_clamp(float3 value, bool clamp) const
{
- if (clamp) {
+ if(clamp) {
value.x = saturate(value.x);
value.y = saturate(value.y);
value.z = saturate(value.z);
@@ -71,10 +76,25 @@ void ConstantFolder::make_constant_clamp(float3 value, bool clamp) const
make_constant(value);
}
+void ConstantFolder::make_zero() const
+{
+ if(output->type() == SocketType::FLOAT) {
+ make_constant(0.0f);
+ }
+ else if(SocketType::is_float3(output->type())) {
+ make_constant(make_float3(0.0f, 0.0f, 0.0f));
+ }
+ else {
+ assert(0);
+ }
+}
+
void ConstantFolder::bypass(ShaderOutput *new_output) const
{
assert(new_output);
+ VLOG(1) << "Folding " << node->name << "::" << output->name() << " to socket " << new_output->parent->name << "::" << new_output->name() << ".";
+
/* Remove all outgoing links from socket and connect them to new_output instead.
* The graph->relink method affects node inputs, so it's not safe to use in constant
* folding if the node has multiple outputs and will thus be folded multiple times. */
@@ -90,6 +110,9 @@ void ConstantFolder::bypass(ShaderOutput *new_output) const
void ConstantFolder::discard() const
{
assert(output->type() == SocketType::CLOSURE);
+
+ VLOG(1) << "Discarding closure " << node->name << ".";
+
graph->disconnect(output);
}
@@ -97,7 +120,7 @@ void ConstantFolder::bypass_or_discard(ShaderInput *input) const
{
assert(input->type() == SocketType::CLOSURE);
- if (input->link) {
+ if(input->link) {
bypass(input->link);
}
else {
@@ -105,11 +128,20 @@ void ConstantFolder::bypass_or_discard(ShaderInput *input) const
}
}
-bool ConstantFolder::try_bypass_or_make_constant(ShaderInput *input, float3 input_value, bool clamp) const
+bool ConstantFolder::try_bypass_or_make_constant(ShaderInput *input, bool clamp) const
{
- if(!input->link) {
- make_constant_clamp(input_value, clamp);
- return true;
+ if(input->type() != output->type()) {
+ return false;
+ }
+ else if(!input->link) {
+ if(input->type() == SocketType::FLOAT) {
+ make_constant_clamp(node->get_float(input->socket_type), clamp);
+ return true;
+ }
+ else if(SocketType::is_float3(input->type())) {
+ make_constant_clamp(node->get_float3(input->socket_type), clamp);
+ return true;
+ }
}
else if(!clamp) {
bypass(input->link);
@@ -119,4 +151,212 @@ bool ConstantFolder::try_bypass_or_make_constant(ShaderInput *input, float3 inpu
return false;
}
+bool ConstantFolder::is_zero(ShaderInput *input) const
+{
+ if(!input->link) {
+ if(input->type() == SocketType::FLOAT) {
+ return node->get_float(input->socket_type) == 0.0f;
+ }
+ else if(SocketType::is_float3(input->type())) {
+ return node->get_float3(input->socket_type) ==
+ make_float3(0.0f, 0.0f, 0.0f);
+ }
+ }
+
+ return false;
+}
+
+bool ConstantFolder::is_one(ShaderInput *input) const
+{
+ if(!input->link) {
+ if(input->type() == SocketType::FLOAT) {
+ return node->get_float(input->socket_type) == 1.0f;
+ }
+ else if(SocketType::is_float3(input->type())) {
+ return node->get_float3(input->socket_type) ==
+ make_float3(1.0f, 1.0f, 1.0f);
+ }
+ }
+
+ return false;
+}
+
+/* Specific nodes */
+
+void ConstantFolder::fold_mix(NodeMix type, bool clamp) const
+{
+ ShaderInput *fac_in = node->input("Fac");
+ ShaderInput *color1_in = node->input("Color1");
+ ShaderInput *color2_in = node->input("Color2");
+
+ float fac = saturate(node->get_float(fac_in->socket_type));
+ bool fac_is_zero = !fac_in->link && fac == 0.0f;
+ bool fac_is_one = !fac_in->link && fac == 1.0f;
+
+ /* remove no-op node when factor is 0.0 */
+ if(fac_is_zero) {
+ /* note that some of the modes will clamp out of bounds values even without use_clamp */
+ if(!(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN)) {
+ if(try_bypass_or_make_constant(color1_in, clamp)) {
+ return;
+ }
+ }
+ }
+
+ switch(type) {
+ case NODE_MIX_BLEND:
+ /* remove useless mix colors nodes */
+ if(color1_in->link && color2_in->link) {
+ if(color1_in->link == color2_in->link) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ break;
+ }
+ }
+ else if(!color1_in->link && !color2_in->link) {
+ float3 color1 = node->get_float3(color1_in->socket_type);
+ float3 color2 = node->get_float3(color2_in->socket_type);
+ if(color1 == color2) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ break;
+ }
+ }
+ /* remove no-op mix color node when factor is 1.0 */
+ if(fac_is_one) {
+ try_bypass_or_make_constant(color2_in, clamp);
+ break;
+ }
+ break;
+ case NODE_MIX_ADD:
+ /* 0 + X (fac 1) == X */
+ if(is_zero(color1_in) && fac_is_one) {
+ try_bypass_or_make_constant(color2_in, clamp);
+ }
+ /* X + 0 (fac ?) == X */
+ else if(is_zero(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ break;
+ case NODE_MIX_SUB:
+ /* X - 0 (fac ?) == X */
+ if(is_zero(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ /* X - X (fac 1) == 0 */
+ else if(color1_in->link && color1_in->link == color2_in->link && fac_is_one) {
+ make_zero();
+ }
+ break;
+ case NODE_MIX_MUL:
+ /* X * 1 (fac ?) == X, 1 * X (fac 1) == X */
+ if(is_one(color1_in) && fac_is_one) {
+ try_bypass_or_make_constant(color2_in, clamp);
+ }
+ else if(is_one(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ /* 0 * ? (fac ?) == 0, ? * 0 (fac 1) == 0 */
+ else if(is_zero(color1_in)) {
+ make_zero();
+ }
+ else if(is_zero(color2_in) && fac_is_one) {
+ make_zero();
+ }
+ break;
+ case NODE_MIX_DIV:
+ /* X / 1 (fac ?) == X */
+ if(is_one(color2_in)) {
+ try_bypass_or_make_constant(color1_in, clamp);
+ }
+ /* 0 / ? (fac ?) == 0 */
+ else if(is_zero(color1_in)) {
+ make_zero();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void ConstantFolder::fold_math(NodeMath type, bool clamp) const
+{
+ ShaderInput *value1_in = node->input("Value1");
+ ShaderInput *value2_in = node->input("Value2");
+
+ switch(type) {
+ case NODE_MATH_ADD:
+ /* X + 0 == 0 + X == X */
+ if(is_zero(value1_in)) {
+ try_bypass_or_make_constant(value2_in, clamp);
+ }
+ else if(is_zero(value2_in)) {
+ try_bypass_or_make_constant(value1_in, clamp);
+ }
+ break;
+ case NODE_MATH_SUBTRACT:
+ /* X - 0 == X */
+ if(is_zero(value2_in)) {
+ try_bypass_or_make_constant(value1_in, clamp);
+ }
+ break;
+ case NODE_MATH_MULTIPLY:
+ /* X * 1 == 1 * X == X */
+ if(is_one(value1_in)) {
+ try_bypass_or_make_constant(value2_in, clamp);
+ }
+ else if(is_one(value2_in)) {
+ try_bypass_or_make_constant(value1_in, clamp);
+ }
+ /* X * 0 == 0 * X == 0 */
+ else if(is_zero(value1_in) || is_zero(value2_in)) {
+ make_zero();
+ }
+ break;
+ case NODE_MATH_DIVIDE:
+ /* X / 1 == X */
+ if(is_one(value2_in)) {
+ try_bypass_or_make_constant(value1_in, clamp);
+ }
+ /* 0 / X == 0 */
+ else if(is_zero(value1_in)) {
+ make_zero();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void ConstantFolder::fold_vector_math(NodeVectorMath type) const
+{
+ ShaderInput *vector1_in = node->input("Vector1");
+ ShaderInput *vector2_in = node->input("Vector2");
+
+ switch(type) {
+ case NODE_VECTOR_MATH_ADD:
+ /* X + 0 == 0 + X == X */
+ if(is_zero(vector1_in)) {
+ try_bypass_or_make_constant(vector2_in);
+ }
+ else if(is_zero(vector2_in)) {
+ try_bypass_or_make_constant(vector1_in);
+ }
+ break;
+ case NODE_VECTOR_MATH_SUBTRACT:
+ /* X - 0 == X */
+ if(is_zero(vector2_in)) {
+ try_bypass_or_make_constant(vector1_in);
+ }
+ break;
+ case NODE_VECTOR_MATH_DOT_PRODUCT:
+ case NODE_VECTOR_MATH_CROSS_PRODUCT:
+ /* X * 0 == 0 * X == 0 */
+ if(is_zero(vector1_in) || is_zero(vector2_in)) {
+ make_zero();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/constant_fold.h b/intern/cycles/render/constant_fold.h
index 978c8e5335a..2b31c2a5887 100644
--- a/intern/cycles/render/constant_fold.h
+++ b/intern/cycles/render/constant_fold.h
@@ -18,6 +18,7 @@
#define __CONSTANT_FOLD_H__
#include "util_types.h"
+#include "svm_types.h"
CCL_NAMESPACE_BEGIN
@@ -36,11 +37,12 @@ public:
bool all_inputs_constant() const;
- /* Constant folding helpers, always return true for convenience. */
+ /* Constant folding helpers */
void make_constant(float value) const;
void make_constant(float3 value) const;
void make_constant_clamp(float value, bool clamp) const;
void make_constant_clamp(float3 value, bool clamp) const;
+ void make_zero() const;
/* Bypass node, relinking to another output socket. */
void bypass(ShaderOutput *output) const;
@@ -50,7 +52,16 @@ public:
void bypass_or_discard(ShaderInput *input) const;
/* Bypass or make constant, unless we can't due to clamp being true. */
- bool try_bypass_or_make_constant(ShaderInput *input, float3 input_value, bool clamp) const;
+ bool try_bypass_or_make_constant(ShaderInput *input, bool clamp = false) const;
+
+ /* Test if shader inputs of the current nodes have fixed values. */
+ bool is_zero(ShaderInput *input) const;
+ bool is_one(ShaderInput *input) const;
+
+ /* Specific nodes. */
+ void fold_mix(NodeMix type, bool clamp) const;
+ void fold_math(NodeMath type, bool clamp) const;
+ void fold_vector_math(NodeVectorMath type) const;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp
index 66601fa3502..6e795ef896a 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -24,6 +24,7 @@
#include "util_debug.h"
#include "util_foreach.h"
#include "util_queue.h"
+#include "util_logging.h"
CCL_NAMESPACE_BEGIN
@@ -543,6 +544,7 @@ void ShaderGraph::deduplicate_nodes()
ShaderNodeSet scheduled, done;
map<ustring, ShaderNodeSet> candidates;
queue<ShaderNode*> traverse_queue;
+ int num_deduplicated = 0;
/* Schedule nodes which doesn't have any dependencies. */
foreach(ShaderNode *node, nodes) {
@@ -557,8 +559,10 @@ void ShaderGraph::deduplicate_nodes()
traverse_queue.pop();
done.insert(node);
/* Schedule the nodes which were depending on the current node. */
+ bool has_output_links = false;
foreach(ShaderOutput *output, node->outputs) {
foreach(ShaderInput *input, output->links) {
+ has_output_links = true;
if(scheduled.find(input->parent) != scheduled.end()) {
/* Node might not be optimized yet but scheduled already
* by other dependencies. No need to re-schedule it.
@@ -572,6 +576,10 @@ void ShaderGraph::deduplicate_nodes()
}
}
}
+ /* Only need to care about nodes that are actually used */
+ if(!has_output_links) {
+ continue;
+ }
/* Try to merge this node with another one. */
ShaderNode *merge_with = NULL;
foreach(ShaderNode *other_node, candidates[node->type->name]) {
@@ -585,11 +593,16 @@ void ShaderGraph::deduplicate_nodes()
for(int i = 0; i < node->outputs.size(); ++i) {
relink(node, node->outputs[i], merge_with->outputs[i]);
}
+ num_deduplicated++;
}
else {
candidates[node->type->name].insert(node);
}
}
+
+ if(num_deduplicated > 0) {
+ VLOG(1) << "Deduplicated " << num_deduplicated << " nodes.";
+ }
}
void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<bool>& on_stack)
@@ -967,6 +980,9 @@ int ShaderGraph::get_num_closures()
else if(CLOSURE_IS_GLASS(closure_type)) {
num_closures += 2;
}
+ else if(CLOSURE_IS_BSDF_MULTISCATTER(closure_type)) {
+ num_closures += 2;
+ }
else {
++num_closures;
}
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 9ef35820254..4cd77f8c6e1 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -209,57 +209,73 @@ void LightManager::disable_ineffective_light(Device *device, Scene *scene)
}
}
+bool LightManager::object_usable_as_light(Object *object) {
+ Mesh *mesh = object->mesh;
+ /* Skip if we are not visible for BSDFs. */
+ if(!(object->visibility & (PATH_RAY_DIFFUSE|PATH_RAY_GLOSSY|PATH_RAY_TRANSMIT))) {
+ return false;
+ }
+ /* Skip motion blurred deforming meshes, not supported yet. */
+ if(mesh->has_motion_blur()) {
+ return false;
+ }
+ /* Skip if we have no emission shaders. */
+ /* TODO(sergey): Ideally we want to avoid such duplicated loop, since it'll
+ * iterate all mesh shaders twice (when counting and when calculating
+ * triangle area.
+ */
+ foreach(const Shader *shader, mesh->used_shaders) {
+ if(shader->use_mis && shader->has_surface_emission) {
+ return true;
+ }
+ }
+ return false;
+}
+
void LightManager::device_update_distribution(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
{
progress.set_status("Updating Lights", "Computing distribution");
/* count */
size_t num_lights = 0;
+ size_t num_portals = 0;
size_t num_background_lights = 0;
size_t num_triangles = 0;
bool background_mis = false;
foreach(Light *light, scene->lights) {
- if(light->is_enabled)
+ if(light->is_enabled) {
num_lights++;
+ }
+ if(light->is_portal) {
+ num_portals++;
+ }
}
foreach(Object *object, scene->objects) {
- Mesh *mesh = object->mesh;
- bool have_emission = false;
-
- /* skip if we are not visible for BSDFs */
- if(!(object->visibility & (PATH_RAY_DIFFUSE|PATH_RAY_GLOSSY|PATH_RAY_TRANSMIT)))
- continue;
+ if(progress.get_cancel()) return;
- /* skip motion blurred deforming meshes, not supported yet */
- if(mesh->has_motion_blur())
+ if(!object_usable_as_light(object)) {
continue;
-
- /* skip if we have no emission shaders */
- foreach(Shader *shader, mesh->used_shaders) {
- if(shader->use_mis && shader->has_surface_emission) {
- have_emission = true;
- break;
- }
}
+ /* Count triangles. */
+ Mesh *mesh = object->mesh;
+ size_t mesh_num_triangles = mesh->num_triangles();
+ for(size_t i = 0; i < mesh_num_triangles; i++) {
+ int shader_index = mesh->shader[i];
+ Shader *shader = (shader_index < mesh->used_shaders.size())
+ ? mesh->used_shaders[shader_index]
+ : scene->default_surface;
- /* count triangles */
- if(have_emission) {
- size_t mesh_num_triangles = mesh->num_triangles();
- for(size_t i = 0; i < mesh_num_triangles; i++) {
- int shader_index = mesh->shader[i];
- Shader *shader = (shader_index < mesh->used_shaders.size()) ?
- mesh->used_shaders[shader_index] : scene->default_surface;
-
- if(shader->use_mis && shader->has_surface_emission)
- num_triangles++;
+ if(shader->use_mis && shader->has_surface_emission) {
+ num_triangles++;
}
}
}
size_t num_distribution = num_triangles + num_lights;
+ VLOG(1) << "Total " << num_distribution << " of light distribution primitives.";
/* emission area */
float4 *distribution = dscene->light_distribution.resize(num_distribution + 1);
@@ -270,87 +286,68 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
int j = 0;
foreach(Object *object, scene->objects) {
- Mesh *mesh = object->mesh;
- bool have_emission = false;
+ if(progress.get_cancel()) return;
- /* skip if we are not visible for BSDFs */
- if(!(object->visibility & (PATH_RAY_DIFFUSE|PATH_RAY_GLOSSY|PATH_RAY_TRANSMIT))) {
+ if(!object_usable_as_light(object)) {
j++;
continue;
}
+ /* Sum area. */
+ Mesh *mesh = object->mesh;
+ bool transform_applied = mesh->transform_applied;
+ Transform tfm = object->tfm;
+ int object_id = j;
+ int shader_flag = 0;
- /* skip motion blurred deforming meshes, not supported yet */
- if(mesh->has_motion_blur()) {
- j++;
- continue;
- }
+ if(transform_applied)
+ object_id = ~object_id;
- /* skip if we have no emission shaders */
- foreach(Shader *shader, mesh->used_shaders) {
- if(shader->use_mis && shader->has_surface_emission) {
- have_emission = true;
- break;
- }
+ if(!(object->visibility & PATH_RAY_DIFFUSE)) {
+ shader_flag |= SHADER_EXCLUDE_DIFFUSE;
+ use_light_visibility = true;
+ }
+ if(!(object->visibility & PATH_RAY_GLOSSY)) {
+ shader_flag |= SHADER_EXCLUDE_GLOSSY;
+ use_light_visibility = true;
+ }
+ if(!(object->visibility & PATH_RAY_TRANSMIT)) {
+ shader_flag |= SHADER_EXCLUDE_TRANSMIT;
+ use_light_visibility = true;
+ }
+ if(!(object->visibility & PATH_RAY_VOLUME_SCATTER)) {
+ shader_flag |= SHADER_EXCLUDE_SCATTER;
+ use_light_visibility = true;
}
- /* sum area */
- if(have_emission) {
- bool transform_applied = mesh->transform_applied;
- Transform tfm = object->tfm;
- int object_id = j;
- int shader_flag = 0;
-
- if(transform_applied)
- object_id = ~object_id;
+ size_t mesh_num_triangles = mesh->num_triangles();
+ for(size_t i = 0; i < mesh_num_triangles; i++) {
+ int shader_index = mesh->shader[i];
+ Shader *shader = (shader_index < mesh->used_shaders.size())
+ ? mesh->used_shaders[shader_index]
+ : scene->default_surface;
- if(!(object->visibility & PATH_RAY_DIFFUSE)) {
- shader_flag |= SHADER_EXCLUDE_DIFFUSE;
- use_light_visibility = true;
- }
- if(!(object->visibility & PATH_RAY_GLOSSY)) {
- shader_flag |= SHADER_EXCLUDE_GLOSSY;
- use_light_visibility = true;
- }
- if(!(object->visibility & PATH_RAY_TRANSMIT)) {
- shader_flag |= SHADER_EXCLUDE_TRANSMIT;
- use_light_visibility = true;
- }
- if(!(object->visibility & PATH_RAY_VOLUME_SCATTER)) {
- shader_flag |= SHADER_EXCLUDE_SCATTER;
- use_light_visibility = true;
- }
-
- size_t mesh_num_triangles = mesh->num_triangles();
- for(size_t i = 0; i < mesh_num_triangles; i++) {
- int shader_index = mesh->shader[i];
- Shader *shader = (shader_index < mesh->used_shaders.size()) ?
- mesh->used_shaders[shader_index] : scene->default_surface;
-
- if(shader->use_mis && shader->has_surface_emission) {
- distribution[offset].x = totarea;
- distribution[offset].y = __int_as_float(i + mesh->tri_offset);
- distribution[offset].z = __int_as_float(shader_flag);
- distribution[offset].w = __int_as_float(object_id);
- offset++;
-
- Mesh::Triangle t = mesh->get_triangle(i);
- float3 p1 = mesh->verts[t.v[0]];
- float3 p2 = mesh->verts[t.v[1]];
- float3 p3 = mesh->verts[t.v[2]];
-
- if(!transform_applied) {
- p1 = transform_point(&tfm, p1);
- p2 = transform_point(&tfm, p2);
- p3 = transform_point(&tfm, p3);
- }
-
- totarea += triangle_area(p1, p2, p3);
+ if(shader->use_mis && shader->has_surface_emission) {
+ distribution[offset].x = totarea;
+ distribution[offset].y = __int_as_float(i + mesh->tri_offset);
+ distribution[offset].z = __int_as_float(shader_flag);
+ distribution[offset].w = __int_as_float(object_id);
+ offset++;
+
+ Mesh::Triangle t = mesh->get_triangle(i);
+ float3 p1 = mesh->verts[t.v[0]];
+ float3 p2 = mesh->verts[t.v[1]];
+ float3 p3 = mesh->verts[t.v[2]];
+
+ if(!transform_applied) {
+ p1 = transform_point(&tfm, p1);
+ p2 = transform_point(&tfm, p2);
+ p3 = transform_point(&tfm, p3);
}
+
+ totarea += triangle_area(p1, p2, p3);
}
}
- if(progress.get_cancel()) return;
-
j++;
}
@@ -443,9 +440,9 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
device->tex_alloc("__light_distribution", dscene->light_distribution);
/* Portals */
- if(num_background_lights > 0 && light_index != scene->lights.size()) {
+ if(num_portals > 0) {
kintegrator->portal_offset = light_index;
- kintegrator->num_portals = scene->lights.size() - light_index;
+ kintegrator->num_portals = num_portals;
kintegrator->portal_pdf = background_mis? 0.5f: 1.0f;
}
else {
@@ -609,10 +606,21 @@ void LightManager::device_update_points(Device *device,
Scene *scene)
{
int num_scene_lights = scene->lights.size();
- if(num_scene_lights == 0)
+
+ int num_lights = 0;
+ foreach(Light *light, scene->lights) {
+ if(light->is_enabled || light->is_portal) {
+ num_lights++;
+ }
+ }
+
+ float4 *light_data = dscene->light_data.resize(num_lights*LIGHT_SIZE);
+
+ if(num_lights == 0) {
+ VLOG(1) << "No effective light, ignoring points update.";
return;
+ }
- float4 *light_data = dscene->light_data.resize(num_scene_lights*LIGHT_SIZE);
int light_index = 0;
foreach(Light *light, scene->lights) {
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index 2f1df1c9417..745caa96159 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -28,6 +28,7 @@ CCL_NAMESPACE_BEGIN
class Device;
class DeviceScene;
+class Object;
class Progress;
class Scene;
class Shader;
@@ -108,6 +109,9 @@ protected:
DeviceScene *dscene,
Scene *scene,
Progress& progress);
+
+ /* Check whether light manager can use the object as a light-emissive. */
+ bool object_usable_as_light(Object *object);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 8b0ed9f77b2..f90c19a11c8 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -30,14 +30,13 @@
#include "osl_globals.h"
+#include "subd_patch_table.h"
+
#include "util_foreach.h"
#include "util_logging.h"
#include "util_progress.h"
#include "util_set.h"
-#include "subd_split.h"
-#include "subd_patch.h"
-
CCL_NAMESPACE_BEGIN
/* Triangle */
@@ -104,18 +103,23 @@ void Mesh::Curve::bounds_grow(const int k,
bounds.grow(upper, mr);
}
+/* SubdFace */
+
+float3 Mesh::SubdFace::normal(const Mesh *mesh) const
+{
+ float3 v0 = mesh->verts[mesh->subd_face_corners[start_corner+0]];
+ float3 v1 = mesh->verts[mesh->subd_face_corners[start_corner+1]];
+ float3 v2 = mesh->verts[mesh->subd_face_corners[start_corner+2]];
+
+ return safe_normalize(cross(v1 - v0, v2 - v0));
+}
+
/* Mesh */
NODE_DEFINE(Mesh)
{
NodeType* type = NodeType::add("mesh", create);
- static NodeEnum displacement_method_enum;
- displacement_method_enum.insert("bump", DISPLACE_BUMP);
- displacement_method_enum.insert("true", DISPLACE_TRUE);
- displacement_method_enum.insert("both", DISPLACE_BOTH);
- SOCKET_ENUM(displacement_method, "Displacement Method", displacement_method_enum, DISPLACE_BUMP);
-
SOCKET_UINT(motion_steps, "Motion Steps", 3);
SOCKET_BOOLEAN(use_motion_blur, "Use Motion Blur", false);
@@ -150,18 +154,32 @@ Mesh::Mesh()
curve_offset = 0;
curvekey_offset = 0;
+ patch_offset = 0;
+ face_offset = 0;
+ corner_offset = 0;
+
+ num_subd_verts = 0;
+
attributes.triangle_mesh = this;
curve_attributes.curve_mesh = this;
+ subd_attributes.subd_mesh = this;
geometry_flags = GEOMETRY_NONE;
has_volume = false;
has_surface_bssrdf = false;
+
+ num_ngons = 0;
+
+ subdivision_type = SUBDIVISION_NONE;
+
+ patch_table = NULL;
}
Mesh::~Mesh()
{
delete bvh;
+ delete patch_table;
}
void Mesh::resize_mesh(int numverts, int numtris)
@@ -171,7 +189,10 @@ void Mesh::resize_mesh(int numverts, int numtris)
shader.resize(numtris);
smooth.resize(numtris);
- forms_quad.resize(numtris);
+ if(subd_faces.size()) {
+ triangle_patch.resize(numtris);
+ vert_patch_uv.resize(numverts);
+ }
attributes.resize();
}
@@ -184,7 +205,10 @@ void Mesh::reserve_mesh(int numverts, int numtris)
shader.reserve(numtris);
smooth.reserve(numtris);
- forms_quad.reserve(numtris);
+ if(subd_faces.size()) {
+ triangle_patch.reserve(numtris);
+ vert_patch_uv.reserve(numverts);
+ }
attributes.resize(true);
}
@@ -209,6 +233,24 @@ void Mesh::reserve_curves(int numcurves, int numkeys)
curve_attributes.resize(true);
}
+void Mesh::resize_subd_faces(int numfaces, int num_ngons_, int numcorners)
+{
+ subd_faces.resize(numfaces);
+ subd_face_corners.resize(numcorners);
+ num_ngons = num_ngons_;
+
+ subd_attributes.resize();
+}
+
+void Mesh::reserve_subd_faces(int numfaces, int num_ngons_, int numcorners)
+{
+ subd_faces.reserve(numfaces);
+ subd_face_corners.reserve(numcorners);
+ num_ngons = num_ngons_;
+
+ subd_attributes.resize(true);
+}
+
void Mesh::clear()
{
/* clear all verts and triangles */
@@ -217,21 +259,33 @@ void Mesh::clear()
shader.clear();
smooth.clear();
- forms_quad.clear();
+ triangle_patch.clear();
+ vert_patch_uv.clear();
curve_keys.clear();
curve_radius.clear();
curve_first_key.clear();
curve_shader.clear();
+ subd_faces.clear();
+ subd_face_corners.clear();
+
+ num_subd_verts = 0;
+
+ subd_creases.clear();
+
attributes.clear();
curve_attributes.clear();
+ subd_attributes.clear();
used_shaders.clear();
transform_applied = false;
transform_negative_scaled = false;
transform_normal = transform_identity();
geometry_flags = GEOMETRY_NONE;
+
+ delete patch_table;
+ patch_table = NULL;
}
int Mesh::split_vertex(int vertex)
@@ -247,27 +301,46 @@ int Mesh::split_vertex(int vertex)
}
}
+ foreach(Attribute& attr, subd_attributes.attributes) {
+ if(attr.element == ATTR_ELEMENT_VERTEX) {
+ vector<char> tmp(attr.data_sizeof());
+ memcpy(&tmp[0], attr.data() + tmp.size()*vertex, tmp.size());
+ attr.add(&tmp[0]);
+ }
+ }
+
return verts.size() - 1;
}
void Mesh::add_vertex(float3 P)
{
verts.push_back_reserved(P);
+
+ if(subd_faces.size()) {
+ vert_patch_uv.push_back_reserved(make_float2(0.0f, 0.0f));
+ }
}
void Mesh::add_vertex_slow(float3 P)
{
verts.push_back_slow(P);
+
+ if(subd_faces.size()) {
+ vert_patch_uv.push_back_slow(make_float2(0.0f, 0.0f));
+ }
}
-void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_, bool forms_quad_)
+void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_)
{
triangles.push_back_reserved(v0);
triangles.push_back_reserved(v1);
triangles.push_back_reserved(v2);
shader.push_back_reserved(shader_);
smooth.push_back_reserved(smooth_);
- forms_quad.push_back_reserved(forms_quad_);
+
+ if(subd_faces.size()) {
+ triangle_patch.push_back_reserved(-1);
+ }
}
void Mesh::add_curve_key(float3 co, float radius)
@@ -282,6 +355,25 @@ void Mesh::add_curve(int first_key, int shader)
curve_shader.push_back_reserved(shader);
}
+void Mesh::add_subd_face(int* corners, int num_corners, int shader_, bool smooth_)
+{
+ int start_corner = subd_face_corners.size();
+
+ for(int i = 0; i < num_corners; i++) {
+ subd_face_corners.push_back_reserved(corners[i]);
+ }
+
+ int ptex_offset = 0;
+
+ if(subd_faces.size()) {
+ SubdFace& s = subd_faces[subd_faces.size()-1];
+ ptex_offset = s.ptex_offset + s.num_ptex_faces();
+ }
+
+ SubdFace face = {start_corner, num_corners, shader_, smooth_, ptex_offset};
+ subd_faces.push_back_reserved(face);
+}
+
void Mesh::compute_bounds()
{
BoundBox bnds = BoundBox::empty;
@@ -505,10 +597,23 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal)
void Mesh::pack_verts(const vector<uint>& tri_prim_index,
uint4 *tri_vindex,
+ uint *tri_patch,
+ float2 *tri_patch_uv,
size_t vert_offset,
size_t tri_offset)
{
- const size_t triangles_size = num_triangles();
+ size_t verts_size = verts.size();
+
+ if(verts_size && subd_faces.size()) {
+ float2 *vert_patch_uv_ptr = &vert_patch_uv[0];
+
+ for(size_t i = 0; i < verts_size; i++) {
+ tri_patch_uv[i] = vert_patch_uv_ptr[i];
+ }
+ }
+
+ size_t triangles_size = num_triangles();
+
if(triangles_size) {
for(size_t i = 0; i < triangles_size; i++) {
Triangle t = get_triangle(i);
@@ -516,6 +621,8 @@ void Mesh::pack_verts(const vector<uint>& tri_prim_index,
t.v[1] + vert_offset,
t.v[2] + vert_offset,
tri_prim_index[i + tri_offset]);
+
+ tri_patch[i] = (!subd_faces.size()) ? -1 : (triangle_patch[i]*8 + patch_offset);
}
}
}
@@ -553,6 +660,54 @@ void Mesh::pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, s
}
}
+void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset)
+{
+ size_t num_faces = subd_faces.size();
+ int ngons = 0;
+
+ if(num_faces) {
+ for(size_t f = 0; f < num_faces; f++) {
+ SubdFace face = subd_faces[f];
+
+ if(face.is_quad()) {
+ int c[4];
+ memcpy(c, &subd_face_corners[face.start_corner], sizeof(int)*4);
+
+ *(patch_data++) = c[0] + vert_offset;
+ *(patch_data++) = c[1] + vert_offset;
+ *(patch_data++) = c[2] + vert_offset;
+ *(patch_data++) = c[3] + vert_offset;
+
+ *(patch_data++) = f+face_offset;
+ *(patch_data++) = face.num_corners;
+ *(patch_data++) = face.start_corner + corner_offset;
+ *(patch_data++) = 0;
+ }
+ else {
+ for(int i = 0; i < face.num_corners; i++) {
+ int c[4];
+ c[0] = subd_face_corners[face.start_corner + mod(i + 0, face.num_corners)];
+ c[1] = subd_face_corners[face.start_corner + mod(i + 1, face.num_corners)];
+ c[2] = verts.size() - num_subd_verts + ngons;
+ c[3] = subd_face_corners[face.start_corner + mod(i - 1, face.num_corners)];
+
+ *(patch_data++) = c[0] + vert_offset;
+ *(patch_data++) = c[1] + vert_offset;
+ *(patch_data++) = c[2] + vert_offset;
+ *(patch_data++) = c[3] + vert_offset;
+
+ *(patch_data++) = f+face_offset;
+ *(patch_data++) = face.num_corners | (i << 16);
+ *(patch_data++) = face.start_corner + corner_offset;
+ *(patch_data++) = subd_face_corners.size() + ngons + corner_offset;
+ }
+
+ ngons++;
+ }
+ }
+ }
+}
+
void Mesh::compute_bvh(DeviceScene *dscene,
SceneParams *params,
Progress *progress,
@@ -626,6 +781,17 @@ bool Mesh::has_motion_blur() const
curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)));
}
+bool Mesh::has_true_displacement() const
+{
+ foreach(Shader *shader, used_shaders) {
+ if(shader->has_displacement && shader->displacement_method != DISPLACE_BUMP) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool Mesh::need_build_bvh() const
{
return !transform_applied || has_surface_bssrdf;
@@ -678,12 +844,14 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
OSLGlobals::Attribute osl_attr;
osl_attr.type = attr.type();
- osl_attr.elem = ATTR_ELEMENT_OBJECT;
+ osl_attr.desc.element = ATTR_ELEMENT_OBJECT;
osl_attr.value = attr;
- osl_attr.offset = 0;
+ osl_attr.desc.offset = 0;
+ osl_attr.desc.flags = 0;
- og->attribute_map[i*ATTR_PRIM_TYPES][attr.name()] = osl_attr;
+ og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_TRIANGLE][attr.name()] = osl_attr;
og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_CURVE][attr.name()] = osl_attr;
+ og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_SUBD][attr.name()] = osl_attr;
}
/* find mesh attributes */
@@ -699,9 +867,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
foreach(AttributeRequest& req, attributes.requests) {
OSLGlobals::Attribute osl_attr;
- if(req.triangle_element != ATTR_ELEMENT_NONE) {
- osl_attr.elem = req.triangle_element;
- osl_attr.offset = req.triangle_offset;
+ if(req.triangle_desc.element != ATTR_ELEMENT_NONE) {
+ osl_attr.desc = req.triangle_desc;
if(req.triangle_type == TypeDesc::TypeFloat)
osl_attr.type = TypeDesc::TypeFloat;
@@ -713,17 +880,16 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
if(req.std != ATTR_STD_NONE) {
/* if standard attribute, add lookup by geom: name convention */
ustring stdname(string("geom:") + string(Attribute::standard_name(req.std)));
- og->attribute_map[i*ATTR_PRIM_TYPES][stdname] = osl_attr;
+ og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_TRIANGLE][stdname] = osl_attr;
}
else if(req.name != ustring()) {
/* add lookup by mesh attribute name */
- og->attribute_map[i*ATTR_PRIM_TYPES][req.name] = osl_attr;
+ og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_TRIANGLE][req.name] = osl_attr;
}
}
- if(req.curve_element != ATTR_ELEMENT_NONE) {
- osl_attr.elem = req.curve_element;
- osl_attr.offset = req.curve_offset;
+ if(req.curve_desc.element != ATTR_ELEMENT_NONE) {
+ osl_attr.desc = req.curve_desc;
if(req.curve_type == TypeDesc::TypeFloat)
osl_attr.type = TypeDesc::TypeFloat;
@@ -742,6 +908,27 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_CURVE][req.name] = osl_attr;
}
}
+
+ if(req.subd_desc.element != ATTR_ELEMENT_NONE) {
+ osl_attr.desc = req.subd_desc;
+
+ if(req.subd_type == TypeDesc::TypeFloat)
+ osl_attr.type = TypeDesc::TypeFloat;
+ else if(req.subd_type == TypeDesc::TypeMatrix)
+ osl_attr.type = TypeDesc::TypeMatrix;
+ else
+ osl_attr.type = TypeDesc::TypeColor;
+
+ if(req.std != ATTR_STD_NONE) {
+ /* if standard attribute, add lookup by geom: name convention */
+ ustring stdname(string("geom:") + string(Attribute::standard_name(req.std)));
+ og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_SUBD][stdname] = osl_attr;
+ }
+ else if(req.name != ustring()) {
+ /* add lookup by mesh attribute name */
+ og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_SUBD][req.name] = osl_attr;
+ }
+ }
}
}
#else
@@ -795,8 +982,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
if(mesh->num_triangles()) {
attr_map[index].x = id;
- attr_map[index].y = req.triangle_element;
- attr_map[index].z = as_uint(req.triangle_offset);
+ attr_map[index].y = req.triangle_desc.element;
+ attr_map[index].z = as_uint(req.triangle_desc.offset);
if(req.triangle_type == TypeDesc::TypeFloat)
attr_map[index].w = NODE_ATTR_FLOAT;
@@ -804,14 +991,16 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
attr_map[index].w = NODE_ATTR_MATRIX;
else
attr_map[index].w = NODE_ATTR_FLOAT3;
+
+ attr_map[index].w |= req.triangle_desc.flags << 8;
}
index++;
if(mesh->num_curves()) {
attr_map[index].x = id;
- attr_map[index].y = req.curve_element;
- attr_map[index].z = as_uint(req.curve_offset);
+ attr_map[index].y = req.curve_desc.element;
+ attr_map[index].z = as_uint(req.curve_desc.offset);
if(req.curve_type == TypeDesc::TypeFloat)
attr_map[index].w = NODE_ATTR_FLOAT;
@@ -819,25 +1008,39 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
attr_map[index].w = NODE_ATTR_MATRIX;
else
attr_map[index].w = NODE_ATTR_FLOAT3;
+
+ attr_map[index].w |= req.curve_desc.flags << 8;
}
index++;
- }
- /* terminator */
- attr_map[index].x = ATTR_STD_NONE;
- attr_map[index].y = 0;
- attr_map[index].z = 0;
- attr_map[index].w = 0;
+ if(mesh->subd_faces.size()) {
+ attr_map[index].x = id;
+ attr_map[index].y = req.subd_desc.element;
+ attr_map[index].z = as_uint(req.subd_desc.offset);
+
+ if(req.subd_type == TypeDesc::TypeFloat)
+ attr_map[index].w = NODE_ATTR_FLOAT;
+ else if(req.subd_type == TypeDesc::TypeMatrix)
+ attr_map[index].w = NODE_ATTR_MATRIX;
+ else
+ attr_map[index].w = NODE_ATTR_FLOAT3;
- index++;
+ attr_map[index].w |= req.subd_desc.flags << 8;
+ }
+
+ index++;
+ }
- attr_map[index].x = ATTR_STD_NONE;
- attr_map[index].y = 0;
- attr_map[index].z = 0;
- attr_map[index].w = 0;
+ /* terminator */
+ for(int i = 0; i < ATTR_PRIM_TYPES; i++) {
+ attr_map[index].x = ATTR_STD_NONE;
+ attr_map[index].y = 0;
+ attr_map[index].z = 0;
+ attr_map[index].w = 0;
- index++;
+ index++;
+ }
}
/* copy to device */
@@ -847,17 +1050,13 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce
static void update_attribute_element_size(Mesh *mesh,
Attribute *mattr,
+ AttributePrimitive prim,
size_t *attr_float_size,
size_t *attr_float3_size,
size_t *attr_uchar4_size)
{
if(mattr) {
- size_t size = mattr->element_size(
- mesh->verts.size(),
- mesh->num_triangles(),
- mesh->motion_steps,
- mesh->num_curves(),
- mesh->curve_keys.size());
+ size_t size = mattr->element_size(mesh, prim);
if(mattr->element == ATTR_ELEMENT_VOXEL) {
/* pass */
@@ -885,22 +1084,21 @@ static void update_attribute_element_offset(Mesh *mesh,
vector<uchar4>& attr_uchar4,
size_t& attr_uchar4_offset,
Attribute *mattr,
+ AttributePrimitive prim,
TypeDesc& type,
- int& offset,
- AttributeElement& element)
+ AttributeDescriptor& desc)
{
if(mattr) {
/* store element and type */
- element = mattr->element;
+ desc.element = mattr->element;
+ desc.flags = mattr->flags;
type = mattr->type;
/* store attribute data in arrays */
- size_t size = mattr->element_size(
- mesh->verts.size(),
- mesh->num_triangles(),
- mesh->motion_steps,
- mesh->num_curves(),
- mesh->curve_keys.size());
+ size_t size = mattr->element_size(mesh, prim);
+
+ AttributeElement& element = desc.element;
+ int& offset = desc.offset;
if(mattr->element == ATTR_ELEMENT_VOXEL) {
/* store slot in offset value */
@@ -950,14 +1148,26 @@ static void update_attribute_element_offset(Mesh *mesh,
/* mesh vertex/curve index is global, not per object, so we sneak
* a correction for that in here */
- if(element == ATTR_ELEMENT_VERTEX)
+ if(mesh->subdivision_type == Mesh::SUBDIVISION_CATMULL_CLARK && desc.flags & ATTR_SUBDIVIDED) {
+ /* indices for subdivided attributes are retrieved
+ * from patch table so no need for correction here*/
+ }
+ else if(element == ATTR_ELEMENT_VERTEX)
offset -= mesh->vert_offset;
else if(element == ATTR_ELEMENT_VERTEX_MOTION)
offset -= mesh->vert_offset;
- else if(element == ATTR_ELEMENT_FACE)
- offset -= mesh->tri_offset;
- else if(element == ATTR_ELEMENT_CORNER || element == ATTR_ELEMENT_CORNER_BYTE)
- offset -= 3*mesh->tri_offset;
+ else if(element == ATTR_ELEMENT_FACE) {
+ if(prim == ATTR_PRIM_TRIANGLE)
+ offset -= mesh->tri_offset;
+ else
+ offset -= mesh->face_offset;
+ }
+ else if(element == ATTR_ELEMENT_CORNER || element == ATTR_ELEMENT_CORNER_BYTE) {
+ if(prim == ATTR_PRIM_TRIANGLE)
+ offset -= 3*mesh->tri_offset;
+ else
+ offset -= mesh->corner_offset;
+ }
else if(element == ATTR_ELEMENT_CURVE)
offset -= mesh->curve_offset;
else if(element == ATTR_ELEMENT_CURVE_KEY)
@@ -967,8 +1177,8 @@ static void update_attribute_element_offset(Mesh *mesh,
}
else {
/* attribute not found */
- element = ATTR_ELEMENT_NONE;
- offset = 0;
+ desc.element = ATTR_ELEMENT_NONE;
+ desc.offset = 0;
}
}
@@ -1007,23 +1217,23 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
foreach(AttributeRequest& req, attributes.requests) {
Attribute *triangle_mattr = mesh->attributes.find(req);
Attribute *curve_mattr = mesh->curve_attributes.find(req);
-
- /* todo: get rid of this exception, it's only here for giving some
- * working texture coordinate for subdivision as we can't preserve
- * any attributes yet */
- if(!triangle_mattr && req.std == ATTR_STD_GENERATED) {
- triangle_mattr = mesh->attributes.add(ATTR_STD_GENERATED);
- if(mesh->verts.size())
- memcpy(triangle_mattr->data_float3(), &mesh->verts[0], sizeof(float3)*mesh->verts.size());
- }
+ Attribute *subd_mattr = mesh->subd_attributes.find(req);
update_attribute_element_size(mesh,
triangle_mattr,
+ ATTR_PRIM_TRIANGLE,
&attr_float_size,
&attr_float3_size,
&attr_uchar4_size);
update_attribute_element_size(mesh,
curve_mattr,
+ ATTR_PRIM_CURVE,
+ &attr_float_size,
+ &attr_float3_size,
+ &attr_uchar4_size);
+ update_attribute_element_size(mesh,
+ subd_mattr,
+ ATTR_PRIM_SUBD,
&attr_float_size,
&attr_float3_size,
&attr_uchar4_size);
@@ -1048,24 +1258,34 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
foreach(AttributeRequest& req, attributes.requests) {
Attribute *triangle_mattr = mesh->attributes.find(req);
Attribute *curve_mattr = mesh->curve_attributes.find(req);
+ Attribute *subd_mattr = mesh->subd_attributes.find(req);
update_attribute_element_offset(mesh,
attr_float, attr_float_offset,
attr_float3, attr_float3_offset,
attr_uchar4, attr_uchar4_offset,
triangle_mattr,
+ ATTR_PRIM_TRIANGLE,
req.triangle_type,
- req.triangle_offset,
- req.triangle_element);
+ req.triangle_desc);
update_attribute_element_offset(mesh,
attr_float, attr_float_offset,
attr_float3, attr_float3_offset,
attr_uchar4, attr_uchar4_offset,
curve_mattr,
+ ATTR_PRIM_CURVE,
req.curve_type,
- req.curve_offset,
- req.curve_element);
+ req.curve_desc);
+
+ update_attribute_element_offset(mesh,
+ attr_float, attr_float_offset,
+ attr_float3, attr_float3_offset,
+ attr_uchar4, attr_uchar4_offset,
+ subd_mattr,
+ ATTR_PRIM_SUBD,
+ req.subd_type,
+ req.subd_desc);
if(progress.get_cancel()) return;
}
@@ -1100,19 +1320,43 @@ void MeshManager::mesh_calc_offset(Scene *scene)
{
size_t vert_size = 0;
size_t tri_size = 0;
+
size_t curve_key_size = 0;
size_t curve_size = 0;
+ size_t patch_size = 0;
+ size_t face_size = 0;
+ size_t corner_size = 0;
+
foreach(Mesh *mesh, scene->meshes) {
mesh->vert_offset = vert_size;
mesh->tri_offset = tri_size;
+
mesh->curvekey_offset = curve_key_size;
mesh->curve_offset = curve_size;
+ mesh->patch_offset = patch_size;
+ mesh->face_offset = face_size;
+ mesh->corner_offset = corner_size;
+
vert_size += mesh->verts.size();
tri_size += mesh->num_triangles();
+
curve_key_size += mesh->curve_keys.size();
curve_size += mesh->num_curves();
+
+ if(mesh->subd_faces.size()) {
+ Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1];
+ patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8;
+
+ /* patch tables are stored in same array so include them in patch_size */
+ if(mesh->patch_table) {
+ mesh->patch_table_offset = patch_size;
+ patch_size += mesh->patch_table->total_size();
+ }
+ }
+ face_size += mesh->subd_faces.size();
+ corner_size += mesh->subd_face_corners.size();
}
}
@@ -1125,14 +1369,31 @@ void MeshManager::device_update_mesh(Device *device,
/* Count. */
size_t vert_size = 0;
size_t tri_size = 0;
+
size_t curve_key_size = 0;
size_t curve_size = 0;
+
+ size_t patch_size = 0;
+
foreach(Mesh *mesh, scene->meshes) {
vert_size += mesh->verts.size();
tri_size += mesh->num_triangles();
+
curve_key_size += mesh->curve_keys.size();
curve_size += mesh->num_curves();
+
+ if(mesh->subd_faces.size()) {
+ Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1];
+ patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8;
+
+ /* patch tables are stored in same array so include them in patch_size */
+ if(mesh->patch_table) {
+ mesh->patch_table_offset = patch_size;
+ patch_size += mesh->patch_table->total_size();
+ }
+ }
}
+
/* Create mapping from triangle to primitive triangle array. */
vector<uint> tri_prim_index(tri_size);
if(for_displacement) {
@@ -1155,6 +1416,7 @@ void MeshManager::device_update_mesh(Device *device,
}
}
}
+
/* Fill in all the arrays. */
if(tri_size != 0) {
/* normals */
@@ -1163,6 +1425,8 @@ void MeshManager::device_update_mesh(Device *device,
uint *tri_shader = dscene->tri_shader.resize(tri_size);
float4 *vnormal = dscene->tri_vnormal.resize(vert_size);
uint4 *tri_vindex = dscene->tri_vindex.resize(tri_size);
+ uint *tri_patch = dscene->tri_patch.resize(tri_size);
+ float2 *tri_patch_uv = dscene->tri_patch_uv.resize(vert_size);
foreach(Mesh *mesh, scene->meshes) {
mesh->pack_normals(scene,
@@ -1170,6 +1434,8 @@ void MeshManager::device_update_mesh(Device *device,
&vnormal[mesh->vert_offset]);
mesh->pack_verts(tri_prim_index,
&tri_vindex[mesh->tri_offset],
+ &tri_patch[mesh->tri_offset],
+ &tri_patch_uv[mesh->vert_offset],
mesh->vert_offset,
mesh->tri_offset);
if(progress.get_cancel()) return;
@@ -1181,7 +1447,10 @@ void MeshManager::device_update_mesh(Device *device,
device->tex_alloc("__tri_shader", dscene->tri_shader);
device->tex_alloc("__tri_vnormal", dscene->tri_vnormal);
device->tex_alloc("__tri_vindex", dscene->tri_vindex);
+ device->tex_alloc("__tri_patch", dscene->tri_patch);
+ device->tex_alloc("__tri_patch_uv", dscene->tri_patch_uv);
}
+
if(curve_size != 0) {
progress.set_status("Updating Mesh", "Copying Strands to device");
@@ -1196,6 +1465,25 @@ void MeshManager::device_update_mesh(Device *device,
device->tex_alloc("__curve_keys", dscene->curve_keys);
device->tex_alloc("__curves", dscene->curves);
}
+
+ if(patch_size != 0) {
+ progress.set_status("Updating Mesh", "Copying Patches to device");
+
+ uint *patch_data = dscene->patches.resize(patch_size);
+
+ foreach(Mesh *mesh, scene->meshes) {
+ mesh->pack_patches(&patch_data[mesh->patch_offset], mesh->vert_offset, mesh->face_offset, mesh->corner_offset);
+
+ if(mesh->patch_table) {
+ mesh->patch_table->copy_adjusting_offsets(&patch_data[mesh->patch_table_offset], mesh->patch_table_offset);
+ }
+
+ if(progress.get_cancel()) return;
+ }
+
+ device->tex_alloc("__patches", dscene->patches);
+ }
+
if(for_displacement) {
float4 *prim_tri_verts = dscene->prim_tri_verts.resize(tri_size * 3);
foreach(Mesh *mesh, scene->meshes) {
@@ -1376,7 +1664,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
bool old_need_object_flags_update = false;
foreach(Mesh *mesh, scene->meshes) {
if(mesh->need_update &&
- mesh->displacement_method != Mesh::DISPLACE_BUMP)
+ mesh->has_true_displacement())
{
true_displacement_used = true;
break;
@@ -1402,6 +1690,10 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
}
if(progress.get_cancel()) return;
+ /* after mesh data has been copied to device memory we need to update
+ * offsets for patch tables as this can't be known before hand */
+ scene->object_manager->device_update_patch_map_offsets(device, dscene, scene);
+
device_update_attributes(device, dscene, scene, progress);
if(progress.get_cancel()) return;
@@ -1433,7 +1725,9 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
num_bvh++;
}
}
+
TaskPool pool;
+
foreach(Mesh *mesh, scene->meshes) {
if(mesh->need_update) {
pool.push(function_bind(&Mesh::compute_bvh,
@@ -1448,6 +1742,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
}
}
}
+
TaskPool::Summary summary;
pool.wait_work(&summary);
VLOG(2) << "Objects BVH build pool statistics:\n"
@@ -1505,8 +1800,11 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
device->tex_free(dscene->tri_shader);
device->tex_free(dscene->tri_vnormal);
device->tex_free(dscene->tri_vindex);
+ device->tex_free(dscene->tri_patch);
+ device->tex_free(dscene->tri_patch_uv);
device->tex_free(dscene->curves);
device->tex_free(dscene->curve_keys);
+ device->tex_free(dscene->patches);
device->tex_free(dscene->attributes_map);
device->tex_free(dscene->attributes_float);
device->tex_free(dscene->attributes_float3);
@@ -1523,8 +1821,11 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
dscene->tri_shader.clear();
dscene->tri_vnormal.clear();
dscene->tri_vindex.clear();
+ dscene->tri_patch.clear();
+ dscene->tri_patch_uv.clear();
dscene->curves.clear();
dscene->curve_keys.clear();
+ dscene->patches.clear();
dscene->attributes_map.clear();
dscene->attributes_float.clear();
dscene->attributes_float3.clear();
@@ -1574,77 +1875,5 @@ bool Mesh::need_attribute(Scene * /*scene*/, ustring name)
return false;
}
-void Mesh::tessellate(DiagSplit *split)
-{
- int num_faces = num_triangles();
-
- add_face_normals();
- add_vertex_normals();
-
- Attribute *attr_fN = attributes.find(ATTR_STD_FACE_NORMAL);
- float3 *fN = attr_fN->data_float3();
-
- Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
- float3 *vN = attr_vN->data_float3();
-
- for(int f = 0; f < num_faces; f++) {
- if(!forms_quad[f]) {
- /* triangle */
- LinearTrianglePatch patch;
- Triangle triangle = get_triangle(f);
- float3 *hull = patch.hull;
- float3 *normals = patch.normals;
-
- for(int i = 0; i < 3; i++) {
- hull[i] = verts[triangle.v[i]];
- }
-
- if(smooth[f]) {
- for(int i = 0; i < 3; i++) {
- normals[i] = vN[triangle.v[i]];
- }
- }
- else {
- for(int i = 0; i < 3; i++) {
- normals[i] = fN[f];
- }
- }
-
- split->split_triangle(&patch);
- }
- else {
- /* quad */
- LinearQuadPatch patch;
- Triangle triangle0 = get_triangle(f);
- Triangle triangle1 = get_triangle(f+1);
- float3 *hull = patch.hull;
- float3 *normals = patch.normals;
-
- hull[0] = verts[triangle0.v[0]];
- hull[1] = verts[triangle0.v[1]];
- hull[3] = verts[triangle0.v[2]];
- hull[2] = verts[triangle1.v[2]];
-
- if(smooth[f]) {
- normals[0] = vN[triangle0.v[0]];
- normals[1] = vN[triangle0.v[1]];
- normals[3] = vN[triangle0.v[2]];
- normals[2] = vN[triangle1.v[2]];
- }
- else {
- for(int i = 0; i < 4; i++) {
- normals[i] = fN[f];
- }
- }
-
- split->split_quad(&patch);
-
- // consume second triangle in quad
- f++;
- }
-
- }
-}
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 0aea55544f2..eff5c50e635 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -40,6 +40,7 @@ class Scene;
class SceneParams;
class AttributeRequest;
class DiagSplit;
+struct PackedPatchTable;
/* Mesh */
@@ -97,15 +98,32 @@ public:
return curve_first_key.size();
}
- /* Displacement */
- enum DisplacementMethod {
- DISPLACE_BUMP = 0,
- DISPLACE_TRUE = 1,
- DISPLACE_BOTH = 2,
+ /* Mesh SubdFace */
+ struct SubdFace {
+ int start_corner;
+ int num_corners;
+ int shader;
+ bool smooth;
+ int ptex_offset;
+
+ bool is_quad() { return num_corners == 4; }
+ float3 normal(const Mesh *mesh) const;
+ int num_ptex_faces() const { return num_corners == 4 ? 1 : num_corners; }
+ };
+
+ struct SubdEdgeCrease {
+ int v[2];
+ float crease;
+ };
- DISPLACE_NUM_METHODS,
+ enum SubdivisionType {
+ SUBDIVISION_NONE,
+ SUBDIVISION_LINEAR,
+ SUBDIVISION_CATMULL_CLARK,
};
+ SubdivisionType subdivision_type;
+
/* Mesh Data */
enum GeometryFlags {
GEOMETRY_NONE = 0,
@@ -119,7 +137,10 @@ public:
array<float3> verts;
array<int> shader;
array<bool> smooth;
- array<bool> forms_quad; /* used to tell if triangle is part of a quad patch */
+
+ /* used for storing patch info for subd triangles, only allocated if there are patches */
+ array<int> triangle_patch; /* must be < 0 for non subd triangles */
+ array<float2> vert_patch_uv;
bool has_volume; /* Set in the device_update_flags(). */
bool has_surface_bssrdf; /* Set in the device_update_flags(). */
@@ -129,15 +150,23 @@ public:
array<int> curve_first_key;
array<int> curve_shader;
+ array<SubdFace> subd_faces;
+ array<int> subd_face_corners;
+ int num_ngons;
+
+ array<SubdEdgeCrease> subd_creases;
+
vector<Shader*> used_shaders;
AttributeSet attributes;
AttributeSet curve_attributes;
+ AttributeSet subd_attributes;
BoundBox bounds;
bool transform_applied;
bool transform_negative_scaled;
Transform transform_normal;
- DisplacementMethod displacement_method;
+
+ PackedPatchTable *patch_table;
uint motion_steps;
bool use_motion_blur;
@@ -154,6 +183,13 @@ public:
size_t curve_offset;
size_t curvekey_offset;
+ size_t patch_offset;
+ size_t patch_table_offset;
+ size_t face_offset;
+ size_t corner_offset;
+
+ size_t num_subd_verts;
+
/* Functions */
Mesh();
~Mesh();
@@ -162,12 +198,15 @@ public:
void reserve_mesh(int numverts, int numfaces);
void resize_curves(int numcurves, int numkeys);
void reserve_curves(int numcurves, int numkeys);
+ void resize_subd_faces(int numfaces, int num_ngons, int numcorners);
+ void reserve_subd_faces(int numfaces, int num_ngons, int numcorners);
void clear();
void add_vertex(float3 P);
void add_vertex_slow(float3 P);
- void add_triangle(int v0, int v1, int v2, int shader, bool smooth, bool forms_quad = false);
+ void add_triangle(int v0, int v1, int v2, int shader, bool smooth);
void add_curve_key(float3 loc, float radius);
void add_curve(int first_key, int shader);
+ void add_subd_face(int* corners, int num_corners, int shader_, bool smooth_);
int split_vertex(int vertex);
void compute_bounds();
@@ -177,9 +216,13 @@ public:
void pack_normals(Scene *scene, uint *shader, float4 *vnormal);
void pack_verts(const vector<uint>& tri_prim_index,
uint4 *tri_vindex,
+ uint *tri_patch,
+ float2 *tri_patch_uv,
size_t vert_offset,
size_t tri_offset);
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
+ void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
+
void compute_bvh(DeviceScene *dscene,
SceneParams *params,
Progress *progress,
@@ -192,6 +235,7 @@ public:
void tag_update(Scene *scene, bool rebuild);
bool has_motion_blur() const;
+ bool has_true_displacement() const;
/* Check whether the mesh should have own BVH built separately. Briefly,
* own BVH is needed for mesh, if:
diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/render/mesh_displace.cpp
index 95f46ff02a2..ef9cfedd412 100644
--- a/intern/cycles/render/mesh_displace.cpp
+++ b/intern/cycles/render/mesh_displace.cpp
@@ -26,19 +26,27 @@
CCL_NAMESPACE_BEGIN
+static float3 compute_face_normal(const Mesh::Triangle& t, float3 *verts)
+{
+ float3 v0 = verts[t.v[0]];
+ float3 v1 = verts[t.v[1]];
+ float3 v2 = verts[t.v[2]];
+
+ float3 norm = cross(v1 - v0, v2 - v0);
+ float normlen = len(norm);
+
+ if(normlen == 0.0f)
+ return make_float3(1.0f, 0.0f, 0.0f);
+
+ return norm / normlen;
+}
+
bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress& progress)
{
/* verify if we have a displacement shader */
- bool has_displacement = false;
-
- if(mesh->displacement_method != Mesh::DISPLACE_BUMP) {
- foreach(Shader *shader, mesh->used_shaders)
- if(shader->has_displacement)
- has_displacement = true;
- }
-
- if(!has_displacement)
+ if(!mesh->has_true_displacement()) {
return false;
+ }
string msg = string_printf("Computing Displacement %s", mesh->name.c_str());
progress.set_status("Updating Mesh", msg);
@@ -67,8 +75,9 @@ bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Me
Shader *shader = (shader_index < mesh->used_shaders.size()) ?
mesh->used_shaders[shader_index] : scene->default_surface;
- if(!shader->has_displacement)
+ if(!shader->has_displacement || shader->displacement_method == DISPLACE_BUMP) {
continue;
+ }
for(int j = 0; j < 3; j++) {
if(done[t.v[j]])
@@ -153,8 +162,9 @@ bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Me
Shader *shader = (shader_index < mesh->used_shaders.size()) ?
mesh->used_shaders[shader_index] : scene->default_surface;
- if(!shader->has_displacement)
+ if(!shader->has_displacement || shader->displacement_method == DISPLACE_BUMP) {
continue;
+ }
for(int j = 0; j < 3; j++) {
if(!done[t.v[j]]) {
@@ -178,9 +188,131 @@ bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Me
mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
mesh->add_face_normals();
- if(mesh->displacement_method == Mesh::DISPLACE_TRUE) {
- mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
- mesh->add_vertex_normals();
+ bool need_recompute_vertex_normals = false;
+
+ foreach(Shader *shader, mesh->used_shaders) {
+ if(shader->has_displacement && shader->displacement_method == DISPLACE_TRUE) {
+ need_recompute_vertex_normals = true;
+ break;
+ }
+ }
+
+ if(need_recompute_vertex_normals) {
+ bool flip = mesh->transform_negative_scaled;
+ vector<bool> tri_has_true_disp(num_triangles, false);
+
+ for(size_t i = 0; i < num_triangles; i++) {
+ int shader_index = mesh->shader[i];
+ Shader *shader = (shader_index < mesh->used_shaders.size()) ?
+ mesh->used_shaders[shader_index] : scene->default_surface;
+
+ tri_has_true_disp[i] = shader->has_displacement && shader->displacement_method == DISPLACE_TRUE;
+ }
+
+ /* static vertex normals */
+
+ /* get attributes */
+ Attribute *attr_fN = mesh->attributes.find(ATTR_STD_FACE_NORMAL);
+ Attribute *attr_vN = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
+
+ float3 *fN = attr_fN->data_float3();
+ float3 *vN = attr_vN->data_float3();
+
+ /* compute vertex normals */
+
+ /* zero vertex normals on triangles with true displacement */
+ for(size_t i = 0; i < num_triangles; i++) {
+ if(tri_has_true_disp[i]) {
+ for(size_t j = 0; j < 3; j++) {
+ vN[mesh->get_triangle(i).v[j]] = make_float3(0.0f, 0.0f, 0.0f);
+ }
+ }
+ }
+
+ /* add face normals to vertex normals */
+ for(size_t i = 0; i < num_triangles; i++) {
+ if(tri_has_true_disp[i]) {
+ for(size_t j = 0; j < 3; j++) {
+ vN[mesh->get_triangle(i).v[j]] += fN[i];
+ }
+ }
+ }
+
+ /* normalize vertex normals */
+ done.clear();
+ done.resize(num_verts, false);
+
+ for(size_t i = 0; i < num_triangles; i++) {
+ if(tri_has_true_disp[i]) {
+ for(size_t j = 0; j < 3; j++) {
+ int vert = mesh->get_triangle(i).v[j];
+
+ if(done[vert]) {
+ continue;
+ }
+
+ vN[vert] = normalize(vN[vert]);
+ if(flip)
+ vN[vert] = -vN[vert];
+
+ done[vert] = true;
+ }
+ }
+ }
+
+ /* motion vertex normals */
+ Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+ Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
+
+ if(mesh->has_motion_blur() && attr_mP && attr_mN) {
+ for(int step = 0; step < mesh->motion_steps - 1; step++) {
+ float3 *mP = attr_mP->data_float3() + step*mesh->verts.size();
+ float3 *mN = attr_mN->data_float3() + step*mesh->verts.size();
+
+ /* compute */
+
+ /* zero vertex normals on triangles with true displacement */
+ for(size_t i = 0; i < num_triangles; i++) {
+ if(tri_has_true_disp[i]) {
+ for(size_t j = 0; j < 3; j++) {
+ mN[mesh->get_triangle(i).v[j]] = make_float3(0.0f, 0.0f, 0.0f);
+ }
+ }
+ }
+
+ /* add face normals to vertex normals */
+ for(size_t i = 0; i < num_triangles; i++) {
+ if(tri_has_true_disp[i]) {
+ for(size_t j = 0; j < 3; j++) {
+ float3 fN = compute_face_normal(mesh->get_triangle(i), mP);
+ mN[mesh->get_triangle(i).v[j]] += fN;
+ }
+ }
+ }
+
+ /* normalize vertex normals */
+ done.clear();
+ done.resize(num_verts, false);
+
+ for(size_t i = 0; i < num_triangles; i++) {
+ if(tri_has_true_disp[i]) {
+ for(size_t j = 0; j < 3; j++) {
+ int vert = mesh->get_triangle(i).v[j];
+
+ if(done[vert]) {
+ continue;
+ }
+
+ mN[vert] = normalize(mN[vert]);
+ if(flip)
+ mN[vert] = -mN[vert];
+
+ done[vert] = true;
+ }
+ }
+ }
+ }
+ }
}
return true;
diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/render/mesh_subdivision.cpp
new file mode 100644
index 00000000000..efb40efbb79
--- /dev/null
+++ b/intern/cycles/render/mesh_subdivision.cpp
@@ -0,0 +1,567 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mesh.h"
+#include "attribute.h"
+
+#include "subd_split.h"
+#include "subd_patch.h"
+#include "subd_patch_table.h"
+
+#include "util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef WITH_OPENSUBDIV
+
+CCL_NAMESPACE_END
+
+#include <opensubdiv/far/topologyRefinerFactory.h>
+#include <opensubdiv/far/primvarRefiner.h>
+#include <opensubdiv/far/patchTableFactory.h>
+#include <opensubdiv/far/patchMap.h>
+
+/* specializations of TopologyRefinerFactory for ccl::Mesh */
+
+namespace OpenSubdiv {
+namespace OPENSUBDIV_VERSION {
+namespace Far {
+ template<>
+ bool TopologyRefinerFactory<ccl::Mesh>::resizeComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh)
+ {
+ setNumBaseVertices(refiner, mesh.verts.size());
+ setNumBaseFaces(refiner, mesh.subd_faces.size());
+
+ ccl::Mesh::SubdFace* face = &mesh.subd_faces[0];
+
+ for(int i = 0; i < mesh.subd_faces.size(); i++, face++) {
+ setNumBaseFaceVertices(refiner, i, face->num_corners);
+ }
+
+ return true;
+ }
+
+ template<>
+ bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh)
+ {
+ ccl::Mesh::SubdFace* face = &mesh.subd_faces[0];
+
+ for(int i = 0; i < mesh.subd_faces.size(); i++, face++) {
+ IndexArray face_verts = getBaseFaceVertices(refiner, i);
+
+ int* corner = &mesh.subd_face_corners[face->start_corner];
+
+ for(int j = 0; j < face->num_corners; j++, corner++) {
+ face_verts[j] = *corner;
+ }
+ }
+
+ return true;
+ }
+
+ template<>
+ bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner& refiner, ccl::Mesh const& mesh)
+ {
+ const ccl::Mesh::SubdEdgeCrease* crease = mesh.subd_creases.data();
+
+ for(int i = 0; i < mesh.subd_creases.size(); i++, crease++) {
+ Index edge = findBaseEdge(refiner, crease->v[0], crease->v[1]);
+
+ if(edge != INDEX_INVALID) {
+ setBaseEdgeSharpness(refiner, edge, crease->crease * 10.0f);
+ }
+ }
+
+ for(int i = 0; i < mesh.verts.size(); i++) {
+ ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
+
+ if(vert_edges.size() == 2) {
+ float sharpness = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
+ sharpness = std::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1]));
+
+ setBaseVertexSharpness(refiner, i, sharpness);
+ }
+ }
+
+ return true;
+ }
+
+ template<>
+ bool TopologyRefinerFactory<ccl::Mesh>::assignFaceVaryingTopology(TopologyRefiner& /*refiner*/, ccl::Mesh const& /*mesh*/)
+ {
+ return true;
+ }
+
+ template<>
+ void TopologyRefinerFactory<ccl::Mesh>::reportInvalidTopology(TopologyError /*err_code*/,
+ char const */*msg*/, ccl::Mesh const& /*mesh*/)
+ {
+ }
+} /* namespace Far */
+} /* namespace OPENSUBDIV_VERSION */
+} /* namespace OpenSubdiv */
+
+CCL_NAMESPACE_BEGIN
+
+using namespace OpenSubdiv;
+
+/* struct that implements OpenSubdiv's vertex interface */
+
+template<typename T>
+struct OsdValue {
+ T value;
+
+ OsdValue() {}
+
+ void Clear(void* = 0) {
+ memset(&value, 0, sizeof(T));
+ }
+
+ void AddWithWeight(OsdValue<T> const& src, float weight) {
+ value += src.value * weight;
+ }
+};
+
+template<>
+void OsdValue<uchar4>::AddWithWeight(OsdValue<uchar4> const& src, float weight)
+{
+ for(int i = 0; i < 4; i++) {
+ value[i] += (uchar)(src.value[i] * weight);
+ }
+}
+
+/* class for holding OpenSubdiv data used during tessellation */
+
+class OsdData {
+ Mesh* mesh;
+ vector<OsdValue<float3> > verts;
+ Far::TopologyRefiner* refiner;
+ Far::PatchTable* patch_table;
+ Far::PatchMap* patch_map;
+
+public:
+ OsdData() : mesh(NULL), refiner(NULL), patch_table(NULL), patch_map(NULL) {}
+
+ ~OsdData()
+ {
+ delete refiner;
+ delete patch_table;
+ delete patch_map;
+ }
+
+ void build_from_mesh(Mesh* mesh_)
+ {
+ mesh = mesh_;
+
+ /* type and options */
+ Sdc::SchemeType type = Sdc::SCHEME_CATMARK;
+
+ Sdc::Options options;
+ options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
+
+ /* create refiner */
+ refiner = Far::TopologyRefinerFactory<Mesh>::Create(*mesh,
+ Far::TopologyRefinerFactory<Mesh>::Options(type, options));
+
+ /* adaptive refinement */
+ int max_isolation = 10;
+ refiner->RefineAdaptive(Far::TopologyRefiner::AdaptiveOptions(max_isolation));
+
+ /* create patch table */
+ Far::PatchTableFactory::Options patch_options;
+ patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
+
+ patch_table = Far::PatchTableFactory::Create(*refiner, patch_options);
+
+ /* interpolate verts */
+ int num_refiner_verts = refiner->GetNumVerticesTotal();
+ int num_local_points = patch_table->GetNumLocalPoints();
+
+ verts.resize(num_refiner_verts + num_local_points);
+ for(int i = 0; i < mesh->verts.size(); i++) {
+ verts[i].value = mesh->verts[i];
+ }
+
+ OsdValue<float3>* src = &verts[0];
+ for(int i = 0; i < refiner->GetMaxLevel(); i++) {
+ OsdValue<float3>* dest = src + refiner->GetLevel(i).GetNumVertices();
+ Far::PrimvarRefiner(*refiner).Interpolate(i+1, src, dest);
+ src = dest;
+ }
+
+ patch_table->ComputeLocalPointValues(&verts[0], &verts[num_refiner_verts]);
+
+ /* create patch map */
+ patch_map = new Far::PatchMap(*patch_table);
+ }
+
+ void subdivide_attribute(Attribute& attr)
+ {
+ Far::PrimvarRefiner primvar_refiner(*refiner);
+
+ if(attr.element == ATTR_ELEMENT_VERTEX) {
+ int num_refiner_verts = refiner->GetNumVerticesTotal();
+ int num_local_points = patch_table->GetNumLocalPoints();
+
+ attr.resize(num_refiner_verts + num_local_points);
+ attr.flags |= ATTR_FINAL_SIZE;
+
+ char* src = &attr.buffer[0];
+
+ for(int i = 0; i < refiner->GetMaxLevel(); i++) {
+ char* dest = src + refiner->GetLevel(i).GetNumVertices() * attr.data_sizeof();
+
+ if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
+ primvar_refiner.Interpolate(i+1, (OsdValue<float>*)src, (OsdValue<float>*&)dest);
+ }
+ else {
+ primvar_refiner.Interpolate(i+1, (OsdValue<float4>*)src, (OsdValue<float4>*&)dest);
+ }
+
+ src = dest;
+ }
+
+ if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) {
+ patch_table->ComputeLocalPointValues((OsdValue<float>*)&attr.buffer[0],
+ (OsdValue<float>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+ }
+ else {
+ patch_table->ComputeLocalPointValues((OsdValue<float4>*)&attr.buffer[0],
+ (OsdValue<float4>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
+ }
+ }
+ else if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
+ // TODO(mai): fvar interpolation
+ }
+ }
+
+ friend struct OsdPatch;
+ friend class Mesh;
+};
+
+/* ccl::Patch implementation that uses OpenSubdiv for eval */
+
+struct OsdPatch : Patch {
+ OsdData* osd_data;
+
+ OsdPatch(OsdData* data) : osd_data(data) {}
+
+ void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
+ {
+ const Far::PatchTable::PatchHandle* handle = osd_data->patch_map->FindPatch(patch_index, u, v);
+ assert(handle);
+
+ float p_weights[20], du_weights[20], dv_weights[20];
+ osd_data->patch_table->EvaluateBasis(*handle, u, v, p_weights, du_weights, dv_weights);
+
+ Far::ConstIndexArray cv = osd_data->patch_table->GetPatchVertices(*handle);
+
+ float3 du, dv;
+ if(P) *P = make_float3(0.0f, 0.0f, 0.0f);
+ du = make_float3(0.0f, 0.0f, 0.0f);
+ dv = make_float3(0.0f, 0.0f, 0.0f);
+
+ for(int i = 0; i < cv.size(); i++) {
+ float3 p = osd_data->verts[cv[i]].value;
+
+ if(P) *P += p * p_weights[i];
+ du += p * du_weights[i];
+ dv += p * dv_weights[i];
+ }
+
+ if(dPdu) *dPdu = du;
+ if(dPdv) *dPdv = dv;
+ if(N) *N = normalize(cross(du, dv));
+ }
+
+ BoundBox bound() { return BoundBox::empty; }
+};
+
+#endif
+
+void Mesh::tessellate(DiagSplit *split)
+{
+#ifdef WITH_OPENSUBDIV
+ OsdData osd_data;
+ bool need_packed_patch_table = false;
+
+ if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+ osd_data.build_from_mesh(this);
+ }
+ else
+#endif
+ {
+ /* force linear subdivision if OpenSubdiv is unavailable to avoid
+ * falling into catmull-clark code paths by accident
+ */
+ subdivision_type = SUBDIVISION_LINEAR;
+
+ /* force disable attribute subdivision for same reason as above */
+ foreach(Attribute& attr, subd_attributes.attributes) {
+ attr.flags &= ~ATTR_SUBDIVIDED;
+ }
+ }
+
+ int num_faces = subd_faces.size();
+
+ Attribute *attr_vN = subd_attributes.find(ATTR_STD_VERTEX_NORMAL);
+ float3* vN = attr_vN->data_float3();
+
+ for(int f = 0; f < num_faces; f++) {
+ SubdFace& face = subd_faces[f];
+
+ if(face.is_quad()) {
+ /* quad */
+ QuadDice::SubPatch subpatch;
+
+ LinearQuadPatch quad_patch;
+#ifdef WITH_OPENSUBDIV
+ OsdPatch osd_patch(&osd_data);
+
+ if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+ osd_patch.patch_index = face.ptex_offset;
+
+ subpatch.patch = &osd_patch;
+ }
+ else
+#endif
+ {
+ float3 *hull = quad_patch.hull;
+ float3 *normals = quad_patch.normals;
+
+ quad_patch.patch_index = face.ptex_offset;
+
+ for(int i = 0; i < 4; i++) {
+ hull[i] = verts[subd_face_corners[face.start_corner+i]];
+ }
+
+ if(face.smooth) {
+ for(int i = 0; i < 4; i++) {
+ normals[i] = vN[subd_face_corners[face.start_corner+i]];
+ }
+ }
+ else {
+ float3 N = face.normal(this);
+ for(int i = 0; i < 4; i++) {
+ normals[i] = N;
+ }
+ }
+
+ swap(hull[2], hull[3]);
+ swap(normals[2], normals[3]);
+
+ subpatch.patch = &quad_patch;
+ }
+
+ subpatch.patch->shader = face.shader;
+
+ /* Quad faces need to be split at least once to line up with split ngons, we do this
+ * here in this manner because if we do it later edge factors may end up slightly off.
+ */
+ subpatch.P00 = make_float2(0.0f, 0.0f);
+ subpatch.P10 = make_float2(0.5f, 0.0f);
+ subpatch.P01 = make_float2(0.0f, 0.5f);
+ subpatch.P11 = make_float2(0.5f, 0.5f);
+ split->split_quad(subpatch.patch, &subpatch);
+
+ subpatch.P00 = make_float2(0.5f, 0.0f);
+ subpatch.P10 = make_float2(1.0f, 0.0f);
+ subpatch.P01 = make_float2(0.5f, 0.5f);
+ subpatch.P11 = make_float2(1.0f, 0.5f);
+ split->split_quad(subpatch.patch, &subpatch);
+
+ subpatch.P00 = make_float2(0.0f, 0.5f);
+ subpatch.P10 = make_float2(0.5f, 0.5f);
+ subpatch.P01 = make_float2(0.0f, 1.0f);
+ subpatch.P11 = make_float2(0.5f, 1.0f);
+ split->split_quad(subpatch.patch, &subpatch);
+
+ subpatch.P00 = make_float2(0.5f, 0.5f);
+ subpatch.P10 = make_float2(1.0f, 0.5f);
+ subpatch.P01 = make_float2(0.5f, 1.0f);
+ subpatch.P11 = make_float2(1.0f, 1.0f);
+ split->split_quad(subpatch.patch, &subpatch);
+ }
+ else {
+ /* ngon */
+#ifdef WITH_OPENSUBDIV
+ if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+ OsdPatch patch(&osd_data);
+
+ patch.shader = face.shader;
+
+ for(int corner = 0; corner < face.num_corners; corner++) {
+ patch.patch_index = face.ptex_offset + corner;
+
+ split->split_quad(&patch);
+ }
+ }
+ else
+#endif
+ {
+ float3 center_vert = make_float3(0.0f, 0.0f, 0.0f);
+ float3 center_normal = make_float3(0.0f, 0.0f, 0.0f);
+
+ float inv_num_corners = 1.0f/float(face.num_corners);
+ for(int corner = 0; corner < face.num_corners; corner++) {
+ center_vert += verts[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
+ center_normal += vN[subd_face_corners[face.start_corner + corner]] * inv_num_corners;
+ }
+
+ for(int corner = 0; corner < face.num_corners; corner++) {
+ LinearQuadPatch patch;
+ float3 *hull = patch.hull;
+ float3 *normals = patch.normals;
+
+ patch.patch_index = face.ptex_offset + corner;
+
+ patch.shader = face.shader;
+
+ hull[0] = verts[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
+ hull[1] = verts[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
+ hull[2] = verts[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
+ hull[3] = center_vert;
+
+ hull[1] = (hull[1] + hull[0]) * 0.5;
+ hull[2] = (hull[2] + hull[0]) * 0.5;
+
+ if(face.smooth) {
+ normals[0] = vN[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]];
+ normals[1] = vN[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]];
+ normals[2] = vN[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]];
+ normals[3] = center_normal;
+
+ normals[1] = (normals[1] + normals[0]) * 0.5;
+ normals[2] = (normals[2] + normals[0]) * 0.5;
+ }
+ else {
+ float3 N = face.normal(this);
+ for(int i = 0; i < 4; i++) {
+ normals[i] = N;
+ }
+ }
+
+ split->split_quad(&patch);
+ }
+ }
+ }
+ }
+
+ /* interpolate center points for attributes */
+ foreach(Attribute& attr, subd_attributes.attributes) {
+#ifdef WITH_OPENSUBDIV
+ if(subdivision_type == SUBDIVISION_CATMULL_CLARK && attr.flags & ATTR_SUBDIVIDED) {
+ if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
+ /* keep subdivision for corner attributes disabled for now */
+ attr.flags &= ~ATTR_SUBDIVIDED;
+ }
+ else {
+ osd_data.subdivide_attribute(attr);
+
+ need_packed_patch_table = true;
+ continue;
+ }
+ }
+#endif
+
+ char* data = attr.data();
+ size_t stride = attr.data_sizeof();
+ int ngons = 0;
+
+ switch(attr.element) {
+ case ATTR_ELEMENT_VERTEX: {
+ for(int f = 0; f < num_faces; f++) {
+ SubdFace& face = subd_faces[f];
+
+ if(!face.is_quad()) {
+ char* center = data + (verts.size() - num_subd_verts + ngons) * stride;
+ attr.zero_data(center);
+
+ float inv_num_corners = 1.0f / float(face.num_corners);
+
+ for(int corner = 0; corner < face.num_corners; corner++) {
+ attr.add_with_weight(center,
+ data + subd_face_corners[face.start_corner + corner] * stride,
+ inv_num_corners);
+ }
+
+ ngons++;
+ }
+ }
+ } break;
+ case ATTR_ELEMENT_VERTEX_MOTION: {
+ // TODO(mai): implement
+ } break;
+ case ATTR_ELEMENT_CORNER: {
+ for(int f = 0; f < num_faces; f++) {
+ SubdFace& face = subd_faces[f];
+
+ if(!face.is_quad()) {
+ char* center = data + (subd_face_corners.size() + ngons) * stride;
+ attr.zero_data(center);
+
+ float inv_num_corners = 1.0f / float(face.num_corners);
+
+ for(int corner = 0; corner < face.num_corners; corner++) {
+ attr.add_with_weight(center,
+ data + (face.start_corner + corner) * stride,
+ inv_num_corners);
+ }
+
+ ngons++;
+ }
+ }
+ } break;
+ case ATTR_ELEMENT_CORNER_BYTE: {
+ for(int f = 0; f < num_faces; f++) {
+ SubdFace& face = subd_faces[f];
+
+ if(!face.is_quad()) {
+ uchar* center = (uchar*)data + (subd_face_corners.size() + ngons) * stride;
+
+ float inv_num_corners = 1.0f / float(face.num_corners);
+ float4 val = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+
+ for(int corner = 0; corner < face.num_corners; corner++) {
+ for(int i = 0; i < 4; i++) {
+ val[i] += float(*(data + (face.start_corner + corner) * stride + i)) * inv_num_corners;
+ }
+ }
+
+ for(int i = 0; i < 4; i++) {
+ center[i] = uchar(min(max(val[i], 0.0f), 255.0f));
+ }
+
+ ngons++;
+ }
+ }
+ } break;
+ default: break;
+ }
+ }
+
+#ifdef WITH_OPENSUBDIV
+ /* pack patch tables */
+ if(need_packed_patch_table) {
+ delete patch_table;
+ patch_table = new PackedPatchTable;
+ patch_table->pack(osd_data.patch_table);
+ }
+#endif
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index e26084c690b..4f54b86fe4a 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -20,6 +20,7 @@
#include "scene.h"
#include "svm.h"
#include "svm_color_util.h"
+#include "svm_ramp_util.h"
#include "svm_math_util.h"
#include "osl.h"
#include "constant_fold.h"
@@ -1688,6 +1689,19 @@ void ConvertNode::constant_fold(const ConstantFolder& folder)
}
}
}
+ else {
+ ShaderInput *in = inputs[0];
+ ShaderNode *prev = in->link->parent;
+
+ /* no-op conversion of A to B to A */
+ if(prev->type == node_types[to][from]) {
+ ShaderInput *prev_in = prev->inputs[0];
+
+ if(SocketType::is_float3(from) && (to == SocketType::FLOAT || SocketType::is_float3(to)) && prev_in->link) {
+ folder.bypass(prev_in->link);
+ }
+ }
+ }
}
void ConvertNode::compile(SVMCompiler& compiler)
@@ -2295,6 +2309,7 @@ NODE_DEFINE(SubsurfaceScatteringNode)
SubsurfaceScatteringNode::SubsurfaceScatteringNode()
: BsdfNode(node_type)
{
+ closure = falloff;
}
void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
@@ -2305,6 +2320,7 @@ void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
void SubsurfaceScatteringNode::compile(OSLCompiler& compiler)
{
+ closure = falloff;
compiler.parameter(this, "falloff");
compiler.add(this, "node_subsurface_scattering");
}
@@ -2323,7 +2339,7 @@ NODE_DEFINE(EmissionNode)
NodeType* type = NodeType::add("emission", create, NodeType::SHADER);
SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f));
- SOCKET_IN_FLOAT(strength, "Strength", 1.0f);
+ SOCKET_IN_FLOAT(strength, "Strength", 10.0f);
SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL);
SOCKET_OUT_CLOSURE(emission, "Emission");
@@ -2344,7 +2360,7 @@ void EmissionNode::compile(SVMCompiler& compiler)
if(color_in->link || strength_in->link) {
compiler.add_node(NODE_EMISSION_WEIGHT,
compiler.stack_assign(color_in),
- compiler.stack_assign(strength_in));
+ compiler.stack_assign(strength_in));
}
else
compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color * strength);
@@ -3698,44 +3714,11 @@ void MixNode::compile(OSLCompiler& compiler)
void MixNode::constant_fold(const ConstantFolder& folder)
{
- ShaderInput *fac_in = input("Fac");
- ShaderInput *color1_in = input("Color1");
- ShaderInput *color2_in = input("Color2");
-
- /* evaluate fully constant node */
if(folder.all_inputs_constant()) {
folder.make_constant_clamp(svm_mix(type, fac, color1, color2), use_clamp);
- return;
- }
-
- /* remove no-op node when factor is 0.0 */
- if(!fac_in->link && fac <= 0.0f) {
- /* note that some of the modes will clamp out of bounds values even without use_clamp */
- if(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN) {
- if(!color1_in->link) {
- folder.make_constant_clamp(svm_mix(type, 0.0f, color1, color1), use_clamp);
- return;
- }
- }
- else if(folder.try_bypass_or_make_constant(color1_in, color1, use_clamp)) {
- return;
- }
}
-
- if(type == NODE_MIX_BLEND) {
- /* remove useless mix colors nodes */
- if(color1_in->link ? (color1_in->link == color2_in->link) : (!color2_in->link && color1 == color2)) {
- if(folder.try_bypass_or_make_constant(color1_in, color1, use_clamp)) {
- return;
- }
- }
-
- /* remove no-op mix color node when factor is 1.0 */
- if(!fac_in->link && fac >= 1.0f) {
- if(folder.try_bypass_or_make_constant(color2_in, color2, use_clamp)) {
- return;
- }
- }
+ else {
+ folder.fold_mix(type, use_clamp);
}
}
@@ -4621,6 +4604,9 @@ void MathNode::constant_fold(const ConstantFolder& folder)
if(folder.all_inputs_constant()) {
folder.make_constant_clamp(svm_math(type, value1, value2), use_clamp);
}
+ else {
+ folder.fold_math(type, use_clamp);
+ }
}
void MathNode::compile(SVMCompiler& compiler)
@@ -4693,6 +4679,9 @@ void VectorMathNode::constant_fold(const ConstantFolder& folder)
folder.make_constant(vector);
}
}
+ else {
+ folder.fold_vector_math(type);
+ }
}
void VectorMathNode::compile(SVMCompiler& compiler)
@@ -4853,6 +4842,30 @@ CurvesNode::CurvesNode(const NodeType *node_type)
{
}
+void CurvesNode::constant_fold(const ConstantFolder& folder, ShaderInput *value_in)
+{
+ ShaderInput *fac_in = input("Fac");
+
+ /* remove no-op node */
+ if(!fac_in->link && fac == 0.0f) {
+ folder.bypass(value_in->link);
+ }
+ /* evaluate fully constant node */
+ else if(folder.all_inputs_constant()) {
+ if (curves.size() == 0)
+ return;
+
+ float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x);
+ float3 result;
+
+ result[0] = rgb_ramp_lookup(curves.data(), pos[0], true, true, curves.size()).x;
+ result[1] = rgb_ramp_lookup(curves.data(), pos[1], true, true, curves.size()).y;
+ result[2] = rgb_ramp_lookup(curves.data(), pos[2], true, true, curves.size()).z;
+
+ folder.make_constant(interp(value, result, fac));
+ }
+}
+
void CurvesNode::compile(SVMCompiler& compiler, int type, ShaderInput *value_in, ShaderOutput *value_out)
{
if(curves.size() == 0)
@@ -4916,6 +4929,11 @@ RGBCurvesNode::RGBCurvesNode()
{
}
+void RGBCurvesNode::constant_fold(const ConstantFolder& folder)
+{
+ CurvesNode::constant_fold(folder, input("Color"));
+}
+
void RGBCurvesNode::compile(SVMCompiler& compiler)
{
CurvesNode::compile(compiler, NODE_RGB_CURVES, input("Color"), output("Color"));
@@ -4949,6 +4967,11 @@ VectorCurvesNode::VectorCurvesNode()
{
}
+void VectorCurvesNode::constant_fold(const ConstantFolder& folder)
+{
+ CurvesNode::constant_fold(folder, input("Vector"));
+}
+
void VectorCurvesNode::compile(SVMCompiler& compiler)
{
CurvesNode::compile(compiler, NODE_VECTOR_CURVES, input("Vector"), output("Vector"));
@@ -4982,6 +5005,31 @@ RGBRampNode::RGBRampNode()
{
}
+void RGBRampNode::constant_fold(const ConstantFolder& folder)
+{
+ if(ramp.size() == 0 || ramp.size() != ramp_alpha.size())
+ return;
+
+ if(folder.all_inputs_constant()) {
+ float f = clamp(fac, 0.0f, 1.0f) * (ramp.size() - 1);
+
+ /* clamp int as well in case of NaN */
+ int i = clamp((int)f, 0, ramp.size()-1);
+ float t = f - (float)i;
+
+ bool use_lerp = interpolate && t > 0.0f;
+
+ if(folder.output == output("Color")) {
+ float3 color = rgb_ramp_lookup(ramp.data(), fac, use_lerp, false, ramp.size());
+ folder.make_constant(color);
+ }
+ else if(folder.output == output("Alpha")) {
+ float alpha = float_ramp_lookup(ramp_alpha.data(), fac, use_lerp, false, ramp_alpha.size());
+ folder.make_constant(alpha);
+ }
+ }
+}
+
void RGBRampNode::compile(SVMCompiler& compiler)
{
if(ramp.size() == 0 || ramp.size() != ramp_alpha.size())
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index caad11af0f8..b0eb2395adf 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -152,8 +152,8 @@ class OutputNode : public ShaderNode {
public:
SHADER_NODE_CLASS(OutputNode)
- void* surface;
- void* volume;
+ void *surface;
+ void *volume;
float displacement;
float3 normal;
@@ -350,6 +350,7 @@ public:
float roughness, anisotropy, rotation;
ClosureType distribution;
+ ClosureType get_closure_type() { return distribution; }
void attributes(Shader *shader, AttributeRequestSet *attributes);
};
@@ -385,6 +386,7 @@ public:
void simplify_settings(Scene *scene);
bool has_integrator_dependency();
+ ClosureType get_closure_type() { return distribution; }
float roughness;
ClosureType distribution, distribution_orig;
@@ -396,6 +398,7 @@ public:
void simplify_settings(Scene *scene);
bool has_integrator_dependency();
+ ClosureType get_closure_type() { return distribution; }
float roughness, IOR;
ClosureType distribution, distribution_orig;
@@ -407,6 +410,7 @@ public:
void simplify_settings(Scene *scene);
bool has_integrator_dependency();
+ ClosureType get_closure_type() { return distribution; }
float roughness, IOR;
ClosureType distribution, distribution_orig;
@@ -425,6 +429,7 @@ public:
SHADER_NODE_CLASS(SubsurfaceScatteringNode)
bool has_surface_bssrdf() { return true; }
bool has_bssrdf_bump();
+ ClosureType get_closure_type() { return falloff; }
float scale;
float3 radius;
@@ -519,6 +524,7 @@ public:
class HairBsdfNode : public BsdfNode {
public:
SHADER_NODE_CLASS(HairBsdfNode)
+ ClosureType get_closure_type() { return component; }
ClosureType component;
float offset;
@@ -883,28 +889,32 @@ public:
virtual int get_group() { return NODE_GROUP_LEVEL_3; }
- bool has_spatial_varying() { return true; }
- void compile(SVMCompiler& compiler, int type, ShaderInput *value_in, ShaderOutput *value_out);
- void compile(OSLCompiler& compiler, const char *name);
-
array<float3> curves;
float min_x, max_x, fac;
float3 value;
+
+protected:
+ void constant_fold(const ConstantFolder& folder, ShaderInput *value_in);
+ void compile(SVMCompiler& compiler, int type, ShaderInput *value_in, ShaderOutput *value_out);
+ void compile(OSLCompiler& compiler, const char *name);
};
class RGBCurvesNode : public CurvesNode {
public:
SHADER_NODE_CLASS(RGBCurvesNode)
+ void constant_fold(const ConstantFolder& folder);
};
class VectorCurvesNode : public CurvesNode {
public:
SHADER_NODE_CLASS(VectorCurvesNode)
+ void constant_fold(const ConstantFolder& folder);
};
class RGBRampNode : public ShaderNode {
public:
SHADER_NODE_CLASS(RGBRampNode)
+ void constant_fold(const ConstantFolder& folder);
virtual int get_group() { return NODE_GROUP_LEVEL_1; }
array<float3> ramp;
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 662d87e8b6b..62076f3a865 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -29,6 +29,8 @@
#include "util_progress.h"
#include "util_vector.h"
+#include "subd_patch_table.h"
+
CCL_NAMESPACE_BEGIN
/* Object */
@@ -55,9 +57,9 @@ Object::Object()
particle_system = NULL;
particle_index = 0;
bounds = BoundBox::empty;
- motion.pre = transform_identity();
- motion.mid = transform_identity();
- motion.post = transform_identity();
+ motion.pre = transform_empty();
+ motion.mid = transform_empty();
+ motion.post = transform_empty();
use_motion = false;
}
@@ -70,19 +72,28 @@ void Object::compute_bounds(bool motion_blur)
BoundBox mbounds = mesh->bounds;
if(motion_blur && use_motion) {
- DecompMotionTransform decomp;
- transform_motion_decompose(&decomp, &motion, &tfm);
+ if(motion.pre == transform_empty() ||
+ motion.post == transform_empty()) {
+ /* Hide objects that have no valid previous or next transform, for
+ * example particle that stop existing. TODO: add support for this
+ * case in the kernel so we don't get render artifacts. */
+ bounds = BoundBox::empty;
+ }
+ else {
+ DecompMotionTransform decomp;
+ transform_motion_decompose(&decomp, &motion, &tfm);
- bounds = BoundBox::empty;
+ bounds = BoundBox::empty;
- /* todo: this is really terrible. according to pbrt there is a better
- * way to find this iteratively, but did not find implementation yet
- * or try to implement myself */
- for(float t = 0.0f; t < 1.0f; t += (1.0f/128.0f)) {
- Transform ttfm;
+ /* todo: this is really terrible. according to pbrt there is a better
+ * way to find this iteratively, but did not find implementation yet
+ * or try to implement myself */
+ for(float t = 0.0f; t < 1.0f; t += (1.0f/128.0f)) {
+ Transform ttfm;
- transform_motion_interpolate(&ttfm, &decomp, t);
- bounds.grow(mbounds.transformed(&ttfm));
+ transform_motion_interpolate(&ttfm, &decomp, t);
+ bounds.grow(mbounds.transformed(&ttfm));
+ }
}
}
else {
@@ -228,7 +239,7 @@ vector<float> Object::motion_times()
bool Object::is_traceable()
{
/* Mesh itself can be empty,can skip all such objects. */
- if (bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) {
+ if (!bounds.valid() || bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) {
return false;
}
/* TODO(sergey): Check for mesh vertices/curves. visibility flags. */
@@ -337,6 +348,15 @@ void ObjectManager::device_update_object_transform(UpdateObejctTransformState *s
Transform mtfm_pre = ob->motion.pre;
Transform mtfm_post = ob->motion.post;
+ /* In case of missing motion information for previous/next frame,
+ * assume there is no motion. */
+ if(!ob->use_motion || mtfm_pre == transform_empty()) {
+ mtfm_pre = ob->tfm;
+ }
+ if(!ob->use_motion || mtfm_post == transform_empty()) {
+ mtfm_post = ob->tfm;
+ }
+
if(!mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) {
mtfm_pre = mtfm_pre * itfm;
mtfm_post = mtfm_post * itfm;
@@ -589,6 +609,40 @@ void ObjectManager::device_update_flags(Device *device,
device->tex_alloc("__object_flag", dscene->object_flag);
}
+void ObjectManager::device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene)
+{
+ if (scene->objects.size() == 0)
+ return;
+
+ uint4* objects = (uint4*)dscene->objects.get_data();
+
+ bool update = false;
+
+ int object_index = 0;
+ foreach(Object *object, scene->objects) {
+ int offset = object_index*OBJECT_SIZE + 11;
+
+ Mesh* mesh = object->mesh;
+
+ if(mesh->patch_table) {
+ uint patch_map_offset = 2*(mesh->patch_table_offset + mesh->patch_table->total_size() -
+ mesh->patch_table->num_nodes * PATCH_NODE_SIZE) - mesh->patch_offset;
+
+ if(objects[offset].x != patch_map_offset) {
+ objects[offset].x = patch_map_offset;
+ update = true;
+ }
+ }
+
+ object_index++;
+ }
+
+ if(update) {
+ device->tex_free(dscene->objects);
+ device->tex_alloc("__objects", dscene->objects);
+ }
+}
+
void ObjectManager::device_free(Device *device, DeviceScene *dscene)
{
device->tex_free(dscene->objects);
@@ -638,7 +692,7 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, u
* Could be solved by moving reference counter to Mesh.
*/
if((mesh_users[object->mesh] == 1 && !object->mesh->has_surface_bssrdf) &&
- object->mesh->displacement_method == Mesh::DISPLACE_BUMP)
+ !object->mesh->has_true_displacement())
{
if(!(motion_blur && object->use_motion)) {
if(!object->mesh->transform_applied) {
diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h
index 7ab73f3c91a..2e5837f672f 100644
--- a/intern/cycles/render/object.h
+++ b/intern/cycles/render/object.h
@@ -97,6 +97,8 @@ public:
Scene *scene,
Progress& progress,
bool bounds_valid = true);
+ void device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene);
+
void device_free(Device *device, DeviceScene *dscene);
void tag_update(Scene *scene);
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 676afad997e..1a6ae5f9277 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -549,7 +549,7 @@ string OSLCompiler::id(ShaderNode *node)
{
/* assign layer unique name based on pointer address + bump mode */
stringstream stream;
- stream << "node_" << node->name << "_" << node;
+ stream << "node_" << node->type->name << "_" << node;
return stream.str();
}
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 05e807ff60c..9e72f197cce 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -74,10 +74,14 @@ public:
device_vector<uint> tri_shader;
device_vector<float4> tri_vnormal;
device_vector<uint4> tri_vindex;
+ device_vector<uint> tri_patch;
+ device_vector<float2> tri_patch_uv;
device_vector<float4> curves;
device_vector<float4> curve_keys;
+ device_vector<uint> patches;
+
/* objects */
device_vector<float4> objects;
device_vector<float4> objects_vector;
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 4cdb878df45..d000cca5a45 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -150,6 +150,12 @@ NODE_DEFINE(Shader)
volume_interpolation_method_enum.insert("cubic", VOLUME_INTERPOLATION_CUBIC);
SOCKET_ENUM(volume_interpolation_method, "Volume Interpolation Method", volume_interpolation_method_enum, VOLUME_INTERPOLATION_LINEAR);
+ static NodeEnum displacement_method_enum;
+ displacement_method_enum.insert("bump", DISPLACE_BUMP);
+ displacement_method_enum.insert("true", DISPLACE_TRUE);
+ displacement_method_enum.insert("both", DISPLACE_BOTH);
+ SOCKET_ENUM(displacement_method, "Displacement Method", displacement_method_enum, DISPLACE_BUMP);
+
return type;
}
@@ -173,6 +179,8 @@ Shader::Shader()
has_object_dependency = false;
has_integrator_dependency = false;
+ displacement_method = DISPLACE_BUMP;
+
id = -1;
used = false;
@@ -310,7 +318,7 @@ int ShaderManager::get_shader_id(Shader *shader, Mesh *mesh, bool smooth)
int id = shader->id*2;
/* index depends bump since this setting is not in the shader */
- if(mesh && mesh->displacement_method != Mesh::DISPLACE_TRUE)
+ if(mesh && shader->displacement_method != DISPLACE_TRUE)
id += 1;
/* smooth flag */
if(smooth)
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index dc57ed4e4eb..060ad7056bc 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -66,6 +66,14 @@ enum VolumeInterpolation {
VOLUME_NUM_INTERPOLATION,
};
+enum DisplacementMethod {
+ DISPLACE_BUMP = 0,
+ DISPLACE_TRUE = 1,
+ DISPLACE_BOTH = 2,
+
+ DISPLACE_NUM_METHODS,
+};
+
/* Shader describing the appearance of a Mesh, Light or Background.
*
* While there is only a single shader graph, it has three outputs: surface,
@@ -110,6 +118,9 @@ public:
bool has_object_dependency;
bool has_integrator_dependency;
+ /* displacement */
+ DisplacementMethod displacement_method;
+
/* requested mesh attributes */
AttributeRequestSet attributes;
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index f0e7ee2bd49..1a166885e2b 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -65,20 +65,21 @@ void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
svm_nodes.push_back(make_int4(NODE_SHADER_JUMP, 0, 0, 0));
svm_nodes.push_back(make_int4(NODE_SHADER_JUMP, 0, 0, 0));
}
-
+
foreach(Shader *shader, scene->shaders) {
if(progress.get_cancel()) return;
assert(shader->graph);
- if(shader->use_mis && shader->has_surface_emission)
- scene->light_manager->need_update = true;
-
SVMCompiler::Summary summary;
SVMCompiler compiler(scene->shader_manager, scene->image_manager);
compiler.background = (shader == scene->default_background);
compiler.compile(scene, shader, svm_nodes, shader->id, &summary);
+ if(shader->use_mis && shader->has_surface_emission) {
+ scene->light_manager->need_update = true;
+ }
+
VLOG(2) << "Compilation summary:\n"
<< "Shader name: " << shader->name << "\n"
<< summary.full_report();
diff --git a/intern/cycles/subd/CMakeLists.txt b/intern/cycles/subd/CMakeLists.txt
index d1708868fd0..9265299e82b 100644
--- a/intern/cycles/subd/CMakeLists.txt
+++ b/intern/cycles/subd/CMakeLists.txt
@@ -14,22 +14,17 @@ set(INC_SYS
set(SRC
subd_dice.cpp
- subd_mesh.cpp
subd_patch.cpp
subd_split.cpp
+ subd_patch_table.cpp
)
set(SRC_HEADERS
subd_dice.h
- subd_mesh.h
subd_patch.h
subd_split.h
)
-if(WITH_CYCLES_OPENSUBDIV)
- add_definitions(-DWITH_OPENSUBDIV)
-endif()
-
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
diff --git a/intern/cycles/subd/subd_dice.cpp b/intern/cycles/subd/subd_dice.cpp
index 7c74f21950e..36981a20f3c 100644
--- a/intern/cycles/subd/subd_dice.cpp
+++ b/intern/cycles/subd/subd_dice.cpp
@@ -48,6 +48,11 @@ void EdgeDice::reserve(int num_verts)
vert_offset = mesh->verts.size();
tri_offset = mesh->num_triangles();
+ /* todo: optimize so we can reserve in advance, this is like push_back_slow() */
+ if(vert_offset + num_verts > mesh->verts.capacity()) {
+ mesh->reserve_mesh(size_t((vert_offset + num_verts) * 1.2), mesh->num_triangles());
+ }
+
mesh->resize_mesh(vert_offset + num_verts, tri_offset);
Attribute *attr_vN = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
@@ -66,6 +71,7 @@ int EdgeDice::add_vert(Patch *patch, float2 uv)
mesh_P[vert_offset] = P;
mesh_N[vert_offset] = N;
+ params.mesh->vert_patch_uv[vert_offset] = make_float2(uv.x, uv.y);
if(params.ptex) {
Attribute *attr_ptex_uv = params.mesh->attributes.add(ATTR_STD_PTEX_UV);
@@ -75,6 +81,8 @@ int EdgeDice::add_vert(Patch *patch, float2 uv)
ptex_uv[vert_offset] = make_float3(uv.x, uv.y, 0.0f);
}
+ params.mesh->num_subd_verts++;
+
return vert_offset++;
}
@@ -86,7 +94,8 @@ void EdgeDice::add_triangle(Patch *patch, int v0, int v1, int v2)
if(mesh->triangles.size() == mesh->triangles.capacity())
mesh->reserve_mesh(mesh->verts.size(), size_t(max(mesh->num_triangles() + 1, 1) * 1.2));
- mesh->add_triangle(v0, v1, v2, params.shader, params.smooth, false);
+ mesh->add_triangle(v0, v1, v2, patch->shader, true);
+ params.mesh->triangle_patch[params.mesh->num_triangles()-1] = patch->patch_index;
if(params.ptex) {
Attribute *attr_ptex_face_id = params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
@@ -340,160 +349,5 @@ void QuadDice::dice(SubPatch& sub, EdgeFactors& ef)
assert(vert_offset == params.mesh->verts.size());
}
-/* TriangleDice */
-
-TriangleDice::TriangleDice(const SubdParams& params_)
-: EdgeDice(params_)
-{
-}
-
-void TriangleDice::reserve(EdgeFactors& ef, int M)
-{
- int num_verts = ef.tu + ef.tv + ef.tw;
-
- for(int m = M-2; m > 0; m -= 2)
- num_verts += 3 + (m-1)*3;
-
- if(!(M & 1))
- num_verts++;
-
- EdgeDice::reserve(num_verts);
-}
-
-float2 TriangleDice::map_uv(SubPatch& sub, float2 uv)
-{
- /* map UV from subpatch to patch parametric coordinates */
- return uv.x*sub.Pu + uv.y*sub.Pv + (1.0f - uv.x - uv.y)*sub.Pw;
-}
-
-int TriangleDice::add_vert(SubPatch& sub, float2 uv)
-{
- return EdgeDice::add_vert(sub.patch, map_uv(sub, uv));
-}
-
-void TriangleDice::add_grid(SubPatch& sub, EdgeFactors& ef, int M)
-{
- // XXX normals are flipped, why?
-
- /* grid is constructed starting from the outside edges, and adding
- * progressively smaller inner triangles that connected to the outer
- * one, until M = 1 or 2, the we fill up the last part. */
- vector<int> outer_u, outer_v, outer_w;
- int m;
-
- /* add outer corners vertices */
- {
- float2 p_u = make_float2(1.0f, 0.0f);
- float2 p_v = make_float2(0.0f, 1.0f);
- float2 p_w = make_float2(0.0f, 0.0f);
-
- int corner_u = add_vert(sub, p_u);
- int corner_v = add_vert(sub, p_v);
- int corner_w = add_vert(sub, p_w);
-
- outer_u.push_back(corner_v);
- outer_v.push_back(corner_w);
- outer_w.push_back(corner_u);
-
- for(int i = 1; i < ef.tu; i++)
- outer_u.push_back(add_vert(sub, interp(p_v, p_w, i/(float)ef.tu)));
- for(int i = 1; i < ef.tv; i++)
- outer_v.push_back(add_vert(sub, interp(p_w, p_u, i/(float)ef.tv)));
- for(int i = 1; i < ef.tw; i++)
- outer_w.push_back(add_vert(sub, interp(p_u, p_v, i/(float)ef.tw)));
-
- outer_u.push_back(corner_w);
- outer_v.push_back(corner_u);
- outer_w.push_back(corner_v);
- }
-
- for(m = M-2; m > 0; m -= 2) {
- vector<int> inner_u, inner_v, inner_w;
-
- const float t0 = m / (float)M;
- float2 center = make_float2(1.0f/3.0f, 1.0f/3.0f);
-
- /* 3 corner vertices */
- float2 p_u = interp(center, make_float2(1.0f, 0.0f), t0);
- float2 p_v = interp(center, make_float2(0.0f, 1.0f), t0);
- float2 p_w = interp(center, make_float2(0.0f, 0.0f), t0);
-
- int corner_u = add_vert(sub, p_u);
- int corner_v = add_vert(sub, p_v);
- int corner_w = add_vert(sub, p_w);
-
- /* construct array of vertex indices for each side */
- inner_u.push_back(corner_v);
- inner_v.push_back(corner_w);
- inner_w.push_back(corner_u);
-
- for(int i = 1; i < m; i++) {
- /* add vertices between corners */
- const float t1 = i / (float)m;
-
- inner_u.push_back(add_vert(sub, interp(p_v, p_w, t1)));
- inner_v.push_back(add_vert(sub, interp(p_w, p_u, t1)));
- inner_w.push_back(add_vert(sub, interp(p_u, p_v, t1)));
- }
-
- inner_u.push_back(corner_w);
- inner_v.push_back(corner_u);
- inner_w.push_back(corner_v);
-
- /* stitch together inner/outer with triangles */
- stitch_triangles(sub.patch, outer_u, inner_u);
- stitch_triangles(sub.patch, outer_v, inner_v);
- stitch_triangles(sub.patch, outer_w, inner_w);
-
- outer_u = inner_u;
- outer_v = inner_v;
- outer_w = inner_w;
- }
-
- /* fill up last part */
- if(m == -1) {
- /* single triangle */
- add_triangle(sub.patch, outer_w[0], outer_u[0], outer_v[0]);
- }
- else {
- /* center vertex + up to 6 triangles */
- int center = add_vert(sub, make_float2(1.0f/3.0f, 1.0f/3.0f));
-
- add_triangle(sub.patch, outer_w[0], outer_w[1], center);
- /* if this is false then there is only one triangle on this side */
- if(outer_w.size() > 2)
- add_triangle(sub.patch, outer_w[1], outer_w[2], center);
-
- add_triangle(sub.patch, outer_u[0], outer_u[1], center);
- if(outer_u.size() > 2)
- add_triangle(sub.patch, outer_u[1], outer_u[2], center);
-
- add_triangle(sub.patch, outer_v[0], outer_v[1], center);
- if(outer_v.size() > 2)
- add_triangle(sub.patch, outer_v[1], outer_v[2], center);
- }
-}
-
-void TriangleDice::dice(SubPatch& sub, EdgeFactors& ef)
-{
- /* todo: handle 2 1 1 resolution */
- int M = max(ef.tu, max(ef.tv, ef.tw));
-
- /* Due to the "slant" of the edges of a triangle compared to a quad, the internal
- * triangles end up smaller, causing over-tessellation. This is to correct for this
- * difference in area. Technically its only correct for equilateral triangles, but
- * its better than how it was.
- *
- * (2*cos(radians(30))/3)**0.5
- */
- float S = 0.7598356856515927f;
- M = max((int)ceil(S*M), 1);
-
- reserve(ef, M);
- add_grid(sub, ef, M);
-
- assert(vert_offset == params.mesh->verts.size());
-}
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/subd/subd_dice.h b/intern/cycles/subd/subd_dice.h
index 85bd0ea28f0..3002ec780e8 100644
--- a/intern/cycles/subd/subd_dice.h
+++ b/intern/cycles/subd/subd_dice.h
@@ -33,8 +33,6 @@ class Patch;
struct SubdParams {
Mesh *mesh;
- int shader;
- bool smooth;
bool ptex;
int test_steps;
@@ -44,11 +42,9 @@ struct SubdParams {
Camera *camera;
Transform objecttoworld;
- SubdParams(Mesh *mesh_, int shader_, bool smooth_ = true, bool ptex_ = false)
+ SubdParams(Mesh *mesh_, bool ptex_ = false)
{
mesh = mesh_;
- shader = shader_;
- smooth = smooth_;
ptex = ptex_;
test_steps = 3;
@@ -136,46 +132,6 @@ public:
void dice(SubPatch& sub, EdgeFactors& ef);
};
-/* Triangle EdgeDice
- *
- * Edge tessellation factors and subpatch coordinates are as follows:
- *
- * Pw
- * /\
- * tv / \ tu
- * / \
- * / \
- * Pu -------- Pv
- * tw
- */
-
-class TriangleDice : public EdgeDice {
-public:
- struct SubPatch {
- Patch *patch;
-
- float2 Pu;
- float2 Pv;
- float2 Pw;
- };
-
- struct EdgeFactors {
- int tu;
- int tv;
- int tw;
- };
-
- explicit TriangleDice(const SubdParams& params);
-
- void reserve(EdgeFactors& ef, int M);
-
- float2 map_uv(SubPatch& sub, float2 uv);
- int add_vert(SubPatch& sub, float2 uv);
-
- void add_grid(SubPatch& sub, EdgeFactors& ef, int M);
- void dice(SubPatch& sub, EdgeFactors& ef);
-};
-
CCL_NAMESPACE_END
#endif /* __SUBD_DICE_H__ */
diff --git a/intern/cycles/subd/subd_mesh.cpp b/intern/cycles/subd/subd_mesh.cpp
deleted file mode 100644
index 56d7d2b2303..00000000000
--- a/intern/cycles/subd/subd_mesh.cpp
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Original code in the public domain -- castanyo@yahoo.es
- *
- * Modifications copyright (c) 2011, Blender Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 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
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-
-#include "subd_mesh.h"
-#include "subd_patch.h"
-#include "subd_split.h"
-
-#include "util_debug.h"
-#include "util_foreach.h"
-
-#ifdef WITH_OPENSUBDIV
-
-#include <osd/vertex.h>
-#include <osd/mesh.h>
-#include <osd/cpuComputeController.h>
-#include <osd/cpuVertexBuffer.h>
-#include <osd/cpuEvalLimitController.h>
-#include <osd/evalLimitContext.h>
-
-CCL_NAMESPACE_BEGIN
-
-/* typedefs */
-typedef OpenSubdiv::OsdVertex OsdVertex;
-typedef OpenSubdiv::FarMesh<OsdVertex> OsdFarMesh;
-typedef OpenSubdiv::FarMeshFactory<OsdVertex> OsdFarMeshFactory;
-typedef OpenSubdiv::HbrCatmarkSubdivision<OsdVertex> OsdHbrCatmarkSubdivision;
-typedef OpenSubdiv::HbrFace<OsdVertex> OsdHbrFace;
-typedef OpenSubdiv::HbrHalfedge<OsdVertex> OsdHbrHalfEdge;
-typedef OpenSubdiv::HbrMesh<OsdVertex> OsdHbrMesh;
-typedef OpenSubdiv::HbrVertex<OsdVertex> OsdHbrVertex;
-typedef OpenSubdiv::OsdCpuComputeContext OsdCpuComputeContext;
-typedef OpenSubdiv::OsdCpuComputeController OsdCpuComputeController;
-typedef OpenSubdiv::OsdCpuEvalLimitContext OsdCpuEvalLimitContext;
-typedef OpenSubdiv::OsdCpuEvalLimitController OsdCpuEvalLimitController;
-typedef OpenSubdiv::OsdCpuVertexBuffer OsdCpuVertexBuffer;
-typedef OpenSubdiv::OsdEvalCoords OsdEvalCoords;
-typedef OpenSubdiv::OsdVertexBufferDescriptor OsdVertexBufferDescriptor;
-
-/* OpenSubdiv Patch */
-
-class OpenSubdPatch : public Patch {
-public:
- int face_id;
-
- OpenSubdPatch(OsdFarMesh *farmesh, OsdCpuVertexBuffer *vbuf_base)
- {
- face_id = 0;
-
- /* create buffers for evaluation */
- vbuf_P = OsdCpuVertexBuffer::Create(3, 1);
- vbuf_dPdu = OsdCpuVertexBuffer::Create(3, 1);
- vbuf_dPdv = OsdCpuVertexBuffer::Create(3, 1);
-
- P = vbuf_P->BindCpuBuffer();
- dPdu = vbuf_dPdu->BindCpuBuffer();
- dPdv = vbuf_dPdv->BindCpuBuffer();
-
- /* setup evaluation context */
- OsdVertexBufferDescriptor in_desc(0, 3, 3), out_desc(0, 3, 3); /* offset, length, stride */
-
- evalctx = OsdCpuEvalLimitContext::Create(farmesh, false);
- evalctx->GetVertexData().Bind(in_desc, vbuf_base, out_desc, vbuf_P, vbuf_dPdu, vbuf_dPdv);
- }
-
- ~OpenSubdPatch()
- {
- evalctx->GetVertexData().Unbind();
-
- delete evalctx;
- delete vbuf_P;
- delete vbuf_dPdu;
- delete vbuf_dPdv;
- }
-
- void eval(float3 *P_, float3 *dPdu_, float3 *dPdv_, float u, float v)
- {
- OsdEvalCoords coords;
- coords.u = u;
- coords.v = v;
- coords.face = face_id;
-
- evalctrl.EvalLimitSample<OsdCpuVertexBuffer,OsdCpuVertexBuffer>(coords, evalctx, 0);
-
- *P_ = make_float3(P[0], P[1], P[2]);
- if(dPdu_) *dPdu_ = make_float3(dPdv[0], dPdv[1], dPdv[2]);
- if(dPdv_) *dPdv_ = make_float3(dPdu[0], dPdu[1], dPdu[2]);
-
- /* optimize: skip evaluating derivatives when not needed */
- /* todo: swapped derivatives, different winding convention? */
- }
-
- BoundBox bound()
- {
- /* not implemented */
- BoundBox bbox = BoundBox::empty;
- return bbox;
- }
-
- int ptex_face_id()
- {
- return face_id;
- }
-
-protected:
- OsdCpuEvalLimitController evalctrl;
- OsdCpuEvalLimitContext *evalctx;
- OsdCpuVertexBuffer *vbuf_P;
- OsdCpuVertexBuffer *vbuf_dPdu;
- OsdCpuVertexBuffer *vbuf_dPdv;
- float *P;
- float *dPdu;
- float *dPdv;
-};
-
-/* OpenSubdiv Mesh */
-
-OpenSubdMesh::OpenSubdMesh()
-{
- /* create osd mesh */
- static OsdHbrCatmarkSubdivision catmark;
- OsdHbrMesh *hbrmesh = new OsdHbrMesh(&catmark);
-
- /* initialize class */
- num_verts = 0;
- num_ptex_faces = 0;
- _hbrmesh = (void*)hbrmesh;
-}
-
-OpenSubdMesh::~OpenSubdMesh()
-{
- OsdHbrMesh *hbrmesh = (OsdHbrMesh*)_hbrmesh;
-
- if(hbrmesh)
- delete hbrmesh;
-}
-
-void OpenSubdMesh::add_vert(const float3& co)
-{
- OsdHbrMesh *hbrmesh = (OsdHbrMesh*)_hbrmesh;
-
- OsdVertex v;
- positions.push_back(co.x);
- positions.push_back(co.y);
- positions.push_back(co.z);
- hbrmesh->NewVertex(num_verts++, v);
-}
-
-void OpenSubdMesh::add_face(int v0, int v1, int v2)
-{
- int index[3] = {v0, v1, v2};
- return add_face(index, 3);
-}
-
-void OpenSubdMesh::add_face(int v0, int v1, int v2, int v3)
-{
- int index[4] = {v0, v1, v2, v3};
- add_face(index, 4);
-}
-
-void OpenSubdMesh::add_face(int *index, int num)
-{
- OsdHbrMesh *hbrmesh = (OsdHbrMesh*)_hbrmesh;
-
-#ifndef NDEBUG
- /* sanity checks */
- for(int j = 0; j < num; j++) {
- OsdHbrVertex *origin = hbrmesh->GetVertex(index[j]);
- OsdHbrVertex *destination = hbrmesh->GetVertex(index[(j+1)%num]);
- OsdHbrHalfEdge *opposite = destination->GetEdge(origin);
-
- if(origin==NULL || destination==NULL)
- assert(!"An edge was specified that connected a nonexistent vertex\n");
-
- if(origin == destination)
- assert(!"An edge was specified that connected a vertex to itself\n");
-
- if(opposite && opposite->GetOpposite())
- assert(!"A non-manifold edge incident to more than 2 faces was found\n");
-
- if(origin->GetEdge(destination)) {
- assert(!"An edge connecting two vertices was specified more than once."
- "It's likely that an incident face was flipped\n");
- }
- }
-#endif
-
- OsdHbrFace *face = hbrmesh->NewFace(num, index, 0);
-
- /* this is required for limit eval patch table? */
- face->SetPtexIndex(num_ptex_faces);
-
- if(num == 4)
- num_ptex_faces++;
- else
- num_ptex_faces += num;
-}
-
-bool OpenSubdMesh::finish()
-{
- OsdHbrMesh *hbrmesh = (OsdHbrMesh*)_hbrmesh;
-
- /* finish hbr mesh construction */
- hbrmesh->SetInterpolateBoundaryMethod(OsdHbrMesh::k_InterpolateBoundaryEdgeOnly);
- hbrmesh->Finish();
-
- return true;
-}
-
-void OpenSubdMesh::tessellate(DiagSplit *split)
-{
- if(num_ptex_faces == 0)
- return;
-
- const int level = 3;
- const bool requirefvar = false;
-
- /* convert HRB to FAR mesh */
- OsdHbrMesh *hbrmesh = (OsdHbrMesh*)_hbrmesh;
-
- OsdFarMeshFactory meshFactory(hbrmesh, level, true);
- OsdFarMesh *farmesh = meshFactory.Create(requirefvar);
- int num_hbr_verts = hbrmesh->GetNumVertices();
-
- delete hbrmesh;
- hbrmesh = NULL;
- _hbrmesh = NULL;
-
- /* refine HBR mesh with vertex coordinates */
- OsdCpuComputeController *compute_controller = new OsdCpuComputeController();
- OsdCpuComputeContext *compute_context = OsdCpuComputeContext::Create(farmesh);
-
- OsdCpuVertexBuffer *vbuf_base = OsdCpuVertexBuffer::Create(3, num_hbr_verts);
- vbuf_base->UpdateData(&positions[0], 0, num_verts);
-
- compute_controller->Refine(compute_context, farmesh->GetKernelBatches(), vbuf_base);
- compute_controller->Synchronize();
-
- /* split & dice patches */
- OpenSubdPatch patch(farmesh, vbuf_base);
-
- for(int f = 0; f < num_ptex_faces; f++) {
- patch.face_id = f;
- split->split_quad(&patch);
- }
-
- /* clean up */
- delete farmesh;
- delete compute_controller;
- delete compute_context;
- delete vbuf_base;
-}
-
-CCL_NAMESPACE_END
-
-#else /* WITH_OPENSUBDIV */
-
-CCL_NAMESPACE_BEGIN
-
-/* Subd Vertex */
-
-class SubdVert
-{
-public:
- int id;
- float3 co;
-
- explicit SubdVert(int id_)
- {
- id = id_;
- co = make_float3(0.0f, 0.0f, 0.0f);
- }
-};
-
-/* Subd Face */
-
-class SubdFace
-{
-public:
- int id;
- int numverts;
- int verts[4];
-
- explicit SubdFace(int id_)
- {
- id = id_;
- numverts = 0;
- }
-};
-
-/* Subd Mesh */
-
-SubdMesh::SubdMesh()
-{
-}
-
-SubdMesh::~SubdMesh()
-{
- foreach(SubdVert *vertex, verts)
- delete vertex;
- foreach(SubdFace *face, faces)
- delete face;
-
- verts.clear();
- faces.clear();
-}
-
-SubdVert *SubdMesh::add_vert(const float3& co)
-{
- SubdVert *v = new SubdVert(verts.size());
- v->co = co;
- verts.push_back(v);
-
- return v;
-}
-
-SubdFace *SubdMesh::add_face(int v0, int v1, int v2)
-{
- int index[3] = {v0, v1, v2};
- return add_face(index, 3);
-}
-
-SubdFace *SubdMesh::add_face(int v0, int v1, int v2, int v3)
-{
- int index[4] = {v0, v1, v2, v3};
- return add_face(index, 4);
-}
-
-SubdFace *SubdMesh::add_face(int *index, int num)
-{
- /* skip ngons */
- if(num < 3 || num > 4)
- return NULL;
-
- SubdFace *f = new SubdFace(faces.size());
-
- for(int i = 0; i < num; i++)
- f->verts[i] = index[i];
-
- f->numverts = num;
- faces.push_back(f);
-
- return f;
-}
-
-bool SubdMesh::finish()
-{
- return true;
-}
-
-void SubdMesh::tessellate(DiagSplit *split)
-{
- int num_faces = faces.size();
-
- for(int f = 0; f < num_faces; f++) {
- SubdFace *face = faces[f];
- Patch *patch;
- float3 *hull;
-
- if(face->numverts == 3) {
- LinearTrianglePatch *lpatch = new LinearTrianglePatch();
- hull = lpatch->hull;
- patch = lpatch;
- }
- else if(face->numverts == 4) {
- LinearQuadPatch *lpatch = new LinearQuadPatch();
- hull = lpatch->hull;
- patch = lpatch;
- }
- else {
- assert(0); /* n-gons should have been split already */
- continue;
- }
-
- for(int i = 0; i < face->numverts; i++)
- hull[i] = verts[face->verts[i]]->co;
-
- if(face->numverts == 4)
- swap(hull[2], hull[3]);
-
- if(patch->is_triangle())
- split->split_triangle(patch);
- else
- split->split_quad(patch);
-
- delete patch;
- }
-}
-
-CCL_NAMESPACE_END
-
-#endif /* WITH_OPENSUBDIV */
-
diff --git a/intern/cycles/subd/subd_mesh.h b/intern/cycles/subd/subd_mesh.h
deleted file mode 100644
index f6aefc20318..00000000000
--- a/intern/cycles/subd/subd_mesh.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Original code in the public domain -- castanyo@yahoo.es
- *
- * Modifications copyright (c) 2011, Blender Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 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
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef __SUBD_MESH_H__
-#define __SUBD_MESH_H__
-
-#include "util_map.h"
-#include "util_types.h"
-#include "util_vector.h"
-
-CCL_NAMESPACE_BEGIN
-
-#ifndef WITH_OPENSUBDIV
-class SubdVert;
-class SubdFace;
-#endif
-
-class DiagSplit;
-class Mesh;
-
-/* Subd Mesh with simple linear subdivision */
-
-class SubdMesh
-{
-public:
- SubdMesh();
- ~SubdMesh();
-
- SubdVert *add_vert(const float3& co);
-
- SubdFace *add_face(int v0, int v1, int v2);
- SubdFace *add_face(int v0, int v1, int v2, int v3);
- SubdFace *add_face(int *index, int num);
-
- bool finish();
- void tessellate(DiagSplit *split);
-
-protected:
-#ifdef WITH_OPENSUBDIV
- void *_hbrmesh;
- vector<float> positions;
- int num_verts, num_ptex_faces;
-#else
- vector<SubdVert*> verts;
- vector<SubdFace*> faces;
-#endif
-
-};
-
-CCL_NAMESPACE_END
-
-#endif /* __SUBD_MESH_H__ */
-
diff --git a/intern/cycles/subd/subd_patch.cpp b/intern/cycles/subd/subd_patch.cpp
index 60a78016054..d3319c5ccf5 100644
--- a/intern/cycles/subd/subd_patch.cpp
+++ b/intern/cycles/subd/subd_patch.cpp
@@ -84,32 +84,6 @@ BoundBox LinearQuadPatch::bound()
return bbox;
}
-/* Linear Triangle Patch */
-
-void LinearTrianglePatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
-{
- *P = u*hull[0] + v*hull[1] + (1.0f - u - v)*hull[2];
-
- if(dPdu && dPdv) {
- *dPdu = hull[0] - hull[2];
- *dPdv = hull[1] - hull[2];
- }
-
- if(N) {
- *N = normalize(u*normals[0] + v*normals[1] + (1.0f - u - v)*normals[2]);
- }
-}
-
-BoundBox LinearTrianglePatch::bound()
-{
- BoundBox bbox = BoundBox::empty;
-
- for(int i = 0; i < 3; i++)
- bbox.grow(hull[i]);
-
- return bbox;
-}
-
/* Bicubic Patch */
void BicubicPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
diff --git a/intern/cycles/subd/subd_patch.h b/intern/cycles/subd/subd_patch.h
index bfa04412c66..360c1abf27b 100644
--- a/intern/cycles/subd/subd_patch.h
+++ b/intern/cycles/subd/subd_patch.h
@@ -26,9 +26,11 @@ class Patch {
public:
virtual ~Patch() {}
virtual void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v) = 0;
- virtual bool is_triangle() { return false; }
virtual BoundBox bound() = 0;
virtual int ptex_face_id() { return -1; }
+
+ int patch_index;
+ int shader;
};
/* Linear Quad Patch */
@@ -39,19 +41,6 @@ public:
float3 normals[4];
void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v);
- bool is_triangle() { return false; }
- BoundBox bound();
-};
-
-/* Linear Triangle Patch */
-
-class LinearTrianglePatch : public Patch {
-public:
- float3 hull[3];
- float3 normals[3];
-
- void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v);
- bool is_triangle() { return true; }
BoundBox bound();
};
@@ -62,7 +51,6 @@ public:
float3 hull[16];
void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v);
- bool is_triangle() { return false; }
BoundBox bound();
};
diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/subd_patch_table.cpp
new file mode 100644
index 00000000000..68ec1b2c6a6
--- /dev/null
+++ b/intern/cycles/subd/subd_patch_table.cpp
@@ -0,0 +1,297 @@
+/*
+ * Based on code from OpenSubdiv released under this license:
+ *
+ * Copyright 2014 DreamWorks Animation LLC.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "Apache License")
+ * with the following modification; you may not use this file except in
+ * compliance with the Apache License and the following modification to it:
+ * Section 6. Trademarks. is deleted and replaced with:
+ *
+ * 6. Trademarks. This License does not grant permission to use the trade
+ * names, trademarks, service marks, or product names of the Licensor
+ * and its affiliates, except as required to comply with Section 4(c) of
+ * the License and to reproduce the content of the NOTICE file.
+ *
+ * You may obtain a copy of the Apache License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Apache License with the above modification is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the Apache License for the specific
+ * language governing permissions and limitations under the Apache License.
+ *
+ */
+
+#include "subd_patch_table.h"
+#include "kernel_types.h"
+
+#include "util_math.h"
+
+#ifdef WITH_OPENSUBDIV
+#include <opensubdiv/far/patchTable.h>
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef WITH_OPENSUBDIV
+
+using namespace OpenSubdiv;
+
+/* functions for building patch maps */
+
+struct PatchMapQuadNode {
+ /* sets all the children to point to the patch of index */
+ void set_child(int index)
+ {
+ for (int i = 0; i < 4; i++) {
+ children[i] = index | PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF;
+ }
+ }
+
+ /* sets the child in quadrant to point to the node or patch of the given index */
+ void set_child(unsigned char quadrant, int index, bool is_leaf=true)
+ {
+ assert(quadrant < 4);
+ children[quadrant] = index | PATCH_MAP_NODE_IS_SET | (is_leaf ? PATCH_MAP_NODE_IS_LEAF : 0);
+ }
+
+ uint children[4];
+};
+
+template<class T>
+static int resolve_quadrant(T& median, T& u, T& v)
+{
+ int quadrant = -1;
+
+ if(u < median) {
+ if(v < median) {
+ quadrant = 0;
+ }
+ else {
+ quadrant = 1;
+ v -= median;
+ }
+ }
+ else {
+ if(v < median) {
+ quadrant = 3;
+ }
+ else {
+ quadrant = 2;
+ v -= median;
+ }
+ u -= median;
+ }
+
+ return quadrant;
+}
+
+static void build_patch_map(PackedPatchTable& table, OpenSubdiv::Far::PatchTable* patch_table, int offset)
+{
+ int num_faces = 0;
+
+ for(int array = 0; array < table.num_arrays; array++) {
+ Far::ConstPatchParamArray params = patch_table->GetPatchParams(array);
+
+ for(int j = 0; j < patch_table->GetNumPatches(array); j++) {
+ num_faces = max(num_faces, (int)params[j].GetFaceId());
+ }
+ }
+ num_faces++;
+
+ vector<PatchMapQuadNode> quadtree;
+ quadtree.reserve(num_faces + table.num_patches);
+ quadtree.resize(num_faces);
+
+ /* adjust offsets to make indices relative to the table */
+ int handle_index = -(table.num_patches * PATCH_HANDLE_SIZE);
+ offset += table.total_size();
+
+ /* populate the quadtree from the FarPatchArrays sub-patches */
+ for(int array = 0; array < table.num_arrays; array++) {
+ Far::ConstPatchParamArray params = patch_table->GetPatchParams(array);
+
+ for(int i = 0; i < patch_table->GetNumPatches(array); i++, handle_index += PATCH_HANDLE_SIZE) {
+ const Far::PatchParam& param = params[i];
+ unsigned short depth = param.GetDepth();
+
+ PatchMapQuadNode* node = &quadtree[params[i].GetFaceId()];
+
+ if(depth == (param.NonQuadRoot() ? 1 : 0)) {
+ /* special case : regular BSpline face w/ no sub-patches */
+ node->set_child(handle_index + offset);
+ continue;
+ }
+
+ int u = param.GetU();
+ int v = param.GetV();
+ int pdepth = param.NonQuadRoot() ? depth-2 : depth-1;
+ int half = 1 << pdepth;
+
+ for(int j = 0; j < depth; j++) {
+ int delta = half >> 1;
+
+ int quadrant = resolve_quadrant(half, u, v);
+ assert(quadrant >= 0);
+
+ half = delta;
+
+ if(j == pdepth) {
+ /* we have reached the depth of the sub-patch : add a leaf */
+ assert(!(node->children[quadrant] & PATCH_MAP_NODE_IS_SET));
+ node->set_child(quadrant, handle_index + offset, true);
+ break;
+ }
+ else {
+ /* travel down the child node of the corresponding quadrant */
+ if(!(node->children[quadrant] & PATCH_MAP_NODE_IS_SET)) {
+ /* create a new branch in the quadrant */
+ quadtree.push_back(PatchMapQuadNode());
+
+ int idx = (int)quadtree.size() - 1;
+ node->set_child(quadrant, idx*4 + offset, false);
+
+ node = &quadtree[idx];
+ }
+ else {
+ /* travel down an existing branch */
+ uint idx = node->children[quadrant] & PATCH_MAP_NODE_INDEX_MASK;
+ node = &(quadtree[(idx - offset)/4]);
+ }
+ }
+ }
+ }
+ }
+
+ /* copy into table */
+ assert(table.table.size() == table.total_size());
+ uint map_offset = table.total_size();
+
+ table.num_nodes = quadtree.size() * 4;
+ table.table.resize(table.total_size());
+
+ uint* data = &table.table[map_offset];
+
+ for(int i = 0; i < quadtree.size(); i++) {
+ for(int j = 0; j < 4; j++) {
+ assert(quadtree[i].children[j] & PATCH_MAP_NODE_IS_SET);
+ *(data++) = quadtree[i].children[j];
+ }
+ }
+}
+
+#endif
+
+/* packed patch table functions */
+
+size_t PackedPatchTable::total_size()
+{
+ return num_arrays * PATCH_ARRAY_SIZE +
+ num_indices +
+ num_patches * (PATCH_PARAM_SIZE + PATCH_HANDLE_SIZE) +
+ num_nodes * PATCH_NODE_SIZE;
+}
+
+void PackedPatchTable::pack(Far::PatchTable* patch_table, int offset)
+{
+ num_arrays = 0;
+ num_patches = 0;
+ num_indices = 0;
+ num_nodes = 0;
+
+#ifdef WITH_OPENSUBDIV
+ num_arrays = patch_table->GetNumPatchArrays();
+
+ for(int i = 0; i < num_arrays; i++) {
+ int patches = patch_table->GetNumPatches(i);
+ int num_control = patch_table->GetPatchArrayDescriptor(i).GetNumControlVertices();
+
+ num_patches += patches;
+ num_indices += patches * num_control;
+ }
+
+ table.resize(total_size());
+ uint* data = &table[0];
+
+ uint* array = data;
+ uint* index = array + num_arrays * PATCH_ARRAY_SIZE;
+ uint* param = index + num_indices;
+ uint* handle = param + num_patches * PATCH_PARAM_SIZE;
+
+ uint current_param = 0;
+
+ for(int i = 0; i < num_arrays; i++) {
+ *(array++) = patch_table->GetPatchArrayDescriptor(i).GetType();
+ *(array++) = patch_table->GetNumPatches(i);
+ *(array++) = (index - data) + offset;
+ *(array++) = (param - data) + offset;
+
+ Far::ConstIndexArray indices = patch_table->GetPatchArrayVertices(i);
+
+ for(int j = 0; j < indices.size(); j++) {
+ *(index++) = indices[j];
+ }
+
+ const Far::PatchParamTable& param_table = patch_table->GetPatchParamTable();
+
+ int num_control = patch_table->GetPatchArrayDescriptor(i).GetNumControlVertices();
+ int patches = patch_table->GetNumPatches(i);
+
+ for(int j = 0; j < patches; j++, current_param++) {
+ *(param++) = param_table[current_param].field0;
+ *(param++) = param_table[current_param].field1;
+
+ *(handle++) = (array - data) - PATCH_ARRAY_SIZE + offset;
+ *(handle++) = (param - data) - PATCH_PARAM_SIZE + offset;
+ *(handle++) = j * num_control;
+ }
+ }
+
+ build_patch_map(*this, patch_table, offset);
+#else
+ (void)patch_table;
+ (void)offset;
+#endif
+}
+
+void PackedPatchTable::copy_adjusting_offsets(uint* dest, int doffset)
+{
+ uint* src = &table[0];
+
+ /* arrays */
+ for(int i = 0; i < num_arrays; i++) {
+ *(dest++) = *(src++);
+ *(dest++) = *(src++);
+ *(dest++) = *(src++) + doffset;
+ *(dest++) = *(src++) + doffset;
+ }
+
+ /* indices */
+ for(int i = 0; i < num_indices; i++) {
+ *(dest++) = *(src++);
+ }
+
+ /* params */
+ for(int i = 0; i < num_patches; i++) {
+ *(dest++) = *(src++);
+ *(dest++) = *(src++);
+ }
+
+ /* handles */
+ for(int i = 0; i < num_patches; i++) {
+ *(dest++) = *(src++) + doffset;
+ *(dest++) = *(src++) + doffset;
+ *(dest++) = *(src++);
+ }
+
+ /* nodes */
+ for(int i = 0; i < num_nodes; i++) {
+ *(dest++) = *(src++) + doffset;
+ }
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/subd/subd_patch_table.h b/intern/cycles/subd/subd_patch_table.h
new file mode 100644
index 00000000000..c8c7ecf9e47
--- /dev/null
+++ b/intern/cycles/subd/subd_patch_table.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SUBD_PATCH_TABLE_H__
+#define __SUBD_PATCH_TABLE_H__
+
+#include "util_types.h"
+#include "util_vector.h"
+
+#ifdef WITH_OPENSUBDIV
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include <opensubdiv/far/patchTable.h>
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef WITH_OPENSUBDIV
+using namespace OpenSubdiv;
+#else
+/* forward declare for when OpenSubdiv is unavailable */
+namespace Far { struct PatchTable; }
+#endif
+
+#define PATCH_ARRAY_SIZE 4
+#define PATCH_PARAM_SIZE 2
+#define PATCH_HANDLE_SIZE 3
+#define PATCH_NODE_SIZE 1
+
+struct PackedPatchTable {
+ vector<uint> table;
+
+ size_t num_arrays;
+ size_t num_indices;
+ size_t num_patches;
+ size_t num_nodes;
+
+ /* calculated size from num_* members */
+ size_t total_size();
+
+ void pack(Far::PatchTable* patch_table, int offset = 0);
+ void copy_adjusting_offsets(uint* dest, int doffset);
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __SUBD_PATCH_TABLE_H__ */
+
diff --git a/intern/cycles/subd/subd_split.cpp b/intern/cycles/subd/subd_split.cpp
index c4af8cc8c43..3c91ad8ab0d 100644
--- a/intern/cycles/subd/subd_split.cpp
+++ b/intern/cycles/subd/subd_split.cpp
@@ -40,12 +40,6 @@ void DiagSplit::dispatch(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef)
edgefactors_quad.push_back(ef);
}
-void DiagSplit::dispatch(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef)
-{
- subpatches_triangle.push_back(sub);
- edgefactors_triangle.push_back(ef);
-}
-
float3 DiagSplit::to_world(Patch *patch, float2 uv)
{
float3 P;
@@ -112,34 +106,6 @@ void DiagSplit::partition_edge(Patch *patch, float2 *P, int *t0, int *t1, float2
}
}
-static float2 right_to_equilateral(float2 P)
-{
- static const float2 A = make_float2(1.0f, 0.5f);
- static const float2 B = make_float2(0.0f, sinf(M_PI_F/3.0f));
- return make_float2(dot(P, A), dot(P, B));
-}
-
-static void limit_edge_factors(const TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int max_t)
-{
- float2 Pu = sub.Pu;
- float2 Pv = sub.Pv;
- float2 Pw = sub.Pw;
-
- if(sub.patch->is_triangle()) {
- Pu = right_to_equilateral(Pu);
- Pv = right_to_equilateral(Pv);
- Pw = right_to_equilateral(Pw);
- }
-
- int tu = int(max_t * len(Pw - Pv));
- int tv = int(max_t * len(Pw - Pu));
- int tw = int(max_t * len(Pv - Pu));
-
- ef.tu = tu <= 1 ? 1 : min(ef.tu, tu);
- ef.tv = tv <= 1 ? 1 : min(ef.tv, tv);
- ef.tw = tw <= 1 ? 1 : min(ef.tw, tw);
-}
-
static void limit_edge_factors(const QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int max_t)
{
float2 P00 = sub.P00;
@@ -147,13 +113,6 @@ static void limit_edge_factors(const QuadDice::SubPatch& sub, QuadDice::EdgeFact
float2 P10 = sub.P10;
float2 P11 = sub.P11;
- if(sub.patch->is_triangle()) {
- P00 = right_to_equilateral(P00);
- P01 = right_to_equilateral(P01);
- P10 = right_to_equilateral(P10);
- P11 = right_to_equilateral(P11);
- }
-
int tu0 = int(max_t * len(P10 - P00));
int tu1 = int(max_t * len(P11 - P01));
int tv0 = int(max_t * len(P01 - P00));
@@ -165,84 +124,6 @@ static void limit_edge_factors(const QuadDice::SubPatch& sub, QuadDice::EdgeFact
ef.tv1 = tv1 <= 1 ? 1 : min(ef.tv1, tv1);
}
-void DiagSplit::split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int depth)
-{
- if(depth > 32) {
- /* We should never get here, but just in case end recursion safely. */
- ef.tu = 1;
- ef.tv = 1;
- ef.tw = 1;
-
- dispatch(sub, ef);
- return;
- }
-
- assert(ef.tu == T(sub.patch, sub.Pv, sub.Pw));
- assert(ef.tv == T(sub.patch, sub.Pw, sub.Pu));
- assert(ef.tw == T(sub.patch, sub.Pu, sub.Pv));
-
- int non_uniform_count = int(ef.tu == DSPLIT_NON_UNIFORM) +
- int(ef.tv == DSPLIT_NON_UNIFORM) +
- int(ef.tw == DSPLIT_NON_UNIFORM);
-
- switch(non_uniform_count) {
- case 1: {
- /* TODO(mai): one edge is non-uniform, split into two triangles */
- // fallthru
- }
- case 2: {
- /* TODO(mai): two edges are non-uniform, split into triangle and quad */
- // fallthru
- }
- case 3: {
- /* all three edges are non-uniform, split into three quads */
-
- /* partition edges */
- QuadDice::EdgeFactors ef0, ef1, ef2;
- float2 Pu, Pv, Pw, Pcenter;
-
- partition_edge(sub.patch, &Pu, &ef1.tv0, &ef2.tu0, sub.Pw, sub.Pv, ef.tu);
- partition_edge(sub.patch, &Pv, &ef0.tv0, &ef1.tu0, sub.Pu, sub.Pw, ef.tv);
- partition_edge(sub.patch, &Pw, &ef2.tv0, &ef0.tu0, sub.Pv, sub.Pu, ef.tw);
- Pcenter = (Pu + Pv + Pw) * (1.0f / 3.0f);
-
- /* split */
- int tsplit01 = T(sub.patch, Pv, Pcenter);
- int tsplit12 = T(sub.patch, Pu, Pcenter);
- int tsplit20 = T(sub.patch, Pw, Pcenter);
-
- ef0.tu1 = tsplit01;
- ef0.tv1 = tsplit20;
-
- ef1.tu1 = tsplit12;
- ef1.tv1 = tsplit01;
-
- ef2.tu1 = tsplit20;
- ef2.tv1 = tsplit12;
-
- /* create subpatches */
- QuadDice::SubPatch sub0 = {sub.patch, sub.Pu, Pw, Pv, Pcenter};
- QuadDice::SubPatch sub1 = {sub.patch, sub.Pw, Pv, Pu, Pcenter};
- QuadDice::SubPatch sub2 = {sub.patch, sub.Pv, Pu, Pw, Pcenter};
-
- limit_edge_factors(sub0, ef0, 1 << params.max_level);
- limit_edge_factors(sub1, ef1, 1 << params.max_level);
- limit_edge_factors(sub2, ef2, 1 << params.max_level);
-
- split(sub0, ef0, depth+1);
- split(sub1, ef1, depth+1);
- split(sub2, ef2, depth+1);
-
- break;
- }
- default: {
- /* all edges uniform, no splitting needed */
- dispatch(sub, ef);
- break;
- }
- }
-}
-
void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int depth)
{
if(depth > 32) {
@@ -259,6 +140,16 @@ void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int de
bool split_u = (ef.tu0 == DSPLIT_NON_UNIFORM || ef.tu1 == DSPLIT_NON_UNIFORM);
bool split_v = (ef.tv0 == DSPLIT_NON_UNIFORM || ef.tv1 == DSPLIT_NON_UNIFORM);
+ /* Split subpatches such that the ratio of T for opposite edges doesn't
+ * exceed 1.5, this reduces over tessellation for some patches
+ */
+ bool tmp_split_v = split_v;
+ if(!split_u && min(ef.tu0, ef.tu1) > 8 && min(ef.tu0, ef.tu1)*1.5f < max(ef.tu0, ef.tu1))
+ split_v = true;
+ if(!tmp_split_v && min(ef.tu0, ef.tu1) > 8 && min(ef.tv0, ef.tv1)*1.5f < max(ef.tv0, ef.tv1))
+ split_u = true;
+
+ /* alternate axis */
if(split_u && split_v) {
split_u = depth % 2;
}
@@ -324,69 +215,21 @@ void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int de
}
}
-void DiagSplit::split_triangle(Patch *patch)
-{
- TriangleDice::SubPatch sub_split;
- TriangleDice::EdgeFactors ef_split;
-
- sub_split.patch = patch;
- sub_split.Pu = make_float2(1.0f, 0.0f);
- sub_split.Pv = make_float2(0.0f, 1.0f);
- sub_split.Pw = make_float2(0.0f, 0.0f);
-
- ef_split.tu = T(patch, sub_split.Pv, sub_split.Pw);
- ef_split.tv = T(patch, sub_split.Pw, sub_split.Pu);
- ef_split.tw = T(patch, sub_split.Pu, sub_split.Pv);
-
- limit_edge_factors(sub_split, ef_split, 1 << params.max_level);
-
- split(sub_split, ef_split);
-
- TriangleDice dice(params);
-
- for(size_t i = 0; i < subpatches_triangle.size(); i++) {
- TriangleDice::SubPatch& sub = subpatches_triangle[i];
- TriangleDice::EdgeFactors& ef = edgefactors_triangle[i];
-
- ef.tu = max(ef.tu, 1);
- ef.tv = max(ef.tv, 1);
- ef.tw = max(ef.tw, 1);
-
- dice.dice(sub, ef);
- }
-
- subpatches_triangle.clear();
- edgefactors_triangle.clear();
-
- /* triangle might be split into quads so dice quad subpatches as well */
- QuadDice qdice(params);
-
- for(size_t i = 0; i < subpatches_quad.size(); i++) {
- QuadDice::SubPatch& sub = subpatches_quad[i];
- QuadDice::EdgeFactors& ef = edgefactors_quad[i];
-
- ef.tu0 = max(ef.tu0, 1);
- ef.tu1 = max(ef.tu1, 1);
- ef.tv0 = max(ef.tv0, 1);
- ef.tv1 = max(ef.tv1, 1);
-
- qdice.dice(sub, ef);
- }
-
- subpatches_quad.clear();
- edgefactors_quad.clear();
-}
-
-void DiagSplit::split_quad(Patch *patch)
+void DiagSplit::split_quad(Patch *patch, QuadDice::SubPatch *subpatch)
{
QuadDice::SubPatch sub_split;
QuadDice::EdgeFactors ef_split;
- sub_split.patch = patch;
- sub_split.P00 = make_float2(0.0f, 0.0f);
- sub_split.P10 = make_float2(1.0f, 0.0f);
- sub_split.P01 = make_float2(0.0f, 1.0f);
- sub_split.P11 = make_float2(1.0f, 1.0f);
+ if(subpatch) {
+ sub_split = *subpatch;
+ }
+ else {
+ sub_split.patch = patch;
+ sub_split.P00 = make_float2(0.0f, 0.0f);
+ sub_split.P10 = make_float2(1.0f, 0.0f);
+ sub_split.P01 = make_float2(0.0f, 1.0f);
+ sub_split.P11 = make_float2(1.0f, 1.0f);
+ }
ef_split.tu0 = T(patch, sub_split.P00, sub_split.P10);
ef_split.tu1 = T(patch, sub_split.P01, sub_split.P11);
diff --git a/intern/cycles/subd/subd_split.h b/intern/cycles/subd/subd_split.h
index bbe921f739c..a2f76dd2e03 100644
--- a/intern/cycles/subd/subd_split.h
+++ b/intern/cycles/subd/subd_split.h
@@ -38,8 +38,6 @@ class DiagSplit {
public:
vector<QuadDice::SubPatch> subpatches_quad;
vector<QuadDice::EdgeFactors> edgefactors_quad;
- vector<TriangleDice::SubPatch> subpatches_triangle;
- vector<TriangleDice::EdgeFactors> edgefactors_triangle;
SubdParams params;
@@ -53,11 +51,7 @@ public:
void dispatch(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef);
void split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int depth=0);
- void dispatch(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef);
- void split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int depth=0);
-
- void split_triangle(Patch *patch);
- void split_quad(Patch *patch);
+ void split_quad(Patch *patch, QuadDice::SubPatch *subpatch=NULL);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt
index 2f3a4d0b1df..9af777fb9dd 100644
--- a/intern/cycles/test/CMakeLists.txt
+++ b/intern/cycles/test/CMakeLists.txt
@@ -14,9 +14,48 @@ endmacro()
set(INC
.
..
+ ../device
+ ../graph
+ ../kernel
+ ../render
../util
)
+set(ALL_CYCLES_LIBRARIES
+ cycles_render
+ cycles_device
+ cycles_bvh
+ cycles_graph
+ cycles_subd
+ cycles_util
+ ${OPENIMAGEIO_LIBRARIES}
+)
+if(WITH_CYCLES_OSL)
+ list(APPEND ALL_CYCLES_LIBRARIES
+ cycles_kernel_osl
+ ${OSL_LIBRARIES}
+ ${LLVM_LIBRARIES}
+ )
+endif()
+if(WITH_IMAGE_OPENJPEG AND NOT WITH_SYSTEM_OPENJPEG)
+ list(APPEND ALL_CYCLES_LIBRARIES
+ extern_openjpeg
+ )
+endif()
+if(WITH_CYCLES_OPENSUBDIV)
+ add_definitions(-DWITH_OPENSUBDIV)
+ include_directories(
+ SYSTEM
+ ${OPENSUBDIV_INCLUDE_DIR}
+ )
+ list(APPEND ALL_CYCLES_LIBRARIES
+ ${OPENSUBDIV_LIBRARIES}
+ )
+endif()
+list(APPEND ALL_CYCLES_LIBRARIES
+ ${BOOST_LIBRARIES}
+)
+
include_directories(${INC})
link_directories(${BOOST_LIBPATH})
@@ -25,6 +64,7 @@ link_directories(${OPENIMAGEIO_LIBPATH})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
+CYCLES_TEST(render_graph_finalize "${ALL_CYCLES_LIBRARIES}")
CYCLES_TEST(util_aligned_malloc "cycles_util")
CYCLES_TEST(util_path "cycles_util;${BOOST_LIBRARIES};${OPENIMAGEIO_LIBRARIES}")
CYCLES_TEST(util_string "cycles_util;${BOOST_LIBRARIES}")
diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp
new file mode 100644
index 00000000000..633e517ce9f
--- /dev/null
+++ b/intern/cycles/test/render_graph_finalize_test.cpp
@@ -0,0 +1,1532 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testing/testing.h"
+#include "testing/mock_log.h"
+
+#include "render/graph.h"
+#include "render/scene.h"
+#include "render/nodes.h"
+#include "util/util_logging.h"
+#include "util/util_string.h"
+#include "util/util_vector.h"
+
+using testing::AnyNumber;
+using testing::HasSubstr;
+using testing::ScopedMockLog;
+using testing::_;
+
+CCL_NAMESPACE_BEGIN
+
+namespace {
+
+template<typename T>
+class ShaderNodeBuilder {
+public:
+ ShaderNodeBuilder(const string& name)
+ : name_(name)
+ {
+ node_ = new T();
+ node_->name = name;
+ }
+
+ const string& name() const {
+ return name_;
+ }
+
+ ShaderNode *node() const {
+ return node_;
+ }
+
+ template<typename V>
+ ShaderNodeBuilder& set(const string& input_name, V value)
+ {
+ ShaderInput *input_socket = node_->input(input_name.c_str());
+ EXPECT_NE((void*)NULL, input_socket);
+ input_socket->set(value);
+ return *this;
+ }
+
+ template<typename T2, typename V>
+ ShaderNodeBuilder& set(V T2::*pfield, V value)
+ {
+ static_cast<T*>(node_)->*pfield = value;
+ return *this;
+ }
+
+protected:
+ string name_;
+ ShaderNode *node_;
+};
+
+class ShaderGraphBuilder {
+public:
+ explicit ShaderGraphBuilder(ShaderGraph *graph)
+ : graph_(graph)
+ {
+ node_map_["Output"] = graph->output();
+ }
+
+ ShaderNode *find_node(const string& name)
+ {
+ map<string, ShaderNode *>::iterator it = node_map_.find(name);
+ if(it == node_map_.end()) {
+ return NULL;
+ }
+ return it->second;
+ }
+
+ template<typename T>
+ ShaderGraphBuilder& add_node(const T& node)
+ {
+ EXPECT_EQ(NULL, find_node(node.name()));
+ graph_->add(node.node());
+ node_map_[node.name()] = node.node();
+ return *this;
+ }
+
+ ShaderGraphBuilder& add_connection(const string& from,
+ const string& to)
+ {
+ vector<string> tokens_from, tokens_to;
+ string_split(tokens_from, from, "::");
+ string_split(tokens_to, to, "::");
+ EXPECT_EQ(2, tokens_from.size());
+ EXPECT_EQ(2, tokens_to.size());
+ ShaderNode *node_from = find_node(tokens_from[0]),
+ *node_to = find_node(tokens_to[0]);
+ EXPECT_NE((void*)NULL, node_from);
+ EXPECT_NE((void*)NULL, node_to);
+ EXPECT_NE(node_from, node_to);
+ ShaderOutput *socket_from = node_from->output(tokens_from[1].c_str());
+ ShaderInput *socket_to = node_to->input(tokens_to[1].c_str());
+ EXPECT_NE((void*)NULL, socket_from);
+ EXPECT_NE((void*)NULL, socket_to);
+ graph_->connect(socket_from, socket_to);
+ return *this;
+ }
+
+ /* Common input/output boilerplate. */
+ ShaderGraphBuilder& add_attribute(const string &name)
+ {
+ return (*this)
+ .add_node(ShaderNodeBuilder<AttributeNode>(name)
+ .set(&AttributeNode::attribute, ustring(name)));
+ }
+
+ ShaderGraphBuilder& output_closure(const string& from)
+ {
+ return (*this).add_connection(from, "Output::Surface");
+ }
+
+ ShaderGraphBuilder& output_color(const string& from)
+ {
+ return (*this)
+ .add_node(ShaderNodeBuilder<EmissionNode>("EmissionNode"))
+ .add_connection(from, "EmissionNode::Color")
+ .output_closure("EmissionNode::Emission");
+ }
+
+ ShaderGraphBuilder& output_value(const string& from)
+ {
+ return (*this)
+ .add_node(ShaderNodeBuilder<EmissionNode>("EmissionNode"))
+ .add_connection(from, "EmissionNode::Strength")
+ .output_closure("EmissionNode::Emission");
+ }
+
+protected:
+ ShaderGraph *graph_;
+ map<string, ShaderNode *> node_map_;
+};
+
+} // namespace
+
+#define DEFINE_COMMON_VARIABLES(builder_name, mock_log_name) \
+ util_logging_start(); \
+ util_logging_verbosity_set(1); \
+ ScopedMockLog mock_log_name; \
+ DeviceInfo device_info; \
+ SceneParams scene_params; \
+ Scene scene(scene_params, device_info); \
+ ShaderGraph graph; \
+ ShaderGraphBuilder builder(&graph); \
+
+#define EXPECT_ANY_MESSAGE(log) \
+ EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber()); \
+
+#define CORRECT_INFO_MESSAGE(log, message) \
+ EXPECT_CALL(log, Log(google::INFO, _, HasSubstr(message)));
+
+#define INVALID_INFO_MESSAGE(log, message) \
+ EXPECT_CALL(log, Log(google::INFO, _, HasSubstr(message))).Times(0);
+
+/*
+ * Test deduplication of nodes that have inputs, some of them folded.
+ */
+TEST(render_graph, deduplicate_deep)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Value1::Value to constant (0.8).");
+ CORRECT_INFO_MESSAGE(log, "Folding Value2::Value to constant (0.8).");
+ CORRECT_INFO_MESSAGE(log, "Deduplicated 2 nodes.");
+
+ builder
+ .add_node(ShaderNodeBuilder<GeometryNode>("Geometry1"))
+ .add_node(ShaderNodeBuilder<GeometryNode>("Geometry2"))
+ .add_node(ShaderNodeBuilder<ValueNode>("Value1")
+ .set(&ValueNode::value, 0.8f))
+ .add_node(ShaderNodeBuilder<ValueNode>("Value2")
+ .set(&ValueNode::value, 0.8f))
+ .add_node(ShaderNodeBuilder<NoiseTextureNode>("Noise1"))
+ .add_node(ShaderNodeBuilder<NoiseTextureNode>("Noise2"))
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_BLEND)
+ .set("Fac", 0.5f))
+ .add_connection("Geometry1::Parametric", "Noise1::Vector")
+ .add_connection("Value1::Value", "Noise1::Scale")
+ .add_connection("Noise1::Color", "Mix::Color1")
+ .add_connection("Geometry2::Parametric", "Noise2::Vector")
+ .add_connection("Value2::Value", "Noise2::Scale")
+ .add_connection("Noise2::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+
+ EXPECT_EQ(graph.nodes.size(), 5);
+}
+
+/*
+ * Test RGB to BW node.
+ */
+TEST(render_graph, constant_fold_rgb_to_bw)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding RGBToBWNodeNode::Val to constant (0.8).");
+ CORRECT_INFO_MESSAGE(log, "Folding convert_float_to_color::value_color to constant (0.8, 0.8, 0.8).");
+
+ builder
+ .add_node(ShaderNodeBuilder<RGBToBWNode>("RGBToBWNodeNode")
+ .set("Color", make_float3(0.8f, 0.8f, 0.8f)))
+ .output_color("RGBToBWNodeNode::Val");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - folding of Emission nodes that don't emit to nothing.
+ */
+TEST(render_graph, constant_fold_emission1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Discarding closure Emission.");
+
+ builder
+ .add_node(ShaderNodeBuilder<EmissionNode>("Emission")
+ .set("Color", make_float3(0.0f, 0.0f, 0.0f)))
+ .output_closure("Emission::Emission");
+
+ graph.finalize(&scene);
+}
+
+TEST(render_graph, constant_fold_emission2)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Discarding closure Emission.");
+
+ builder
+ .add_node(ShaderNodeBuilder<EmissionNode>("Emission")
+ .set("Strength", 0.0f))
+ .output_closure("Emission::Emission");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - folding of Background nodes that don't emit to nothing.
+ */
+TEST(render_graph, constant_fold_background1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Discarding closure Background.");
+
+ builder
+ .add_node(ShaderNodeBuilder<BackgroundNode>("Background")
+ .set("Color", make_float3(0.0f, 0.0f, 0.0f)))
+ .output_closure("Background::Background");
+
+ graph.finalize(&scene);
+}
+
+TEST(render_graph, constant_fold_background2)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Discarding closure Background.");
+
+ builder
+ .add_node(ShaderNodeBuilder<BackgroundNode>("Background")
+ .set("Strength", 0.0f))
+ .output_closure("Background::Background");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Add Closure with only one input.
+ */
+TEST(render_graph, constant_fold_shader_add)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding AddClosure1::Closure to socket Diffuse::BSDF.");
+ CORRECT_INFO_MESSAGE(log, "Folding AddClosure2::Closure to socket Diffuse::BSDF.");
+ INVALID_INFO_MESSAGE(log, "Folding AddClosure3");
+
+ builder
+ .add_node(ShaderNodeBuilder<DiffuseBsdfNode>("Diffuse"))
+ .add_node(ShaderNodeBuilder<AddClosureNode>("AddClosure1"))
+ .add_node(ShaderNodeBuilder<AddClosureNode>("AddClosure2"))
+ .add_node(ShaderNodeBuilder<AddClosureNode>("AddClosure3"))
+ .add_connection("Diffuse::BSDF", "AddClosure1::Closure1")
+ .add_connection("Diffuse::BSDF", "AddClosure2::Closure2")
+ .add_connection("AddClosure1::Closure", "AddClosure3::Closure1")
+ .add_connection("AddClosure2::Closure", "AddClosure3::Closure2")
+ .output_closure("AddClosure3::Closure");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Mix Closure with 0 or 1 fac.
+ * - Folding of Mix Closure with both inputs folded to the same node.
+ */
+TEST(render_graph, constant_fold_shader_mix)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding MixClosure1::Closure to socket Diffuse::BSDF.");
+ CORRECT_INFO_MESSAGE(log, "Folding MixClosure2::Closure to socket Diffuse::BSDF.");
+ CORRECT_INFO_MESSAGE(log, "Folding MixClosure3::Closure to socket Diffuse::BSDF.");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<DiffuseBsdfNode>("Diffuse"))
+ /* choose left */
+ .add_node(ShaderNodeBuilder<MixClosureNode>("MixClosure1")
+ .set("Fac", 0.0f))
+ .add_connection("Diffuse::BSDF", "MixClosure1::Closure1")
+ /* choose right */
+ .add_node(ShaderNodeBuilder<MixClosureNode>("MixClosure2")
+ .set("Fac", 1.0f))
+ .add_connection("Diffuse::BSDF", "MixClosure2::Closure2")
+ /* both inputs folded the same */
+ .add_node(ShaderNodeBuilder<MixClosureNode>("MixClosure3"))
+ .add_connection("Attribute::Fac", "MixClosure3::Fac")
+ .add_connection("MixClosure1::Closure", "MixClosure3::Closure1")
+ .add_connection("MixClosure2::Closure", "MixClosure3::Closure2")
+ .output_closure("MixClosure3::Closure");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Invert with all constant inputs.
+ */
+TEST(render_graph, constant_fold_invert)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to constant (0.68, 0.5, 0.32).");
+
+ builder
+ .add_node(ShaderNodeBuilder<InvertNode>("Invert")
+ .set("Fac", 0.8f)
+ .set("Color", make_float3(0.2f, 0.5f, 0.8f)))
+ .output_color("Invert::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Invert with zero Fac.
+ */
+TEST(render_graph, constant_fold_invert_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to socket Attribute::Color.");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<InvertNode>("Invert")
+ .set("Fac", 0.0f))
+ .add_connection("Attribute::Color", "Invert::Color")
+ .output_color("Invert::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of MixRGB Add with all constant inputs (clamp false).
+ */
+TEST(render_graph, constant_fold_mix_add)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding MixAdd::Color to constant (0.62, 1.14, 1.42).");
+
+ builder
+ .add_node(ShaderNodeBuilder<MixNode>("MixAdd")
+ .set(&MixNode::type, NODE_MIX_ADD)
+ .set(&MixNode::use_clamp, false)
+ .set("Fac", 0.8f)
+ .set("Color1", make_float3(0.3, 0.5, 0.7))
+ .set("Color2", make_float3(0.4, 0.8, 0.9)))
+ .output_color("MixAdd::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of MixRGB Add with all constant inputs (clamp true).
+ */
+TEST(render_graph, constant_fold_mix_add_clamp)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding MixAdd::Color to constant (0.62, 1, 1).");
+
+ builder
+ .add_node(ShaderNodeBuilder<MixNode>("MixAdd")
+ .set(&MixNode::type, NODE_MIX_ADD)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 0.8f)
+ .set("Color1", make_float3(0.3, 0.5, 0.7))
+ .set("Color2", make_float3(0.4, 0.8, 0.9)))
+ .output_color("MixAdd::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - No folding on fac 0 for dodge.
+ */
+TEST(render_graph, constant_fold_part_mix_dodge_no_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding ");
+
+ builder
+ .add_attribute("Attribute1")
+ .add_attribute("Attribute2")
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_DODGE)
+ .set(&MixNode::use_clamp, false)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute1::Color", "Mix::Color1")
+ .add_connection("Attribute2::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - No folding on fac 0 for light.
+ */
+TEST(render_graph, constant_fold_part_mix_light_no_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding ");
+
+ builder
+ .add_attribute("Attribute1")
+ .add_attribute("Attribute2")
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_LIGHT)
+ .set(&MixNode::use_clamp, false)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute1::Color", "Mix::Color1")
+ .add_connection("Attribute2::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - No folding on fac 0 for burn.
+ */
+TEST(render_graph, constant_fold_part_mix_burn_no_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding ");
+
+ builder
+ .add_attribute("Attribute1")
+ .add_attribute("Attribute2")
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_BURN)
+ .set(&MixNode::use_clamp, false)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute1::Color", "Mix::Color1")
+ .add_connection("Attribute2::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - No folding on fac 0 for clamped blend.
+ */
+TEST(render_graph, constant_fold_part_mix_blend_clamped_no_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding ");
+
+ builder
+ .add_attribute("Attribute1")
+ .add_attribute("Attribute2")
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_BLEND)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute1::Color", "Mix::Color1")
+ .add_connection("Attribute2::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Mix with 0 or 1 Fac.
+ * - Folding of Mix with both inputs folded to the same node.
+ */
+TEST(render_graph, constant_fold_part_mix_blend)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding MixBlend1::Color to socket Attribute1::Color.");
+ CORRECT_INFO_MESSAGE(log, "Folding MixBlend2::Color to socket Attribute1::Color.");
+ CORRECT_INFO_MESSAGE(log, "Folding MixBlend3::Color to socket Attribute1::Color.");
+
+ builder
+ .add_attribute("Attribute1")
+ .add_attribute("Attribute2")
+ /* choose left */
+ .add_node(ShaderNodeBuilder<MixNode>("MixBlend1")
+ .set(&MixNode::type, NODE_MIX_BLEND)
+ .set(&MixNode::use_clamp, false)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute1::Color", "MixBlend1::Color1")
+ .add_connection("Attribute2::Color", "MixBlend1::Color2")
+ /* choose right */
+ .add_node(ShaderNodeBuilder<MixNode>("MixBlend2")
+ .set(&MixNode::type, NODE_MIX_BLEND)
+ .set(&MixNode::use_clamp, false)
+ .set("Fac", 1.0f))
+ .add_connection("Attribute1::Color", "MixBlend2::Color2")
+ .add_connection("Attribute2::Color", "MixBlend2::Color1")
+ /* both inputs folded to Attribute1 */
+ .add_node(ShaderNodeBuilder<MixNode>("MixBlend3")
+ .set(&MixNode::type, NODE_MIX_BLEND)
+ .set(&MixNode::use_clamp, false))
+ .add_connection("Attribute1::Fac", "MixBlend3::Fac")
+ .add_connection("MixBlend1::Color", "MixBlend3::Color1")
+ .add_connection("MixBlend2::Color", "MixBlend3::Color2")
+ .output_color("MixBlend3::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - NOT folding of MixRGB Sub with the same inputs and fac NOT 1.
+ */
+TEST(render_graph, constant_fold_part_mix_sub_same_fac_bad)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding Mix::");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_SUB)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 0.5f))
+ .add_connection("Attribute::Color", "Mix::Color1")
+ .add_connection("Attribute::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of MixRGB Sub with the same inputs and fac 1.
+ */
+TEST(render_graph, constant_fold_part_mix_sub_same_fac_1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Mix::Color to constant (0, 0, 0).");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_SUB)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 1.0f))
+ .add_connection("Attribute::Color", "Mix::Color1")
+ .add_connection("Attribute::Color", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Graph for testing partial folds of MixRGB with one constant argument.
+ * Includes 4 tests: constant on each side with fac either unknown or 1.
+ */
+static void build_mix_partial_test_graph(ShaderGraphBuilder &builder, NodeMix type, float3 constval)
+{
+ builder
+ .add_attribute("Attribute")
+ /* constant on the left */
+ .add_node(ShaderNodeBuilder<MixNode>("Mix_Cx_Fx")
+ .set(&MixNode::type, type)
+ .set(&MixNode::use_clamp, false)
+ .set("Color1", constval))
+ .add_node(ShaderNodeBuilder<MixNode>("Mix_Cx_F1")
+ .set(&MixNode::type, type)
+ .set(&MixNode::use_clamp, false)
+ .set("Color1", constval)
+ .set("Fac", 1.0f))
+ .add_connection("Attribute::Fac", "Mix_Cx_Fx::Fac")
+ .add_connection("Attribute::Color", "Mix_Cx_Fx::Color2")
+ .add_connection("Attribute::Color", "Mix_Cx_F1::Color2")
+ /* constant on the right */
+ .add_node(ShaderNodeBuilder<MixNode>("Mix_xC_Fx")
+ .set(&MixNode::type, type)
+ .set(&MixNode::use_clamp, false)
+ .set("Color2", constval))
+ .add_node(ShaderNodeBuilder<MixNode>("Mix_xC_F1")
+ .set(&MixNode::type, type)
+ .set(&MixNode::use_clamp, false)
+ .set("Color2", constval)
+ .set("Fac", 1.0f))
+ .add_connection("Attribute::Fac", "Mix_xC_Fx::Fac")
+ .add_connection("Attribute::Color", "Mix_xC_Fx::Color1")
+ .add_connection("Attribute::Color", "Mix_xC_F1::Color1")
+ /* results of actual tests simply added up to connect to output */
+ .add_node(ShaderNodeBuilder<MixNode>("Out12")
+ .set(&MixNode::type, NODE_MIX_ADD)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 1.0f))
+ .add_node(ShaderNodeBuilder<MixNode>("Out34")
+ .set(&MixNode::type, NODE_MIX_ADD)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 1.0f))
+ .add_node(ShaderNodeBuilder<MixNode>("Out1234")
+ .set(&MixNode::type, NODE_MIX_ADD)
+ .set(&MixNode::use_clamp, true)
+ .set("Fac", 1.0f))
+ .add_connection("Mix_Cx_Fx::Color", "Out12::Color1")
+ .add_connection("Mix_Cx_F1::Color", "Out12::Color2")
+ .add_connection("Mix_xC_Fx::Color", "Out34::Color1")
+ .add_connection("Mix_xC_F1::Color", "Out34::Color2")
+ .add_connection("Out12::Color", "Out1234::Color1")
+ .add_connection("Out34::Color", "Out1234::Color2")
+ .output_color("Out1234::Color");
+}
+
+/*
+ * Tests: partial folding for RGB Add with known 0.
+ */
+TEST(render_graph, constant_fold_part_mix_add_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* 0 + X (fac 1) == X */
+ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to socket Attribute::Color.");
+ /* X + 0 (fac ?) == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color.");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color.");
+ INVALID_INFO_MESSAGE(log, "Folding Out");
+
+ build_mix_partial_test_graph(builder, NODE_MIX_ADD, make_float3(0, 0, 0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for RGB Sub with known 0.
+ */
+TEST(render_graph, constant_fold_part_mix_sub_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color");
+ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color");
+ /* X - 0 (fac ?) == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color.");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color.");
+ INVALID_INFO_MESSAGE(log, "Folding Out");
+
+ build_mix_partial_test_graph(builder, NODE_MIX_SUB, make_float3(0, 0, 0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for RGB Mul with known 1.
+ */
+TEST(render_graph, constant_fold_part_mix_mul_1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* 1 * X (fac 1) == X */
+ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to socket Attribute::Color.");
+ /* X * 1 (fac ?) == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color.");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color.");
+ INVALID_INFO_MESSAGE(log, "Folding Out");
+
+ build_mix_partial_test_graph(builder, NODE_MIX_MUL, make_float3(1, 1, 1));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for RGB Div with known 1.
+ */
+TEST(render_graph, constant_fold_part_mix_div_1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color");
+ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color");
+ /* X / 1 (fac ?) == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color.");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color.");
+ INVALID_INFO_MESSAGE(log, "Folding Out");
+
+ build_mix_partial_test_graph(builder, NODE_MIX_DIV, make_float3(1, 1, 1));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for RGB Mul with known 0.
+ */
+TEST(render_graph, constant_fold_part_mix_mul_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* 0 * ? (fac ?) == 0 */
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to constant (0, 0, 0).");
+ /* ? * 0 (fac 1) == 0 */
+ INVALID_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to constant (0, 0, 0).");
+
+ CORRECT_INFO_MESSAGE(log, "Folding Out12::Color to constant (0, 0, 0).");
+ INVALID_INFO_MESSAGE(log, "Folding Out1234");
+
+ build_mix_partial_test_graph(builder, NODE_MIX_MUL, make_float3(0, 0, 0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for RGB Div with known 0.
+ */
+TEST(render_graph, constant_fold_part_mix_div_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* 0 / ? (fac ?) == 0 */
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to constant (0, 0, 0).");
+ INVALID_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color");
+ INVALID_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color");
+
+ CORRECT_INFO_MESSAGE(log, "Folding Out12::Color to constant (0, 0, 0).");
+ INVALID_INFO_MESSAGE(log, "Folding Out1234");
+
+ build_mix_partial_test_graph(builder, NODE_MIX_DIV, make_float3(0, 0, 0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Separate/Combine RGB with all constant inputs.
+ */
+TEST(render_graph, constant_fold_separate_combine_rgb)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateRGB::R to constant (0.3).");
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateRGB::G to constant (0.5).");
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateRGB::B to constant (0.7).");
+ CORRECT_INFO_MESSAGE(log, "Folding CombineRGB::Image to constant (0.3, 0.5, 0.7).");
+
+ builder
+ .add_node(ShaderNodeBuilder<SeparateRGBNode>("SeparateRGB")
+ .set("Image", make_float3(0.3f, 0.5f, 0.7f)))
+ .add_node(ShaderNodeBuilder<CombineRGBNode>("CombineRGB"))
+ .add_connection("SeparateRGB::R", "CombineRGB::R")
+ .add_connection("SeparateRGB::G", "CombineRGB::G")
+ .add_connection("SeparateRGB::B", "CombineRGB::B")
+ .output_color("CombineRGB::Image");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Separate/Combine XYZ with all constant inputs.
+ */
+TEST(render_graph, constant_fold_separate_combine_xyz)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateXYZ::X to constant (0.3).");
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateXYZ::Y to constant (0.5).");
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateXYZ::Z to constant (0.7).");
+ CORRECT_INFO_MESSAGE(log, "Folding CombineXYZ::Vector to constant (0.3, 0.5, 0.7).");
+ CORRECT_INFO_MESSAGE(log, "Folding convert_vector_to_color::value_color to constant (0.3, 0.5, 0.7).");
+
+ builder
+ .add_node(ShaderNodeBuilder<SeparateXYZNode>("SeparateXYZ")
+ .set("Vector", make_float3(0.3f, 0.5f, 0.7f)))
+ .add_node(ShaderNodeBuilder<CombineXYZNode>("CombineXYZ"))
+ .add_connection("SeparateXYZ::X", "CombineXYZ::X")
+ .add_connection("SeparateXYZ::Y", "CombineXYZ::Y")
+ .add_connection("SeparateXYZ::Z", "CombineXYZ::Z")
+ .output_color("CombineXYZ::Vector");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Separate/Combine HSV with all constant inputs.
+ */
+TEST(render_graph, constant_fold_separate_combine_hsv)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateHSV::H to constant (0.583333).");
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateHSV::S to constant (0.571429).");
+ CORRECT_INFO_MESSAGE(log, "Folding SeparateHSV::V to constant (0.7).");
+ CORRECT_INFO_MESSAGE(log, "Folding CombineHSV::Color to constant (0.3, 0.5, 0.7).");
+
+ builder
+ .add_node(ShaderNodeBuilder<SeparateHSVNode>("SeparateHSV")
+ .set("Color", make_float3(0.3f, 0.5f, 0.7f)))
+ .add_node(ShaderNodeBuilder<CombineHSVNode>("CombineHSV"))
+ .add_connection("SeparateHSV::H", "CombineHSV::H")
+ .add_connection("SeparateHSV::S", "CombineHSV::S")
+ .add_connection("SeparateHSV::V", "CombineHSV::V")
+ .output_color("CombineHSV::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Gamma with all constant inputs.
+ */
+TEST(render_graph, constant_fold_gamma)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Gamma::Color to constant (0.164317, 0.353553, 0.585662).");
+
+ builder
+ .add_node(ShaderNodeBuilder<GammaNode>("Gamma")
+ .set("Color", make_float3(0.3f, 0.5f, 0.7f))
+ .set("Gamma", 1.5f))
+ .output_color("Gamma::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: BrightnessContrast with all constant inputs.
+ */
+TEST(render_graph, constant_fold_bright_contrast)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding BrightContrast::Color to constant (0.16, 0.6, 1.04).");
+
+ builder
+ .add_node(ShaderNodeBuilder<BrightContrastNode>("BrightContrast")
+ .set("Color", make_float3(0.3f, 0.5f, 0.7f))
+ .set("Bright", 0.1f)
+ .set("Contrast", 1.2f))
+ .output_color("BrightContrast::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: blackbody with all constant inputs.
+ */
+TEST(render_graph, constant_fold_blackbody)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Blackbody::Color to constant (3.94163, 0.226523, 0).");
+
+ builder
+ .add_node(ShaderNodeBuilder<BlackbodyNode>("Blackbody")
+ .set("Temperature", 1200.0f))
+ .output_color("Blackbody::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Math with all constant inputs (clamp false).
+ */
+TEST(render_graph, constant_fold_math)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Math::Value to constant (1.6).");
+
+ builder
+ .add_node(ShaderNodeBuilder<MathNode>("Math")
+ .set(&MathNode::type, NODE_MATH_ADD)
+ .set(&MathNode::use_clamp, false)
+ .set("Value1", 0.7f)
+ .set("Value2", 0.9f))
+ .output_value("Math::Value");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Math with all constant inputs (clamp true).
+ */
+TEST(render_graph, constant_fold_math_clamp)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Math::Value to constant (1).");
+
+ builder
+ .add_node(ShaderNodeBuilder<MathNode>("Math")
+ .set(&MathNode::type, NODE_MATH_ADD)
+ .set(&MathNode::use_clamp, true)
+ .set("Value1", 0.7f)
+ .set("Value2", 0.9f))
+ .output_value("Math::Value");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Graph for testing partial folds of Math with one constant argument.
+ * Includes 2 tests: constant on each side.
+ */
+static void build_math_partial_test_graph(ShaderGraphBuilder &builder, NodeMath type, float constval)
+{
+ builder
+ .add_attribute("Attribute")
+ /* constant on the left */
+ .add_node(ShaderNodeBuilder<MathNode>("Math_Cx")
+ .set(&MathNode::type, type)
+ .set(&MathNode::use_clamp, false)
+ .set("Value1", constval))
+ .add_connection("Attribute::Fac", "Math_Cx::Value2")
+ /* constant on the right */
+ .add_node(ShaderNodeBuilder<MathNode>("Math_xC")
+ .set(&MathNode::type, type)
+ .set(&MathNode::use_clamp, false)
+ .set("Value2", constval))
+ .add_connection("Attribute::Fac", "Math_xC::Value1")
+ /* output sum */
+ .add_node(ShaderNodeBuilder<MathNode>("Out")
+ .set(&MathNode::type, NODE_MATH_ADD)
+ .set(&MathNode::use_clamp, true))
+ .add_connection("Math_Cx::Value", "Out::Value1")
+ .add_connection("Math_xC::Value", "Out::Value2")
+ .output_value("Out::Value");
+}
+
+/*
+ * Tests: partial folding for Math Add with known 0.
+ */
+TEST(render_graph, constant_fold_part_math_add_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X + 0 == 0 + X == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to socket Attribute::Fac.");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac.");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_math_partial_test_graph(builder, NODE_MATH_ADD, 0.0f);
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Math Sub with known 0.
+ */
+TEST(render_graph, constant_fold_part_math_sub_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X - 0 == X */
+ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac.");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_math_partial_test_graph(builder, NODE_MATH_SUBTRACT, 0.0f);
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Math Mul with known 1.
+ */
+TEST(render_graph, constant_fold_part_math_mul_1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X * 1 == 1 * X == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to socket Attribute::Fac.");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac.");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_math_partial_test_graph(builder, NODE_MATH_MULTIPLY, 1.0f);
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Math Div with known 1.
+ */
+TEST(render_graph, constant_fold_part_math_div_1)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X / 1 == X */
+ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac.");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_math_partial_test_graph(builder, NODE_MATH_DIVIDE, 1.0f);
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Math Mul with known 0.
+ */
+TEST(render_graph, constant_fold_part_math_mul_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X * 0 == 0 * X == 0 */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to constant (0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to constant (0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Out::Value to constant (0)");
+ CORRECT_INFO_MESSAGE(log, "Discarding closure EmissionNode.");
+
+ build_math_partial_test_graph(builder, NODE_MATH_MULTIPLY, 0.0f);
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Math Div with known 0.
+ */
+TEST(render_graph, constant_fold_part_math_div_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* 0 / X == 0 */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to constant (0).");
+ INVALID_INFO_MESSAGE(log, "Folding Math_xC::");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_math_partial_test_graph(builder, NODE_MATH_DIVIDE, 0.0f);
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Vector Math with all constant inputs.
+ */
+TEST(render_graph, constant_fold_vector_math)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding VectorMath::Value to constant (1).");
+ CORRECT_INFO_MESSAGE(log, "Folding VectorMath::Vector to constant (3, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding convert_vector_to_float::value_float to constant (1).");
+ CORRECT_INFO_MESSAGE(log, "Folding Math::Value to constant (2).");
+ CORRECT_INFO_MESSAGE(log, "Folding convert_float_to_color::value_color to constant (2, 2, 2).");
+
+ builder
+ .add_node(ShaderNodeBuilder<VectorMathNode>("VectorMath")
+ .set(&VectorMathNode::type, NODE_VECTOR_MATH_SUBTRACT)
+ .set("Vector1", make_float3(1.3f, 0.5f, 0.7f))
+ .set("Vector2", make_float3(-1.7f, 0.5f, 0.7f)))
+ .add_node(ShaderNodeBuilder<MathNode>("Math")
+ .set(&MathNode::type, NODE_MATH_ADD))
+ .add_connection("VectorMath::Vector", "Math::Value1")
+ .add_connection("VectorMath::Value", "Math::Value2")
+ .output_color("Math::Value");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Graph for testing partial folds of Vector Math with one constant argument.
+ * Includes 2 tests: constant on each side.
+ */
+static void build_vecmath_partial_test_graph(ShaderGraphBuilder &builder, NodeVectorMath type, float3 constval)
+{
+ builder
+ .add_attribute("Attribute")
+ /* constant on the left */
+ .add_node(ShaderNodeBuilder<VectorMathNode>("Math_Cx")
+ .set(&VectorMathNode::type, type)
+ .set("Vector1", constval))
+ .add_connection("Attribute::Vector", "Math_Cx::Vector2")
+ /* constant on the right */
+ .add_node(ShaderNodeBuilder<VectorMathNode>("Math_xC")
+ .set(&VectorMathNode::type, type)
+ .set("Vector2", constval))
+ .add_connection("Attribute::Vector", "Math_xC::Vector1")
+ /* output sum */
+ .add_node(ShaderNodeBuilder<VectorMathNode>("Out")
+ .set(&VectorMathNode::type, NODE_VECTOR_MATH_ADD))
+ .add_connection("Math_Cx::Vector", "Out::Vector1")
+ .add_connection("Math_xC::Vector", "Out::Vector2")
+ .output_color("Out::Vector");
+}
+
+/*
+ * Tests: partial folding for Vector Math Add with known 0.
+ */
+TEST(render_graph, constant_fold_part_vecmath_add_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X + 0 == 0 + X == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Vector to socket Attribute::Vector.");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to socket Attribute::Vector.");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_ADD, make_float3(0,0,0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Vector Math Sub with known 0.
+ */
+TEST(render_graph, constant_fold_part_vecmath_sub_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X - 0 == X */
+ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to socket Attribute::Vector.");
+ INVALID_INFO_MESSAGE(log, "Folding Out::");
+
+ build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_SUBTRACT, make_float3(0,0,0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Vector Math Dot Product with known 0.
+ */
+TEST(render_graph, constant_fold_part_vecmath_dot_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X * 0 == 0 * X == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Vector to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Out::Vector to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Discarding closure EmissionNode.");
+
+ build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_DOT_PRODUCT, make_float3(0,0,0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Vector Math Cross Product with known 0.
+ */
+TEST(render_graph, constant_fold_part_vecmath_cross_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ /* X * 0 == 0 * X == X */
+ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Vector to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Folding Out::Vector to constant (0, 0, 0).");
+ CORRECT_INFO_MESSAGE(log, "Discarding closure EmissionNode.");
+
+ build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_CROSS_PRODUCT, make_float3(0,0,0));
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Bump with no height input folded to Normal input.
+ */
+TEST(render_graph, constant_fold_bump)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Bump::Normal to socket Geometry1::Normal.");
+
+ builder
+ .add_node(ShaderNodeBuilder<GeometryNode>("Geometry1"))
+ .add_node(ShaderNodeBuilder<BumpNode>("Bump"))
+ .add_connection("Geometry1::Normal", "Bump::Normal")
+ .output_color("Bump::Normal");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests: Bump with no inputs folded to Geometry::Normal.
+ */
+TEST(render_graph, constant_fold_bump_no_input)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Bump::Normal to socket geometry::Normal.");
+
+ builder
+ .add_node(ShaderNodeBuilder<BumpNode>("Bump"))
+ .output_color("Bump::Normal");
+
+ graph.finalize(&scene);
+}
+
+template<class T>
+void init_test_curve(array<T> &buffer, T start, T end, int steps)
+{
+ buffer.resize(steps);
+
+ for (int i = 0; i < steps; i++)
+ buffer[i] = lerp(start, end, float(i)/(steps-1));
+}
+
+/*
+ * Tests:
+ * - Folding of RGB Curves with all constant inputs.
+ */
+TEST(render_graph, constant_fold_rgb_curves)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Curves::Color to constant (0.275, 0.5, 0.475).");
+
+ array<float3> curve;
+ init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
+
+ builder
+ .add_node(ShaderNodeBuilder<RGBCurvesNode>("Curves")
+ .set(&CurvesNode::curves, curve)
+ .set(&CurvesNode::min_x, 0.1f)
+ .set(&CurvesNode::max_x, 0.9f)
+ .set("Fac", 0.5f)
+ .set("Color", make_float3(0.3f, 0.5f, 0.7f)))
+ .output_color("Curves::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of RGB Curves with zero Fac.
+ */
+TEST(render_graph, constant_fold_rgb_curves_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Curves::Color to socket Attribute::Color.");
+
+ array<float3> curve;
+ init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<RGBCurvesNode>("Curves")
+ .set(&CurvesNode::curves, curve)
+ .set(&CurvesNode::min_x, 0.1f)
+ .set(&CurvesNode::max_x, 0.9f)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute::Color", "Curves::Color")
+ .output_color("Curves::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Vector Curves with all constant inputs.
+ */
+TEST(render_graph, constant_fold_vector_curves)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Curves::Vector to constant (0.275, 0.5, 0.475).");
+
+ array<float3> curve;
+ init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
+
+ builder
+ .add_node(ShaderNodeBuilder<VectorCurvesNode>("Curves")
+ .set(&CurvesNode::curves, curve)
+ .set(&CurvesNode::min_x, 0.1f)
+ .set(&CurvesNode::max_x, 0.9f)
+ .set("Fac", 0.5f)
+ .set("Vector", make_float3(0.3f, 0.5f, 0.7f)))
+ .output_color("Curves::Vector");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Vector Curves with zero Fac.
+ */
+TEST(render_graph, constant_fold_vector_curves_fac_0)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Curves::Vector to socket Attribute::Vector.");
+
+ array<float3> curve;
+ init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<VectorCurvesNode>("Curves")
+ .set(&CurvesNode::curves, curve)
+ .set(&CurvesNode::min_x, 0.1f)
+ .set(&CurvesNode::max_x, 0.9f)
+ .set("Fac", 0.0f))
+ .add_connection("Attribute::Vector", "Curves::Vector")
+ .output_color("Curves::Vector");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Color Ramp with all constant inputs.
+ */
+TEST(render_graph, constant_fold_rgb_ramp)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Ramp::Color to constant (0.14, 0.39, 0.64).");
+ CORRECT_INFO_MESSAGE(log, "Folding Ramp::Alpha to constant (0.89).");
+
+ array<float3> curve;
+ array<float> alpha;
+ init_test_curve(curve, make_float3(0.0f, 0.25f, 0.5f), make_float3(0.25f, 0.5f, 0.75f), 9);
+ init_test_curve(alpha, 0.75f, 1.0f, 9);
+
+ builder
+ .add_node(ShaderNodeBuilder<RGBRampNode>("Ramp")
+ .set(&RGBRampNode::ramp, curve)
+ .set(&RGBRampNode::ramp_alpha, alpha)
+ .set(&RGBRampNode::interpolate, true)
+ .set("Fac", 0.56f))
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_ADD))
+ .add_connection("Ramp::Color", "Mix::Color1")
+ .add_connection("Ramp::Alpha", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of Color Ramp with all constant inputs (interpolate false).
+ */
+TEST(render_graph, constant_fold_rgb_ramp_flat)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Ramp::Color to constant (0.125, 0.375, 0.625).");
+ CORRECT_INFO_MESSAGE(log, "Folding Ramp::Alpha to constant (0.875).");
+
+ array<float3> curve;
+ array<float> alpha;
+ init_test_curve(curve, make_float3(0.0f, 0.25f, 0.5f), make_float3(0.25f, 0.5f, 0.75f), 9);
+ init_test_curve(alpha, 0.75f, 1.0f, 9);
+
+ builder
+ .add_node(ShaderNodeBuilder<RGBRampNode>("Ramp")
+ .set(&RGBRampNode::ramp, curve)
+ .set(&RGBRampNode::ramp_alpha, alpha)
+ .set(&RGBRampNode::interpolate, false)
+ .set("Fac", 0.56f))
+ .add_node(ShaderNodeBuilder<MixNode>("Mix")
+ .set(&MixNode::type, NODE_MIX_ADD))
+ .add_connection("Ramp::Color", "Mix::Color1")
+ .add_connection("Ramp::Alpha", "Mix::Color2")
+ .output_color("Mix::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of redundant conversion of float to color to float.
+ */
+TEST(render_graph, constant_fold_convert_float_color_float)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to socket convert_float_to_color::value_color.");
+ CORRECT_INFO_MESSAGE(log, "Folding convert_color_to_float::value_float to socket Attribute::Fac.");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<InvertNode>("Invert")
+ .set("Fac", 0.0f))
+ .add_connection("Attribute::Fac", "Invert::Color")
+ .output_value("Invert::Color");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - Folding of redundant conversion of color to vector to color.
+ */
+TEST(render_graph, constant_fold_convert_color_vector_color)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding VecAdd::Vector to socket convert_color_to_vector::value_vector.");
+ CORRECT_INFO_MESSAGE(log, "Folding convert_vector_to_color::value_color to socket Attribute::Color.");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<VectorMathNode>("VecAdd")
+ .set(&VectorMathNode::type, NODE_VECTOR_MATH_ADD)
+ .set("Vector2", make_float3(0,0,0)))
+ .add_connection("Attribute::Color", "VecAdd::Vector1")
+ .output_color("VecAdd::Vector");
+
+ graph.finalize(&scene);
+}
+
+/*
+ * Tests:
+ * - NOT folding conversion of color to float to color.
+ */
+TEST(render_graph, constant_fold_convert_color_float_color)
+{
+ DEFINE_COMMON_VARIABLES(builder, log);
+
+ EXPECT_ANY_MESSAGE(log);
+ CORRECT_INFO_MESSAGE(log, "Folding MathAdd::Value to socket convert_color_to_float::value_float.");
+ INVALID_INFO_MESSAGE(log, "Folding convert_float_to_color::");
+
+ builder
+ .add_attribute("Attribute")
+ .add_node(ShaderNodeBuilder<MathNode>("MathAdd")
+ .set(&MathNode::type, NODE_MATH_ADD)
+ .set("Value2", 0.0f))
+ .add_connection("Attribute::Color", "MathAdd::Value1")
+ .output_color("MathAdd::Value");
+
+ graph.finalize(&scene);
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index cfe6fa65143..89a882d9b9d 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -174,6 +174,11 @@ ccl_device_inline float clamp(float a, float mn, float mx)
return min(max(a, mn), mx);
}
+ccl_device_inline float mix(float a, float b, float t)
+{
+ return a + t*(b - a);
+}
+
#endif
#ifndef __KERNEL_CUDA__
@@ -219,6 +224,11 @@ ccl_device_inline float smoothstepf(float f)
return (3.0f*ff - 2.0f*ff*f);
}
+ccl_device_inline int mod(int x, int m)
+{
+ return (x % m + m) % m;
+}
+
/* Float2 Vector */
#ifndef __KERNEL_OPENCL__
@@ -562,6 +572,12 @@ ccl_device_inline float3 safe_normalize(const float3 a)
return (t != 0.0f)? a/t: a;
}
+ccl_device_inline float3 safe_normalize_len(const float3 a, float *t)
+{
+ *t = len(a);
+ return (*t != 0.0f)? a/(*t): a;
+}
+
#ifndef __KERNEL_OPENCL__
ccl_device_inline bool operator==(const float3 a, const float3 b)
@@ -652,6 +668,15 @@ ccl_device_inline float3 interp(float3 a, float3 b, float t)
return a + t*(b - a);
}
+#ifndef __KERNEL_OPENCL__
+
+ccl_device_inline float3 mix(float3 a, float3 b, float t)
+{
+ return a + t*(b - a);
+}
+
+#endif
+
ccl_device_inline bool is_zero(const float3 a)
{
#ifdef __KERNEL_SSE__
@@ -671,6 +696,15 @@ ccl_device_inline float average(const float3 a)
return reduce_add(a)*(1.0f/3.0f);
}
+ccl_device_inline bool isequal_float3(const float3 a, const float3 b)
+{
+#ifdef __KERNEL_OPENCL__
+ return all(a == b);
+#else
+ return a == b;
+#endif
+}
+
/* Float4 Vector */
#ifdef __KERNEL_SSE__
@@ -1449,10 +1483,10 @@ ccl_device bool ray_triangle_intersect(
return true;
}
-ccl_device bool ray_triangle_intersect_uv(
- float3 ray_P, float3 ray_D, float ray_t,
- float3 v0, float3 v1, float3 v2,
- float *isect_u, float *isect_v, float *isect_t)
+ccl_device_inline bool ray_triangle_intersect_uv(
+ float3 ray_P, float3 ray_D, float ray_t,
+ float3 v0, float3 v1, float3 v2,
+ float *isect_u, float *isect_v, float *isect_t)
{
/* Calculate intersection */
float3 e1 = v1 - v0;
diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/util_path.cpp
index 0c848beaafd..f23f2cb0168 100644
--- a/intern/cycles/util/util_path.cpp
+++ b/intern/cycles/util/util_path.cpp
@@ -728,6 +728,17 @@ bool path_remove(const string& path)
return remove(path.c_str()) == 0;
}
+static string line_directive(const string& path, int line)
+{
+ string escaped_path = path;
+ string_replace(escaped_path, "\"", "\\\"");
+ string_replace(escaped_path, "\'", "\\\'");
+ string_replace(escaped_path, "\?", "\\\?");
+ string_replace(escaped_path, "\\", "\\\\");
+ return string_printf("#line %d \"%s\"", line, escaped_path.c_str());
+}
+
+
string path_source_replace_includes(const string& source, const string& path)
{
/* Our own little c preprocessor that replaces #includes with the file
@@ -737,7 +748,7 @@ string path_source_replace_includes(const string& source, const string& path)
string result = "";
vector<string> lines;
- string_split(lines, source, "\n");
+ string_split(lines, source, "\n", false);
for(size_t i = 0; i < lines.size(); ++i) {
string line = lines[i];
@@ -759,7 +770,10 @@ string path_source_replace_includes(const string& source, const string& path)
text = path_source_replace_includes(
text, path_dirname(filepath));
text = path_source_replace_includes(text, path);
- line = token.replace(0, n_end + 1, "\n" + text + "\n");
+ /* Use line directives for better error messages. */
+ line = line_directive(filepath, 1)
+ + token.replace(0, n_end + 1, "\n" + text + "\n")
+ + line_directive(path, i);
}
}
}
diff --git a/intern/cycles/util/util_string.cpp b/intern/cycles/util/util_string.cpp
index c1c5a6b084b..5594aa8edb6 100644
--- a/intern/cycles/util/util_string.cpp
+++ b/intern/cycles/util/util_string.cpp
@@ -74,7 +74,10 @@ bool string_iequals(const string& a, const string& b)
return false;
}
-void string_split(vector<string>& tokens, const string& str, const string& separators)
+void string_split(vector<string>& tokens,
+ const string& str,
+ const string& separators,
+ bool skip_empty_tokens)
{
size_t token_start = 0, token_length = 0;
for(size_t i = 0; i < str.size(); ++i) {
@@ -87,9 +90,9 @@ void string_split(vector<string>& tokens, const string& str, const string& separ
}
else {
/* Current character is a separator,
- * append current token to the list (if token is not empty).
+ * append current token to the list.
*/
- if(token_length > 0) {
+ if(!skip_empty_tokens || token_length > 0) {
string token = str.substr(token_start, token_length);
tokens.push_back(token);
}
diff --git a/intern/cycles/util/util_string.h b/intern/cycles/util/util_string.h
index d3b5248c380..7aeed96f00b 100644
--- a/intern/cycles/util/util_string.h
+++ b/intern/cycles/util/util_string.h
@@ -39,7 +39,10 @@ using std::istringstream;
string string_printf(const char *format, ...) PRINTF_ATTRIBUTE;
bool string_iequals(const string& a, const string& b);
-void string_split(vector<string>& tokens, const string& str, const string& separators = "\t ");
+void string_split(vector<string>& tokens,
+ const string& str,
+ const string& separators = "\t ",
+ bool skip_empty_tokens = true);
void string_replace(string& haystack, const string& needle, const string& other);
bool string_startswith(const string& s, const char *start);
bool string_endswith(const string& s, const char *end);
diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h
index 6fed18a3db8..bfc8f55feed 100644
--- a/intern/cycles/util/util_transform.h
+++ b/intern/cycles/util/util_transform.h
@@ -323,6 +323,15 @@ ccl_device_inline Transform transform_clear_scale(const Transform& tfm)
return ntfm;
}
+ccl_device_inline Transform transform_empty()
+{
+ return make_transform(
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0);
+}
+
#endif
/* Motion Transform */
diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/util_vector.h
index 6f8c3f6f3de..546b17570bb 100644
--- a/intern/cycles/util/util_vector.h
+++ b/intern/cycles/util/util_vector.h
@@ -222,6 +222,11 @@ public:
return datasize_;
}
+ T* data()
+ {
+ return data_;
+ }
+
const T* data() const
{
return data_;
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index bce390732fe..4b8cb537ecf 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -289,6 +289,7 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
GHOST_SystemCocoa *systemCocoa;
}
- (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
- (void)applicationWillTerminate:(NSNotification *)aNotification;
@@ -302,6 +303,15 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
systemCocoa = sysCocoa;
}
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ // raise application to front, convenient when starting from the terminal
+ // and important for launching the animation player. we call this after the
+ // application finishes launching, as doing it earlier can make us end up
+ // with a frontmost window but an inactive application
+ [NSApp activateIgnoringOtherApps:YES];
+}
+
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
return systemCocoa->handleOpenDocumentRequest(filename);
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index 6f349543eed..727bc9a01fb 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -61,6 +61,11 @@
#include <X11/XF86keysym.h>
#endif
+/* for XIWarpPointer */
+#ifdef WITH_X11_XINPUT
+# include <X11/extensions/XInput2.h>
+#endif
+
/* For timing */
#include <sys/time.h>
#include <unistd.h>
@@ -1469,7 +1474,22 @@ setCursorPosition(
int relx = x - cx;
int rely = y - cy;
- XWarpPointer(m_display, None, None, 0, 0, 0, 0, relx, rely);
+#ifdef WITH_X11_XINPUT
+ if ((m_xinput_version.present) &&
+ (m_xinput_version.major_version >= 2))
+ {
+ /* Needed to account for XInput "Coordinate Transformation Matrix", see T48901 */
+ int device_id;
+ if (XIGetClientPointer(m_display, None, &device_id) != False) {
+ XIWarpPointer(m_display, device_id, None, None, 0, 0, 0, 0, relx, rely);
+ }
+ }
+ else
+#endif
+ {
+ XWarpPointer(m_display, None, None, 0, 0, 0, 0, relx, rely);
+ }
+
XSync(m_display, 0); /* Sync to process all requests */
return GHOST_kSuccess;
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index 00e00b6a1ea..e23cf9ccd37 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -622,8 +622,6 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(
m_lionStyleFullScreen = true;
}
- [NSApp activateIgnoringOtherApps:YES]; // raise application to front, important for new blender instance animation play case
-
[pool drain];
}
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index fd002cec80c..ec2b65e67d0 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -51,6 +51,11 @@
# include "GHOST_ContextGLX.h"
#endif
+/* for XIWarpPointer */
+#ifdef WITH_X11_XINPUT
+# include <X11/extensions/XInput2.h>
+#endif
+
#if defined(__sun__) || defined(__sun) || defined(__sparc) || defined(__sparc__) || defined(_AIX)
# include <strings.h>
#endif
@@ -1520,7 +1525,21 @@ setWindowCursorGrab(
/* use to generate a mouse move event, otherwise the last event
* blender gets can be outside the screen causing menus not to show
* properly unless the user moves the mouse */
- XWarpPointer(m_display, None, None, 0, 0, 0, 0, 0, 0);
+
+#ifdef WITH_X11_XINPUT
+ if ((m_system->m_xinput_version.present) &&
+ (m_system->m_xinput_version.major_version >= 2))
+ {
+ int device_id;
+ if (XIGetClientPointer(m_display, None, &device_id) != False) {
+ XIWarpPointer(m_display, device_id, None, None, 0, 0, 0, 0, 0, 0);
+ }
+ }
+ else
+#endif
+ {
+ XWarpPointer(m_display, None, None, 0, 0, 0, 0, 0, 0);
+ }
}
/* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
diff --git a/intern/opensubdiv/gpu_shader_opensubd_display.glsl b/intern/opensubdiv/gpu_shader_opensubd_display.glsl
index cc9e05a11c8..5193d3a71dc 100644
--- a/intern/opensubdiv/gpu_shader_opensubd_display.glsl
+++ b/intern/opensubdiv/gpu_shader_opensubd_display.glsl
@@ -242,6 +242,7 @@ void main()
vec3 L_diffuse = vec3(0.0);
vec3 L_specular = vec3(0.0);
+#ifdef USE_LIGHTING
#ifndef USE_COLOR_MATERIAL
/* Assume NUM_SOLID_LIGHTS directional lights. */
for (int i = 0; i < NUM_SOLID_LIGHTS; i++) {
@@ -312,6 +313,9 @@ void main()
L_specular += light_specular * specular_bsdf * intensity;
}
#endif /* USE_COLOR_MATERIAL */
+#else /* USE_LIGHTING */
+ L_diffuse = vec3(1.0);
+#endif
/* Compute diffuse color. */
#ifdef USE_TEXTURE_2D
diff --git a/intern/opensubdiv/opensubdiv_gpu_capi.cc b/intern/opensubdiv/opensubdiv_gpu_capi.cc
index 752dce7744c..0cf6fcfef43 100644
--- a/intern/opensubdiv/opensubdiv_gpu_capi.cc
+++ b/intern/opensubdiv/opensubdiv_gpu_capi.cc
@@ -100,6 +100,12 @@ static GLuint g_flat_fill_solid_program = 0;
static GLuint g_flat_fill_texture2d_program = 0;
static GLuint g_smooth_fill_solid_program = 0;
static GLuint g_smooth_fill_texture2d_program = 0;
+
+static GLuint g_flat_fill_solid_shadeless_program = 0;
+static GLuint g_flat_fill_texture2d_shadeless_program = 0;
+static GLuint g_smooth_fill_solid_shadeless_program = 0;
+static GLuint g_smooth_fill_texture2d_shadeless_program = 0;
+
static GLuint g_wireframe_program = 0;
static GLuint g_lighting_ub = 0;
@@ -425,21 +431,45 @@ bool openSubdiv_osdGLDisplayInit(void)
g_flat_fill_solid_program = linkProgram(
version,
"#define USE_COLOR_MATERIAL\n"
+ "#define USE_LIGHTING\n"
"#define FLAT_SHADING\n");
g_flat_fill_texture2d_program = linkProgram(
version,
"#define USE_COLOR_MATERIAL\n"
+ "#define USE_LIGHTING\n"
"#define USE_TEXTURE_2D\n"
"#define FLAT_SHADING\n");
g_smooth_fill_solid_program = linkProgram(
version,
"#define USE_COLOR_MATERIAL\n"
+ "#define USE_LIGHTING\n"
"#define SMOOTH_SHADING\n");
g_smooth_fill_texture2d_program = linkProgram(
version,
"#define USE_COLOR_MATERIAL\n"
+ "#define USE_LIGHTING\n"
"#define USE_TEXTURE_2D\n"
"#define SMOOTH_SHADING\n");
+
+ g_flat_fill_solid_shadeless_program = linkProgram(
+ version,
+ "#define USE_COLOR_MATERIAL\n"
+ "#define FLAT_SHADING\n");
+ g_flat_fill_texture2d_shadeless_program = linkProgram(
+ version,
+ "#define USE_COLOR_MATERIAL\n"
+ "#define USE_TEXTURE_2D\n"
+ "#define FLAT_SHADING\n");
+ g_smooth_fill_solid_shadeless_program = linkProgram(
+ version,
+ "#define USE_COLOR_MATERIAL\n"
+ "#define SMOOTH_SHADING\n");
+ g_smooth_fill_texture2d_shadeless_program = linkProgram(
+ version,
+ "#define USE_COLOR_MATERIAL\n"
+ "#define USE_TEXTURE_2D\n"
+ "#define SMOOTH_SHADING\n");
+
g_wireframe_program = linkProgram(
version,
"#define WIREFRAME\n");
@@ -464,21 +494,24 @@ void openSubdiv_osdGLDisplayDeinit(void)
if (g_lighting_ub != 0) {
glDeleteBuffers(1, &g_lighting_ub);
}
- if (g_flat_fill_solid_program) {
- glDeleteProgram(g_flat_fill_solid_program);
- }
- if (g_flat_fill_texture2d_program) {
- glDeleteProgram(g_flat_fill_texture2d_program);
- }
- if (g_smooth_fill_solid_program) {
- glDeleteProgram(g_flat_fill_solid_program);
- }
- if (g_smooth_fill_texture2d_program) {
- glDeleteProgram(g_smooth_fill_texture2d_program);
- }
- if (g_wireframe_program) {
- glDeleteProgram(g_wireframe_program);
- }
+#define SAFE_DELETE_PROGRAM(program) \
+ do { \
+ if (program) { \
+ glDeleteProgram(program); \
+ } \
+ } while (false)
+
+ SAFE_DELETE_PROGRAM(g_flat_fill_solid_program);
+ SAFE_DELETE_PROGRAM(g_flat_fill_texture2d_program);
+ SAFE_DELETE_PROGRAM(g_smooth_fill_solid_program);
+ SAFE_DELETE_PROGRAM(g_smooth_fill_texture2d_program);
+ SAFE_DELETE_PROGRAM(g_flat_fill_solid_shadeless_program);
+ SAFE_DELETE_PROGRAM(g_flat_fill_texture2d_shadeless_program);
+ SAFE_DELETE_PROGRAM(g_smooth_fill_solid_shadeless_program);
+ SAFE_DELETE_PROGRAM(g_smooth_fill_texture2d_shadeless_program);
+ SAFE_DELETE_PROGRAM(g_wireframe_program);
+
+#undef SAFE_DELETE_PROGRAM
}
void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
@@ -599,23 +632,32 @@ static GLuint prepare_patchDraw(OpenSubdiv_GLMesh *gl_mesh,
if (fill_quads) {
int model;
- GLboolean use_texture_2d;
+ GLboolean use_texture_2d, use_lighting;
glGetIntegerv(GL_SHADE_MODEL, &model);
glGetBooleanv(GL_TEXTURE_2D, &use_texture_2d);
+ glGetBooleanv(GL_LIGHTING, &use_lighting);
if (model == GL_FLAT) {
if (use_texture_2d) {
- program = g_flat_fill_texture2d_program;
+ program = use_lighting
+ ? g_flat_fill_texture2d_program
+ : g_flat_fill_texture2d_shadeless_program;
}
else {
- program = g_flat_fill_solid_program;
+ program = use_lighting
+ ? g_flat_fill_solid_program
+ : g_flat_fill_solid_shadeless_program;
}
}
else {
if (use_texture_2d) {
- program = g_smooth_fill_texture2d_program;
+ program = use_lighting
+ ? g_smooth_fill_texture2d_program
+ : g_smooth_fill_texture2d_shadeless_program;
}
else {
- program = g_smooth_fill_solid_program;
+ program = use_lighting
+ ? g_smooth_fill_solid_program
+ : g_smooth_fill_solid_shadeless_program;
}
}
}
diff --git a/intern/smoke/intern/FLUID_3D.cpp b/intern/smoke/intern/FLUID_3D.cpp
index 4faec894801..8a27818ff36 100644
--- a/intern/smoke/intern/FLUID_3D.cpp
+++ b/intern/smoke/intern/FLUID_3D.cpp
@@ -993,6 +993,9 @@ void FLUID_3D::project()
copyBorderAll(_pressure, 0, _zRes);
+ // fix fluid compression caused in isolated components by obstacle movement
+ fixObstacleCompression(_divergence);
+
// solve Poisson equation
solvePressurePre(_pressure, _divergence, _obstacles);
@@ -1291,6 +1294,142 @@ void FLUID_3D::setObstacleBoundaries(float *_pressure, int zBegin, int zEnd)
} // z-loop
}
+void FLUID_3D::floodFillComponent(int *buffer, size_t *queue, size_t limit, size_t pos, int from, int to)
+{
+ /* Flood 'from' cells with 'to' in the grid. Rely on (from != 0 && from != to && edges == 0) to stop. */
+ int offsets[] = { -1, +1, -_xRes, +_xRes, -_slabSize, +_slabSize };
+ size_t qend = 0;
+
+ buffer[pos] = to;
+ queue[qend++] = pos;
+
+ for (size_t qidx = 0; qidx < qend; qidx++)
+ {
+ pos = queue[qidx];
+
+ for (int i = 0; i < 6; i++)
+ {
+ size_t next = pos + offsets[i];
+
+ if (next < limit && buffer[next] == from)
+ {
+ buffer[next] = to;
+ queue[qend++] = next;
+ }
+ }
+ }
+}
+
+void FLUID_3D::mergeComponents(int *buffer, size_t *queue, size_t cur, size_t other)
+{
+ /* Replace higher value with lower. */
+ if (buffer[other] < buffer[cur])
+ {
+ floodFillComponent(buffer, queue, cur, cur, buffer[cur], buffer[other]);
+ }
+ else if (buffer[cur] < buffer[other])
+ {
+ floodFillComponent(buffer, queue, cur, other, buffer[other], buffer[cur]);
+ }
+}
+
+void FLUID_3D::fixObstacleCompression(float *divergence)
+{
+ int x, y, z;
+ size_t index;
+
+ /* Find compartments completely separated by obstacles.
+ * Edge of the domain is automatically component 0. */
+ int *component = new int[_totalCells];
+ size_t *queue = new size_t[_totalCells];
+
+ memset(component, 0, sizeof(int) * _totalCells);
+
+ int next_id = 1;
+
+ for (z = 1, index = _slabSize + _xRes + 1; z < _zRes - 1; z++, index += 2 * _xRes)
+ {
+ for (y = 1; y < _yRes - 1; y++, index += 2)
+ {
+ for (x = 1; x < _xRes - 1; x++, index++)
+ {
+ if(!_obstacles[index])
+ {
+ /* Check for connection to the domain edge at iteration end. */
+ if ((x == _xRes-2 && !_obstacles[index + 1]) ||
+ (y == _yRes-2 && !_obstacles[index + _xRes]) ||
+ (z == _zRes-2 && !_obstacles[index + _slabSize]))
+ {
+ component[index] = 0;
+ }
+ else {
+ component[index] = next_id;
+ }
+
+ if (!_obstacles[index - 1])
+ mergeComponents(component, queue, index, index - 1);
+ if (!_obstacles[index - _xRes])
+ mergeComponents(component, queue, index, index - _xRes);
+ if (!_obstacles[index - _slabSize])
+ mergeComponents(component, queue, index, index - _slabSize);
+
+ if (component[index] == next_id)
+ next_id++;
+ }
+ }
+ }
+ }
+
+ delete[] queue;
+
+ /* Compute average divergence within each component. */
+ float *total_divergence = new float[next_id];
+ int *component_size = new int[next_id];
+
+ memset(total_divergence, 0, sizeof(float) * next_id);
+ memset(component_size, 0, sizeof(int) * next_id);
+
+ for (z = 1, index = _slabSize + _xRes + 1; z < _zRes - 1; z++, index += 2 * _xRes)
+ {
+ for (y = 1; y < _yRes - 1; y++, index += 2)
+ {
+ for (x = 1; x < _xRes - 1; x++, index++)
+ {
+ if(!_obstacles[index])
+ {
+ int ci = component[index];
+
+ component_size[ci]++;
+ total_divergence[ci] += divergence[index];
+ }
+ }
+ }
+ }
+
+ /* Adjust divergence to make the average zero in each component except the edge. */
+ total_divergence[0] = 0.0f;
+
+ for (z = 1, index = _slabSize + _xRes + 1; z < _zRes - 1; z++, index += 2 * _xRes)
+ {
+ for (y = 1; y < _yRes - 1; y++, index += 2)
+ {
+ for (x = 1; x < _xRes - 1; x++, index++)
+ {
+ if(!_obstacles[index])
+ {
+ int ci = component[index];
+
+ divergence[index] -= total_divergence[ci] / component_size[ci];
+ }
+ }
+ }
+ }
+
+ delete[] component;
+ delete[] component_size;
+ delete[] total_divergence;
+}
+
//////////////////////////////////////////////////////////////////////
// add buoyancy forces
//////////////////////////////////////////////////////////////////////
@@ -1650,4 +1789,4 @@ void FLUID_3D::updateFlame(float *react, float *flame, int total_cells)
else
flame[index] = 0.0f;
}
-} \ No newline at end of file
+}
diff --git a/intern/smoke/intern/FLUID_3D.h b/intern/smoke/intern/FLUID_3D.h
index cd2147b2bee..fe20c10d71d 100644
--- a/intern/smoke/intern/FLUID_3D.h
+++ b/intern/smoke/intern/FLUID_3D.h
@@ -195,6 +195,8 @@ struct FLUID_3D
void setObstacleBoundaries(float *_pressure, int zBegin, int zEnd);
void setObstaclePressure(float *_pressure, int zBegin, int zEnd);
+ void fixObstacleCompression(float *divergence);
+
public:
// advection, accessed e.g. by WTURBULENCE class
//void advectMacCormack();
@@ -202,6 +204,9 @@ struct FLUID_3D
void advectMacCormackEnd1(int zBegin, int zEnd);
void advectMacCormackEnd2(int zBegin, int zEnd);
+ void floodFillComponent(int *components, size_t *queue, size_t limit, size_t start, int from, int to);
+ void mergeComponents(int *components, size_t *queue, size_t cur, size_t other);
+
/* burning */
float *_burning_rate; // RNA pointer
float *_flame_smoke; // RNA pointer
diff --git a/release/bin/blender-thumbnailer.py b/release/bin/blender-thumbnailer.py
index fe5d462bba9..5d2dd958a92 100755
--- a/release/bin/blender-thumbnailer.py
+++ b/release/bin/blender-thumbnailer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# ##### BEGIN GPL LICENSE BLOCK #####
#
diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg
index 40b974661fa..030953cf614 100644
--- a/release/datafiles/blender_icons.svg
+++ b/release/datafiles/blender_icons.svg
@@ -30957,6 +30957,93 @@
y1="-16"
x2="-93.75"
y2="-16.264704" />
+ <filter
+ inkscape:label="Opacity"
+ style="color-interpolation-filters:sRGB"
+ id="filter17385">
+ <feColorMatrix
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 5 -1 "
+ result="colormatrix"
+ id="feColorMatrix17387" />
+ <feComposite
+ k4="0"
+ k3="0"
+ k1="0"
+ in2="colormatrix"
+ operator="arithmetic"
+ k2="0.24"
+ result="composite"
+ id="feComposite17389" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18134"
+ id="radialGradient37501-3-6-2"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.08933014,-0.7764284,0.7350832,-0.08334857,57.410559,233.30156)"
+ cx="135.83771"
+ cy="117.97826"
+ fx="135.83771"
+ fy="117.97826"
+ r="8" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18134"
+ id="radialGradient37501-3-6"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.08933014,-0.7764284,0.7350832,-0.08334857,57.410559,233.30156)"
+ cx="135.83771"
+ cy="117.97826"
+ fx="135.83771"
+ fy="117.97826"
+ r="8" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18134"
+ id="linearGradient17463"
+ gradientUnits="userSpaceOnUse"
+ x1="121.19734"
+ y1="105.94044"
+ x2="148.06364"
+ y2="137.6748" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient16595"
+ id="linearGradient17281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(144,188)"
+ x1="209"
+ y1="238"
+ x2="226.625"
+ y2="251.71078" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18134"
+ id="linearGradient31399"
+ gradientUnits="userSpaceOnUse"
+ x1="62.793919"
+ y1="133.73566"
+ x2="64.109718"
+ y2="135.18265" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient319"
+ id="linearGradient31452"
+ gradientUnits="userSpaceOnUse"
+ x1="121.19734"
+ y1="105.94044"
+ x2="148.06364"
+ y2="137.6748" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1610-36-6-5"
+ id="linearGradient31454"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(144,188)"
+ x1="209"
+ y1="238"
+ x2="226.625"
+ y2="251.71078" />
</defs>
<sodipodi:namedview
id="base"
@@ -30968,16 +31055,16 @@
objecttolerance="10000"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="40.768576"
- inkscape:cx="236.56738"
- inkscape:cy="163.14077"
+ inkscape:zoom="10.192144"
+ inkscape:cx="406.73801"
+ inkscape:cy="204.25086"
inkscape:document-units="px"
inkscape:current-layer="g24024-1"
showgrid="true"
- inkscape:window-width="1920"
- inkscape:window-height="982"
- inkscape:window-x="0"
- inkscape:window-y="28"
+ inkscape:window-width="1680"
+ inkscape:window-height="987"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
inkscape:snap-nodes="false"
inkscape:snap-bbox="true"
showguides="true"
@@ -91590,8 +91677,7 @@
cx="304.0946"
cy="242.89087"
rx="0.59120387"
- ry="0.61330098"
- d="m 304.68581,242.89087 c 0,0.33872 -0.26469,0.6133 -0.59121,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32652,0 0.59121,0.27458 0.59121,0.6133 z" />
+ ry="0.61330098" />
<ellipse
sodipodi:ry="0.61330098"
sodipodi:rx="0.59120387"
@@ -91602,8 +91688,7 @@
cx="315.98523"
cy="242.95337"
rx="0.59120387"
- ry="0.61330098"
- d="m 316.57643,242.95337 c 0,0.33872 -0.26469,0.6133 -0.5912,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32651,0 0.5912,0.27458 0.5912,0.6133 z" />
+ ry="0.61330098" />
</g>
<g
clip-path="url(#clipPath15455-9)"
@@ -91819,8 +91904,7 @@
cy="242.89087"
cx="304.0946"
id="ellipse15366"
- style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 304.68581,242.89087 c 0,0.33872 -0.26469,0.6133 -0.59121,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32652,0 0.59121,0.27458 0.59121,0.6133 z" />
+ style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<ellipse
sodipodi:ry="0.61330098"
sodipodi:rx="0.59120387"
@@ -91831,8 +91915,7 @@
cy="242.95337"
cx="315.98523"
id="ellipse15368"
- style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 316.57643,242.95337 c 0,0.33872 -0.26469,0.6133 -0.5912,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32651,0 0.5912,0.27458 0.5912,0.6133 z" />
+ style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
transform="translate(189.00803,-79.37555)"
@@ -91942,6 +92025,382 @@
style="color:#000000;fill:url(#radialGradient14216);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" />
</g>
</g>
+ <g
+ style="display:inline;enable-background:new"
+ id="g17465"
+ transform="translate(62.844714,-106.93345)">
+ <g
+ transform="matrix(0.7,0,0,0.7,146.7,264.8)"
+ id="ICON_COLOR-1-9"
+ style="display:inline;enable-background:new">
+ <rect
+ y="238"
+ x="341"
+ height="16"
+ width="16"
+ id="rect36341-2-0"
+ style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" />
+ <g
+ id="g36343-7-5"
+ transform="translate(0,-12)">
+ <g
+ transform="matrix(1.1658027,0,0,1.1657997,198.71028,-2.0560643)"
+ id="g36345-0-4">
+ <path
+ transform="matrix(0.6969448,0,0,0.6969467,36.918512,140.83126)"
+ sodipodi:type="arc"
+ style="fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36349-4-2"
+ sodipodi:cx="132"
+ sodipodi:cy="118"
+ sodipodi:rx="8"
+ sodipodi:ry="8"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ sodipodi:start="4.712389"
+ sodipodi:end="5.7595865"
+ inkscape:transform-center-x="-2.8145849"
+ inkscape:transform-center-y="-3.2499984" />
+ <path
+ inkscape:transform-center-y="1.6729808e-005"
+ inkscape:transform-center-x="-3.2630798"
+ sodipodi:end="5.7595865"
+ sodipodi:start="4.712389"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ sodipodi:ry="8"
+ sodipodi:rx="8"
+ sodipodi:cy="118"
+ sodipodi:cx="132"
+ id="path36351-3-6"
+ style="fill:#ad2f94;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc"
+ transform="matrix(0.3484724,0.6035735,-0.603572,0.3484734,154.13836,102.27942)" />
+ <path
+ transform="matrix(-0.3484724,0.6035735,-0.603572,-0.3484733,246.13507,184.51913)"
+ sodipodi:type="arc"
+ style="fill:#0060f0;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36353-9-8"
+ sodipodi:cx="132"
+ sodipodi:cy="118"
+ sodipodi:rx="8"
+ sodipodi:ry="8"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ sodipodi:start="4.712389"
+ sodipodi:end="5.7595865"
+ inkscape:transform-center-x="-2.8145756"
+ inkscape:transform-center-y="3.2500173" />
+ <path
+ inkscape:transform-center-y="3.249994"
+ inkscape:transform-center-x="2.8145978"
+ sodipodi:end="5.7595865"
+ sodipodi:start="4.712389"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ sodipodi:ry="8"
+ sodipodi:rx="8"
+ sodipodi:cy="118"
+ sodipodi:cx="132"
+ id="path36355-6-4"
+ style="fill:#00d4aa;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc"
+ transform="matrix(-0.6969448,2.2484149e-8,-4.6257528e-8,-0.6969467,220.91956,305.31067)" />
+ <path
+ transform="matrix(-0.3484724,-0.6035734,0.603572,-0.3484734,103.69972,343.86251)"
+ sodipodi:type="arc"
+ style="fill:#ccff00;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36357-5-2"
+ sodipodi:cx="132"
+ sodipodi:cy="118"
+ sodipodi:rx="8"
+ sodipodi:ry="8"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ sodipodi:start="4.712389"
+ sodipodi:end="5.7595865"
+ inkscape:transform-center-x="3.2630773" />
+ <path
+ inkscape:transform-center-x="2.8145777"
+ sodipodi:end="5.7595865"
+ sodipodi:start="4.712389"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ sodipodi:ry="8"
+ sodipodi:rx="8"
+ sodipodi:cy="118"
+ sodipodi:cx="132"
+ id="path36359-0-5"
+ style="fill:#ffbf0e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc"
+ transform="matrix(0.3484724,-0.6035734,0.603572,0.3484733,11.703006,261.6228)"
+ inkscape:transform-center-y="-3.2500006" />
+ </g>
+ <circle
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ id="path36361-8-8"
+ style="fill:none;stroke:#000000;stroke-width:0.98948926;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ transform="matrix(0.8124999,0,0,0.8045157,241.75,163.13011)"
+ cx="132"
+ cy="118"
+ r="8" />
+ <circle
+ style="display:inline;opacity:0.3;fill:url(#radialGradient37501-3-6);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36363-2-9"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ transform="matrix(-0.7451143,-0.08386971,0.08492794,-0.7396793,437.33358,356.39712)"
+ cx="132"
+ cy="118"
+ r="8" />
+ <circle
+ transform="matrix(0.6860851,0,0,0.6874876,258.44808,176.87656)"
+ style="fill:none;stroke:url(#linearGradient17463);stroke-width:1.45605874;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path36365-2-9"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ cx="132"
+ cy="118"
+ r="8" />
+ </g>
+ </g>
+ <g
+ inkscape:transform-center-y="0.19622957"
+ inkscape:transform-center-x="-1.373607"
+ id="ICON_RESTRICT_SELECT_OFF-9"
+ style="display:inline;enable-background:new"
+ transform="matrix(0.45975513,-0.19653299,0.19653299,0.45975513,138.04837,311.70175)">
+ <path
+ style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient17281);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001;marker:none;enable-background:accumulate"
+ d="m 367.75,440.75 1.75,-1.5 2.5,5.25 1.75,-1 -2.25,-5 2.5,0 -6.25,-6.25 z"
+ id="path45378-1-5-6-6"
+ sodipodi:nodetypes="cccccccc"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect45374-0-5-6-1"
+ width="16"
+ height="16"
+ x="362"
+ y="430" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 367.5,431.5 7,7.25 -3,0 2.5,4.75 -1.75,1 -2.5,-5 -2.25,2.25 z"
+ id="path17835-7-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 368.34375,433.75 0,5.75"
+ id="path17845-9-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ </g>
+ <g
+ style="display:inline;filter:url(#filter17385);enable-background:new"
+ id="g17465-0"
+ transform="translate(41.727744,-106.93345)">
+ <g
+ transform="matrix(0.7,0,0,0.7,146.7,264.8)"
+ id="ICON_COLOR-1-9-4"
+ style="display:inline;enable-background:new">
+ <rect
+ y="238"
+ x="341"
+ height="16"
+ width="16"
+ id="rect36341-2-0-1"
+ style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" />
+ <g
+ id="g36343-7-5-5"
+ transform="translate(0,-12)">
+ <g
+ transform="matrix(1.1658027,0,0,1.1657997,198.71028,-2.0560643)"
+ id="g36345-0-4-4">
+ <path
+ transform="matrix(0.6969448,0,0,0.6969467,36.918512,140.83126)"
+ sodipodi:type="arc"
+ style="fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36349-4-2-6"
+ sodipodi:cx="132"
+ sodipodi:cy="118"
+ sodipodi:rx="8"
+ sodipodi:ry="8"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ sodipodi:start="4.712389"
+ sodipodi:end="5.7595865"
+ inkscape:transform-center-x="-2.8145849"
+ inkscape:transform-center-y="-3.2499984" />
+ <path
+ inkscape:transform-center-y="1.6729808e-005"
+ inkscape:transform-center-x="-3.2630798"
+ sodipodi:end="5.7595865"
+ sodipodi:start="4.712389"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ sodipodi:ry="8"
+ sodipodi:rx="8"
+ sodipodi:cy="118"
+ sodipodi:cx="132"
+ id="path36351-3-6-5"
+ style="fill:#ad2f94;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc"
+ transform="matrix(0.3484724,0.6035735,-0.603572,0.3484734,154.13836,102.27942)" />
+ <path
+ transform="matrix(-0.3484724,0.6035735,-0.603572,-0.3484733,246.13507,184.51913)"
+ sodipodi:type="arc"
+ style="fill:#0060f0;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36353-9-8-2"
+ sodipodi:cx="132"
+ sodipodi:cy="118"
+ sodipodi:rx="8"
+ sodipodi:ry="8"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ sodipodi:start="4.712389"
+ sodipodi:end="5.7595865"
+ inkscape:transform-center-x="-2.8145756"
+ inkscape:transform-center-y="3.2500173" />
+ <path
+ inkscape:transform-center-y="3.249994"
+ inkscape:transform-center-x="2.8145978"
+ sodipodi:end="5.7595865"
+ sodipodi:start="4.712389"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ sodipodi:ry="8"
+ sodipodi:rx="8"
+ sodipodi:cy="118"
+ sodipodi:cx="132"
+ id="path36355-6-4-4"
+ style="fill:#00d4aa;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc"
+ transform="matrix(-0.6969448,2.2484149e-8,-4.6257528e-8,-0.6969467,220.91956,305.31067)" />
+ <path
+ transform="matrix(-0.3484724,-0.6035734,0.603572,-0.3484734,103.69972,343.86251)"
+ sodipodi:type="arc"
+ style="fill:#ccff00;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36357-5-2-5"
+ sodipodi:cx="132"
+ sodipodi:cy="118"
+ sodipodi:rx="8"
+ sodipodi:ry="8"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ sodipodi:start="4.712389"
+ sodipodi:end="5.7595865"
+ inkscape:transform-center-x="3.2630773" />
+ <path
+ inkscape:transform-center-x="2.8145777"
+ sodipodi:end="5.7595865"
+ sodipodi:start="4.712389"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z"
+ sodipodi:ry="8"
+ sodipodi:rx="8"
+ sodipodi:cy="118"
+ sodipodi:cx="132"
+ id="path36359-0-5-9"
+ style="fill:#ffbf0e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc"
+ transform="matrix(0.3484724,-0.6035734,0.603572,0.3484733,11.703006,261.6228)"
+ inkscape:transform-center-y="-3.2500006" />
+ </g>
+ <circle
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ id="path36361-8-8-0"
+ style="fill:none;stroke:#000000;stroke-width:0.98948926;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ transform="matrix(0.8124999,0,0,0.8045157,241.75,163.13011)"
+ cx="132"
+ cy="118"
+ r="8" />
+ <circle
+ style="display:inline;opacity:0.3;fill:url(#radialGradient37501-3-6-2);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path36363-2-9-7"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ transform="matrix(-0.7451143,-0.08386971,0.08492794,-0.7396793,437.33358,356.39712)"
+ cx="132"
+ cy="118"
+ r="8" />
+ <circle
+ transform="matrix(0.6860851,0,0,0.6874876,258.44808,176.87656)"
+ style="fill:none;stroke:url(#linearGradient31452);stroke-width:1.45605874;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path36365-2-9-7"
+ inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ cx="132"
+ cy="118"
+ r="8" />
+ </g>
+ </g>
+ <g
+ inkscape:transform-center-y="0.19622957"
+ inkscape:transform-center-x="-1.373607"
+ id="ICON_RESTRICT_SELECT_OFF-9-8"
+ style="display:inline;enable-background:new"
+ transform="matrix(0.45975513,-0.19653299,0.19653299,0.45975513,138.04837,311.70175)">
+ <path
+ style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient31454);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001;marker:none;enable-background:accumulate"
+ d="m 367.75,440.75 1.75,-1.5 2.5,5.25 1.75,-1 -2.25,-5 2.5,0 -6.25,-6.25 z"
+ id="path45378-1-5-6-6-9"
+ sodipodi:nodetypes="cccccccc"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ id="rect45374-0-5-6-1-0"
+ width="16"
+ height="16"
+ x="362"
+ y="430" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 367.5,431.5 7,7.25 -3,0 2.5,4.75 -1.75,1 -2.5,-5 -2.25,2.25 z"
+ id="path17835-7-5-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 368.34375,433.75 0,5.75"
+ id="path17845-9-6-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ </g>
</g>
</g>
<g
diff --git a/release/datafiles/blender_icons16/icon16_restrict_color_off.dat b/release/datafiles/blender_icons16/icon16_restrict_color_off.dat
new file mode 100644
index 00000000000..d313539e3c5
--- /dev/null
+++ b/release/datafiles/blender_icons16/icon16_restrict_color_off.dat
Binary files differ
diff --git a/release/datafiles/blender_icons16/icon16_restrict_color_on.dat b/release/datafiles/blender_icons16/icon16_restrict_color_on.dat
new file mode 100644
index 00000000000..bb7782bb42a
--- /dev/null
+++ b/release/datafiles/blender_icons16/icon16_restrict_color_on.dat
Binary files differ
diff --git a/release/datafiles/blender_icons32/icon32_restrict_color_off.dat b/release/datafiles/blender_icons32/icon32_restrict_color_off.dat
new file mode 100644
index 00000000000..bbe5f61935d
--- /dev/null
+++ b/release/datafiles/blender_icons32/icon32_restrict_color_off.dat
Binary files differ
diff --git a/release/datafiles/blender_icons32/icon32_restrict_color_on.dat b/release/datafiles/blender_icons32/icon32_restrict_color_on.dat
new file mode 100644
index 00000000000..46ba9e31407
--- /dev/null
+++ b/release/datafiles/blender_icons32/icon32_restrict_color_on.dat
Binary files differ
diff --git a/release/datafiles/ctodata.py b/release/datafiles/ctodata.py
index 079f6c3b918..8c1d4a4fb34 100755
--- a/release/datafiles/ctodata.py
+++ b/release/datafiles/ctodata.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ***** BEGIN GPL LICENSE BLOCK *****
diff --git a/release/scripts/freestyle/modules/parameter_editor.py b/release/scripts/freestyle/modules/parameter_editor.py
index 082ce139a59..93305cb7c5a 100644
--- a/release/scripts/freestyle/modules/parameter_editor.py
+++ b/release/scripts/freestyle/modules/parameter_editor.py
@@ -914,14 +914,25 @@ class QuantitativeInvisibilityRangeUP1D(UnaryPredicate1D):
return self.qi_start <= qi <= self.qi_end
+def getQualifiedObjectName(ob):
+ if ob.library is not None:
+ return ob.library.filepath + '/' + ob.name
+ return ob.name
+
+
class ObjectNamesUP1D(UnaryPredicate1D):
def __init__(self, names, negative):
UnaryPredicate1D.__init__(self)
self.names = names
self.negative = negative
+ def getViewShapeName(self, vs):
+ if vs.library_path is not None:
+ return vs.library_path + '/' + vs.name
+ return vs.name
+
def __call__(self, viewEdge):
- found = viewEdge.viewshape.name in self.names
+ found = self.getViewShapeName(viewEdge.viewshape) in self.names
if self.negative:
return not found
return found
@@ -1256,7 +1267,7 @@ def process(layer_name, lineset_name):
# prepare selection criteria by group of objects
if lineset.select_by_group:
if lineset.group is not None:
- names = {ob.name: True for ob in lineset.group.objects}
+ names = {getQualifiedObjectName(ob): True for ob in lineset.group.objects}
upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE')
selection_criteria.append(upred)
# prepare selection criteria by image border
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 95c0e5f187d..0f096f5812c 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -26,7 +26,7 @@ __all__ = (
"disable",
"reset_all",
"module_bl_info",
- )
+)
import bpy as _bpy
_user_preferences = _bpy.context.user_preferences
@@ -458,7 +458,7 @@ def module_bl_info(mod, info_basis=None):
"category": "",
"warning": "",
"show_expanded": False,
- }
+ }
addon_info = getattr(mod, "bl_info", {})
diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
index 674c1c00ab1..6c29223cf55 100644
--- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py
+++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
@@ -21,9 +21,7 @@
# Populate a template file (POT format currently) from Blender RNA/py/C data.
# Note: This script is meant to be used from inside Blender!
-import collections
import os
-import sys
import bpy
from mathutils import Vector, Euler, Matrix
@@ -67,6 +65,8 @@ def rna_backup_restore(data, backup):
def do_previews(do_objects, do_groups, do_scenes, do_data_intern):
+ import collections
+
# Helpers.
RenderContext = collections.namedtuple("RenderContext", (
"scene", "world", "camera", "lamp", "camera_data", "lamp_data", "image", # All those are names!
@@ -362,7 +362,6 @@ def do_previews(do_objects, do_groups, do_scenes, do_data_intern):
# File "<string>", line 327, in do_previews
# OverflowError: Python int too large to convert to C long
# ... :(
- import sys
scene = bpy.data.scenes[render_context.scene, None]
for obname in objects:
ob = bpy.data.objects[obname, None]
diff --git a/release/scripts/modules/blend_render_info.py b/release/scripts/modules/blend_render_info.py
index a62eaeea143..5e4ee0ee4b1 100755
--- a/release/scripts/modules/blend_render_info.py
+++ b/release/scripts/modules/blend_render_info.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# ##### BEGIN GPL LICENSE BLOCK #####
#
diff --git a/release/scripts/modules/bpy/__init__.py b/release/scripts/modules/bpy/__init__.py
index f012c1317d4..26fdbc8cc56 100644
--- a/release/scripts/modules/bpy/__init__.py
+++ b/release/scripts/modules/bpy/__init__.py
@@ -31,7 +31,7 @@ __all__ = (
"props",
"types",
"utils",
- )
+)
# internal blender C module
@@ -57,11 +57,11 @@ def main():
# fake module to allow:
# from bpy.types import Panel
sys.modules.update({
- "bpy.app": app,
- "bpy.app.handlers": app.handlers,
- "bpy.app.translations": app.translations,
- "bpy.types": types,
- })
+ "bpy.app": app,
+ "bpy.app.handlers": app.handlers,
+ "bpy.app.translations": app.translations,
+ "bpy.types": types,
+ })
# Initializes Python classes.
# (good place to run a profiler or trace).
diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py
index 30f6c8eebed..a864a86eba7 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -39,16 +39,16 @@ __all__ = (
"reduce_dirs",
"relpath",
"resolve_ncase",
- )
+)
import bpy as _bpy
import os as _os
from _bpy_path import (
- extensions_audio,
- extensions_movie,
- extensions_image,
- )
+ extensions_audio,
+ extensions_movie,
+ extensions_image,
+)
def _getattr_bytes(var, attr):
@@ -71,22 +71,22 @@ def abspath(path, start=None, library=None):
if path.startswith(b"//"):
if library:
start = _os.path.dirname(
- abspath(_getattr_bytes(library, "filepath")))
+ abspath(_getattr_bytes(library, "filepath")))
return _os.path.join(
- _os.path.dirname(_getattr_bytes(_bpy.data, "filepath"))
- if start is None else start,
- path[2:],
- )
+ _os.path.dirname(_getattr_bytes(_bpy.data, "filepath"))
+ if start is None else start,
+ path[2:],
+ )
else:
if path.startswith("//"):
if library:
start = _os.path.dirname(
- abspath(library.filepath))
+ abspath(library.filepath))
return _os.path.join(
- _os.path.dirname(_bpy.data.filepath)
- if start is None else start,
- path[2:],
- )
+ _os.path.dirname(_bpy.data.filepath)
+ if start is None else start,
+ path[2:],
+ )
return path
@@ -175,7 +175,7 @@ def clean_name(name, replace="_"):
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
- )
+ )
trans = str.maketrans({char: replace for char in bad_chars})
trans_cache[replace] = trans
return trans
diff --git a/release/scripts/modules/bpy_extras/keyconfig_utils.py b/release/scripts/modules/bpy_extras/keyconfig_utils.py
index 534dabf3d43..ee0638e254d 100644
--- a/release/scripts/modules/bpy_extras/keyconfig_utils.py
+++ b/release/scripts/modules/bpy_extras/keyconfig_utils.py
@@ -28,14 +28,14 @@ KM_HIERARCHY = [
('Screen', 'EMPTY', 'WINDOW', [ # full screen, undo, screenshot
('Screen Editing', 'EMPTY', 'WINDOW', []), # re-sizing, action corners
('Header', 'EMPTY', 'WINDOW', []), # header stuff (per region)
- ]),
+ ]),
('View2D', 'EMPTY', 'WINDOW', []), # view 2d navigation (per region)
('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation
('User Interface', 'EMPTY', 'WINDOW', [
('Eyedropper Modal Map', 'EMPTY', 'WINDOW', []),
- ]),
+ ]),
('3D View', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform)
('Object Mode', 'EMPTY', 'WINDOW', []),
@@ -71,18 +71,18 @@ KM_HIERARCHY = [
('View3D Dolly Modal', 'EMPTY', 'WINDOW', []),
('3D View Generic', 'VIEW_3D', 'WINDOW', []), # toolbar and properties
- ]),
+ ]),
('Graph Editor', 'GRAPH_EDITOR', 'WINDOW', [
('Graph Editor Generic', 'GRAPH_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Dopesheet', 'DOPESHEET_EDITOR', 'WINDOW', [
('Dopesheet Generic', 'DOPESHEET_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('NLA Editor', 'NLA_EDITOR', 'WINDOW', [
('NLA Channels', 'NLA_EDITOR', 'WINDOW', []),
('NLA Generic', 'NLA_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Timeline', 'TIMELINE', 'WINDOW', []),
('Image', 'IMAGE_EDITOR', 'WINDOW', [
@@ -90,23 +90,23 @@ KM_HIERARCHY = [
('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d
('UV Sculpt', 'EMPTY', 'WINDOW', []),
('Image Generic', 'IMAGE_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Outliner', 'OUTLINER', 'WINDOW', []),
('Node Editor', 'NODE_EDITOR', 'WINDOW', [
('Node Generic', 'NODE_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', [
('SequencerCommon', 'SEQUENCE_EDITOR', 'WINDOW', []),
('SequencerPreview', 'SEQUENCE_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Logic Editor', 'LOGIC_EDITOR', 'WINDOW', []),
('File Browser', 'FILE_BROWSER', 'WINDOW', [
('File Browser Main', 'FILE_BROWSER', 'WINDOW', []),
('File Browser Buttons', 'FILE_BROWSER', 'WINDOW', []),
- ]),
+ ]),
('Info', 'INFO', 'WINDOW', []),
@@ -114,17 +114,17 @@ KM_HIERARCHY = [
('Text', 'TEXT_EDITOR', 'WINDOW', [
('Text Generic', 'TEXT_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Console', 'CONSOLE', 'WINDOW', []),
('Clip', 'CLIP_EDITOR', 'WINDOW', [
('Clip Editor', 'CLIP_EDITOR', 'WINDOW', []),
('Clip Graph Editor', 'CLIP_EDITOR', 'WINDOW', []),
('Clip Dopesheet Editor', 'CLIP_EDITOR', 'WINDOW', []),
- ]),
+ ]),
('Grease Pencil', 'EMPTY', 'WINDOW', [ # grease pencil stuff (per region)
('Grease Pencil Stroke Edit Mode', 'EMPTY', 'WINDOW', []),
- ]),
+ ]),
('Mask Editing', 'EMPTY', 'WINDOW', []),
('Frames', 'EMPTY', 'WINDOW', []), # frame navigation (per region)
('Markers', 'EMPTY', 'WINDOW', []), # markers (per region)
@@ -138,7 +138,7 @@ KM_HIERARCHY = [
('Standard Modal Map', 'EMPTY', 'WINDOW', []),
('Transform Modal Map', 'EMPTY', 'WINDOW', []),
- ]
+]
# -----------------------------------------------------------------------------
@@ -239,7 +239,8 @@ def keyconfig_export(wm, kc, filepath):
" except Exception as e:\n"
" print(\"Warning: %r\" % e)\n\n")
f.write("wm = bpy.context.window_manager\n")
- f.write("kc = wm.keyconfigs.new(os.path.splitext(os.path.basename(__file__))[0])\n\n") # keymap must be created by caller
+ # keymap must be created by caller
+ f.write("kc = wm.keyconfigs.new(os.path.splitext(os.path.basename(__file__))[0])\n\n")
# Generate a list of keymaps to export:
#
@@ -267,7 +268,8 @@ def keyconfig_export(wm, kc, filepath):
km = km.active()
f.write("# Map %s\n" % km.name)
- f.write("km = kc.keymaps.new('%s', space_type='%s', region_type='%s', modal=%s)\n\n" % (km.name, km.space_type, km.region_type, km.is_modal))
+ f.write("km = kc.keymaps.new('%s', space_type='%s', region_type='%s', modal=%s)\n\n" %
+ (km.name, km.space_type, km.region_type, km.is_modal))
for kmi in km.keymap_items:
f.write(_kmistr(kmi, km.is_modal))
f.write("\n")
diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py
index 5f83cdcfa0d..a4834a4531c 100644
--- a/release/scripts/modules/bpy_extras/view3d_utils.py
+++ b/release/scripts/modules/bpy_extras/view3d_utils.py
@@ -23,7 +23,7 @@ __all__ = (
"region_2d_to_origin_3d",
"region_2d_to_location_3d",
"location_3d_to_region_2d",
- )
+)
def region_2d_to_vector_3d(region, rv3d, coord):
@@ -50,7 +50,7 @@ def region_2d_to_vector_3d(region, rv3d, coord):
out = Vector(((2.0 * coord[0] / region.width) - 1.0,
(2.0 * coord[1] / region.height) - 1.0,
-0.5
- ))
+ ))
w = out.dot(persinv[3].xyz) + persinv[3][3]
diff --git a/release/scripts/modules/bpy_restrict_state.py b/release/scripts/modules/bpy_restrict_state.py
index 4658fddf0dc..589b3960b04 100644
--- a/release/scripts/modules/bpy_restrict_state.py
+++ b/release/scripts/modules/bpy_restrict_state.py
@@ -24,7 +24,7 @@ This module contains RestrictBlend context manager.
__all__ = (
"RestrictBlend",
- )
+)
import bpy as _bpy
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index a5b4ed40455..2f26f482a87 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -39,7 +39,7 @@ class Context(StructRNA):
generic_attrs = (
*StructRNA.__dict__.keys(),
"bl_rna", "rna_type", "copy",
- )
+ )
for attr in dir(self):
if not (attr.startswith("_") or attr in generic_attrs):
value = getattr(self, attr)
@@ -542,6 +542,7 @@ class Sound(bpy_types.ID):
class RNAMeta(type):
+
def __new__(cls, name, bases, classdict, **args):
result = type.__new__(cls, name, bases, classdict)
if bases and bases[0] is not StructRNA:
@@ -562,6 +563,7 @@ class RNAMeta(type):
class OrderedDictMini(dict):
+
def __init__(self, *args):
self.order = []
dict.__init__(self, args)
@@ -581,6 +583,7 @@ class RNAMetaPropGroup(StructMetaPropGroup, RNAMeta):
class OrderedMeta(RNAMeta):
+
def __init__(cls, name, bases, attributes):
if attributes.__class__ is OrderedDictMini:
cls.order = attributes.order
@@ -635,7 +638,7 @@ class Macro(StructRNA, metaclass=OrderedMeta):
class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
- __slots__ = ()
+ __slots__ = ()
class RenderEngine(StructRNA, metaclass=RNAMeta):
diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py
index f28f61b303d..e7218594a86 100644
--- a/release/scripts/modules/console/complete_import.py
+++ b/release/scripts/modules/console/complete_import.py
@@ -112,10 +112,10 @@ def module_list(path):
folder_list = []
#folder_list = glob.glob(os.path.join(path,'*'))
folder_list = [
- p for p in folder_list
- if (os.path.exists(os.path.join(path, p, '__init__.py')) or
- p[-3:] in {'.py', '.so'} or
- p[-4:] in {'.pyc', '.pyo', '.pyd'})]
+ p for p in folder_list
+ if (os.path.exists(os.path.join(path, p, '__init__.py')) or
+ p[-3:] in {'.py', '.so'} or
+ p[-4:] in {'.pyc', '.pyo', '.pyd'})]
folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
return folder_list
@@ -161,7 +161,7 @@ def complete(line):
if (not hasattr(m, '__file__')) or (not only_modules) or\
(hasattr(m, '__file__') and '__init__' in m.__file__):
completion_list = [attr for attr in dir(m)
- if is_importable(m, attr)]
+ if is_importable(m, attr)]
else:
completion_list = []
completion_list.extend(getattr(m, '__all__', []))
diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py
index 992bb72aa08..3f223ba93dc 100644
--- a/release/scripts/modules/console/complete_namespace.py
+++ b/release/scripts/modules/console/complete_namespace.py
@@ -85,7 +85,7 @@ def complete_indices(word, namespace, obj=None, base=None):
>>> complete_indices("foo['b", {'foo': {'bar':0, 1:2}}, base='foo')
["foo['bar']"]
"""
- #FIXME: 'foo["b'
+ # FIXME: 'foo["b'
if base is None:
base = word
if obj is None:
@@ -148,7 +148,7 @@ def complete(word, namespace, private=True):
if re_incomplete_index:
# ignore incomplete index at the end, e.g 'a[1' -> 'a'
matches = complete_indices(word, namespace,
- base=re_incomplete_index.group(1))
+ base=re_incomplete_index.group(1))
elif not('[' in word):
matches = complete_names(word, namespace)
diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py
index b694cceafea..84d4bff281d 100644
--- a/release/scripts/modules/console/intellisense.py
+++ b/release/scripts/modules/console/intellisense.py
@@ -121,8 +121,8 @@ def expand(line, cursor, namespace, private=True):
"""
if line[:cursor].strip().endswith('('):
from . import complete_calltip
- matches, word, scrollback = complete_calltip.complete(line,
- cursor, namespace)
+ matches, word, scrollback = complete_calltip.complete(
+ line, cursor, namespace)
prefix = os.path.commonprefix(matches)[len(word):]
no_calltip = False
else:
@@ -138,11 +138,11 @@ def expand(line, cursor, namespace, private=True):
white_space = " " + (" " * (cursor + len(prefix)))
word_prefix = word + prefix
scrollback = '\n'.join(
- [white_space + m[len(word_prefix):]
- if (word_prefix and m.startswith(word_prefix))
- else
- white_space + m.split('.')[-1]
- for m in matches])
+ [white_space + m[len(word_prefix):]
+ if (word_prefix and m.startswith(word_prefix))
+ else
+ white_space + m.split('.')[-1]
+ for m in matches])
no_calltip = True
diff --git a/release/scripts/modules/console_python.py b/release/scripts/modules/console_python.py
index 64bb002d6a1..a740f31c830 100644
--- a/release/scripts/modules/console_python.py
+++ b/release/scripts/modules/console_python.py
@@ -142,9 +142,9 @@ def execute(context, is_interactive):
# redirect output
from contextlib import (
- redirect_stdout,
- redirect_stderr,
- )
+ redirect_stdout,
+ redirect_stderr,
+ )
# not included with Python
class redirect_stdin(redirect_stdout.__base__):
@@ -152,8 +152,8 @@ def execute(context, is_interactive):
# don't allow the stdin to be used, can lock blender.
with redirect_stdout(stdout), \
- redirect_stderr(stderr), \
- redirect_stdin(None):
+ redirect_stderr(stderr), \
+ redirect_stdin(None):
# in case exception happens
line = "" # in case of encoding error
@@ -258,10 +258,10 @@ def autocomplete(context):
# This function isn't aware of the text editor or being an operator
# just does the autocomplete then copy its results back
result = intellisense.expand(
- line=line,
- cursor=current_line.current_character,
- namespace=console.locals,
- private=bpy.app.debug_python)
+ line=line,
+ cursor=current_line.current_character,
+ namespace=console.locals,
+ private=bpy.app.debug_python)
line_new = result[0]
current_line.body, current_line.current_character, scrollback = result
diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py
index 375ee3feebe..8cef64c3590 100644
--- a/release/scripts/modules/keyingsets_utils.py
+++ b/release/scripts/modules/keyingsets_utils.py
@@ -28,7 +28,7 @@ __all__ = (
"RKS_POLL_selected_objects",
"RKS_POLL_selected_bones",
"RKS_POLL_selected_items",
- "RKS_ITER_selected_object",
+ "RKS_ITER_selected_objects",
"RKS_ITER_selected_bones",
"RKS_ITER_selected_item",
"RKS_GEN_available",
@@ -239,6 +239,7 @@ bbone_property_ids = (
"bone.bbone_out",
)
+
# Add Keying Set entries for bendy bones
def RKS_GEN_bendy_bones(ksi, context, ks, data):
# get id-block and path info
diff --git a/release/scripts/modules/progress_report.py b/release/scripts/modules/progress_report.py
index fc77a3e998e..bcce44aab9f 100644
--- a/release/scripts/modules/progress_report.py
+++ b/release/scripts/modules/progress_report.py
@@ -92,7 +92,8 @@ class ProgressReport:
self.wm.progress_update(steps)
if msg:
prefix = " " * (len(self.steps) - 1)
- print(prefix + "(%8.4f sec | %8.4f sec) %s\nProgress: %6.2f%%\r" % (tm, loc_tm, msg, steps_percent), end='')
+ print(prefix + "(%8.4f sec | %8.4f sec) %s\nProgress: %6.2f%%\r" %
+ (tm, loc_tm, msg, steps_percent), end='')
else:
print("Progress: %6.2f%%\r" % (steps_percent,), end='')
diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py
index dae262e93d7..94f5e9e17bb 100644
--- a/release/scripts/modules/rna_info.py
+++ b/release/scripts/modules/rna_info.py
@@ -95,7 +95,7 @@ class InfoStructRNA:
"children",
"references",
"properties",
- )
+ )
global_lookup = {}
@@ -119,8 +119,10 @@ class InfoStructRNA:
def build(self):
rna_type = self.bl_rna
parent_id = self.identifier
- self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id) for rna_prop in get_direct_properties(rna_type) if rna_prop.identifier != "rna_type"]
- self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id) for rna_prop in get_direct_functions(rna_type)]
+ self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id)
+ for rna_prop in get_direct_properties(rna_type) if rna_prop.identifier != "rna_type"]
+ self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id)
+ for rna_prop in get_direct_functions(rna_type)]
def get_bases(self):
bases = []
@@ -216,7 +218,7 @@ class InfoPropertyRNA:
"is_required",
"is_readonly",
"is_never_none",
- )
+ )
global_lookup = {}
def __init__(self, rna_prop):
@@ -362,7 +364,7 @@ class InfoFunctionRNA:
"args",
"return_values",
"is_classmethod",
- )
+ )
global_lookup = {}
def __init__(self, rna_func):
@@ -408,7 +410,7 @@ class InfoOperatorRNA:
"func_name",
"description",
"args",
- )
+ )
global_lookup = {}
def __init__(self, rna_op):
@@ -532,7 +534,7 @@ def BuildRNAInfo():
rna_struct = getattr(rna_type, "bl_rna", None)
if rna_struct:
- #if not rna_type_name.startswith('__'):
+ # if not rna_type_name.startswith('__'):
identifier = rna_struct.identifier
@@ -600,7 +602,8 @@ def BuildRNAInfo():
for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", None)):
# Does this property point to me?
if rna_prop_ptr:
- rna_references_dict[rna_prop_ptr.identifier].append("%s.%s" % (rna_struct_path, rna_prop_identifier))
+ rna_references_dict[rna_prop_ptr.identifier].append(
+ "%s.%s" % (rna_struct_path, rna_prop_identifier))
for rna_func in get_direct_functions(rna_struct):
for rna_prop_identifier, rna_prop in rna_func.parameters.items():
@@ -612,7 +615,8 @@ def BuildRNAInfo():
# Does this property point to me?
if rna_prop_ptr:
- rna_references_dict[rna_prop_ptr.identifier].append("%s.%s" % (rna_struct_path, rna_func.identifier))
+ rna_references_dict[rna_prop_ptr.identifier].append(
+ "%s.%s" % (rna_struct_path, rna_func.identifier))
# Store nested children
nested = rna_struct.nested
@@ -625,8 +629,8 @@ def BuildRNAInfo():
info_structs = []
for (rna_base, identifier, rna_struct) in structs:
- #if rna_struct.nested:
- # continue
+ # if rna_struct.nested:
+ # continue
#write_struct(rna_struct, '')
info_struct = GetInfoStructRNA(rna_struct)
@@ -664,7 +668,8 @@ def BuildRNAInfo():
default = prop.default
if type(default) in {float, int}:
if default < prop.min or default > prop.max:
- print("\t %s.%s, %s not in [%s - %s]" % (rna_info.identifier, prop.identifier, default, prop.min, prop.max))
+ print("\t %s.%s, %s not in [%s - %s]" %
+ (rna_info.identifier, prop.identifier, default, prop.min, prop.max))
# now for operators
op_mods = dir(bpy.ops)
@@ -691,8 +696,8 @@ def BuildRNAInfo():
for rna_prop in rna_info.args:
rna_prop.build()
- #for rna_info in InfoStructRNA.global_lookup.values():
- # print(rna_info)
+ # for rna_info in InfoStructRNA.global_lookup.values():
+ # print(rna_info)
return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup
@@ -701,7 +706,7 @@ def main():
struct = rna_info.BuildRNAInfo()[0]
data = []
for struct_id, v in sorted(struct.items()):
- struct_id_str = v.identifier #~ "".join(sid for sid in struct_id if struct_id)
+ struct_id_str = v.identifier # "".join(sid for sid in struct_id if struct_id)
for base in v.get_bases():
struct_id_str = base.identifier + "|" + struct_id_str
@@ -714,7 +719,10 @@ def main():
if prop.array_length > 0:
prop_type += "[%d]" % prop.array_length
- data.append("%s.%s -> %s: %s%s %s" % (struct_id_str, prop.identifier, prop.identifier, prop_type, ", (read-only)" if prop.is_readonly else "", prop.description))
+ data.append(
+ "%s.%s -> %s: %s%s %s" %
+ (struct_id_str, prop.identifier, prop.identifier, prop_type,
+ ", (read-only)" if prop.is_readonly else "", prop.description))
data.sort()
if bpy.app.background:
diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py
index ad4809efbe1..e8705834df3 100644
--- a/release/scripts/modules/rna_xml.py
+++ b/release/scripts/modules/rna_xml.py
@@ -90,7 +90,7 @@ def rna2xml(fw=print_ln,
bpy.types.PoseBone,
bpy.types.Node,
bpy.types.Sequence,
- )
+ )
def number_to_str(val, val_type):
if val_type == int:
@@ -276,7 +276,8 @@ def xml2rna(root_xml,
if value_xml.startswith("#"):
# read hexidecimal value as float array
value_xml_split = value_xml[1:]
- value_xml_coerce = [int(value_xml_split[i:i + 2], 16) / 255 for i in range(0, len(value_xml_split), 2)]
+ value_xml_coerce = [int(value_xml_split[i:i + 2], 16) /
+ 255 for i in range(0, len(value_xml_split), 2)]
del value_xml_split
else:
value_xml_split = value_xml.split()
diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py
index 49395dd48f0..30b9cdfaf37 100644
--- a/release/scripts/modules/sys_info.py
+++ b/release/scripts/modules/sys_info.py
@@ -24,7 +24,6 @@
def write_sysinfo(filepath):
import sys
- import textwrap
import subprocess
import bpy
@@ -53,7 +52,8 @@ def write_sysinfo(filepath):
# build info
output.write(title("Blender"))
- output.write("version: %s, branch: %s, commit date: %s %s, hash: %s, type: %s\n" %
+ output.write(
+ "version: %s, branch: %s, commit date: %s %s, hash: %s, type: %s\n" %
(bpy.app.version_string,
prepr(bpy.app.build_branch),
prepr(bpy.app.build_commit_date),
@@ -81,9 +81,9 @@ def write_sysinfo(filepath):
output.write("binary path: %s\n" % prepr(bpy.app.binary_path_python))
try:
py_ver = prepr(subprocess.check_output([
- bpy.app.binary_path_python,
- "--version",
- ]).strip())
+ bpy.app.binary_path_python,
+ "--version",
+ ]).strip())
except Exception as e:
py_ver = str(e)
output.write("version: %s\n" % py_ver)
@@ -105,8 +105,9 @@ def write_sysinfo(filepath):
ffmpeg = bpy.app.ffmpeg
if ffmpeg.supported:
for lib in ("avcodec", "avdevice", "avformat", "avutil", "swscale"):
- output.write("%s:%s%r\n" % (lib, " " * (10 - len(lib)),
- getattr(ffmpeg, lib + "_version_string")))
+ output.write(
+ "%s:%s%r\n" % (lib, " " * (10 - len(lib)),
+ getattr(ffmpeg, lib + "_version_string")))
else:
output.write("Blender was built without FFmpeg support\n")
@@ -157,6 +158,13 @@ def write_sysinfo(filepath):
else:
output.write("Blender was built without OpenVDB support\n")
+ alembic = bpy.app.alembic
+ output.write("Alembic: ")
+ if alembic.supported:
+ output.write("%s\n" % alembic.version_string)
+ else:
+ output.write("Blender was built without Alembic support\n")
+
if not bpy.app.build_options.sdl:
output.write("SDL: Blender was built without SDL support\n")
@@ -205,4 +213,3 @@ def write_sysinfo(filepath):
output.write(cycles.engine.system_info())
output.close()
-
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index 4ca2f773dcc..cb5f1595ff3 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -880,6 +880,19 @@ class ConstraintButtonsPanel:
layout.operator("clip.constraint_to_fcurve")
+ def TRANSFORM_CACHE(self, context, layout, con):
+ layout.label(text="Cache File Properties:")
+ box = layout.box()
+ box.template_cache_file(con, "cache_file")
+
+ cache_file = con.cache_file
+
+ layout.label(text="Constraint Properties:")
+ box = layout.box()
+
+ if cache_file is not None:
+ box.prop_search(con, "object_path", cache_file, "object_paths")
+
def SCRIPT(self, context, layout, con):
layout.label("Blender 2.6 doesn't support python constraints yet")
diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py
index 853d45e2396..3a5475514c9 100644
--- a/release/scripts/startup/bl_ui/properties_data_bone.py
+++ b/release/scripts/startup/bl_ui/properties_data_bone.py
@@ -153,7 +153,7 @@ class BONE_PT_curved(BoneButtonsPanel, Panel):
def draw(self, context):
ob = context.object
bone = context.bone
- arm = context.armature
+ # arm = context.armature
pchan = None
if ob and bone:
@@ -268,7 +268,6 @@ class BONE_PT_relations(BoneButtonsPanel, Panel):
sub.prop(bone, "use_local_location")
-
class BONE_PT_display(BoneButtonsPanel, Panel):
bl_label = "Display"
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index 39ab3d926ac..8d60a136511 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -155,14 +155,15 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col = split.column()
col.label(text="Operation:")
col.prop(md, "operation", text="")
- row = layout.row()
- row.label("Solver:")
- row.prop(md, "solver", expand=True)
col = split.column()
col.label(text="Object:")
col.prop(md, "object", text="")
+ split = layout.split()
+ split.column().label(text="Solver:")
+ split.column().prop(md, "solver", text="")
+
if md.solver == 'BMESH':
layout.prop(md, "double_threshold")
@@ -184,6 +185,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
layout.prop(md, "cache_format")
layout.prop(md, "filepath")
+ if md.cache_format == 'ABC':
+ layout.prop(md, "sub_object")
+
layout.label(text="Evaluation:")
layout.prop(md, "factor", slider=True)
layout.prop(md, "deform_mode")
@@ -218,6 +222,22 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
row = split.row()
row.prop(md, "flip_axis")
+ def MESH_SEQUENCE_CACHE(self, layout, ob, md):
+ layout.label(text="Cache File Properties:")
+ box = layout.box()
+ box.template_cache_file(md, "cache_file")
+
+ cache_file = md.cache_file
+
+ layout.label(text="Modifier Properties:")
+ box = layout.box()
+
+ if cache_file is not None:
+ box.prop_search(md, "object_path", cache_file, "object_paths")
+
+ if ob.type == 'MESH':
+ box.row().prop(md, "read_data")
+
def CAST(self, layout, ob, md):
split = layout.split(percentage=0.25)
@@ -883,9 +903,21 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
split = layout.split()
col = split.column()
- col.label(text="Subdivisions:")
- col.prop(md, "levels", text="View")
- col.prop(md, "render_levels", text="Render")
+
+ engine = bpy.context.scene.render.engine
+ if engine == "CYCLES" and md == ob.modifiers[-1] and bpy.context.scene.cycles.feature_set == "EXPERIMENTAL":
+ col.label(text="Preview:")
+ col.prop(md, "levels", text="Levels")
+ col.label(text="Render:")
+ col.prop(ob.cycles, "use_adaptive_subdivision", text="Adaptive")
+ if ob.cycles.use_adaptive_subdivision:
+ col.prop(ob.cycles, "dicing_rate")
+ else:
+ col.prop(md, "render_levels", text="Levels")
+ else:
+ col.label(text="Subdivisions:")
+ col.prop(md, "levels", text="View")
+ col.prop(md, "render_levels", text="Render")
col = split.column()
col.label(text="Options:")
diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py
index 300be708049..386ad254892 100644
--- a/release/scripts/startup/bl_ui/properties_game.py
+++ b/release/scripts/startup/bl_ui/properties_game.py
@@ -568,7 +568,7 @@ class WORLD_PT_game_context_world(WorldButtonsPanel, Panel):
@classmethod
def poll(cls, context):
rd = context.scene.render
- return (context.scene) and (rd.use_game_engine)
+ return (context.scene) and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index ac412688fa1..cabcd49891a 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -63,7 +63,7 @@ class GreasePencilDrawingToolsPanel:
def draw(self, context):
layout = self.layout
- is_3d_view = context.space_data.type == 'VIEW_3D'
+ is_3d_view = context.space_data.type == 'VIEW_3D'
is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
col = layout.column(align=True)
@@ -80,6 +80,7 @@ class GreasePencilDrawingToolsPanel:
sub = col.column(align=True)
sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing")
sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
+ sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back")
col.separator()
col.separator()
@@ -136,7 +137,7 @@ class GreasePencilStrokeEditPanel:
def draw(self, context):
layout = self.layout
- is_3d_view = context.space_data.type == 'VIEW_3D'
+ is_3d_view = context.space_data.type == 'VIEW_3D'
if not is_3d_view:
layout.label(text="Select:")
@@ -151,18 +152,19 @@ class GreasePencilStrokeEditPanel:
col.operator("gpencil.select_linked")
col.operator("gpencil.select_more")
col.operator("gpencil.select_less")
-
- layout.separator()
+ col.operator("gpencil.palettecolor_select")
layout.label(text="Edit:")
row = layout.row(align=True)
row.operator("gpencil.copy", text="Copy")
- row.operator("gpencil.paste", text="Paste")
+ row.operator("gpencil.paste", text="Paste").type = 'COPY'
+ row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE'
col = layout.column(align=True)
- col.operator("gpencil.delete", text="Delete")
+ col.operator("gpencil.delete")
col.operator("gpencil.duplicate_move", text="Duplicate")
- col.operator("transform.mirror", text="Mirror")
+ if is_3d_view:
+ col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
layout.separator()
@@ -176,9 +178,92 @@ class GreasePencilStrokeEditPanel:
col = layout.column(align=True)
col.operator("transform.bend", text="Bend")
+ col.operator("transform.mirror", text="Mirror")
col.operator("transform.shear", text="Shear")
col.operator("transform.tosphere", text="To Sphere")
+ layout.separator()
+ col = layout.column(align=True)
+ col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction")
+ col.operator("gpencil.stroke_change_color", text="Move to Color")
+
+ layout.separator()
+ col = layout.column(align=True)
+ col.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
+ col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
+ col.operator("gpencil.stroke_flip", text="Flip Direction")
+
+ gpd = context.gpencil_data
+ if gpd:
+ col.prop(gpd, "show_stroke_direction", text="Show Directions")
+
+
+class GreasePencilBrushPanel:
+ # subclass must set
+ # bl_space_type = 'IMAGE_EDITOR'
+ bl_label = "Drawing Brushes"
+ bl_category = "Grease Pencil"
+ bl_region_type = 'TOOLS'
+
+ @staticmethod
+ def draw(self, context):
+ layout = self.layout
+
+ row = layout.row()
+ col = row.column()
+ ts = context.scene.tool_settings
+ if len(ts.gpencil_brushes) >= 2:
+ brows = 3
+ else:
+ brows = 2
+ col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows)
+
+ col = row.column()
+
+ sub = col.column(align=True)
+ sub.operator("gpencil.brush_add", icon='ZOOMIN', text="")
+ sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="")
+ sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="")
+ brush = context.active_gpencil_brush
+ if brush:
+ if len(ts.gpencil_brushes) > 1:
+ col.separator()
+ sub = col.column(align=True)
+ sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP'
+ sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN'
+
+ # Brush details
+ if brush is not None:
+ row = layout.row()
+ row.prop(brush, "line_width")
+ row = layout.row(align=True)
+ row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE')
+ row.prop(brush, "pen_sensitivity_factor", slider=True)
+ row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE')
+ row = layout.row(align=True)
+ row.prop(brush, "use_random_strength", text='', icon='RNDCURVE')
+ row.prop(brush, "strength", slider=True)
+ row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE')
+ row = layout.row(align=True)
+ row.prop(brush, "random_press", slider=True)
+
+ row = layout.row(align=True)
+ row.prop(brush, "jitter", slider=True)
+ row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE')
+ row = layout.row()
+ row.prop(brush, "angle", slider=True)
+ row.prop(brush, "angle_factor", text="Factor", slider=True)
+
+ box = layout.box()
+ col = box.column(align=True)
+ col.label(text="Stroke Quality:")
+ col.prop(brush, "pen_smooth_factor")
+ col.prop(brush, "pen_smooth_steps")
+ col.separator()
+ row = col.row(align=False)
+ row.prop(brush, "pen_subdivision_steps")
+ row.prop(brush, "random_subdiv", text='Randomness', slider=True)
+
class GreasePencilStrokeSculptPanel:
# subclass must set
@@ -203,7 +288,7 @@ class GreasePencilStrokeSculptPanel:
tool = settings.tool
brush = settings.brush
- layout.column().prop(settings, "tool", expand=True)
+ layout.column().prop(settings, "tool")
col = layout.column()
col.prop(brush, "size", slider=True)
@@ -211,12 +296,17 @@ class GreasePencilStrokeSculptPanel:
row.prop(brush, "strength", slider=True)
row.prop(brush, "use_pressure_strength", text="")
col.prop(brush, "use_falloff")
+ if tool in {'SMOOTH', 'RANDOMIZE'}:
+ row = layout.row(align=True)
+ row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
+ row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
+ row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
layout.separator()
- if settings.tool == 'THICKNESS':
+ if tool == 'THICKNESS':
layout.row().prop(brush, "direction", expand=True)
- elif settings.tool == 'PINCH':
+ elif tool == 'PINCH':
row = layout.row(align=True)
row.prop_enum(brush, "direction", 'ADD', text="Pinch")
row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate")
@@ -225,13 +315,49 @@ class GreasePencilStrokeSculptPanel:
row.prop_enum(brush, "direction", 'SUBTRACT', text="CW")
row.prop_enum(brush, "direction", 'ADD', text="CCW")
- layout.separator()
- layout.prop(settings, "use_select_mask")
+ row = layout.row(align=True)
+ row.prop(settings, "use_select_mask")
+ row = layout.row(align=True)
+ row.prop(settings, "selection_alpha", slider=True)
- if settings.tool == 'SMOOTH':
+ if tool == 'SMOOTH':
layout.prop(brush, "affect_pressure")
+class GreasePencilBrushCurvesPanel:
+ # subclass must set
+ # bl_space_type = 'IMAGE_EDITOR'
+ bl_label = "Brush Curves"
+ bl_category = "Grease Pencil"
+ bl_region_type = 'TOOLS'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ if context.active_gpencil_brush is None:
+ return False
+
+ brush = context.active_gpencil_brush
+ return bool(brush)
+
+ @staticmethod
+ def draw(self, context):
+ layout = self.layout
+ brush = context.active_gpencil_brush
+ # Brush
+ layout.label("Sensitivity")
+ box = layout.box()
+ box.template_curve_mapping(brush, "curve_sensitivity", brush=True)
+
+ layout.label("Strength")
+ box = layout.box()
+ box.template_curve_mapping(brush, "curve_strength", brush=True)
+
+ layout.label("Jitter")
+ box = layout.box()
+ box.template_curve_mapping(brush, "curve_jitter", brush=True)
+
+
###############################
class GPENCIL_PIE_tool_palette(Menu):
@@ -282,6 +408,7 @@ class GPENCIL_PIE_tool_palette(Menu):
col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
+ col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR')
# NE - Select (Modal)
col = pie.column()
@@ -315,24 +442,47 @@ class GPENCIL_PIE_settings_palette(Menu):
pie = layout.menu_pie()
# gpd = context.gpencil_data
gpl = context.active_gpencil_layer
+ palcolor = context.active_gpencil_palettecolor
+ brush = context.active_gpencil_brush
# W - Stroke draw settings
col = pie.column(align=True)
- col.label(text="Stroke")
- col.prop(gpl, "color", text="")
- col.prop(gpl, "alpha", text="", slider=True)
+ if palcolor is not None:
+ col.label(text="Stroke")
+ col.prop(palcolor, "color", text="")
+ col.prop(palcolor, "alpha", text="", slider=True)
# E - Fill draw settings
col = pie.column(align=True)
- col.label(text="Fill")
- col.prop(gpl, "fill_color", text="")
- col.prop(gpl, "fill_alpha", text="", slider=True)
+ if palcolor is not None:
+ col.label(text="Fill")
+ col.prop(palcolor, "fill_color", text="")
+ col.prop(palcolor, "fill_alpha", text="", slider=True)
- # S - Layer settings
+ # S Brush settings
col = pie.column()
- col.prop(gpl, "line_width", slider=True)
- # col.prop(gpl, "use_volumetric_strokes")
- col.prop(gpl, "use_onion_skinning")
+ col.label("Active Brush: ")
+
+ row = col.row()
+ row.operator_context = 'EXEC_REGION_WIN'
+ row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA')
+ row.prop(brush, "name", text="")
+
+ col.prop(brush, "line_width", slider=True)
+ row = col.row(align=True)
+ row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE')
+ row.prop(brush, "pen_sensitivity_factor", slider=True)
+ row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE')
+ row = col.row(align=True)
+ row.prop(brush, "use_random_strength", text='', icon='RNDCURVE')
+ row.prop(brush, "strength", slider=True)
+ row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE')
+ row = col.row(align=True)
+ row.prop(brush, "jitter", slider=True)
+ row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE')
+ row = col.row()
+ row.prop(brush, "angle", slider=True)
+ row.prop(brush, "angle_factor", text="Factor", slider=True)
# N - Active Layer
col = pie.column()
@@ -347,6 +497,35 @@ class GPENCIL_PIE_settings_palette(Menu):
row = col.row()
row.prop(gpl, "lock")
row.prop(gpl, "hide")
+ col.prop(gpl, "use_onion_skinning")
+
+ # NW - Move stroke Down
+ col = pie.column(align=True)
+ col.label("Arrange Strokes")
+ col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM'
+ col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN'
+
+ # NE - Move stroke Up
+ col = pie.column(align=True)
+ col.label("Arrange Strokes")
+ col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP'
+ col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP'
+
+ # SW - Move stroke to color
+ col = pie.column(align=True)
+ col.operator("gpencil.stroke_change_color", text="Move to Color")
+
+ # SE - Join strokes
+ col = pie.column(align=True)
+ col.label("Join Strokes")
+ row = col.row()
+ row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
+ row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
+ col.operator("gpencil.stroke_flip", text="Flip direction")
+
+ gpd = context.gpencil_data
+ if gpd:
+ col.prop(gpd, "show_stroke_direction", text="Show drawing direction")
class GPENCIL_PIE_tools_more(Menu):
@@ -411,6 +590,11 @@ class GPENCIL_PIE_sculpt(Menu):
row.prop(brush, "strength", slider=True)
# row.prop(brush, "use_pressure_strength", text="", icon_only=True)
col.prop(brush, "use_falloff")
+ if settings.tool in {'SMOOTH', 'RANDOMIZE'}:
+ row = col.row(align=True)
+ row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
+ row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
+ row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
# S - Change Brush Type Shortcuts
row = pie.row()
@@ -422,6 +606,7 @@ class GPENCIL_PIE_sculpt(Menu):
row = pie.row()
row.prop_enum(settings, "tool", value='SMOOTH')
row.prop_enum(settings, "tool", value='THICKNESS')
+ row.prop_enum(settings, "tool", value='STRENGTH')
row.prop_enum(settings, "tool", value='RANDOMIZE')
@@ -448,6 +633,48 @@ class GPENCIL_MT_snap(Menu):
###############################
+class GPENCIL_UL_brush(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ # assert(isinstance(item, bpy.types.GPencilBrush)
+ brush = item
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ row = layout.row(align=True)
+ row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA')
+ elif self.layout_type == 'GRID':
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
+class GPENCIL_UL_palettecolor(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ # assert(isinstance(item, bpy.types.PaletteColor)
+ palcolor = item
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ if palcolor.lock:
+ layout.active = False
+
+ split = layout.split(percentage=0.25)
+ row = split.row(align=True)
+ row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible)
+ row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible)
+ split.prop(palcolor, "info", text="", emboss=False)
+
+ row = layout.row(align=True)
+ row.prop(palcolor, "lock", text="", emboss=False)
+ row.prop(palcolor, "hide", text="", emboss=False)
+ if palcolor.ghost is True:
+ icon = 'GHOST_DISABLED'
+ else:
+ icon = 'GHOST_ENABLED'
+ row.prop(palcolor, "ghost", text="", icon=icon, emboss=False)
+
+ elif self.layout_type == 'GRID':
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
class GPENCIL_UL_layer(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# assert(isinstance(item, bpy.types.GPencilLayer)
@@ -457,15 +684,19 @@ class GPENCIL_UL_layer(UIList):
if gpl.lock:
layout.active = False
- split = layout.split(percentage=0.25)
- row = split.row(align=True)
- row.prop(gpl, "color", text="", emboss=gpl.is_stroke_visible)
- row.prop(gpl, "fill_color", text="", emboss=gpl.is_fill_visible)
- split.prop(gpl, "info", text="", emboss=False)
+ row = layout.row(align=True)
+ if gpl.is_parented:
+ icon = 'BONE_DATA'
+ else:
+ icon = 'BLANK1'
+
+ row.label(text="", icon=icon)
+ row.prop(gpl, "info", text="", emboss=False)
row = layout.row(align=True)
row.prop(gpl, "lock", text="", emboss=False)
row.prop(gpl, "hide", text="", emboss=False)
+ row.prop(gpl, "unlock_color", text="", emboss=False)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
@@ -489,11 +720,40 @@ class GPENCIL_MT_layer_specials(Menu):
layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
+ layout.separator()
+
+ layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down")
+
+
+class GPENCIL_MT_brush_specials(Menu):
+ bl_label = "Layer"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy current drawing brush")
+ layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a set of predefined brushes")
+
+
+class GPENCIL_MT_palettecolor_specials(Menu):
+ bl_label = "Layer"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
+ layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
+
+ layout.separator()
+
+ layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All")
+ layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All")
+ layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color")
+
class GreasePencilDataPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
- bl_label = "Grease Pencil"
+ bl_label = "Grease Pencil Layers"
bl_region_type = 'UI'
@staticmethod
@@ -553,45 +813,49 @@ class GreasePencilDataPanel:
col.separator()
sub = col.column(align=True)
- sub.operator("gpencil.layer_isolate", icon='SOLO_OFF', text="").affect_visibility = False
+ sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
if gpl:
- self.draw_layer(layout, gpl)
+ self.draw_layer(context, layout, gpl)
- def draw_layer(self, layout, gpl):
- # layer settings
+ def draw_layer(self, context, layout, gpl):
+ row = layout.row(align=True)
+ row.prop(gpl, "opacity", text="Opacity", slider=True)
+
+ # Layer options
split = layout.split(percentage=0.5)
split.active = not gpl.lock
+ split.prop(gpl, "show_x_ray")
+ split.prop(gpl, "show_points")
- # Column 1 - Stroke
- col = split.column(align=True)
- col.label(text="Stroke:")
- col.prop(gpl, "color", text="")
- col.prop(gpl, "alpha", slider=True)
-
- # Column 2 - Fill
- col = split.column(align=True)
- col.label(text="Fill:")
- col.prop(gpl, "fill_color", text="")
- col.prop(gpl, "fill_alpha", text="Opacity", slider=True)
+ # Offsets + Parenting (where available)
+ split = layout.split(percentage=0.5)
+ split.active = not gpl.lock
- # Options
- col = layout.column(align=True)
- col.active = not gpl.lock
- col.prop(gpl, "line_width", slider=True)
+ # Offsets - Color Tint
+ col = split.column()
+ subcol = col.column(align=True)
+ subcol.label("Tint")
+ subcol.prop(gpl, "tint_color", text="")
+ subcol.prop(gpl, "tint_factor", text="Factor", slider=True)
+ # Offsets - Thickness
+ row = col.row(align=True)
+ row.prop(gpl, "line_change", text="Thickness Change", slider=True)
+ row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="")
- split = layout.split(percentage=0.5)
- split.active = not gpl.lock
+ # Parenting
+ if context.space_data.type == 'VIEW_3D':
+ col = split.column(align=True)
+ col.label(text="Parent:")
+ col.prop(gpl, "parent", text="")
- col = split.column(align=True)
- col.prop(gpl, "use_volumetric_strokes")
- col.prop(gpl, "show_points", text="Points")
-
- col = split.column(align=True)
- col.prop(gpl, "use_hq_fill")
- col.prop(gpl, "show_x_ray")
+ sub = col.column()
+ sub.prop(gpl, "parent_type", text="")
+ parent = gpl.parent
+ if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
+ sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="")
layout.separator()
@@ -634,14 +898,103 @@ class GreasePencilDataPanel:
row.prop(gpl, "after_color", text="")
sub.prop(gpl, "ghost_after_range", text="After")
- # Smooth and subdivide new strokes
- layout.separator()
- col = layout.column(align=True)
- col.label(text="New Stroke Quality:")
- col.prop(gpl, "pen_smooth_factor")
- col.prop(gpl, "pen_smooth_steps")
- col.separator()
- col.prop(gpl, "pen_subdivision_steps")
+
+class GreasePencilPaletteColorPanel:
+ # subclass must set
+ bl_label = "Grease Pencil Colors"
+ bl_region_type = 'UI'
+
+ @classmethod
+ def poll(cls, context):
+ if context.gpencil_data is None:
+ return False
+
+ gpd = context.gpencil_data
+ return bool(gpd.layers.active)
+
+ @staticmethod
+ def draw(self, context):
+ layout = self.layout
+ palette = context.active_gpencil_palette
+
+ if palette:
+ row = layout.row(align=True)
+ row.operator_context = 'EXEC_REGION_WIN'
+ row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR')
+ row.prop(palette, "name", text="")
+ row.operator("gpencil.palette_add", icon='ZOOMIN', text="")
+ row.operator("gpencil.palette_remove", icon='X', text="")
+
+ # Palette colors
+ row = layout.row()
+ col = row.column()
+ if len(palette.colors) >= 2:
+ color_rows = 5
+ else:
+ color_rows = 2
+ col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index",
+ rows=color_rows)
+
+ col = row.column()
+
+ sub = col.column(align=True)
+ sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="")
+ sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="")
+
+ palcol = context.active_gpencil_palettecolor
+ if palcol:
+ sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="")
+
+ if len(palette.colors) > 1:
+ col.separator()
+
+ sub = col.column(align=True)
+ sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP'
+ sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
+
+ col.separator()
+ sub = col.column(align=True)
+ sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False
+ sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
+ sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="")
+ sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="")
+
+ pcolor = palette.colors.active
+ if pcolor:
+ self.draw_palettecolors(layout, pcolor)
+
+ # ----------------------------------------------
+ # Draw palette colors
+ # ----------------------------------------------
+ def draw_palettecolors(self, layout, pcolor):
+ # color settings
+ split = layout.split(percentage=0.5)
+ split.active = not pcolor.lock
+
+ # Column 1 - Stroke
+ col = split.column(align=True)
+ col.active = not pcolor.lock
+ col.label(text="Stroke:")
+ col.prop(pcolor, "color", text="")
+ col.prop(pcolor, "alpha", slider=True)
+
+ # Column 2 - Fill
+ col = split.column(align=True)
+ col.active = not pcolor.lock
+ col.label(text="Fill:")
+ col.prop(pcolor, "fill_color", text="")
+ col.prop(pcolor, "fill_alpha", text="Opacity", slider=True)
+
+ # Options
+ split = layout.split(percentage=0.5)
+ split.active = not pcolor.lock
+
+ col = split.column(align=True)
+ col.active = not pcolor.lock
+ col.prop(pcolor, "use_volumetric_strokes")
+ col = split.column(align=True)
+ col.active = not pcolor.lock
+ col.prop(pcolor, "use_hq_fill")
class GreasePencilToolsPanel:
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index 08290f20a69..89ea9dff69b 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -605,6 +605,8 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, Panel):
row.prop(part, "use_size_deflect")
row.prop(part, "use_die_on_collision")
+ layout.prop(part, "collision_group")
+
if part.physics_type == 'FLUID':
fluid = part.fluid
@@ -714,6 +716,8 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, Panel):
col.prop(boids, "land_personal_space")
col.prop(boids, "land_stick_force")
+ layout.prop(part, "collision_group")
+
split = layout.split()
col = split.column(align=True)
diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py
index a5cbffb2e2c..3ebf2691b4c 100644
--- a/release/scripts/startup/bl_ui/properties_physics_cloth.py
+++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py
@@ -46,11 +46,12 @@ class PhysicButtonsPanel:
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and ob.type == 'MESH') and (not rd.use_game_engine) and (context.cloth)
+ return (ob and ob.type == 'MESH') and (rd.engine in cls.COMPAT_ENGINES) and (context.cloth)
class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
bl_label = "Cloth"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@@ -59,20 +60,29 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
ob = context.object
cloth = md.settings
- split = layout.split()
-
- split.active = cloth_panel_enabled(md)
+ layout.active = cloth_panel_enabled(md)
- col = split.column()
+ split = layout.split(percentage=0.25)
- col.label(text="Presets:")
- sub = col.row(align=True)
+ split.label(text="Presets:")
+ sub = split.row(align=True)
sub.menu("CLOTH_MT_presets", text=bpy.types.CLOTH_MT_presets.bl_label)
sub.operator("cloth.preset_add", text="", icon='ZOOMIN')
sub.operator("cloth.preset_add", text="", icon='ZOOMOUT').remove_active = True
- col.label(text="Quality:")
- col.prop(cloth, "quality", text="Steps", slider=True)
+ split = layout.split(percentage=0.25)
+
+ split.label(text="Quality:")
+ split.prop(cloth, "quality", text="Steps")
+
+ split = layout.split(percentage=0.25)
+
+ split.label(text="Speed:")
+ split.prop(cloth, "time_scale", text="Multiplier")
+
+ split = layout.split()
+
+ col = split.column()
col.label(text="Material:")
col.prop(cloth, "mass")
@@ -86,7 +96,11 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
col.prop(cloth, "air_damping", text="Air")
col.prop(cloth, "vel_damping", text="Velocity")
- col.prop(cloth, "use_pin_cloth", text="Pinning")
+ split = layout.split()
+
+ col = split.column()
+
+ col.prop(cloth, "use_pin_cloth", text="Pinning:")
sub = col.column()
sub.active = cloth.use_pin_cloth
sub.prop_search(cloth, "vertex_group_mass", ob, "vertex_groups", text="")
@@ -103,16 +117,23 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, Panel):
col.prop(cloth, "goal_friction", text="Friction")
"""
+ col = split.column()
+
+ col.prop(cloth, "use_dynamic_mesh", text="Dynamic Mesh")
+
key = ob.data.shape_keys
if key:
- col.label(text="Rest Shape Key:")
- col.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="")
+ sub = col.column()
+ sub.active = not cloth.use_dynamic_mesh
+ sub.label(text="Rest Shape Key:")
+ sub.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="")
class PHYSICS_PT_cloth_cache(PhysicButtonsPanel, Panel):
bl_label = "Cloth Cache"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
md = context.cloth
@@ -122,6 +143,7 @@ class PHYSICS_PT_cloth_cache(PhysicButtonsPanel, Panel):
class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
bl_label = "Cloth Collision"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw_header(self, context):
cloth = context.cloth.collision_settings
@@ -141,7 +163,7 @@ class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
split = layout.split()
col = split.column()
- col.prop(cloth, "collision_quality", slider=True, text="Quality")
+ col.prop(cloth, "collision_quality", text="Quality")
col.prop(cloth, "distance_min", slider=True, text="Distance")
col.prop(cloth, "repel_force", slider=True, text="Repel")
col.prop(cloth, "distance_repel", slider=True, text="Repel Distance")
@@ -151,7 +173,7 @@ class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
col.prop(cloth, "use_self_collision", text="Self Collision")
sub = col.column()
sub.active = cloth.use_self_collision
- sub.prop(cloth, "self_collision_quality", slider=True, text="Quality")
+ sub.prop(cloth, "self_collision_quality", text="Quality")
sub.prop(cloth, "self_distance_min", slider=True, text="Distance")
sub.prop_search(cloth, "vertex_group_self_collisions", ob, "vertex_groups", text="")
@@ -161,6 +183,7 @@ class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
class PHYSICS_PT_cloth_stiffness(PhysicButtonsPanel, Panel):
bl_label = "Cloth Stiffness Scaling"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw_header(self, context):
cloth = context.cloth.settings
@@ -193,6 +216,7 @@ class PHYSICS_PT_cloth_stiffness(PhysicButtonsPanel, Panel):
class PHYSICS_PT_cloth_sewing(PhysicButtonsPanel, Panel):
bl_label = "Cloth Sewing Springs"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw_header(self, context):
cloth = context.cloth.settings
@@ -226,6 +250,7 @@ class PHYSICS_PT_cloth_sewing(PhysicButtonsPanel, Panel):
class PHYSICS_PT_cloth_field_weights(PhysicButtonsPanel, Panel):
bl_label = "Cloth Field Weights"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
cloth = context.cloth.settings
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 82eecf0fb5a..277b59d187d 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -31,7 +31,7 @@ class PhysicButtonsPanel:
@classmethod
def poll(cls, context):
rd = context.scene.render
- return (context.object) and (not rd.use_game_engine)
+ return (context.object) and rd.engine in cls.COMPAT_ENGINES
def physics_add(self, layout, md, name, type, typeicon, toggles):
@@ -57,6 +57,7 @@ def physics_add_special(self, layout, data, name, addop, removeop, typeicon):
class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
obj = context.object
diff --git a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
index 269ffa6d371..6c3a3246cf6 100644
--- a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
+++ b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py
@@ -56,11 +56,12 @@ class PhysicButtonsPanel:
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and ob.type == 'MESH') and (not rd.use_game_engine) and context.dynamic_paint
+ return (ob and ob.type == 'MESH') and rd.engine in cls.COMPAT_ENGINES and context.dynamic_paint
class PHYSICS_PT_dynamic_paint(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@@ -137,12 +138,13 @@ class PHYSICS_PT_dynamic_paint(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_advanced_canvas(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Advanced"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.dynamic_paint
rd = context.scene.render
- return md and md.ui_type == 'CANVAS' and md.canvas_settings and md.canvas_settings.canvas_surfaces.active and (not rd.use_game_engine)
+ return md and md.ui_type == 'CANVAS' and md.canvas_settings and md.canvas_settings.canvas_surfaces.active and rd.engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
@@ -213,6 +215,7 @@ class PHYSICS_PT_dp_advanced_canvas(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_canvas_output(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Output"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -223,7 +226,7 @@ class PHYSICS_PT_dp_canvas_output(PhysicButtonsPanel, Panel):
surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
return (surface and
(not (surface.surface_format == 'VERTEX' and (surface.surface_type in {'DISPLACE', 'WAVE'}))) and
- (not rd.use_game_engine))
+ (rd.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
@@ -306,6 +309,7 @@ class PHYSICS_PT_dp_canvas_output(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_canvas_initial_color(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Initial Color"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -314,7 +318,7 @@ class PHYSICS_PT_dp_canvas_initial_color(PhysicButtonsPanel, Panel):
if not (md and md.ui_type == 'CANVAS' and md.canvas_settings):
return 0
surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
- return (surface and surface.surface_type == 'PAINT') and (not rd.use_game_engine)
+ return (surface and surface.surface_type == 'PAINT') and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -342,6 +346,7 @@ class PHYSICS_PT_dp_canvas_initial_color(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_effects(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Effects"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -350,7 +355,7 @@ class PHYSICS_PT_dp_effects(PhysicButtonsPanel, Panel):
if not (md and md.ui_type == 'CANVAS' and md.canvas_settings):
return False
surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
- return (surface and surface.surface_type == 'PAINT') and (not rd.use_game_engine)
+ return (surface and surface.surface_type == 'PAINT') and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -391,6 +396,7 @@ class PHYSICS_PT_dp_effects(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_cache(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Cache"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -401,7 +407,7 @@ class PHYSICS_PT_dp_cache(PhysicButtonsPanel, Panel):
md.canvas_settings and
md.canvas_settings.canvas_surfaces.active and
md.canvas_settings.canvas_surfaces.active.is_cache_user and
- (not rd.use_game_engine))
+ (rd.engine in cls.COMPAT_ENGINES))
def draw(self, context):
surface = context.dynamic_paint.canvas_settings.canvas_surfaces.active
@@ -412,12 +418,13 @@ class PHYSICS_PT_dp_cache(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Source"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.dynamic_paint
rd = context.scene.render
- return md and md.ui_type == 'BRUSH' and md.brush_settings and (not rd.use_game_engine)
+ return md and md.ui_type == 'BRUSH' and md.brush_settings and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -465,12 +472,13 @@ class PHYSICS_PT_dp_brush_source(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_brush_velocity(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Velocity"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.dynamic_paint
rd = context.scene.render
- return md and md.ui_type == 'BRUSH' and md.brush_settings and (not rd.use_game_engine)
+ return md and md.ui_type == 'BRUSH' and md.brush_settings and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -501,12 +509,13 @@ class PHYSICS_PT_dp_brush_velocity(PhysicButtonsPanel, Panel):
class PHYSICS_PT_dp_brush_wave(PhysicButtonsPanel, Panel):
bl_label = "Dynamic Paint Waves"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.dynamic_paint
rd = context.scene.render
- return md and md.ui_type == 'BRUSH' and md.brush_settings and (not rd.use_game_engine)
+ return md and md.ui_type == 'BRUSH' and md.brush_settings and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py
index 348f66da145..1cebc0496b0 100644
--- a/release/scripts/startup/bl_ui/properties_physics_field.py
+++ b/release/scripts/startup/bl_ui/properties_physics_field.py
@@ -34,17 +34,18 @@ class PhysicButtonsPanel:
@classmethod
def poll(cls, context):
rd = context.scene.render
- return (context.object) and (not rd.use_game_engine)
+ return (context.object) and (rd.engine in cls.COMPAT_ENGINES)
class PHYSICS_PT_field(PhysicButtonsPanel, Panel):
bl_label = "Force Fields"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (not rd.use_game_engine) and (ob.field) and (ob.field.type != 'NONE')
+ return (rd.engine in cls.COMPAT_ENGINES) and (ob.field) and (ob.field.type != 'NONE')
def draw(self, context):
layout = self.layout
@@ -176,12 +177,13 @@ class PHYSICS_PT_field(PhysicButtonsPanel, Panel):
class PHYSICS_PT_collision(PhysicButtonsPanel, Panel):
bl_label = "Collision"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and ob.type == 'MESH') and (not rd.use_game_engine) and (context.collision)
+ return (ob and ob.type == 'MESH') and (rd.engine in cls.COMPAT_ENGINES) and (context.collision)
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py
index d6fd8f3792c..5d7034c2e68 100644
--- a/release/scripts/startup/bl_ui/properties_physics_fluid.py
+++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py
@@ -38,11 +38,12 @@ class PhysicButtonsPanel:
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and ob.type == 'MESH') and (not rd.use_game_engine) and (context.fluid)
+ return (ob and ob.type == 'MESH') and rd.engine in cls.COMPAT_ENGINES and (context.fluid)
class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel):
bl_label = "Fluid"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@@ -205,12 +206,13 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel):
class PHYSICS_PT_domain_gravity(PhysicButtonsPanel, Panel):
bl_label = "Fluid World"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.fluid
rd = context.scene.render
- return md and md.settings and (md.settings.type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and md.settings and (md.settings.type == 'DOMAIN') and rd.engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
@@ -258,12 +260,13 @@ class PHYSICS_PT_domain_gravity(PhysicButtonsPanel, Panel):
class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, Panel):
bl_label = "Fluid Boundary"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.fluid
rd = context.scene.render
- return md and md.settings and (md.settings.type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and md.settings and (md.settings.type == 'DOMAIN') and rd.engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
@@ -288,12 +291,13 @@ class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, Panel):
class PHYSICS_PT_domain_particles(PhysicButtonsPanel, Panel):
bl_label = "Fluid Particles"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.fluid
rd = context.scene.render
- return md and md.settings and (md.settings.type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and md.settings and (md.settings.type == 'DOMAIN') and rd.engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
index e7225d73e41..eeb21c046bf 100644
--- a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
+++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py
@@ -29,12 +29,13 @@ class PHYSICS_PT_rigidbody_panel:
class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Rigid Body"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
obj = context.object
return (obj and obj.rigid_body and
- (not context.scene.render.use_game_engine))
+ (context.scene.render.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
@@ -55,12 +56,13 @@ class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel):
class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Rigid Body Collisions"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
obj = context.object
return (obj and obj.rigid_body and
- (not context.scene.render.use_game_engine))
+ (context.scene.render.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
@@ -99,13 +101,14 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel):
class PHYSICS_PT_rigid_body_dynamics(PHYSICS_PT_rigidbody_panel, Panel):
bl_label = "Rigid Body Dynamics"
bl_default_closed = True
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
obj = context.object
return (obj and obj.rigid_body and
obj.rigid_body.type == 'ACTIVE' and
- (not context.scene.render.use_game_engine))
+ (context.scene.render.engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
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 3f5e0f1fc9b..38c97746f4a 100644
--- a/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody_constraint.py
@@ -29,12 +29,13 @@ class PHYSICS_PT_rigidbody_constraint_panel:
class PHYSICS_PT_rigid_body_constraint(PHYSICS_PT_rigidbody_constraint_panel, Panel):
bl_label = "Rigid Body Constraint"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and ob.rigid_body_constraint and (not rd.use_game_engine))
+ return (ob and ob.rigid_body_constraint and rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index ef7d25e0a42..99aa54a958a 100644
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -35,11 +35,12 @@ class PhysicButtonsPanel:
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and ob.type == 'MESH') and (not rd.use_game_engine) and (context.smoke)
+ return (ob and ob.type == 'MESH') and (rd.engine in cls.COMPAT_ENGINES) and (context.smoke)
class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
bl_label = "Smoke"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@@ -131,6 +132,7 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_flow_advanced(PhysicButtonsPanel, Panel):
bl_label = "Smoke Flow Advanced"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -165,6 +167,7 @@ class PHYSICS_PT_smoke_flow_advanced(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
bl_label = "Smoke Flames"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -194,6 +197,7 @@ class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
bl_label = "Smoke Adaptive Domain"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
@@ -227,12 +231,13 @@ class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
bl_label = "Smoke High Resolution"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
md = context.smoke.domain_settings
@@ -266,12 +271,13 @@ class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_groups(PhysicButtonsPanel, Panel):
bl_label = "Smoke Groups"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -294,12 +300,13 @@ class PHYSICS_PT_smoke_groups(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
bl_label = "Smoke Cache"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -330,12 +337,13 @@ class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
bl_label = "Smoke Field Weights"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
domain = context.smoke.domain_settings
diff --git a/release/scripts/startup/bl_ui/properties_physics_softbody.py b/release/scripts/startup/bl_ui/properties_physics_softbody.py
index e74cac3d06b..a458af739f2 100644
--- a/release/scripts/startup/bl_ui/properties_physics_softbody.py
+++ b/release/scripts/startup/bl_ui/properties_physics_softbody.py
@@ -39,11 +39,12 @@ class PhysicButtonsPanel:
def poll(cls, context):
ob = context.object
rd = context.scene.render
- return (ob and (ob.type == 'MESH' or ob.type == 'LATTICE'or ob.type == 'CURVE')) and (not rd.use_game_engine) and (context.soft_body)
+ return (ob and (ob.type == 'MESH' or ob.type == 'LATTICE'or ob.type == 'CURVE')) and (rd.engine in cls.COMPAT_ENGINES) and (context.soft_body)
class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel):
bl_label = "Soft Body"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@@ -67,10 +68,13 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel):
col.label(text="Simulation:")
col.prop(softbody, "speed")
+ layout.prop(softbody, "collision_group")
+
class PHYSICS_PT_softbody_cache(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Cache"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
md = context.soft_body
@@ -80,6 +84,7 @@ class PHYSICS_PT_softbody_cache(PhysicButtonsPanel, Panel):
class PHYSICS_PT_softbody_goal(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Goal"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw_header(self, context):
softbody = context.soft_body.settings
@@ -119,6 +124,7 @@ class PHYSICS_PT_softbody_goal(PhysicButtonsPanel, Panel):
class PHYSICS_PT_softbody_edge(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Edges"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw_header(self, context):
softbody = context.soft_body.settings
@@ -168,6 +174,7 @@ class PHYSICS_PT_softbody_edge(PhysicButtonsPanel, Panel):
class PHYSICS_PT_softbody_collision(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Self Collision"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw_header(self, context):
softbody = context.soft_body.settings
@@ -196,6 +203,7 @@ class PHYSICS_PT_softbody_collision(PhysicButtonsPanel, Panel):
class PHYSICS_PT_softbody_solver(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Solver"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@@ -228,6 +236,7 @@ class PHYSICS_PT_softbody_solver(PhysicButtonsPanel, Panel):
class PHYSICS_PT_softbody_field_weights(PhysicButtonsPanel, Panel):
bl_label = "Soft Body Field Weights"
bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
def draw(self, context):
md = context.soft_body
diff --git a/release/scripts/startup/bl_ui/properties_world.py b/release/scripts/startup/bl_ui/properties_world.py
index 2c8a6fac967..e07349a99ee 100644
--- a/release/scripts/startup/bl_ui/properties_world.py
+++ b/release/scripts/startup/bl_ui/properties_world.py
@@ -41,7 +41,7 @@ class WORLD_PT_context_world(WorldButtonsPanel, Panel):
@classmethod
def poll(cls, context):
rd = context.scene.render
- return (not rd.use_game_engine) and (rd.engine in cls.COMPAT_ENGINES)
+ return rd.engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
@@ -69,7 +69,7 @@ class WORLD_PT_preview(WorldButtonsPanel, Panel):
@classmethod
def poll(cls, context):
rd = context.scene.render
- return (context.world) and (not rd.use_game_engine) and (rd.engine in cls.COMPAT_ENGINES)
+ return (context.world) and (rd.engine in cls.COMPAT_ENGINES)
def draw(self, context):
self.layout.template_preview(context.world)
diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py
index 58bb956f653..799f1e20dc6 100644
--- a/release/scripts/startup/bl_ui/space_clip.py
+++ b/release/scripts/startup/bl_ui/space_clip.py
@@ -25,8 +25,10 @@ from bl_ui.properties_grease_pencil_common import (
GreasePencilDrawingToolsPanel,
GreasePencilStrokeEditPanel,
GreasePencilStrokeSculptPanel,
- GreasePencilDataPanel
- )
+ GreasePencilBrushPanel,
+ GreasePencilBrushCurvesPanel,
+ GreasePencilDataPanel,
+ GreasePencilPaletteColorPanel)
class CLIP_UL_tracking_objects(UIList):
@@ -1126,6 +1128,16 @@ class CLIP_PT_grease_pencil(GreasePencilDataPanel, CLIP_PT_clip_view_panel, Pane
# But, this should only be visible in "clip" view
+# Grease Pencil palette colors
+class CLIP_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, CLIP_PT_clip_view_panel, Panel):
+ bl_space_type = 'CLIP_EDITOR'
+ bl_region_type = 'UI'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ # NOTE: this is just a wrapper around the generic GP Panel
+ # But, this should only be visible in "clip" view
+
+
# Grease Pencil drawing tools
class CLIP_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
bl_space_type = 'CLIP_EDITOR'
@@ -1141,6 +1153,15 @@ class CLIP_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
bl_space_type = 'CLIP_EDITOR'
+# Grease Pencil drawing brushes
+class CLIP_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel):
+ bl_space_type = 'CLIP_EDITOR'
+
+
+# Grease Pencil drawing curves
+class CLIP_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel):
+ bl_space_type = 'CLIP_EDITOR'
+
class CLIP_MT_view(Menu):
bl_label = "View"
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index d6d91980201..4d365c8dc08 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -103,6 +103,7 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
layout.prop(dopesheet, "use_datablock_sort", text="")
+
#######################################
# DopeSheet Editor - General/Standard UI
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index c191a4b5bdc..bf6df05c2b2 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -29,7 +29,10 @@ from bl_ui.properties_grease_pencil_common import (
GreasePencilDrawingToolsPanel,
GreasePencilStrokeEditPanel,
GreasePencilStrokeSculptPanel,
+ GreasePencilBrushPanel,
+ GreasePencilBrushCurvesPanel,
GreasePencilDataPanel,
+ GreasePencilPaletteColorPanel
)
from bpy.app.translations import pgettext_iface as iface_
@@ -729,7 +732,6 @@ class IMAGE_PT_paint(Panel, ImagePaintPanel):
@classmethod
def poll(cls, context):
sima = context.space_data
- toolsettings = context.tool_settings.image_paint
return sima.show_paint
@@ -987,7 +989,7 @@ class IMAGE_PT_tools_paint_options(BrushButtonsPanel, Panel):
layout = self.layout
toolsettings = context.tool_settings
- brush = toolsettings.image_paint.brush
+ # brush = toolsettings.image_paint.brush
ups = toolsettings.unified_paint_settings
@@ -1188,6 +1190,14 @@ class IMAGE_PT_grease_pencil(GreasePencilDataPanel, Panel):
# NOTE: this is just a wrapper around the generic GP Panel
+# Grease Pencil palette colors
+class IMAGE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel):
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'UI'
+
+ # NOTE: this is just a wrapper around the generic GP Panel
+
+
# Grease Pencil drawing tools
class IMAGE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
bl_space_type = 'IMAGE_EDITOR'
@@ -1203,5 +1213,15 @@ class IMAGE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
bl_space_type = 'IMAGE_EDITOR'
+# Grease Pencil drawing brushes
+class IMAGE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel):
+ bl_space_type = 'IMAGE_EDITOR'
+
+
+# Grease Pencil drawing curves
+class IMAGE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel):
+ bl_space_type = 'IMAGE_EDITOR'
+
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index 97ef37fa5e0..780dc4cf982 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -158,6 +158,8 @@ class INFO_MT_file_import(Menu):
def draw(self, context):
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)")
+ if bpy.app.build_options.alembic:
+ self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
class INFO_MT_file_export(Menu):
@@ -167,6 +169,8 @@ class INFO_MT_file_export(Menu):
def draw(self, context):
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)")
+ if bpy.app.build_options.alembic:
+ self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
class INFO_MT_file_external_data(Menu):
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index ee342265f3d..8821fa0ca58 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -25,8 +25,11 @@ from bl_ui.properties_grease_pencil_common import (
GreasePencilDrawingToolsPanel,
GreasePencilStrokeEditPanel,
GreasePencilStrokeSculptPanel,
+ GreasePencilBrushPanel,
+ GreasePencilBrushCurvesPanel,
GreasePencilDataPanel,
- GreasePencilToolsPanel,
+ GreasePencilPaletteColorPanel,
+ GreasePencilToolsPanel
)
@@ -464,6 +467,19 @@ class NODE_PT_grease_pencil(GreasePencilDataPanel, Panel):
return snode is not None and snode.node_tree is not None
+# Grease Pencil palette colors
+class NODE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel):
+ bl_space_type = 'NODE_EDITOR'
+ bl_region_type = 'UI'
+
+ # NOTE: this is just a wrapper around the generic GP Panel
+
+ @classmethod
+ def poll(cls, context):
+ snode = context.space_data
+ return snode is not None and snode.node_tree is not None
+
+
class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
@@ -494,6 +510,16 @@ class NODE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'TOOLS'
+# Grease Pencil drawing brushes
+class NODE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel):
+ bl_space_type = 'NODE_EDITOR'
+ bl_region_type = 'TOOLS'
+
+# Grease Pencil drawing curves
+class NODE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel):
+ bl_space_type = 'NODE_EDITOR'
+ bl_region_type = 'TOOLS'
+
# -----------------------------
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 4d1b9104344..26136a8e024 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -20,7 +20,11 @@
import bpy
from bpy.types import Header, Menu, Panel
from rna_prop_ui import PropertyPanel
-from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel, GreasePencilToolsPanel
+from bl_ui.properties_grease_pencil_common import (
+ GreasePencilDataPanel,
+ GreasePencilPaletteColorPanel,
+ GreasePencilToolsPanel,
+ )
from bpy.app.translations import pgettext_iface as iface_
@@ -1186,6 +1190,14 @@ class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Ou
# But, it should only show up when there are images in the preview region
+class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel):
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+ # NOTE: this is just a wrapper around the generic GP Panel
+ # But, it should only show up when there are images in the preview region
+
+
class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
bl_space_type = 'SEQUENCE_EDITOR'
bl_region_type = 'UI'
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index fdf0996ac1e..5f16957b435 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -19,7 +19,10 @@
# <pep8 compliant>
import bpy
from bpy.types import Header, Menu, Panel
-from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel
+from bl_ui.properties_grease_pencil_common import (
+ GreasePencilDataPanel,
+ GreasePencilPaletteColorPanel,
+ )
from bl_ui.properties_paint_common import UnifiedPaintPanel
from bpy.app.translations import contexts as i18n_contexts
@@ -37,7 +40,6 @@ class VIEW3D_HT_header(Header):
row = layout.row(align=True)
row.template_header()
- sub = row.row(align=True)
VIEW3D_MT_editor_menus.draw_collapsible(context, layout)
@@ -137,11 +139,12 @@ class VIEW3D_HT_header(Header):
row.operator("gpencil.copy", text="", icon='COPYDOWN')
row.operator("gpencil.paste", text="", icon='PASTEDOWN')
- layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH') # XXX: icon
-
- layout.prop(context.tool_settings.gpencil_sculpt, "use_select_mask")
-
+ # XXX: icon
+ layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH')
+ row = layout.row(align=True)
+ row.prop(context.tool_settings.gpencil_sculpt, "use_select_mask")
+ row.prop(context.tool_settings.gpencil_sculpt, "selection_alpha", slider=True)
class VIEW3D_MT_editor_menus(Menu):
@@ -1505,14 +1508,14 @@ class VIEW3D_MT_object_apply(Menu):
props.location, props.rotation, props.scale = False, True, True
layout.separator()
-
+
layout.operator("object.transforms_to_deltas", text="Location to Deltas", text_ctxt=i18n_contexts.default).mode = 'LOC'
layout.operator("object.transforms_to_deltas", text="Rotation to Deltas", text_ctxt=i18n_contexts.default).mode = 'ROT'
layout.operator("object.transforms_to_deltas", text="Scale to Deltas", text_ctxt=i18n_contexts.default).mode = 'SCALE'
-
+
layout.operator("object.transforms_to_deltas", text="All Transforms to Deltas", text_ctxt=i18n_contexts.default).mode = 'ALL'
layout.operator("object.anim_transforms_to_deltas")
-
+
layout.separator()
layout.operator("object.visual_transform_apply", text="Visual Transform", text_ctxt=i18n_contexts.default)
@@ -3081,6 +3084,13 @@ class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel):
# NOTE: this is just a wrapper around the generic GP Panel
+class VIEW3D_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel):
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+
+ # NOTE: this is just a wrapper around the generic GP Panel
+
+
class VIEW3D_PT_view3d_properties(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index e9f4d715ec2..8019c8d2f34 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -22,7 +22,9 @@ from bpy.types import Menu, Panel, UIList
from bl_ui.properties_grease_pencil_common import (
GreasePencilDrawingToolsPanel,
GreasePencilStrokeEditPanel,
- GreasePencilStrokeSculptPanel
+ GreasePencilStrokeSculptPanel,
+ GreasePencilBrushPanel,
+ GreasePencilBrushCurvesPanel
)
from bl_ui.properties_paint_common import (
UnifiedPaintPanel,
@@ -605,6 +607,7 @@ class VIEW3D_PT_tools_curveedit_options_stroke(View3DPanel, Panel):
colsub = layout.column(align=True)
colsub.prop(cps, "surface_plane", expand=True)
+
# ********** default tools for editmode_surface ****************
class VIEW3D_PT_tools_transform_surface(View3DPanel, Panel):
@@ -1964,6 +1967,15 @@ class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel)
bl_space_type = 'VIEW_3D'
+# Grease Pencil drawing brushes
+class VIEW3D_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel):
+ bl_space_type = 'VIEW_3D'
+
+# Grease Pencil drawingcurves
+class VIEW3D_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel):
+ bl_space_type = 'VIEW_3D'
+
+
# Note: moved here so that it's always in last position in 'Tools' panels!
class VIEW3D_PT_tools_history(View3DPanel, Panel):
bl_category = "Tools"
diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index 3e245b07dbd..0b4fbd52b5d 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -175,6 +175,7 @@ class BUILTIN_KSI_RotScale(KeyingSetInfo):
# ------------
+
# Bendy Bones
class BUILTIN_KSI_BendyBones(KeyingSetInfo):
"""Insert a keyframe for each of the BBone shape properties"""
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index ca014f68a7b..f365093c795 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -32,6 +32,7 @@ class SortedNodeCategory(NodeCategory):
super().__init__(identifier, name, description, items)
+
class CompositorNodeCategory(SortedNodeCategory):
@classmethod
def poll(cls, context):
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index 12f27b5ecec..c094abbd489 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -31,6 +31,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cloth_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_color_types.h
@@ -154,3 +155,6 @@ if(WITH_FREESTYLE)
add_subdirectory(freestyle)
endif()
+if(WITH_ALEMBIC)
+ add_subdirectory(alembic)
+endif()
diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h
new file mode 100644
index 00000000000..cf121f8488c
--- /dev/null
+++ b/source/blender/alembic/ABC_alembic.h
@@ -0,0 +1,110 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_ALEMBIC_H__
+#define __ABC_ALEMBIC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bContext;
+struct DerivedMesh;
+struct ListBase;
+struct Object;
+struct Scene;
+
+typedef struct AbcArchiveHandle AbcArchiveHandle;
+
+enum {
+ ABC_ARCHIVE_OGAWA = 0,
+ ABC_ARCHIVE_HDF5 = 1,
+};
+
+int ABC_get_version(void);
+
+struct AlembicExportParams {
+ double frame_start;
+ double frame_end;
+
+ double frame_step_xform;
+ double frame_step_shape;
+
+ double shutter_open;
+ double shutter_close;
+
+ /* bools */
+ unsigned int selected_only : 1;
+ unsigned int uvs : 1;
+ unsigned int normals : 1;
+ unsigned int vcolors : 1;
+ unsigned int apply_subdiv : 1;
+ unsigned int flatten_hierarchy : 1;
+ unsigned int visible_layers_only : 1;
+ unsigned int renderable_only : 1;
+ unsigned int face_sets : 1;
+ unsigned int use_subdiv_schema : 1;
+ unsigned int packuv : 1;
+
+ unsigned int compression_type : 1;
+ float global_scale;
+};
+
+void ABC_export(
+ struct Scene *scene,
+ struct bContext *C,
+ const char *filepath,
+ const struct AlembicExportParams *params);
+
+void ABC_import(struct bContext *C,
+ const char *filepath,
+ float scale,
+ bool is_sequence,
+ bool set_frame_range,
+ int sequence_len,
+ int offset,
+ bool validate_meshes);
+
+AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths);
+
+void ABC_free_handle(AbcArchiveHandle *handle);
+
+void ABC_get_transform(AbcArchiveHandle *handle,
+ struct Object *ob,
+ const char *object_path,
+ float r_mat[4][4],
+ float time,
+ float scale);
+
+struct DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
+ struct Object *ob,
+ struct DerivedMesh *dm,
+ const char *object_path,
+ const float time,
+ const char **err_str,
+ int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ABC_ALEMBIC_H__ */
diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt
new file mode 100644
index 00000000000..42bd6a9c340
--- /dev/null
+++ b/source/blender/alembic/CMakeLists.txt
@@ -0,0 +1,81 @@
+# ***** 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) 2006, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Kevin Dietrich.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../blenkernel
+ ../blenlib
+ ../blenloader
+ ../editors/include
+ ../makesdna
+ ../makesrna
+ ../windowmanager
+ ../../../intern/guardedalloc
+)
+
+set(INC_SYS
+ ${ALEMBIC_INCLUDE_DIRS}
+ ${HDF5_INCLUDE_DIRS}
+ ${OPENEXR_INCLUDE_DIRS}
+)
+if(APPLE OR WIN32)
+ list(APPEND INC_SYS
+ ${BOOST_INCLUDE_DIR}
+ )
+endif()
+
+set(SRC
+ intern/abc_camera.cc
+ intern/abc_customdata.cc
+ intern/abc_curves.cc
+ intern/abc_exporter.cc
+ intern/abc_hair.cc
+ intern/abc_mesh.cc
+ intern/abc_nurbs.cc
+ intern/abc_object.cc
+ intern/abc_points.cc
+ intern/abc_transform.cc
+ intern/abc_util.cc
+ intern/alembic_capi.cc
+
+ ABC_alembic.h
+ intern/abc_camera.h
+ intern/abc_customdata.h
+ intern/abc_curves.h
+ intern/abc_exporter.h
+ intern/abc_hair.h
+ intern/abc_mesh.h
+ intern/abc_nurbs.h
+ intern/abc_object.h
+ intern/abc_points.h
+ intern/abc_transform.h
+ intern/abc_util.h
+)
+
+if(WITH_ALEMBIC_HDF5)
+ add_definitions(-DWITH_ALEMBIC_HDF5)
+endif()
+
+blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/alembic/intern/abc_camera.cc b/source/blender/alembic/intern/abc_camera.cc
new file mode 100644
index 00000000000..38a6d3d33cf
--- /dev/null
+++ b/source/blender/alembic/intern/abc_camera.cc
@@ -0,0 +1,162 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_camera.h"
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_camera.h"
+#include "BKE_object.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+}
+
+using Alembic::AbcGeom::ICamera;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IFloatProperty;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OCamera;
+using Alembic::AbcGeom::OFloatProperty;
+
+using Alembic::AbcGeom::CameraSample;
+using Alembic::AbcGeom::kWrapExisting;
+
+/* ************************************************************************** */
+
+AbcCameraWriter::AbcCameraWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ OCamera camera(parent->alembicXform(), m_name, m_time_sampling);
+ m_camera_schema = camera.getSchema();
+
+ m_custom_data_container = m_camera_schema.getUserProperties();
+ m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling);
+ m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling);
+}
+
+void AbcCameraWriter::do_write()
+{
+ Camera *cam = static_cast<Camera *>(m_object->data);
+
+ m_stereo_distance.set(cam->stereo.convergence_distance);
+ m_eye_separation.set(cam->stereo.interocular_distance);
+
+ const double apperture_x = cam->sensor_x / 10.0;
+ const double apperture_y = cam->sensor_y / 10.0;
+ const double film_aspect = apperture_x / apperture_y;
+
+ m_camera_sample.setFocalLength(cam->lens);
+ m_camera_sample.setHorizontalAperture(apperture_x);
+ m_camera_sample.setVerticalAperture(apperture_y);
+ m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
+ m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
+ m_camera_sample.setNearClippingPlane(cam->clipsta);
+ m_camera_sample.setFarClippingPlane(cam->clipend);
+
+ if (cam->dof_ob) {
+ Imath::V3f v(m_object->loc[0] - cam->dof_ob->loc[0],
+ m_object->loc[1] - cam->dof_ob->loc[1],
+ m_object->loc[2] - cam->dof_ob->loc[2]);
+ m_camera_sample.setFocusDistance(v.length());
+ }
+ else {
+ m_camera_sample.setFocusDistance(cam->gpu_dof.focus_distance);
+ }
+
+ /* Blender camera does not have an fstop param, so try to find a custom prop
+ * instead. */
+ m_camera_sample.setFStop(cam->gpu_dof.fstop);
+
+ m_camera_sample.setLensSqueezeRatio(1.0);
+ m_camera_schema.set(m_camera_sample);
+}
+
+/* ************************************************************************** */
+
+AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ ICamera abc_cam(m_iobject, kWrapExisting);
+ m_schema = abc_cam.getSchema();
+
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcCameraReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcCameraReader::readObjectData(Main *bmain, float time)
+{
+ Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, "abc_camera"));
+
+ ISampleSelector sample_sel(time);
+ CameraSample cam_sample;
+ m_schema.get(cam_sample, sample_sel);
+
+ ICompoundProperty customDataContainer = m_schema.getUserProperties();
+
+ if (customDataContainer.valid() &&
+ customDataContainer.getPropertyHeader("stereoDistance") &&
+ customDataContainer.getPropertyHeader("eyeSeparation"))
+ {
+ IFloatProperty convergence_plane(customDataContainer, "stereoDistance");
+ IFloatProperty eye_separation(customDataContainer, "eyeSeparation");
+
+ bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel);
+ bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel);
+ }
+
+ const float lens = cam_sample.getFocalLength();
+ const float apperture_x = cam_sample.getHorizontalAperture();
+ const float apperture_y = cam_sample.getVerticalAperture();
+ const float h_film_offset = cam_sample.getHorizontalFilmOffset();
+ const float v_film_offset = cam_sample.getVerticalFilmOffset();
+ const float film_aspect = apperture_x / apperture_y;
+
+ bcam->lens = lens;
+ bcam->sensor_x = apperture_x * 10;
+ bcam->sensor_y = apperture_y * 10;
+ bcam->shiftx = h_film_offset / apperture_x;
+ bcam->shifty = v_film_offset / apperture_y / film_aspect;
+ bcam->clipsta = max_ff(0.1f, cam_sample.getNearClippingPlane());
+ bcam->clipend = cam_sample.getFarClippingPlane();
+ bcam->gpu_dof.focus_distance = cam_sample.getFocusDistance();
+ bcam->gpu_dof.fstop = cam_sample.getFStop();
+
+ BLI_strncpy(bcam->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
+
+ m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str());
+ m_object->data = bcam;
+}
diff --git a/source/blender/alembic/intern/abc_camera.h b/source/blender/alembic/intern/abc_camera.h
new file mode 100644
index 00000000000..fafb4d3eb39
--- /dev/null
+++ b/source/blender/alembic/intern/abc_camera.h
@@ -0,0 +1,61 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_CAMERA_H__
+#define __ABC_CAMERA_H__
+
+#include "abc_object.h"
+
+/* ************************************************************************** */
+
+class AbcCameraWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OCameraSchema m_camera_schema;
+ Alembic::AbcGeom::CameraSample m_camera_sample;
+ Alembic::AbcGeom::OCompoundProperty m_custom_data_container;
+ Alembic::AbcGeom::OFloatProperty m_stereo_distance;
+ Alembic::AbcGeom::OFloatProperty m_eye_separation;
+
+public:
+ AbcCameraWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+private:
+ virtual void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcCameraReader : public AbcObjectReader {
+ Alembic::AbcGeom::ICameraSchema m_schema;
+
+public:
+ AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+#endif /* __ABC_CAMERA_H__ */ \ No newline at end of file
diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc
new file mode 100644
index 00000000000..4e1e4e7e490
--- /dev/null
+++ b/source/blender/alembic/intern/abc_curves.cc
@@ -0,0 +1,355 @@
+/*
+ * ***** 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) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_curves.h"
+
+#include <cstdio>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+
+#include "BKE_curve.h"
+#include "BKE_object.h"
+
+#include "ED_curve.h"
+}
+
+using Alembic::Abc::IInt32ArrayProperty;
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::FloatArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+using Alembic::Abc::UcharArraySamplePtr;
+
+using Alembic::AbcGeom::ICurves;
+using Alembic::AbcGeom::ICurvesSchema;
+using Alembic::AbcGeom::IFloatGeomParam;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::CurvePeriodicity;
+
+using Alembic::AbcGeom::OCurves;
+using Alembic::AbcGeom::OCurvesSchema;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OV2fGeomParam;
+
+/* ************************************************************************** */
+
+AbcCurveWriter::AbcCurveWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+ m_schema = curves.getSchema();
+}
+
+void AbcCurveWriter::do_write()
+{
+ Curve *curve = static_cast<Curve *>(m_object->data);
+
+ std::vector<Imath::V3f> verts;
+ std::vector<int32_t> vert_counts;
+ std::vector<float> widths;
+ std::vector<float> weights;
+ std::vector<float> knots;
+ std::vector<uint8_t> orders;
+ Imath::V3f temp_vert;
+
+ Alembic::AbcGeom::BasisType curve_basis;
+ Alembic::AbcGeom::CurveType curve_type;
+ Alembic::AbcGeom::CurvePeriodicity periodicity;
+
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (; nurbs; nurbs = nurbs->next) {
+ if (nurbs->bp) {
+ curve_basis = Alembic::AbcGeom::kNoBasis;
+ curve_type = Alembic::AbcGeom::kLinear;
+
+ const int totpoint = nurbs->pntsu * nurbs->pntsv;
+
+ const BPoint *point = nurbs->bp;
+
+ for (int i = 0; i < totpoint; ++i, ++point) {
+ copy_zup_yup(temp_vert.getValue(), point->vec);
+ verts.push_back(temp_vert);
+ weights.push_back(point->vec[3]);
+ widths.push_back(point->radius);
+ }
+ }
+ else if (nurbs->bezt) {
+ curve_basis = Alembic::AbcGeom::kBezierBasis;
+ curve_type = Alembic::AbcGeom::kCubic;
+
+ const int totpoint = nurbs->pntsu;
+
+ const BezTriple *bezier = nurbs->bezt;
+
+ /* TODO(kevin): store info about handles, Alembic doesn't have this. */
+ for (int i = 0; i < totpoint; ++i, ++bezier) {
+ copy_zup_yup(temp_vert.getValue(), bezier->vec[1]);
+ verts.push_back(temp_vert);
+ widths.push_back(bezier->radius);
+ }
+ }
+
+ if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) {
+ periodicity = Alembic::AbcGeom::kNonPeriodic;
+ }
+ else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) {
+ periodicity = Alembic::AbcGeom::kPeriodic;
+
+ /* Duplicate the start points to indicate that the curve is actually
+ * cyclic since other software need those.
+ */
+
+ for (int i = 0; i < nurbs->orderu; ++i) {
+ verts.push_back(verts[i]);
+ }
+ }
+
+ if (nurbs->knotsu != NULL) {
+ const size_t num_knots = KNOTSU(nurbs);
+
+ /* Add an extra knot at the beggining and end of the array since most apps
+ * require/expect them. */
+ knots.resize(num_knots + 2);
+
+ for (int i = 0; i < num_knots; ++i) {
+ knots[i + 1] = nurbs->knotsu[i];
+ }
+
+ if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) {
+ knots[0] = nurbs->knotsu[0];
+ knots[num_knots - 1] = nurbs->knotsu[num_knots - 1];
+ }
+ else {
+ knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]);
+ knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - nurbs->knotsu[num_knots - 2]);
+ }
+ }
+
+ orders.push_back(nurbs->orderu + 1);
+ vert_counts.push_back(verts.size());
+ }
+
+ Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
+ width_sample.setVals(widths);
+
+ m_sample = OCurvesSchema::Sample(verts,
+ vert_counts,
+ curve_type,
+ periodicity,
+ width_sample,
+ OV2fGeomParam::Sample(), /* UVs */
+ ON3fGeomParam::Sample(), /* normals */
+ curve_basis,
+ weights,
+ orders,
+ knots);
+
+ m_sample.setSelfBounds(bounds());
+ m_schema.set(m_sample);
+}
+
+/* ************************************************************************** */
+
+AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ ICurves abc_curves(object, kWrapExisting);
+ m_curves_schema = abc_curves.getSchema();
+
+ get_min_max_time(m_curves_schema, m_min_time, m_max_time);
+}
+
+bool AbcCurveReader::valid() const
+{
+ return m_curves_schema.valid();
+}
+
+void AbcCurveReader::readObjectData(Main *bmain, float time)
+{
+ Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE);
+
+ cu->flag |= CU_DEFORM_FILL | CU_3D;
+ cu->actvert = CU_ACT_NONE;
+
+ m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());
+ m_object->data = cu;
+
+ read_curve_sample(cu, m_curves_schema, time);
+
+ if (has_animations(m_curves_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+/* ************************************************************************** */
+
+void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time)
+{
+ const ISampleSelector sample_sel(time);
+ ICurvesSchema::Sample smp = schema.getValue(sample_sel);
+ const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices();
+ const P3fArraySamplePtr positions = smp.getPositions();
+ const FloatArraySamplePtr weights = smp.getPositionWeights();
+ const FloatArraySamplePtr knots = smp.getKnots();
+ const CurvePeriodicity periodicity = smp.getWrap();
+ const UcharArraySamplePtr orders = smp.getOrders();
+
+ const IFloatGeomParam widths_param = schema.getWidthsParam();
+ FloatArraySamplePtr radiuses;
+
+ if (widths_param.valid()) {
+ IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
+ radiuses = wsample.getVals();
+ }
+
+ int knot_offset = 0;
+
+ size_t idx = 0;
+ for (size_t i = 0; i < num_vertices->size(); ++i) {
+ const int num_verts = (*num_vertices)[i];
+
+ Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+ nu->pntsu = num_verts;
+ nu->pntsv = 1;
+ nu->flag |= CU_SMOOTH;
+
+ nu->orderu = num_verts;
+
+ if (smp.getType() == Alembic::AbcGeom::kCubic) {
+ nu->orderu = 3;
+ }
+ else if (orders && orders->size() > i) {
+ nu->orderu = static_cast<short>((*orders)[i] - 1);
+ }
+
+ if (periodicity == Alembic::AbcGeom::kNonPeriodic) {
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+ else if (periodicity == Alembic::AbcGeom::kPeriodic) {
+ nu->flagu |= CU_NURB_CYCLIC;
+
+ /* Check the number of points which overlap, we don't have
+ * overlapping points in Blender, but other software do use them to
+ * indicate that a curve is actually cyclic. Usually the number of
+ * overlapping points is equal to the order/degree of the curve.
+ */
+
+ const int start = idx;
+ const int end = idx + num_verts;
+ int overlap = 0;
+
+ for (int j = start, k = end - nu->orderu; j < nu->orderu; ++j, ++k) {
+ const Imath::V3f &p1 = (*positions)[j];
+ const Imath::V3f &p2 = (*positions)[k];
+
+ if (p1 != p2) {
+ break;
+ }
+
+ ++overlap;
+ }
+
+ /* TODO: Special case, need to figure out how it coincides with knots. */
+ if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) {
+ overlap = 1;
+ }
+
+ /* There is no real cycles. */
+ if (overlap == 0) {
+ nu->flagu &= ~CU_NURB_CYCLIC;
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+
+ nu->pntsu -= overlap;
+ }
+
+ const bool do_weights = (weights != NULL) && (weights->size() > 1);
+ float weight = 1.0f;
+
+ const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1);
+ float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f;
+
+ nu->type = CU_NURBS;
+
+ nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb"));
+ BPoint *bp = nu->bp;
+
+ for (int j = 0; j < nu->pntsu; ++j, ++bp, ++idx) {
+ const Imath::V3f &pos = (*positions)[idx];
+
+ if (do_radius) {
+ radius = (*radiuses)[idx];
+ }
+
+ if (do_weights) {
+ weight = (*weights)[idx];
+ }
+
+ copy_yup_zup(bp->vec, pos.getValue());
+ bp->vec[3] = weight;
+ bp->f1 = SELECT;
+ bp->radius = radius;
+ bp->weight = 1.0f;
+ }
+
+ if (knots && knots->size() != 0) {
+ nu->knotsu = static_cast<float *>(MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu"));
+
+ /* TODO: second check is temporary, for until the check for cycles is rock solid. */
+ if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) {
+ /* Skip first and last knots. */
+ for (size_t i = 1; i < knots->size() - 1; ++i) {
+ nu->knotsu[i - 1] = (*knots)[knot_offset + i];
+ }
+ }
+ else {
+ /* TODO: figure out how to use the knots array from other
+ * software in this case. */
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ knot_offset += knots->size();
+ }
+ else {
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+ }
+}
diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h
new file mode 100644
index 00000000000..ee47f1931ea
--- /dev/null
+++ b/source/blender/alembic/intern/abc_curves.h
@@ -0,0 +1,65 @@
+/*
+ * ***** 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) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_CURVES_H__
+#define __ABC_CURVES_H__
+
+#include "abc_object.h"
+
+struct Curve;
+
+/* ************************************************************************** */
+
+class AbcCurveWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OCurvesSchema m_schema;
+ Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+
+public:
+ AbcCurveWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+ void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcCurveReader : public AbcObjectReader {
+ Alembic::AbcGeom::ICurvesSchema m_curves_schema;
+
+public:
+ AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+/* ************************************************************************** */
+
+void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time);
+
+#endif /* __ABC_CURVES_H__ */ \ No newline at end of file
diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc
new file mode 100644
index 00000000000..ebf1b2ba96e
--- /dev/null
+++ b/source/blender/alembic/intern/abc_customdata.cc
@@ -0,0 +1,379 @@
+/*
+ * ***** 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) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_customdata.h"
+
+#include <Alembic/AbcGeom/All.h>
+#include <algorithm>
+
+extern "C" {
+#include "DNA_customdata_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+}
+
+/* NOTE: for now only UVs and Vertex Colors are supported for streaming.
+ * Although Alembic only allows for a single UV layer per {I|O}Schema, and does
+ * not have a vertex color concept, there is a convention between DCCs to write
+ * such data in a way that lets other DCC know what they are for. See comments
+ * in the write code for the conventions. */
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kFacevaryingScope;
+
+using Alembic::Abc::C4fArraySample;
+using Alembic::Abc::UInt32ArraySample;
+using Alembic::Abc::V2fArraySample;
+
+using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OC4fGeomParam;
+
+static void get_uvs(const CDStreamConfig &config,
+ std::vector<Imath::V2f> &uvs,
+ std::vector<uint32_t> &uvidx,
+ void *cd_data)
+{
+ MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data);
+
+ if (!mloopuv_array) {
+ return;
+ }
+
+ const int num_poly = config.totpoly;
+ MPoly *polygons = config.mpoly;
+
+ if (!config.pack_uvs) {
+ int cnt = 0;
+ uvidx.resize(config.totloop);
+ uvs.resize(config.totloop);
+
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &current_poly = polygons[i];
+ MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
+
+ for (int j = 0; j < current_poly.totloop; ++j, ++cnt) {
+ --loopuvpoly;
+
+ uvidx[cnt] = cnt;
+ uvs[cnt][0] = loopuvpoly->uv[0];
+ uvs[cnt][1] = loopuvpoly->uv[1];
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &current_poly = polygons[i];
+ MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
+
+ for (int j = 0; j < current_poly.totloop; ++j) {
+ loopuvpoly--;
+ Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]);
+
+ std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv);
+
+ if (it == uvs.end()) {
+ uvidx.push_back(uvs.size());
+ uvs.push_back(uv);
+ }
+ else {
+ uvidx.push_back(std::distance(uvs.begin(), it));
+ }
+ }
+ }
+ }
+}
+
+const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data)
+{
+ const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV);
+
+ if (active_uvlayer < 0) {
+ return "";
+ }
+
+ void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer);
+
+ get_uvs(config, sample.uvs, sample.indices, cd_data);
+
+ return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer);
+}
+
+/* Convention to write UVs:
+ * - V2fGeomParam on the arbGeomParam
+ * - set scope as face varying
+ * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV
+ */
+static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
+{
+ std::vector<uint32_t> indices;
+ std::vector<Imath::V2f> uvs;
+
+ get_uvs(config, uvs, indices, data);
+
+ if (indices.empty() || uvs.empty()) {
+ return;
+ }
+
+ OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1);
+
+ OV2fGeomParam::Sample sample(
+ V2fArraySample(&uvs.front(), uvs.size()),
+ UInt32ArraySample(&indices.front(), indices.size()),
+ kFacevaryingScope);
+
+ param.set(sample);
+}
+
+/* Convention to write Vertex Colors:
+ * - C3fGeomParam/C4fGeomParam on the arbGeomParam
+ * - set scope as vertex varying
+ */
+static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
+{
+ const float cscale = 1.0f / 255.0f;
+ MPoly *polys = config.mpoly;
+ MLoop *mloops = config.mloop;
+ MCol *cfaces = static_cast<MCol *>(data);
+
+ std::vector<Imath::C4f> buffer(config.totvert);
+
+ Imath::C4f col;
+
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly *p = &polys[i];
+ MCol *cface = &cfaces[p->loopstart + p->totloop];
+ MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+ for (int j = 0; j < p->totloop; ++j) {
+ cface--;
+ mloop--;
+
+ col[0] = cface->a * cscale;
+ col[1] = cface->r * cscale;
+ col[2] = cface->g * cscale;
+ col[3] = cface->b * cscale;
+
+ buffer[mloop->v] = col;
+ }
+ }
+
+ OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1);
+
+ OC4fGeomParam::Sample sample(
+ C4fArraySample(&buffer.front(), buffer.size()),
+ kVertexScope);
+
+ param.set(sample);
+}
+
+void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type)
+{
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+
+ if (!CustomData_has_layer(data, cd_data_type)) {
+ return;
+ }
+
+ const int active_layer = CustomData_get_active_layer(data, cd_data_type);
+ const int tot_layers = CustomData_number_of_layers(data, cd_data_type);
+
+ for (int i = 0; i < tot_layers; ++i) {
+ void *cd_data = CustomData_get_layer_n(data, cd_data_type, i);
+ const char *name = CustomData_get_layer_name(data, cd_data_type, i);
+
+ if (cd_data_type == CD_MLOOPUV) {
+ /* Already exported. */
+ if (i == active_layer) {
+ continue;
+ }
+
+ write_uv(prop, config, cd_data, name);
+ }
+ else if (cd_data_type == CD_MLOOPCOL) {
+ write_mcol(prop, config, cd_data, name);
+ }
+ }
+}
+
+/* ************************************************************************** */
+
+using Alembic::Abc::C3fArraySamplePtr;
+using Alembic::Abc::C4fArraySamplePtr;
+using Alembic::Abc::PropertyHeader;
+
+using Alembic::AbcGeom::IC3fGeomParam;
+using Alembic::AbcGeom::IC4fGeomParam;
+using Alembic::AbcGeom::IV2fGeomParam;
+
+static void read_mcols(const CDStreamConfig &config, void *data,
+ const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr)
+{
+ MCol *cfaces = static_cast<MCol *>(data);
+ MPoly *polys = config.mpoly;
+ MLoop *mloops = config.mloop;
+
+ if (c3f_ptr) {
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly *p = &polys[i];
+ MCol *cface = &cfaces[p->loopstart + p->totloop];
+ MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+ for (int j = 0; j < p->totloop; ++j) {
+ cface--;
+ mloop--;
+ const Imath::C3f &color = (*c3f_ptr)[mloop->v];
+ cface->a = FTOCHAR(color[0]);
+ cface->r = FTOCHAR(color[1]);
+ cface->g = FTOCHAR(color[2]);
+ cface->b = 255;
+ }
+ }
+ }
+ else if (c4f_ptr) {
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly *p = &polys[i];
+ MCol *cface = &cfaces[p->loopstart + p->totloop];
+ MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+ for (int j = 0; j < p->totloop; ++j) {
+ cface--;
+ mloop--;
+ const Imath::C4f &color = (*c4f_ptr)[mloop->v];
+ cface->a = FTOCHAR(color[0]);
+ cface->r = FTOCHAR(color[1]);
+ cface->g = FTOCHAR(color[2]);
+ cface->b = FTOCHAR(color[3]);
+ }
+ }
+ }
+}
+
+static void read_uvs(const CDStreamConfig &config, void *data,
+ const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
+ const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
+{
+ MPoly *mpolys = config.mpoly;
+ MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
+
+ unsigned int uv_index, loop_index;
+
+ for (int i = 0; i < config.totpoly; ++i) {
+ MPoly &poly = mpolys[i];
+
+ for (int f = 0; f < poly.totloop; ++f) {
+ loop_index = poly.loopstart + f;
+ uv_index = (*indices)[loop_index];
+ const Imath::V2f &uv = (*uvs)[uv_index];
+
+ MLoopUV &loopuv = mloopuvs[loop_index];
+ loopuv.uv[0] = uv[0];
+ loopuv.uv[1] = uv[1];
+ }
+ }
+}
+
+static void read_custom_data_ex(const ICompoundProperty &prop,
+ const PropertyHeader &prop_header,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss,
+ int data_type)
+{
+ if (data_type == CD_MLOOPCOL) {
+ C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
+ C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
+
+ if (IC3fGeomParam::matches(prop_header)) {
+ IC3fGeomParam color_param(prop, prop_header.getName());
+ IC3fGeomParam::Sample sample;
+ color_param.getIndexed(sample, iss);
+
+ c3f_ptr = sample.getVals();
+ }
+ else if (IC4fGeomParam::matches(prop_header)) {
+ IC4fGeomParam color_param(prop, prop_header.getName());
+ IC4fGeomParam::Sample sample;
+ color_param.getIndexed(sample, iss);
+
+ c4f_ptr = sample.getVals();
+ }
+
+ void *cd_data = config.add_customdata_cb(config.user_data,
+ prop_header.getName().c_str(),
+ data_type);
+
+ read_mcols(config, cd_data, c3f_ptr, c4f_ptr);
+ }
+ else if (data_type == CD_MLOOPUV) {
+ IV2fGeomParam uv_param(prop, prop_header.getName());
+ IV2fGeomParam::Sample sample;
+ uv_param.getIndexed(sample, iss);
+
+ if (uv_param.getScope() != kFacevaryingScope) {
+ return;
+ }
+
+ void *cd_data = config.add_customdata_cb(config.user_data,
+ prop_header.getName().c_str(),
+ data_type);
+
+ read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
+ }
+}
+
+void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
+{
+ if (!prop.valid()) {
+ return;
+ }
+
+ int num_uvs = 0;
+ int num_colors = 0;
+
+ const size_t num_props = prop.getNumProperties();
+
+ for (size_t i = 0; i < num_props; ++i) {
+ const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
+
+ /* Read UVs according to convention. */
+ if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
+ if (++num_uvs > MAX_MTFACE) {
+ continue;
+ }
+
+ read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV);
+ continue;
+ }
+
+ /* Read vertex colors according to convention. */
+ if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
+ if (++num_colors > MAX_MCOL) {
+ continue;
+ }
+
+ read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL);
+ continue;
+ }
+ }
+}
diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h
new file mode 100644
index 00000000000..3b16c0d9796
--- /dev/null
+++ b/source/blender/alembic/intern/abc_customdata.h
@@ -0,0 +1,93 @@
+/*
+ * ***** 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) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_CUSTOMDATA_H__
+#define __ABC_CUSTOMDATA_H__
+
+#include <Alembic/Abc/All.h>
+
+struct CustomData;
+struct MLoop;
+struct MLoopUV;
+struct MPoly;
+struct MVert;
+
+using Alembic::Abc::ICompoundProperty;
+using Alembic::Abc::OCompoundProperty;
+
+struct UVSample {
+ std::vector<Imath::V2f> uvs;
+ std::vector<uint32_t> indices;
+};
+
+struct CDStreamConfig {
+ MLoop *mloop;
+ int totloop;
+
+ MPoly *mpoly;
+ int totpoly;
+
+ MVert *mvert;
+ int totvert;
+
+ MLoopUV *mloopuv;
+
+ CustomData *loopdata;
+
+ bool pack_uvs;
+
+ /* TODO(kevin): might need a better way to handle adding and/or updating
+ * custom datas such that it updates the custom data holder and its pointers
+ * properly. */
+ void *user_data;
+ void *(*add_customdata_cb)(void *user_data, const char *name, int data_type);
+
+ CDStreamConfig()
+ : mloop(NULL)
+ , totloop(0)
+ , mpoly(NULL)
+ , totpoly(0)
+ , totvert(0)
+ , pack_uvs(false)
+ , user_data(NULL)
+ , add_customdata_cb(NULL)
+ {}
+};
+
+/* Get the UVs for the main UV property on a OSchema.
+ * Returns the name of the UV layer.
+ *
+ * For now the active layer is used, maybe needs a better way to choose this. */
+const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
+
+void write_custom_data(const OCompoundProperty &prop,
+ const CDStreamConfig &config,
+ CustomData *data,
+ int data_type);
+
+void read_custom_data(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss);
+
+#endif /* __ABC_CUSTOMDATA_H__ */
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
new file mode 100644
index 00000000000..127e8853789
--- /dev/null
+++ b/source/blender/alembic/intern/abc_exporter.cc
@@ -0,0 +1,600 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_exporter.h"
+
+#include <cmath>
+
+#ifdef WITH_ALEMBIC_HDF5
+# include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+
+#include "abc_camera.h"
+#include "abc_curves.h"
+#include "abc_hair.h"
+#include "abc_mesh.h"
+#include "abc_nurbs.h"
+#include "abc_points.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_camera_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h" /* for FILE_MAX */
+
+#include "BLI_string.h"
+
+#ifdef WIN32
+/* needed for MSCV because of snprintf from BLI_string */
+# include "BLI_winstuff.h"
+#endif
+
+#include "BKE_anim.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+}
+
+using Alembic::Abc::TimeSamplingPtr;
+using Alembic::Abc::OBox3dProperty;
+
+/* ************************************************************************** */
+
+ExportSettings::ExportSettings()
+ : scene(NULL)
+ , selected_only(false)
+ , visible_layers_only(false)
+ , renderable_only(false)
+ , frame_start(1)
+ , frame_end(1)
+ , frame_step_xform(1)
+ , frame_step_shape(1)
+ , shutter_open(0.0)
+ , shutter_close(1.0)
+ , global_scale(1.0f)
+ , flatten_hierarchy(false)
+ , export_normals(false)
+ , export_uvs(false)
+ , export_vcols(false)
+ , export_face_sets(false)
+ , export_vweigths(false)
+ , apply_subdiv(false)
+ , use_subdiv_schema(false)
+ , export_child_hairs(true)
+ , export_ogawa(true)
+ , pack_uv(false)
+ , do_convert_axis(false)
+{}
+
+static bool object_is_smoke_sim(Object *ob)
+{
+ ModifierData *md = modifiers_findByType(ob, eModifierType_Smoke);
+
+ if (md) {
+ SmokeModifierData *smd = reinterpret_cast<SmokeModifierData *>(md);
+ return (smd->type == MOD_SMOKE_TYPE_DOMAIN);
+ }
+
+ return false;
+}
+
+static bool object_is_shape(Object *ob)
+{
+ switch (ob->type) {
+ case OB_MESH:
+ if (object_is_smoke_sim(ob)) {
+ return false;
+ }
+
+ return true;
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_CAMERA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool export_object(const ExportSettings * const settings, Object *ob)
+{
+ if (settings->selected_only && !parent_selected(ob)) {
+ return false;
+ }
+
+ if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) {
+ return false;
+ }
+
+ if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* ************************************************************************** */
+
+AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &settings)
+ : m_settings(settings)
+ , m_filename(filename)
+ , m_trans_sampling_index(0)
+ , m_shape_sampling_index(0)
+ , m_scene(scene)
+{}
+
+AbcExporter::~AbcExporter()
+{
+ std::map<std::string, AbcTransformWriter*>::iterator it, e;
+ for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) {
+ delete it->second;
+ }
+
+ for (int i = 0, e = m_shapes.size(); i != e; ++i) {
+ delete m_shapes[i];
+ }
+}
+
+void AbcExporter::getShutterSamples(double step, bool time_relative,
+ std::vector<double> &samples)
+{
+ samples.clear();
+
+ const double time_factor = time_relative ? m_scene->r.frs_sec : 1.0;
+ const double shutter_open = m_settings.shutter_open;
+ const double shutter_close = m_settings.shutter_close;
+
+ /* sample all frame */
+ if (shutter_open == 0.0 && shutter_close == 1.0) {
+ for (double t = 0; t < 1.0; t += step) {
+ samples.push_back(t / time_factor);
+ }
+ }
+ else {
+ /* sample between shutter open & close */
+ const int nsamples = std::max((1.0 / step) - 1.0, 1.0);
+ const double time_inc = (shutter_close - shutter_open) / nsamples;
+
+ for (double t = shutter_open; t <= shutter_close; t += time_inc) {
+ samples.push_back(t / time_factor);
+ }
+ }
+}
+
+Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step)
+{
+ TimeSamplingPtr time_sampling;
+ std::vector<double> samples;
+
+ if (m_settings.frame_start == m_settings.frame_end) {
+ time_sampling.reset(new Alembic::Abc::TimeSampling());
+ return time_sampling;
+ }
+
+ getShutterSamples(step, true, samples);
+
+ Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / m_scene->r.frs_sec);
+ time_sampling.reset(new Alembic::Abc::TimeSampling(ts, samples));
+
+ return time_sampling;
+}
+
+void AbcExporter::getFrameSet(double step, std::set<double> &frames)
+{
+ frames.clear();
+
+ std::vector<double> shutter_samples;
+
+ getShutterSamples(step, false, shutter_samples);
+
+ for (int frame = m_settings.frame_start; frame <= m_settings.frame_end; ++frame) {
+ for (int j = 0, e = shutter_samples.size(); j < e; ++j) {
+ frames.insert(frame + shutter_samples[j]);
+ }
+ }
+}
+
+void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
+{
+ std::string scene_name;
+
+ if (bmain->name[0] != '\0') {
+ char scene_file_name[FILE_MAX];
+ BLI_strncpy(scene_file_name, bmain->name, FILE_MAX);
+ scene_name = scene_file_name;
+ }
+ else {
+ scene_name = "untitled";
+ }
+
+ Scene *scene = m_scene;
+ const int fps = FPS;
+ char buf[16];
+ snprintf(buf, 15, "%d", fps);
+ const std::string str_fps = buf;
+
+ Alembic::AbcCoreAbstract::MetaData md;
+ md.set("FramesPerTimeUnit", str_fps);
+
+ Alembic::Abc::Argument arg(md);
+
+#ifdef WITH_ALEMBIC_HDF5
+ if (!m_settings.export_ogawa) {
+ m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
+ m_filename,
+ "Blender",
+ scene_name,
+ Alembic::Abc::ErrorHandler::kThrowPolicy,
+ arg);
+ }
+ else
+#endif
+ {
+ m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
+ m_filename,
+ "Blender",
+ scene_name,
+ Alembic::Abc::ErrorHandler::kThrowPolicy,
+ arg);
+ }
+
+ /* Create time samplings for transforms and shapes. */
+
+ TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_step_xform);
+
+ m_trans_sampling_index = m_archive.addTimeSampling(*trans_time);
+
+ TimeSamplingPtr shape_time;
+
+ if ((m_settings.frame_step_shape == m_settings.frame_step_xform) ||
+ (m_settings.frame_start == m_settings.frame_end))
+ {
+ shape_time = trans_time;
+ m_shape_sampling_index = m_trans_sampling_index;
+ }
+ else {
+ shape_time = createTimeSampling(m_settings.frame_step_shape);
+ m_shape_sampling_index = m_archive.addTimeSampling(*shape_time);
+ }
+
+ OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_archive, m_trans_sampling_index);
+
+ if (m_settings.flatten_hierarchy) {
+ createTransformWritersFlat();
+ }
+ else {
+ createTransformWritersHierarchy(bmain->eval_ctx);
+ }
+
+ createShapeWriters(bmain->eval_ctx);
+
+ /* Make a list of frames to export. */
+
+ std::set<double> xform_frames;
+ getFrameSet(m_settings.frame_step_xform, xform_frames);
+
+ std::set<double> shape_frames;
+ getFrameSet(m_settings.frame_step_shape, shape_frames);
+
+ /* Merge all frames needed. */
+
+ std::set<double> frames(xform_frames);
+ frames.insert(shape_frames.begin(), shape_frames.end());
+
+ /* Export all frames. */
+
+ std::set<double>::const_iterator begin = frames.begin();
+ std::set<double>::const_iterator end = frames.end();
+
+ const float size = static_cast<float>(frames.size());
+ size_t i = 0;
+
+ for (; begin != end; ++begin) {
+ progress = (++i / size);
+
+ if (G.is_break) {
+ was_canceled = true;
+ break;
+ }
+
+ double f = *begin;
+ setCurrentFrame(bmain, f);
+
+ if (shape_frames.count(f) != 0) {
+ for (int i = 0, e = m_shapes.size(); i != e; ++i) {
+ m_shapes[i]->write();
+ }
+ }
+
+ if (xform_frames.count(f) == 0) {
+ continue;
+ }
+
+ std::map<std::string, AbcTransformWriter *>::iterator xit, xe;
+ for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
+ xit->second->write();
+ }
+
+ /* Save the archive 's bounding box. */
+ Imath::Box3d bounds;
+
+ for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
+ Imath::Box3d box = xit->second->bounds();
+ bounds.extendBy(box);
+ }
+
+ archive_bounds_prop.set(bounds);
+ }
+}
+
+void AbcExporter::createTransformWritersHierarchy(EvaluationContext *eval_ctx)
+{
+ Base *base = static_cast<Base *>(m_scene->base.first);
+
+ while (base) {
+ Object *ob = base->object;
+
+ if (export_object(&m_settings, ob)) {
+ switch(ob->type) {
+ case OB_LAMP:
+ case OB_LATTICE:
+ case OB_MBALL:
+ case OB_SPEAKER:
+ /* We do not export transforms for objects of these classes. */
+ break;
+
+ default:
+ exploreTransform(eval_ctx, ob, ob->parent, NULL);
+ }
+ }
+
+ base = base->next;
+ }
+}
+
+void AbcExporter::createTransformWritersFlat()
+{
+ Base *base = static_cast<Base *>(m_scene->base.first);
+
+ while (base) {
+ Object *ob = base->object;
+
+ if (export_object(&m_settings, ob) && object_is_shape(ob)) {
+ std::string name = get_id_name(ob);
+ m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), 0, m_trans_sampling_index, m_settings);
+ }
+
+ base = base->next;
+ }
+}
+
+void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent)
+{
+ createTransformWriter(ob, parent, dupliObParent);
+
+ ListBase *lb = object_duplilist(eval_ctx, m_scene, ob);
+
+ if (lb) {
+ DupliObject *link = static_cast<DupliObject *>(lb->first);
+ Object *dupli_ob = NULL;
+ Object *dupli_parent = NULL;
+
+ while (link) {
+ if (link->type == OB_DUPLIGROUP) {
+ dupli_ob = link->ob;
+ dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob;
+
+ exploreTransform(eval_ctx, dupli_ob, dupli_parent, ob);
+ }
+
+ link = link->next;
+ }
+ }
+
+ free_object_duplilist(lb);
+}
+
+void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent)
+{
+ const std::string name = get_object_dag_path_name(ob, dupliObParent);
+
+ /* check if we have already created a transform writer for this object */
+ if (m_xforms.find(name) != m_xforms.end()){
+ std::cerr << "xform " << name << " already exists\n";
+ return;
+ }
+
+ AbcTransformWriter *parent_xform = NULL;
+
+ if (parent) {
+ const std::string parentname = get_object_dag_path_name(parent, dupliObParent);
+ parent_xform = getXForm(parentname);
+
+ if (!parent_xform) {
+ if (parent->parent) {
+ createTransformWriter(parent, parent->parent, dupliObParent);
+ }
+ else {
+ createTransformWriter(parent, dupliObParent, dupliObParent);
+ }
+
+ parent_xform = getXForm(parentname);
+ }
+ }
+
+ if (parent_xform) {
+ m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings);
+ m_xforms[name]->setParent(parent);
+ }
+ else {
+ m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), NULL, m_trans_sampling_index, m_settings);
+ }
+}
+
+void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx)
+{
+ Base *base = static_cast<Base *>(m_scene->base.first);
+
+ while (base) {
+ Object *ob = base->object;
+ exploreObject(eval_ctx, ob, NULL);
+
+ base = base->next;
+ }
+}
+
+void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent)
+{
+ ListBase *lb = object_duplilist(eval_ctx, m_scene, ob);
+
+ createShapeWriter(ob, dupliObParent);
+
+ if (lb) {
+ DupliObject *dupliob = static_cast<DupliObject *>(lb->first);
+
+ while (dupliob) {
+ if (dupliob->type == OB_DUPLIGROUP) {
+ exploreObject(eval_ctx, dupliob->ob, ob);
+ }
+
+ dupliob = dupliob->next;
+ }
+ }
+
+ free_object_duplilist(lb);
+}
+
+void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
+{
+ if (!object_is_shape(ob)) {
+ return;
+ }
+
+ if (!export_object(&m_settings, ob)) {
+ return;
+ }
+
+ std::string name;
+
+ if (m_settings.flatten_hierarchy) {
+ name = get_id_name(ob);
+ }
+ else {
+ name = get_object_dag_path_name(ob, dupliObParent);
+ }
+
+ AbcTransformWriter *xform = getXForm(name);
+
+ if (!xform) {
+ std::cerr << __func__ << ": xform " << name << " is NULL\n";
+ return;
+ }
+
+ ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
+
+ for (; psys; psys = psys->next) {
+ if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
+ continue;
+ }
+
+ if (psys->part->type == PART_HAIR) {
+ m_settings.export_child_hairs = true;
+ m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+ }
+ else if (psys->part->type == PART_EMITTER) {
+ m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+ }
+ }
+
+ switch(ob->type) {
+ case OB_MESH:
+ {
+ Mesh *me = static_cast<Mesh *>(ob->data);
+
+ if (!me || me->totvert == 0) {
+ return;
+ }
+
+ m_shapes.push_back(new AbcMeshWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ break;
+ }
+ case OB_SURF:
+ {
+ Curve *cu = static_cast<Curve *>(ob->data);
+
+ if (!cu) {
+ return;
+ }
+
+ m_shapes.push_back(new AbcNurbsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ break;
+ }
+ case OB_CURVE:
+ {
+ Curve *cu = static_cast<Curve *>(ob->data);
+
+ if (!cu) {
+ return;
+ }
+
+ m_shapes.push_back(new AbcCurveWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ break;
+ }
+ case OB_CAMERA:
+ {
+ Camera *cam = static_cast<Camera *>(ob->data);
+
+ if (cam->type == CAM_PERSP) {
+ m_shapes.push_back(new AbcCameraWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+ }
+
+ break;
+ }
+ }
+}
+
+AbcTransformWriter *AbcExporter::getXForm(const std::string &name)
+{
+ std::map<std::string, AbcTransformWriter *>::iterator it = m_xforms.find(name);
+
+ if (it == m_xforms.end()) {
+ return NULL;
+ }
+
+ return it->second;
+}
+
+void AbcExporter::setCurrentFrame(Main *bmain, double t)
+{
+ m_scene->r.cfra = std::floor(t);
+ m_scene->r.subframe = t - m_scene->r.cfra;
+ BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, m_scene, m_scene->lay);
+}
diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h
new file mode 100644
index 00000000000..070eb9ea81a
--- /dev/null
+++ b/source/blender/alembic/intern/abc_exporter.h
@@ -0,0 +1,112 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_EXPORTER_H__
+#define __ABC_EXPORTER_H__
+
+#include <Alembic/Abc/All.h>
+#include <map>
+#include <set>
+#include <vector>
+
+class AbcObjectWriter;
+class AbcTransformWriter;
+
+struct EvaluationContext;
+struct Main;
+struct Object;
+struct Scene;
+
+struct ExportSettings {
+ ExportSettings();
+
+ Scene *scene;
+
+ bool selected_only;
+ bool visible_layers_only;
+ bool renderable_only;
+
+ double frame_start, frame_end;
+ double frame_step_xform;
+ double frame_step_shape;
+ double shutter_open;
+ double shutter_close;
+ float global_scale;
+
+ bool flatten_hierarchy;
+
+ bool export_normals;
+ bool export_uvs;
+ bool export_vcols;
+ bool export_face_sets;
+ bool export_vweigths;
+
+ bool apply_subdiv;
+ bool use_subdiv_schema;
+ bool export_child_hairs;
+ bool export_ogawa;
+ bool pack_uv;
+
+ bool do_convert_axis;
+ float convert_matrix[3][3];
+};
+
+class AbcExporter {
+ ExportSettings &m_settings;
+
+ const char *m_filename;
+
+ Alembic::Abc::OArchive m_archive;
+ unsigned int m_trans_sampling_index, m_shape_sampling_index;
+
+ Scene *m_scene;
+
+ std::map<std::string, AbcTransformWriter *> m_xforms;
+ std::vector<AbcObjectWriter *> m_shapes;
+
+public:
+ AbcExporter(Scene *scene, const char *filename, ExportSettings &settings);
+ ~AbcExporter();
+
+ void operator()(Main *bmain, float &progress, bool &was_canceled);
+
+private:
+ void getShutterSamples(double step, bool time_relative, std::vector<double> &samples);
+
+ Alembic::Abc::TimeSamplingPtr createTimeSampling(double step);
+
+ void getFrameSet(double step, std::set<double> &frames);
+
+ void createTransformWritersHierarchy(EvaluationContext *eval_ctx);
+ void createTransformWritersFlat();
+ void createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
+ void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL);
+ void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent);
+ void createShapeWriters(EvaluationContext *eval_ctx);
+ void createShapeWriter(Object *ob, Object *dupliObParent);
+
+ AbcTransformWriter *getXForm(const std::string &name);
+
+ void setCurrentFrame(Main *bmain, double t);
+};
+
+#endif /* __ABC_EXPORTER_H__ */
diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc
new file mode 100644
index 00000000000..45bf9b7ab8a
--- /dev/null
+++ b/source/blender/alembic/intern/abc_hair.cc
@@ -0,0 +1,290 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_hair.h"
+
+#include <cstdio>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_modifier_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_geom.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+}
+
+using Alembic::Abc::P3fArraySamplePtr;
+
+using Alembic::AbcGeom::OCurves;
+using Alembic::AbcGeom::OCurvesSchema;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OV2fGeomParam;
+
+/* ************************************************************************** */
+
+AbcHairWriter::AbcHairWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_psys = psys;
+
+ OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+ m_schema = curves.getSchema();
+}
+
+void AbcHairWriter::do_write()
+{
+ if (!m_psys) {
+ return;
+ }
+
+ ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys);
+
+ if (!psmd->dm_final) {
+ return;
+ }
+
+ DerivedMesh *dm = mesh_create_derived_view(m_scene, m_object, CD_MASK_MESH);
+ DM_ensure_tessface(dm);
+ DM_update_tessface_data(dm);
+
+ std::vector<Imath::V3f> verts;
+ std::vector<int32_t> hvertices;
+ std::vector<Imath::V2f> uv_values;
+ std::vector<Imath::V3f> norm_values;
+
+ if (m_psys->pathcache) {
+ ParticleSettings *part = m_psys->part;
+
+ write_hair_sample(dm, part, verts, norm_values, uv_values, hvertices);
+
+ if (m_settings.export_child_hairs && m_psys->childcache) {
+ write_hair_child_sample(dm, part, verts, norm_values, uv_values, hvertices);
+ }
+ }
+
+ dm->release(dm);
+
+ Alembic::Abc::P3fArraySample iPos(verts);
+ m_sample = OCurvesSchema::Sample(iPos, hvertices);
+ m_sample.setBasis(Alembic::AbcGeom::kNoBasis);
+ m_sample.setType(Alembic::AbcGeom::kLinear);
+ m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
+
+ if (!uv_values.empty()) {
+ OV2fGeomParam::Sample uv_smp;
+ uv_smp.setVals(uv_values);
+ m_sample.setUVs(uv_smp);
+ }
+
+ if (!norm_values.empty()) {
+ ON3fGeomParam::Sample norm_smp;
+ norm_smp.setVals(norm_values);
+ m_sample.setNormals(norm_smp);
+ }
+
+ m_sample.setSelfBounds(bounds());
+ m_schema.set(m_sample);
+}
+
+void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices)
+{
+ /* Get untransformed vertices, there's a xform under the hair. */
+ float inv_mat[4][4];
+ invert_m4_m4_safe(inv_mat, m_object->obmat);
+
+ MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE));
+ MFace *mface = dm->getTessFaceArray(dm);
+ MVert *mverts = dm->getVertArray(dm);
+
+ if (!mtface || !mface) {
+ std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n");
+ }
+
+ ParticleData * pa = m_psys->particles;
+ int k;
+
+ ParticleCacheKey **cache = m_psys->pathcache;
+ ParticleCacheKey *path;
+ float normal[3];
+ Imath::V3f tmp_nor;
+
+ for (int p = 0; p < m_psys->totpart; ++p, ++pa) {
+ /* underlying info for faces-only emission */
+ path = cache[p];
+
+ if (part->from == PART_FROM_FACE && mtface) {
+ const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num;
+
+ if (num < dm->getNumTessFaces(dm)) {
+ MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE));
+ MTFace *tface = mtface + num;
+
+ if (mface) {
+ float r_uv[2], mapfw[4], vec[3];
+
+ psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv);
+ uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1]));
+
+ psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL, NULL);
+
+ copy_zup_yup(tmp_nor.getValue(), normal);
+ norm_values.push_back(tmp_nor);
+ }
+ }
+ else {
+ std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, dm->getNumTessFaces(dm));
+ }
+ }
+ else if (part->from == PART_FROM_VERT && mtface) {
+ /* vertex id */
+ const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num;
+
+ /* iterate over all faces to find a corresponding underlying UV */
+ for (int n = 0; n < dm->getNumTessFaces(dm); ++n) {
+ MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, n, CD_MFACE));
+ MTFace *tface = mtface + n;
+ unsigned int vtx[4];
+ vtx[0] = face->v1;
+ vtx[1] = face->v2;
+ vtx[2] = face->v3;
+ vtx[3] = face->v4;
+ bool found = false;
+
+ for (int o = 0; o < 4; ++o) {
+ if (o > 2 && vtx[o] == 0) {
+ break;
+ }
+
+ if (vtx[o] == num) {
+ uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1]));
+
+ MVert *mv = mverts + vtx[o];
+
+ normal_short_to_float_v3(normal, mv->no);
+ copy_zup_yup(tmp_nor.getValue(), normal);
+ norm_values.push_back(tmp_nor);
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ int steps = path->segments + 1;
+ hvertices.push_back(steps);
+
+ for (k = 0; k < steps; ++k) {
+ float vert[3];
+ copy_v3_v3(vert, path->co);
+ mul_m4_v3(inv_mat, vert);
+
+ /* Convert Z-up to Y-up. */
+ verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1]));
+
+ ++path;
+ }
+ }
+}
+
+void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices)
+{
+ /* Get untransformed vertices, there's a xform under the hair. */
+ float inv_mat[4][4];
+ invert_m4_m4_safe(inv_mat, m_object->obmat);
+
+ MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE));
+ MFace *mface = dm->getTessFaceArray(dm);
+ MVert *mverts = dm->getVertArray(dm);
+
+ if (!mtface || !mface) {
+ std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n");
+ }
+
+ ParticleCacheKey **cache = m_psys->childcache;
+ ParticleCacheKey *path;
+
+ ChildParticle *pc = m_psys->child;
+
+ for (int p = 0; p < m_psys->totchild; ++p, ++pc) {
+ path = cache[p];
+
+ if (part->from == PART_FROM_FACE) {
+ const int num = pc->num;
+
+ MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE));
+ MTFace *tface = mtface + num;
+
+ if (mface && mtface) {
+ float r_uv[2], tmpnor[3], mapfw[4], vec[3];
+
+ psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv);
+ uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1]));
+
+ psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL, NULL);
+
+ /* Convert Z-up to Y-up. */
+ norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1]));
+ }
+ }
+
+ int steps = path->segments + 1;
+ hvertices.push_back(steps);
+
+ for (int k = 0; k < steps; ++k) {
+ float vert[3];
+ copy_v3_v3(vert, path->co);
+ mul_m4_v3(inv_mat, vert);
+
+ /* Convert Z-up to Y-up. */
+ verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1]));
+
+ ++path;
+ }
+ }
+}
diff --git a/source/blender/alembic/intern/abc_hair.h b/source/blender/alembic/intern/abc_hair.h
new file mode 100644
index 00000000000..d132b60be12
--- /dev/null
+++ b/source/blender/alembic/intern/abc_hair.h
@@ -0,0 +1,66 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_HAIR_H__
+#define __ABC_HAIR_H__
+
+#include "abc_object.h"
+
+struct DerivedMesh;
+struct ParticleSettings;
+struct ParticleSystem;
+
+/* ************************************************************************** */
+
+class AbcHairWriter : public AbcObjectWriter {
+ ParticleSystem *m_psys;
+
+ Alembic::AbcGeom::OCurvesSchema m_schema;
+ Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+
+public:
+ AbcHairWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys);
+
+private:
+ virtual void do_write();
+
+ void write_hair_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices);
+
+ void write_hair_child_sample(DerivedMesh *dm,
+ ParticleSettings *part,
+ std::vector<Imath::V3f> &verts,
+ std::vector<Imath::V3f> &norm_values,
+ std::vector<Imath::V2f> &uv_values,
+ std::vector<int32_t> &hvertices);
+};
+
+#endif /* __ABC_HAIR_H__ */
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
new file mode 100644
index 00000000000..f1c7b6b3aa3
--- /dev/null
+++ b/source/blender/alembic/intern/abc_mesh.cc
@@ -0,0 +1,1213 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_mesh.h"
+
+#include <algorithm>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_fluidsim.h"
+#include "DNA_object_types.h"
+
+#include "BLI_math_geom.h"
+#include "BLI_string.h"
+
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_mesh.h"
+}
+
+using Alembic::Abc::FloatArraySample;
+using Alembic::Abc::ICompoundProperty;
+using Alembic::Abc::Int32ArraySample;
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+using Alembic::Abc::V2fArraySample;
+using Alembic::Abc::V3fArraySample;
+using Alembic::Abc::C4fArraySample;
+
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::IFaceSetSchema;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::ISubD;
+using Alembic::AbcGeom::ISubDSchema;
+using Alembic::AbcGeom::IV2fGeomParam;
+
+using Alembic::AbcGeom::OArrayProperty;
+using Alembic::AbcGeom::OBoolProperty;
+using Alembic::AbcGeom::OC3fArrayProperty;
+using Alembic::AbcGeom::OC3fGeomParam;
+using Alembic::AbcGeom::OC4fGeomParam;
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::OFaceSet;
+using Alembic::AbcGeom::OFaceSetSchema;
+using Alembic::AbcGeom::OFloatGeomParam;
+using Alembic::AbcGeom::OInt32GeomParam;
+using Alembic::AbcGeom::ON3fArrayProperty;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OPolyMesh;
+using Alembic::AbcGeom::OPolyMeshSchema;
+using Alembic::AbcGeom::OSubD;
+using Alembic::AbcGeom::OSubDSchema;
+using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OV3fGeomParam;
+
+using Alembic::AbcGeom::kFacevaryingScope;
+using Alembic::AbcGeom::kVaryingScope;
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::UInt32ArraySample;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::IN3fGeomParam;
+
+/* ************************************************************************** */
+
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+
+static void get_vertices(DerivedMesh *dm, std::vector<Imath::V3f> &points)
+{
+ points.clear();
+ points.resize(dm->getNumVerts(dm));
+
+ MVert *verts = dm->getVertArray(dm);
+
+ for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
+ copy_zup_yup(points[i].getValue(), verts[i].co);
+ }
+}
+
+static void get_topology(DerivedMesh *dm,
+ std::vector<int32_t> &poly_verts,
+ std::vector<int32_t> &loop_counts,
+ bool &smooth_normal)
+{
+ const int num_poly = dm->getNumPolys(dm);
+ const int num_loops = dm->getNumLoops(dm);
+ MLoop *mloop = dm->getLoopArray(dm);
+ MPoly *mpoly = dm->getPolyArray(dm);
+
+ poly_verts.clear();
+ loop_counts.clear();
+ poly_verts.reserve(num_loops);
+ loop_counts.reserve(num_poly);
+
+ /* NOTE: data needs to be written in the reverse order. */
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &poly = mpoly[i];
+ loop_counts.push_back(poly.totloop);
+
+ smooth_normal |= ((poly.flag & ME_SMOOTH) != 0);
+
+ MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+
+ for (int j = 0; j < poly.totloop; ++j, --loop) {
+ poly_verts.push_back(loop->v);
+ }
+ }
+}
+
+static void get_material_indices(DerivedMesh *dm, std::vector<int32_t> &indices)
+{
+ indices.clear();
+ indices.reserve(dm->getNumTessFaces(dm));
+
+ MPoly *mpolys = dm->getPolyArray(dm);
+
+ for (int i = 1, e = dm->getNumPolys(dm); i < e; ++i) {
+ MPoly *mpoly = &mpolys[i];
+ indices.push_back(mpoly->mat_nr);
+ }
+}
+
+static void get_creases(DerivedMesh *dm,
+ std::vector<int32_t> &indices,
+ std::vector<int32_t> &lengths,
+ std::vector<float> &sharpnesses)
+{
+ const float factor = 1.0f / 255.0f;
+
+ indices.clear();
+ lengths.clear();
+ sharpnesses.clear();
+
+ MEdge *edge = dm->getEdgeArray(dm);
+
+ for (int i = 0, e = dm->getNumEdges(dm); i < e; ++i) {
+ const float sharpness = static_cast<float>(edge[i].crease) * factor;
+
+ if (sharpness != 0.0f) {
+ indices.push_back(edge[i].v1);
+ indices.push_back(edge[i].v2);
+ sharpnesses.push_back(sharpness);
+ }
+ }
+
+ lengths.resize(sharpnesses.size(), 2);
+}
+
+static void get_vertex_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
+{
+ normals.clear();
+ normals.resize(dm->getNumVerts(dm));
+
+ MVert *verts = dm->getVertArray(dm);
+ float no[3];
+
+ for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
+ normal_short_to_float_v3(no, verts[i].no);
+ copy_zup_yup(normals[i].getValue(), no);
+ }
+}
+
+static void get_loop_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
+{
+ MPoly *mpoly = dm->getPolyArray(dm);
+ MPoly *mp = mpoly;
+
+ MLoop *mloop = dm->getLoopArray(dm);
+ MLoop *ml = mloop;
+
+ MVert *verts = dm->getVertArray(dm);
+
+ const float (*lnors)[3] = static_cast<float(*)[3]>(dm->getLoopDataArray(dm, CD_NORMAL));
+
+ normals.clear();
+ normals.resize(dm->getNumLoops(dm));
+
+ unsigned loop_index = 0;
+
+ /* NOTE: data needs to be written in the reverse order. */
+
+ if (lnors) {
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
+ ml = mloop + mp->loopstart + (mp->totloop - 1);
+
+ for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+ const int index = ml->v;
+ copy_zup_yup(normals[loop_index].getValue(), lnors[index]);
+ }
+ }
+ }
+ else {
+ float no[3];
+
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
+ ml = mloop + mp->loopstart + (mp->totloop - 1);
+
+ /* Flat shaded, use common normal for all verts. */
+ if ((mp->flag & ME_SMOOTH) == 0) {
+ BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no);
+
+ for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+ copy_zup_yup(normals[loop_index].getValue(), no);
+ }
+ }
+ else {
+ /* Smooth shaded, use individual vert normals. */
+ for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+ normal_short_to_float_v3(no, verts[ml->v].no);
+ copy_zup_yup(normals[loop_index].getValue(), no);
+ }
+ }
+ }
+ }
+}
+
+/* *************** Modifiers *************** */
+
+/* check if the mesh is a subsurf, ignoring disabled modifiers and
+ * displace if it's after subsurf. */
+static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
+{
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+
+ for (; md; md = md->prev) {
+ if (!modifier_isEnabled(scene, md, eModifierMode_Render)) {
+ continue;
+ }
+
+ if (md->type == eModifierType_Subsurf) {
+ SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData*>(md);
+
+ if (smd->subdivType == ME_CC_SUBSURF) {
+ return md;
+ }
+ }
+
+ /* mesh is not a subsurf. break */
+ if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
+{
+ ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim);
+
+ if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) {
+ FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md);
+
+ if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) {
+ return md;
+ }
+ }
+
+ return NULL;
+}
+
+/* ************************************************************************** */
+
+AbcMeshWriter::AbcMeshWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_is_animated = isAnimated();
+ m_subsurf_mod = NULL;
+ m_has_per_face_materials = false;
+ m_is_subd = false;
+
+ /* If the object is static, use the default static time sampling. */
+ if (!m_is_animated) {
+ time_sampling = 0;
+ }
+
+ if (!m_settings.apply_subdiv) {
+ m_subsurf_mod = get_subsurf_modifier(m_scene, m_object);
+ m_is_subd = (m_subsurf_mod != NULL);
+ }
+
+ m_is_liquid = (get_liquid_sim_modifier(m_scene, m_object) != NULL);
+
+ while (parent->alembicXform().getChildHeader(m_name)) {
+ m_name.append("_");
+ }
+
+ if (m_settings.use_subdiv_schema && m_is_subd) {
+ OSubD subd(parent->alembicXform(), m_name, m_time_sampling);
+ m_subdiv_schema = subd.getSchema();
+ }
+ else {
+ OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling);
+ m_mesh_schema = mesh.getSchema();
+
+ OCompoundProperty typeContainer = m_mesh_schema.getUserProperties();
+ OBoolProperty type(typeContainer, "meshtype");
+ type.set(m_is_subd);
+ }
+}
+
+AbcMeshWriter::~AbcMeshWriter()
+{
+ if (m_subsurf_mod) {
+ m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
+ }
+}
+
+bool AbcMeshWriter::isAnimated() const
+{
+ /* Check if object has shape keys. */
+ Mesh *me = static_cast<Mesh *>(m_object->data);
+
+ if (me->key) {
+ return true;
+ }
+
+ /* Test modifiers. */
+ ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
+
+ while (md) {
+ if (md->type != eModifierType_Subsurf) {
+ return true;
+ }
+
+ md = md->next;
+ }
+
+ return false;
+}
+
+void AbcMeshWriter::do_write()
+{
+ /* We have already stored a sample for this object. */
+ if (!m_first_frame && !m_is_animated)
+ return;
+
+ DerivedMesh *dm = getFinalMesh();
+
+ try {
+ if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
+ writeSubD(dm);
+ }
+ else {
+ writeMesh(dm);
+ }
+
+ freeMesh(dm);
+ }
+ catch (...) {
+ freeMesh(dm);
+ throw;
+ }
+}
+
+void AbcMeshWriter::writeMesh(DerivedMesh *dm)
+{
+ std::vector<Imath::V3f> points, normals;
+ std::vector<int32_t> poly_verts, loop_counts;
+
+ bool smooth_normal = false;
+
+ get_vertices(dm, points);
+ get_topology(dm, poly_verts, loop_counts, smooth_normal);
+
+ if (m_first_frame) {
+ writeCommonData(dm, m_mesh_schema);
+ }
+
+ m_mesh_sample = OPolyMeshSchema::Sample(V3fArraySample(points),
+ Int32ArraySample(poly_verts),
+ Int32ArraySample(loop_counts));
+
+ UVSample sample;
+ if (m_settings.export_uvs) {
+ const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
+
+ if (!sample.indices.empty() && !sample.uvs.empty()) {
+ OV2fGeomParam::Sample uv_sample;
+ uv_sample.setVals(V2fArraySample(sample.uvs));
+ uv_sample.setIndices(UInt32ArraySample(sample.indices));
+ uv_sample.setScope(kFacevaryingScope);
+
+ m_mesh_schema.setUVSourceName(name);
+ m_mesh_sample.setUVs(uv_sample);
+ }
+
+ write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
+ }
+
+ if (m_settings.export_normals) {
+ if (smooth_normal) {
+ get_loop_normals(dm, normals);
+ }
+ else {
+ get_vertex_normals(dm, normals);
+ }
+
+ ON3fGeomParam::Sample normals_sample;
+ if (!normals.empty()) {
+ normals_sample.setScope((smooth_normal) ? kFacevaryingScope : kVertexScope);
+ normals_sample.setVals(V3fArraySample(normals));
+ }
+
+ m_mesh_sample.setNormals(normals_sample);
+ }
+
+ if (m_is_liquid) {
+ std::vector<Imath::V3f> velocities;
+ getVelocities(dm, velocities);
+
+ m_mesh_sample.setVelocities(V3fArraySample(velocities));
+ }
+
+ m_mesh_sample.setSelfBounds(bounds());
+
+ m_mesh_schema.set(m_mesh_sample);
+
+ writeArbGeoParams(dm);
+}
+
+void AbcMeshWriter::writeSubD(DerivedMesh *dm)
+{
+ std::vector<float> crease_sharpness;
+ std::vector<Imath::V3f> points;
+ std::vector<int32_t> poly_verts, loop_counts;
+ std::vector<int32_t> crease_indices, crease_lengths;
+
+ bool smooth_normal = false;
+
+ get_vertices(dm, points);
+ get_topology(dm, poly_verts, loop_counts, smooth_normal);
+ get_creases(dm, crease_indices, crease_lengths, crease_sharpness);
+
+ if (m_first_frame) {
+ /* create materials' face_sets */
+ writeCommonData(dm, m_subdiv_schema);
+ }
+
+ m_subdiv_sample = OSubDSchema::Sample(V3fArraySample(points),
+ Int32ArraySample(poly_verts),
+ Int32ArraySample(loop_counts));
+
+ UVSample sample;
+ if (m_settings.export_uvs) {
+ const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
+
+ if (!sample.indices.empty() && !sample.uvs.empty()) {
+ OV2fGeomParam::Sample uv_sample;
+ uv_sample.setVals(V2fArraySample(sample.uvs));
+ uv_sample.setIndices(UInt32ArraySample(sample.indices));
+ uv_sample.setScope(kFacevaryingScope);
+
+ m_subdiv_schema.setUVSourceName(name);
+ m_subdiv_sample.setUVs(uv_sample);
+ }
+
+ write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
+ }
+
+ if (!crease_indices.empty()) {
+ m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
+ m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
+ m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
+ }
+
+ m_subdiv_sample.setSelfBounds(bounds());
+ m_subdiv_schema.set(m_subdiv_sample);
+
+ writeArbGeoParams(dm);
+}
+
+template <typename Schema>
+void AbcMeshWriter::writeCommonData(DerivedMesh *dm, Schema &schema)
+{
+ std::map< std::string, std::vector<int32_t> > geo_groups;
+ getGeoGroups(dm, geo_groups);
+
+ std::map< std::string, std::vector<int32_t> >::iterator it;
+ for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
+ OFaceSet face_set = schema.createFaceSet(it->first);
+ OFaceSetSchema::Sample samp;
+ samp.setFaces(Int32ArraySample(it->second));
+ face_set.getSchema().set(samp);
+ }
+}
+
+DerivedMesh *AbcMeshWriter::getFinalMesh()
+{
+ /* We don't want subdivided mesh data */
+ if (m_subsurf_mod) {
+ m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
+ }
+
+ DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH);
+
+ if (m_subsurf_mod) {
+ m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
+ }
+
+ m_custom_data_config.pack_uvs = m_settings.pack_uv;
+ m_custom_data_config.mpoly = dm->getPolyArray(dm);
+ m_custom_data_config.mloop = dm->getLoopArray(dm);
+ m_custom_data_config.totpoly = dm->getNumPolys(dm);
+ m_custom_data_config.totloop = dm->getNumLoops(dm);
+ m_custom_data_config.totvert = dm->getNumVerts(dm);
+
+ return dm;
+}
+
+void AbcMeshWriter::freeMesh(DerivedMesh *dm)
+{
+ dm->release(dm);
+}
+
+void AbcMeshWriter::writeArbGeoParams(DerivedMesh *dm)
+{
+ if (m_is_liquid) {
+ /* We don't need anything more for liquid meshes. */
+ return;
+ }
+
+ if (m_settings.export_vcols) {
+ if (m_subdiv_schema.valid()) {
+ write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
+ }
+ else {
+ write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
+ }
+ }
+
+ if (m_first_frame && m_has_per_face_materials) {
+ std::vector<int32_t> material_indices;
+
+ if (m_settings.export_face_sets) {
+ get_material_indices(dm, material_indices);
+
+ OFaceSetSchema::Sample samp;
+ samp.setFaces(Int32ArraySample(material_indices));
+ m_face_set.getSchema().set(samp);
+ }
+ }
+}
+
+void AbcMeshWriter::getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels)
+{
+ const int totverts = dm->getNumVerts(dm);
+
+ vels.clear();
+ vels.resize(totverts);
+
+ ModifierData *md = get_liquid_sim_modifier(m_scene, m_object);
+ FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md);
+ FluidsimSettings *fss = fmd->fss;
+
+ if (fss->meshVelocities) {
+ float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities);
+
+ for (int i = 0; i < totverts; ++i) {
+ copy_zup_yup(vels[i].getValue(), mesh_vels);
+ mesh_vels += 3;
+ }
+ }
+ else {
+ std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f));
+ }
+}
+
+void AbcMeshWriter::getGeoGroups(
+ DerivedMesh *dm,
+ std::map<std::string, std::vector<int32_t> > &geo_groups)
+{
+ const int num_poly = dm->getNumPolys(dm);
+ MPoly *polygons = dm->getPolyArray(dm);
+
+ for (int i = 0; i < num_poly; ++i) {
+ MPoly &current_poly = polygons[i];
+ short mnr = current_poly.mat_nr;
+
+ Material *mat = give_current_material(m_object, mnr + 1);
+
+ if (!mat) {
+ continue;
+ }
+
+ std::string name = get_id_name(&mat->id);
+
+ if (geo_groups.find(name) == geo_groups.end()) {
+ std::vector<int32_t> faceArray;
+ geo_groups[name] = faceArray;
+ }
+
+ geo_groups[name].push_back(i);
+ }
+
+ if (geo_groups.size() == 0) {
+ Material *mat = give_current_material(m_object, 1);
+
+ std::string name = (mat) ? get_id_name(&mat->id) : "default";
+
+ std::vector<int32_t> faceArray;
+
+ for (int i = 0, e = dm->getNumTessFaces(dm); i < e; ++i) {
+ faceArray.push_back(i);
+ }
+
+ geo_groups[name] = faceArray;
+ }
+}
+
+/* ************************************************************************** */
+
+/* Some helpers for mesh generation */
+namespace utils {
+
+void mesh_add_verts(Mesh *mesh, size_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ const int totvert = mesh->totvert + len;
+ CustomData vdata;
+ CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
+ CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
+
+ if (!CustomData_has_layer(&vdata, CD_MVERT)) {
+ CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
+ }
+
+ CustomData_free(&mesh->vdata, mesh->totvert);
+ mesh->vdata = vdata;
+ BKE_mesh_update_customdata_pointers(mesh, false);
+
+ mesh->totvert = totvert;
+}
+
+static void mesh_add_mloops(Mesh *mesh, size_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ /* new face count */
+ const int totloops = mesh->totloop + len;
+
+ CustomData ldata;
+ CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloops);
+ CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
+
+ if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
+ CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloops);
+ }
+
+ CustomData_free(&mesh->ldata, mesh->totloop);
+ mesh->ldata = ldata;
+ BKE_mesh_update_customdata_pointers(mesh, false);
+
+ mesh->totloop = totloops;
+}
+
+static void mesh_add_mpolygons(Mesh *mesh, size_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ const int totpolys = mesh->totpoly + len;
+
+ CustomData pdata;
+ CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpolys);
+ CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
+
+ if (!CustomData_has_layer(&pdata, CD_MPOLY)) {
+ CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpolys);
+ }
+
+ CustomData_free(&mesh->pdata, mesh->totpoly);
+ mesh->pdata = pdata;
+ BKE_mesh_update_customdata_pointers(mesh, false);
+
+ mesh->totpoly = totpolys;
+}
+
+static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map)
+{
+ Material *material = static_cast<Material *>(bmain->mat.first);
+
+ for (; material; material = static_cast<Material *>(material->id.next)) {
+ mat_map[material->id.name + 2] = material;
+ }
+}
+
+static void assign_materials(Main *bmain, Object *ob, const std::map<std::string, int> &mat_index_map)
+{
+ bool can_assign = true;
+ std::map<std::string, int>::const_iterator it = mat_index_map.begin();
+
+ int matcount = 0;
+ for (; it != mat_index_map.end(); ++it, ++matcount) {
+ if (!BKE_object_material_slot_add(ob)) {
+ can_assign = false;
+ break;
+ }
+ }
+
+ /* TODO(kevin): use global map? */
+ std::map<std::string, Material *> mat_map;
+ build_mat_map(bmain, mat_map);
+
+ std::map<std::string, Material *>::iterator mat_iter;
+
+ if (can_assign) {
+ it = mat_index_map.begin();
+
+ for (; it != mat_index_map.end(); ++it) {
+ std::string mat_name = it->first;
+ mat_iter = mat_map.find(mat_name.c_str());
+
+ Material *assigned_name;
+
+ if (mat_iter == mat_map.end()) {
+ assigned_name = BKE_material_add(bmain, mat_name.c_str());
+ mat_map[mat_name] = assigned_name;
+ }
+ else {
+ assigned_name = mat_iter->second;
+ }
+
+ assign_material(ob, assigned_name, it->second, BKE_MAT_ASSIGN_OBJECT);
+ }
+ }
+}
+
+} /* namespace utils */
+
+/* ************************************************************************** */
+
+using Alembic::AbcGeom::UInt32ArraySamplePtr;
+using Alembic::AbcGeom::V2fArraySamplePtr;
+
+struct AbcMeshData {
+ Int32ArraySamplePtr face_indices;
+ Int32ArraySamplePtr face_counts;
+
+ P3fArraySamplePtr positions;
+
+ N3fArraySamplePtr vertex_normals;
+ N3fArraySamplePtr face_normals;
+
+ V2fArraySamplePtr uvs;
+ UInt32ArraySamplePtr uvs_indices;
+};
+
+static void *add_customdata_cb(void *user_data, const char *name, int data_type)
+{
+ Mesh *mesh = static_cast<Mesh *>(user_data);
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+ void *cd_ptr = NULL;
+
+ int index = -1;
+ if (cd_data_type == CD_MLOOPUV) {
+ index = ED_mesh_uv_texture_add(mesh, name, true);
+ cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type);
+ }
+ else if (cd_data_type == CD_MLOOPCOL) {
+ index = ED_mesh_color_add(mesh, name, true);
+ cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type);
+ }
+
+ if (index == -1) {
+ return NULL;
+ }
+
+ return cd_ptr;
+}
+
+CDStreamConfig create_config(Mesh *mesh)
+{
+ CDStreamConfig config;
+
+ config.mvert = mesh->mvert;
+ config.mpoly = mesh->mpoly;
+ config.mloop = mesh->mloop;
+ config.totpoly = mesh->totpoly;
+ config.totloop = mesh->totloop;
+ config.user_data = mesh;
+ config.loopdata = &mesh->ldata;
+ config.add_customdata_cb = add_customdata_cb;
+
+ return config;
+}
+
+static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+ MVert *mverts = config.mvert;
+ const P3fArraySamplePtr &positions = mesh_data.positions;
+ const N3fArraySamplePtr &normals = mesh_data.vertex_normals;
+
+ read_mverts(mverts, positions, normals);
+}
+
+void read_mverts(MVert *mverts, const P3fArraySamplePtr &positions, const N3fArraySamplePtr &normals)
+{
+ for (int i = 0; i < positions->size(); ++i) {
+ MVert &mvert = mverts[i];
+ Imath::V3f pos_in = (*positions)[i];
+
+ copy_yup_zup(mvert.co, pos_in.getValue());
+
+ mvert.bweight = 0;
+
+ if (normals) {
+ Imath::V3f nor_in = (*normals)[i];
+
+ short no[3];
+ normal_float_to_short_v3(no, nor_in.getValue());
+
+ copy_yup_zup(mvert.no, no);
+ }
+ }
+}
+
+static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+ MPoly *mpolys = config.mpoly;
+ MLoop *mloops = config.mloop;
+ MLoopUV *mloopuvs = config.mloopuv;
+
+ const Int32ArraySamplePtr &face_indices = mesh_data.face_indices;
+ const Int32ArraySamplePtr &face_counts = mesh_data.face_counts;
+ const V2fArraySamplePtr &uvs = mesh_data.uvs;
+ const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
+ const N3fArraySamplePtr &normals = mesh_data.face_normals;
+
+ const bool do_uvs = (mloopuvs && uvs && uvs_indices) && (uvs_indices->size() == face_indices->size());
+ unsigned int loop_index = 0;
+ unsigned int rev_loop_index = 0;
+ unsigned int uv_index = 0;
+
+ for (int i = 0; i < face_counts->size(); ++i) {
+ const int face_size = (*face_counts)[i];
+
+ MPoly &poly = mpolys[i];
+ poly.loopstart = loop_index;
+ poly.totloop = face_size;
+
+ if (normals != NULL) {
+ poly.flag |= ME_SMOOTH;
+ }
+
+ /* NOTE: Alembic data is stored in the reverse order. */
+ rev_loop_index = loop_index + (face_size - 1);
+
+ for (int f = 0; f < face_size; ++f, ++loop_index, --rev_loop_index) {
+ MLoop &loop = mloops[rev_loop_index];
+ loop.v = (*face_indices)[loop_index];
+
+ if (do_uvs) {
+ MLoopUV &loopuv = mloopuvs[rev_loop_index];
+
+ uv_index = (*uvs_indices)[loop_index];
+ loopuv.uv[0] = (*uvs)[uv_index][0];
+ loopuv.uv[1] = (*uvs)[uv_index][1];
+ }
+ }
+ }
+}
+
+ABC_INLINE void read_uvs_params(CDStreamConfig &config,
+ AbcMeshData &abc_data,
+ const IV2fGeomParam &uv,
+ const ISampleSelector &selector)
+{
+ if (!uv.valid()) {
+ return;
+ }
+
+ IV2fGeomParam::Sample uvsamp;
+ uv.getIndexed(uvsamp, selector);
+
+ abc_data.uvs = uvsamp.getVals();
+ abc_data.uvs_indices = uvsamp.getIndices();
+
+ if (abc_data.uvs_indices->size() == config.totloop) {
+ std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
+
+ /* According to the convention, primary UVs should have had their name
+ * set using Alembic::Abc::SetSourceName, but you can't expect everyone
+ * to follow it! :) */
+ if (name.empty()) {
+ name = uv.getName();
+ }
+
+ void *cd_ptr = config.add_customdata_cb(config.user_data, name.c_str(), CD_MLOOPUV);
+ config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
+ }
+}
+
+/* TODO(kevin): normals from Alembic files are not read in anymore, this is due
+ * to the fact that there are many issues that are not so easy to solve, mainly
+ * regarding the way normals are handled in Blender (MPoly.flag vs loop normals).
+ */
+ABC_INLINE void read_normals_params(AbcMeshData &abc_data,
+ const IN3fGeomParam &normals,
+ const ISampleSelector &selector)
+{
+ if (!normals.valid()) {
+ return;
+ }
+
+ IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
+
+ if (normals.getScope() == kFacevaryingScope) {
+ abc_data.face_normals = normsamp.getVals();
+ }
+ else if ((normals.getScope() == kVertexScope) || (normals.getScope() == kVaryingScope)) {
+ abc_data.vertex_normals = N3fArraySamplePtr();
+ }
+}
+
+/* ************************************************************************** */
+
+AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
+
+ IPolyMesh ipoly_mesh(m_iobject, kWrapExisting);
+ m_schema = ipoly_mesh.getSchema();
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcMeshReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcMeshReader::readObjectData(Main *bmain, float time)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+ m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+ m_object->data = mesh;
+
+ const ISampleSelector sample_sel(time);
+ const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ utils::mesh_add_verts(mesh, positions->size());
+ utils::mesh_add_mpolygons(mesh, face_counts->size());
+ utils::mesh_add_mloops(mesh, face_indices->size());
+
+ m_mesh_data = create_config(mesh);
+
+ bool has_smooth_normals = false;
+ read_mesh_sample(m_settings, m_schema, sample_sel, m_mesh_data, has_smooth_normals);
+
+ BKE_mesh_calc_normals(mesh);
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ readFaceSetsSample(bmain, mesh, 0, sample_sel);
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
+ const ISampleSelector &sample_sel)
+{
+ std::vector<std::string> face_sets;
+ m_schema.getFaceSetNames(face_sets);
+
+ if (face_sets.empty()) {
+ return;
+ }
+
+ std::map<std::string, int> mat_map;
+ int current_mat = 0;
+
+ for (int i = 0; i < face_sets.size(); ++i) {
+ const std::string &grp_name = face_sets[i];
+
+ if (mat_map.find(grp_name) == mat_map.end()) {
+ mat_map[grp_name] = 1 + current_mat++;
+ }
+
+ const int assigned_mat = mat_map[grp_name];
+
+ const IFaceSet faceset = m_schema.getFaceSet(grp_name);
+
+ if (!faceset.valid()) {
+ continue;
+ }
+
+ const IFaceSetSchema face_schem = faceset.getSchema();
+ const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
+ const Int32ArraySamplePtr group_faces = face_sample.getFaces();
+ const size_t num_group_faces = group_faces->size();
+
+ for (size_t l = 0; l < num_group_faces; l++) {
+ size_t pos = (*group_faces)[l] + poly_start;
+
+ if (pos >= mesh->totpoly) {
+ std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
+ break;
+ }
+
+ MPoly &poly = mesh->mpoly[pos];
+ poly.mat_nr = assigned_mat - 1;
+ }
+ }
+
+ utils::assign_materials(bmain, m_object, mat_map);
+}
+
+void read_mesh_sample(ImportSettings *settings,
+ const IPolyMeshSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config,
+ bool &do_normals)
+{
+ const IPolyMeshSchema::Sample sample = schema.getValue(selector);
+
+ AbcMeshData abc_mesh_data;
+ abc_mesh_data.face_counts = sample.getFaceCounts();
+ abc_mesh_data.face_indices = sample.getFaceIndices();
+ abc_mesh_data.positions = sample.getPositions();
+
+ read_normals_params(abc_mesh_data, schema.getNormalsParam(), selector);
+
+ do_normals = (abc_mesh_data.face_normals != NULL);
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+ read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ read_mverts(config, abc_mesh_data);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+ read_mpolys(config, abc_mesh_data);
+ }
+
+ if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+ read_custom_data(schema.getArbGeomParams(), config, selector);
+ }
+
+ /* TODO: face sets */
+}
+
+/* ************************************************************************** */
+
+ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
+{
+ for (int i = 0, e = totedge; i < e; ++i) {
+ MEdge &edge = edges[i];
+
+ if (edge.v1 == v1 && edge.v2 == v2) {
+ return &edge;
+ }
+ }
+
+ return NULL;
+}
+
+AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
+
+ ISubD isubd_mesh(m_iobject, kWrapExisting);
+ m_schema = isubd_mesh.getSchema();
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcSubDReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcSubDReader::readObjectData(Main *bmain, float time)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+ m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+ m_object->data = mesh;
+
+ const ISampleSelector sample_sel(time);
+ const ISubDSchema::Sample sample = m_schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ utils::mesh_add_verts(mesh, positions->size());
+ utils::mesh_add_mpolygons(mesh, face_counts->size());
+ utils::mesh_add_mloops(mesh, face_indices->size());
+
+ m_mesh_data = create_config(mesh);
+
+ read_subd_sample(m_settings, m_schema, sample_sel, m_mesh_data);
+
+ Int32ArraySamplePtr indices = sample.getCreaseIndices();
+ Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
+
+ MEdge *edges = mesh->medge;
+
+ if (indices && sharpnesses) {
+ for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, ++s) {
+ MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]);
+
+ if (edge) {
+ edge->crease = FTOCHAR((*sharpnesses)[s]);
+ }
+ }
+
+ mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
+ }
+
+ BKE_mesh_calc_normals(mesh);
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+void read_subd_sample(ImportSettings *settings,
+ const ISubDSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config)
+{
+ const ISubDSchema::Sample sample = schema.getValue(selector);
+
+ AbcMeshData abc_mesh_data;
+ abc_mesh_data.face_counts = sample.getFaceCounts();
+ abc_mesh_data.face_indices = sample.getFaceIndices();
+ abc_mesh_data.vertex_normals = N3fArraySamplePtr();
+ abc_mesh_data.face_normals = N3fArraySamplePtr();
+ abc_mesh_data.positions = sample.getPositions();
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+ read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ read_mverts(config, abc_mesh_data);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+ read_mpolys(config, abc_mesh_data);
+ }
+
+ if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+ read_custom_data(schema.getArbGeomParams(), config, selector);
+ }
+
+ /* TODO: face sets */
+}
diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h
new file mode 100644
index 00000000000..9dc222e5206
--- /dev/null
+++ b/source/blender/alembic/intern/abc_mesh.h
@@ -0,0 +1,152 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_MESH_H__
+#define __ABC_MESH_H__
+
+#include "abc_customdata.h"
+#include "abc_object.h"
+
+struct DerivedMesh;
+struct Mesh;
+struct ModifierData;
+
+/* ************************************************************************** */
+
+class AbcMeshWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema;
+ Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample;
+
+ Alembic::AbcGeom::OSubDSchema m_subdiv_schema;
+ Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample;
+
+ bool m_has_per_face_materials;
+ Alembic::AbcGeom::OFaceSet m_face_set;
+ Alembic::Abc::OArrayProperty m_mat_indices;
+
+ bool m_is_animated;
+ ModifierData *m_subsurf_mod;
+
+ CDStreamConfig m_custom_data_config;
+
+ bool m_is_liquid;
+ bool m_is_subd;
+
+public:
+ AbcMeshWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+ ~AbcMeshWriter();
+
+private:
+ virtual void do_write();
+
+ bool isAnimated() const;
+
+ void writeMesh(DerivedMesh *dm);
+ void writeSubD(DerivedMesh *dm);
+
+ void getMeshInfo(DerivedMesh *dm, std::vector<float> &points,
+ std::vector<int32_t> &facePoints,
+ std::vector<int32_t> &faceCounts,
+ std::vector<int32_t> &creaseIndices,
+ std::vector<int32_t> &creaseLengths,
+ std::vector<float> &creaseSharpness);
+
+ DerivedMesh *getFinalMesh();
+ void freeMesh(DerivedMesh *dm);
+
+ void getMaterialIndices(DerivedMesh *dm, std::vector<int32_t> &indices);
+
+ void writeArbGeoParams(DerivedMesh *dm);
+ void getGeoGroups(DerivedMesh *dm, std::map<std::string, std::vector<int32_t> > &geoGroups);
+
+ /* fluid surfaces support */
+ void getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels);
+
+ template <typename Schema>
+ void writeCommonData(DerivedMesh *dm, Schema &schema);
+};
+
+/* ************************************************************************** */
+
+class AbcMeshReader : public AbcObjectReader {
+ Alembic::AbcGeom::IPolyMeshSchema m_schema;
+
+ CDStreamConfig m_mesh_data;
+
+public:
+ AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+
+private:
+ void readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
+ const Alembic::AbcGeom::ISampleSelector &sample_sel);
+};
+
+void read_mesh_sample(ImportSettings *settings,
+ const Alembic::AbcGeom::IPolyMeshSchema &schema,
+ const Alembic::AbcGeom::ISampleSelector &selector,
+ CDStreamConfig &config,
+ bool &do_normals);
+
+/* ************************************************************************** */
+
+class AbcSubDReader : public AbcObjectReader {
+ Alembic::AbcGeom::ISubDSchema m_schema;
+
+ CDStreamConfig m_mesh_data;
+
+public:
+ AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+void read_subd_sample(ImportSettings *settings,
+ const Alembic::AbcGeom::ISubDSchema &schema,
+ const Alembic::AbcGeom::ISampleSelector &selector,
+ CDStreamConfig &config);
+
+/* ************************************************************************** */
+
+namespace utils {
+
+void mesh_add_verts(struct Mesh *mesh, size_t len);
+
+}
+
+void read_mverts(MVert *mverts,
+ const Alembic::AbcGeom::P3fArraySamplePtr &positions,
+ const Alembic::AbcGeom::N3fArraySamplePtr &normals);
+
+CDStreamConfig create_config(Mesh *mesh);
+
+#endif /* __ABC_MESH_H__ */
diff --git a/source/blender/alembic/intern/abc_nurbs.cc b/source/blender/alembic/intern/abc_nurbs.cc
new file mode 100644
index 00000000000..a3c18ad6301
--- /dev/null
+++ b/source/blender/alembic/intern/abc_nurbs.cc
@@ -0,0 +1,367 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_nurbs.h"
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "BKE_curve.h"
+#include "BKE_object.h"
+}
+
+using Alembic::AbcGeom::bool_t;
+using Alembic::AbcGeom::FloatArraySample;
+using Alembic::AbcGeom::FloatArraySamplePtr;
+using Alembic::AbcGeom::MetaData;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::kWrapExisting;
+
+using Alembic::AbcGeom::IBoolProperty;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::INuPatch;
+using Alembic::AbcGeom::INuPatchSchema;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OBoolProperty;
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::ONuPatch;
+using Alembic::AbcGeom::ONuPatchSchema;
+
+/* ************************************************************************** */
+
+AbcNurbsWriter::AbcNurbsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_is_animated = isAnimated();
+
+ /* if the object is static, use the default static time sampling */
+ if (!m_is_animated) {
+ m_time_sampling = 0;
+ }
+
+ Curve *curve = static_cast<Curve *>(m_object->data);
+ size_t numNurbs = BLI_listbase_count(&curve->nurb);
+
+ for (size_t i = 0; i < numNurbs; ++i) {
+ std::stringstream str;
+ str << m_name << '_' << i;
+
+ while (parent->alembicXform().getChildHeader(str.str())) {
+ str << "_";
+ }
+
+ ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
+ m_nurbs_schema.push_back(nurbs.getSchema());
+ }
+}
+
+bool AbcNurbsWriter::isAnimated() const
+{
+ /* check if object has shape keys */
+ Curve *cu = static_cast<Curve *>(m_object->data);
+ return (cu->key != NULL);
+}
+
+static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
+{
+ if (num_knots <= 1) {
+ return;
+ }
+
+ /* Add an extra knot at the beggining and end of the array since most apps
+ * require/expect them. */
+ knots.reserve(num_knots + 2);
+
+ knots.push_back(0.0f);
+
+ for (int i = 0; i < num_knots; ++i) {
+ knots.push_back(nu_knots[i]);
+ }
+
+ knots[0] = 2.0f * knots[1] - knots[2];
+ knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
+}
+
+void AbcNurbsWriter::do_write()
+{
+ /* we have already stored a sample for this object. */
+ if (!m_first_frame && !m_is_animated) {
+ return;
+ }
+
+ if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
+ return;
+ }
+
+ Curve *curve = static_cast<Curve *>(m_object->data);
+ ListBase *nulb;
+
+ if (m_object->curve_cache->deformed_nurbs.first != NULL) {
+ nulb = &m_object->curve_cache->deformed_nurbs;
+ }
+ else {
+ nulb = BKE_curve_nurbs_get(curve);
+ }
+
+ size_t count = 0;
+ for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) {
+ std::vector<float> knotsU;
+ get_knots(knotsU, KNOTSU(nu), nu->knotsu);
+
+ std::vector<float> knotsV;
+ get_knots(knotsV, KNOTSV(nu), nu->knotsv);
+
+ const int size = nu->pntsu * nu->pntsv;
+ std::vector<Imath::V3f> positions(size);
+ std::vector<float> weights(size);
+
+ const BPoint *bp = nu->bp;
+
+ for (int i = 0; i < size; ++i, ++bp) {
+ copy_zup_yup(positions[i].getValue(), bp->vec);
+ weights[i] = bp->vec[3];
+ }
+
+ ONuPatchSchema::Sample sample;
+ sample.setUOrder(nu->orderu + 1);
+ sample.setVOrder(nu->orderv + 1);
+ sample.setPositions(positions);
+ sample.setPositionWeights(weights);
+ sample.setUKnot(FloatArraySample(knotsU));
+ sample.setVKnot(FloatArraySample(knotsV));
+ sample.setNu(nu->pntsu);
+ sample.setNv(nu->pntsv);
+
+ /* TODO(kevin): to accomodate other software we should duplicate control
+ * points to indicate that a NURBS is cyclic. */
+ OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
+
+ if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
+ OBoolProperty prop(user_props, "endpoint_u");
+ prop.set(true);
+ }
+
+ if ((nu->flagv & CU_NURB_ENDPOINT) != 0) {
+ OBoolProperty prop(user_props, "endpoint_v");
+ prop.set(true);
+ }
+
+ if ((nu->flagu & CU_NURB_CYCLIC) != 0) {
+ OBoolProperty prop(user_props, "cyclic_u");
+ prop.set(true);
+ }
+
+ if ((nu->flagv & CU_NURB_CYCLIC) != 0) {
+ OBoolProperty prop(user_props, "cyclic_v");
+ prop.set(true);
+ }
+
+ m_nurbs_schema[count].set(sample);
+ }
+}
+
+/* ************************************************************************** */
+
+AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ getNurbsPatches(m_iobject);
+ get_min_max_time(m_schemas[0].first, m_min_time, m_max_time);
+}
+
+bool AbcNurbsReader::valid() const
+{
+ if (m_schemas.empty()) {
+ return false;
+ }
+
+ std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it;
+ for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
+ const INuPatchSchema &schema = it->first;
+
+ if (!schema.valid()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
+{
+ if (!knots || knots->size() == 0) {
+ return false;
+ }
+
+ /* Skip first and last knots, as they are used for padding. */
+ const size_t num_knots = knots->size() - 2;
+ nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu"));
+
+ for (size_t i = 0; i < num_knots; ++i) {
+ nu_knots[i] = (*knots)[i + 1];
+ }
+
+ return true;
+}
+
+void AbcNurbsReader::readObjectData(Main *bmain, float time)
+{
+ Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF));
+ cu->actvert = CU_ACT_NONE;
+
+ std::vector< std::pair<INuPatchSchema, IObject> >::iterator it;
+
+ for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
+ Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
+ nu->flag = CU_SMOOTH;
+ nu->type = CU_NURBS;
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+
+ const ISampleSelector sample_sel(time);
+ const INuPatchSchema &schema = it->first;
+ const INuPatchSchema::Sample smp = schema.getValue(sample_sel);
+
+ nu->orderu = smp.getUOrder() - 1;
+ nu->orderv = smp.getVOrder() - 1;
+ nu->pntsu = smp.getNumU();
+ nu->pntsv = smp.getNumV();
+
+ /* Read positions and weights. */
+
+ const P3fArraySamplePtr positions = smp.getPositions();
+ const FloatArraySamplePtr weights = smp.getPositionWeights();
+
+ const size_t num_points = positions->size();
+
+ nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype"));
+
+ BPoint *bp = nu->bp;
+ float posw_in = 1.0f;
+
+ for (int i = 0; i < num_points; ++i, ++bp) {
+ const Imath::V3f &pos_in = (*positions)[i];
+
+ if (weights) {
+ posw_in = (*weights)[i];
+ }
+
+ copy_yup_zup(bp->vec, pos_in.getValue());
+ bp->vec[3] = posw_in;
+ bp->f1 = SELECT;
+ bp->radius = 1.0f;
+ bp->weight = 1.0f;
+ }
+
+ /* Read knots. */
+
+ if (!set_knots(smp.getUKnot(), nu->knotsu)) {
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ if (!set_knots(smp.getVKnot(), nu->knotsv)) {
+ BKE_nurb_knot_calc_v(nu);
+ }
+
+ /* Read flags. */
+
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ if (has_property(user_props, "enpoint_u")) {
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+
+ if (has_property(user_props, "enpoint_v")) {
+ nu->flagv |= CU_NURB_ENDPOINT;
+ }
+
+ if (has_property(user_props, "cyclic_u")) {
+ nu->flagu |= CU_NURB_CYCLIC;
+ }
+
+ if (has_property(user_props, "cyclic_v")) {
+ nu->flagv |= CU_NURB_CYCLIC;
+ }
+
+ BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+ }
+
+ BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
+
+ m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str());
+ m_object->data = cu;
+}
+
+void AbcNurbsReader::getNurbsPatches(const IObject &obj)
+{
+ if (!obj.valid()) {
+ return;
+ }
+
+ const int num_children = obj.getNumChildren();
+
+ if (num_children == 0) {
+ INuPatch abc_nurb(obj, kWrapExisting);
+ INuPatchSchema schem = abc_nurb.getSchema();
+ m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj));
+ return;
+ }
+
+ for (int i = 0; i < num_children; ++i) {
+ bool ok = true;
+ IObject child(obj, obj.getChildHeader(i).getName());
+
+ if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) {
+ ok = false;
+ }
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ const MetaData &md = child.getMetaData();
+
+ if (INuPatch::matches(md) && ok) {
+ INuPatch abc_nurb(child, kWrapExisting);
+ INuPatchSchema schem = abc_nurb.getSchema();
+ m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child));
+ }
+
+ getNurbsPatches(child);
+ }
+}
diff --git a/source/blender/alembic/intern/abc_nurbs.h b/source/blender/alembic/intern/abc_nurbs.h
new file mode 100644
index 00000000000..1b2e7a8391f
--- /dev/null
+++ b/source/blender/alembic/intern/abc_nurbs.h
@@ -0,0 +1,63 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_NURBS_H__
+#define __ABC_NURBS_H__
+
+#include "abc_object.h"
+
+/* ************************************************************************** */
+
+class AbcNurbsWriter : public AbcObjectWriter {
+ std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema;
+ bool m_is_animated;
+
+public:
+ AbcNurbsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings);
+
+private:
+ virtual void do_write();
+
+ bool isAnimated() const;
+};
+
+/* ************************************************************************** */
+
+class AbcNurbsReader : public AbcObjectReader {
+ std::vector< std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject> > m_schemas;
+
+public:
+ AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+
+private:
+ void getNurbsPatches(const Alembic::Abc::IObject &obj);
+};
+
+#endif /* __ABC_NURBS_H__ */
diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc
new file mode 100644
index 00000000000..5b7b85f10ea
--- /dev/null
+++ b/source/blender/alembic/intern/abc_object.cc
@@ -0,0 +1,238 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_object.h"
+
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_cachefile_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_space_types.h" /* for FILE_MAX */
+
+#include "BKE_constraint.h"
+#include "BKE_depsgraph.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+}
+
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::IXformSchema;
+
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::ODoubleArrayProperty;
+using Alembic::AbcGeom::ODoubleProperty;
+using Alembic::AbcGeom::OFloatArrayProperty;
+using Alembic::AbcGeom::OFloatProperty;
+using Alembic::AbcGeom::OInt32ArrayProperty;
+using Alembic::AbcGeom::OInt32Property;
+using Alembic::AbcGeom::OStringArrayProperty;
+using Alembic::AbcGeom::OStringProperty;
+
+/* ************************************************************************** */
+
+AbcObjectWriter::AbcObjectWriter(Scene *scene,
+ Object *ob,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ AbcObjectWriter *parent)
+ : m_object(ob)
+ , m_settings(settings)
+ , m_scene(scene)
+ , m_time_sampling(time_sampling)
+ , m_first_frame(true)
+{
+ m_name = get_id_name(m_object) + "Shape";
+
+ if (parent) {
+ parent->addChild(this);
+ }
+}
+
+AbcObjectWriter::~AbcObjectWriter()
+{}
+
+void AbcObjectWriter::addChild(AbcObjectWriter *child)
+{
+ m_children.push_back(child);
+}
+
+Imath::Box3d AbcObjectWriter::bounds()
+{
+ BoundBox *bb = BKE_object_boundbox_get(this->m_object);
+
+ if (!bb) {
+ if (this->m_object->type != OB_CAMERA) {
+ std::cerr << "Boundbox is null!\n";
+ }
+
+ return Imath::Box3d();
+ }
+
+ /* Convert Z-up to Y-up. */
+ this->m_bounds.min.x = bb->vec[0][0];
+ this->m_bounds.min.y = bb->vec[0][2];
+ this->m_bounds.min.z = -bb->vec[0][1];
+
+ this->m_bounds.max.x = bb->vec[6][0];
+ this->m_bounds.max.y = bb->vec[6][2];
+ this->m_bounds.max.z = -bb->vec[6][1];
+
+ return this->m_bounds;
+}
+
+void AbcObjectWriter::write()
+{
+ do_write();
+ m_first_frame = false;
+}
+
+/* ************************************************************************** */
+
+AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings)
+ : m_name("")
+ , m_object_name("")
+ , m_data_name("")
+ , m_object(NULL)
+ , m_iobject(object)
+ , m_settings(&settings)
+ , m_min_time(std::numeric_limits<chrono_t>::max())
+ , m_max_time(std::numeric_limits<chrono_t>::min())
+{
+ m_name = object.getFullName();
+ std::vector<std::string> parts;
+ split(m_name, '/', parts);
+
+ if (parts.size() >= 2) {
+ m_object_name = parts[parts.size() - 2];
+ m_data_name = parts[parts.size() - 1];
+ }
+ else {
+ m_object_name = m_data_name = parts[parts.size() - 1];
+ }
+}
+
+AbcObjectReader::~AbcObjectReader()
+{}
+
+const IObject &AbcObjectReader::iobject() const
+{
+ return m_iobject;
+}
+
+Object *AbcObjectReader::object() const
+{
+ return m_object;
+}
+
+void AbcObjectReader::readObjectMatrix(const float time)
+{
+ IXform ixform;
+ bool has_alembic_parent = false;
+
+ /* Check that we have an empty object (locator, bone head/tail...). */
+ if (IXform::matches(m_iobject.getMetaData())) {
+ ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
+
+ /* See comment below. */
+ has_alembic_parent = m_iobject.getParent().getParent().valid();
+ }
+ /* Check that we have an object with actual data. */
+ else if (IXform::matches(m_iobject.getParent().getMetaData())) {
+ ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting);
+
+ /* This is a bit hackish, but we need to make sure that extra
+ * transformations added to the matrix (rotation/scale) are only applied
+ * to root objects. The way objects and their hierarchy are created will
+ * need to be revisited at some point but for now this seems to do the
+ * trick.
+ *
+ * Explanation of the trick:
+ * The first getParent() will return this object's transformation matrix.
+ * The second getParent() will get the parent of the transform, but this
+ * might be the archive root ('/') which is valid, so we go passed it to
+ * make sure that there is no parent.
+ */
+ has_alembic_parent = m_iobject.getParent().getParent().getParent().valid();
+ }
+ /* Should not happen. */
+ else {
+ return;
+ }
+
+ const IXformSchema &schema(ixform.getSchema());
+
+ if (!schema.valid()) {
+ return;
+ }
+
+ Alembic::AbcGeom::ISampleSelector sample_sel(time);
+ Alembic::AbcGeom::XformSample xs;
+ schema.get(xs, sample_sel);
+
+ create_input_transform(sample_sel, ixform, m_object, m_object->obmat, m_settings->scale, has_alembic_parent);
+
+ invert_m4_m4(m_object->imat, m_object->obmat);
+
+ BKE_object_apply_mat4(m_object, m_object->obmat, false, false);
+
+ if (!schema.isConstant()) {
+ bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE);
+ bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
+ BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+
+ data->cache_file = m_settings->cache_file;
+ id_us_plus(&data->cache_file->id);
+ }
+}
+
+void AbcObjectReader::addCacheModifier() const
+{
+ ModifierData *md = modifier_new(eModifierType_MeshSequenceCache);
+ BLI_addtail(&m_object->modifiers, md);
+
+ MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
+
+ mcmd->cache_file = m_settings->cache_file;
+ id_us_plus(&mcmd->cache_file->id);
+
+ BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+}
+
+chrono_t AbcObjectReader::minTime() const
+{
+ return m_min_time;
+}
+
+chrono_t AbcObjectReader::maxTime() const
+{
+ return m_max_time;
+}
diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h
new file mode 100644
index 00000000000..2e885f296b1
--- /dev/null
+++ b/source/blender/alembic/intern/abc_object.h
@@ -0,0 +1,169 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_OBJECT_H__
+#define __ABC_OBJECT_H__
+
+#include <Alembic/Abc/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#include "abc_exporter.h"
+
+extern "C" {
+#include "DNA_ID.h"
+}
+
+class AbcTransformWriter;
+
+struct Main;
+struct Object;
+
+/* ************************************************************************** */
+
+class AbcObjectWriter {
+protected:
+ Object *m_object;
+ ExportSettings &m_settings;
+
+ Scene *m_scene;
+ uint32_t m_time_sampling;
+
+ Imath::Box3d m_bounds;
+ std::vector<AbcObjectWriter *> m_children;
+
+ std::vector< std::pair<std::string, IDProperty *> > m_props;
+
+ bool m_first_frame;
+ std::string m_name;
+
+public:
+ AbcObjectWriter(Scene *scene,
+ Object *ob,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ AbcObjectWriter *parent = NULL);
+
+ virtual ~AbcObjectWriter();
+
+ void addChild(AbcObjectWriter *child);
+
+ virtual Imath::Box3d bounds();
+
+ void write();
+
+private:
+ virtual void do_write() = 0;
+};
+
+/* ************************************************************************** */
+
+class CacheFile;
+
+struct ImportSettings {
+ bool do_convert_mat;
+ float conversion_mat[4][4];
+
+ int from_up;
+ int from_forward;
+ float scale;
+ bool is_sequence;
+ bool set_frame_range;
+
+ /* Length and frame offset of file sequences. */
+ int sequence_len;
+ int offset;
+
+ /* From MeshSeqCacheModifierData.read_flag */
+ int read_flag;
+
+ bool validate_meshes;
+
+ CacheFile *cache_file;
+
+ ImportSettings()
+ : do_convert_mat(false)
+ , from_up(0)
+ , from_forward(0)
+ , scale(1.0f)
+ , is_sequence(false)
+ , set_frame_range(false)
+ , sequence_len(1)
+ , offset(0)
+ , read_flag(0)
+ , validate_meshes(false)
+ , cache_file(NULL)
+ {}
+};
+
+template <typename Schema>
+static bool has_animations(Schema &schema, ImportSettings *settings)
+{
+ if (settings->is_sequence) {
+ return true;
+ }
+
+ if (!schema.isConstant()) {
+ return true;
+ }
+
+ return false;
+}
+
+/* ************************************************************************** */
+
+using Alembic::AbcCoreAbstract::chrono_t;
+
+class AbcObjectReader {
+protected:
+ std::string m_name;
+ std::string m_object_name;
+ std::string m_data_name;
+ Object *m_object;
+ Alembic::Abc::IObject m_iobject;
+
+ ImportSettings *m_settings;
+
+ chrono_t m_min_time;
+ chrono_t m_max_time;
+
+public:
+ explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ virtual ~AbcObjectReader();
+
+ const Alembic::Abc::IObject &iobject() const;
+
+ Object *object() const;
+
+ virtual bool valid() const = 0;
+
+ virtual void readObjectData(Main *bmain, float time) = 0;
+
+ void readObjectMatrix(const float time);
+
+ void addCacheModifier() const;
+
+ chrono_t minTime() const;
+ chrono_t maxTime() const;
+};
+
+#endif /* __ABC_OBJECT_H__ */
diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc
new file mode 100644
index 00000000000..fa5b71ac094
--- /dev/null
+++ b/source/blender/alembic/intern/abc_points.cc
@@ -0,0 +1,198 @@
+/*
+ * ***** 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) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_points.h"
+
+#include "abc_mesh.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_mesh_types.h"
+
+#include "BKE_lattice.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+
+#include "BLI_math.h"
+}
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IN3fArrayProperty;
+using Alembic::AbcGeom::IPoints;
+using Alembic::AbcGeom::IPointsSchema;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OPoints;
+using Alembic::AbcGeom::OPointsSchema;
+
+/* ************************************************************************** */
+
+AbcPointsWriter::AbcPointsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys)
+ : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+ m_psys = psys;
+
+ OPoints points(parent->alembicXform(), m_name, m_time_sampling);
+ m_schema = points.getSchema();
+}
+
+void AbcPointsWriter::do_write()
+{
+ if (!m_psys) {
+ return;
+ }
+
+ std::vector<Imath::V3f> points;
+ std::vector<Imath::V3f> velocities;
+ std::vector<float> widths;
+ std::vector<uint64_t> ids;
+
+ ParticleKey state;
+
+ ParticleSimulationData sim;
+ sim.scene = m_scene;
+ sim.ob = m_object;
+ sim.psys = m_psys;
+
+ m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ uint64_t index = 0;
+ for (int p = 0; p < m_psys->totpart; p++) {
+ float pos[3], vel[3];
+
+ if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
+ continue;
+ }
+
+ state.time = BKE_scene_frame_get(m_scene);
+
+ if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
+ continue;
+ }
+
+ /* location */
+ mul_v3_m4v3(pos, m_object->imat, state.co);
+
+ /* velocity */
+ sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co);
+
+ /* Convert Z-up to Y-up. */
+ points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
+ velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1]));
+ widths.push_back(m_psys->particles[p].size);
+ ids.push_back(index++);
+ }
+
+ if (m_psys->lattice_deform_data) {
+ end_latt_deform(m_psys->lattice_deform_data);
+ m_psys->lattice_deform_data = NULL;
+ }
+
+ Alembic::Abc::P3fArraySample psample(points);
+ Alembic::Abc::UInt64ArraySample idsample(ids);
+ Alembic::Abc::V3fArraySample vsample(velocities);
+ Alembic::Abc::FloatArraySample wsample_array(widths);
+ Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
+
+ m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample);
+ m_sample.setSelfBounds(bounds());
+
+ m_schema.set(m_sample);
+}
+
+/* ************************************************************************** */
+
+AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ IPoints ipoints(m_iobject, kWrapExisting);
+ m_schema = ipoints.getSchema();
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcPointsReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcPointsReader::readObjectData(Main *bmain, float time)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+ const ISampleSelector sample_sel(time);
+ m_sample = m_schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = m_sample.getPositions();
+ utils::mesh_add_verts(mesh, positions->size());
+
+ CDStreamConfig config = create_config(mesh);
+ read_points_sample(m_schema, sample_sel, config, time);
+
+ if (m_settings->validate_meshes) {
+ BKE_mesh_validate(mesh, false, false);
+ }
+
+ m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+ m_object->data = mesh;
+
+ if (has_animations(m_schema, m_settings)) {
+ addCacheModifier();
+ }
+}
+
+void read_points_sample(const IPointsSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config,
+ float time)
+{
+ Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+
+ ICompoundProperty prop = schema.getArbGeomParams();
+ N3fArraySamplePtr vnormals;
+
+ if (has_property(prop, "N")) {
+ const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", time);
+
+ if (normals_prop) {
+ vnormals = normals_prop.getValue(selector);
+ }
+ }
+
+ read_mverts(config.mvert, positions, vnormals);
+}
diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h
new file mode 100644
index 00000000000..51f3103bd8b
--- /dev/null
+++ b/source/blender/alembic/intern/abc_points.h
@@ -0,0 +1,70 @@
+/*
+ * ***** 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) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_POINTS_H__
+#define __ABC_POINTS_H__
+
+#include "abc_object.h"
+#include "abc_customdata.h"
+
+class ParticleSystem;
+
+/* ************************************************************************** */
+
+class AbcPointsWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OPointsSchema m_schema;
+ Alembic::AbcGeom::OPointsSchema::Sample m_sample;
+ ParticleSystem *m_psys;
+
+public:
+ AbcPointsWriter(Scene *scene,
+ Object *ob,
+ AbcTransformWriter *parent,
+ uint32_t time_sampling,
+ ExportSettings &settings,
+ ParticleSystem *psys);
+
+ void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcPointsReader : public AbcObjectReader {
+ Alembic::AbcGeom::IPointsSchema m_schema;
+ Alembic::AbcGeom::IPointsSchema::Sample m_sample;
+
+public:
+ AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema,
+ const Alembic::AbcGeom::ISampleSelector &selector,
+ CDStreamConfig &config,
+ float time);
+
+#endif /* __ABC_POINTS_H__ */
diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc
new file mode 100644
index 00000000000..3326ae0ed84
--- /dev/null
+++ b/source/blender/alembic/intern/abc_transform.cc
@@ -0,0 +1,152 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_transform.h"
+
+#include <OpenEXR/ImathBoxAlgo.h>
+
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+
+#include "BKE_object.h"
+}
+
+using Alembic::AbcGeom::OObject;
+using Alembic::AbcGeom::OXform;
+
+/* ************************************************************************** */
+
+static bool has_parent_camera(Object *ob)
+{
+ if (!ob->parent) {
+ return false;
+ }
+
+ Object *parent = ob->parent;
+
+ if (parent->type == OB_CAMERA) {
+ return true;
+ }
+
+ return has_parent_camera(parent);
+}
+
+/* ************************************************************************** */
+
+AbcTransformWriter::AbcTransformWriter(Object *ob,
+ const OObject &abc_parent,
+ AbcTransformWriter *parent,
+ unsigned int time_sampling,
+ ExportSettings &settings)
+ : AbcObjectWriter(NULL, ob, time_sampling, settings, parent)
+{
+ m_is_animated = hasAnimation(m_object);
+ m_parent = NULL;
+
+ if (!m_is_animated) {
+ time_sampling = 0;
+ }
+
+ m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
+ m_schema = m_xform.getSchema();
+}
+
+void AbcTransformWriter::do_write()
+{
+ if (m_first_frame) {
+ m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(m_xform, m_xform.getSchema().getTimeSampling());
+ }
+
+ m_visibility.set(!(m_object->restrictflag & OB_RESTRICT_VIEW));
+
+ if (!m_first_frame && !m_is_animated) {
+ return;
+ }
+
+ float mat[4][4];
+ create_transform_matrix(m_object, mat);
+
+ /* Only apply rotation to root camera, parenting will propagate it. */
+ if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) {
+ float rot_mat[4][4];
+ unit_m4(rot_mat);
+ rotate_m4(rot_mat, 'X', -M_PI_2);
+ mul_m4_m4m4(mat, mat, rot_mat);
+ }
+
+ if (!m_object->parent) {
+ /* Only apply scaling to root objects, parenting will propagate it. */
+ float scale_mat[4][4];
+ scale_m4_fl(scale_mat, m_settings.global_scale);
+ mul_m4_m4m4(mat, mat, scale_mat);
+ mul_v3_fl(mat[3], m_settings.global_scale);
+ }
+
+ m_matrix = convert_matrix(mat);
+
+ m_sample.setMatrix(m_matrix);
+ m_schema.set(m_sample);
+}
+
+Imath::Box3d AbcTransformWriter::bounds()
+{
+ Imath::Box3d bounds;
+
+ for (int i = 0; i < m_children.size(); ++i) {
+ Imath::Box3d box(m_children[i]->bounds());
+ bounds.extendBy(box);
+ }
+
+ return Imath::transform(bounds, m_matrix);
+}
+
+bool AbcTransformWriter::hasAnimation(Object */*ob*/) const
+{
+ /* TODO(kevin): implement this. */
+ return true;
+}
+
+/* ************************************************************************** */
+
+AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+ Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting);
+ m_schema = xform.getSchema();
+
+ get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcEmptyReader::valid() const
+{
+ return m_schema.valid();
+}
+
+void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/)
+{
+ m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str());
+ m_object->data = NULL;
+}
diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h
new file mode 100644
index 00000000000..6a3aae216f2
--- /dev/null
+++ b/source/blender/alembic/intern/abc_transform.h
@@ -0,0 +1,73 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_TRANSFORM_H__
+#define __ABC_TRANSFORM_H__
+
+#include "abc_object.h"
+
+#include <Alembic/AbcGeom/All.h>
+
+/* ************************************************************************** */
+
+class AbcTransformWriter : public AbcObjectWriter {
+ Alembic::AbcGeom::OXform m_xform;
+ Alembic::AbcGeom::OXformSchema m_schema;
+ Alembic::AbcGeom::XformSample m_sample;
+ Alembic::AbcGeom::OVisibilityProperty m_visibility;
+ Alembic::Abc::M44d m_matrix;
+
+ bool m_is_animated;
+ Object *m_parent;
+ bool m_visible;
+
+public:
+ AbcTransformWriter(Object *ob,
+ const Alembic::AbcGeom::OObject &abc_parent,
+ AbcTransformWriter *parent,
+ unsigned int time_sampling,
+ ExportSettings &settings);
+
+ Alembic::AbcGeom::OXform &alembicXform() { return m_xform;}
+ virtual Imath::Box3d bounds();
+ void setParent(Object *p) { m_parent = p; }
+
+private:
+ virtual void do_write();
+
+ bool hasAnimation(Object *ob) const;
+};
+
+/* ************************************************************************** */
+
+class AbcEmptyReader : public AbcObjectReader {
+ Alembic::AbcGeom::IXformSchema m_schema;
+
+public:
+ AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const;
+
+ void readObjectData(Main *bmain, float time);
+};
+
+#endif /* __ABC_TRANSFORM_H__ */
diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc
new file mode 100644
index 00000000000..fbab0bcdf34
--- /dev/null
+++ b/source/blender/alembic/intern/abc_util.cc
@@ -0,0 +1,437 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_util.h"
+
+#include <algorithm>
+
+extern "C" {
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+}
+
+std::string get_id_name(Object *ob)
+{
+ if (!ob) {
+ return "";
+ }
+
+ return get_id_name(&ob->id);
+}
+
+std::string get_id_name(ID *id)
+{
+ std::string name(id->name + 2);
+ std::replace(name.begin(), name.end(), ' ', '_');
+ std::replace(name.begin(), name.end(), '.', '_');
+ std::replace(name.begin(), name.end(), ':', '_');
+
+ return name;
+}
+
+std::string get_object_dag_path_name(Object *ob, Object *dupli_parent)
+{
+ std::string name = get_id_name(ob);
+
+ Object *p = ob->parent;
+
+ while (p) {
+ name = get_id_name(p) + "/" + name;
+ p = p->parent;
+ }
+
+ if (dupli_parent && (ob != dupli_parent)) {
+ name = get_id_name(dupli_parent) + "/" + name;
+ }
+
+ return name;
+}
+
+bool object_selected(Object *ob)
+{
+ return ob->flag & SELECT;
+}
+
+bool parent_selected(Object *ob)
+{
+ if (object_selected(ob)) {
+ return true;
+ }
+
+ bool do_export = false;
+
+ Object *parent = ob->parent;
+
+ while (parent != NULL) {
+ if (object_selected(parent)) {
+ do_export = true;
+ break;
+ }
+
+ parent = parent->parent;
+ }
+
+ return do_export;
+}
+
+Imath::M44d convert_matrix(float mat[4][4])
+{
+ Imath::M44d m;
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ m[i][j] = mat[i][j];
+ }
+ }
+
+ return m;
+}
+
+void split(const std::string &s, const char delim, std::vector<std::string> &tokens)
+{
+ tokens.clear();
+
+ std::stringstream ss(s);
+ std::string item;
+
+ while (std::getline(ss, item, delim)) {
+ if (!item.empty()) {
+ tokens.push_back(item);
+ }
+ }
+}
+
+/* Create a rotation matrix for each axis from euler angles.
+ * Euler angles are swaped to change coordinate system. */
+static void create_rotation_matrix(
+ float rot_x_mat[3][3], float rot_y_mat[3][3],
+ float rot_z_mat[3][3], const float euler[3], const bool to_yup)
+{
+ const float rx = euler[0];
+ const float ry = (to_yup) ? euler[2] : -euler[2];
+ const float rz = (to_yup) ? -euler[1] : euler[1];
+
+ unit_m3(rot_x_mat);
+ unit_m3(rot_y_mat);
+ unit_m3(rot_z_mat);
+
+ rot_x_mat[1][1] = cos(rx);
+ rot_x_mat[2][1] = -sin(rx);
+ rot_x_mat[1][2] = sin(rx);
+ rot_x_mat[2][2] = cos(rx);
+
+ rot_y_mat[2][2] = cos(ry);
+ rot_y_mat[0][2] = -sin(ry);
+ rot_y_mat[2][0] = sin(ry);
+ rot_y_mat[0][0] = cos(ry);
+
+ rot_z_mat[0][0] = cos(rz);
+ rot_z_mat[1][0] = -sin(rz);
+ rot_z_mat[0][1] = sin(rz);
+ rot_z_mat[1][1] = cos(rz);
+}
+
+/* Recompute transform matrix of object in new coordinate system
+ * (from Y-Up to Z-Up). */
+void create_transform_matrix(float r_mat[4][4])
+{
+ float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], transform_mat[4][4];
+ float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
+ float loc[3], scale[3], euler[3];
+
+ zero_v3(loc);
+ zero_v3(scale);
+ zero_v3(euler);
+ unit_m3(rot);
+ unit_m3(rot_mat);
+ unit_m4(scale_mat);
+ unit_m4(transform_mat);
+ unit_m4(invmat);
+
+ /* Compute rotation matrix. */
+
+ /* Extract location, rotation, and scale from matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, r_mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, false);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ /* Add rotation matrix to transformation matrix. */
+ copy_m4_m3(transform_mat, rot_mat);
+
+ /* Add translation to transformation matrix. */
+ copy_yup_zup(transform_mat[3], loc);
+
+ /* Create scale matrix. */
+ scale_mat[0][0] = scale[0];
+ scale_mat[1][1] = scale[2];
+ scale_mat[2][2] = scale[1];
+
+ /* Add scale to transformation matrix. */
+ mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
+
+ copy_m4_m4(r_mat, transform_mat);
+}
+
+void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel,
+ const Alembic::AbcGeom::IXform &ixform, Object *ob,
+ float r_mat[4][4], float scale, bool has_alembic_parent)
+{
+
+ const Alembic::AbcGeom::IXformSchema &ixform_schema = ixform.getSchema();
+ Alembic::AbcGeom::XformSample xs;
+ ixform_schema.get(xs, sample_sel);
+ const Imath::M44d &xform = xs.getMatrix();
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ r_mat[i][j] = xform[i][j];
+ }
+ }
+
+ if (ob->type == OB_CAMERA) {
+ float cam_to_yup[4][4];
+ unit_m4(cam_to_yup);
+ rotate_m4(cam_to_yup, 'X', M_PI_2);
+ mul_m4_m4m4(r_mat, r_mat, cam_to_yup);
+ }
+
+ create_transform_matrix(r_mat);
+
+ if (ob->parent) {
+ mul_m4_m4m4(r_mat, ob->parent->obmat, r_mat);
+ }
+ /* TODO(kevin) */
+ else if (!has_alembic_parent) {
+ /* Only apply scaling to root objects, parenting will propagate it. */
+ float scale_mat[4][4];
+ scale_m4_fl(scale_mat, scale);
+ mul_m4_m4m4(r_mat, r_mat, scale_mat);
+ mul_v3_fl(r_mat[3], scale);
+ }
+}
+
+/* Recompute transform matrix of object in new coordinate system (from Z-Up to Y-Up). */
+void create_transform_matrix(Object *obj, float transform_mat[4][4])
+{
+ float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], mat[4][4];
+ float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
+ float loc[3], scale[3], euler[3];
+
+ zero_v3(loc);
+ zero_v3(scale);
+ zero_v3(euler);
+ unit_m3(rot);
+ unit_m3(rot_mat);
+ unit_m4(scale_mat);
+ unit_m4(transform_mat);
+ unit_m4(invmat);
+ unit_m4(mat);
+
+ /* get local matrix. */
+ if (obj->parent) {
+ invert_m4_m4(invmat, obj->parent->obmat);
+ mul_m4_m4m4(mat, invmat, obj->obmat);
+ }
+ else {
+ copy_m4_m4(mat, obj->obmat);
+ }
+
+ /* Compute rotation matrix. */
+ switch (obj->rotmode) {
+ case ROT_MODE_AXISANGLE:
+ {
+ /* Get euler angles from axis angle rotation. */
+ axis_angle_to_eulO(euler, ROT_MODE_XYZ, obj->rotAxis, obj->rotAngle);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ /* Extract location and scale from matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ break;
+ }
+ case ROT_MODE_QUAT:
+ {
+ float q[4];
+ copy_v4_v4(q, obj->quat);
+
+ /* Swap axis. */
+ q[2] = obj->quat[3];
+ q[3] = -obj->quat[2];
+
+ /* Compute rotation matrix from quaternion. */
+ quat_to_mat3(rot_mat, q);
+
+ /* Extract location and scale from matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ break;
+ }
+ case ROT_MODE_XYZ:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ break;
+ }
+ case ROT_MODE_XZY:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_XZY, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+ break;
+ }
+ case ROT_MODE_YXZ:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_YXZ, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+
+ break;
+ }
+ case ROT_MODE_YZX:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_YZX, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+
+ break;
+ }
+ case ROT_MODE_ZXY:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_ZXY, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+
+ break;
+ }
+ case ROT_MODE_ZYX:
+ {
+ /* Extract location, rotation, and scale form matrix. */
+ mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+ /* Get euler angles from rotation matrix. */
+ mat3_to_eulO(euler, ROT_MODE_ZYX, rot);
+
+ /* Create X, Y, Z rotation matrices from euler angles. */
+ create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+ /* Concatenate rotation matrices. */
+ mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+ mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+
+ break;
+ }
+ }
+
+ /* Add rotation matrix to transformation matrix. */
+ copy_m4_m3(transform_mat, rot_mat);
+
+ /* Add translation to transformation matrix. */
+ copy_zup_yup(transform_mat[3], loc);
+
+ /* Create scale matrix. */
+ scale_mat[0][0] = scale[0];
+ scale_mat[1][1] = scale[2];
+ scale_mat[2][2] = scale[1];
+
+ /* Add scale to transformation matrix. */
+ mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
+}
+
+bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
+{
+ if (!prop.valid()) {
+ return false;
+ }
+
+ return prop.getPropertyHeader(name) != NULL;
+}
diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h
new file mode 100644
index 00000000000..688a25d85f6
--- /dev/null
+++ b/source/blender/alembic/intern/abc_util.h
@@ -0,0 +1,125 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_UTIL_H__
+#define __ABC_UTIL_H__
+
+#include <Alembic/Abc/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#ifdef _MSC_VER
+# define ABC_INLINE static __forceinline
+#else
+# define ABC_INLINE static inline
+#endif
+
+using Alembic::Abc::chrono_t;
+
+class ImportSettings;
+
+struct ID;
+struct Object;
+
+std::string get_id_name(ID *id);
+std::string get_id_name(Object *ob);
+std::string get_object_dag_path_name(Object *ob, Object *dupli_parent);
+
+bool object_selected(Object *ob);
+bool parent_selected(Object *ob);
+
+Imath::M44d convert_matrix(float mat[4][4]);
+void create_transform_matrix(float r_mat[4][4]);
+void create_transform_matrix(Object *obj, float transform_mat[4][4]);
+
+void split(const std::string &s, const char delim, std::vector<std::string> &tokens);
+
+template<class TContainer>
+bool begins_with(const TContainer &input, const TContainer &match)
+{
+ return input.size() >= match.size()
+ && std::equal(match.begin(), match.end(), input.begin());
+}
+
+void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel,
+ const Alembic::AbcGeom::IXform &ixform, Object *ob,
+ float r_mat[4][4], float scale, bool has_alembic_parent = false);
+
+template <typename Schema>
+void get_min_max_time(const Schema &schema, chrono_t &min, chrono_t &max)
+{
+ const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling();
+
+ if (!schema.isConstant()) {
+ const size_t num_samps = schema.getNumSamples();
+
+ if (num_samps > 0) {
+ const chrono_t min_time = time_samp->getSampleTime(0);
+ min = std::min(min, min_time);
+
+ const chrono_t max_time = time_samp->getSampleTime(num_samps - 1);
+ max = std::max(max, max_time);
+ }
+ }
+}
+
+bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name);
+
+/* ************************** */
+
+/* TODO(kevin): for now keeping these transformations hardcoded to make sure
+ * everything works properly, and also because Alembic is almost exclusively
+ * used in Y-up software, but eventually they'll be set by the user in the UI
+ * like other importers/exporters do, to support other axis. */
+
+/* Copy from Y-up to Z-up. */
+
+ABC_INLINE void copy_yup_zup(float zup[3], const float yup[3])
+{
+ zup[0] = yup[0];
+ zup[1] = -yup[2];
+ zup[2] = yup[1];
+}
+
+ABC_INLINE void copy_yup_zup(short zup[3], const short yup[3])
+{
+ zup[0] = yup[0];
+ zup[1] = -yup[2];
+ zup[2] = yup[1];
+}
+
+/* Copy from Z-up to Y-up. */
+
+ABC_INLINE void copy_zup_yup(float yup[3], const float zup[3])
+{
+ yup[0] = zup[0];
+ yup[1] = zup[2];
+ yup[2] = -zup[1];
+}
+
+ABC_INLINE void copy_zup_yup(short yup[3], const short zup[3])
+{
+ yup[0] = zup[0];
+ yup[1] = zup[2];
+ yup[2] = -zup[1];
+}
+
+#endif /* __ABC_UTIL_H__ */
diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc
new file mode 100644
index 00000000000..0e96ac22e11
--- /dev/null
+++ b/source/blender/alembic/intern/alembic_capi.cc
@@ -0,0 +1,1136 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../ABC_alembic.h"
+
+#ifdef WITH_ALEMBIC_HDF5
+# include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+#include <Alembic/AbcMaterial/IMaterial.h>
+
+#include "abc_camera.h"
+#include "abc_curves.h"
+#include "abc_hair.h"
+#include "abc_mesh.h"
+#include "abc_nurbs.h"
+#include "abc_points.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_cachefile.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+/* SpaceType struct has a member called 'new' which obviously conflicts with C++
+ * so temporarily redefining the new keyword to make it compile. */
+#define new extern_new
+#include "BKE_screen.h"
+#undef new
+
+#include "BLI_fileops.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+}
+
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::ObjectHeader;
+
+using Alembic::AbcGeom::ErrorHandler;
+using Alembic::AbcGeom::Exception;
+using Alembic::AbcGeom::MetaData;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::kWrapExisting;
+
+using Alembic::AbcGeom::IArchive;
+using Alembic::AbcGeom::ICamera;
+using Alembic::AbcGeom::ICurves;
+using Alembic::AbcGeom::ICurvesSchema;
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::ILight;
+using Alembic::AbcGeom::INuPatch;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IPoints;
+using Alembic::AbcGeom::IPointsSchema;
+using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::ISubD;
+using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::IXformSchema;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::XformSample;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IN3fArrayProperty;
+using Alembic::AbcGeom::IN3fGeomParam;
+using Alembic::AbcGeom::V3fArraySamplePtr;
+
+using Alembic::AbcMaterial::IMaterial;
+
+struct AbcArchiveHandle {
+ int unused;
+};
+
+ABC_INLINE IArchive *archive_from_handle(AbcArchiveHandle *handle)
+{
+ return reinterpret_cast<IArchive *>(handle);
+}
+
+ABC_INLINE AbcArchiveHandle *handle_from_archive(IArchive *archive)
+{
+ return reinterpret_cast<AbcArchiveHandle *>(archive);
+}
+
+static IArchive *open_archive(const std::string &filename)
+{
+ Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
+ IArchive *archive;
+
+ try {
+ archive = new IArchive(Alembic::AbcCoreOgawa::ReadArchive(),
+ filename.c_str(), ErrorHandler::kThrowPolicy,
+ cache_ptr);
+ }
+ catch (const Exception &e) {
+ std::cerr << e.what() << '\n';
+
+#ifdef WITH_ALEMBIC_HDF5
+ try {
+ archive = new IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
+ filename.c_str(), ErrorHandler::kThrowPolicy,
+ cache_ptr);
+ }
+ catch (const Exception &) {
+ std::cerr << e.what() << '\n';
+ return NULL;
+ }
+#else
+ return NULL;
+#endif
+ }
+
+ return archive;
+}
+
+//#define USE_NURBS
+
+/* NOTE: this function is similar to visit_objects below, need to keep them in
+ * sync. */
+static void gather_objects_paths(const IObject &object, ListBase *object_paths)
+{
+ if (!object.valid()) {
+ return;
+ }
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ bool get_path = false;
+
+ const MetaData &md = child.getMetaData();
+
+ if (IXform::matches(md)) {
+ /* Check whether or not this object is a Maya locator, which is
+ * similar to empties used as parent object in Blender. */
+ if (has_property(child.getProperties(), "locator")) {
+ get_path = true;
+ }
+ else {
+ /* Avoid creating an empty object if the child of this transform
+ * is not a transform (that is an empty). */
+ if (child.getNumChildren() == 1) {
+ if (IXform::matches(child.getChild(0).getMetaData())) {
+ get_path = true;
+ }
+#if 0
+ else {
+ std::cerr << "Skipping " << child.getFullName() << '\n';
+ }
+#endif
+ }
+ else {
+ get_path = true;
+ }
+ }
+ }
+ else if (IPolyMesh::matches(md)) {
+ get_path = true;
+ }
+ else if (ISubD::matches(md)) {
+ get_path = true;
+ }
+ else if (INuPatch::matches(md)) {
+#ifdef USE_NURBS
+ get_path = true;
+#endif
+ }
+ else if (ICamera::matches(md)) {
+ get_path = true;
+ }
+ else if (IPoints::matches(md)) {
+ get_path = true;
+ }
+ else if (IMaterial::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (ILight::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (IFaceSet::matches(md)) {
+ /* Pass, those are handled in the mesh reader. */
+ }
+ else if (ICurves::matches(md)) {
+ get_path = true;
+ }
+ else {
+ assert(false);
+ }
+
+ if (get_path) {
+ AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
+ MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+
+ BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
+
+ BLI_addtail(object_paths, abc_path);
+ }
+
+ gather_objects_paths(child, object_paths);
+ }
+}
+
+AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
+{
+ IArchive *archive = open_archive(filename);
+
+ if (!archive) {
+ return NULL;
+ }
+
+ if (object_paths) {
+ gather_objects_paths(archive->getTop(), object_paths);
+ }
+
+ return handle_from_archive(archive);
+}
+
+void ABC_free_handle(AbcArchiveHandle *handle)
+{
+ delete archive_from_handle(handle);
+}
+
+int ABC_get_version()
+{
+ return ALEMBIC_LIBRARY_VERSION;
+}
+
+static void find_iobject(const IObject &object, IObject &ret,
+ const std::string &path)
+{
+ if (!object.valid()) {
+ return;
+ }
+
+ std::vector<std::string> tokens;
+ split(path, '/', tokens);
+
+ IObject tmp = object;
+
+ std::vector<std::string>::iterator iter;
+ for (iter = tokens.begin(); iter != tokens.end(); ++iter) {
+ IObject child = tmp.getChild(*iter);
+ tmp = child;
+ }
+
+ ret = tmp;
+}
+
+struct ExportJobData {
+ Scene *scene;
+ Main *bmain;
+
+ char filename[1024];
+ ExportSettings settings;
+
+ short *stop;
+ short *do_update;
+ float *progress;
+
+ bool was_canceled;
+};
+
+static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+
+ /* XXX annoying hack: needed to prevent data corruption when changing
+ * scene frame in separate threads
+ */
+ G.is_rendering = true;
+ BKE_spacedata_draw_locks(true);
+
+ G.is_break = false;
+
+ try {
+ Scene *scene = data->scene;
+ AbcExporter exporter(scene, data->filename, data->settings);
+
+ const int orig_frame = CFRA;
+
+ data->was_canceled = false;
+ exporter(data->bmain, *data->progress, data->was_canceled);
+
+ if (CFRA != orig_frame) {
+ CFRA = orig_frame;
+
+ BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain,
+ scene, scene->lay);
+ }
+ }
+ catch (const std::exception &e) {
+ std::cerr << "Abc Export error: " << e.what() << '\n';
+ }
+ catch (...) {
+ std::cerr << "Abc Export error\n";
+ }
+}
+
+static void export_endjob(void *customdata)
+{
+ ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+ if (data->was_canceled && BLI_exists(data->filename)) {
+ BLI_delete(data->filename, false, false);
+ }
+
+ G.is_rendering = false;
+ BKE_spacedata_draw_locks(false);
+}
+
+void ABC_export(
+ Scene *scene,
+ bContext *C,
+ const char *filepath,
+ const struct AlembicExportParams *params)
+{
+ ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
+ job->scene = scene;
+ job->bmain = CTX_data_main(C);
+ BLI_strncpy(job->filename, filepath, 1024);
+
+ job->settings.scene = job->scene;
+ job->settings.frame_start = params->frame_start;
+ job->settings.frame_end = params->frame_end;
+ job->settings.frame_step_xform = params->frame_step_xform;
+ job->settings.frame_step_shape = params->frame_step_shape;
+ job->settings.shutter_open = params->shutter_open;
+ job->settings.shutter_close = params->shutter_close;
+ job->settings.selected_only = params->selected_only;
+ job->settings.export_face_sets = params->face_sets;
+ job->settings.export_normals = params->normals;
+ job->settings.export_uvs = params->uvs;
+ job->settings.export_vcols = params->vcolors;
+ job->settings.apply_subdiv = params->apply_subdiv;
+ job->settings.flatten_hierarchy = params->flatten_hierarchy;
+ job->settings.visible_layers_only = params->visible_layers_only;
+ job->settings.renderable_only = params->renderable_only;
+ job->settings.use_subdiv_schema = params->use_subdiv_schema;
+ job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA);
+ job->settings.pack_uv = params->packuv;
+ job->settings.global_scale = params->global_scale;
+
+ if (job->settings.frame_start > job->settings.frame_end) {
+ std::swap(job->settings.frame_start, job->settings.frame_end);
+ }
+
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ job->scene,
+ "Alembic Export",
+ WM_JOB_PROGRESS,
+ WM_JOB_TYPE_ALEMBIC);
+
+ /* setup job */
+ WM_jobs_customdata_set(wm_job, job, MEM_freeN);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+ WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+}
+
+/* ********************** Import file ********************** */
+
+static void visit_object(const IObject &object,
+ std::vector<AbcObjectReader *> &readers,
+ GHash *parent_map,
+ ImportSettings &settings)
+{
+ if (!object.valid()) {
+ return;
+ }
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ AbcObjectReader *reader = NULL;
+
+ const MetaData &md = child.getMetaData();
+
+ if (IXform::matches(md)) {
+ bool create_xform = false;
+
+ /* Check whether or not this object is a Maya locator, which is
+ * similar to empties used as parent object in Blender. */
+ if (has_property(child.getProperties(), "locator")) {
+ create_xform = true;
+ }
+ else {
+ /* Avoid creating an empty object if the child of this transform
+ * is not a transform (that is an empty). */
+ if (child.getNumChildren() == 1) {
+ if (IXform::matches(child.getChild(0).getMetaData())) {
+ create_xform = true;
+ }
+#if 0
+ else {
+ std::cerr << "Skipping " << child.getFullName() << '\n';
+ }
+#endif
+ }
+ else {
+ create_xform = true;
+ }
+ }
+
+ if (create_xform) {
+ reader = new AbcEmptyReader(child, settings);
+ }
+ }
+ else if (IPolyMesh::matches(md)) {
+ reader = new AbcMeshReader(child, settings);
+ }
+ else if (ISubD::matches(md)) {
+ reader = new AbcSubDReader(child, settings);
+ }
+ else if (INuPatch::matches(md)) {
+#ifdef USE_NURBS
+ /* TODO(kevin): importing cyclic NURBS from other software crashes
+ * at the moment. This is due to the fact that NURBS in other
+ * software have duplicated points which causes buffer overflows in
+ * Blender. Need to figure out exactly how these points are
+ * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
+ * Until this is fixed, disabling NURBS reading. */
+ reader = new AbcNurbsReader(child, settings);
+#endif
+ }
+ else if (ICamera::matches(md)) {
+ reader = new AbcCameraReader(child, settings);
+ }
+ else if (IPoints::matches(md)) {
+ reader = new AbcPointsReader(child, settings);
+ }
+ else if (IMaterial::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (ILight::matches(md)) {
+ /* Pass for now. */
+ }
+ else if (IFaceSet::matches(md)) {
+ /* Pass, those are handled in the mesh reader. */
+ }
+ else if (ICurves::matches(md)) {
+ reader = new AbcCurveReader(child, settings);
+ }
+ else {
+ assert(false);
+ }
+
+ if (reader) {
+ readers.push_back(reader);
+
+ AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
+ MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+
+ BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
+
+ BLI_addtail(&settings.cache_file->object_paths, abc_path);
+
+ /* Cast to `void *` explicitly to avoid compiler errors because it
+ * is a `const char *` which the compiler cast to `const void *`
+ * instead of the expected `void *`. */
+ BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader);
+ }
+
+ visit_object(child, readers, parent_map, settings);
+ }
+}
+
+enum {
+ ABC_NO_ERROR = 0,
+ ABC_ARCHIVE_FAIL,
+};
+
+struct ImportJobData {
+ Main *bmain;
+ Scene *scene;
+
+ char filename[1024];
+ ImportSettings settings;
+
+ GHash *parent_map;
+ std::vector<AbcObjectReader *> readers;
+
+ short *stop;
+ short *do_update;
+ float *progress;
+
+ char error_code;
+ bool was_cancelled;
+};
+
+ABC_INLINE bool is_mesh_and_strands(const IObject &object)
+{
+ if (object.getNumChildren() != 2) {
+ return false;
+ }
+
+ bool has_mesh = false;
+ bool has_curve = false;
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ const IObject &child = object.getChild(i);
+
+ if (!child.valid()) {
+ continue;
+ }
+
+ const MetaData &md = child.getMetaData();
+
+ if (IPolyMesh::matches(md)) {
+ has_mesh = true;
+ }
+ else if (ISubD::matches(md)) {
+ has_mesh = true;
+ }
+ else if (ICurves::matches(md)) {
+ has_curve = true;
+ }
+ }
+
+ return has_mesh && has_curve;
+}
+
+static void import_startjob(void *user_data, short *stop, short *do_update, float *progress)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+
+ IArchive *archive = open_archive(data->filename);
+
+ if (!archive || !archive->valid()) {
+ data->error_code = ABC_ARCHIVE_FAIL;
+ return;
+ }
+
+ CacheFile *cache_file = static_cast<CacheFile *>(BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename)));
+
+ /* Decrement the ID ref-count because it is going to be incremented for each
+ * modifier and constraint that it will be attached to, so since currently
+ * it is not used by anyone, its use count will off by one. */
+ id_us_min(&cache_file->id);
+
+ cache_file->is_sequence = data->settings.is_sequence;
+ cache_file->scale = data->settings.scale;
+ cache_file->handle = handle_from_archive(archive);
+ BLI_strncpy(cache_file->filepath, data->filename, 1024);
+
+ data->settings.cache_file = cache_file;
+
+ *data->do_update = true;
+ *data->progress = 0.05f;
+
+ data->parent_map = BLI_ghash_str_new("alembic parent ghash");
+
+ /* Parse Alembic Archive. */
+
+ visit_object(archive->getTop(), data->readers, data->parent_map, data->settings);
+
+ if (G.is_break) {
+ data->was_cancelled = true;
+ return;
+ }
+
+ *data->do_update = true;
+ *data->progress = 0.1f;
+
+ /* Create objects and set scene frame range. */
+
+ const float size = static_cast<float>(data->readers.size());
+ size_t i = 0;
+
+ chrono_t min_time = std::numeric_limits<chrono_t>::max();
+ chrono_t max_time = std::numeric_limits<chrono_t>::min();
+
+ std::vector<AbcObjectReader *>::iterator iter;
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ AbcObjectReader *reader = *iter;
+
+ if (reader->valid()) {
+ reader->readObjectData(data->bmain, 0.0f);
+ reader->readObjectMatrix(0.0f);
+
+ min_time = std::min(min_time, reader->minTime());
+ max_time = std::max(max_time, reader->maxTime());
+ }
+
+ *data->progress = 0.1f + 0.6f * (++i / size);
+ *data->do_update = true;
+
+ if (G.is_break) {
+ data->was_cancelled = true;
+ return;
+ }
+ }
+
+ if (data->settings.set_frame_range) {
+ Scene *scene = data->scene;
+
+ if (data->settings.is_sequence) {
+ SFRA = data->settings.offset;
+ EFRA = SFRA + (data->settings.sequence_len - 1);
+ CFRA = SFRA;
+ }
+ else if (min_time < max_time) {
+ SFRA = min_time * FPS;
+ EFRA = max_time * FPS;
+ CFRA = SFRA;
+ }
+ }
+
+ /* Setup parentship. */
+
+ i = 0;
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ const AbcObjectReader *reader = *iter;
+ const AbcObjectReader *parent_reader = NULL;
+ const IObject &iobject = reader->iobject();
+
+ IObject parent = iobject.getParent();
+
+ if (!IXform::matches(iobject.getHeader())) {
+ /* In the case of an non XForm node, the parent is the transform
+ * matrix of the data itself, so we get the its grand parent.
+ */
+
+ /* Special case with object only containing a mesh and some strands,
+ * we want both objects to be parented to the same object. */
+ if (!is_mesh_and_strands(parent)) {
+ parent = parent.getParent();
+ }
+ }
+
+ parent_reader = reinterpret_cast<AbcObjectReader *>(
+ BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str()));
+
+ if (parent_reader) {
+ Object *parent = parent_reader->object();
+
+ if (parent != NULL && reader->object() != parent) {
+ Object *ob = reader->object();
+ ob->parent = parent;
+ }
+ }
+
+ *data->progress = 0.7f + 0.3f * (++i / size);
+ *data->do_update = true;
+
+ if (G.is_break) {
+ data->was_cancelled = true;
+ return;
+ }
+ }
+}
+
+static void import_endjob(void *user_data)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+ std::vector<AbcObjectReader *>::iterator iter;
+
+ /* Delete objects on cancelation. */
+ if (data->was_cancelled) {
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ Object *ob = (*iter)->object();
+
+ if (ob->data) {
+ BKE_libblock_free_us(data->bmain, ob->data);
+ ob->data = NULL;
+ }
+
+ BKE_libblock_free_us(data->bmain, ob);
+ }
+ }
+ else {
+ /* Add object to scene. */
+ BKE_scene_base_deselect_all(data->scene);
+
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ Object *ob = (*iter)->object();
+ ob->lay = data->scene->lay;
+
+ BKE_scene_base_add(data->scene, ob);
+
+ DAG_id_tag_update_ex(data->bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
+ }
+
+ DAG_relations_tag_update(data->bmain);
+ }
+
+ for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+ delete *iter;
+ }
+
+ if (data->parent_map) {
+ BLI_ghash_free(data->parent_map, NULL, NULL);
+ }
+
+ switch (data->error_code) {
+ default:
+ case ABC_NO_ERROR:
+ break;
+ case ABC_ARCHIVE_FAIL:
+ WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
+ break;
+ }
+
+ WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
+}
+
+static void import_freejob(void *user_data)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+ delete data;
+}
+
+void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes)
+{
+ /* Using new here since MEM_* funcs do not call ctor to properly initialize
+ * data. */
+ ImportJobData *job = new ImportJobData();
+ job->bmain = CTX_data_main(C);
+ job->scene = CTX_data_scene(C);
+ BLI_strncpy(job->filename, filepath, 1024);
+
+ job->settings.scale = scale;
+ job->settings.is_sequence = is_sequence;
+ job->settings.set_frame_range = set_frame_range;
+ job->settings.sequence_len = sequence_len;
+ job->settings.offset = offset;
+ job->settings.validate_meshes = validate_meshes;
+ job->parent_map = NULL;
+ job->error_code = ABC_NO_ERROR;
+ job->was_cancelled = false;
+
+ G.is_break = false;
+
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ job->scene,
+ "Alembic Import",
+ WM_JOB_PROGRESS,
+ WM_JOB_TYPE_ALEMBIC);
+
+ /* setup job */
+ WM_jobs_customdata_set(wm_job, job, import_freejob);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+ WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+}
+
+/* ******************************* */
+
+void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale)
+{
+ IArchive *archive = archive_from_handle(handle);
+
+ if (!archive || !archive->valid()) {
+ return;
+ }
+
+ IObject tmp;
+ find_iobject(archive->getTop(), tmp, object_path);
+
+ IXform ixform;
+
+ if (IXform::matches(tmp.getHeader())) {
+ ixform = IXform(tmp, kWrapExisting);
+ }
+ else {
+ ixform = IXform(tmp.getParent(), kWrapExisting);
+ }
+
+ IXformSchema schema = ixform.getSchema();
+
+ if (!schema.valid()) {
+ return;
+ }
+
+ ISampleSelector sample_sel(time);
+
+ create_input_transform(sample_sel, ixform, ob, r_mat, scale);
+}
+
+/* ***************************************** */
+
+static bool check_smooth_poly_flag(DerivedMesh *dm)
+{
+ MPoly *mpolys = dm->getPolyArray(dm);
+
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
+ MPoly &poly = mpolys[i];
+
+ if ((poly.flag & ME_SMOOTH) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void set_smooth_poly_flag(DerivedMesh *dm)
+{
+ MPoly *mpolys = dm->getPolyArray(dm);
+
+ for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
+ MPoly &poly = mpolys[i];
+ poly.flag |= ME_SMOOTH;
+ }
+}
+
+static void *add_customdata_cb(void *user_data, const char *name, int data_type)
+{
+ DerivedMesh *dm = static_cast<DerivedMesh *>(user_data);
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+ void *cd_ptr = NULL;
+
+ if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
+ cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name);
+
+ if (cd_ptr == NULL) {
+ cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm),
+ cd_data_type,
+ CD_DEFAULT,
+ NULL,
+ dm->getNumLoops(dm),
+ name);
+ }
+ }
+
+ return cd_ptr;
+}
+
+ABC_INLINE CDStreamConfig get_config(DerivedMesh *dm)
+{
+ CDStreamConfig config;
+
+ config.user_data = dm;
+ config.mvert = dm->getVertArray(dm);
+ config.mloop = dm->getLoopArray(dm);
+ config.mpoly = dm->getPolyArray(dm);
+ config.totloop = dm->getNumLoops(dm);
+ config.totpoly = dm->getNumPolys(dm);
+ config.loopdata = dm->getLoopDataLayout(dm);
+ config.add_customdata_cb = add_customdata_cb;
+
+ return config;
+}
+
+static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag)
+{
+ IPolyMesh mesh(iobject, kWrapExisting);
+ IPolyMeshSchema schema = mesh.getSchema();
+ ISampleSelector sample_sel(time);
+ const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ DerivedMesh *new_dm = NULL;
+
+ /* Only read point data when streaming meshes, unless we need to create new ones. */
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (dm->getNumVerts(dm) != positions->size()) {
+ new_dm = CDDM_from_template(dm,
+ positions->size(),
+ 0,
+ 0,
+ face_indices->size(),
+ face_counts->size());
+
+ settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+ }
+
+ CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+
+ bool has_loop_normals = false;
+ read_mesh_sample(&settings, schema, sample_sel, config, has_loop_normals);
+
+ if (new_dm) {
+ /* Check if we had ME_SMOOTH flag set to restore it. */
+ if (!has_loop_normals && check_smooth_poly_flag(dm)) {
+ set_smooth_poly_flag(new_dm);
+ }
+
+ CDDM_calc_normals(new_dm);
+ CDDM_calc_edges(new_dm);
+
+ return new_dm;
+ }
+
+ return dm;
+}
+
+using Alembic::AbcGeom::ISubDSchema;
+
+static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag)
+{
+ ISubD mesh(iobject, kWrapExisting);
+ ISubDSchema schema = mesh.getSchema();
+ ISampleSelector sample_sel(time);
+ const ISubDSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+ const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+ DerivedMesh *new_dm = NULL;
+
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (dm->getNumVerts(dm) != positions->size()) {
+ new_dm = CDDM_from_template(dm,
+ positions->size(),
+ 0,
+ 0,
+ face_indices->size(),
+ face_counts->size());
+
+ settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+ }
+
+ /* Only read point data when streaming meshes, unless we need to create new ones. */
+ CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+ read_subd_sample(&settings, schema, sample_sel, config);
+
+ if (new_dm) {
+ /* Check if we had ME_SMOOTH flag set to restore it. */
+ if (check_smooth_poly_flag(dm)) {
+ set_smooth_poly_flag(new_dm);
+ }
+
+ CDDM_calc_normals(new_dm);
+ CDDM_calc_edges(new_dm);
+
+ return new_dm;
+ }
+
+ return dm;
+}
+
+static DerivedMesh *read_points_sample(DerivedMesh *dm, const IObject &iobject, const float time)
+{
+ IPoints points(iobject, kWrapExisting);
+ IPointsSchema schema = points.getSchema();
+ ISampleSelector sample_sel(time);
+ const IPointsSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+
+ DerivedMesh *new_dm = NULL;
+
+ if (dm->getNumVerts(dm) != positions->size()) {
+ new_dm = CDDM_new(positions->size(), 0, 0, 0, 0);
+ }
+
+ CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+ read_points_sample(schema, sample_sel, config, time);
+
+ return new_dm ? new_dm : dm;
+}
+
+/* NOTE: Alembic only stores data about control points, but the DerivedMesh
+ * passed from the cache modifier contains the displist, which has more data
+ * than the control points, so to avoid corrupting the displist we modify the
+ * object directly and create a new DerivedMesh from that. Also we might need to
+ * create new or delete existing NURBS in the curve.
+ */
+static DerivedMesh *read_curves_sample(Object *ob, const IObject &iobject, const float time)
+{
+ ICurves points(iobject, kWrapExisting);
+ ICurvesSchema schema = points.getSchema();
+ ISampleSelector sample_sel(time);
+ const ICurvesSchema::Sample sample = schema.getValue(sample_sel);
+
+ const P3fArraySamplePtr &positions = sample.getPositions();
+ const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices();
+
+ int vertex_idx = 0;
+ int curve_idx = 0;
+ Curve *curve = static_cast<Curve *>(ob->data);
+
+ const int curve_count = BLI_listbase_count(&curve->nurb);
+
+ if (curve_count != num_vertices->size()) {
+ BKE_nurbList_free(&curve->nurb);
+ read_curve_sample(curve, schema, time);
+ }
+ else {
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (; nurbs; nurbs = nurbs->next, ++curve_idx) {
+ const int totpoint = (*num_vertices)[curve_idx];
+
+ if (nurbs->bp) {
+ BPoint *point = nurbs->bp;
+
+ for (int i = 0; i < totpoint; ++i, ++point, ++vertex_idx) {
+ const Imath::V3f &pos = (*positions)[vertex_idx];
+ copy_yup_zup(point->vec, pos.getValue());
+ }
+ }
+ else if (nurbs->bezt) {
+ BezTriple *bezier = nurbs->bezt;
+
+ for (int i = 0; i < totpoint; ++i, ++bezier, ++vertex_idx) {
+ const Imath::V3f &pos = (*positions)[vertex_idx];
+ copy_yup_zup(bezier->vec[1], pos.getValue());
+ }
+ }
+ }
+ }
+
+ return CDDM_from_curve(ob);
+}
+
+DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
+ Object *ob,
+ DerivedMesh *dm,
+ const char *object_path,
+ const float time,
+ const char **err_str,
+ int read_flag)
+{
+ IArchive *archive = archive_from_handle(handle);
+
+ if (!archive || !archive->valid()) {
+ *err_str = "Invalid archive!";
+ return NULL;
+ }
+
+ IObject iobject;
+ find_iobject(archive->getTop(), iobject, object_path);
+
+ if (!iobject.valid()) {
+ *err_str = "Invalid object: verify object path";
+ return NULL;
+ }
+
+ const ObjectHeader &header = iobject.getHeader();
+
+ if (IPolyMesh::matches(header)) {
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch: object path points to a mesh!";
+ return NULL;
+ }
+
+ return read_mesh_sample(dm, iobject, time, read_flag);
+ }
+ else if (ISubD::matches(header)) {
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch: object path points to a subdivision mesh!";
+ return NULL;
+ }
+
+ return read_subd_sample(dm, iobject, time, read_flag);
+ }
+ else if (IPoints::matches(header)) {
+ if (ob->type != OB_MESH) {
+ *err_str = "Object type mismatch: object path points to a point cloud (requires a mesh object)!";
+ return NULL;
+ }
+
+ return read_points_sample(dm, iobject, time);
+ }
+ else if (ICurves::matches(header)) {
+ if (ob->type != OB_CURVE) {
+ *err_str = "Object type mismatch: object path points to a curve!";
+ return NULL;
+ }
+
+ return read_curves_sample(ob, iobject, time);
+ }
+
+ *err_str = "Unsupported object type: verify object path"; // or poke developer
+ return NULL;
+}
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index 983f3ce22b8..00ea323f934 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -37,6 +37,7 @@ struct Main;
struct AnimData;
struct KeyingSet;
struct KS_Path;
+struct PathResolvedRNA;
struct bContext;
struct PointerRNA;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 618b36c5851..483fefbd89c 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 277
-#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_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h
new file mode 100644
index 00000000000..51b161f1a06
--- /dev/null
+++ b/source/blender/blenkernel/BKE_cachefile.h
@@ -0,0 +1,67 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_CACHEFILE_H__
+#define __BKE_CACHEFILE_H__
+
+/** \file BKE_cachefile.h
+ * \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct CacheFile;
+struct Main;
+struct Scene;
+
+void *BKE_cachefile_add(struct Main *bmain, const char *name);
+
+void BKE_cachefile_init(struct CacheFile *cache_file);
+
+void BKE_cachefile_free(struct CacheFile *cache_file);
+
+struct CacheFile *BKE_cachefile_copy(struct Main *bmain, struct CacheFile *cache_file);
+
+void BKE_cachefile_make_local(struct Main *bmain, struct CacheFile *cache_file, const bool lib_local);
+
+void BKE_cachefile_reload(const struct Main *bmain, struct CacheFile *cache_file);
+
+void BKE_cachefile_ensure_handle(const struct Main *bmain, struct CacheFile *cache_file);
+
+void BKE_cachefile_update_frame(struct Main *bmain, struct Scene *scene, float ctime, const float fps);
+
+bool BKE_cachefile_filepath_get(
+ const struct Main *bmain, const struct CacheFile *cache_file, float frame,
+ char r_filename[1024]);
+
+float BKE_cachefile_time_offset(struct CacheFile *cache_file, const float time, const float fps);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BKE_CACHEFILE_H__ */
diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index 50fcbd8e139..36330242f18 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -171,6 +171,7 @@ typedef enum {
CLOTH_SIMSETTINGS_FLAG_CCACHE_EDIT = (1 << 12), /* edit cache in editmode */
CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS = (1 << 13), /* don't allow spring compression */
CLOTH_SIMSETTINGS_FLAG_SEW = (1 << 14), /* pull ends of loose edges together */
+ CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH = (1 << 15), /* make simulation respect deformations in the base object */
} CLOTH_SIMSETTINGS_FLAGS;
/* COLLISION FLAGS */
diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h
index e5d348031e9..5b4f5910821 100644
--- a/source/blender/blenkernel/BKE_colortools.h
+++ b/source/blender/blenkernel/BKE_colortools.h
@@ -61,7 +61,7 @@ void curvemap_reset(struct CurveMap *cuma, const struct rctf
void curvemap_remove(struct CurveMap *cuma, const short flag);
bool curvemap_remove_point(struct CurveMap *cuma, struct CurveMapPoint *cmp);
struct CurveMapPoint *curvemap_insert(struct CurveMap *cuma, float x, float y);
-void curvemap_sethandle(struct CurveMap *cuma, int type);
+void curvemap_handle_set(struct CurveMap *cuma, int type);
void curvemapping_changed(struct CurveMapping *cumap, const bool rem_doubles);
void curvemapping_changed_all(struct CurveMapping *cumap);
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 65a68a4387c..4da6a61cbfa 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -39,6 +39,7 @@ extern "C" {
struct ARegion;
struct bScreen;
+struct CacheFile;
struct ListBase;
struct Main;
struct Object;
@@ -58,6 +59,9 @@ struct bPoseChannel;
struct bGPdata;
struct bGPDlayer;
struct bGPDframe;
+struct bGPDpalette;
+struct bGPDpalettecolor;
+struct bGPDbrush;
struct wmWindow;
struct wmWindowManager;
struct SpaceText;
@@ -268,6 +272,8 @@ struct Text *CTX_data_edit_text(const bContext *C);
struct MovieClip *CTX_data_edit_movieclip(const bContext *C);
struct Mask *CTX_data_edit_mask(const bContext *C);
+struct CacheFile *CTX_data_edit_cachefile(const bContext *C);
+
int CTX_data_selected_nodes(const bContext *C, ListBase *list);
struct EditBone *CTX_data_active_bone(const bContext *C);
@@ -283,6 +289,9 @@ int CTX_data_visible_pose_bones(const bContext *C, ListBase *list);
struct bGPdata *CTX_data_gpencil_data(const bContext *C);
struct bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C);
struct bGPDframe *CTX_data_active_gpencil_frame(const bContext *C);
+struct bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C);
+struct bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C);
+struct bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C);
int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index bb4eb652ae2..3a45097efc5 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -48,6 +48,7 @@ struct AnimData;
struct bAction;
struct BezTriple;
struct StructRNA;
+struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
@@ -107,7 +108,7 @@ bool driver_get_variable_property(
struct ChannelDriver *driver, struct DriverTarget *dtar,
struct PointerRNA *r_ptr, struct PropertyRNA **r_prop, int *r_index);
-float evaluate_driver(struct ChannelDriver *driver, const float evaltime);
+float evaluate_driver(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime);
/* ************** F-Curve Modifiers *************** */
@@ -278,8 +279,9 @@ void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]);
/* evaluate fcurve */
float evaluate_fcurve(struct FCurve *fcu, float evaltime);
+float evaluate_fcurve_driver(struct PathResolvedRNA *anim_rna, struct FCurve *fcu, float evaltime);
/* evaluate fcurve and store value */
-float calculate_fcurve(struct FCurve *fcu, float evaltime);
+float calculate_fcurve(struct PathResolvedRNA *anim_rna, struct FCurve *fcu, float evaltime);
/* ************* F-Curve Samples API ******************** */
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index e9e3cd3b16e..ab8b83f8271 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -31,38 +31,53 @@
* \author Joshua Leung
*/
+struct ToolSettings;
struct ListBase;
struct bGPdata;
struct bGPDlayer;
struct bGPDframe;
struct bGPDstroke;
+struct bGPDpalette;
+struct bGPDpalettecolor;
struct Main;
/* ------------ Grease-Pencil API ------------------ */
-bool free_gpencil_strokes(struct bGPDframe *gpf);
-void free_gpencil_frames(struct bGPDlayer *gpl);
-void free_gpencil_layers(struct ListBase *list);
-void BKE_gpencil_free(struct bGPdata *gpd);
+void BKE_gpencil_free_stroke(struct bGPDstroke *gps);
+bool BKE_gpencil_free_strokes(struct bGPDframe *gpf);
+void BKE_gpencil_free_frames(struct bGPDlayer *gpl);
+void BKE_gpencil_free_layers(struct ListBase *list);
+void BKE_gpencil_free_brushes(struct ListBase *list);
+void BKE_gpencil_free_palettes(struct ListBase *list);
+void BKE_gpencil_free(struct bGPdata *gpd, bool free_palettes);
-void gpencil_stroke_sync_selection(struct bGPDstroke *gps);
+void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps);
-struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
-struct bGPDframe *gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
-struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive);
-struct bGPdata *gpencil_data_addnew(const char name[]);
+struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
+struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
+struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive);
+struct bGPdata *BKE_gpencil_data_addnew(const char name[]);
-struct bGPDframe *gpencil_frame_duplicate(struct bGPDframe *src);
-struct bGPDlayer *gpencil_layer_duplicate(struct bGPDlayer *src);
-struct bGPdata *gpencil_data_duplicate(struct Main *bmain, struct bGPdata *gpd, bool internal_copy);
+struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src);
+struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src);
+struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain, struct bGPdata *gpd, bool internal_copy);
void BKE_gpencil_make_local(struct Main *bmain, struct bGPdata *gpd, const bool lib_local);
-void gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf);
+void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf);
+
+struct bGPDpalette *BKE_gpencil_palette_addnew(struct bGPdata *gpd, const char *name, bool setactive);
+struct bGPDpalette *BKE_gpencil_palette_duplicate(const struct bGPDpalette *palette_src);
+struct bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(struct bGPDpalette *palette, const char *name, bool setactive);
+
+struct bGPDbrush *BKE_gpencil_brush_addnew(struct ToolSettings *ts, const char *name, bool setactive);
+struct bGPDbrush *BKE_gpencil_brush_duplicate(const struct bGPDbrush *brush_src);
+void BKE_gpencil_brush_init_presets(struct ToolSettings *ts);
/* Stroke and Fill - Alpha Visibility Threshold */
#define GPENCIL_ALPHA_OPACITY_THRESH 0.001f
+#define GPENCIL_STRENGTH_MIN 0.003f
bool gpencil_layer_is_editable(const struct bGPDlayer *gpl);
@@ -79,12 +94,28 @@ typedef enum eGP_GetFrame_Mode {
GP_GETFRAME_ADD_COPY = 2
} eGP_GetFrame_Mode;
-struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew);
+struct bGPDframe *BKE_gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew);
struct bGPDframe *BKE_gpencil_layer_find_frame(struct bGPDlayer *gpl, int cframe);
-bool gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf);
-
-struct bGPDlayer *gpencil_layer_getactive(struct bGPdata *gpd);
-void gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active);
-void gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl);
+bool BKE_gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf);
+
+struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd);
+void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active);
+void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl);
+
+struct bGPDbrush *BKE_gpencil_brush_getactive(struct ToolSettings *ts);
+void BKE_gpencil_brush_setactive(struct ToolSettings *ts, struct bGPDbrush *active);
+void BKE_gpencil_brush_delete(struct ToolSettings *ts, struct bGPDbrush *palette);
+
+struct bGPDpalette *BKE_gpencil_palette_getactive(struct bGPdata *gpd);
+void BKE_gpencil_palette_setactive(struct bGPdata *gpd, struct bGPDpalette *active);
+void BKE_gpencil_palette_delete(struct bGPdata *gpd, struct bGPDpalette *palette);
+void BKE_gpencil_palette_change_strokes(struct bGPdata *gpd);
+
+struct bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(struct bGPDpalette *palette);
+void BKE_gpencil_palettecolor_setactive(struct bGPDpalette *palette, struct bGPDpalettecolor *active);
+void BKE_gpencil_palettecolor_delete(struct bGPDpalette *palette, struct bGPDpalettecolor *palcolor);
+struct bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(struct bGPDpalette *palette, char *name);
+void BKE_gpencil_palettecolor_changename(struct bGPdata *gpd, char *oldname, const char *newname);
+void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name);
#endif /* __BKE_GPENCIL_H__ */
diff --git a/source/blender/blenkernel/BKE_idcode.h b/source/blender/blenkernel/BKE_idcode.h
index 6de0efe2709..964a49435f1 100644
--- a/source/blender/blenkernel/BKE_idcode.h
+++ b/source/blender/blenkernel/BKE_idcode.h
@@ -42,6 +42,8 @@ bool BKE_idcode_is_valid(short idcode);
int BKE_idcode_to_idfilter(const short idcode);
short BKE_idcode_from_idfilter(const int idfilter);
+int BKE_idcode_to_index(const short idcode);
+
/**
* Return an ID code and steps the index forward 1.
*
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index 2ac7e3c97fb..e49019fcfae 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -86,6 +86,7 @@ bool id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, s
bool id_copy(struct Main *bmain, struct ID *id, struct ID **newid, bool test);
void id_sort_by_name(struct ListBase *lb, struct ID *id);
void BKE_id_expand_local(struct ID *id);
+void BKE_id_copy_ensure_local(struct Main *bmain, struct ID *old_id, struct ID *new_id);
bool new_id(struct ListBase *lb, struct ID *id, const char *name);
void id_clear_lib_data(struct Main *bmain, struct ID *id);
@@ -93,7 +94,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma
struct ListBase *which_libbase(struct Main *mainlib, short type);
-#define MAX_LIBARRAY 34
+#define MAX_LIBARRAY 35
int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
/* Main API */
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 44e4da4e0a3..a4f5c425282 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -42,6 +42,8 @@
*/
#include "DNA_listBase.h"
+#include "BKE_library.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -102,8 +104,9 @@ typedef struct Main {
ListBase movieclip;
ListBase mask;
ListBase linestyle;
+ ListBase cachefiles;
- char id_tag_update[256];
+ char id_tag_update[MAX_LIBARRAY];
/* Evaluation context used by viewport */
struct EvaluationContext *eval_ctx;
diff --git a/source/blender/blenkernel/BKE_movieclip.h b/source/blender/blenkernel/BKE_movieclip.h
index 3d963ac109c..3237c146bc5 100644
--- a/source/blender/blenkernel/BKE_movieclip.h
+++ b/source/blender/blenkernel/BKE_movieclip.h
@@ -41,6 +41,9 @@ struct MovieDistortion;
void BKE_movieclip_free(struct MovieClip *clip);
+struct MovieClip *BKE_movieclip_copy(struct Main *bmain, struct MovieClip *clip);
+void BKE_movieclip_make_local(struct Main *bmain, struct MovieClip *clip, const bool lib_local);
+
struct MovieClip *BKE_movieclip_file_add(struct Main *bmain, const char *name);
struct MovieClip *BKE_movieclip_file_add_exists_ex(struct Main *bmain, const char *name, bool *r_exists);
struct MovieClip *BKE_movieclip_file_add_exists(struct Main *bmain, const char *name);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index bf1cfb263eb..0a3cc950f32 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -99,6 +99,8 @@ void BKE_paint_set_overlay_override(enum OverlayFlags flag);
/* palettes */
void BKE_palette_free(struct Palette *palette);
struct Palette *BKE_palette_add(struct Main *bmain, const char *name);
+struct Palette *BKE_palette_copy(struct Main *bmain, struct Palette *palette);
+void BKE_palette_make_local(struct Main *bmain, struct Palette *palette, const bool lib_local);
struct PaletteColor *BKE_palette_color_add(struct Palette *palette);
bool BKE_palette_is_empty(const struct Palette *palette);
void BKE_palette_color_remove(struct Palette *palette, struct PaletteColor *color);
@@ -107,6 +109,8 @@ void BKE_palette_clear(struct Palette *palette);
/* paint curves */
struct PaintCurve *BKE_paint_curve_add(struct Main *bmain, const char *name);
void BKE_paint_curve_free(struct PaintCurve *pc);
+struct PaintCurve *BKE_paint_curve_copy(struct Main *bmain, struct PaintCurve *pc);
+void BKE_paint_curve_make_local(struct Main *bmain, struct PaintCurve *pc, const bool lib_local);
void BKE_paint_init(struct Scene *sce, PaintMode mode, const char col[3]);
void BKE_paint_free(struct Paint *p);
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index ace30b38b3d..42eaba6f2fb 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -64,6 +64,8 @@ struct BVHTreeRayHit;
struct EdgeHash;
struct EffectorContext;
+#define PARTICLE_COLLISION_MAX_COLLISIONS 10
+
#define PARTICLE_P ParticleData * pa; int p
#define LOOP_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++)
#define LOOP_EXISTING_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & PARS_UNEXIST))
@@ -206,8 +208,7 @@ typedef struct ParticleCollisionElement {
typedef struct ParticleCollision {
struct Object *current;
struct Object *hit;
- struct Object *prev;
- struct Object *skip;
+ struct Object *skip[PARTICLE_COLLISION_MAX_COLLISIONS+1];
struct Object *emitter;
struct CollisionModifierData *md; // collision modifier for current object;
@@ -219,7 +220,7 @@ typedef struct ParticleCollision {
float original_ray_length; //original length of co2-co1, needed for collision time evaluation
- int prev_index;
+ int skip_count;
ParticleCollisionElement pce;
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index 30bb6954019..811e9136fc9 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -477,4 +477,6 @@ struct ImBuf *BKE_sequencer_render_mask_input(
int cfra, int fra_offset, bool make_float);
void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, struct ImBuf *ibuf, float mul, bool make_float, struct ImBuf *mask_input);
+void BKE_sequencer_all_free_anim_ibufs(int cfra);
+
#endif /* __BKE_SEQUENCER_H__ */
diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h
index 2ca88425c29..1938bb08395 100644
--- a/source/blender/blenkernel/BKE_tracking.h
+++ b/source/blender/blenkernel/BKE_tracking.h
@@ -52,6 +52,7 @@ struct rcti;
/* **** Common functions **** */
void BKE_tracking_free(struct MovieTracking *tracking);
+void BKE_tracking_copy(struct MovieTracking *tracking_dst, struct MovieTracking *tracking_src);
void BKE_tracking_settings_init(struct MovieTracking *tracking);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 51eeb79c414..ae952faf8a4 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -82,6 +82,7 @@ set(SRC
intern/brush.c
intern/bullet.c
intern/bvhutils.c
+ intern/cachefile.c
intern/camera.c
intern/cdderivedmesh.c
intern/cloth.c
@@ -208,6 +209,7 @@ set(SRC
BKE_brush.h
BKE_bullet.h
BKE_bvhutils.h
+ BKE_cachefile.h
BKE_camera.h
BKE_ccg.h
BKE_cdderivedmesh.h
@@ -501,6 +503,13 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_ALEMBIC)
+ list(APPEND INC
+ ../alembic
+ )
+ add_definitions(-DWITH_ALEMBIC)
+endif()
+
if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
list(APPEND INC_SYS
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 8f82610a8bb..470098f8c7c 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -156,10 +156,7 @@ bAction *BKE_action_copy(Main *bmain, bAction *src)
}
}
- if (ID_IS_LINKED_DATABLOCK(src)) {
- BKE_id_expand_local(&dst->id);
- BKE_id_lib_local_paths(bmain, src->id.lib, &dst->id);
- }
+ BKE_id_copy_ensure_local(bmain, &src->id, &dst->id);
return dst;
}
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index d04b950c043..1a5d77bbc07 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -97,6 +97,7 @@ bool id_type_can_have_animdata(const short id_type)
case ID_MC:
case ID_MSK:
case ID_GD:
+ case ID_CF:
return true;
/* no AnimData */
@@ -1160,6 +1161,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u
/* grease pencil */
ANIMDATA_IDS_CB(mainptr->gpencil.first);
+
+ /* cache files */
+ ANIMDATA_IDS_CB(mainptr->cachefiles.first);
}
/* Fix all RNA-Paths throughout the database (directly access the Global.main version)
@@ -1250,6 +1254,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const cha
/* grease pencil */
RENAMEFIX_ANIM_IDS(mainptr->gpencil.first);
+
+ /* cache files */
+ RENAMEFIX_ANIM_IDS(mainptr->cachefiles.first);
/* scenes */
RENAMEFIX_ANIM_NODETREE_IDS(mainptr->scene.first, Scene);
@@ -1482,161 +1489,193 @@ static bool animsys_remap_path(AnimMapper *UNUSED(remap), char *path, char **dst
return false;
}
+static bool animsys_store_rna_setting(
+ PointerRNA *ptr, AnimMapper *remap,
+ /* typically 'fcu->rna_path', 'fcu->array_index' */
+ const char *rna_path, const int array_index,
+ PathResolvedRNA *r_result)
+{
+ bool success = false;
+
+ char *path = NULL;
+ bool free_path;
+
+ /* get path, remapped as appropriate to work in its new environment */
+ free_path = animsys_remap_path(remap, (char *)rna_path, &path);
+
+ /* write value to setting */
+ if (path) {
+ /* get property to write to */
+ if (RNA_path_resolve_property(ptr, path, &r_result->ptr, &r_result->prop)) {
+ if ((ptr->id.data == NULL) || RNA_property_animateable(&r_result->ptr, r_result->prop)) {
+ int array_len = RNA_property_array_length(&r_result->ptr, r_result->prop);
+
+ if (array_len && array_index >= array_len) {
+ if (G.debug & G_DEBUG) {
+ printf("Animato: Invalid array index. ID = '%s', '%s[%d]', array length is %d\n",
+ (ptr && ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>",
+ path, array_index, array_len - 1);
+ }
+ }
+ else {
+ r_result->prop_index = array_len ? array_index : -1;
+ success = true;
+ }
+ }
+ }
+ else {
+ /* failed to get path */
+ /* XXX don't tag as failed yet though, as there are some legit situations (Action Constraint)
+ * where some channels will not exist, but shouldn't lock up Action */
+ if (G.debug & G_DEBUG) {
+ printf("Animato: Invalid path. ID = '%s', '%s[%d]'\n",
+ (ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>",
+ path, array_index);
+ }
+ }
+ }
+
+ /* free temp path-info */
+ if (free_path) {
+ MEM_freeN((void *)path);
+ }
+
+ return success;
+}
+
/* less than 1.0 evaluates to false, use epsilon to avoid float error */
#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > ((1.0f - FLT_EPSILON)))
/* Write the given value to a setting using RNA, and return success */
-static bool animsys_write_rna_setting(PointerRNA *ptr, char *path, int array_index, float value)
+static bool animsys_write_rna_setting(PathResolvedRNA *anim_rna, const float value)
{
- PropertyRNA *prop;
- PointerRNA new_ptr;
+ PropertyRNA *prop = anim_rna->prop;
+ PointerRNA *ptr = &anim_rna->ptr;
+ int array_index = anim_rna->prop_index;
- //printf("%p %s %i %f\n", ptr, path, array_index, value);
-
- /* get property to write to */
- if (RNA_path_resolve_property(ptr, path, &new_ptr, &prop)) {
- /* set value for animatable numerical values only
- * HACK: some local F-Curves (e.g. those on NLA Strips) are evaluated
- * without an ID provided, which causes the animateable test to fail!
- */
- if (RNA_property_animateable(&new_ptr, prop) || (ptr->id.data == NULL)) {
- int array_len = RNA_property_array_length(&new_ptr, prop);
- bool written = false;
-
- if (array_len && array_index >= array_len) {
- if (G.debug & G_DEBUG) {
- printf("Animato: Invalid array index. ID = '%s', '%s[%d]', array length is %d\n",
- (ptr && ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>",
- path, array_index, array_len - 1);
+ /* caller must ensure this is animatable */
+ BLI_assert(RNA_property_animateable(ptr, prop) || ptr->id.data == NULL);
+
+ /* set value for animatable numerical values only
+ * HACK: some local F-Curves (e.g. those on NLA Strips) are evaluated
+ * without an ID provided, which causes the animateable test to fail!
+ */
+ bool written = false;
+
+ switch (RNA_property_type(prop)) {
+ case PROP_BOOLEAN:
+ {
+ const int value_coerce = ANIMSYS_FLOAT_AS_BOOL(value);
+ if (array_index != -1) {
+ if (RNA_property_boolean_get_index(ptr, prop, array_index) != value_coerce) {
+ RNA_property_boolean_set_index(ptr, prop, array_index, value_coerce);
+ written = true;
}
-
- return false;
}
-
- switch (RNA_property_type(prop)) {
- case PROP_BOOLEAN:
- if (array_len) {
- if (RNA_property_boolean_get_index(&new_ptr, prop, array_index) != ANIMSYS_FLOAT_AS_BOOL(value)) {
- RNA_property_boolean_set_index(&new_ptr, prop, array_index, ANIMSYS_FLOAT_AS_BOOL(value));
- written = true;
- }
- }
- else {
- if (RNA_property_boolean_get(&new_ptr, prop) != ANIMSYS_FLOAT_AS_BOOL(value)) {
- RNA_property_boolean_set(&new_ptr, prop, ANIMSYS_FLOAT_AS_BOOL(value));
- written = true;
- }
- }
- break;
- case PROP_INT:
- if (array_len) {
- if (RNA_property_int_get_index(&new_ptr, prop, array_index) != (int)value) {
- RNA_property_int_set_index(&new_ptr, prop, array_index, (int)value);
- written = true;
- }
- }
- else {
- if (RNA_property_int_get(&new_ptr, prop) != (int)value) {
- RNA_property_int_set(&new_ptr, prop, (int)value);
- written = true;
- }
- }
- break;
- case PROP_FLOAT:
- if (array_len) {
- if (RNA_property_float_get_index(&new_ptr, prop, array_index) != value) {
- RNA_property_float_set_index(&new_ptr, prop, array_index, value);
- written = true;
- }
- }
- else {
- if (RNA_property_float_get(&new_ptr, prop) != value) {
- RNA_property_float_set(&new_ptr, prop, value);
- written = true;
- }
- }
- break;
- case PROP_ENUM:
- if (RNA_property_enum_get(&new_ptr, prop) != (int)value) {
- RNA_property_enum_set(&new_ptr, prop, (int)value);
- written = true;
- }
- break;
- default:
- /* nothing can be done here... so it is unsuccessful? */
- return false;
+ else {
+ if (RNA_property_boolean_get(ptr, prop) != value_coerce) {
+ RNA_property_boolean_set(ptr, prop, value_coerce);
+ written = true;
+ }
}
-
- /* RNA property update disabled for now - [#28525] [#28690] [#28774] [#28777] */
-#if 0
- /* buffer property update for later flushing */
- if (written && RNA_property_update_check(prop)) {
- short skip_updates_hack = 0;
-
- /* optimization hacks: skip property updates for those properties
- * for we know that which the updates in RNA were really just for
- * flushing property editing via UI/Py
- */
- if (new_ptr.type == &RNA_PoseBone) {
- /* bone transforms - update pose (i.e. tag depsgraph) */
- skip_updates_hack = 1;
+ break;
+ }
+ case PROP_INT:
+ {
+ const int value_coerce = (int)value;
+ if (array_index != -1) {
+ if (RNA_property_int_get_index(ptr, prop, array_index) != value_coerce) {
+ RNA_property_int_set_index(ptr, prop, array_index, value_coerce);
+ written = true;
}
-
- if (skip_updates_hack == 0)
- RNA_property_update_cache_add(&new_ptr, prop);
}
-#endif
-
- /* as long as we don't do property update, we still tag datablock
- * as having been updated. this flag does not cause any updates to
- * be run, it's for e.g. render engines to synchronize data */
- if (written && new_ptr.id.data) {
- ID *id = new_ptr.id.data;
-
- /* 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;
- DAG_id_type_tag(G.main, GS(id->name));
+ else {
+ if (RNA_property_int_get(ptr, prop) != value_coerce) {
+ RNA_property_int_set(ptr, prop, value_coerce);
+ written = true;
}
}
+ break;
}
-
- /* successful */
- return true;
+ case PROP_FLOAT:
+ {
+ if (array_index != -1) {
+ if (RNA_property_float_get_index(ptr, prop, array_index) != value) {
+ RNA_property_float_set_index(ptr, prop, array_index, value);
+ written = true;
+ }
+ }
+ else {
+ if (RNA_property_float_get(ptr, prop) != value) {
+ RNA_property_float_set(ptr, prop, value);
+ written = true;
+ }
+ }
+ break;
+ }
+ case PROP_ENUM:
+ {
+ const int value_coerce = (int)value;
+ if (RNA_property_enum_get(ptr, prop) != value_coerce) {
+ RNA_property_enum_set(ptr, prop, value_coerce);
+ written = true;
+ }
+ break;
+ }
+ default:
+ /* nothing can be done here... so it is unsuccessful? */
+ return false;
}
- else {
- /* failed to get path */
- /* XXX don't tag as failed yet though, as there are some legit situations (Action Constraint)
- * where some channels will not exist, but shouldn't lock up Action */
- if (G.debug & G_DEBUG) {
- printf("Animato: Invalid path. ID = '%s', '%s[%d]'\n",
- (ptr->id.data) ? (((ID *)ptr->id.data)->name + 2) : "<No ID>",
- path, array_index);
+
+ /* RNA property update disabled for now - [#28525] [#28690] [#28774] [#28777] */
+#if 0
+ /* buffer property update for later flushing */
+ if (written && RNA_property_update_check(prop)) {
+ short skip_updates_hack = 0;
+
+ /* optimization hacks: skip property updates for those properties
+ * for we know that which the updates in RNA were really just for
+ * flushing property editing via UI/Py
+ */
+ if (new_ptr.type == &RNA_PoseBone) {
+ /* bone transforms - update pose (i.e. tag depsgraph) */
+ skip_updates_hack = 1;
+ }
+
+ if (skip_updates_hack == 0)
+ RNA_property_update_cache_add(ptr, prop);
+ }
+#endif
+
+ /* as long as we don't do property update, we still tag datablock
+ * as having been updated. this flag does not cause any updates to
+ * be run, it's for e.g. render engines to synchronize data */
+ if (written && ptr->id.data) {
+ ID *id = ptr->id.data;
+
+ /* 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;
+ DAG_id_type_tag(G.main, GS(id->name));
}
- return false;
}
+
+ /* successful */
+ return true;
}
/* Simple replacement based data-setting of the FCurve using RNA */
bool BKE_animsys_execute_fcurve(PointerRNA *ptr, AnimMapper *remap, FCurve *fcu, float curval)
{
- char *path = NULL;
- bool free_path = false;
+ PathResolvedRNA anim_rna;
bool ok = false;
-
- /* get path, remapped as appropriate to work in its new environment */
- free_path = animsys_remap_path(remap, fcu->rna_path, &path);
-
- /* write value to setting */
- if (path)
- ok = animsys_write_rna_setting(ptr, path, fcu->array_index, curval);
-
- /* free temp path-info */
- if (free_path)
- MEM_freeN(path);
-
+
+ if (animsys_store_rna_setting(ptr, remap, fcu->rna_path, fcu->array_index, &anim_rna)) {
+ ok = animsys_write_rna_setting(&anim_rna, curval);
+ }
+
/* return whether we were successful */
return ok;
}
@@ -1654,8 +1693,11 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, ListBase *list, AnimMapper
if ((fcu->grp == NULL) || (fcu->grp->flag & AGRP_MUTED) == 0) {
/* check if this curve should be skipped */
if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0) {
- const float curval = calculate_fcurve(fcu, ctime);
- BKE_animsys_execute_fcurve(ptr, remap, fcu, curval);
+ PathResolvedRNA anim_rna;
+ if (animsys_store_rna_setting(ptr, remap, fcu->rna_path, fcu->array_index, &anim_rna)) {
+ const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ animsys_write_rna_setting(&anim_rna, curval);
+ }
}
}
}
@@ -1684,8 +1726,12 @@ static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime
/* evaluate this using values set already in other places
* NOTE: for 'layering' option later on, we should check if we should remove old value before adding
* new to only be done when drivers only changed */
- const float curval = calculate_fcurve(fcu, ctime);
- ok = BKE_animsys_execute_fcurve(ptr, NULL, fcu, curval);
+
+ PathResolvedRNA anim_rna;
+ if (animsys_store_rna_setting(ptr, NULL, fcu->rna_path, fcu->array_index, &anim_rna)) {
+ const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ ok = animsys_write_rna_setting(&anim_rna, curval);
+ }
/* clear recalc flag */
driver->flag &= ~DRIVER_FLAG_RECALC;
@@ -1753,8 +1799,11 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup *
for (fcu = agrp->channels.first; (fcu) && (fcu->grp == agrp); fcu = fcu->next) {
/* check if this curve should be skipped */
if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0) {
- const float curval = calculate_fcurve(fcu, ctime);
- BKE_animsys_execute_fcurve(ptr, remap, fcu, curval);
+ PathResolvedRNA anim_rna;
+ if (animsys_store_rna_setting(ptr, remap, fcu->rna_path, fcu->array_index, &anim_rna)) {
+ const float curval = calculate_fcurve(&anim_rna, fcu, ctime);
+ animsys_write_rna_setting(&anim_rna, curval);
+ }
}
}
}
@@ -2612,8 +2661,12 @@ static void animsys_evaluate_overrides(PointerRNA *ptr, AnimData *adt)
AnimOverride *aor;
/* for each override, simply execute... */
- for (aor = adt->overrides.first; aor; aor = aor->next)
- animsys_write_rna_setting(ptr, aor->rna_path, aor->array_index, aor->value);
+ for (aor = adt->overrides.first; aor; aor = aor->next) {
+ PathResolvedRNA anim_rna;
+ if (animsys_store_rna_setting(ptr, NULL, aor->rna_path, aor->array_index, &anim_rna)) {
+ animsys_write_rna_setting(&anim_rna, aor->value);
+ }
+ }
}
/* ***************************************** */
@@ -2827,6 +2880,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime)
/* grease pencil */
EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM);
+
+ /* cache files */
+ EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM);
/* objects */
/* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets
@@ -2888,8 +2944,13 @@ void BKE_animsys_eval_driver(EvaluationContext *eval_ctx,
* NOTE: for 'layering' option later on, we should check if we should remove old value before adding
* new to only be done when drivers only changed */
//printf("\told val = %f\n", fcu->curval);
- const float curval = calculate_fcurve(fcu, eval_ctx->ctime);
- ok = BKE_animsys_execute_fcurve(&id_ptr, NULL, fcu, curval);
+
+ PathResolvedRNA anim_rna;
+ if (animsys_store_rna_setting(&id_ptr, NULL, fcu->rna_path, fcu->array_index, &anim_rna)) {
+ const float curval = calculate_fcurve(&anim_rna, fcu, eval_ctx->ctime);
+ ok = animsys_write_rna_setting(&anim_rna, curval);
+ }
+
//printf("\tnew val = %f\n", fcu->curval);
/* clear recalc flag */
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 790272c4411..c644fe09364 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -194,10 +194,7 @@ bArmature *BKE_armature_copy(Main *bmain, bArmature *arm)
newArm->act_edbone = NULL;
newArm->sketch = NULL;
- if (ID_IS_LINKED_DATABLOCK(arm)) {
- BKE_id_expand_local(&newArm->id);
- BKE_id_lib_local_paths(bmain, arm->id.lib, &newArm->id);
- }
+ BKE_id_copy_ensure_local(bmain, &arm->id, &newArm->id);
return newArm;
}
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index a708c59fa97..487b8ffa2b5 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -48,6 +48,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_image_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
@@ -653,6 +654,12 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data);
break;
}
+ case ID_CF:
+ {
+ CacheFile *cache_file = (CacheFile *)id;
+ rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data);
+ break;
+ }
default:
/* Nothing to do for other IDs that don't contain file paths. */
break;
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 9027287a457..8ef1fae1155 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -197,10 +197,7 @@ Brush *BKE_brush_copy(Main *bmain, Brush *brush)
/* enable fake user by default */
id_fake_user_set(&brush->id);
- if (ID_IS_LINKED_DATABLOCK(brush)) {
- BKE_id_expand_local(&brushn->id);
- BKE_id_lib_local_paths(bmain, brush->id.lib, &brushn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &brush->id, &brushn->id);
return brushn;
}
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
new file mode 100644
index 00000000000..16f263791db
--- /dev/null
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -0,0 +1,173 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/cachefile.c
+ * \ingroup bke
+ */
+
+#include "DNA_anim_types.h"
+#include "DNA_cachefile_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_animsys.h"
+#include "BKE_cachefile.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+#ifdef WITH_ALEMBIC
+# include "ABC_alembic.h"
+#endif
+
+void *BKE_cachefile_add(Main *bmain, const char *name)
+{
+ CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, name);
+
+ BKE_cachefile_init(cache_file);
+
+ return cache_file;
+}
+
+void BKE_cachefile_init(CacheFile *cache_file)
+{
+ cache_file->handle = NULL;
+ cache_file->filepath[0] = '\0';
+ cache_file->override_frame = false;
+ cache_file->frame = 0.0f;
+ cache_file->is_sequence = false;
+ cache_file->scale = 1.0f;
+}
+
+/** Free (or release) any data used by this cachefile (does not free the cachefile itself). */
+void BKE_cachefile_free(CacheFile *cache_file)
+{
+ BKE_animdata_free((ID *)cache_file, false);
+
+#ifdef WITH_ALEMBIC
+ ABC_free_handle(cache_file->handle);
+#endif
+
+ BLI_freelistN(&cache_file->object_paths);
+}
+
+CacheFile *BKE_cachefile_copy(Main *bmain, CacheFile *cache_file)
+{
+ CacheFile *new_cache_file = BKE_libblock_copy(bmain, &cache_file->id);
+ new_cache_file->handle = NULL;
+
+ BLI_listbase_clear(&cache_file->object_paths);
+
+ BKE_id_copy_ensure_local(bmain, &cache_file->id, &new_cache_file->id);
+
+ return new_cache_file;
+}
+
+void BKE_cachefile_make_local(Main *bmain, CacheFile *cache_file, const bool lib_local)
+{
+ BKE_id_make_local_generic(bmain, &cache_file->id, true, lib_local);
+}
+
+void BKE_cachefile_reload(const Main *bmain, CacheFile *cache_file)
+{
+ char filepath[FILE_MAX];
+
+ BLI_strncpy(filepath, cache_file->filepath, sizeof(filepath));
+ BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &cache_file->id));
+
+#ifdef WITH_ALEMBIC
+ if (cache_file->handle) {
+ ABC_free_handle(cache_file->handle);
+ }
+
+ cache_file->handle = ABC_create_handle(filepath, &cache_file->object_paths);
+#endif
+}
+
+void BKE_cachefile_ensure_handle(const Main *bmain, CacheFile *cache_file)
+{
+ if (cache_file->handle == NULL) {
+ BKE_cachefile_reload(bmain, cache_file);
+ }
+}
+
+void BKE_cachefile_update_frame(Main *bmain, Scene *scene, const float ctime, const float fps)
+{
+ CacheFile *cache_file;
+ char filename[FILE_MAX];
+
+ for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
+ /* Execute drivers only, as animation has already been done. */
+ BKE_animsys_evaluate_animdata(scene, &cache_file->id, cache_file->adt, ctime, ADT_RECALC_DRIVERS);
+
+ if (!cache_file->is_sequence) {
+ continue;
+ }
+
+ const float time = BKE_cachefile_time_offset(cache_file, ctime, fps);
+
+ if (BKE_cachefile_filepath_get(bmain, cache_file, time, filename)) {
+#ifdef WITH_ALEMBIC
+ ABC_free_handle(cache_file->handle);
+ cache_file->handle = ABC_create_handle(filename, NULL);
+#endif
+ }
+ }
+}
+
+bool BKE_cachefile_filepath_get(
+ const Main *bmain, const CacheFile *cache_file, float frame,
+ char r_filepath[FILE_MAX])
+{
+ BLI_strncpy(r_filepath, cache_file->filepath, FILE_MAX);
+ BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id));
+
+ int fframe;
+ int frame_len;
+
+ if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) {
+ char ext[32];
+ BLI_path_frame_strip(r_filepath, true, ext);
+ BLI_path_frame(r_filepath, frame, frame_len);
+ BLI_ensure_extension(r_filepath, FILE_MAX, ext);
+
+ /* TODO(kevin): store sequence range? */
+ return BLI_exists(r_filepath);
+ }
+
+ return true;
+}
+
+float BKE_cachefile_time_offset(CacheFile *cache_file, const float time, const float fps)
+{
+ const float frame = (cache_file->override_frame ? cache_file->frame : time);
+ return cache_file->is_sequence ? frame : frame / fps;
+}
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 4229b2a727e..85ce399b770 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -99,10 +99,7 @@ Camera *BKE_camera_copy(Main *bmain, Camera *cam)
camn = BKE_libblock_copy(bmain, &cam->id);
- if (ID_IS_LINKED_DATABLOCK(cam)) {
- BKE_id_expand_local(&camn->id);
- BKE_id_lib_local_paths(bmain, cam->id.lib, &camn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &cam->id, &camn->id);
return camn;
}
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 159d5b82a6c..d257a1cfcae 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -3402,7 +3402,7 @@ void CDDM_calc_edges(DerivedMesh *dm)
BLI_edgehashIterator_getKey(ehi, &med->v1, &med->v2);
j = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
- if (j == 0) {
+ if (j == 0 || !eindex) {
med->flag = ME_EDGEDRAW | ME_EDGERENDER;
*index = ORIGINDEX_NONE;
}
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index b979f892e86..329060644b9 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -58,6 +58,7 @@ static void cloth_to_object (Object *ob, ClothModifierData *clmd, float (*verte
static void cloth_from_mesh ( ClothModifierData *clmd, DerivedMesh *dm );
static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first);
static void cloth_update_springs( ClothModifierData *clmd );
+static void cloth_update_verts( Object *ob, ClothModifierData *clmd, DerivedMesh *dm );
static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm );
static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm );
static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm );
@@ -96,6 +97,7 @@ void cloth_init(ClothModifierData *clmd )
clmd->sim_parms->avg_spring_len = 0.0;
clmd->sim_parms->presets = 2; /* cotton as start setting */
clmd->sim_parms->timescale = 1.0f; /* speed factor, describes how fast cloth moves */
+ clmd->sim_parms->time_scale = 1.0f; /* multiplies cloth speed */
clmd->sim_parms->reset = 0;
clmd->sim_parms->vel_damping = 1.0f; /* 1.0 = no damping, 0.0 = fully dampened */
@@ -368,10 +370,13 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights, true);
+ if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH )
+ cloth_update_verts ( ob, clmd, result );
+
/* Support for dynamic vertex groups, changing from frame to frame */
cloth_apply_vgroup ( clmd, result );
- if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW )
+ if ( clmd->sim_parms->flags & (CLOTH_SIMSETTINGS_FLAG_SEW | CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH) )
cloth_update_spring_lengths ( clmd, result );
cloth_update_springs( clmd );
@@ -407,7 +412,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
BKE_ptcache_id_from_cloth(&pid, ob, clmd);
BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, &timescale);
- clmd->sim_parms->timescale= timescale;
+ clmd->sim_parms->timescale= timescale * clmd->sim_parms->time_scale;
if (clmd->sim_parms->reset || (clmd->clothObject && dm->getNumVerts(dm) != clmd->clothObject->mvert_num)) {
clmd->sim_parms->reset = 0;
@@ -804,7 +809,7 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d
clmd->clothObject->springs = NULL;
clmd->clothObject->numsprings = -1;
- if ( clmd->sim_parms->shapekey_rest )
+ if ( clmd->sim_parms->shapekey_rest && !(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH ) )
shapekey_rest = dm->getVertDataArray ( dm, CD_CLOTH_ORCO );
mvert = dm->getVertArray (dm);
@@ -1180,6 +1185,20 @@ static void cloth_update_springs( ClothModifierData *clmd )
cloth_hair_update_bending_targets(clmd);
}
+/* Update rest verts, for dynamically deformable cloth */
+static void cloth_update_verts( Object *ob, ClothModifierData *clmd, DerivedMesh *dm )
+{
+ unsigned int i = 0;
+ MVert *mvert = dm->getVertArray (dm);
+ ClothVertex *verts = clmd->clothObject->verts;
+
+ /* vertex count is already ensured to match */
+ for ( i = 0; i < dm->getNumVerts(dm); i++, verts++ ) {
+ copy_v3_v3(verts->xrest, mvert[i].co);
+ mul_m4_v3(ob->obmat, verts->xrest);
+ }
+}
+
/* Update spring rest lenght, for dynamically deformable cloth */
static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm )
{
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index 53a74024c51..4f3ffed41bc 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -293,8 +293,8 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
cuma->curve[1].x = clipr->xmax;
cuma->curve[1].y = clipr->ymin;
if (slope == CURVEMAP_SLOPE_POS_NEG) {
- cuma->curve[0].flag |= CUMA_VECTOR;
- cuma->curve[1].flag |= CUMA_VECTOR;
+ cuma->curve[0].flag |= CUMA_HANDLE_VECTOR;
+ cuma->curve[1].flag |= CUMA_HANDLE_VECTOR;
}
break;
case CURVE_PRESET_SHARP:
@@ -391,15 +391,25 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
}
}
-/* if type==1: vector, else auto */
-void curvemap_sethandle(CurveMap *cuma, int type)
+/**
+ * \param type: eBezTriple_Handle
+ */
+void curvemap_handle_set(CurveMap *cuma, int type)
{
int a;
for (a = 0; a < cuma->totpoint; a++) {
if (cuma->curve[a].flag & CUMA_SELECT) {
- if (type) cuma->curve[a].flag |= CUMA_VECTOR;
- else cuma->curve[a].flag &= ~CUMA_VECTOR;
+ cuma->curve[a].flag &= ~(CUMA_HANDLE_VECTOR | CUMA_HANDLE_AUTO_ANIM);
+ if (type == HD_VECT) {
+ cuma->curve[a].flag |= CUMA_HANDLE_VECTOR;
+ }
+ else if (type == HD_AUTO_ANIM) {
+ cuma->curve[a].flag |= CUMA_HANDLE_AUTO_ANIM;
+ }
+ else {
+ /* pass */
+ }
}
}
}
@@ -457,7 +467,7 @@ static void calchandle_curvemap(
if (len_a == 0.0f) len_a = 1.0f;
if (len_b == 0.0f) len_b = 1.0f;
- if (bezt->h1 == HD_AUTO || bezt->h2 == HD_AUTO) { /* auto */
+ if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { /* auto */
float tvec[2];
tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
@@ -465,13 +475,57 @@ static void calchandle_curvemap(
len = len_v2(tvec) * 2.5614f;
if (len != 0.0f) {
- if (bezt->h1 == HD_AUTO) {
+ if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
len_a /= len;
madd_v2_v2v2fl(p2_h1, p2, tvec, -len_a);
+
+ if ((bezt->h1 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
+ const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
+ const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
+ if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) ||
+ (ydiff1 >= 0.0f && ydiff2 >= 0.0f))
+ {
+ bezt->vec[0][1] = bezt->vec[1][1];
+ }
+ else { /* handles should not be beyond y coord of two others */
+ if (ydiff1 <= 0.0f) {
+ if (prev->vec[1][1] > bezt->vec[0][1]) {
+ bezt->vec[0][1] = prev->vec[1][1];
+ }
+ }
+ else {
+ if (prev->vec[1][1] < bezt->vec[0][1]) {
+ bezt->vec[0][1] = prev->vec[1][1];
+ }
+ }
+ }
+ }
}
- if (bezt->h2 == HD_AUTO) {
+ if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {
len_b /= len;
madd_v2_v2v2fl(p2_h2, p2, tvec, len_b);
+
+ if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
+ const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
+ const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
+ if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f)||
+ (ydiff1 >= 0.0f && ydiff2 >= 0.0f))
+ {
+ bezt->vec[2][1] = bezt->vec[1][1];
+ }
+ else { /* handles should not be beyond y coord of two others */
+ if (ydiff1 <= 0.0f) {
+ if (next->vec[1][1] < bezt->vec[2][1]) {
+ bezt->vec[2][1] = next->vec[1][1];
+ }
+ }
+ else {
+ if (next->vec[1][1] > bezt->vec[2][1]) {
+ bezt->vec[2][1] = next->vec[1][1];
+ }
+ }
+ }
+ }
}
}
}
@@ -540,10 +594,15 @@ static void curvemap_make_table(CurveMap *cuma, const rctf *clipr)
cuma->maxtable = max_ff(cuma->maxtable, cmp[a].x);
bezt[a].vec[1][0] = cmp[a].x;
bezt[a].vec[1][1] = cmp[a].y;
- if (cmp[a].flag & CUMA_VECTOR)
+ if (cmp[a].flag & CUMA_HANDLE_VECTOR) {
bezt[a].h1 = bezt[a].h2 = HD_VECT;
- else
+ }
+ else if (cmp[a].flag & CUMA_HANDLE_AUTO_ANIM) {
+ bezt[a].h1 = bezt[a].h2 = HD_AUTO_ANIM;
+ }
+ else {
bezt[a].h1 = bezt[a].h2 = HD_AUTO;
+ }
}
const BezTriple *bezt_prev = NULL;
@@ -773,12 +832,12 @@ void curvemapping_changed(CurveMapping *cumap, const bool rem_doubles)
dy = cmp[a].y - cmp[a + 1].y;
if (sqrtf(dx * dx + dy * dy) < thresh) {
if (a == 0) {
- cmp[a + 1].flag |= CUMA_VECTOR;
+ cmp[a + 1].flag |= CUMA_HANDLE_VECTOR;
if (cmp[a + 1].flag & CUMA_SELECT)
cmp[a].flag |= CUMA_SELECT;
}
else {
- cmp[a].flag |= CUMA_VECTOR;
+ cmp[a].flag |= CUMA_HANDLE_VECTOR;
if (cmp[a].flag & CUMA_SELECT)
cmp[a + 1].flag |= CUMA_SELECT;
}
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 4c9ddd495e3..70fdd4aa72e 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -46,6 +46,7 @@
#include "BLT_translation.h"
#include "DNA_armature_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_constraint_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
@@ -63,6 +64,7 @@
#include "BKE_anim.h" /* for the curve calculation part */
#include "BKE_armature.h"
#include "BKE_bvhutils.h"
+#include "BKE_cachefile.h"
#include "BKE_camera.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
@@ -86,6 +88,10 @@
# include "BPY_extern.h"
#endif
+#ifdef WITH_ALEMBIC
+# include "ABC_alembic.h"
+#endif
+
/* ---------------------------------------------------------------------------- */
/* Useful macros for testing various common flag combinations */
@@ -4333,6 +4339,73 @@ static bConstraintTypeInfo CTI_OBJECTSOLVER = {
objectsolver_evaluate /* evaluate */
};
+/* ----------- Transform Cache ------------- */
+
+static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata)
+{
+ bTransformCacheConstraint *data = con->data;
+ func(con, (ID **)&data->cache_file, false, userdata);
+}
+
+static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
+{
+#ifdef WITH_ALEMBIC
+ bTransformCacheConstraint *data = con->data;
+ Scene *scene = cob->scene;
+
+ const float frame = BKE_scene_frame_get(scene);
+ const float time = BKE_cachefile_time_offset(data->cache_file, frame, FPS);
+
+ CacheFile *cache_file = data->cache_file;
+
+ BKE_cachefile_ensure_handle(G.main, cache_file);
+
+ ABC_get_transform(cache_file->handle, cob->ob, data->object_path,
+ cob->matrix, time, cache_file->scale);
+#else
+ UNUSED_VARS(con, cob);
+#endif
+
+ UNUSED_VARS(targets);
+}
+
+static void transformcache_copy(bConstraint *con, bConstraint *srccon)
+{
+ bTransformCacheConstraint *src = srccon->data;
+ bTransformCacheConstraint *dst = con->data;
+
+ BLI_strncpy(dst->object_path, src->object_path, sizeof(dst->object_path));
+ dst->cache_file = src->cache_file;
+
+ if (dst->cache_file) {
+ id_us_plus(&dst->cache_file->id);
+ }
+}
+
+static void transformcache_free(bConstraint *con)
+{
+ bTransformCacheConstraint *data = con->data;
+
+ if (data->cache_file) {
+ id_us_min(&data->cache_file->id);
+ }
+}
+
+static bConstraintTypeInfo CTI_TRANSFORM_CACHE = {
+ CONSTRAINT_TYPE_TRANSFORM_CACHE, /* type */
+ sizeof(bTransformCacheConstraint), /* size */
+ "Transform Cache", /* name */
+ "bTransformCacheConstraint", /* struct name */
+ transformcache_free, /* free data */
+ transformcache_id_looper, /* id looper */
+ transformcache_copy, /* copy data */
+ NULL, /* new data */
+ NULL, /* get constraint targets */
+ NULL, /* flush constraint targets */
+ NULL, /* get target matrix */
+ transformcache_evaluate /* evaluate */
+};
+
/* ************************* Constraints Type-Info *************************** */
/* All of the constraints api functions use bConstraintTypeInfo structs to carry out
* and operations that involve constraint specific code.
@@ -4374,6 +4447,7 @@ static void constraints_init_typeinfo(void)
constraintsTypeInfo[26] = &CTI_FOLLOWTRACK; /* Follow Track Constraint */
constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */
constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */
+ constraintsTypeInfo[29] = &CTI_TRANSFORM_CACHE; /* Transform Cache Constraint */
}
/* This function should be used for getting the appropriate type-info when only
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 5b7698544e0..926ca8da192 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1067,6 +1067,11 @@ struct EditBone *CTX_data_active_bone(const bContext *C)
return ctx_data_pointer_get(C, "active_bone");
}
+struct CacheFile *CTX_data_edit_cachefile(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "edit_cachefile");
+}
+
int CTX_data_selected_bones(const bContext *C, ListBase *list)
{
return ctx_data_collection_get(C, "selected_bones", list);
@@ -1112,6 +1117,21 @@ bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C)
return ctx_data_pointer_get(C, "active_gpencil_layer");
}
+bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "active_gpencil_palette");
+}
+
+bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "active_gpencil_palettecolor");
+}
+
+bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "active_gpencil_brush");
+}
+
bGPDframe *CTX_data_active_gpencil_frame(const bContext *C)
{
return ctx_data_pointer_get(C, "active_gpencil_frame");
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 07f4e4f1610..90a514781d7 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -207,10 +207,7 @@ Curve *BKE_curve_copy(Main *bmain, Curve *cu)
id_us_plus((ID *)cun->vfonti);
id_us_plus((ID *)cun->vfontbi);
- if (ID_IS_LINKED_DATABLOCK(cu)) {
- BKE_id_expand_local(&cun->id);
- BKE_id_lib_local_paths(bmain, cu->id.lib, &cun->id);
- }
+ BKE_id_copy_ensure_local(bmain, &cu->id, &cun->id);
return cun;
}
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 7a3071227e6..85dd39698de 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -46,6 +46,7 @@
#include "DNA_anim_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_group_types.h"
#include "DNA_lamp_types.h"
#include "DNA_lattice_types.h"
@@ -66,6 +67,7 @@
#include "BKE_effect.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
+#include "BKE_idcode.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_library.h"
@@ -2175,7 +2177,12 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob)
if (cti) {
/* special case for camera tracking -- it doesn't use targets to define relations */
- if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER, CONSTRAINT_TYPE_OBJECTSOLVER)) {
+ if (ELEM(cti->type,
+ CONSTRAINT_TYPE_FOLLOWTRACK,
+ CONSTRAINT_TYPE_CAMERASOLVER,
+ CONSTRAINT_TYPE_OBJECTSOLVER,
+ CONSTRAINT_TYPE_TRANSFORM_CACHE))
+ {
ob->recalc |= OB_RECALC_OB;
}
else if (cti->get_constraint_targets) {
@@ -2806,9 +2813,7 @@ void DAG_ids_flush_tagged(Main *bmain)
ListBase *lb = lbarray[a];
ID *id = lb->first;
- /* we tag based on first ID type character to avoid
- * looping over all ID's in case there are no tags */
- if (id && bmain->id_tag_update[id->name[0]]) {
+ if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) {
for (; id; id = id->next) {
if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)) {
@@ -2848,9 +2853,7 @@ void DAG_ids_check_recalc(Main *bmain, Scene *scene, bool time)
ListBase *lb = lbarray[a];
ID *id = lb->first;
- /* we tag based on first ID type character to avoid
- * looping over all ID's in case there are no tags */
- if (id && bmain->id_tag_update[id->name[0]]) {
+ if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) {
updated = true;
break;
}
@@ -2931,9 +2934,7 @@ void DAG_ids_clear_recalc(Main *bmain)
ListBase *lb = lbarray[a];
ID *id = lb->first;
- /* we tag based on first ID type character to avoid
- * looping over all ID's in case there are no tags */
- if (id && bmain->id_tag_update[id->name[0]]) {
+ if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) {
for (; id; id = id->next) {
if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA))
id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA);
@@ -3008,6 +3009,33 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
/* BLI_assert(!"invalid flag for this 'idtype'"); */
}
}
+ else if (GS(id->name) == ID_CF) {
+ for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
+ ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache);
+
+ if (md) {
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
+
+ if (mcmd->cache_file && (&mcmd->cache_file->id == id)) {
+ ob->recalc |= OB_RECALC_DATA;
+ continue;
+ }
+ }
+
+ for (bConstraint *con = ob->constraints.first; con; con = con->next) {
+ if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) {
+ continue;
+ }
+
+ bTransformCacheConstraint *data = con->data;
+
+ if (data->cache_file && (&data->cache_file->id == id)) {
+ ob->recalc |= OB_RECALC_DATA;
+ break;
+ }
+ }
+ }
+ }
}
void DAG_id_tag_update(ID *id, short flag)
@@ -3027,12 +3055,12 @@ void DAG_id_type_tag(Main *bmain, short idtype)
DAG_id_type_tag(bmain, ID_SCE);
}
- bmain->id_tag_update[((char *)&idtype)[0]] = 1;
+ bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1;
}
int DAG_id_type_tagged(Main *bmain, short idtype)
{
- return bmain->id_tag_update[((char *)&idtype)[0]];
+ return bmain->id_tag_update[BKE_idcode_to_index(idtype)];
}
#if 0 // UNUSED
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 395161aa6ed..a89d423e7a6 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -1859,7 +1859,7 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
* - "evaltime" is the frame at which F-Curve is being evaluated
* - has to return a float value
*/
-float evaluate_driver(ChannelDriver *driver, const float evaltime)
+float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, const float evaltime)
{
DriverVar *dvar;
@@ -1944,7 +1944,9 @@ float evaluate_driver(ChannelDriver *driver, const float evaltime)
* - on errors it reports, then returns 0.0f
*/
BLI_mutex_lock(&python_driver_lock);
- driver->curval = BPY_driver_exec(driver, evaltime);
+
+ driver->curval = BPY_driver_exec(anim_rna, driver, evaltime);
+
BLI_mutex_unlock(&python_driver_lock);
}
#else /* WITH_PYTHON*/
@@ -2599,25 +2601,64 @@ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime)
/* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime")
* Note: this is also used for drivers
*/
-float evaluate_fcurve(FCurve *fcu, float evaltime)
+static float evaluate_fcurve_ex(FCurve *fcu, float evaltime, float cvalue)
{
FModifierStackStorage *storage;
- float cvalue = 0.0f;
float devaltime;
+
+ /* evaluate modifiers which modify time to evaluate the base curve at */
+ storage = evaluate_fmodifiers_storage_new(&fcu->modifiers);
+ devaltime = evaluate_time_fmodifiers(storage, &fcu->modifiers, fcu, cvalue, evaltime);
+
+ /* evaluate curve-data
+ * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying
+ * F-Curve modifier on the stack requested the curve to be evaluated at
+ */
+ if (fcu->bezt)
+ cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime);
+ else if (fcu->fpt)
+ cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime);
+
+ /* evaluate modifiers */
+ evaluate_value_fmodifiers(storage, &fcu->modifiers, fcu, &cvalue, devaltime);
+
+ evaluate_fmodifiers_storage_free(storage);
+
+ /* if curve can only have integral values, perform truncation (i.e. drop the decimal part)
+ * here so that the curve can be sampled correctly
+ */
+ if (fcu->flag & FCURVE_INT_VALUES)
+ cvalue = floorf(cvalue + 0.5f);
- /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime"
+ /* return evaluated value */
+ return cvalue;
+}
+
+float evaluate_fcurve(FCurve *fcu, float evaltime)
+{
+ BLI_assert(fcu->driver == NULL);
+
+ return evaluate_fcurve_ex(fcu, evaltime, 0.0);
+}
+
+float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime)
+{
+ BLI_assert(fcu->driver != NULL);
+ float cvalue = 0.0f;
+
+ /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime"
* since drivers essentially act as alternative input (i.e. in place of 'time') for F-Curves
*/
if (fcu->driver) {
/* evaltime now serves as input for the curve */
- evaltime = evaluate_driver(fcu->driver, evaltime);
-
+ evaltime = evaluate_driver(anim_rna, fcu->driver, evaltime);
+
/* only do a default 1-1 mapping if it's unlikely that anything else will set a value... */
if (fcu->totvert == 0) {
FModifier *fcm;
bool do_linear = true;
-
- /* out-of-range F-Modifiers will block, as will those which just plain overwrite the values
+
+ /* out-of-range F-Modifiers will block, as will those which just plain overwrite the values
* XXX: additive is a bit more dicey; it really depends then if things are in range or not...
*/
for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) {
@@ -2634,7 +2675,7 @@ float evaluate_fcurve(FCurve *fcu, float evaltime)
do_linear = false;
}
}
-
+
/* only copy over results if none of the modifiers disagreed with this */
if (do_linear) {
cvalue = evaltime;
@@ -2642,36 +2683,11 @@ float evaluate_fcurve(FCurve *fcu, float evaltime)
}
}
- /* evaluate modifiers which modify time to evaluate the base curve at */
- storage = evaluate_fmodifiers_storage_new(&fcu->modifiers);
- devaltime = evaluate_time_fmodifiers(storage, &fcu->modifiers, fcu, cvalue, evaltime);
-
- /* evaluate curve-data
- * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying
- * F-Curve modifier on the stack requested the curve to be evaluated at
- */
- if (fcu->bezt)
- cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime);
- else if (fcu->fpt)
- cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime);
-
- /* evaluate modifiers */
- evaluate_value_fmodifiers(storage, &fcu->modifiers, fcu, &cvalue, devaltime);
-
- evaluate_fmodifiers_storage_free(storage);
-
- /* if curve can only have integral values, perform truncation (i.e. drop the decimal part)
- * here so that the curve can be sampled correctly
- */
- if (fcu->flag & FCURVE_INT_VALUES)
- cvalue = floorf(cvalue + 0.5f);
-
- /* return evaluated value */
- return cvalue;
+ return evaluate_fcurve_ex(fcu, evaltime, cvalue);
}
/* Calculate the value of the given F-Curve at the given frame, and set its curval */
-float calculate_fcurve(FCurve *fcu, float evaltime)
+float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime)
{
/* only calculate + set curval (overriding the existing value) if curve has
* any data which warrants this...
@@ -2680,7 +2696,13 @@ float calculate_fcurve(FCurve *fcu, float evaltime)
list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE))
{
/* calculate and set curval (evaluates driver too if necessary) */
- float curval = evaluate_fcurve(fcu, evaltime);
+ float curval;
+ if (fcu->driver) {
+ curval = evaluate_fcurve_driver(anim_rna, fcu, evaltime);
+ }
+ else {
+ curval = evaluate_fcurve(fcu, evaltime);
+ }
fcu->curval = curval; /* debug display only, not thread safe! */
return curval;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 8621da0d42e..e4bac0a947a 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -44,10 +44,12 @@
#include "DNA_gpencil_types.h"
#include "DNA_userdef_types.h"
+#include "DNA_scene_types.h"
#include "BKE_animsys.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_colortools.h"
#include "BKE_library.h"
#include "BKE_main.h"
@@ -57,76 +59,155 @@
/* --------- Memory Management ------------ */
+/* free stroke, doesn't unlink from any listbase */
+void BKE_gpencil_free_stroke(bGPDstroke *gps)
+{
+ if (gps == NULL) {
+ return;
+ }
+
+ /* free stroke memory arrays, then stroke itself */
+ if (gps->points)
+ MEM_freeN(gps->points);
+ if (gps->triangles)
+ MEM_freeN(gps->triangles);
+
+ MEM_freeN(gps);
+}
+
/* Free strokes belonging to a gp-frame */
-bool free_gpencil_strokes(bGPDframe *gpf)
+bool BKE_gpencil_free_strokes(bGPDframe *gpf)
{
- bGPDstroke *gps, *gpsn;
+ bGPDstroke *gps_next;
bool changed = (BLI_listbase_is_empty(&gpf->strokes) == false);
/* free strokes */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- /* free stroke memory arrays, then stroke itself */
- if (gps->points) MEM_freeN(gps->points);
- if (gps->triangles) MEM_freeN(gps->triangles);
- BLI_freelinkN(&gpf->strokes, gps);
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps_next) {
+ gps_next = gps->next;
+ BKE_gpencil_free_stroke(gps);
}
+ BLI_listbase_clear(&gpf->strokes);
return changed;
}
/* Free all of a gp-layer's frames */
-void free_gpencil_frames(bGPDlayer *gpl)
+void BKE_gpencil_free_frames(bGPDlayer *gpl)
{
- bGPDframe *gpf, *gpfn;
+ bGPDframe *gpf_next;
/* error checking */
if (gpl == NULL) return;
/* free frames */
- for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
- gpfn = gpf->next;
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf_next) {
+ gpf_next = gpf->next;
/* free strokes and their associated memory */
- free_gpencil_strokes(gpf);
+ BKE_gpencil_free_strokes(gpf);
BLI_freelinkN(&gpl->frames, gpf);
}
gpl->actframe = NULL;
}
+/* Free all of a gp-colors */
+static void free_gpencil_colors(bGPDpalette *palette)
+{
+ /* error checking */
+ if (palette == NULL) {
+ return;
+ }
+
+ /* free colors */
+ BLI_freelistN(&palette->colors);
+}
+
+/* Free all of the gp-palettes and colors */
+void BKE_gpencil_free_palettes(ListBase *list)
+{
+ bGPDpalette *palette_next;
+
+ /* error checking */
+ if (list == NULL) {
+ return;
+ }
+
+ /* delete palettes */
+ for (bGPDpalette *palette = list->first; palette; palette = palette_next) {
+ palette_next = palette->next;
+ /* free palette colors */
+ free_gpencil_colors(palette);
+
+ MEM_freeN(palette);
+ }
+ BLI_listbase_clear(list);
+}
+
+/* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */
+void BKE_gpencil_free_brushes(ListBase *list)
+{
+ bGPDbrush *brush_next;
+
+ /* error checking */
+ if (list == NULL) {
+ return;
+ }
+
+ /* delete brushes */
+ for (bGPDbrush *brush = list->first; brush; brush = brush_next) {
+ brush_next = brush->next;
+ /* free curves */
+ if (brush->cur_sensitivity) {
+ curvemapping_free(brush->cur_sensitivity);
+ }
+ if (brush->cur_strength) {
+ curvemapping_free(brush->cur_strength);
+ }
+ if (brush->cur_jitter) {
+ curvemapping_free(brush->cur_jitter);
+ }
+
+ MEM_freeN(brush);
+ }
+ BLI_listbase_clear(list);
+}
+
/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */
-void free_gpencil_layers(ListBase *list)
+void BKE_gpencil_free_layers(ListBase *list)
{
- bGPDlayer *gpl, *gpln;
+ bGPDlayer *gpl_next;
/* error checking */
if (list == NULL) return;
/* delete layers */
- for (gpl = list->first; gpl; gpl = gpln) {
- gpln = gpl->next;
+ for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) {
+ gpl_next = gpl->next;
/* free layers and their data */
- free_gpencil_frames(gpl);
+ BKE_gpencil_free_frames(gpl);
BLI_freelinkN(list, gpl);
}
}
-/* Free all of GPencil datablock's related data, but not the block itself */
/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */
-void BKE_gpencil_free(bGPdata *gpd)
+void BKE_gpencil_free(bGPdata *gpd, bool free_palettes)
{
BKE_animdata_free(&gpd->id, false);
/* free layers */
- free_gpencil_layers(&gpd->layers);
+ BKE_gpencil_free_layers(&gpd->layers);
+
+ /* free palettes */
+ if (free_palettes) {
+ BKE_gpencil_free_palettes(&gpd->palettes);
+ }
}
/* -------- Container Creation ---------- */
/* add a new gp-frame to the given layer */
-bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
+bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
{
bGPDframe *gpf = NULL, *gf = NULL;
short state = 0;
@@ -178,9 +259,9 @@ bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
}
/* add a copy of the active gp-frame to the given layer */
-bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
+bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
{
- bGPDframe *new_frame, *gpf;
+ bGPDframe *new_frame;
bool found = false;
/* Error checking/handling */
@@ -190,14 +271,14 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
}
else if (gpl->actframe == NULL) {
/* no active frame, so just create a new one from scratch */
- return gpencil_frame_addnew(gpl, cframe);
+ return BKE_gpencil_frame_addnew(gpl, cframe);
}
/* Create a copy of the frame */
- new_frame = gpencil_frame_duplicate(gpl->actframe);
+ new_frame = BKE_gpencil_frame_duplicate(gpl->actframe);
/* Find frame to insert it before */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
if (gpf->framenum > cframe) {
/* Add it here */
BLI_insertlinkbefore(&gpl->frames, gpf, new_frame);
@@ -209,7 +290,7 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
/* This only happens when we're editing with framelock on...
* - Delete the new frame and don't do anything else here...
*/
- free_gpencil_strokes(new_frame);
+ BKE_gpencil_free_strokes(new_frame);
MEM_freeN(new_frame);
new_frame = NULL;
@@ -233,7 +314,7 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
}
/* add a new gp-layer and make it the active layer */
-bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
+bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
{
bGPDlayer *gpl;
@@ -249,8 +330,11 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
/* set basic settings */
copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
- gpl->thickness = 3;
-
+ /* Since GPv2 thickness must be 0 */
+ gpl->thickness = 0;
+
+ gpl->opacity = 1.0f;
+
/* onion-skinning settings */
if (gpd->flag & GP_DATA_SHOW_ONIONSKINS)
gpl->flag |= GP_LAYER_ONIONSKIN;
@@ -263,23 +347,280 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
/* high quality fill by default */
gpl->flag |= GP_LAYER_HQ_FILL;
- /* default smooth iterations */
- gpl->draw_smoothlvl = 1;
-
/* auto-name */
BLI_strncpy(gpl->info, name, sizeof(gpl->info));
BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info));
/* make this one the active one */
if (setactive)
- gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_setactive(gpd, gpl);
/* return layer */
return gpl;
}
+/* add a new gp-palette and make it the active */
+bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive)
+{
+ bGPDpalette *palette;
+
+ /* check that list is ok */
+ if (gpd == NULL) {
+ return NULL;
+ }
+
+ /* allocate memory and add to end of list */
+ palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette");
+
+ /* add to datablock */
+ BLI_addtail(&gpd->palettes, palette);
+
+ /* set basic settings */
+ /* auto-name */
+ BLI_strncpy(palette->info, name, sizeof(palette->info));
+ BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info),
+ sizeof(palette->info));
+
+ /* make this one the active one */
+ if (setactive) {
+ BKE_gpencil_palette_setactive(gpd, palette);
+ }
+
+ /* return palette */
+ return palette;
+}
+
+/* create a set of default drawing brushes with predefined presets */
+void BKE_gpencil_brush_init_presets(ToolSettings *ts)
+{
+ bGPDbrush *brush;
+ /* Basic brush */
+ brush = BKE_gpencil_brush_addnew(ts, "Basic", true);
+ brush->thickness = 3.0f;
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
+ brush->draw_sensitivity = 1.0f;
+ brush->flag |= GP_BRUSH_USE_PRESSURE;
+
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
+ brush->draw_strength = 1.0f;
+ brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE;
+
+ brush->draw_random_press = 0.0f;
+
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ brush->draw_angle = 0.0f;
+ brush->draw_angle_factor = 0.0f;
+
+ brush->draw_smoothfac = 0.0f;
+ brush->draw_smoothlvl = 1;
+ brush->sublevel = 0;
+ brush->draw_random_sub = 0.0f;
+
+ /* Pencil brush */
+ brush = BKE_gpencil_brush_addnew(ts, "Pencil", false);
+ brush->thickness = 7.0f;
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
+ brush->draw_sensitivity = 1.0f;
+ brush->flag |= GP_BRUSH_USE_PRESSURE;
+
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
+ brush->draw_strength = 0.7f;
+ brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
+
+ brush->draw_random_press = 0.0f;
+
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ brush->draw_angle = 0.0f;
+ brush->draw_angle_factor = 0.0f;
+
+ brush->draw_smoothfac = 1.0f;
+ brush->draw_smoothlvl = 2;
+ brush->sublevel = 2;
+ brush->draw_random_sub = 0.0f;
+
+ /* Ink brush */
+ brush = BKE_gpencil_brush_addnew(ts, "Ink", false);
+ brush->thickness = 7.0f;
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
+ brush->draw_sensitivity = 1.6f;
+ brush->flag |= GP_BRUSH_USE_PRESSURE;
+
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
+ brush->draw_strength = 1.0f;
+ brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE;
+
+ brush->draw_random_press = 0.0f;
+
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ brush->draw_angle = 0.0f;
+ brush->draw_angle_factor = 0.0f;
+
+ brush->draw_smoothfac = 1.1f;
+ brush->draw_smoothlvl = 2;
+ brush->sublevel = 2;
+ brush->draw_random_sub = 0.0f;
+
+ /* Ink Noise brush */
+ brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false);
+ brush->thickness = 6.0f;
+ brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE;
+ brush->draw_sensitivity = 1.611f;
+ brush->flag |= GP_BRUSH_USE_PRESSURE;
+
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
+ brush->draw_strength = 1.0f;
+ brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
+
+ brush->draw_random_press = 1.0f;
+
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ brush->draw_angle = 0.0f;
+ brush->draw_angle_factor = 0.0f;
+
+ brush->draw_smoothfac = 1.1f;
+ brush->draw_smoothlvl = 2;
+ brush->sublevel = 2;
+ brush->draw_random_sub = 0.0f;
+
+ /* Marker brush */
+ brush = BKE_gpencil_brush_addnew(ts, "Marker", false);
+ brush->thickness = 10.0f;
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
+ brush->draw_sensitivity = 2.0f;
+ brush->flag &= ~GP_BRUSH_USE_PRESSURE;
+
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
+ brush->draw_strength = 1.0f;
+ brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE;
+
+ brush->draw_random_press = 0.0f;
+
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ brush->draw_angle = M_PI_4; /* 45 degrees */
+ brush->draw_angle_factor = 1.0f;
+
+ brush->draw_smoothfac = 1.0f;
+ brush->draw_smoothlvl = 2;
+ brush->sublevel = 2;
+ brush->draw_random_sub = 0.0f;
+
+ /* Crayon brush */
+ brush = BKE_gpencil_brush_addnew(ts, "Crayon", false);
+ brush->thickness = 10.0f;
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
+ brush->draw_sensitivity = 3.0f;
+ brush->flag &= ~GP_BRUSH_USE_PRESSURE;
+
+ brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
+ brush->draw_strength = 0.140f;
+ brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
+
+ brush->draw_random_press = 0.0f;
+
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ brush->draw_angle = 0.0f;
+ brush->draw_angle_factor = 0.0f;
+
+ brush->draw_smoothfac = 0.0f;
+ brush->draw_smoothlvl = 1;
+ brush->sublevel = 2;
+ brush->draw_random_sub = 0.5f;
+
+}
+
+/* add a new gp-brush and make it the active */
+bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive)
+{
+ bGPDbrush *brush;
+
+ /* check that list is ok */
+ if (ts == NULL) {
+ return NULL;
+ }
+
+ /* allocate memory and add to end of list */
+ brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush");
+
+ /* add to datablock */
+ BLI_addtail(&ts->gp_brushes, brush);
+
+ /* set basic settings */
+ brush->thickness = 3;
+ brush->draw_smoothlvl = 1;
+ brush->flag |= GP_BRUSH_USE_PRESSURE;
+ brush->draw_sensitivity = 1.0f;
+ brush->draw_strength = 1.0f;
+ brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
+ brush->draw_jitter = 0.0f;
+ brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+
+ /* curves */
+ brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+
+ /* auto-name */
+ BLI_strncpy(brush->info, name, sizeof(brush->info));
+ BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info));
+
+ /* make this one the active one */
+ if (setactive) {
+ BKE_gpencil_brush_setactive(ts, brush);
+ }
+
+ /* return brush */
+ return brush;
+}
+
+/* add a new gp-palettecolor and make it the active */
+bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive)
+{
+ bGPDpalettecolor *palcolor;
+
+ /* check that list is ok */
+ if (palette == NULL) {
+ return NULL;
+ }
+
+ /* allocate memory and add to end of list */
+ palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor");
+
+ /* add to datablock */
+ BLI_addtail(&palette->colors, palcolor);
+
+ /* set basic settings */
+ palcolor->flag |= PC_COLOR_HQ_FILL;
+ copy_v4_v4(palcolor->color, U.gpencil_new_layer_col);
+ ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f);
+
+ /* auto-name */
+ BLI_strncpy(palcolor->info, name, sizeof(palcolor->info));
+ BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info),
+ sizeof(palcolor->info));
+
+ /* make this one the active one */
+ if (setactive) {
+ BKE_gpencil_palettecolor_setactive(palette, palcolor);
+ }
+
+ /* return palette color */
+ return palcolor;
+}
+
/* add a new gp-datablock */
-bGPdata *gpencil_data_addnew(const char name[])
+bGPdata *BKE_gpencil_data_addnew(const char name[])
{
bGPdata *gpd;
@@ -300,94 +641,157 @@ bGPdata *gpencil_data_addnew(const char name[])
/* -------- Data Duplication ---------- */
/* make a copy of a given gpencil frame */
-bGPDframe *gpencil_frame_duplicate(bGPDframe *src)
+bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src)
{
- bGPDstroke *gps, *gpsd;
- bGPDframe *dst;
+ bGPDstroke *gps_dst;
+ bGPDframe *gpf_dst;
/* error checking */
- if (src == NULL)
+ if (gpf_src == NULL) {
return NULL;
+ }
/* make a copy of the source frame */
- dst = MEM_dupallocN(src);
- dst->prev = dst->next = NULL;
+ gpf_dst = MEM_dupallocN(gpf_src);
+ gpf_dst->prev = gpf_dst->next = NULL;
/* copy strokes */
- BLI_listbase_clear(&dst->strokes);
- for (gps = src->strokes.first; gps; gps = gps->next) {
+ BLI_listbase_clear(&gpf_dst->strokes);
+ for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) {
/* make copy of source stroke, then adjust pointer to points too */
- gpsd = MEM_dupallocN(gps);
- gpsd->points = MEM_dupallocN(gps->points);
- gpsd->triangles = MEM_dupallocN(gps->triangles);
- gpsd->flag |= GP_STROKE_RECALC_CACHES;
- BLI_addtail(&dst->strokes, gpsd);
+ gps_dst = MEM_dupallocN(gps_src);
+ gps_dst->points = MEM_dupallocN(gps_src->points);
+ gps_dst->triangles = MEM_dupallocN(gps_src->triangles);
+ gps_dst->flag |= GP_STROKE_RECALC_CACHES;
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
}
/* return new frame */
- return dst;
+ return gpf_dst;
+}
+
+/* make a copy of a given gpencil brush */
+bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src)
+{
+ bGPDbrush *brush_dst;
+
+ /* error checking */
+ if (brush_src == NULL) {
+ return NULL;
+ }
+
+ /* make a copy of source brush */
+ brush_dst = MEM_dupallocN(brush_src);
+ brush_dst->prev = brush_dst->next = NULL;
+ /* make a copy of curves */
+ brush_dst->cur_sensitivity = curvemapping_copy(brush_src->cur_sensitivity);
+ brush_dst->cur_strength = curvemapping_copy(brush_src->cur_strength);
+ brush_dst->cur_jitter = curvemapping_copy(brush_src->cur_jitter);
+
+ /* return new brush */
+ return brush_dst;
}
+/* make a copy of a given gpencil palette */
+bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src)
+{
+ bGPDpalette *palette_dst;
+ const bGPDpalettecolor *palcolor_src;
+ bGPDpalettecolor *palcolord_dst;
+
+ /* error checking */
+ if (palette_src == NULL) {
+ return NULL;
+ }
+
+ /* make a copy of source palette */
+ palette_dst = MEM_dupallocN(palette_src);
+ palette_dst->prev = palette_dst->next = NULL;
+
+ /* copy colors */
+ BLI_listbase_clear(&palette_dst->colors);
+ for (palcolor_src = palette_src->colors.first; palcolor_src; palcolor_src = palcolor_src->next) {
+ /* make a copy of source */
+ palcolord_dst = MEM_dupallocN(palcolor_src);
+ BLI_addtail(&palette_dst->colors, palcolord_dst);
+ }
+
+ /* return new palette */
+ return palette_dst;
+}
/* make a copy of a given gpencil layer */
-bGPDlayer *gpencil_layer_duplicate(bGPDlayer *src)
+bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src)
{
- bGPDframe *gpf, *gpfd;
- bGPDlayer *dst;
+ const bGPDframe *gpf_src;
+ bGPDframe *gpf_dst;
+ bGPDlayer *gpl_dst;
/* error checking */
- if (src == NULL)
+ if (gpl_src == NULL) {
return NULL;
+ }
/* make a copy of source layer */
- dst = MEM_dupallocN(src);
- dst->prev = dst->next = NULL;
+ gpl_dst = MEM_dupallocN(gpl_src);
+ gpl_dst->prev = gpl_dst->next = NULL;
/* copy frames */
- BLI_listbase_clear(&dst->frames);
- for (gpf = src->frames.first; gpf; gpf = gpf->next) {
+ BLI_listbase_clear(&gpl_dst->frames);
+ for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) {
/* make a copy of source frame */
- gpfd = gpencil_frame_duplicate(gpf);
- BLI_addtail(&dst->frames, gpfd);
+ gpf_dst = BKE_gpencil_frame_duplicate(gpf_src);
+ BLI_addtail(&gpl_dst->frames, gpf_dst);
/* if source frame was the current layer's 'active' frame, reassign that too */
- if (gpf == dst->actframe)
- dst->actframe = gpfd;
+ if (gpf_src == gpl_dst->actframe)
+ gpl_dst->actframe = gpf_dst;
}
/* return new layer */
- return dst;
+ return gpl_dst;
}
/* make a copy of a given gpencil datablock */
-bGPdata *gpencil_data_duplicate(Main *bmain, bGPdata *src, bool internal_copy)
+bGPdata *BKE_gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_copy)
{
- bGPDlayer *gpl, *gpld;
- bGPdata *dst;
-
+ const bGPDlayer *gpl_src;
+ bGPDlayer *gpl_dst;
+ bGPdata *gpd_dst;
+
/* error checking */
- if (src == NULL)
+ if (gpd_src == NULL) {
return NULL;
+ }
/* make a copy of the base-data */
if (internal_copy) {
/* make a straight copy for undo buffers used during stroke drawing */
- dst = MEM_dupallocN(src);
+ gpd_dst = MEM_dupallocN(gpd_src);
}
else {
/* make a copy when others use this */
- dst = BKE_libblock_copy(bmain, &src->id);
+ gpd_dst = BKE_libblock_copy(bmain, &gpd_src->id);
}
/* copy layers */
- BLI_listbase_clear(&dst->layers);
- for (gpl = src->layers.first; gpl; gpl = gpl->next) {
+ BLI_listbase_clear(&gpd_dst->layers);
+ for (gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) {
/* make a copy of source layer and its data */
- gpld = gpencil_layer_duplicate(gpl);
- BLI_addtail(&dst->layers, gpld);
+ gpl_dst = BKE_gpencil_layer_duplicate(gpl_src);
+ BLI_addtail(&gpd_dst->layers, gpl_dst);
+ }
+ if (!internal_copy) {
+ /* copy palettes */
+ bGPDpalette *palette_src, *palette_dst;
+ BLI_listbase_clear(&gpd_dst->palettes);
+ for (palette_src = gpd_src->palettes.first; palette_src; palette_src = palette_src->next) {
+ palette_dst = BKE_gpencil_palette_duplicate(palette_src);
+ BLI_addtail(&gpd_dst->palettes, palette_dst);
+ }
}
/* return new */
- return dst;
+ return gpd_dst;
}
void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local)
@@ -398,7 +802,7 @@ void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local)
/* -------- GP-Stroke API --------- */
/* ensure selection status of stroke is in sync with its points */
-void gpencil_stroke_sync_selection(bGPDstroke *gps)
+void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
{
bGPDspoint *pt;
int i;
@@ -423,7 +827,7 @@ void gpencil_stroke_sync_selection(bGPDstroke *gps)
/* -------- GP-Frame API ---------- */
/* delete the last stroke of the given frame */
-void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
+void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
{
bGPDstroke *gps = (gpf) ? gpf->strokes.last : NULL;
int cfra = (gpf) ? gpf->framenum : 0; /* assume that the current frame was not locked */
@@ -439,8 +843,8 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
/* if frame has no strokes after this, delete it */
if (BLI_listbase_is_empty(&gpf->strokes)) {
- gpencil_layer_delframe(gpl, gpf);
- gpencil_layer_getframe(gpl, cfra, 0);
+ BKE_gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_getframe(gpl, cfra, 0);
}
}
@@ -458,7 +862,7 @@ bool gpencil_layer_is_editable(const bGPDlayer *gpl)
/* Opacity must be sufficiently high that it is still "visible"
* Otherwise, it's not really "visible" to the user, so no point editing...
*/
- if ((gpl->color[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH)) {
+ if (gpl->opacity > GPENCIL_ALPHA_OPACITY_THRESH) {
return true;
}
}
@@ -488,7 +892,7 @@ bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe)
* - this sets the layer's actframe var (if allowed to)
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
*/
-bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
+bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
{
bGPDframe *gpf = NULL;
short found = 0;
@@ -527,9 +931,9 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode
if ((found) && (gpf->framenum == cframe))
gpl->actframe = gpf;
else if (addnew == GP_GETFRAME_ADD_COPY)
- gpl->actframe = gpencil_frame_addcopy(gpl, cframe);
+ gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe);
else
- gpl->actframe = gpencil_frame_addnew(gpl, cframe);
+ gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe);
}
else if (found)
gpl->actframe = gpf;
@@ -549,9 +953,9 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode
if ((found) && (gpf->framenum == cframe))
gpl->actframe = gpf;
else if (addnew == GP_GETFRAME_ADD_COPY)
- gpl->actframe = gpencil_frame_addcopy(gpl, cframe);
+ gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe);
else
- gpl->actframe = gpencil_frame_addnew(gpl, cframe);
+ gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe);
}
else if (found)
gpl->actframe = gpf;
@@ -588,7 +992,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode
if ((found) && (gpf->framenum == cframe))
gpl->actframe = gpf;
else
- gpl->actframe = gpencil_frame_addnew(gpl, cframe);
+ gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe);
}
else if (found)
gpl->actframe = gpf;
@@ -601,7 +1005,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode
else {
/* currently no frames (add if allowed to) */
if (addnew)
- gpl->actframe = gpencil_frame_addnew(gpl, cframe);
+ gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe);
else {
/* don't do anything... this may be when no frames yet! */
/* gpl->actframe should still be NULL */
@@ -613,7 +1017,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode
}
/* delete the given frame from a layer */
-bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf)
+bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf)
{
bool changed = false;
@@ -630,14 +1034,14 @@ bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf)
gpl->actframe = NULL;
/* free the frame and its data */
- changed = free_gpencil_strokes(gpf);
+ changed = BKE_gpencil_free_strokes(gpf);
BLI_freelinkN(&gpl->frames, gpf);
return changed;
}
/* get the active gp-layer for editing */
-bGPDlayer *gpencil_layer_getactive(bGPdata *gpd)
+bGPDlayer *BKE_gpencil_layer_getactive(bGPdata *gpd)
{
bGPDlayer *gpl;
@@ -656,7 +1060,7 @@ bGPDlayer *gpencil_layer_getactive(bGPdata *gpd)
}
/* set the active gp-layer */
-void gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active)
+void BKE_gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active)
{
bGPDlayer *gpl;
@@ -673,15 +1077,255 @@ void gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active)
}
/* delete the active gp-layer */
-void gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl)
+void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl)
{
/* error checking */
if (ELEM(NULL, gpd, gpl))
return;
/* free layer */
- free_gpencil_frames(gpl);
+ BKE_gpencil_free_frames(gpl);
BLI_freelinkN(&gpd->layers, gpl);
}
/* ************************************************** */
+/* get the active gp-brush for editing */
+bGPDbrush *BKE_gpencil_brush_getactive(ToolSettings *ts)
+{
+ bGPDbrush *brush;
+
+ /* error checking */
+ if (ELEM(NULL, ts, ts->gp_brushes.first)) {
+ return NULL;
+ }
+
+ /* loop over brushes until found (assume only one active) */
+ for (brush = ts->gp_brushes.first; brush; brush = brush->next) {
+ if (brush->flag & GP_BRUSH_ACTIVE) {
+ return brush;
+ }
+ }
+
+ /* no active brush found */
+ return NULL;
+}
+
+/* set the active gp-brush */
+void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active)
+{
+ bGPDbrush *brush;
+
+ /* error checking */
+ if (ELEM(NULL, ts, ts->gp_brushes.first, active)) {
+ return;
+ }
+
+ /* loop over brushes deactivating all */
+ for (brush = ts->gp_brushes.first; brush; brush = brush->next) {
+ brush->flag &= ~GP_BRUSH_ACTIVE;
+ }
+
+ /* set as active one */
+ active->flag |= GP_BRUSH_ACTIVE;
+}
+
+/* delete the active gp-brush */
+void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush)
+{
+ /* error checking */
+ if (ELEM(NULL, ts, brush)) {
+ return;
+ }
+
+ /* free curves */
+ if (brush->cur_sensitivity) {
+ curvemapping_free(brush->cur_sensitivity);
+ }
+ if (brush->cur_strength) {
+ curvemapping_free(brush->cur_strength);
+ }
+ if (brush->cur_jitter) {
+ curvemapping_free(brush->cur_jitter);
+ }
+
+ /* free */
+ BLI_freelinkN(&ts->gp_brushes, brush);
+}
+
+/* ************************************************** */
+/* get the active gp-palette for editing */
+bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd)
+{
+ bGPDpalette *palette;
+
+ /* error checking */
+ if (ELEM(NULL, gpd, gpd->palettes.first)) {
+ return NULL;
+ }
+
+ /* loop over palettes until found (assume only one active) */
+ for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ if (palette->flag & PL_PALETTE_ACTIVE)
+ return palette;
+ }
+
+ /* no active palette found */
+ return NULL;
+}
+
+/* set the active gp-palette */
+void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active)
+{
+ bGPDpalette *palette;
+
+ /* error checking */
+ if (ELEM(NULL, gpd, gpd->palettes.first, active)) {
+ return;
+ }
+
+ /* loop over palettes deactivating all */
+ for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ palette->flag &= ~PL_PALETTE_ACTIVE;
+ }
+
+ /* set as active one */
+ active->flag |= PL_PALETTE_ACTIVE;
+ /* force color recalc */
+ BKE_gpencil_palette_change_strokes(gpd);
+}
+
+/* delete the active gp-palette */
+void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette)
+{
+ /* error checking */
+ if (ELEM(NULL, gpd, palette)) {
+ return;
+ }
+
+ /* free colors */
+ free_gpencil_colors(palette);
+ BLI_freelinkN(&gpd->palettes, palette);
+ /* force color recalc */
+ BKE_gpencil_palette_change_strokes(gpd);
+}
+
+/* Set all strokes to recalc the palette color */
+void BKE_gpencil_palette_change_strokes(bGPdata *gpd)
+{
+ bGPDlayer *gpl;
+ bGPDframe *gpf;
+ bGPDstroke *gps;
+
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ gps->flag |= GP_STROKE_RECALC_COLOR;
+ }
+ }
+ }
+}
+
+
+/* get the active gp-palettecolor for editing */
+bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette)
+{
+ bGPDpalettecolor *palcolor;
+
+ /* error checking */
+ if (ELEM(NULL, palette, palette->colors.first)) {
+ return NULL;
+ }
+
+ /* loop over colors until found (assume only one active) */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ if (palcolor->flag & PC_COLOR_ACTIVE) {
+ return palcolor;
+ }
+ }
+
+ /* no active color found */
+ return NULL;
+}
+/* get the gp-palettecolor looking for name */
+bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name)
+{
+ /* error checking */
+ if (ELEM(NULL, palette, name)) {
+ return NULL;
+ }
+
+ return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info));
+}
+
+/* Change color name in all strokes */
+void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname)
+{
+ bGPDlayer *gpl;
+ bGPDframe *gpf;
+ bGPDstroke *gps;
+
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (STREQ(gps->colorname, oldname)) {
+ strcpy(gps->colorname, newname);
+ }
+ }
+ }
+ }
+
+}
+
+/* Delete all strokes of the color */
+void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name)
+{
+ bGPDlayer *gpl;
+ bGPDframe *gpf;
+ bGPDstroke *gps, *gpsn;
+
+ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+
+ if (STREQ(gps->colorname, name)) {
+ if (gps->points) MEM_freeN(gps->points);
+ if (gps->triangles) MEM_freeN(gps->triangles);
+ BLI_freelinkN(&gpf->strokes, gps);
+ }
+ }
+ }
+ }
+
+}
+
+/* set the active gp-palettecolor */
+void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active)
+{
+ bGPDpalettecolor *palcolor;
+
+ /* error checking */
+ if (ELEM(NULL, palette, palette->colors.first, active)) {
+ return;
+ }
+
+ /* loop over colors deactivating all */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~PC_COLOR_ACTIVE;
+ }
+
+ /* set as active one */
+ active->flag |= PC_COLOR_ACTIVE;
+}
+
+/* delete the active gp-palettecolor */
+void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor)
+{
+ /* error checking */
+ if (ELEM(NULL, palette, palcolor)) {
+ return;
+ }
+
+ /* free */
+ BLI_freelinkN(&palette->colors, palcolor);
+}
diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c
index f58d26f47dc..9b011dbb003 100644
--- a/source/blender/blenkernel/intern/group.c
+++ b/source/blender/blenkernel/intern/group.c
@@ -97,10 +97,7 @@ Group *BKE_group_copy(Main *bmain, Group *group)
/* Do not copy group's preview (same behavior as for objects). */
groupn->preview = NULL;
- if (ID_IS_LINKED_DATABLOCK(group)) {
- BKE_id_expand_local(&groupn->id);
- BKE_id_lib_local_paths(bmain, group->id.lib, &groupn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &group->id, &groupn->id);
return groupn;
}
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c
index 90b3713e47c..70d037d85f3 100644
--- a/source/blender/blenkernel/intern/idcode.c
+++ b/source/blender/blenkernel/intern/idcode.c
@@ -60,6 +60,7 @@ static IDType idtypes[] = {
{ ID_AR, "Armature", "armatures", BLT_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_BR, "Brush", "brushes", BLT_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CA, "Camera", "cameras", BLT_I18NCONTEXT_ID_CAMERA, IDTYPE_FLAGS_ISLINKABLE },
+ { ID_CF, "CacheFile", "cache_files", BLT_I18NCONTEXT_ID_CACHEFILE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */
{ ID_GR, "Group", "groups", BLT_I18NCONTEXT_ID_GROUP, IDTYPE_FLAGS_ISLINKABLE },
@@ -184,6 +185,7 @@ int BKE_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(AR);
CASE_IDFILTER(BR);
CASE_IDFILTER(CA);
+ CASE_IDFILTER(CF);
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
@@ -227,6 +229,7 @@ short BKE_idcode_from_idfilter(const int idfilter)
CASE_IDFILTER(AR);
CASE_IDFILTER(BR);
CASE_IDFILTER(CA);
+ CASE_IDFILTER(CF);
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
@@ -259,6 +262,56 @@ short BKE_idcode_from_idfilter(const int idfilter)
}
/**
+ * Convert an idcode into an index (e.g. ID_OB -> INDEX_ID_OB).
+ */
+int BKE_idcode_to_index(const short idcode)
+{
+#define CASE_IDINDEX(_id) case ID_##_id: return INDEX_ID_##_id
+
+ switch ((ID_Type)idcode) {
+ CASE_IDINDEX(AC);
+ CASE_IDINDEX(AR);
+ CASE_IDINDEX(BR);
+ CASE_IDINDEX(CA);
+ CASE_IDINDEX(CF);
+ CASE_IDINDEX(CU);
+ CASE_IDINDEX(GD);
+ CASE_IDINDEX(GR);
+ CASE_IDINDEX(IM);
+ CASE_IDINDEX(KE);
+ CASE_IDINDEX(IP);
+ CASE_IDINDEX(LA);
+ CASE_IDINDEX(LI);
+ CASE_IDINDEX(LS);
+ CASE_IDINDEX(LT);
+ CASE_IDINDEX(MA);
+ CASE_IDINDEX(MB);
+ CASE_IDINDEX(MC);
+ CASE_IDINDEX(ME);
+ CASE_IDINDEX(MSK);
+ CASE_IDINDEX(NT);
+ CASE_IDINDEX(OB);
+ CASE_IDINDEX(PA);
+ CASE_IDINDEX(PAL);
+ CASE_IDINDEX(PC);
+ CASE_IDINDEX(SCE);
+ CASE_IDINDEX(SCR);
+ CASE_IDINDEX(SPK);
+ CASE_IDINDEX(SO);
+ CASE_IDINDEX(TE);
+ CASE_IDINDEX(TXT);
+ CASE_IDINDEX(VF);
+ CASE_IDINDEX(WM);
+ CASE_IDINDEX(WO);
+ }
+
+ BLI_assert(0);
+ return -1;
+
+#undef CASE_IDINDEX
+}
+
+/**
* Convert an idcode into a name (plural).
*
* \param idcode: The code to convert.
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 86d010f5f7c..b2641b110f8 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -940,17 +940,18 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
prop->subtype = IDP_STRING_SUB_BYTE;
}
else {
- if (st == NULL) {
+ if (st == NULL || val->string.len <= 1) {
prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
*IDP_String(prop) = '\0';
prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
}
else {
- int stlen = (int)strlen(st) + 1;
- prop->data.pointer = MEM_mallocN((size_t)stlen, "id property string 3");
- prop->len = prop->totallen = stlen;
- memcpy(prop->data.pointer, st, (size_t)stlen);
+ BLI_assert((int)val->string.len <= (int)strlen(st) + 1);
+ prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 3");
+ memcpy(prop->data.pointer, st, (size_t)val->string.len - 1);
+ IDP_String(prop)[val->string.len - 1] = '\0';
+ prop->len = prop->totallen = val->string.len;
}
prop->subtype = IDP_STRING_SUB_UTF8;
}
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index b1fc9e0fbe0..93d835c34e4 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -436,6 +436,7 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src)
Image *BKE_image_copy(Main *bmain, Image *ima)
{
Image *nima = image_alloc(bmain, ima->id.name + 2, ima->source, ima->type);
+ ima->id.newid = &nima->id;
BLI_strncpy(nima->name, ima->name, sizeof(ima->name));
@@ -462,10 +463,7 @@ Image *BKE_image_copy(Main *bmain, Image *ima)
BKE_previewimg_id_copy(&nima->id, &ima->id);
- if (ID_IS_LINKED_DATABLOCK(ima)) {
- BKE_id_expand_local(&nima->id);
- BKE_id_lib_local_paths(bmain, ima->id.lib, &nima->id);
- }
+ BKE_id_copy_ensure_local(bmain, &ima->id, &nima->id);
return nima;
}
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index a524f927cad..6cdeaf5e59b 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -170,10 +170,7 @@ Key *BKE_key_copy(Main *bmain, Key *key)
kb = kb->next;
}
- if (ID_IS_LINKED_DATABLOCK(key)) {
- BKE_id_expand_local(&keyn->id);
- BKE_id_lib_local_paths(bmain, key->id.lib, &keyn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &key->id, &keyn->id);
return keyn;
}
diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c
index 35fcf211b05..e9d039ad480 100644
--- a/source/blender/blenkernel/intern/lamp.c
+++ b/source/blender/blenkernel/intern/lamp.c
@@ -138,10 +138,7 @@ Lamp *BKE_lamp_copy(Main *bmain, Lamp *la)
BKE_previewimg_id_copy(&lan->id, &la->id);
- if (ID_IS_LINKED_DATABLOCK(la)) {
- BKE_id_expand_local(&lan->id);
- BKE_id_lib_local_paths(bmain, la->id.lib, &lan->id);
- }
+ BKE_id_copy_ensure_local(bmain, &la->id, &lan->id);
return lan;
}
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 82b179d4f1c..b0671f33094 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -297,10 +297,7 @@ Lattice *BKE_lattice_copy(Main *bmain, Lattice *lt)
ltn->editlatt = NULL;
- if (ID_IS_LINKED_DATABLOCK(lt)) {
- BKE_id_expand_local(&ltn->id);
- BKE_id_lib_local_paths(bmain, lt->id.lib, &ltn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &lt->id, &ltn->id);
return ltn;
}
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index c31df685136..6d94cd28b31 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -45,6 +45,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
@@ -56,6 +57,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
#include "DNA_node_types.h"
@@ -81,6 +83,7 @@
#include "BKE_bpath.h"
#include "BKE_brush.h"
#include "BKE_camera.h"
+#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
@@ -103,8 +106,10 @@
#include "BKE_main.h"
#include "BKE_mball.h"
#include "BKE_mask.h"
+#include "BKE_movieclip.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_packedFile.h"
#include "BKE_sound.h"
@@ -272,6 +277,17 @@ void BKE_id_expand_local(ID *id)
}
/**
+ * Ensure new (copied) ID is fully made local.
+ */
+void BKE_id_copy_ensure_local(Main *bmain, ID *old_id, ID *new_id)
+{
+ if (ID_IS_LINKED_DATABLOCK(old_id)) {
+ BKE_id_expand_local(new_id);
+ BKE_id_lib_local_paths(bmain, old_id->lib, new_id);
+ }
+}
+
+/**
* Generic 'make local' function, works for most of datablock types...
*/
void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, const bool lib_local)
@@ -308,7 +324,6 @@ void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, c
}
}
}
-
}
/**
@@ -320,16 +335,16 @@ void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, c
*/
bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
{
- if (id->tag & LIB_TAG_INDIRECT)
+ /* We don't care whether ID is directly or indirectly linked in case we are making a whole lib local... */
+ if (!lib_local && (id->tag & LIB_TAG_INDIRECT)) {
return false;
+ }
- switch (GS(id->name)) {
+ switch ((ID_Type)GS(id->name)) {
case ID_SCE:
/* Partially implemented (has no copy...). */
if (!test) BKE_scene_make_local(bmain, (Scene *)id, lib_local);
return true;
- case ID_LI:
- return false; /* can't be linked */
case ID_OB:
if (!test) BKE_object_make_local(bmain, (Object *)id, lib_local);
return true;
@@ -363,15 +378,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
case ID_SPK:
if (!test) BKE_speaker_make_local(bmain, (Speaker *)id, lib_local);
return true;
- case ID_IP:
- return false; /* deprecated */
- case ID_KE:
- return false; /* can't be linked */
case ID_WO:
if (!test) BKE_world_make_local(bmain, (World *)id, lib_local);
return true;
- case ID_SCR:
- return false; /* can't be linked */
case ID_VF:
/* Partially implemented (has no copy...). */
if (!test) BKE_vfont_make_local(bmain, (VFont *)id, lib_local);
@@ -401,17 +410,34 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
case ID_PA:
if (!test) BKE_particlesettings_make_local(bmain, (ParticleSettings *)id, lib_local);
return true;
- case ID_WM:
- return false; /* can't be linked */
case ID_GD:
if (!test) BKE_gpencil_make_local(bmain, (bGPdata *)id, lib_local);
return true;
+ case ID_MC:
+ if (!test) BKE_movieclip_make_local(bmain, (MovieClip *)id, lib_local);
+ return true;
case ID_MSK:
if (!test) BKE_mask_make_local(bmain, (Mask *)id, lib_local);
return true;
case ID_LS:
if (!test) BKE_linestyle_make_local(bmain, (FreestyleLineStyle *)id, lib_local);
return true;
+ case ID_PAL:
+ if (!test) BKE_palette_make_local(bmain, (Palette *)id, lib_local);
+ return true;
+ case ID_PC:
+ if (!test) BKE_paint_curve_make_local(bmain, (PaintCurve *)id, lib_local);
+ return true;
+ case ID_CF:
+ if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local);
+ return true;
+ case ID_SCR:
+ case ID_LI:
+ case ID_KE:
+ case ID_WM:
+ return false; /* can't be linked */
+ case ID_IP:
+ return false; /* deprecated */
}
return false;
@@ -430,11 +456,7 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
/* conventions:
* - make shallow copy, only this ID block
* - id.us of the new ID is set to 1 */
- switch (GS(id->name)) {
- case ID_SCE:
- return false; /* can't be copied from here */
- case ID_LI:
- return false; /* can't be copied from here */
+ switch ((ID_Type)GS(id->name)) {
case ID_OB:
if (!test) *newid = (ID *)BKE_object_copy(bmain, (Object *)id);
return true;
@@ -468,23 +490,15 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
case ID_CA:
if (!test) *newid = (ID *)BKE_camera_copy(bmain, (Camera *)id);
return true;
- case ID_IP:
- return false; /* deprecated */
case ID_KE:
if (!test) *newid = (ID *)BKE_key_copy(bmain, (Key *)id);
return true;
case ID_WO:
if (!test) *newid = (ID *)BKE_world_copy(bmain, (World *)id);
return true;
- case ID_SCR:
- return false; /* can't be copied from here */
- case ID_VF:
- return false; /* not implemented */
case ID_TXT:
if (!test) *newid = (ID *)BKE_text_copy(bmain, (Text *)id);
return true;
- case ID_SO:
- return false; /* not implemented */
case ID_GR:
if (!test) *newid = (ID *)BKE_group_copy(bmain, (Group *)id);
return true;
@@ -503,10 +517,11 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
case ID_PA:
if (!test) *newid = (ID *)BKE_particlesettings_copy(bmain, (ParticleSettings *)id);
return true;
- case ID_WM:
- return false; /* can't be copied from here */
case ID_GD:
- if (!test) *newid = (ID *)gpencil_data_duplicate(bmain, (bGPdata *)id, false);
+ if (!test) *newid = (ID *)BKE_gpencil_data_duplicate(bmain, (bGPdata *)id, false);
+ return true;
+ case ID_MC:
+ if (!test) *newid = (ID *)BKE_movieclip_copy(bmain, (MovieClip *)id);
return true;
case ID_MSK:
if (!test) *newid = (ID *)BKE_mask_copy(bmain, (Mask *)id);
@@ -514,6 +529,25 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
case ID_LS:
if (!test) *newid = (ID *)BKE_linestyle_copy(bmain, (FreestyleLineStyle *)id);
return true;
+ case ID_PAL:
+ if (!test) *newid = (ID *)BKE_palette_copy(bmain, (Palette *)id);
+ return true;
+ case ID_PC:
+ if (!test) *newid = (ID *)BKE_paint_curve_copy(bmain, (PaintCurve *)id);
+ return true;
+ case ID_CF:
+ if (!test) *newid = (ID *)BKE_cachefile_copy(bmain, (CacheFile *)id);
+ return true;
+ case ID_SCE:
+ case ID_LI:
+ case ID_SCR:
+ case ID_WM:
+ return false; /* can't be copied from here */
+ case ID_VF:
+ case ID_SO:
+ return false; /* not implemented */
+ case ID_IP:
+ return false; /* deprecated */
}
return false;
@@ -549,7 +583,7 @@ bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop)
ListBase *which_libbase(Main *mainlib, short type)
{
- switch (type) {
+ switch ((ID_Type)type) {
case ID_SCE:
return &(mainlib->scene);
case ID_LI:
@@ -616,6 +650,8 @@ ListBase *which_libbase(Main *mainlib, short type)
return &(mainlib->palettes);
case ID_PC:
return &(mainlib->paintcurves);
+ case ID_CF:
+ return &(mainlib->cachefiles);
}
return NULL;
}
@@ -717,59 +753,56 @@ void BKE_main_lib_objects_recalc_all(Main *bmain)
* \note MAX_LIBARRAY define should match this code */
int set_listbasepointers(Main *main, ListBase **lb)
{
- int a = 0;
-
/* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last.
* This is important because freeing data decreases usercounts of other datablocks,
* if this data is its self freed it can crash. */
- lb[a++] = &(main->library); /* Libraries may be accessed from pretty much any other ID... */
- lb[a++] = &(main->ipo);
- lb[a++] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */
- lb[a++] = &(main->key);
- lb[a++] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */
- lb[a++] = &(main->nodetree);
- lb[a++] = &(main->image);
- lb[a++] = &(main->tex);
- lb[a++] = &(main->mat);
- lb[a++] = &(main->vfont);
+ lb[INDEX_ID_LI] = &(main->library); /* Libraries may be accessed from pretty much any other ID... */
+ lb[INDEX_ID_IP] = &(main->ipo);
+ lb[INDEX_ID_AC] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */
+ lb[INDEX_ID_KE] = &(main->key);
+ lb[INDEX_ID_GD] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */
+ lb[INDEX_ID_NT] = &(main->nodetree);
+ lb[INDEX_ID_IM] = &(main->image);
+ lb[INDEX_ID_TE] = &(main->tex);
+ lb[INDEX_ID_MA] = &(main->mat);
+ lb[INDEX_ID_VF] = &(main->vfont);
/* Important!: When adding a new object type,
* the specific data should be inserted here
*/
- lb[a++] = &(main->armature);
-
- lb[a++] = &(main->mesh);
- lb[a++] = &(main->curve);
- lb[a++] = &(main->mball);
-
- lb[a++] = &(main->latt);
- lb[a++] = &(main->lamp);
- lb[a++] = &(main->camera);
-
- lb[a++] = &(main->text);
- lb[a++] = &(main->sound);
- lb[a++] = &(main->group);
- lb[a++] = &(main->palettes);
- lb[a++] = &(main->paintcurves);
- lb[a++] = &(main->brush);
- lb[a++] = &(main->particle);
- lb[a++] = &(main->speaker);
-
- lb[a++] = &(main->world);
- lb[a++] = &(main->movieclip);
- lb[a++] = &(main->screen);
- lb[a++] = &(main->object);
- lb[a++] = &(main->linestyle); /* referenced by scenes */
- lb[a++] = &(main->scene);
- lb[a++] = &(main->wm);
- lb[a++] = &(main->mask);
+ lb[INDEX_ID_AR] = &(main->armature);
+
+ lb[INDEX_ID_CF] = &(main->cachefiles);
+ lb[INDEX_ID_ME] = &(main->mesh);
+ lb[INDEX_ID_CU] = &(main->curve);
+ lb[INDEX_ID_MB] = &(main->mball);
+
+ lb[INDEX_ID_LT] = &(main->latt);
+ lb[INDEX_ID_LA] = &(main->lamp);
+ lb[INDEX_ID_CA] = &(main->camera);
+
+ lb[INDEX_ID_TXT] = &(main->text);
+ lb[INDEX_ID_SO] = &(main->sound);
+ lb[INDEX_ID_GR] = &(main->group);
+ lb[INDEX_ID_PAL] = &(main->palettes);
+ lb[INDEX_ID_PC] = &(main->paintcurves);
+ lb[INDEX_ID_BR] = &(main->brush);
+ lb[INDEX_ID_PA] = &(main->particle);
+ lb[INDEX_ID_SPK] = &(main->speaker);
+
+ lb[INDEX_ID_WO] = &(main->world);
+ lb[INDEX_ID_MC] = &(main->movieclip);
+ lb[INDEX_ID_SCR] = &(main->screen);
+ lb[INDEX_ID_OB] = &(main->object);
+ lb[INDEX_ID_LS] = &(main->linestyle); /* referenced by scenes */
+ lb[INDEX_ID_SCE] = &(main->scene);
+ lb[INDEX_ID_WM] = &(main->wm);
+ lb[INDEX_ID_MSK] = &(main->mask);
- lb[a] = NULL;
-
- BLI_assert(a + 1 == MAX_LIBARRAY);
+ lb[INDEX_ID_NULL] = NULL;
- return a;
+ return (MAX_LIBARRAY - 1);
}
/* *********** ALLOC AND FREE *****************
@@ -790,7 +823,7 @@ void *BKE_libblock_alloc_notest(short type)
{
ID *id = NULL;
- switch (type) {
+ switch ((ID_Type)type) {
case ID_SCE:
id = MEM_callocN(sizeof(Scene), "scene");
break;
@@ -890,6 +923,9 @@ void *BKE_libblock_alloc_notest(short type)
case ID_PC:
id = MEM_callocN(sizeof(PaintCurve), "Paint Curve");
break;
+ case ID_CF:
+ id = MEM_callocN(sizeof(CacheFile), "Cache File");
+ break;
}
return id;
}
@@ -927,7 +963,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name)
void BKE_libblock_init_empty(ID *id)
{
/* Note that only ID types that are not valid when filled of zero should have a callback here. */
- switch (GS(id->name)) {
+ switch ((ID_Type)GS(id->name)) {
case ID_SCE:
BKE_scene_init((Scene *)id);
break;
@@ -971,15 +1007,6 @@ void BKE_libblock_init_empty(ID *id)
case ID_CA:
BKE_camera_init((Camera *)id);
break;
- case ID_IP:
- /* Should not be needed - animation from lib pre-2.5 is broken anyway. */
- BLI_assert(0);
- break;
- case ID_KE:
- /* Shapekeys are a complex topic too - they depend on their 'user' data type...
- * They are not linkable, though, so it should never reach here anyway. */
- BLI_assert(0);
- break;
case ID_WO:
BKE_world_init((World *)id);
break;
@@ -1016,10 +1043,6 @@ void BKE_libblock_init_empty(ID *id)
case ID_PC:
/* Nothing to do. */
break;
- case ID_WM:
- /* We should never reach this. */
- BLI_assert(0);
- break;
case ID_GD:
/* Nothing to do. */
break;
@@ -1029,6 +1052,24 @@ void BKE_libblock_init_empty(ID *id)
case ID_LS:
BKE_linestyle_init((FreestyleLineStyle *)id);
break;
+ case ID_CF:
+ BKE_cachefile_init((CacheFile *)id);
+ break;
+ case ID_KE:
+ /* Shapekeys are a complex topic too - they depend on their 'user' data type...
+ * They are not linkable, though, so it should never reach here anyway. */
+ BLI_assert(0);
+ break;
+ case ID_WM:
+ /* We should never reach this. */
+ BLI_assert(0);
+ break;
+ case ID_IP:
+ /* Should not be needed - animation from lib pre-2.5 is broken anyway. */
+ BLI_assert(0);
+ break;
+ default:
+ BLI_assert(0); /* Should never reach this point... */
}
}
@@ -1201,6 +1242,7 @@ void BKE_main_free(Main *mainvar)
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;
default:
BLI_assert(0);
break;
@@ -1612,16 +1654,22 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
int a;
for (a = set_listbasepointers(bmain, lbarray); a--; ) {
- for (id = lbarray[a]->first; id; id = id_next) {
+ id = lbarray[a]->first;
+
+ /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), they are assumed to be handled
+ * by real datablocks responsible of them. */
+ const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name)));
+
+ for (; id; id = id_next) {
id->newid = NULL;
id_next = id->next; /* id is possibly being inserted again */
-
+
/* The check on the second 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 (id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) &&
+ if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) &&
((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING)))
{
if (lib == NULL || id->lib == lib) {
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c
index 1ded6f6679f..cb864334208 100644
--- a/source/blender/blenkernel/intern/library_query.c
+++ b/source/blender/blenkernel/intern/library_query.c
@@ -305,7 +305,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
library_foreach_animationData(&data, adt);
}
- switch (GS(id->name)) {
+ switch ((ID_Type)GS(id->name)) {
case ID_LI:
{
Library *lib = (Library *) id;
@@ -499,8 +499,12 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, &data);
}
- if (object->soft && object->soft->effector_weights) {
- CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_NOP);
+ if (object->soft) {
+ CALLBACK_INVOKE(object->soft->collision_group, IDWALK_NOP);
+
+ if (object->soft->effector_weights) {
+ CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_NOP);
+ }
}
BKE_sca_sensors_id_loop(&object->sensors, library_foreach_sensorsObjectLooper, &data);
@@ -639,6 +643,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
case ID_KE:
{
+ /* XXX Only ID pointer from shapekeys is the 'from' one, which is not actually ID usage.
+ * Maybe we should even nuke it from here, not 100% sure yet...
+ * (see also foreach_libblock_id_users_callback).
+ */
Key *key = (Key *) id;
CALLBACK_INVOKE_ID(key->from, IDWALK_NOP);
break;
@@ -711,6 +719,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
CALLBACK_INVOKE(psett->dup_group, IDWALK_NOP);
CALLBACK_INVOKE(psett->dup_ob, IDWALK_NOP);
CALLBACK_INVOKE(psett->bb_ob, IDWALK_NOP);
+ CALLBACK_INVOKE(psett->collision_group, IDWALK_NOP);
for (i = 0; i < MAX_MTEX; i++) {
if (psett->mtex[i]) {
@@ -833,6 +842,25 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
}
break;
}
+
+ /* Nothing needed for those... */
+ case ID_IM:
+ case ID_VF:
+ case ID_TXT:
+ case ID_SO:
+ case ID_AR:
+ case ID_AC:
+ case ID_GD:
+ case ID_WM:
+ case ID_PAL:
+ case ID_PC:
+ case ID_CF:
+ break;
+
+ /* Deprecated. */
+ case ID_IP:
+ break;
+
}
} while ((id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL));
@@ -875,7 +903,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
return id_type_can_have_animdata(id_type_owner);
}
- switch (id_type_owner) {
+ switch ((ID_Type)id_type_owner) {
case ID_LI:
return ELEM(id_type_used, ID_LI);
case ID_SCE:
@@ -934,9 +962,24 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */
case ID_LS:
return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
- default:
+ case ID_IM:
+ case ID_VF:
+ case ID_TXT:
+ case ID_SO:
+ case ID_AR:
+ case ID_AC:
+ case ID_GD:
+ case ID_WM:
+ case ID_PAL:
+ case ID_PC:
+ case ID_CF:
+ /* Those types never use/reference other IDs... */
+ return false;
+ case ID_IP:
+ /* Deprecated... */
return false;
}
+ return false;
}
@@ -951,10 +994,18 @@ typedef struct IDUsersIter {
int count_direct, count_indirect; /* Set by callback. */
} IDUsersIter;
-static int foreach_libblock_id_users_callback(void *user_data, ID *UNUSED(self_id), ID **id_p, int cb_flag)
+static int foreach_libblock_id_users_callback(void *user_data, ID *self_id, ID **id_p, int cb_flag)
{
IDUsersIter *iter = user_data;
+ /* XXX This is actually some kind of hack...
+ * Issue is, only ID pointer from shapekeys is the 'from' one, which is not actually ID usage.
+ * Maybe we should even nuke it from BKE_library_foreach_ID_link, not 100% sure yet...
+ */
+ if (GS(self_id->name) == ID_KE) {
+ return IDWALK_RET_NOP;
+ }
+
if (*id_p && (*id_p == iter->id)) {
#if 0
printf("%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d, indirect_usage: %d)\n",
@@ -1015,6 +1066,10 @@ static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked)
}
for (; id_curr && !is_defined; id_curr = id_curr->next) {
+ if (id_curr == id) {
+ /* We are not interested in self-usages (mostly from drivers or bone constraints...). */
+ continue;
+ }
iter.curr_id = id_curr;
BKE_library_foreach_ID_link(
id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_NOP);
@@ -1063,6 +1118,10 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo
}
for (; id_curr && !is_defined; id_curr = id_curr->next) {
+ if (id_curr == id) {
+ /* We are not interested in self-usages (mostly from drivers or bone constraints...). */
+ continue;
+ }
iter.curr_id = id_curr;
BKE_library_foreach_ID_link(id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_NOP);
diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c
index 74b3b1b6c18..1830ca0bd90 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -38,6 +38,7 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_ipo_types.h"
@@ -69,6 +70,7 @@
#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_camera.h"
+#include "BKE_cachefile.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
#include "BKE_fcurve.h"
@@ -795,7 +797,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user)
free_windowmanager_cb(NULL, (wmWindowManager *)id);
break;
case ID_GD:
- BKE_gpencil_free((bGPdata *)id);
+ BKE_gpencil_free((bGPdata *)id, true);
break;
case ID_MC:
BKE_movieclip_free((MovieClip *)id);
@@ -812,6 +814,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user)
case ID_PC:
BKE_paint_curve_free((PaintCurve *)id);
break;
+ case ID_CF:
+ BKE_cachefile_free((CacheFile *)id);
+ break;
}
/* avoid notifying on removed data */
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index 1aff5d502f8..430935a5fad 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -218,10 +218,7 @@ FreestyleLineStyle *BKE_linestyle_copy(struct Main *bmain, FreestyleLineStyle *l
for (m = (LineStyleModifier *)linestyle->geometry_modifiers.first; m; m = m->next)
BKE_linestyle_geometry_modifier_copy(new_linestyle, m);
- if (ID_IS_LINKED_DATABLOCK(linestyle)) {
- BKE_id_expand_local(&new_linestyle->id);
- BKE_id_lib_local_paths(bmain, linestyle->id.lib, &new_linestyle->id);
- }
+ BKE_id_copy_ensure_local(bmain, &linestyle->id, &new_linestyle->id);
return new_linestyle;
}
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 014461e0d22..21023d9f53c 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -853,10 +853,7 @@ Mask *BKE_mask_copy(Main *bmain, Mask *mask)
/* enable fake user by default */
id_fake_user_set(&mask->id);
- if (ID_IS_LINKED_DATABLOCK(mask)) {
- BKE_id_expand_local(&mask_new->id);
- BKE_id_lib_local_paths(bmain, mask->id.lib, &mask_new->id);
- }
+ BKE_id_copy_ensure_local(bmain, &mask->id, &mask_new->id);
return mask_new;
}
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 62aba1af694..470108545b8 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -247,10 +247,7 @@ Material *BKE_material_copy(Main *bmain, Material *ma)
BLI_listbase_clear(&man->gpumaterial);
- if (ID_IS_LINKED_DATABLOCK(ma)) {
- BKE_id_expand_local(&man->id);
- BKE_id_lib_local_paths(bmain, ma->id.lib, &man->id);
- }
+ BKE_id_copy_ensure_local(bmain, &ma->id, &man->id);
return man;
}
@@ -745,17 +742,18 @@ void assign_material(Object *ob, Material *ma, short act, int assign_type)
if (mao)
id_us_min(&mao->id);
ob->mat[act - 1] = ma;
+ test_object_materials(ob, ob->data);
}
else { /* in data */
mao = (*matarar)[act - 1];
if (mao)
id_us_min(&mao->id);
(*matarar)[act - 1] = ma;
+ test_all_objects_materials(G.main, ob->data); /* Data may be used by several objects... */
}
if (ma)
id_us_plus(&ma->id);
- test_object_materials(ob, ob->data);
}
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index 7e363e5600f..8d024ea9aa5 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -119,10 +119,7 @@ MetaBall *BKE_mball_copy(Main *bmain, MetaBall *mb)
mbn->editelems = NULL;
mbn->lastelem = NULL;
- if (ID_IS_LINKED_DATABLOCK(mb)) {
- BKE_id_expand_local(&mbn->id);
- BKE_id_lib_local_paths(bmain, mb->id.lib, &mbn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &mb->id, &mbn->id);
return mbn;
}
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 2b35cdc9d64..733e9030056 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -531,10 +531,7 @@ Mesh *BKE_mesh_copy(Main *bmain, Mesh *me)
men->key->from = (ID *)men;
}
- if (ID_IS_LINKED_DATABLOCK(me)) {
- BKE_id_expand_local(&men->id);
- BKE_id_lib_local_paths(bmain, me->id.lib, &men->id);
- }
+ BKE_id_copy_ensure_local(bmain, &me->id, &men->id);
return men;
}
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 1c86fbcfe8e..fa113ef5eef 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -434,7 +434,7 @@ MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr)
}
/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
-#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f)
+#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
/* Should only be called once.
* Beware, this modifies ref_vec and other_vec in place!
diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c
index 1a20300457f..8562988b5e1 100644
--- a/source/blender/blenkernel/intern/mesh_mapping.c
+++ b/source/blender/blenkernel/intern/mesh_mapping.c
@@ -934,6 +934,7 @@ static bool mesh_check_island_boundary_uv(
}
else {
BLI_assert(loops[edge_to_loops->indices[i]].v == v2);
+ UNUSED_VARS_NDEBUG(v2);
if (!equals_v2v2(uvco_v2, luvs[edge_to_loops->indices[i]].uv) ||
!equals_v2v2(uvco_v1, luvs[edge_to_loops->indices[i + 1]].uv))
{
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index f99457a4c26..0d362086134 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -1491,6 +1491,32 @@ void BKE_movieclip_free(MovieClip *clip)
BKE_tracking_free(&clip->tracking);
}
+MovieClip *BKE_movieclip_copy(Main *bmain, MovieClip *clip)
+{
+ MovieClip *clip_new;
+
+ clip_new = BKE_libblock_copy(bmain, &clip->id);
+
+ clip_new->anim = NULL;
+ clip_new->cache = NULL;
+
+ BKE_tracking_copy(&clip_new->tracking, &clip->tracking);
+ clip_new->tracking_context = NULL;
+
+ id_us_plus((ID *)clip_new->gpd);
+
+ BKE_color_managed_colorspace_settings_copy(&clip_new->colorspace_settings, &clip->colorspace_settings);
+
+ BKE_id_copy_ensure_local(bmain, &clip->id, &clip_new->id);
+
+ return clip_new;
+}
+
+void BKE_movieclip_make_local(Main *bmain, MovieClip *clip, const bool lib_local)
+{
+ BKE_id_make_local_generic(bmain, &clip->id, true, lib_local);
+}
+
float BKE_movieclip_remap_scene_to_clip_frame(MovieClip *clip, float framenr)
{
return framenr - (float) clip->start_frame + 1.0f;
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index d360356aa3d..87de7d589b7 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -1300,10 +1300,7 @@ static bNodeTree *ntreeCopyTree_internal(bNodeTree *ntree, Main *bmain, bool ski
/* node tree will generate its own interface type */
newtree->interface_type = NULL;
- if (ID_IS_LINKED_DATABLOCK(ntree)) {
- BKE_id_expand_local(&newtree->id);
- BKE_id_lib_local_paths(bmain, ntree->id.lib, &newtree->id);
- }
+ BKE_id_copy_ensure_local(bmain, &ntree->id, &newtree->id);
return newtree;
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 9c48fdfbf08..d736a455163 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1169,10 +1169,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
/* Copy runtime surve data. */
obn->curve_cache = NULL;
- if (ID_IS_LINKED_DATABLOCK(ob)) {
- BKE_id_expand_local(&obn->id);
- BKE_id_lib_local_paths(bmain, ob->id.lib, &obn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &ob->id, &obn->id);
/* Do not copy object's preview (mostly due to the fact renderers create temp copy of objects). */
obn->preview = NULL;
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index cd457c77f89..fbe542ddbb5 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -83,7 +83,6 @@ typedef struct DupliContext {
int persistent_id[MAX_DUPLI_RECUR];
int level;
- int index;
const struct DupliGenerator *gen;
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 8c1502643c5..53675f33a69 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -314,6 +314,26 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name)
return pc;
}
+PaintCurve *BKE_paint_curve_copy(Main *bmain, PaintCurve *pc)
+{
+ PaintCurve *pc_new;
+
+ pc_new = BKE_libblock_copy(bmain, &pc->id);
+
+ if (pc->tot_points != 0) {
+ pc_new->points = MEM_dupallocN(pc->points);
+ }
+
+ BKE_id_copy_ensure_local(bmain, &pc->id, &pc_new->id);
+
+ return pc_new;
+}
+
+void BKE_paint_curve_make_local(Main *bmain, PaintCurve *pc, const bool lib_local)
+{
+ BKE_id_make_local_generic(bmain, &pc->id, true, lib_local);
+}
+
Palette *BKE_paint_palette(Paint *p)
{
return p ? p->palette : NULL;
@@ -376,6 +396,24 @@ Palette *BKE_palette_add(Main *bmain, const char *name)
return palette;
}
+Palette *BKE_palette_copy(Main *bmain, Palette *palette)
+{
+ Palette *palette_new;
+
+ palette_new = BKE_libblock_copy(bmain, &palette->id);
+
+ BLI_duplicatelist(&palette_new->colors, &palette->colors);
+
+ BKE_id_copy_ensure_local(bmain, &palette->id, &palette_new->id);
+
+ return palette_new;
+}
+
+void BKE_palette_make_local(Main *bmain, Palette *palette, bool lib_local)
+{
+ BKE_id_make_local_generic(bmain, &palette->id, true, lib_local);
+}
+
/** Free (or release) any data used by this palette (does not free the palette itself). */
void BKE_palette_free(Palette *palette)
{
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 2c0f183d78f..5a44f480de4 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -3335,10 +3335,7 @@ ParticleSettings *BKE_particlesettings_copy(Main *bmain, ParticleSettings *part)
BLI_duplicatelist(&partn->dupliweights, &part->dupliweights);
- if (ID_IS_LINKED_DATABLOCK(part)) {
- BKE_id_expand_local(&partn->id);
- BKE_id_lib_local_paths(bmain, part->id.lib, &partn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &part->id, &partn->id);
return partn;
}
@@ -4053,13 +4050,16 @@ void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part,
uv[0] = uv[1] = 0.f;
+ /* Grid distribution doesn't support UV or emit from vertex mode */
+ bool is_grid = (part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT);
+
if (cpa) {
if ((part->childtype == PART_CHILD_FACES) && (psmd->dm_final != NULL)) {
CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final);
const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE);
mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx);
- if (mtface) {
+ if (mtface && !is_grid) {
mface = psmd->dm_final->getTessFaceData(psmd->dm_final, cpa->num, CD_MFACE);
mtface += cpa->num;
psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv);
@@ -4073,7 +4073,7 @@ void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part,
}
}
- if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL)) {
+ if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL) && !is_grid) {
CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final);
const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE);
mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx);
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index d89eac327d5..4768e00f75e 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -2183,7 +2183,6 @@ static void basic_rotate(ParticleSettings *part, ParticleData *pa, float dfra, f
* http://en.wikipedia.org/wiki/Newton's_method
*
************************************************/
-#define COLLISION_MAX_COLLISIONS 10
#define COLLISION_MIN_RADIUS 0.001f
#define COLLISION_MIN_DISTANCE 0.0001f
#define COLLISION_ZERO 0.00001f
@@ -2570,10 +2569,6 @@ void BKE_psys_collision_neartest_cb(void *userdata, int index, const BVHTreeRay
pce.inside = 0;
pce.index = index;
- /* don't collide with same face again */
- if (col->hit == col->current && col->pce.index == index && col->pce.tot == 3)
- return;
-
collision = collision_sphere_to_tri(col, ray->radius, &pce, &t);
if (col->pce.inside == 0) {
collision += collision_sphere_to_edges(col, ray->radius, &pce, &t);
@@ -2609,8 +2604,17 @@ static int collision_detect(ParticleData *pa, ParticleCollision *col, BVHTreeRay
hit->dist = col->original_ray_length = 0.000001f;
for (coll = colliders->first; coll; coll=coll->next) {
- /* for boids: don't check with current ground object */
- if (coll->ob == col->skip)
+ /* for boids: don't check with current ground object; also skip if permeated */
+ bool skip = false;
+
+ for (int i = 0; i < col->skip_count; i++) {
+ if (coll->ob == col->skip[i]) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip)
continue;
/* particles should not collide with emitter at birth */
@@ -2746,7 +2750,7 @@ static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeR
if (through==0 && ((vc_dot>0.0f && v0_dot>0.0f && vc_dot>v0_dot) || (vc_dot<0.0f && v0_dot<0.0f && vc_dot<v0_dot)))
mul_v3_v3fl(v0_nor, pce->nor, vc_dot);
else if (v0_dot > 0.f)
- mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? -1.0f : 1.0f) * v0_dot);
+ mul_v3_v3fl(v0_nor, pce->nor, vc_dot + v0_dot);
else
mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? 1.0f : -1.0f) * v0_dot);
@@ -2801,8 +2805,10 @@ static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeR
col->f = f;
}
- col->prev = col->hit;
- col->prev_index = hit->index;
+ /* if permeability random roll succeeded, disable collider for this sim step */
+ if (through) {
+ col->skip[col->skip_count++] = col->hit;
+ }
return 1;
}
@@ -2863,16 +2869,16 @@ static void collision_check(ParticleSimulationData *sim, int p, float dfra, floa
if (part->phystype == PART_PHYS_BOIDS && part->boids->options & BOID_ALLOW_LAND) {
col.boid = 1;
col.boid_z = pa->state.co[2];
- col.skip = pa->boid->ground;
+ col.skip[col.skip_count++] = pa->boid->ground;
}
/* 10 iterations to catch multiple collisions */
- while (collision_count < COLLISION_MAX_COLLISIONS) {
+ while (collision_count < PARTICLE_COLLISION_MAX_COLLISIONS) {
if (collision_detect(pa, &col, &hit, sim->colliders)) {
collision_count++;
- if (collision_count == COLLISION_MAX_COLLISIONS)
+ if (collision_count == PARTICLE_COLLISION_MAX_COLLISIONS)
collision_fail(pa, &col);
else if (collision_response(pa, &col, &hit, part->flag & PART_DIE_ON_COL, part->flag & PART_ROT_DYN)==0)
return;
@@ -3311,7 +3317,7 @@ static float get_base_time_step(ParticleSettings *part)
return 1.0f / (float) (part->subframes + 1);
}
/* Update time step size to suit current conditions. */
-static float update_timestep(ParticleSystem *psys, ParticleSimulationData *sim, float t_frac)
+static void update_timestep(ParticleSystem *psys, ParticleSimulationData *sim)
{
float dt_target;
if (sim->courant_num == 0.0f)
@@ -3331,7 +3337,10 @@ static float update_timestep(ParticleSystem *psys, ParticleSimulationData *sim,
psys->dt_frac = interpf(dt_target, psys->dt_frac, TIMESTEP_EXPANSION_FACTOR);
else
psys->dt_frac = dt_target;
+}
+static float sync_timestep(ParticleSystem *psys, float t_frac)
+{
/* Sync with frame end if it's close. */
if (t_frac == 1.0f)
return psys->dt_frac;
@@ -3493,7 +3502,7 @@ static void dynamics_step(ParticleSimulationData *sim, float cfra)
psys_update_effectors(sim);
if (part->type != PART_HAIR)
- sim->colliders = get_collider_cache(sim->scene, sim->ob, NULL);
+ sim->colliders = get_collider_cache(sim->scene, sim->ob, part->collision_group);
/* initialize physics type specific stuff */
switch (part->phystype) {
@@ -3991,7 +4000,9 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_
printf("%f,%f,%f,%f\n", cfra+dframe+t_frac - 1.f, t_frac, dt_frac, sim->courant_num);
#endif
if (part->time_flag & PART_TIME_AUTOSF)
- dt_frac = update_timestep(psys, sim, t_frac);
+ update_timestep(psys, sim);
+ /* Even without AUTOSF dt_frac may not add up to 1.0 due to float precision. */
+ dt_frac = sync_timestep(psys, t_frac);
}
}
}
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index e0a3e9743db..69a98c06000 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -386,9 +386,9 @@ static void ptcache_particle_interpolate(int index, void *psys_v, void **data, f
}
}
- /* determine rotation from velocity */
+ /* default to no rotation */
if (data[BPHYS_DATA_LOCATION] && !data[BPHYS_DATA_ROTATION]) {
- vec_to_quat(keys[2].rot, keys[2].vel, OB_NEGX, OB_POSZ);
+ unit_qt(keys[2].rot);
}
if (cfra > pa->time)
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 3e37ee83cea..c3c23756b70 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -49,6 +49,7 @@
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_gpencil_types.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
@@ -64,6 +65,7 @@
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_armature.h"
+#include "BKE_cachefile.h"
#include "BKE_colortools.h"
#include "BKE_depsgraph.h"
#include "BKE_editmesh.h"
@@ -286,6 +288,13 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type)
ts->imapaint.paintcursor = NULL;
id_us_plus((ID *)ts->imapaint.stencil);
ts->particle.paintcursor = NULL;
+ /* duplicate Grease Pencil Drawing Brushes */
+ BLI_listbase_clear(&ts->gp_brushes);
+ for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) {
+ bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush);
+ BLI_addtail(&ts->gp_brushes, newbrush);
+ }
+
}
/* make a private copy of the avicodecdata */
@@ -334,7 +343,7 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type)
/* grease pencil */
if (scen->gpd) {
if (type == SCE_COPY_FULL) {
- scen->gpd = gpencil_data_duplicate(bmain, scen->gpd, false);
+ scen->gpd = BKE_gpencil_data_duplicate(bmain, scen->gpd, false);
}
else if (type == SCE_COPY_EMPTY) {
scen->gpd = NULL;
@@ -432,6 +441,10 @@ void BKE_scene_free(Scene *sce)
BKE_paint_free(&sce->toolsettings->uvsculpt->paint);
MEM_freeN(sce->toolsettings->uvsculpt);
}
+ /* free Grease Pencil Drawing Brushes */
+ BKE_gpencil_free_brushes(&sce->toolsettings->gp_brushes);
+ BLI_freelistN(&sce->toolsettings->gp_brushes);
+
BKE_paint_free(&sce->toolsettings->imapaint.paint);
MEM_freeN(sce->toolsettings);
@@ -764,6 +777,11 @@ void BKE_scene_init(Scene *sce)
gp_brush->strength = 0.5f;
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
+ gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH];
+ gp_brush->size = 25;
+ gp_brush->strength = 0.5f;
+ gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
+
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB];
gp_brush->size = 50;
gp_brush->strength = 0.3f;
@@ -1909,6 +1927,9 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain,
BKE_mask_evaluate_all_masks(bmain, ctime, true);
+ /* Update animated cache files for modifiers. */
+ BKE_cachefile_update_frame(bmain, sce, ctime, (((double)sce->r.frs_sec) / (double)sce->r.frs_sec_base));
+
#ifdef POSE_ANIMATION_WORKAROUND
scene_armature_depsgraph_workaround(bmain);
#endif
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 6067a8b2d9b..c240aa27343 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -1656,6 +1656,9 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render
else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) {
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
}
+ else if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
+ BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
+ }
else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) {
char fname[FILE_MAXFILE];
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
@@ -1675,13 +1678,21 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render
if (view_id > 0)
BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id);
- if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim &&
+ if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE &&
ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE)
{
- BLI_join_dirfile(name, PROXY_MAXFILE,
- dir, proxy->file);
- BLI_path_abs(name, G.main->name);
- BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix);
+ char fname[FILE_MAXFILE];
+ BLI_join_dirfile(fname, PROXY_MAXFILE, dir, proxy->file);
+ BLI_path_abs(fname, G.main->name);
+ if (suffix[0] != '\0') {
+ /* TODO(sergey): This will actually append suffix after extension
+ * which is weird but how was originally coded in multiview branch.
+ */
+ BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix);
+ }
+ else {
+ BLI_strncpy(name, fname, PROXY_MAXFILE);
+ }
return true;
}
@@ -5595,3 +5606,31 @@ int BKE_sequencer_find_next_prev_edit(
return best_frame;
}
+
+static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int cfra)
+{
+ for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) {
+ if (seq->enddisp < cfra || seq->startdisp > cfra) {
+ BKE_sequence_free_anim(seq);
+ }
+ if (seq->type == SEQ_TYPE_META) {
+ sequencer_all_free_anim_ibufs(&seq->seqbase, cfra);
+ }
+ }
+}
+
+void BKE_sequencer_all_free_anim_ibufs(int cfra)
+{
+ BKE_sequencer_cache_cleanup();
+ for (Scene *scene = G.main->scene.first;
+ scene != NULL;
+ scene = scene->id.next)
+ {
+ Editing *ed = BKE_sequencer_editing_get(scene, false);
+ if (ed == NULL) {
+ /* Ignore scenes without sequencer. */
+ continue;
+ }
+ sequencer_all_free_anim_ibufs(&ed->seqbase, cfra);
+ }
+}
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index 799cdeeb3d0..72f058bff34 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -707,6 +707,7 @@ typedef struct ObstaclesFromDMData {
bool has_velocity;
float *vert_vel;
float *velocityX, *velocityY, *velocityZ;
+ int *num_obstacles;
} ObstaclesFromDMData;
static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z)
@@ -755,8 +756,10 @@ static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z)
/* tag obstacle cells */
data->obstacle_map[index] = 1;
- if (data->has_velocity)
+ if (data->has_velocity) {
data->obstacle_map[index] |= 8;
+ data->num_obstacles[index]++;
+ }
}
}
}
@@ -764,7 +767,7 @@ static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z)
static void obstacles_from_derivedmesh(
Object *coll_ob, SmokeDomainSettings *sds, SmokeCollSettings *scs,
- unsigned char *obstacle_map, float *velocityX, float *velocityY, float *velocityZ, float dt)
+ unsigned char *obstacle_map, float *velocityX, float *velocityY, float *velocityZ, int *num_obstacles, float dt)
{
if (!scs->dm) return;
{
@@ -835,7 +838,8 @@ static void obstacles_from_derivedmesh(
.sds = sds, .mvert = mvert, .mloop = mloop, .looptri = looptri,
.tree = &treeData, .obstacle_map = obstacle_map,
.has_velocity = has_velocity, .vert_vel = vert_vel,
- .velocityX = velocityX, .velocityY = velocityY, .velocityZ = velocityZ
+ .velocityX = velocityX, .velocityY = velocityY, .velocityZ = velocityZ,
+ .num_obstacles = num_obstacles
};
BLI_task_parallel_range(
sds->res_min[2], sds->res_max[2], &data, obstacles_from_derivedmesh_task_cb, true);
@@ -871,6 +875,8 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds,
float *b = smoke_get_color_b(sds->fluid);
unsigned int z;
+ int *num_obstacles = MEM_callocN(sizeof(int) * sds->res[0] * sds->res[1] * sds->res[2], "smoke_num_obstacles");
+
smoke_get_ob_velocity(sds->fluid, &velx, &vely, &velz);
// TODO: delete old obstacle flags
@@ -900,7 +906,7 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds,
if ((smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll)
{
SmokeCollSettings *scs = smd2->coll;
- obstacles_from_derivedmesh(collob, sds, scs, obstacles, velx, vely, velz, dt);
+ obstacles_from_derivedmesh(collob, sds, scs, obstacles, velx, vely, velz, num_obstacles, dt);
}
}
@@ -926,7 +932,15 @@ static void update_obstacles(Scene *scene, Object *ob, SmokeDomainSettings *sds,
b[z] = 0;
}
}
+ /* average velocities from multiple obstacles in one cell */
+ if (num_obstacles[z]) {
+ velx[z] /= num_obstacles[z];
+ vely[z] /= num_obstacles[z];
+ velz[z] /= num_obstacles[z];
+ }
}
+
+ MEM_freeN(num_obstacles);
}
/**********************************************************
@@ -2772,8 +2786,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
// create shadows before writing cache so they get stored
smoke_calc_transparency(sds, scene);
- if (sds->wt)
- {
+ if (sds->wt && sds->total_cells > 1) {
smoke_turbulence_step(sds->wt, sds->fluid);
}
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index d1c7b240b94..7f5c8af2686 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -63,6 +63,7 @@ variables on the UI for now
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_group_types.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -496,59 +497,98 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
}
}
-static void ccd_build_deflector_hash(Scene *scene, Object *vertexowner, GHash *hash)
+static void ccd_build_deflector_hash_single(GHash *hash, Object *ob)
+{
+ /* only with deflecting set */
+ if (ob->pd && ob->pd->deflect) {
+ void **val_p;
+ if (!BLI_ghash_ensure_p(hash, ob, &val_p)) {
+ ccd_Mesh *ccdmesh = ccd_mesh_make(ob);
+ *val_p = ccdmesh;
+ }
+ }
+}
+
+/**
+ * \note group overrides scene when not NULL.
+ */
+static void ccd_build_deflector_hash(Scene *scene, Group *group, Object *vertexowner, GHash *hash)
{
- Base *base= scene->base.first;
Object *ob;
if (!hash) return;
- while (base) {
- /*Only proceed for mesh object in same layer */
- if (base->object->type==OB_MESH && (base->lay & vertexowner->lay)) {
- ob= base->object;
- if ((vertexowner) && (ob == vertexowner)) {
- /* if vertexowner is given we don't want to check collision with owner object */
- base = base->next;
+
+ if (group) {
+ /* Explicit collision group */
+ for (GroupObject *go = group->gobject.first; go; go = go->next) {
+ ob = go->ob;
+
+ if (ob == vertexowner || ob->type != OB_MESH)
continue;
- }
- /*+++ only with deflecting set */
- if (ob->pd && ob->pd->deflect && BLI_ghash_lookup(hash, ob) == NULL) {
- ccd_Mesh *ccdmesh = ccd_mesh_make(ob);
- BLI_ghash_insert(hash, ob, ccdmesh);
- }/*--- only with deflecting set */
+ ccd_build_deflector_hash_single(hash, ob);
+ }
+ }
+ else {
+ for (Base *base = scene->base.first; base; base = base->next) {
+ /*Only proceed for mesh object in same layer */
+ if (base->object->type == OB_MESH && (base->lay & vertexowner->lay)) {
+ ob= base->object;
+ if ((vertexowner) && (ob == vertexowner)) {
+ /* if vertexowner is given we don't want to check collision with owner object */
+ continue;
+ }
+
+ ccd_build_deflector_hash_single(hash, ob);
+ }
+ }
+ }
+}
- }/* mesh && layer*/
- base = base->next;
- } /* while (base) */
+static void ccd_update_deflector_hash_single(GHash *hash, Object *ob)
+{
+ if (ob->pd && ob->pd->deflect) {
+ ccd_Mesh *ccdmesh = BLI_ghash_lookup(hash, ob);
+ if (ccdmesh) {
+ ccd_mesh_update(ob, ccdmesh);
+ }
+ }
}
-static void ccd_update_deflector_hash(Scene *scene, Object *vertexowner, GHash *hash)
+/**
+ * \note group overrides scene when not NULL.
+ */
+static void ccd_update_deflector_hash(Scene *scene, Group *group, Object *vertexowner, GHash *hash)
{
- Base *base= scene->base.first;
Object *ob;
if ((!hash) || (!vertexowner)) return;
- while (base) {
- /*Only proceed for mesh object in same layer */
- if (base->object->type==OB_MESH && (base->lay & vertexowner->lay)) {
- ob= base->object;
- if (ob == vertexowner) {
- /* if vertexowner is given we don't want to check collision with owner object */
- base = base->next;
+
+ if (group) {
+ /* Explicit collision group */
+ for (GroupObject *go = group->gobject.first; go; go = go->next) {
+ ob = go->ob;
+
+ if (ob == vertexowner || ob->type != OB_MESH)
continue;
- }
- /*+++ only with deflecting set */
- if (ob->pd && ob->pd->deflect) {
- ccd_Mesh *ccdmesh = BLI_ghash_lookup(hash, ob);
- if (ccdmesh)
- ccd_mesh_update(ob, ccdmesh);
- }/*--- only with deflecting set */
+ ccd_update_deflector_hash_single(hash, ob);
+ }
+ }
+ else {
+ for (Base *base = scene->base.first; base; base = base->next) {
+ /*Only proceed for mesh object in same layer */
+ if (base->object->type == OB_MESH && (base->lay & vertexowner->lay)) {
+ ob= base->object;
+ if (ob == vertexowner) {
+ /* if vertexowner is given we don't want to check collision with owner object */
+ continue;
+ }
- }/* mesh && layer*/
- base = base->next;
- } /* while (base) */
+ ccd_update_deflector_hash_single(hash, ob);
+ }
+ }
+ }
}
@@ -934,22 +974,32 @@ static void free_softbody_intern(SoftBody *sb)
/* +++ dependency information functions*/
-static int are_there_deflectors(Scene *scene, unsigned int layer)
+/**
+ * \note group overrides scene when not NULL.
+ */
+static bool are_there_deflectors(Scene *scene, Group *group, unsigned int layer)
{
- Base *base;
-
- for (base = scene->base.first; base; base= base->next) {
- if ( (base->lay & layer) && base->object->pd) {
- if (base->object->pd->deflect)
+ if (group) {
+ for (GroupObject *go = group->gobject.first; go; go = go->next) {
+ if (go->ob->pd && go->ob->pd->deflect)
return 1;
}
}
+ else {
+ for (Base *base = scene->base.first; base; base= base->next) {
+ if ( (base->lay & layer) && base->object->pd) {
+ if (base->object->pd->deflect)
+ return 1;
+ }
+ }
+ }
+
return 0;
}
-static int query_external_colliders(Scene *scene, Object *me)
+static int query_external_colliders(Scene *scene, Group *group, Object *me)
{
- return(are_there_deflectors(scene, me->lay));
+ return(are_there_deflectors(scene, group, me->lay));
}
/* --- dependency information functions*/
@@ -2197,7 +2247,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl
/* gravity = sb->grav * sb_grav_force_scale(ob); */ /* UNUSED */
/* check conditions for various options */
- do_deflector= query_external_colliders(scene, ob);
+ do_deflector= query_external_colliders(scene, sb->collision_group, ob);
/* do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF)); */ /* UNUSED */
do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL);
do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES));
@@ -2261,7 +2311,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa
}
/* check conditions for various options */
- do_deflector= query_external_colliders(scene, ob);
+ do_deflector= query_external_colliders(scene, sb->collision_group, ob);
do_selfcollision=((ob->softflag & OB_SB_EDGES) && (sb->bspring)&& (ob->softflag & OB_SB_SELF));
do_springcollision=do_deflector && (ob->softflag & OB_SB_EDGES) &&(ob->softflag & OB_SB_EDGECOLL);
do_aero=((sb->aeroedge)&& (ob->softflag & OB_SB_EDGES));
@@ -3472,11 +3522,11 @@ static void softbody_step(Scene *scene, Object *ob, SoftBody *sb, float dtime)
*/
if (dtime < 0 || dtime > 10.5f) return;
- ccd_update_deflector_hash(scene, ob, sb->scratch->colliderhash);
+ ccd_update_deflector_hash(scene, sb->collision_group, ob, sb->scratch->colliderhash);
if (sb->scratch->needstobuildcollider) {
- if (query_external_colliders(scene, ob)) {
- ccd_build_deflector_hash(scene, ob, sb->scratch->colliderhash);
+ if (query_external_colliders(scene, sb->collision_group, ob)) {
+ ccd_build_deflector_hash(scene, sb->collision_group, ob, sb->scratch->colliderhash);
}
sb->scratch->needstobuildcollider=0;
}
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index 80ee6d50d7e..ee6886e3fb2 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -77,10 +77,7 @@ Speaker *BKE_speaker_copy(Main *bmain, Speaker *spk)
if (spkn->sound)
id_us_plus(&spkn->sound->id);
- if (ID_IS_LINKED_DATABLOCK(spk)) {
- BKE_id_expand_local(&spkn->id);
- BKE_id_lib_local_paths(G.main, spk->id.lib, &spkn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &spk->id, &spkn->id);
return spkn;
}
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 10a0a39fcdd..e876bf43809 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -2699,6 +2699,7 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
num_draw_patches);
}
}
+ glShadeModel(GL_SMOOTH);
return;
}
#endif
@@ -2805,6 +2806,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm,
num_draw_patches);
}
}
+ glShadeModel(GL_SMOOTH);
return;
}
#endif
@@ -3214,6 +3216,7 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm,
glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT);
setMaterial(userData, new_matnr, &gattribs);
ccgSubSurf_drawGLMesh(ss, true, -1, -1);
+ glShadeModel(GL_SMOOTH);
return;
}
#endif
@@ -3424,15 +3427,18 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
int current_patch = 0;
int mat_nr = -1;
int start_draw_patch = 0, num_draw_patches = 0;
+ bool draw_smooth = false;
for (i = 0; i < num_base_faces; ++i) {
const int num_face_verts = ccgSubSurf_getNumGLMeshBaseFaceVerts(ss, i);
const int num_patches = (num_face_verts == 4) ? face_patches
: num_face_verts * grid_patches;
if (faceFlags) {
- mat_nr = faceFlags[i].mat_nr + 1;
+ mat_nr = faceFlags[i].mat_nr;
+ draw_smooth = (faceFlags[i].flag & ME_SMOOTH);
}
else {
mat_nr = 0;
+ draw_smooth = false;
}
if (drawParams != NULL) {
@@ -3447,8 +3453,13 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
flush = (draw_option == DM_DRAW_OPTION_SKIP) || (i == num_base_faces - 1);
+ const int next_face = min_ii(i + 1, num_base_faces - 1);
if (!flush && compareDrawOptions) {
- flush |= compareDrawOptions(userData, i, min_ii(i + 1, num_base_faces - 1)) == 0;
+ flush |= compareDrawOptions(userData, i, next_face) == 0;
+ }
+ if (!flush && faceFlags) {
+ bool new_draw_smooth = (faceFlags[next_face].flag & ME_SMOOTH);
+ flush |= (new_draw_smooth != draw_smooth);
}
current_patch += num_patches;
@@ -3458,6 +3469,7 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
num_draw_patches += num_patches;
}
if (num_draw_patches != 0) {
+ glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT);
ccgSubSurf_drawGLMesh(ss,
true,
start_draw_patch,
@@ -3470,6 +3482,7 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
num_draw_patches += num_patches;
}
}
+ glShadeModel(GL_SMOOTH);
return;
}
#endif
@@ -3669,6 +3682,7 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT);
ccgSubSurf_drawGLMesh(ss, true, -1, -1);
}
+ glShadeModel(GL_SMOOTH);
return;
}
#endif
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 82c3132d73e..1636042f479 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -491,10 +491,7 @@ Text *BKE_text_copy(Main *bmain, Text *ta)
init_undo_text(tan);
- if (ID_IS_LINKED_DATABLOCK(ta)) {
- BKE_id_expand_local(&tan->id);
- BKE_id_lib_local_paths(bmain, ta->id.lib, &tan->id);
- }
+ BKE_id_copy_ensure_local(bmain, &ta->id, &tan->id);
return tan;
}
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 8ef950294b8..94a955be801 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -876,10 +876,7 @@ Tex *BKE_texture_copy(Main *bmain, Tex *tex)
BKE_previewimg_id_copy(&texn->id, &tex->id);
- if (ID_IS_LINKED_DATABLOCK(tex)) {
- BKE_id_expand_local(&texn->id);
- BKE_id_lib_local_paths(bmain, tex->id.lib, &texn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &tex->id, &texn->id);
return texn;
}
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index 3b76e456ff7..a56fc0f9abe 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -44,6 +44,7 @@
#include "DNA_scene_types.h"
#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_math_base.h"
#include "BLI_listbase.h"
@@ -55,6 +56,7 @@
#include "BKE_fcurve.h"
#include "BKE_tracking.h"
+#include "BKE_library.h"
#include "BKE_movieclip.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -186,6 +188,133 @@ void BKE_tracking_free(MovieTracking *tracking)
tracking_dopesheet_free(&tracking->dopesheet);
}
+/* Copy the whole list of tracks. */
+static void tracking_tracks_copy(ListBase *tracks_dst, ListBase *tracks_src, GHash *tracks_mapping)
+{
+ MovieTrackingTrack *track_dst, *track_src;
+
+ BLI_listbase_clear(tracks_dst);
+ BLI_ghash_clear(tracks_mapping, NULL, NULL);
+
+ for (track_src = tracks_src->first; track_src != NULL; track_src = track_src->next) {
+ track_dst = MEM_dupallocN(track_src);
+ if (track_src->markers) {
+ track_dst->markers = MEM_dupallocN(track_src->markers);
+ }
+ id_us_plus(&track_dst->gpd->id);
+ BLI_addtail(tracks_dst, track_dst);
+ BLI_ghash_insert(tracks_mapping, track_src, track_dst);
+ }
+}
+
+/* copy the whole list of plane tracks (need whole MovieTracking structures due to embedded pointers to tracks).
+ * WARNING: implies tracking_[dst/src] and their tracks have already been copied. */
+static void tracking_plane_tracks_copy(ListBase *plane_tracks_dst, ListBase *plane_tracks_src, GHash *tracks_mapping)
+{
+ MovieTrackingPlaneTrack *plane_track_dst, *plane_track_src;
+
+ BLI_listbase_clear(plane_tracks_dst);
+
+ for (plane_track_src = plane_tracks_src->first; plane_track_src != NULL; plane_track_src = plane_track_src->next) {
+ plane_track_dst = MEM_dupallocN(plane_tracks_src);
+ if (plane_track_src->markers) {
+ plane_track_dst->markers = MEM_dupallocN(plane_track_src->markers);
+ }
+ plane_track_dst->point_tracks = MEM_mallocN(sizeof(*plane_track_dst->point_tracks) * plane_track_dst->point_tracksnr, __func__);
+ for (int i = 0; i < plane_track_dst->point_tracksnr; i++) {
+ plane_track_dst->point_tracks[i] = BLI_ghash_lookup(tracks_mapping, plane_track_src->point_tracks[i]);
+ }
+ id_us_plus(&plane_track_dst->image->id);
+ BLI_addtail(plane_tracks_dst, plane_track_dst);
+ }
+}
+
+/* Copy reconstruction structure. */
+static void tracking_reconstruction_copy(
+ MovieTrackingReconstruction *reconstruction_dst, MovieTrackingReconstruction *reconstruction_src)
+{
+ *reconstruction_dst = *reconstruction_src;
+ if (reconstruction_src->cameras) {
+ reconstruction_dst->cameras = MEM_dupallocN(reconstruction_src->cameras);
+ }
+}
+
+/* Copy stabilization structure. */
+static void tracking_stabilization_copy(
+ MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src,
+ GHash *tracks_mapping)
+{
+ *stabilization_dst = *stabilization_src;
+ if (stabilization_src->rot_track) {
+ stabilization_dst->rot_track = BLI_ghash_lookup(tracks_mapping, stabilization_src->rot_track);
+ }
+}
+
+/* Copy tracking object. */
+static void tracking_object_copy(
+ MovieTrackingObject *object_dst, MovieTrackingObject *object_src, GHash *tracks_mapping)
+{
+ *object_dst = *object_src;
+ tracking_tracks_copy(&object_dst->tracks, &object_src->tracks, tracks_mapping);
+ tracking_plane_tracks_copy(&object_dst->plane_tracks, &object_src->plane_tracks, tracks_mapping);
+ tracking_reconstruction_copy(&object_dst->reconstruction, &object_src->reconstruction);
+}
+
+/* Copy list of tracking objects. */
+static void tracking_objects_copy(ListBase *objects_dst, ListBase *objects_src, GHash *tracks_mapping)
+{
+ MovieTrackingObject *object_dst, *object_src;
+
+ BLI_listbase_clear(objects_dst);
+
+ for (object_src = objects_src->first; object_src != NULL; object_src = object_src->next) {
+ object_dst = MEM_mallocN(sizeof(*object_dst), __func__);
+ tracking_object_copy(object_dst, object_src, tracks_mapping);
+ BLI_addtail(objects_dst, object_dst);
+ }
+}
+
+/* Copy tracking structure content. */
+void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src)
+{
+ GHash *tracks_mapping = BLI_ghash_ptr_new(__func__);
+
+ *tracking_dst = *tracking_src;
+
+ tracking_tracks_copy(&tracking_dst->tracks, &tracking_src->tracks, tracks_mapping);
+ tracking_plane_tracks_copy(&tracking_dst->plane_tracks, &tracking_src->plane_tracks, tracks_mapping);
+ tracking_reconstruction_copy(&tracking_dst->reconstruction, &tracking_src->reconstruction);
+ tracking_stabilization_copy(&tracking_dst->stabilization, &tracking_src->stabilization, tracks_mapping);
+ if (tracking_src->act_track) {
+ tracking_dst->act_track = BLI_ghash_lookup(tracks_mapping, tracking_src->act_track);
+ }
+ if (tracking_src->act_plane_track) {
+ MovieTrackingPlaneTrack *plane_track_src, *plane_track_dst;
+ for (plane_track_src = tracking_src->plane_tracks.first, plane_track_dst = tracking_dst->plane_tracks.first;
+ !ELEM(NULL, plane_track_src, plane_track_dst);
+ plane_track_src = plane_track_src->next, plane_track_dst = plane_track_dst->next)
+ {
+ if (plane_track_src == tracking_src->act_plane_track) {
+ tracking_dst->act_plane_track = plane_track_dst;
+ break;
+ }
+ }
+ }
+
+ /* Warning! Will override tracks_mapping. */
+ tracking_objects_copy(&tracking_dst->objects, &tracking_src->objects, tracks_mapping);
+
+ /* Those remaining are runtime data, they will be reconstructed as needed, do not bother copying them. */
+ tracking_dst->dopesheet.ok = false;
+ BLI_listbase_clear(&tracking_dst->dopesheet.channels);
+ BLI_listbase_clear(&tracking_dst->dopesheet.coverage_segments);
+
+ tracking_dst->camera.intrinsics = NULL;
+ tracking_dst->stats = NULL;
+
+ BLI_ghash_free(tracks_mapping, NULL, NULL);
+}
+
/* Initialize motion tracking settings to default values,
* used when new movie clip datablock is creating.
*/
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index 78342e9919a..de1e3187a70 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -142,10 +142,7 @@ World *BKE_world_copy(Main *bmain, World *wrld)
BLI_listbase_clear(&wrldn->gpumaterial);
- if (ID_IS_LINKED_DATABLOCK(wrld)) {
- BKE_id_expand_local(&wrldn->id);
- BKE_id_lib_local_paths(bmain, wrld->id.lib, &wrldn->id);
- }
+ BKE_id_copy_ensure_local(bmain, &wrld->id, &wrldn->id);
return wrldn;
}
diff --git a/source/blender/blenlib/intern/BLI_heap.c b/source/blender/blenlib/intern/BLI_heap.c
index 0a8dafc2dc1..d7fd1caa8da 100644
--- a/source/blender/blenlib/intern/BLI_heap.c
+++ b/source/blender/blenlib/intern/BLI_heap.c
@@ -55,9 +55,10 @@ struct HeapNode_Chunk {
* or we allocate past the reserved number.
*
* \note Optimize number for 64kb allocs.
+ * \note keep type in sync with tot_nodes in heap_node_alloc_chunk.
*/
#define HEAP_CHUNK_DEFAULT_NUM \
- ((MEM_SIZE_OPTIMAL((1 << 16) - sizeof(struct HeapNode_Chunk))) / sizeof(HeapNode))
+ ((unsigned int)((MEM_SIZE_OPTIMAL((1 << 16) - sizeof(struct HeapNode_Chunk))) / sizeof(HeapNode)))
struct Heap {
unsigned int size;
diff --git a/source/blender/blenlib/intern/scanfill_utils.c b/source/blender/blenlib/intern/scanfill_utils.c
index 059d5cb05a4..3b6ab99ae86 100644
--- a/source/blender/blenlib/intern/scanfill_utils.c
+++ b/source/blender/blenlib/intern/scanfill_utils.c
@@ -257,7 +257,7 @@ static bool scanfill_preprocess_self_isect(
if (UNLIKELY(e_ls == NULL)) {
/* only happens in very rare cases (entirely overlapping splines).
- * in this case se can't do much useful. but at least don't crash */
+ * in this case we can't do much useful. but at least don't crash */
continue;
}
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index 479d3a15e6c..8cb9ef837b2 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -77,6 +77,13 @@ if(WITH_CODEC_FFMPEG)
add_definitions(-DWITH_FFMPEG)
endif()
+if(WITH_ALEMBIC)
+ list(APPEND INC
+ ../alembic
+ )
+ add_definitions(-DWITH_ALEMBIC)
+endif()
+
blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}")
# needed so writefile.c can use dna_type_offsets.h
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index c7dec460b3c..8c15940d9c9 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -59,6 +59,7 @@
#include "DNA_actuator_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_cloth_types.h"
#include "DNA_controller_types.h"
#include "DNA_constraint_types.h"
@@ -114,6 +115,7 @@
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_brush.h"
+#include "BKE_cachefile.h"
#include "BKE_cloth.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@@ -144,7 +146,7 @@
#include "BKE_sequencer.h"
#include "BKE_outliner_treehash.h"
#include "BKE_sound.h"
-
+#include "BKE_colortools.h"
#include "NOD_common.h"
#include "NOD_socket.h"
@@ -2691,6 +2693,36 @@ static void direct_link_animdata(FileData *fd, AnimData *adt)
adt->actstrip = newdataadr(fd, adt->actstrip);
}
+/* ************ READ CACHEFILES *************** */
+
+static void lib_link_cachefiles(FileData *fd, Main *bmain)
+{
+ CacheFile *cache_file;
+
+ /* only link ID pointers */
+ for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
+ if (cache_file->id.tag & LIB_TAG_NEED_LINK) {
+ cache_file->id.tag &= ~LIB_TAG_NEED_LINK;
+ }
+
+ BLI_listbase_clear(&cache_file->object_paths);
+ cache_file->handle = NULL;
+
+ if (cache_file->adt) {
+ lib_link_animdata(fd, &cache_file->id, cache_file->adt);
+ }
+ }
+}
+
+static void direct_link_cachefile(FileData *fd, CacheFile *cache_file)
+{
+ cache_file->handle = NULL;
+
+ /* relink animdata */
+ cache_file->adt = newdataadr(fd, cache_file->adt);
+ direct_link_animdata(fd, cache_file->adt);
+}
+
/* ************ READ MOTION PATHS *************** */
/* direct data for cache */
@@ -4034,6 +4066,7 @@ static void lib_link_particlesettings(FileData *fd, Main *main)
part->dup_group = newlibadr(fd, part->id.lib, part->dup_group);
part->eff_group = newlibadr(fd, part->id.lib, part->eff_group);
part->bb_ob = newlibadr(fd, part->id.lib, part->bb_ob);
+ part->collision_group = newlibadr(fd, part->id.lib, part->collision_group);
lib_link_partdeflect(fd, &part->id, part->pd);
lib_link_partdeflect(fd, &part->id, part->pd2);
@@ -4885,8 +4918,11 @@ static void lib_link_object(FileData *fd, Main *main)
if (ob->pd)
lib_link_partdeflect(fd, &ob->id, ob->pd);
- if (ob->soft)
+ if (ob->soft) {
+ ob->soft->collision_group = newlibadr(fd, ob->id.lib, ob->soft->collision_group);
+
ob->soft->effector_weights->group = newlibadr(fd, ob->id.lib, ob->soft->effector_weights->group);
+ }
lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem);
lib_link_modifiers(fd, ob);
@@ -5865,6 +5901,22 @@ static void direct_link_scene(FileData *fd, Scene *sce)
sce->toolsettings->wpaint->wpaint_prev = NULL;
sce->toolsettings->wpaint->tot = 0;
}
+ /* relink grease pencil drawing brushes */
+ link_list(fd, &sce->toolsettings->gp_brushes);
+ for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) {
+ brush->cur_sensitivity = newdataadr(fd, brush->cur_sensitivity);
+ if (brush->cur_sensitivity) {
+ direct_link_curvemapping(fd, brush->cur_sensitivity);
+ }
+ brush->cur_strength = newdataadr(fd, brush->cur_strength);
+ if (brush->cur_strength) {
+ direct_link_curvemapping(fd, brush->cur_strength);
+ }
+ brush->cur_jitter = newdataadr(fd, brush->cur_jitter);
+ if (brush->cur_jitter) {
+ direct_link_curvemapping(fd, brush->cur_jitter);
+ }
+ }
}
if (sce->ed) {
@@ -6147,7 +6199,8 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
bGPDlayer *gpl;
bGPDframe *gpf;
bGPDstroke *gps;
-
+ bGPDpalette *palette;
+
/* we must firstly have some grease-pencil data to link! */
if (gpd == NULL)
return;
@@ -6155,11 +6208,19 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
/* relink animdata */
gpd->adt = newdataadr(fd, gpd->adt);
direct_link_animdata(fd, gpd->adt);
-
+
+ /* relink palettes */
+ link_list(fd, &gpd->palettes);
+ for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ link_list(fd, &palette->colors);
+ }
+
/* relink layers */
link_list(fd, &gpd->layers);
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* parent */
+ gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent);
/* relink frames */
link_list(fd, &gpl->frames);
gpl->actframe = newdataadr(fd, gpl->actframe);
@@ -6172,9 +6233,12 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
gps->points = newdataadr(fd, gps->points);
/* the triangulation is not saved, so need to be recalculated */
- gps->flag |= GP_STROKE_RECALC_CACHES;
gps->triangles = NULL;
gps->tot_triangles = 0;
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ /* the color pointer is not saved, so need to be recalculated using the color name */
+ gps->palcolor = NULL;
+ gps->flag |= GP_STROKE_RECALC_COLOR;
}
}
}
@@ -6331,10 +6395,8 @@ static void lib_link_screen(FileData *fd, Main *main)
snode->id = newlibadr(fd, sc->id.lib, snode->id);
snode->from = newlibadr(fd, sc->id.lib, snode->from);
- if (snode->id) {
- ntree = ntreeFromID(snode->id);
- snode->nodetree = ntree ? ntree : newlibadr_us(fd, sc->id.lib, snode->nodetree);
- }
+ ntree = snode->id ? ntreeFromID(snode->id) : NULL;
+ snode->nodetree = ntree ? ntree : newlibadr_us(fd, sc->id.lib, snode->nodetree);
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
@@ -6713,11 +6775,8 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
snode->id = restore_pointer_by_name(id_map, snode->id, USER_REAL);
snode->from = restore_pointer_by_name(id_map, snode->from, USER_IGNORE);
- if (snode->id) {
- ntree = ntreeFromID(snode->id);
- snode->nodetree = ntree ? ntree :
- restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL);
- }
+ ntree = snode->id ? ntreeFromID(snode->id) : NULL;
+ snode->nodetree = ntree ? ntree : restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL);
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
@@ -7468,6 +7527,7 @@ static void lib_link_movieclip(FileData *fd, Main *main)
for (object = tracking->objects.first; object; object = object->next) {
lib_link_movieTracks(fd, clip, &object->tracks);
+ lib_link_moviePlaneTracks(fd, clip, &object->plane_tracks);
}
clip->id.tag &= ~LIB_TAG_NEED_LINK;
@@ -7881,6 +7941,7 @@ static const char *dataname(short id_code)
case ID_MC: return "Data from MC";
case ID_MSK: return "Data from MSK";
case ID_LS: return "Data from LS";
+ case ID_CF: return "Data from CF";
}
return "Data from Lib Block";
@@ -8132,6 +8193,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
case ID_PC:
direct_link_paint_curve(fd, (PaintCurve *)id);
break;
+ case ID_CF:
+ direct_link_cachefile(fd, (CacheFile *)id);
+ break;
}
oldnewmap_free_unused(fd->datamap);
@@ -8325,6 +8389,7 @@ static void lib_link_all(FileData *fd, Main *main)
lib_link_mask(fd, main);
lib_link_linestyle(fd, main);
lib_link_gpencil(fd, main);
+ lib_link_cachefiles(fd, main);
lib_link_mesh(fd, main); /* as last: tpage images with users at zero */
@@ -8836,6 +8901,7 @@ static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSetting
expand_doit(fd, mainvar, part->dup_group);
expand_doit(fd, mainvar, part->eff_group);
expand_doit(fd, mainvar, part->bb_ob);
+ expand_doit(fd, mainvar, part->collision_group);
if (part->adt)
expand_animdata(fd, mainvar, part->adt);
@@ -8846,6 +8912,37 @@ static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSetting
expand_doit(fd, mainvar, part->mtex[a]->object);
}
}
+
+ if (part->effector_weights) {
+ expand_doit(fd, mainvar, part->effector_weights->group);
+ }
+
+ if (part->pd) {
+ expand_doit(fd, mainvar, part->pd->tex);
+ expand_doit(fd, mainvar, part->pd->f_source);
+ }
+ if (part->pd2) {
+ expand_doit(fd, mainvar, part->pd2->tex);
+ expand_doit(fd, mainvar, part->pd2->f_source);
+ }
+
+ if (part->boids) {
+ BoidState *state;
+ BoidRule *rule;
+
+ for (state = part->boids->states.first; state; state = state->next) {
+ for (rule = state->rules.first; rule; rule = rule->next) {
+ if (rule->type == eBoidRuleType_Avoid) {
+ BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
+ expand_doit(fd, mainvar, gabr->ob);
+ }
+ else if (rule->type == eBoidRuleType_FollowLeader) {
+ BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule;
+ expand_doit(fd, mainvar, flbr->ob);
+ }
+ }
+ }
+ }
}
static void expand_group(FileData *fd, Main *mainvar, Group *group)
@@ -9212,7 +9309,7 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
for (psys = ob->particlesystem.first; psys; psys = psys->next)
expand_doit(fd, mainvar, psys->part);
-
+
for (sens = ob->sensors.first; sens; sens = sens->next) {
if (sens->type == SENS_MESSAGE) {
bMessageSensor *ms = sens->data;
@@ -9291,8 +9388,18 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
}
}
- if (ob->pd && ob->pd->tex)
+ if (ob->pd) {
expand_doit(fd, mainvar, ob->pd->tex);
+ expand_doit(fd, mainvar, ob->pd->f_source);
+ }
+
+ if (ob->soft) {
+ expand_doit(fd, mainvar, ob->soft->collision_group);
+
+ if (ob->soft->effector_weights) {
+ expand_doit(fd, mainvar, ob->soft->effector_weights->group);
+ }
+ }
if (ob->rigidbody_constraint) {
expand_doit(fd, mainvar, ob->rigidbody_constraint->ob1);
@@ -9392,6 +9499,13 @@ static void expand_camera(FileData *fd, Main *mainvar, Camera *ca)
expand_animdata(fd, mainvar, ca->adt);
}
+static void expand_cachefile(FileData *fd, Main *mainvar, CacheFile *cache_file)
+{
+ if (cache_file->adt) {
+ expand_animdata(fd, mainvar, cache_file->adt);
+ }
+}
+
static void expand_speaker(FileData *fd, Main *mainvar, Speaker *spk)
{
expand_doit(fd, mainvar, spk->sound);
@@ -9587,6 +9701,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
case ID_GD:
expand_gpencil(fd, mainvar, (bGPdata *)id);
break;
+ case ID_CF:
+ expand_cachefile(fd, mainvar, (CacheFile *)id);
+ break;
}
do_it = true;
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index a254a854c66..af95649a559 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -63,6 +63,7 @@
#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "BKE_screen.h"
+#include "BKE_gpencil.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
@@ -1075,15 +1076,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
- /* init grease pencil smooth level iterations */
- for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) {
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- if (gpl->draw_smoothlvl == 0) {
- gpl->draw_smoothlvl = 1;
- }
- }
- }
-
for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
@@ -1192,7 +1184,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
for (Camera *camera = main->camera.first; camera != NULL; camera = camera->id.next) {
if (camera->stereo.pole_merge_angle_from == 0.0f &&
- camera->stereo.pole_merge_angle_to == 0.0f)
+ camera->stereo.pole_merge_angle_to == 0.0f)
{
camera->stereo.pole_merge_angle_from = DEG2RADF(60.0f);
camera->stereo.pole_merge_angle_to = DEG2RADF(75.0f);
@@ -1231,5 +1223,102 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
br->flag |= BRUSH_ACCUMULATE;
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "time_scale")) {
+ Object *ob;
+ ModifierData *md;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Cloth) {
+ ClothModifierData *clmd = (ClothModifierData *)md;
+ clmd->sim_parms->time_scale = 1.0f;
+ }
+ else if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
+ if (pmd->psys->clmd) {
+ pmd->psys->clmd->sim_parms->time_scale = 1.0f;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(main, 277, 3)) {
+ /* ------- init of grease pencil initialization --------------- */
+ if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "bGPDpalettecolor", "palcolor")) {
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ ToolSettings *ts = scene->toolsettings;
+ /* initialize use position for sculpt brushes */
+ ts->gp_sculpt.flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
+ /* initialize selected vertices alpha factor */
+ ts->gp_sculpt.alpha = 1.0f;
+
+ /* new strength sculpt brush */
+ if (ts->gp_sculpt.brush[0].size >= 11) {
+ GP_BrushEdit_Settings *gset = &ts->gp_sculpt;
+ GP_EditBrush_Data *brush;
+
+ brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH];
+ brush->size = 25;
+ brush->strength = 0.5f;
+ brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
+ }
+ }
+ /* create a default grease pencil drawing brushes set */
+ if (!BLI_listbase_is_empty(&main->gpencil)) {
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ ToolSettings *ts = scene->toolsettings;
+ if (BLI_listbase_is_empty(&ts->gp_brushes)) {
+ BKE_gpencil_brush_init_presets(ts);
+ }
+ }
+ }
+ /* Convert Grease Pencil to new palettes/brushes
+ * Loop all strokes and create the palette and all colors
+ */
+ for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) {
+ if (BLI_listbase_is_empty(&gpd->palettes)) {
+ /* create palette */
+ bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, "GP_Palette", true);
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* create color using layer name */
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_addnew(palette, gpl->info, true);
+ if (palcolor != NULL) {
+ /* set color attributes */
+ copy_v4_v4(palcolor->color, gpl->color);
+ copy_v4_v4(palcolor->fill, gpl->fill);
+ palcolor->flag = gpl->flag;
+ /* set layer opacity to 1 */
+ gpl->opacity = 1.0f;
+ /* set tint color */
+ ARRAY_SET_ITEMS(gpl->tintcolor, 0.0f, 0.0f, 0.0f, 0.0f);
+
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* set stroke to palette and force recalculation */
+ strcpy(gps->colorname, gpl->info);
+ gps->palcolor = NULL;
+ gps->flag |= GP_STROKE_RECALC_COLOR;
+ gps->thickness = gpl->thickness;
+ /* set alpha strength to 1 */
+ for (int i = 0; i < gps->totpoints; i++) {
+ gps->points[i].strength = 1.0f;
+ }
+
+ }
+ }
+ }
+ /* set thickness to 0 (now it is a factor to override stroke thickness) */
+ gpl->thickness = 0.0f;
+ }
+ /* set first color as active */
+ if (palette->colors.first)
+ BKE_gpencil_palettecolor_setactive(palette, palette->colors.first);
+ }
+ }
+ }
+ /* ------- end of grease pencil initialization --------------- */
}
+
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 0ed7a397e0b..ec817b9b261 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -108,6 +108,11 @@ void BLO_update_defaults_startup_blend(Main *bmain)
brush->strength = 0.5f;
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
+ brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH];
+ brush->size = 25;
+ brush->strength = 0.5f;
+ brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
+
brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB];
brush->size = 50;
brush->strength = 0.3f;
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 379e52c925b..84595b2f4a0 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -107,6 +107,7 @@
#include "DNA_armature_types.h"
#include "DNA_actuator_types.h"
#include "DNA_brush_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_constraint_types.h"
@@ -2668,6 +2669,20 @@ static void write_scenes(WriteData *wd, ListBase *scebase)
writestruct(wd, DATA, UvSculpt, 1, tos->uvsculpt);
write_paint(wd, &tos->uvsculpt->paint);
}
+ /* write grease-pencil drawing brushes to file */
+ writelist(wd, DATA, bGPDbrush, &tos->gp_brushes);
+ for (bGPDbrush *brush = tos->gp_brushes.first; brush; brush = brush->next) {
+ if (brush->cur_sensitivity) {
+ write_curvemapping(wd, brush->cur_sensitivity);
+ }
+ if (brush->cur_strength) {
+ write_curvemapping(wd, brush->cur_strength);
+ }
+ if (brush->cur_jitter) {
+ write_curvemapping(wd, brush->cur_jitter);
+ }
+ }
+
write_paint(wd, &tos->imapaint.paint);
@@ -2830,6 +2845,7 @@ static void write_gpencils(WriteData *wd, ListBase *lb)
bGPDlayer *gpl;
bGPDframe *gpf;
bGPDstroke *gps;
+ bGPDpalette *palette;
for (gpd = lb->first; gpd; gpd = gpd->id.next) {
if (gpd->id.us > 0 || wd->current) {
@@ -2856,6 +2872,11 @@ static void write_gpencils(WriteData *wd, ListBase *lb)
}
}
}
+ /* write grease-pencil palettes */
+ writelist(wd, DATA, bGPDpalette, &gpd->palettes);
+ for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ writelist(wd, DATA, bGPDpalettecolor, &palette->colors);
+ }
}
}
@@ -3857,6 +3878,21 @@ static void write_linestyles(WriteData *wd, ListBase *idbase)
}
}
+static void write_cachefiles(WriteData *wd, ListBase *idbase)
+{
+ CacheFile *cache_file;
+
+ for (cache_file = idbase->first; cache_file; cache_file = cache_file->id.next) {
+ if (cache_file->id.us > 0 || wd->current) {
+ writestruct(wd, ID_CF, CacheFile, 1, cache_file);
+
+ if (cache_file->adt) {
+ write_animdata(wd, cache_file->adt);
+ }
+ }
+ }
+}
+
/* Keep it last of write_foodata functions. */
static void write_libraries(WriteData *wd, Main *main)
{
@@ -4054,6 +4090,7 @@ static bool write_file_handle(
write_paintcurves(wd, &mainvar->paintcurves);
write_gpencils(wd, &mainvar->gpencil);
write_linestyles(wd, &mainvar->linestyle);
+ write_cachefiles(wd, &mainvar->cachefiles);
write_libraries(wd, mainvar->next);
/* So changes above don't cause a 'DNA1' to be detected as changed on undo. */
diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h
index 3838e8c827c..1d76077c9f1 100644
--- a/source/blender/blentranslation/BLT_translation.h
+++ b/source/blender/blentranslation/BLT_translation.h
@@ -120,6 +120,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_ARMATURE "Armature"
#define BLT_I18NCONTEXT_ID_BRUSH "Brush"
#define BLT_I18NCONTEXT_ID_CAMERA "Camera"
+#define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile"
#define BLT_I18NCONTEXT_ID_CURVE "Curve"
#define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle"
#define BLT_I18NCONTEXT_ID_GPENCIL "GPencil"
@@ -171,6 +172,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ARMATURE, "id_armature"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_BRUSH, "id_brush"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \
diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c
index 9b074dc4db4..b5f9575aff5 100644
--- a/source/blender/bmesh/intern/bmesh_queries.c
+++ b/source/blender/bmesh/intern/bmesh_queries.c
@@ -2065,7 +2065,7 @@ bool BM_face_exists_multi(BMVert **varr, BMEdge **earr, int len)
}
/* 2) loop over non-boundary edges that use boundary verts,
- * check each have 2 tagges faces connected (faces that only use 'varr' verts) */
+ * check each have 2 tagged faces connected (faces that only use 'varr' verts) */
ok = true;
for (i = 0; i < len; i++) {
BM_ITER_ELEM (e, &fiter, varr[i], BM_EDGES_OF_VERT) {
diff --git a/source/blender/compositor/nodes/COM_FilterNode.cpp b/source/blender/compositor/nodes/COM_FilterNode.cpp
index 7493f24ba6b..e8b08ce2ce1 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.cpp
+++ b/source/blender/compositor/nodes/COM_FilterNode.cpp
@@ -49,7 +49,7 @@ void FilterNode::convertToOperations(NodeConverter &converter, const CompositorC
operation->set3x3Filter(-1, -1, -1, -1, 9, -1, -1, -1, -1);
break;
case CMP_FILT_LAPLACE:
- operation = new ConvolutionFilterOperation();
+ operation = new ConvolutionEdgeFilterOperation();
operation->set3x3Filter(-1 / 8.0f, -1 / 8.0f, -1 / 8.0f, -1 / 8.0f, 1.0f, -1 / 8.0f, -1 / 8.0f, -1 / 8.0f, -1 / 8.0f);
break;
case CMP_FILT_SOBEL:
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index d1de83ec8a9..945a4785b9c 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -168,7 +168,7 @@ void DEG_evaluate_on_framechange(struct EvaluationContext *eval_ctx,
struct Main *bmain,
Depsgraph *graph,
float ctime,
- const int layer);
+ const unsigned int layer);
/* Data changed recalculation entry point.
* < context_type: context to perform evaluation for
@@ -176,7 +176,7 @@ void DEG_evaluate_on_framechange(struct EvaluationContext *eval_ctx,
*/
void DEG_evaluate_on_refresh_ex(struct EvaluationContext *eval_ctx,
Depsgraph *graph,
- const int layers);
+ const unsigned int layers);
/* Data changed recalculation entry point.
* < context_type: context to perform evaluation for
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index ce30df29e26..98ccce7e34e 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -81,6 +81,7 @@ void DEG_scene_graph_free(struct Scene *scene);
* as a symbolic reference to the current DepsNode.
* All relations will be defined in reference to that node.
*/
+struct CacheFile;
typedef enum {
DEG_COMPONENT_PARAMETERS, /* Parameters Component - Default when nothing else fits (i.e. just SDNA property setting) */
@@ -93,6 +94,7 @@ typedef enum {
DEG_COMPONENT_BONE, /* Bone Component - Child/Subcomponent of Pose */
DEG_COMPONENT_EVAL_PARTICLES, /* Particle Systems Component */
DEG_COMPONENT_SHADING, /* Material Shading Component */
+ DEG_COMPONENT_CACHE, /* Cache Component */
} eDepsComponent;
struct DepsNodeHandle
@@ -122,6 +124,10 @@ struct DepsNodeHandle
struct Image *ima,
eDepsComponent component,
const char *description);
+ void (*add_cache_relation)(struct DepsNodeHandle *handle,
+ struct CacheFile *cache_file,
+ eDepsComponent component,
+ const char *description);
};
void DEG_add_scene_relation(struct DepsNodeHandle *node,
@@ -149,6 +155,10 @@ void DEG_add_image_relation(struct DepsNodeHandle *handle,
struct Image *ima,
eDepsComponent component,
const char *description);
+void DEG_add_cache_relation(struct DepsNodeHandle *handle,
+ struct CacheFile *cache_file,
+ eDepsComponent 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);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index 7ca2ef05a36..42b00438d33 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -140,6 +140,7 @@ DEG::eDepsNode_Type deg_build_get_component_type(
case DEG_COMPONENT_BONE: return DEG::DEPSNODE_TYPE_BONE;
case DEG_COMPONENT_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES;
case DEG_COMPONENT_SHADING: return DEG::DEPSNODE_TYPE_SHADING;
+ case DEG_COMPONENT_CACHE: return DEG::DEPSNODE_TYPE_CACHE;
}
return DEG::DEPSNODE_TYPE_UNDEFINED;
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 9c0f88a8042..71b2cc16871 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -46,6 +46,7 @@ extern "C" {
#include "DNA_action_types.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
@@ -133,6 +134,15 @@ struct NodeBuilderHandle
handle->builder->build_image(ima);
}
+ static void add_cache_relation(DepsNodeHandle *_handle,
+ CacheFile *cache_file,
+ eDepsComponent component,
+ const char *description)
+ {
+ NodeBuilderHandle *handle = (NodeBuilderHandle *)_handle;
+ handle->builder->build_cachefile(cache_file);
+ }
+
NodeBuilderHandle(DepsgraphNodeBuilder *builder) :
builder(builder)
{
@@ -140,6 +150,7 @@ struct NodeBuilderHandle
base.add_texture_relation = add_texture_relation;
base.add_nodetree_relation = add_nodetree_relation;
base.add_image_relation = add_image_relation;
+ base.add_cache_relation = add_cache_relation;
}
DepsNodeHandle base;
@@ -374,6 +385,14 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene)
if (scene->gpd) {
build_gpencil(scene->gpd);
}
+
+ /* cache files */
+ for (CacheFile *cachefile = static_cast<CacheFile *>(bmain->cachefiles.first);
+ cachefile;
+ cachefile = static_cast<CacheFile *>(cachefile->id.next))
+ {
+ build_cachefile(cachefile);
+ }
}
void DepsgraphNodeBuilder::build_group(Main *bmain,
@@ -1345,4 +1364,18 @@ void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd)
build_animdata(gpd_id);
}
+void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file)
+{
+ ID *cache_file_id = &cache_file->id;
+
+ add_component_node(cache_file_id, DEPSNODE_TYPE_CACHE);
+
+ add_operation_node(cache_file_id, DEPSNODE_TYPE_CACHE,
+ DEPSOP_TYPE_EXEC, NULL,
+ DEG_OPCODE_PLACEHOLDER, "Cache File Update");
+
+ add_id_node(cache_file_id);
+ build_animdata(cache_file_id);
+}
+
} // namespace DEG
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 35ef26713fd..e61c7572327 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -33,6 +33,7 @@
#include "intern/depsgraph_types.h"
struct Base;
+struct CacheFile;
struct bGPdata;
struct ListBase;
struct GHash;
@@ -148,6 +149,7 @@ struct DepsgraphNodeBuilder {
void build_world(World *world);
void build_compositor(Scene *scene);
void build_gpencil(bGPdata *gpd);
+ void build_cachefile(CacheFile *cache_file);
protected:
Main *m_bmain;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 17d36c9645d..2e8fee7aea6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -47,6 +47,7 @@ extern "C" {
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_effect_types.h"
@@ -174,6 +175,16 @@ struct RelationBuilderHandle
handle->builder->add_node_relation(comp_key, handle->node, DEPSREL_TYPE_STANDARD, description);
}
+ static void add_cache_relation(DepsNodeHandle *_handle,
+ CacheFile *cache_file,
+ eDepsComponent component,
+ const char *description)
+ {
+ RelationBuilderHandle *handle = cast(_handle);
+ ComponentKey comp_key(&cache_file->id, deg_build_get_component_type(component));
+ handle->builder->add_node_relation(comp_key, handle->node, DEG::DEPSREL_TYPE_CACHE, description);
+ }
+
RelationBuilderHandle(DepsgraphRelationBuilder *builder, OperationDepsNode *node, const string &default_name = "") :
builder(builder),
node(node),
@@ -187,6 +198,7 @@ struct RelationBuilderHandle
base.add_texture_relation = add_texture_relation;
base.add_nodetree_relation = add_nodetree_relation;
base.add_image_relation = add_image_relation;
+ base.add_cache_relation = add_cache_relation;
}
DepsNodeHandle base;
@@ -683,6 +695,18 @@ void DepsgraphRelationBuilder::build_constraints(Scene *scene, ID *id, eDepsNode
TimeSourceKey time_src_key;
add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]");
}
+ else if (cti->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
+ /* TODO(kevin): This is more a TimeSource -> CacheFile -> Constraint dependency chain. */
+ TimeSourceKey time_src_key;
+ add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]");
+
+ bTransformCacheConstraint *data = (bTransformCacheConstraint *)con->data;
+
+ if (data->cache_file) {
+ ComponentKey cache_key(&data->cache_file->id, DEPSNODE_TYPE_CACHE);
+ add_relation(cache_key, constraint_op_key, DEPSREL_TYPE_CACHE, cti->name);
+ }
+ }
else if (cti->get_constraint_targets) {
ListBase targets = {NULL, NULL};
cti->get_constraint_targets(con, &targets);
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
index 5ce84ee29db..70cd5f11a47 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
@@ -90,6 +90,7 @@ static const int deg_debug_node_type_color_map[][2] = {
{DEPSNODE_TYPE_GEOMETRY, 8},
{DEPSNODE_TYPE_SEQUENCER, 9},
{DEPSNODE_TYPE_SHADING, 10},
+ {DEPSNODE_TYPE_CACHE, 11},
{-1, 0}
};
#endif
@@ -288,7 +289,7 @@ static void deg_debug_graphviz_node_single(const DebugContext &ctx,
if (node->type == DEPSNODE_TYPE_ID_REF) {
IDDepsNode *id_node = (IDDepsNode *)node;
char buf[256];
- BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers);
+ BLI_snprintf(buf, sizeof(buf), " (Layers: %u)", id_node->layers);
name += buf;
}
if (ctx.show_eval_priority && node->tclass == DEPSNODE_CLASS_OPERATION) {
@@ -324,7 +325,7 @@ static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx,
if (node->type == DEPSNODE_TYPE_ID_REF) {
IDDepsNode *id_node = (IDDepsNode *)node;
char buf[256];
- BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers);
+ BLI_snprintf(buf, sizeof(buf), " (Layers: %u)", id_node->layers);
name += buf;
}
deg_debug_fprintf(ctx, "// %s\n", name.c_str());
@@ -401,6 +402,7 @@ static void deg_debug_graphviz_node(const DebugContext &ctx,
case DEPSNODE_TYPE_EVAL_POSE:
case DEPSNODE_TYPE_BONE:
case DEPSNODE_TYPE_SHADING:
+ case DEPSNODE_TYPE_CACHE:
case DEPSNODE_TYPE_EVAL_PARTICLES:
{
ComponentDepsNode *comp_node = (ComponentDepsNode *)node;
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index 213bb304d73..08b264f8497 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -191,7 +191,7 @@ struct Depsgraph {
/* Layers Visibility .................. */
/* Visible layers bitfield, used for skipping invisible objects updates. */
- int layers;
+ unsigned int layers;
// XXX: additional stuff like eval contexts, mempools for allocating nodes from, etc.
};
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 67707834076..6c4b7229a41 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -33,6 +33,7 @@
#include "MEM_guardedalloc.h"
extern "C" {
+#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -120,6 +121,15 @@ void DEG_add_image_relation(struct DepsNodeHandle *handle,
handle->add_image_relation(handle, ima, component, description);
}
+void DEG_add_cache_relation(struct DepsNodeHandle *handle,
+ struct CacheFile *cache_file,
+ eDepsComponent component,
+ const char *description)
+{
+ if (handle->add_cache_relation)
+ handle->add_cache_relation(handle, cache_file, component, description);
+}
+
void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag)
{
DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc
index ab82421195f..2e4904e4281 100644
--- a/source/blender/depsgraph/intern/depsgraph_eval.cc
+++ b/source/blender/depsgraph/intern/depsgraph_eval.cc
@@ -133,7 +133,7 @@ void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx,
Main *bmain,
Depsgraph *graph,
float ctime,
- const int layers)
+ const unsigned int layers)
{
DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
/* Update time on primary timesource. */
diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index cac4eaae215..7f2f6a65f5e 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -33,6 +33,7 @@
#include "MEM_guardedalloc.h"
extern "C" {
+#include "BKE_idcode.h"
#include "BKE_main.h"
#include "DEG_depsgraph_query.h"
@@ -42,7 +43,7 @@ extern "C" {
bool DEG_id_type_tagged(Main *bmain, short idtype)
{
- return bmain->id_tag_update[((unsigned char *)&idtype)[0]] != 0;
+ return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0;
}
short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id)
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index ea5afaab3f7..b7b62bd59f9 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -44,6 +44,7 @@ extern "C" {
#include "BLI_task.h"
+#include "BKE_idcode.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_node.h"
@@ -259,10 +260,8 @@ void DEG_id_type_tag(Main *bmain, short idtype)
DEG_id_type_tag(bmain, ID_WO);
DEG_id_type_tag(bmain, ID_SCE);
}
- /* We tag based on first ID type character to avoid
- * looping over all ID's in case there are no tags.
- */
- bmain->id_tag_update[((unsigned char *)&idtype)[0]] = 1;
+
+ bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1;
}
/* Recursively push updates out to all nodes dependent on this,
@@ -373,10 +372,7 @@ void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time)
ListBase *lb = lbarray[a];
ID *id = (ID *)lb->first;
- /* We tag based on first ID type character to avoid
- * looping over all ID's in case there are no tags.
- */
- if (id && bmain->id_tag_update[(unsigned char)id->name[0]]) {
+ if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) {
updated = true;
break;
}
@@ -401,10 +397,7 @@ void DEG_ids_clear_recalc(Main *bmain)
ListBase *lb = lbarray[a];
ID *id = (ID *)lb->first;
- /* We tag based on first ID type character to avoid
- * looping over all ID's in case there are no tags.
- */
- if (id && bmain->id_tag_update[(unsigned char)id->name[0]]) {
+ if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) {
for (; id; id = (ID *)id->next) {
id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA);
diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h
index a0157190b9e..1e20bbdd142 100644
--- a/source/blender/depsgraph/intern/depsgraph_types.h
+++ b/source/blender/depsgraph/intern/depsgraph_types.h
@@ -133,6 +133,8 @@ typedef enum eDepsNode_Type {
DEPSNODE_TYPE_EVAL_PARTICLES = 23,
/* Material Shading Component */
DEPSNODE_TYPE_SHADING = 24,
+ /* Cache Component */
+ DEPSNODE_TYPE_CACHE = 25,
} eDepsNode_Type;
/* Identifiers for common operations (as an enum). */
@@ -347,6 +349,9 @@ typedef enum eDepsRelation_Type {
/* relationship is used to trigger editor/screen updates */
DEPSREL_TYPE_UPDATE_UI,
+
+ /* cache dependency */
+ DEPSREL_TYPE_CACHE,
} eDepsRelation_Type;
} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 3024d625846..c3fd202d832 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -72,13 +72,13 @@ namespace DEG {
static void schedule_children(TaskPool *pool,
Depsgraph *graph,
OperationDepsNode *node,
- const int layers,
+ const unsigned int layers,
const int thread_id);
struct DepsgraphEvalState {
EvaluationContext *eval_ctx;
Depsgraph *graph;
- int layers;
+ unsigned int layers;
};
static void deg_task_run_func(TaskPool *pool,
@@ -140,7 +140,7 @@ static void deg_task_run_func(TaskPool *pool,
OperationDepsNode *child = (OperationDepsNode *)rel->to;
BLI_assert(child->type == DEPSNODE_TYPE_OPERATION);
if (!child->scheduled) {
- int id_layers = child->owner->owner->layers;
+ unsigned int id_layers = child->owner->owner->layers;
if (!((child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 &&
(id_layers & state->layers) != 0))
{
@@ -197,14 +197,14 @@ static void deg_task_run_func(TaskPool *pool,
typedef struct CalculatePengindData {
Depsgraph *graph;
- int layers;
+ unsigned int layers;
} CalculatePengindData;
static void calculate_pending_func(void *data_v, int i)
{
CalculatePengindData *data = (CalculatePengindData *)data_v;
Depsgraph *graph = data->graph;
- int layers = data->layers;
+ unsigned int layers = data->layers;
OperationDepsNode *node = graph->operations[i];
IDDepsNode *id_node = node->owner->owner;
@@ -231,7 +231,7 @@ static void calculate_pending_func(void *data_v, int i)
}
}
-static void calculate_pending_parents(Depsgraph *graph, int layers)
+static void calculate_pending_parents(Depsgraph *graph, unsigned int layers)
{
const int num_operations = graph->operations.size();
const bool do_threads = num_operations > 256;
@@ -276,11 +276,11 @@ static void calculate_eval_priority(OperationDepsNode *node)
* dec_parents: Decrement pending parents count, true when child nodes are
* scheduled after a task has been completed.
*/
-static void schedule_node(TaskPool *pool, Depsgraph *graph, int layers,
+static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers,
OperationDepsNode *node, bool dec_parents,
const int thread_id)
{
- int id_layers = node->owner->owner->layers;
+ unsigned int id_layers = node->owner->owner->layers;
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 &&
(id_layers & layers) != 0)
@@ -314,7 +314,7 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, int layers,
static void schedule_graph(TaskPool *pool,
Depsgraph *graph,
- const int layers)
+ const unsigned int layers)
{
foreach (OperationDepsNode *node, graph->operations) {
schedule_node(pool, graph, layers, node, false, 0);
@@ -324,7 +324,7 @@ static void schedule_graph(TaskPool *pool,
static void schedule_children(TaskPool *pool,
Depsgraph *graph,
OperationDepsNode *node,
- const int layers,
+ const unsigned int layers,
const int thread_id)
{
foreach (DepsRelation *rel, node->outlinks) {
@@ -352,7 +352,7 @@ static void schedule_children(TaskPool *pool,
*/
void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
Depsgraph *graph,
- const int layers)
+ const unsigned int layers)
{
/* Generate base evaluation context, upon which all the others are derived. */
// TODO: this needs both main and scene access...
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h
index 92f87b03803..49c0379939e 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval.h
@@ -47,6 +47,6 @@ struct Depsgraph;
*/
void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
Depsgraph *graph,
- const int layers);
+ const unsigned int layers);
} // namespace DEG
diff --git a/source/blender/depsgraph/intern/nodes/deg_node.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc
index db807d22b89..eb408f293de 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node.cc
+++ b/source/blender/depsgraph/intern/nodes/deg_node.cc
@@ -182,7 +182,7 @@ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata))
this->eval_flags = 0;
/* For object we initialize layers to layer from base. */
- if (GS(id) == ID_OB) {
+ if (GS(id->name) == ID_OB) {
this->layers = 0;
}
diff --git a/source/blender/depsgraph/intern/nodes/deg_node.h b/source/blender/depsgraph/intern/nodes/deg_node.h
index 416996052c9..b2262c4bd12 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node.h
+++ b/source/blender/depsgraph/intern/nodes/deg_node.h
@@ -176,7 +176,7 @@ struct IDDepsNode : public DepsNode {
GHash *components;
/* Layers of this node with accumulated layers of it's output relations. */
- int layers;
+ unsigned int layers;
/* Additional flags needed for scene evaluation.
* TODO(sergey): Only needed for until really granular updates
diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc
index 6ac45c99798..01f33b6368b 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc
@@ -120,7 +120,7 @@ string ComponentDepsNode::identifier() const
sprintf(typebuf, "(%d)", type);
char layers[16];
- sprintf(layers, "%d", this->layers);
+ sprintf(layers, "%u", this->layers);
return string(typebuf) + name + " : " + idname + " (Layers: " + layers + ")";
}
@@ -366,6 +366,11 @@ static DepsNodeFactoryImpl<ParticlesComponentDepsNode> DNTI_EVAL_PARTICLES;
DEG_DEPSNODE_DEFINE(ShadingComponentDepsNode, DEPSNODE_TYPE_SHADING, "Shading Component");
static DepsNodeFactoryImpl<ShadingComponentDepsNode> DNTI_SHADING;
+/* Cache Component Defines ============================ */
+
+DEG_DEPSNODE_DEFINE(CacheComponentDepsNode, DEPSNODE_TYPE_CACHE, "Cache Component");
+static DepsNodeFactoryImpl<CacheComponentDepsNode> DNTI_CACHE;
+
/* Node Types Register =================================== */
@@ -383,6 +388,8 @@ void deg_register_component_depsnodes()
deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES);
deg_register_node_typeinfo(&DNTI_SHADING);
+
+ deg_register_node_typeinfo(&DNTI_CACHE);
}
} // namespace DEG
diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h
index 6f62d91cd79..7dec8eaaa90 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_component.h
+++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h
@@ -159,7 +159,7 @@ struct ComponentDepsNode : public DepsNode {
// XXX: a poll() callback to check if component's first node can be started?
/* Temporary bitmask, used during graph construction. */
- int layers;
+ unsigned int layers;
};
/* ---------------------------------------- */
@@ -209,6 +209,10 @@ struct ShadingComponentDepsNode : public ComponentDepsNode {
DEG_DEPSNODE_DECLARE;
};
+struct CacheComponentDepsNode : public ComponentDepsNode {
+ DEG_DEPSNODE_DECLARE;
+};
+
void deg_register_component_depsnodes();
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index ea2f7fc5588..3085e383909 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -40,6 +40,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
@@ -1578,6 +1579,88 @@ static bAnimChannelType ACF_DSTEX =
/* Camera Expander ------------------------------------------- */
// TODO: just get this from RNA?
+static int acf_dscachefile_icon(bAnimListElem *ale)
+{
+ UNUSED_VARS(ale);
+ return ICON_FILE;
+}
+
+/* get the appropriate flag(s) for the setting when it is valid */
+static int acf_dscachefile_setting_flag(bAnimContext *ac, eAnimChannel_Settings setting, bool *neg)
+{
+ /* clear extra return data first */
+ *neg = false;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return CACHEFILE_DS_EXPAND;
+
+ case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
+ return ADT_NLA_EVAL_OFF;
+
+ case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
+ *neg = true;
+ return ADT_CURVES_NOT_VISIBLE;
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ return ADT_UI_SELECTED;
+
+ default: /* unsupported */
+ return 0;
+ }
+
+ UNUSED_VARS(ac);
+}
+
+/* get pointer to the setting */
+static void *acf_dscachefile_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type)
+{
+ CacheFile *cache_file = (CacheFile *)ale->data;
+
+ /* clear extra return data first */
+ *type = 0;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return GET_ACF_FLAG_PTR(cache_file->flag, type);
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
+ case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
+ if (cache_file->adt) {
+ return GET_ACF_FLAG_PTR(cache_file->adt->flag, type);
+ }
+
+ return NULL;
+
+ default: /* unsupported */
+ return NULL;
+ }
+}
+
+/* CacheFile expander type define. */
+static bAnimChannelType ACF_DSCACHEFILE =
+{
+ "Cache File Expander", /* type name */
+ ACHANNEL_ROLE_EXPANDER, /* role */
+
+ acf_generic_dataexpand_color, /* backdrop color */
+ acf_generic_dataexpand_backdrop, /* backdrop */
+ acf_generic_indention_1, /* indent level */
+ acf_generic_basic_offset, /* offset */
+
+ acf_generic_idblock_name, /* name */
+ acf_generic_idfill_name_prop, /* name prop */
+ acf_dscachefile_icon, /* icon */
+
+ acf_generic_dataexpand_setting_valid, /* has setting */
+ acf_dscachefile_setting_flag, /* flag for setting */
+ acf_dscachefile_setting_ptr /* pointer for setting */
+};
+
+/* Camera Expander ------------------------------------------- */
+
+// TODO: just get this from RNA?
static int acf_dscam_icon(bAnimListElem *UNUSED(ale))
{
return ICON_CAMERA_DATA;
@@ -3388,6 +3471,7 @@ static void ANIM_init_channel_typeinfo_data(void)
animchannelTypeInfo[type++] = &ACF_DSMAT; /* Material Channel */
animchannelTypeInfo[type++] = &ACF_DSLAM; /* Lamp Channel */
animchannelTypeInfo[type++] = &ACF_DSCAM; /* Camera Channel */
+ animchannelTypeInfo[type++] = &ACF_DSCACHEFILE; /* CacheFile Channel */
animchannelTypeInfo[type++] = &ACF_DSCUR; /* Curve Channel */
animchannelTypeInfo[type++] = &ACF_DSSKEY; /* ShapeKey Channel */
animchannelTypeInfo[type++] = &ACF_DSWOR; /* World Channel */
@@ -4208,6 +4292,8 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
offset += ICON_WIDTH;
}
else if (ale->type == ANIMTYPE_GPLAYER) {
+#if 0
+ /* XXX: Maybe need a better design */
/* color swatch for layer color */
bGPDlayer *gpl = (bGPDlayer *)ale->data;
PointerRNA ptr;
@@ -4216,7 +4302,6 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr);
UI_block_align_begin(block);
-
UI_block_emboss_set(block, RNA_boolean_get(&ptr, "is_stroke_visible") ? UI_EMBOSS : UI_EMBOSS_NONE);
uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset, yminc, w, ICON_WIDTH,
&ptr, "color", -1,
@@ -4226,11 +4311,11 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset + w, yminc, w, ICON_WIDTH,
&ptr, "fill_color", -1,
0, 0, 0, 0, gpl->info);
-
UI_block_emboss_set(block, UI_EMBOSS_NONE);
UI_block_align_end(block);
-
+
offset += ICON_WIDTH;
+#endif
}
}
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index af9b0a176f5..cb65a9aecad 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -120,6 +120,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@@ -175,6 +176,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@@ -275,6 +277,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@@ -370,6 +373,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@@ -1686,7 +1690,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
bGPDlayer *gpl = (bGPDlayer *)ale->data;
/* try to delete the layer's data and the layer itself */
- free_gpencil_frames(gpl);
+ BKE_gpencil_free_frames(gpl);
BLI_freelinkN(&gpd->layers, gpl);
break;
}
@@ -2716,6 +2720,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 0d1a13fa95d..a6dc974179d 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -53,6 +53,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_lamp_types.h"
#include "DNA_lattice_types.h"
#include "DNA_linestyle_types.h"
@@ -199,6 +200,16 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->mode = saction->mode;
return true;
+
+ case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */
+ /* update scene-pointer (no need to check for pinning yet, as not implemented) */
+ saction->ads.source = (ID *)ac->scene;
+
+ ac->datatype = ANIMCONT_CHANNEL;
+ ac->data = &saction->ads;
+
+ ac->mode = saction->mode;
+ return true;
case SACTCONT_MASK: /* Mask */ /* XXX review how this mode is handled... */
{
@@ -660,6 +671,19 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne
ale->adt = BKE_animdata_from_id(data);
break;
}
+ case ANIMTYPE_DSCACHEFILE:
+ {
+ CacheFile *cache_file = (CacheFile *)data;
+ AnimData *adt = cache_file->adt;
+
+ ale->flag = FILTER_CACHEFILE_OBJD(cache_file);
+
+ ale->key_data = (adt) ? adt->action : NULL;
+ ale->datatype = ALE_ACT;
+
+ ale->adt = BKE_animdata_from_id(data);
+ break;
+ }
case ANIMTYPE_DSCUR:
{
Curve *cu = (Curve *)data;
@@ -1751,6 +1775,42 @@ static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data,
return items;
}
+/* Helper for Cache File data integrated with main DopeSheet */
+static size_t animdata_filter_ds_cachefile(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, CacheFile *cache_file, int filter_mode)
+{
+ ListBase tmp_data = {NULL, NULL};
+ size_t tmp_items = 0;
+ size_t items = 0;
+
+ /* add relevant animation channels for Cache File */
+ BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_CACHEFILE_OBJD(cache_file))
+ {
+ /* add animation channels */
+ tmp_items += animfilter_block_data(ac, &tmp_data, ads, &cache_file->id, filter_mode);
+ }
+ END_ANIMFILTER_SUBCHANNELS;
+
+ /* did we find anything? */
+ if (tmp_items) {
+ /* include data-expand widget first */
+ if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
+ /* check if filtering by active status */
+ // XXX: active check here needs checking
+ if (ANIMCHANNEL_ACTIVEOK(cache_file)) {
+ ANIMCHANNEL_NEW_CHANNEL(cache_file, ANIMTYPE_DSCACHEFILE, cache_file);
+ }
+ }
+
+ /* now add the list of collected channels */
+ BLI_movelisttolist(anim_data, &tmp_data);
+ BLI_assert(BLI_listbase_is_empty(&tmp_data));
+ items += tmp_items;
+ }
+
+ /* return the number of items added to the list */
+ return items;
+}
+
/* Helper for Mask Editing - mask layers */
static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode)
{
@@ -2843,6 +2903,12 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, b
filter_mode |= ANIMFILTER_SELEDIT;
}
+ /* Cache files level animations (frame duration and such). */
+ CacheFile *cache_file = G.main->cachefiles.first;
+ for (; cache_file; cache_file = cache_file->id.next) {
+ items += animdata_filter_ds_cachefile(ac, anim_data, ads, cache_file, filter_mode);
+ }
+
/* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */
items += animdata_filter_dopesheet_scene(ac, anim_data, ads, scene, filter_mode);
@@ -2954,7 +3020,11 @@ static size_t animdata_filter_animchan(bAnimContext *ac, ListBase *anim_data, bD
case ANIMTYPE_OBJECT:
items += animdata_filter_dopesheet_ob(ac, anim_data, ads, channel->data, filter_mode);
break;
-
+
+ case ANIMTYPE_DSCACHEFILE:
+ items += animdata_filter_ds_cachefile(ac, anim_data, ads, channel->data, filter_mode);
+ break;
+
case ANIMTYPE_ANIMDATA:
items += animfilter_block_data(ac, anim_data, ads, channel->id, filter_mode);
break;
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 6e776953356..5f675e690b9 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -44,6 +44,7 @@
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_gpencil_types.h"
@@ -965,6 +966,37 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *bl
ANIM_animdata_freelist(&anim_data);
}
+void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *keys, DLRBT_Tree *blocks)
+{
+ if (cache_file == NULL) {
+ return;
+ }
+
+ /* create a dummy wrapper data to work with */
+ bAnimListElem dummychan = {NULL};
+ dummychan.type = ANIMTYPE_DSCACHEFILE;
+ dummychan.data = cache_file;
+ dummychan.id = &cache_file->id;
+ dummychan.adt = cache_file->adt;
+
+ bAnimContext ac = {NULL};
+ ac.ads = ads;
+ ac.data = &dummychan;
+ ac.datatype = ANIMCONT_CHANNEL;
+
+ /* get F-Curves to take keyframes from */
+ ListBase anim_data = { NULL, NULL };
+ int filter = ANIMFILTER_DATA_VISIBLE; // curves only
+ ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* loop through each F-Curve, grabbing the keyframes */
+ for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) {
+ fcurve_to_keylist(ale->adt, ale->data, keys, blocks);
+ }
+
+ ANIM_animdata_freelist(&anim_data);
+}
+
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, DLRBT_Tree *blocks)
{
BezTriple *bezt;
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 0c0f54f0179..98be77b491f 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -930,11 +930,18 @@ bool insert_keyframe_direct(ReportList *reports, PointerRNA ptr, PropertyRNA *pr
/* update F-Curve flags to ensure proper behaviour for property type */
update_autoflags_fcurve_direct(fcu, prop);
-
+
/* adjust frame on which to add keyframe */
if ((flag & INSERTKEY_DRIVER) && (fcu->driver)) {
- /* for making it easier to add corrective drivers... */
- cfra = evaluate_driver(fcu->driver, cfra);
+ PathResolvedRNA anim_rna;
+
+ if (RNA_path_resolved_create(&ptr, prop, fcu->array_index, &anim_rna)) {
+ /* for making it easier to add corrective drivers... */
+ cfra = evaluate_driver(&anim_rna, fcu->driver, cfra);
+ }
+ else {
+ cfra = 0.0f;
+ }
}
/* obtain value to give keyframe */
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 4d0a2fa53cd..2d8fc76ee7e 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -64,8 +64,8 @@
#endif
/* Distance between input samples */
-#define STROKE_SAMPLE_DIST_MIN_PX 3
-#define STROKE_SAMPLE_DIST_MAX_PX 6
+#define STROKE_SAMPLE_DIST_MIN_PX 1
+#define STROKE_SAMPLE_DIST_MAX_PX 3
/* Distance between start/end points to consider cyclic */
#define STROKE_CYCLIC_DIST_PX 8
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 79a2c494239..4ef76f5ee25 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -52,6 +52,7 @@
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_userdef_types.h"
+#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -94,9 +95,32 @@ typedef enum eDrawStrokeFlags {
#define GP_DRAWTHICKNESS_SPECIAL 3
/* ----- Tool Buffer Drawing ------ */
+/* helper function to set color of buffer point */
+static void gp_set_tpoint_color(tGPspoint *pt, float ink[4])
+{
+ float alpha = ink[3] * pt->strength;
+ CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
+ glColor4f(ink[0], ink[1], ink[2], alpha);
+}
+
+/* helper function to set color of point */
+static void gp_set_point_color(bGPDspoint *pt, float ink[4])
+{
+ float alpha = ink[3] * pt->strength;
+ CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
+ glColor4f(ink[0], ink[1], ink[2], alpha);
+}
+
+/* helper function to set color and point */
+static void gp_set_color_and_tpoint(tGPspoint *pt, float ink[4])
+{
+ gp_set_tpoint_color(pt, ink);
+ glVertex2iv(&pt->x);
+}
/* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */
-static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag)
+static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness,
+ short dflag, short sflag, float ink[4])
{
tGPspoint *pt;
int i;
@@ -113,7 +137,8 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
/* if drawing a single point, draw it larger */
glPointSize((float)(thickness + 2) * points->pressure);
glBegin(GL_POINTS);
- glVertex2iv(&points->x);
+
+ gp_set_color_and_tpoint(points, ink);
glEnd();
}
else if (sflag & GP_STROKE_ERASER) {
@@ -138,15 +163,18 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
glBegin(GL_LINE_STRIP);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
- if (i != 0) glVertex2iv(&(pt - 1)->x);
+ if (i != 0) {
+ gp_set_color_and_tpoint((pt - 1), ink);
+ }
/* now the point we want... */
- glVertex2iv(&pt->x);
+ gp_set_color_and_tpoint(pt, ink);
oldpressure = pt->pressure;
}
- else
- glVertex2iv(&pt->x);
+ else {
+ gp_set_color_and_tpoint(pt, ink);
+ }
}
glEnd();
@@ -155,37 +183,35 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
}
/* --------- 2D Stroke Drawing Helpers --------- */
-
-/* helper function to calculate x-y drawing coordinates for 2D points */
-static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
+/* change in parameter list */
+static void gp_calc_2d_stroke_fxy(float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
{
if (sflag & GP_STROKE_2DSPACE) {
- r_co[0] = pt->x;
- r_co[1] = pt->y;
+ r_co[0] = pt[0];
+ r_co[1] = pt[1];
}
else if (sflag & GP_STROKE_2DIMAGE) {
- const float x = (float)((pt->x * winx) + offsx);
- const float y = (float)((pt->y * winy) + offsy);
-
+ const float x = (float)((pt[0] * winx) + offsx);
+ const float y = (float)((pt[1] * winy) + offsy);
+
r_co[0] = x;
r_co[1] = y;
}
else {
- const float x = (float)(pt->x / 100 * winx) + offsx;
- const float y = (float)(pt->y / 100 * winy) + offsy;
-
+ const float x = (float)(pt[0] / 100 * winx) + offsx;
+ const float y = (float)(pt[1] / 100 * winy) + offsy;
+
r_co[0] = x;
r_co[1] = y;
}
}
-
/* ----------- Volumetric Strokes --------------- */
/* draw a 2D buffer stroke in "volumetric" style
* NOTE: the stroke buffer doesn't have any coordinate offsets/transforms
*/
static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness,
- short dflag, short UNUSED(sflag))
+ short dflag, short UNUSED(sflag), float ink[4])
{
GLUquadricObj *qobj = gluNewQuadric();
float modelview[4][4];
@@ -216,6 +242,7 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s
glLoadMatrixf((float *)modelview);
/* draw the disk using the current state... */
+ gp_set_tpoint_color(pt, ink);
gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1);
@@ -229,7 +256,8 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s
/* draw a 2D strokes in "volumetric" style */
static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness,
short dflag, short sflag,
- int offsx, int offsy, int winx, int winy)
+ int offsx, int offsy, int winx, int winy,
+ float diff_mat[4][4], float ink[4])
{
GLUquadricObj *qobj = gluNewQuadric();
float modelview[4][4];
@@ -238,7 +266,7 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor
bGPDspoint *pt;
int i;
-
+ float fpt[3];
/* HACK: We need a scale factor for the drawing in the image editor,
* which seems to use 1 unit as it's maximum size, whereas everything
@@ -256,10 +284,14 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor
glPushMatrix();
for (i = 0, pt = points; i < totpoints; i++, pt++) {
+ /* color of point */
+ gp_set_point_color(pt, ink);
+
/* set the transformed position */
float co[2];
- gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
translate_m4(modelview, co[0], co[1], 0.0f);
glLoadMatrixf((float *)modelview);
@@ -276,8 +308,9 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor
}
/* draw a 3D stroke in "volumetric" style */
-static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness,
- short UNUSED(dflag), short UNUSED(sflag))
+static void gp_draw_stroke_volumetric_3d(
+ bGPDspoint *points, int totpoints, short thickness,
+ short UNUSED(dflag), short UNUSED(sflag), float diff_mat[4][4], float ink[4])
{
GLUquadricObj *qobj = gluNewQuadric();
@@ -286,7 +319,7 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor
bGPDspoint *pt;
int i;
-
+ float fpt[3];
/* Get the basic modelview matrix we use for performing calculations */
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview);
@@ -305,8 +338,13 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor
glPushMatrix();
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
+ /* color of point */
+ gp_set_point_color(pt, ink);
+
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
/* apply translation to base_modelview, so that the translated point is put in the right place */
- translate_m4(base_modelview, pt->x, pt->y, pt->z);
+ translate_m4(base_modelview, fpt[0], fpt[1], fpt[2]);
/* copy the translation component to the billboard matrix we're going to use,
* then reset the base matrix to the original values so that we can do the same
@@ -378,9 +416,9 @@ static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d
static void gp_triangulate_stroke_fill(bGPDstroke *gps)
{
BLI_assert(gps->totpoints >= 3);
- gps->tot_triangles = gps->totpoints - 2;
-
+
/* allocate memory for temporary areas */
+ gps->tot_triangles = gps->totpoints - 2;
unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation");
float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points");
@@ -390,6 +428,8 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps)
gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles);
+ /* Number of triangles */
+ gps->tot_triangles = gps->totpoints - 2;
/* save triangulation data in stroke cache */
if (gps->tot_triangles > 0) {
if (gps->triangles == NULL) {
@@ -399,9 +439,7 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps)
gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles);
}
- int i;
-
- for (i = 0; i < gps->tot_triangles; i++) {
+ for (int i = 0; i < gps->tot_triangles; i++) {
bGPDtriangle *stroke_triangle = &gps->triangles[i];
stroke_triangle->v1 = tmp_triangles[i][0];
stroke_triangle->v2 = tmp_triangles[i][1];
@@ -428,21 +466,27 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps)
/* draw fills for shapes */
-static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy)
+static void gp_draw_stroke_fill(
+ bGPdata *gpd, bGPDstroke *gps,
+ int offsx, int offsy, int winx, int winy, float diff_mat[4][4])
{
+ bGPDpalettecolor *palcolor;
+ int i;
+ float fpt[3];
+
BLI_assert(gps->totpoints >= 3);
-
+
+ palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+
/* Triangulation fill if high quality flag is enabled */
- if (dflag & GP_DRAWDATA_HQ_FILL) {
+ if (palcolor->flag & PC_COLOR_HQ_FILL) {
bGPDtriangle *stroke_triangle;
bGPDspoint *pt;
- int i;
-
+
/* Calculate triangles cache for filling area (must be done only after changes) */
if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) {
gp_triangulate_stroke_fill(gps);
}
-
/* Draw all triangles for filling the polygon (cache must be calculated before) */
BLI_assert(gps->tot_triangles >= 1);
glBegin(GL_TRIANGLES);
@@ -450,32 +494,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short
if (gps->flag & GP_STROKE_3DSPACE) {
/* vertex 1 */
pt = &gps->points[stroke_triangle->v1];
- glVertex3fv(&pt->x);
-
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
/* vertex 2 */
pt = &gps->points[stroke_triangle->v2];
- glVertex3fv(&pt->x);
-
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
/* vertex 3 */
pt = &gps->points[stroke_triangle->v3];
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
-
/* vertex 1 */
pt = &gps->points[stroke_triangle->v1];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
-
/* vertex 2 */
pt = &gps->points[stroke_triangle->v2];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
-
/* vertex 3 */
pt = &gps->points[stroke_triangle->v3];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
@@ -483,30 +528,31 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short
}
else {
/* As an initial implementation, we use the OpenGL filled polygon drawing
- * here since it's the easiest option to implement for this case. It does
- * come with limitations (notably for concave shapes), though it works well
- * enough for many simple situations.
- *
- * We keep this legacy implementation around despite now having the high quality
- * fills, as this is necessary for keeping everything working nicely for files
- * created using old versions of Blender which may have depended on the artifacts
- * the old fills created.
- */
+ * here since it's the easiest option to implement for this case. It does
+ * come with limitations (notably for concave shapes), though it shouldn't
+ * be much of an issue in most cases.
+ *
+ * We keep this legacy implementation around despite now having the high quality
+ * fills, as this is necessary for keeping everything working nicely for files
+ * created using old versions of Blender which may have depended on the artifacts
+ * the old fills created.
+ */
bGPDspoint *pt;
- int i;
-
+
glBegin(GL_POLYGON);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (gps->flag & GP_STROKE_3DSPACE) {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
-
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
+
glEnd();
}
}
@@ -514,23 +560,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short
/* ----- Existing Strokes Drawing (3D and Point) ------ */
/* draw a given stroke - just a single dot (only one point) */
-static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dflag, short sflag,
- int offsx, int offsy, int winx, int winy)
+static void gp_draw_stroke_point(
+ bGPDspoint *points, short thickness, short dflag, short sflag,
+ int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4])
{
+ float fpt[3];
+ bGPDspoint *pt = &points[0];
+
+ /* color of point */
+ gp_set_point_color(pt, ink);
+
/* set point thickness (since there's only one of these) */
glPointSize((float)(thickness + 2) * points->pressure);
+ /* get final position using parent matrix */
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
/* draw point */
if (sflag & GP_STROKE_3DSPACE) {
glBegin(GL_POINTS);
- glVertex3fv(&points->x);
+ glVertex3fv(fpt);
glEnd();
}
else {
float co[2];
/* get coordinates of point */
- gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
/* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok
* - also mandatory in if Image Editor 'image-based' dot
@@ -559,16 +615,21 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla
}
/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */
-static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short UNUSED(sflag))
+static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug,
+ short UNUSED(sflag), float diff_mat[4][4], float ink[4], bool cyclic)
{
- bGPDspoint *pt;
+ bGPDspoint *pt, *pt2;
float curpressure = points[0].pressure;
int i;
-
+ float fpt[3];
+ float cyclic_fpt[3];
+
/* draw stroke curve */
glLineWidth(max_ff(curpressure * thickness, 1.0f));
glBegin(GL_LINE_STRIP);
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
+ gp_set_point_color(pt, ink);
+
/* if there was a significant pressure change, stop the curve, change the thickness of the stroke,
* and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP)
* Note: we want more visible levels of pressures when thickness is bigger.
@@ -580,15 +641,29 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
glBegin(GL_LINE_STRIP);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
- if (i != 0) glVertex3fv(&(pt - 1)->x);
+ if (i != 0) {
+ pt2 = pt - 1;
+ mul_v3_m4v3(fpt, diff_mat, &pt2->x);
+ glVertex3fv(fpt);
+ }
/* now the point we want... */
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
+ }
+ /* saves first point to use in cyclic */
+ if (i == 0) {
+ copy_v3_v3(cyclic_fpt, fpt);
}
}
+ /* if cyclic draw line to first point */
+ if (cyclic) {
+ glVertex3fv(cyclic_fpt);
+ }
glEnd();
/* draw debug points of curve on top? */
@@ -597,9 +672,12 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
glPointSize((float)(thickness + 2));
glBegin(GL_POINTS);
- for (i = 0, pt = points; i < totpoints && pt; i++, pt++)
- glVertex3fv(&pt->x);
+ for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
+ }
glEnd();
+
}
}
@@ -607,7 +685,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
/* draw a given stroke in 2d */
static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag,
- bool debug, int offsx, int offsy, int winx, int winy)
+ bool debug, int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4])
{
/* otherwise thickness is twice that of the 3D view */
float thickness = (float)thickness_s * 0.5f;
@@ -625,6 +703,7 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
bGPDspoint *pt1, *pt2;
float pm[2];
int i;
+ float fpt[3];
glShadeModel(GL_FLAT);
glBegin(GL_QUADS);
@@ -635,10 +714,13 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
float m1[2], m2[2]; /* gradient and normal */
float mt[2], sc[2]; /* gradient for thickness, point for end-cap */
float pthick; /* thickness at segment point */
-
+
/* get x and y coordinates from points */
- gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0);
- gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1);
+ mul_v3_m4v3(fpt, diff_mat, &pt1->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0);
+
+ mul_v3_m4v3(fpt, diff_mat, &pt2->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1);
/* calculate gradient and normal - 'angle'=(ny/nx) */
m1[1] = s1[1] - s0[1];
@@ -650,6 +732,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
/* always use pressure from first point here */
pthick = (pt1->pressure * thickness * scalefac);
+ /* color of point */
+ gp_set_point_color(pt1, ink);
+
/* if the first segment, start of segment is segment's normal */
if (i == 0) {
/* draw start cap first
@@ -725,6 +810,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
/* for once, we use second point's pressure (otherwise it won't be drawn) */
pthick = (pt2->pressure * thickness * scalefac);
+ /* color of point */
+ gp_set_point_color(pt2, ink);
+
/* calculate points for end of segment */
mt[0] = m2[0] * pthick;
mt[1] = m2[1] * pthick;
@@ -770,14 +858,15 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
if (debug) {
bGPDspoint *pt;
int i;
-
+ float fpt[3];
+
glPointSize((float)(thickness_s + 2));
glBegin(GL_POINTS);
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
float co[2];
-
- gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
glEnd();
@@ -818,26 +907,45 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
}
/* draw a set of strokes */
-static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
- bool debug, short lthick, const float color[4], const float fill_color[4])
+static void gp_draw_strokes(
+ bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
+ bool debug, short lthick, const float opacity, const float tintcolor[4],
+ const bool onion, const bool custonion, float diff_mat[4][4])
{
bGPDstroke *gps;
-
+ float tcolor[4];
+ float tfill[4];
+ short sthickness;
+ float ink[4];
+
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* check if stroke can be drawn */
- if (gp_can_draw_stroke(gps, dflag) == false)
+ if (gp_can_draw_stroke(gps, dflag) == false) {
continue;
-
+ }
+ /* check if the color is visible */
+ bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+ if ((palcolor == NULL) ||
+ (palcolor->flag & PC_COLOR_HIDE) ||
+ /* if onion and ghost flag do not draw*/
+ (onion && (palcolor->flag & PC_COLOR_ONIONSKIN)))
+ {
+ continue;
+ }
+
+ /* calculate thickness */
+ sthickness = gps->thickness + lthick;
+
/* check which stroke-drawer to use */
if (dflag & GP_DRAWDATA_ONLY3D) {
const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
int mask_orig = 0;
-
+
if (no_xray) {
glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
glDepthMask(0);
glEnable(GL_DEPTH_TEST);
-
+
/* first arg is normally rv3d->dist, but this isn't
* available here and seems to work quite well without */
bglPolygonOffset(1.0f, 1.0f);
@@ -846,34 +954,65 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
glPolygonOffset(-1.0f, -1.0f);
#endif
}
-
+
/* 3D Fill */
- if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
- glColor4fv(fill_color);
- gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy);
+ //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
+ if (gps->totpoints >= 3) {
+ /* set color using palette, tint color and opacity */
+ interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]);
+ tfill[3] = palcolor->fill[3] * opacity;
+ if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) {
+ if (!onion) {
+ glColor4fv(tfill);
+ }
+ else {
+ if (custonion) {
+ glColor4fv(tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]);
+ glColor4fv(tfill);
+ }
+ }
+ gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat);
+ }
}
-
+
/* 3D Stroke */
- glColor4fv(color);
-
- if (dflag & GP_DRAWDATA_VOLUMETRIC) {
+ /* set color using palette, tint color and opacity */
+ if (!onion) {
+ interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]);
+ tcolor[3] = palcolor->color[3] * opacity;
+ copy_v4_v4(ink, tcolor);
+ }
+ else {
+ if (custonion) {
+ copy_v4_v4(ink, tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity);
+ copy_v4_v4(ink, tcolor);
+ }
+ }
+ if (palcolor->flag & PC_COLOR_VOLUMETRIC) {
/* volumetric stroke drawing */
- gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag);
+ gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, diff_mat, ink);
}
else {
/* 3D Lines - OpenGL primitives-based */
if (gps->totpoints == 1) {
- gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
+ gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy,
+ diff_mat, ink);
}
else {
- gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag);
+ gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag,
+ diff_mat, ink, gps->flag & GP_STROKE_CYCLIC);
}
}
-
if (no_xray) {
glDepthMask(mask_orig);
glDisable(GL_DEPTH_TEST);
-
+
bglPolygonOffset(0.0, 0.0);
#if 0
glDisable(GL_POLYGON_OFFSET_LINE);
@@ -883,25 +1022,58 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
}
else {
/* 2D - Fill */
- if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
- glColor4fv(fill_color);
- gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy);
+ if (gps->totpoints >= 3) {
+ /* set color using palette, tint color and opacity */
+ interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]);
+ tfill[3] = palcolor->fill[3] * opacity;
+ if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) {
+ if (!onion) {
+ glColor4fv(tfill);
+ }
+ else {
+ if (custonion) {
+ glColor4fv(tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2],
+ tintcolor[3]);
+ glColor4fv(tfill);
+ }
+ }
+ gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat);
+ }
}
-
+
/* 2D Strokes... */
- glColor4fv(color);
-
- if (dflag & GP_DRAWDATA_VOLUMETRIC) {
+ /* set color using palette, tint color and opacity */
+ if (!onion) {
+ interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]);
+ tcolor[3] = palcolor->color[3] * opacity;
+ copy_v4_v4(ink, tcolor);
+ }
+ else {
+ if (custonion) {
+ copy_v4_v4(ink, tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity);
+ copy_v4_v4(ink, tcolor);
+ }
+ }
+ if (palcolor->flag & PC_COLOR_VOLUMETRIC) {
/* blob/disk-based "volumetric" drawing */
- gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
+ gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag,
+ offsx, offsy, winx, winy, diff_mat, ink);
}
else {
/* normal 2D strokes */
if (gps->totpoints == 1) {
- gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
+ gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy,
+ diff_mat, ink);
}
else {
- gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy);
+ gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug,
+ offsx, offsy, winx, winy, diff_mat, ink);
}
}
}
@@ -909,13 +1081,19 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
}
/* Draw selected verts for strokes being edited */
-static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3])
+static void gp_draw_strokes_edit(
+ bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag,
+ short lflag, float diff_mat[4][4], float alpha)
{
bGPDstroke *gps;
+ /* if alpha 0 do not draw */
+ if (alpha == 0.0f)
+ return;
+
const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0;
int mask_orig = 0;
-
+
/* set up depth masks... */
if (dflag & GP_DRAWDATA_ONLY3D) {
if (no_xray) {
@@ -939,7 +1117,8 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
bGPDspoint *pt;
float vsize, bsize;
int i;
-
+ float fpt[3];
+
/* check if stroke can be drawn */
if (gp_can_draw_stroke(gps, dflag) == false)
continue;
@@ -951,6 +1130,19 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
if ((gps->flag & GP_STROKE_SELECT) == 0)
continue;
+ /* verify palette color lock */
+ {
+ bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+ if (palcolor != NULL) {
+ if (palcolor->flag & PC_COLOR_HIDE) {
+ continue;
+ }
+ if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) {
+ continue;
+ }
+ }
+ }
+
/* Get size of verts:
* - The selected state needs to be larger than the unselected state so that
* they stand out more.
@@ -966,25 +1158,23 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
}
/* First Pass: Draw all the verts (i.e. these become the unselected state) */
- if (tcolor != NULL) {
- /* for now, we assume that the base color of the points is not too close to the real color */
- glColor3fv(tcolor);
- }
- else {
- /* this doesn't work well with the default theme and black strokes... */
- UI_ThemeColor(TH_GP_VERTEX);
- }
+ /* for now, we assume that the base color of the points is not too close to the real color */
+ /* set color using palette */
+ bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+ glColor3fv(palcolor->color);
+
glPointSize(bsize);
glBegin(GL_POINTS);
for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
if (gps->flag & GP_STROKE_3DSPACE) {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
-
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
@@ -992,24 +1182,54 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
/* Second Pass: Draw only verts which are selected */
- UI_ThemeColor(TH_GP_VERTEX_SELECT);
+ float curColor[4];
+ UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, curColor);
+ glColor4f(curColor[0], curColor[1], curColor[2], alpha);
+
glPointSize(vsize);
glBegin(GL_POINTS);
for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
if (gps->flag & GP_STROKE_3DSPACE) {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
}
glEnd();
+
+ /* Draw start and end point if enabled stroke direction hint */
+ if ((gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1)) {
+ bGPDspoint *p;
+
+ glPointSize(vsize + 4);
+ glBegin(GL_POINTS);
+
+ /* start point in green bigger */
+ glColor3f(0.0f, 1.0f, 0.0f);
+ p = &gps->points[0];
+ mul_v3_m4v3(fpt, diff_mat, &p->x);
+ glVertex3fv(fpt);
+ glEnd();
+
+ /* end point in red smaller */
+ glPointSize(vsize + 1);
+ glBegin(GL_POINTS);
+
+ glColor3f(1.0f, 0.0f, 0.0f);
+ p = &gps->points[gps->totpoints - 1];
+ mul_v3_m4v3(fpt, diff_mat, &p->x);
+ glVertex3fv(fpt);
+ glEnd();
+ }
}
@@ -1031,18 +1251,20 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
/* ----- General Drawing ------ */
/* draw onion-skinning for a layer */
-static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy,
- int UNUSED(cfra), int dflag, bool debug, short lthick)
+static void gp_draw_onionskins(
+ bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy,
+ int UNUSED(cfra), int dflag, bool debug, float diff_mat[4][4])
{
- const float alpha = gpl->color[3];
+ const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)};
+ const float alpha = 1.0f;
float color[4];
-
+
/* 1) Draw Previous Frames First */
if (gpl->flag & GP_LAYER_GHOST_PREVCOL) {
copy_v3_v3(color, gpl->gcolor_prev);
}
else {
- copy_v3_v3(color, gpl->color);
+ copy_v3_v3(color, default_color);
}
if (gpl->gstep > 0) {
@@ -1056,7 +1278,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* alpha decreases with distance from curframe index */
fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1));
color[3] = alpha * fac * 0.66f;
- gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat);
}
else
break;
@@ -1066,7 +1289,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
if (gpf->prev) {
color[3] = (alpha / 7);
- gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat);
}
}
else {
@@ -1079,7 +1303,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
copy_v3_v3(color, gpl->gcolor_next);
}
else {
- copy_v3_v3(color, gpl->color);
+ copy_v3_v3(color, default_color);
}
if (gpl->gstep_next > 0) {
@@ -1093,7 +1317,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* alpha decreases with distance from curframe index */
fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1));
color[3] = alpha * fac * 0.66f;
- gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat);
}
else
break;
@@ -1103,34 +1328,38 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
if (gpf->next) {
color[3] = (alpha / 4);
- gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat);
}
}
else {
/* don't draw - disabled */
}
- /* 3) restore alpha */
- glColor4fv(gpl->color);
}
/* loop over gpencil data layers, drawing them */
-static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
+static void gp_draw_data_layers(
+ bGPDbrush *brush, float alpha, bGPdata *gpd,
+ int offsx, int offsy, int winx, int winy, int cfra, int dflag)
{
bGPDlayer *gpl;
-
+ float diff_mat[4][4];
+
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
bGPDframe *gpf;
-
+ /* calculate parent position */
+ ED_gpencil_parent_location(gpl, diff_mat);
+
bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false;
- short lthick = gpl->thickness;
+ short lthick = brush->thickness + gpl->thickness;
/* don't draw layer if hidden */
if (gpl->flag & GP_LAYER_HIDE)
continue;
/* get frame to draw */
- gpf = gpencil_layer_getframe(gpl, cfra, 0);
+ gpf = BKE_gpencil_layer_getframe(gpl, cfra, 0);
if (gpf == NULL)
continue;
@@ -1155,9 +1384,6 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
/* HQ fills... */
GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL);
- /* fill strokes... */
- // XXX: this is not a very good limit
- GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL);
#undef GP_DRAWFLAG_APPLY
/* draw 'onionskins' (frame left + right) */
@@ -1165,11 +1391,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
/* Drawing method - only immediately surrounding (gstep = 0),
* or within a frame range on either side (gstep > 0)
*/
- gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick);
+ gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat);
}
/* draw the strokes already in active frame */
- gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill);
+ gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness,
+ gpl->opacity, gpl->tintcolor, false, false, diff_mat);
/* Draw verts of selected strokes
* - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering
@@ -1183,8 +1410,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
(gpl->flag & GP_LAYER_LOCKED) == 0 &&
(gpd->flag & GP_DATA_STROKE_EDITMODE))
{
- gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag,
- (gpl->color[3] < 0.95f) ? gpl->color : NULL);
+ gp_draw_strokes_edit(gpd, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, diff_mat, alpha);
}
/* Check if may need to draw the active stroke cache, only if this layer is the active layer
@@ -1194,7 +1420,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
(gpf->flag & GP_FRAME_PAINT))
{
/* Set color for drawing buffer stroke - since this may not be set yet */
- glColor4fv(gpl->color);
+ // glColor4fv(gpl->color);
/* Buffer stroke needs to be drawn with a different linestyle
* to help differentiate them from normal strokes.
@@ -1202,11 +1428,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
* It should also be noted that sbuffer contains temporary point types
* i.e. tGPspoints NOT bGPDspoints
*/
- if (gpl->flag & GP_LAYER_VOLUMETRIC) {
- gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
+ if (gpd->sflag & PC_COLOR_VOLUMETRIC) {
+ gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick,
+ dflag, gpd->sbuffer_sflag, gpd->scolor);
}
else {
- gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
+ gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor);
}
}
}
@@ -1258,7 +1485,9 @@ static void gp_draw_status_text(bGPdata *gpd, ARegion *ar)
}
/* draw grease-pencil datablock */
-static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
+static void gp_draw_data(
+ bGPDbrush *brush, float alpha, bGPdata *gpd,
+ int offsx, int offsy, int winx, int winy, int cfra, int dflag)
{
/* reset line drawing style (in case previous user didn't reset) */
setlinestyle(0);
@@ -1276,7 +1505,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
glEnable(GL_BLEND);
/* draw! */
- gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag);
+ gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag);
/* turn off alpha blending, then smooth lines */
glDisable(GL_BLEND); // alpha blending
@@ -1303,14 +1532,25 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i
}
if (gpd_source) {
- gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag);
+ ToolSettings *ts = scene->toolsettings;
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+ if (brush != NULL) {
+ gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source,
+ offsx, offsy, winx, winy, cfra, dflag);
+ }
+
}
}
/* scene/clip data has already been drawn, only object/track data is drawn here
* if gpd_source == gpd, we don't have any object/track data and we can skip */
if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) {
- gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag);
+ ToolSettings *ts = scene->toolsettings;
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+ if (brush != NULL) {
+ gp_draw_data(brush, ts->gp_sculpt.alpha, gpd,
+ offsx, offsy, winx, winy, cfra, dflag);
+ }
}
}
@@ -1479,6 +1719,7 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, Scene *scene, View3D *v3d, AReg
/* draw it! */
gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype);
+
}
void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype)
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
index 738496a67c6..bd4856f1b93 100644
--- a/source/blender/editors/gpencil/editaction_gpencil.c
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -253,7 +253,7 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl)
gpfn = gpf->next;
if (gpf->flag & GP_FRAME_SELECT)
- changed |= gpencil_layer_delframe(gpl, gpf);
+ changed |= BKE_gpencil_layer_delframe(gpl, gpf);
}
return changed;
@@ -277,7 +277,7 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl)
bGPDframe *gpfd;
/* duplicate frame, and deselect self */
- gpfd = gpencil_frame_duplicate(gpf);
+ gpfd = BKE_gpencil_frame_duplicate(gpf);
gpf->flag &= ~GP_FRAME_SELECT;
BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
@@ -323,7 +323,7 @@ static int gp_anim_copy_cfra = 0;
/* This function frees any MEM_calloc'ed copy/paste buffer data */
void ED_gpencil_anim_copybuf_free(void)
{
- free_gpencil_layers(&gp_anim_copybuf);
+ BKE_gpencil_free_layers(&gp_anim_copybuf);
BLI_listbase_clear(&gp_anim_copybuf);
gp_anim_copy_firstframe = 999999999;
@@ -364,7 +364,7 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
/* if frame is selected, make duplicate it and its strokes */
if (gpf->flag & GP_FRAME_SELECT) {
/* make a copy of this frame */
- bGPDframe *new_frame = gpencil_frame_duplicate(gpf);
+ bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf);
BLI_addtail(&copied_frames, new_frame);
/* extend extents for keyframes encountered */
@@ -475,7 +475,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
gpfs->framenum += offset;
/* get frame to copy data into (if no frame returned, then just ignore) */
- gpf = gpencil_layer_getframe(gpld, gpfs->framenum, 1);
+ gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, 1);
if (gpf) {
bGPDstroke *gps, *gpsn;
@@ -498,7 +498,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
/* if no strokes (i.e. new frame) added, free gpf */
if (BLI_listbase_is_empty(&gpf->strokes))
- gpencil_layer_delframe(gpld, gpf);
+ BKE_gpencil_layer_delframe(gpld, gpf);
}
/* unapply offset from buffer-frame */
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c
index 0271afd6827..fcb2ce02bde 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_brush.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2015, Blender Foundation
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -51,6 +51,7 @@
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -223,9 +224,26 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i
GP_EditBrush_Data *brush = gso->brush;
float inf = gp_brush_influence_calc(gso, radius, co);
bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0;
-
+ /* need one flag enabled by default */
+ if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
+ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0)
+ {
+ gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
+ }
+
/* perform smoothing */
- return gp_smooth_stroke(gps, i, inf, affect_pressure);
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) {
+ gp_smooth_stroke(gps, i, inf, affect_pressure);
+ }
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) {
+ gp_smooth_stroke_strength(gps, i, inf);
+ }
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) {
+ gp_smooth_stroke_thickness(gps, i, inf);
+ }
+
+ return true;
}
/* ----------------------------------------------- */
@@ -268,6 +286,41 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
/* ----------------------------------------------- */
+/* Color Strength Brush */
+
+/* Make color more or less transparent by the specified amounts */
+static bool gp_brush_strength_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int i,
+ const int radius, const int co[2])
+{
+ bGPDspoint *pt = gps->points + i;
+ float inf;
+
+ /* Compute strength of effect
+ * - We divide the strength by 10, so that users can set "sane" values.
+ * Otherwise, good default values are in the range of 0.093
+ */
+ inf = gp_brush_influence_calc(gso, radius, co) / 10.0f;
+
+ /* apply */
+ // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff
+ if (gp_brush_invert_check(gso)) {
+ /* make line thinner - reduce stroke pressure */
+ pt->strength -= inf;
+ }
+ else {
+ /* make line thicker - increase stroke pressure */
+ pt->strength += inf;
+ }
+
+ /* Strength should stay within [0.0, 1.0] */
+ CLAMP(pt->strength, 0.0f, 1.0f);
+
+ return true;
+}
+
+
+/* ----------------------------------------------- */
/* Grab Brush */
/* Custom data per stroke for the Grab Brush
@@ -373,11 +426,12 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
}
/* Apply grab transform to all relevant points of the affected strokes */
-static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps)
+static void gp_brush_grab_apply_cached(
+ tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4])
{
tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
int i;
-
+
/* Apply dvec to all of the stored points */
for (i = 0; i < data->size; i++) {
bGPDspoint *pt = &gps->points[data->points[i]];
@@ -385,9 +439,23 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps)
/* adjust the amount of displacement to apply */
mul_v3_v3fl(delta, gso->dvec, data->weights[i]);
+ if (!parented) {
+ /* apply */
+ add_v3_v3(&pt->x, delta);
+ }
+ else {
+ float fpt[3];
+ /* apply transformation */
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ /* apply */
+ add_v3_v3(fpt, delta);
+ copy_v3_v3(&pt->x, fpt);
+ /* undo transformation to the init parent position */
+ float inverse_diff_mat[4][4];
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+ mul_m4_v3(inverse_diff_mat, &pt->x);
+ }
- /* apply */
- add_v3_v3(&pt->x, delta);
}
}
@@ -592,62 +660,92 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
*/
const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f;
const float fac = BLI_frand() * inf;
-
- /* Jitter is applied perpendicular to the mouse movement vector
- * - We compute all effects in screenspace (since it's easier)
- * and then project these to get the points/distances in
- * viewspace as needed
- */
- float mvec[2], svec[2];
-
- /* mouse movement in ints -> floats */
- mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
- mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
-
- /* rotate mvec by 90 degrees... */
- svec[0] = -mvec[1];
- svec[1] = mvec[0];
-
- //printf("svec = %f %f, ", svec[0], svec[1]);
-
- /* scale the displacement by the random displacement, and apply */
- if (BLI_frand() > 0.5f) {
- mul_v2_fl(svec, -fac);
- }
- else {
- mul_v2_fl(svec, fac);
+ /* need one flag enabled by default */
+ if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
+ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0)
+ {
+ gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
}
-
- //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]);
-
- /* convert to dataspace */
- if (gps->flag & GP_STROKE_3DSPACE) {
- /* 3D: Project to 3D space */
- if (gso->sa->spacetype == SPACE_VIEW3D) {
- bool flip;
- RegionView3D *rv3d = gso->ar->regiondata;
- float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
- if (flip == false) {
- float dvec[3];
- ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac);
- add_v3_v3(&pt->x, dvec);
+
+ /* apply random to position */
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) {
+ /* Jitter is applied perpendicular to the mouse movement vector
+ * - We compute all effects in screenspace (since it's easier)
+ * and then project these to get the points/distances in
+ * viewspace as needed
+ */
+ float mvec[2], svec[2];
+
+ /* mouse movement in ints -> floats */
+ mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
+ mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
+
+ /* rotate mvec by 90 degrees... */
+ svec[0] = -mvec[1];
+ svec[1] = mvec[0];
+
+ /* scale the displacement by the random displacement, and apply */
+ if (BLI_frand() > 0.5f) {
+ mul_v2_fl(svec, -fac);
+ }
+ else {
+ mul_v2_fl(svec, fac);
+ }
+
+ //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]);
+
+ /* convert to dataspace */
+ if (gps->flag & GP_STROKE_3DSPACE) {
+ /* 3D: Project to 3D space */
+ if (gso->sa->spacetype == SPACE_VIEW3D) {
+ bool flip;
+ RegionView3D *rv3d = gso->ar->regiondata;
+ float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
+ if (flip == false) {
+ float dvec[3];
+ ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac);
+ add_v3_v3(&pt->x, dvec);
+ }
+ }
+ else {
+ /* ERROR */
+ BLI_assert("3D stroke being sculpted in non-3D view");
}
}
else {
- /* ERROR */
- BLI_assert("3D stroke being sculpted in non-3D view");
+ /* 2D: As-is */
+ // XXX: v2d scaling/offset?
+ float nco[2];
+ nco[0] = (float)co[0] + svec[0];
+ nco[1] = (float)co[1] + svec[1];
+
+ copy_v2_v2(&pt->x, nco);
}
}
- else {
- /* 2D: As-is */
- // XXX: v2d scaling/offset?
- float nco[2];
- nco[0] = (float)co[0] + svec[0];
- nco[1] = (float)co[1] + svec[1];
-
- copy_v2_v2(&pt->x, nco);
+ /* apply random to strength */
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) {
+ if (BLI_frand() > 0.5f) {
+ pt->strength += fac;
+ }
+ else {
+ pt->strength -= fac;
+ }
+ CLAMP_MIN(pt->strength, 0.0f);
+ CLAMP_MAX(pt->strength, 1.0f);
}
-
+ /* apply random to thickness (use pressure) */
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) {
+ if (BLI_frand() > 0.5f) {
+ pt->pressure += fac;
+ }
+ else {
+ pt->pressure -= fac;
+ }
+ /* only limit lower value */
+ CLAMP_MIN(pt->pressure, 0.0f);
+ }
+
/* done */
return true;
}
@@ -743,7 +841,7 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
Scene *scene = gso->scene;
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
- bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, true);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
bGPDstroke *gps;
float delta[3];
@@ -1087,7 +1185,7 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
*/
// XXX: should this be allowed when framelock is enabled?
if (gpf->framenum != cfra) {
- gpencil_frame_addcopy(gpl, cfra);
+ BKE_gpencil_frame_addcopy(gpl, cfra);
}
}
}
@@ -1099,7 +1197,9 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
/* Apply ----------------------------------------------- */
/* Apply brush operation to points in this stroke */
-static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP_BrushApplyCb apply)
+static bool gpsculpt_brush_do_stroke(
+ tGP_BrushEditData *gso, bGPDstroke *gps, bool parented,
+ float diff_mat[4][4], GP_BrushApplyCb apply)
{
GP_SpaceConversion *gsc = &gso->gsc;
rcti *rect = &gso->brush_rect;
@@ -1111,9 +1211,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP
int i;
bool include_last = false;
bool changed = false;
-
+
if (gps->totpoints == 1) {
- gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ }
+ else {
+ bGPDspoint pt_temp;
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ }
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
@@ -1140,10 +1247,20 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP
continue;
}
}
-
- gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]);
- gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]);
-
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]);
+ gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]);
+ }
+ else {
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+ }
+
+
/* Check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
@@ -1228,76 +1345,105 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
/* Find visible strokes, and perform operations on those if hit */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ float diff_mat[4][4];
+ bool parented = false;
+
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
{
- switch (gso->brush_type) {
- case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_smooth_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_thickness_apply);
- break;
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ parented = true;
+ }
+ else {
+ parented = false;
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
}
-
- case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */
- {
- if (gso->first) {
- /* First time this brush stroke is being applied:
- * 1) Prepare data buffers (init/clear) for this stroke
- * 2) Use the points now under the cursor
- */
- gp_brush_grab_stroke_init(gso, gps);
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_grab_store_points);
+
+ switch (gso->brush_type) {
+ case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply);
+ break;
}
- else {
- /* Apply effect to the stored points */
- gp_brush_grab_apply_cached(gso, gps);
- changed |= true;
+
+ case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply);
+ break;
}
- break;
- }
-
- case GP_EDITBRUSH_TYPE_PUSH: /* Push points */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_push_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_pinch_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_twist_apply);
- break;
+
+ case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */
+ {
+ if (gso->first) {
+ /* First time this brush stroke is being applied:
+ * 1) Prepare data buffers (init/clear) for this stroke
+ * 2) Use the points now under the cursor
+ */
+ gp_brush_grab_stroke_init(gso, gps);
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points);
+ }
+ else {
+ /* Apply effect to the stored points */
+ gp_brush_grab_apply_cached(gso, gps, parented, diff_mat);
+ changed |= true;
+ }
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_PUSH: /* Push points */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply);
+ break;
+ }
+
+ default:
+ printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
+ break;
}
-
- case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_randomize_apply);
- break;
+ /* Triangulation must be calculated if changed */
+ if (changed) {
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ gps->tot_triangles = 0;
}
-
- default:
- printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
- break;
- }
-
- /* Triangulation must be calculated if changed */
- if (changed) {
- gps->flag |= GP_STROKE_RECALC_CACHES;
- gps->tot_triangles = 0;
}
}
CTX_DATA_END;
-
+
return changed;
}
@@ -1441,6 +1587,11 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve
needs_timer = true;
break;
+ case GP_EDITBRUSH_TYPE_STRENGTH:
+ brush_rate = 0.01f; // XXX: hardcoded
+ needs_timer = true;
+ break;
+
case GP_EDITBRUSH_TYPE_PINCH:
brush_rate = 0.001f; // XXX: hardcoded
needs_timer = true;
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index c47985ebc1b..c502ed1aa83 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -142,12 +142,31 @@ static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRN
/* convert the coordinates from the given stroke point into 3d-coordinates
* - assumes that the active space is the 3D-View
*/
-static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
+static void gp_strokepoint_convertcoords(
+ bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt,
+ float p3d[3], const rctf *subrect)
{
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
ARegion *ar = CTX_wm_region(C);
-
+ bGPDspoint mypt, *pt;
+
+ float diff_mat[4][4];
+ pt = &mypt;
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent == NULL) {
+ copy_v3_v3(&pt->x, &source_pt->x);
+ }
+ else {
+ /* apply parent transform */
+ float fpt[3];
+ ED_gpencil_parent_location(gpl, diff_mat);
+ mul_v3_m4v3(fpt, diff_mat, &source_pt->x);
+ copy_v3_v3(&pt->x, fpt);
+ }
+
+
if (gps->flag & GP_STROKE_3DSPACE) {
/* directly use 3d-coordinates */
copy_v3_v3(p3d, &pt->x);
@@ -628,7 +647,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
bp = &nu->bp[old_nbp - 1];
/* First point */
- gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
if (prev_bp) {
interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC);
if (do_gtd) {
@@ -649,7 +668,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
/* Second point */
/* Note dt2 is always negative, which marks the gap. */
if (gps->totpoints > 1) {
- gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
interp_v3_v3v3(p2, p, next_p, -GAP_DFAC);
if (do_gtd) {
dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
@@ -670,9 +689,9 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
float p[3], next_p[3];
float dt = 0.0f;
- gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
if (gps->totpoints > 1) {
- gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
interp_v3_v3v3(p, p, next_p, -GAP_DFAC);
if (do_gtd) {
dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
@@ -701,10 +720,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
i++, pt++, bp++)
{
float p[3];
- float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
+ float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC;
/* get coordinates to add at */
- gp_strokepoint_convertcoords(C, gps, pt, p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect);
gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time,
width, rad_fac, minmax_weights);
@@ -816,12 +835,12 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu
/* get initial coordinates */
pt = gps->points;
if (tot) {
- gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
if (tot > 1) {
- gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
}
if (stitch && tot > 2) {
- gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
}
}
@@ -940,7 +959,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu
/* add points */
for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) {
- float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
+ float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC;
if (i || old_nbezt) {
interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC);
@@ -964,7 +983,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu
copy_v3_v3(p3d_cur, p3d_next);
if (i + 2 < tot) {
- gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
}
prev_bezt = bezt;
@@ -1104,7 +1123,7 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG
struct Main *bmain = CTX_data_main(C);
View3D *v3d = CTX_wm_view3d(C); /* may be NULL */
Scene *scene = CTX_data_scene(C);
- bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
bGPDstroke *gps, *prev_gps = NULL;
Object *ob;
Curve *cu;
@@ -1216,7 +1235,7 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe
int i;
bool valid = true;
- if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first))
+ if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first))
return false;
do {
@@ -1273,8 +1292,8 @@ static int gp_convert_poll(bContext *C)
* and if we are not in edit mode!
*/
return ((sa && sa->spacetype == SPACE_VIEW3D) &&
- (gpl = gpencil_layer_getactive(gpd)) &&
- (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) &&
+ (gpl = BKE_gpencil_layer_getactive(gpd)) &&
+ (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) &&
(gpf->strokes.first) &&
(scene->obedit == NULL));
}
@@ -1283,7 +1302,7 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data");
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
Scene *scene = CTX_data_scene(C);
const int mode = RNA_enum_get(op->ptr, "type");
const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights");
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 746497f0ff5..876f873e5fa 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -40,6 +40,8 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
#include "BLT_translation.h"
@@ -57,6 +59,7 @@
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "BKE_colortools.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -72,6 +75,8 @@
#include "gpencil_intern.h"
+/* maximum sizes of gp-session buffer */
+#define GP_STROKE_BUFFER_MAX 5000
/* ************************************************ */
/* Datablock Operators */
@@ -92,7 +97,7 @@ static int gp_data_add_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (*gpd_ptr);
id_us_min(&gpd->id);
- *gpd_ptr = gpencil_data_addnew(DATA_("GPencil"));
+ *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
}
/* notifiers */
@@ -179,10 +184,10 @@ static int gp_layer_add_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (*gpd_ptr == NULL)
- *gpd_ptr = gpencil_data_addnew(DATA_("GPencil"));
+ *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
/* add new layer now */
- gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -209,7 +214,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot)
static int gp_layer_remove_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
/* sanity checks */
if (ELEM(NULL, gpd, gpl))
@@ -225,12 +230,12 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op)
* - if this is the only layer, this naturally becomes NULL
*/
if (gpl->prev)
- gpencil_layer_setactive(gpd, gpl->prev);
+ BKE_gpencil_layer_setactive(gpd, gpl->prev);
else
- gpencil_layer_setactive(gpd, gpl->next);
+ BKE_gpencil_layer_setactive(gpd, gpl->next);
/* delete the layer now... */
- gpencil_layer_delete(gpd, gpl);
+ BKE_gpencil_layer_delete(gpd, gpl);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -262,7 +267,7 @@ enum {
static int gp_layer_move_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
int direction = RNA_enum_get(op->ptr, "type");
@@ -316,7 +321,7 @@ void GPENCIL_OT_layer_move(wmOperatorType *ot)
static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
bGPDlayer *new_layer;
/* sanity checks */
@@ -324,12 +329,12 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
/* make copy of layer, and add it immediately after the existing layer */
- new_layer = gpencil_layer_duplicate(gpl);
+ new_layer = BKE_gpencil_layer_duplicate(gpl);
BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
/* ensure new layer has a unique name, and is now the active layer */
BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info));
- gpencil_layer_setactive(gpd, new_layer);
+ BKE_gpencil_layer_setactive(gpd, new_layer);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -357,7 +362,7 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
static int gp_hide_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *layer = gpencil_layer_getactive(gpd);
+ bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
bool unselected = RNA_boolean_get(op->ptr, "unselected");
/* sanity checks */
@@ -525,7 +530,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot)
static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *layer = gpencil_layer_getactive(gpd);
+ bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd);
bGPDlayer *gpl;
int flags = GP_LAYER_LOCKED;
bool isolate = false;
@@ -596,6 +601,61 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
"In addition to toggling the editability, also affect the visibility");
}
+/* ********************** Merge Layer with the next layer **************************** */
+
+static int gp_merge_layer_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl_next = gpl_current->next;
+
+ if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
+ BKE_report(op->reports, RPT_ERROR, "No layers to merge");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */
+ GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64);
+ for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) {
+ BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf);
+ }
+
+ /* read all frames from next layer */
+ for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) {
+ /* try to find frame in active layer */
+ bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum));
+ if (!frame) {
+ /* nothing found, create new */
+ frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum);
+ }
+ /* add to tail all strokes */
+ BLI_movelisttolist(&frame->strokes, &gpf->strokes);
+ }
+ /* Now delete next layer */
+ BKE_gpencil_layer_delete(gpd, gpl_next);
+ BLI_ghash_free(gh_frames_cur, NULL, NULL);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_merge(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Merge Down";
+ ot->idname = "GPENCIL_OT_layer_merge";
+ ot->description = "Merge the current layer with the layer below";
+
+ /* callbacks */
+ ot->exec = gp_merge_layer_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* ********************** Change Layer ***************************** */
static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
@@ -621,7 +681,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op)
/* Get layer or create new one */
if (layer_num == -1) {
/* Create layer */
- gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
else {
/* Try to get layer */
@@ -634,7 +694,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op)
}
/* Set active layer */
- gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_setactive(gpd, gpl);
/* updates */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -663,3 +723,1741 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot)
}
/* ************************************************ */
+
+/* ******************* Arrange Stroke Up/Down in drawing order ************************** */
+
+enum {
+ GP_STROKE_MOVE_UP = -1,
+ GP_STROKE_MOVE_DOWN = 1,
+ GP_STROKE_MOVE_TOP = 2,
+ GP_STROKE_MOVE_BOTTOM = 3
+};
+
+static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDstroke *gps;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ /* temp listbase to store selected strokes */
+ ListBase selected = {NULL};
+ const int direction = RNA_enum_get(op->ptr, "direction");
+
+ /* verify if any selected stroke is in the extreme of the stack and select to move */
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* only if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+ /* some stroke is already at front*/
+ if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) {
+ if (gps == gpf->strokes.last) {
+ BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on top");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ /* some stroke is already at botom */
+ if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) {
+ if (gps == gpf->strokes.first) {
+ BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on bottom");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ /* add to list */
+ BLI_addtail(&selected, BLI_genericNodeN(gps));
+ }
+ }
+
+ /* Now do the movement of the stroke */
+ switch (direction) {
+ /* Bring to Front */
+ case GP_STROKE_MOVE_TOP:
+ for (LinkData *link = selected.first; link; link = link->next) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkafter(&gpf->strokes, gpf->strokes.last, gps);
+ }
+ break;
+ /* Bring Forward */
+ case GP_STROKE_MOVE_UP:
+ for (LinkData *link = selected.last; link; link = link->prev) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkafter(&gpf->strokes, gps->next, gps);
+ }
+ break;
+ /* Send Backward */
+ case GP_STROKE_MOVE_DOWN:
+ for (LinkData *link = selected.first; link; link = link->next) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkbefore(&gpf->strokes, gps->prev, gps);
+ }
+ break;
+ /* Send to Back */
+ case GP_STROKE_MOVE_BOTTOM:
+ for (LinkData *link = selected.last; link; link = link->prev) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps);
+ }
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""},
+ {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""},
+ {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""},
+ {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""},
+ {0, NULL, 0, NULL, NULL }
+ };
+
+ /* identifiers */
+ ot->name = "Arrange Stroke";
+ ot->idname = "GPENCIL_OT_stroke_arrange";
+ ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_arrange_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", "");
+}
+/* ******************* Move Stroke to new color ************************** */
+
+static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette;
+ bGPDpalettecolor *color;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ palette = BKE_gpencil_palette_getactive(gpd);
+ color = BKE_gpencil_palettecolor_getactive(palette);
+ if (ELEM(NULL, palette, color)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* loop all strokes */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ /* only if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false)
+ continue;
+
+ /* asign new color (only if different) */
+ if (STREQ(gps->colorname, color->info) == false) {
+ strcpy(gps->colorname, color->info);
+ gps->flag |= GP_STROKE_RECALC_COLOR;
+ }
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Stroke Color";
+ ot->idname = "GPENCIL_OT_stroke_change_color";
+ ot->description = "Move selected strokes to active color";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_change_color_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Lock color of non selected Strokes colors ************************** */
+
+static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ palette = BKE_gpencil_palette_getactive(gpd);
+ if (ELEM(NULL, palette))
+ return OPERATOR_CANCELLED;
+
+ /* first lock all colors */
+ for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag |= PC_COLOR_LOCKED;
+ }
+
+ /* loop all selected strokes and unlock any color */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ /* only if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* unlock color */
+ if (gps->palcolor != NULL) {
+ gps->palcolor->flag &= ~PC_COLOR_LOCKED;
+ }
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Lock Unused Colors";
+ ot->idname = "GPENCIL_OT_stroke_lock_color";
+ ot->description = "Lock any color not used in any selected stroke";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_lock_color_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Apply layer thickness change to Strokes ************************** */
+
+static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl, gpl->frames.first))
+ return OPERATOR_CANCELLED;
+
+ /* loop all strokes */
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* Apply thickness */
+ gps->thickness = gps->thickness + gpl->thickness;
+ }
+ }
+ /* clear value */
+ gpl->thickness = 0.0f;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Apply Stroke Thickness";
+ ot->idname = "GPENCIL_OT_stroke_apply_thickness";
+ ot->description = "Apply the thickness change of the layer to its strokes";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_apply_thickness_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Close Strokes ************************** */
+
+enum {
+ GP_STROKE_CYCLIC_CLOSE = 1,
+ GP_STROKE_CYCLIC_OPEN = 2,
+ GP_STROKE_CYCLIC_TOGGLE = 3
+};
+
+static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const int type = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* loop all selected strokes */
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+ {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ bGPDpalettecolor *palcolor = gps->palcolor;
+
+ /* skip strokes that are not selected or invalid for current view */
+ if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* skip hidden or locked colors */
+ if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
+ continue;
+
+ switch (type) {
+ case GP_STROKE_CYCLIC_CLOSE:
+ /* Close all (enable) */
+ gps->flag |= GP_STROKE_CYCLIC;
+ break;
+ case GP_STROKE_CYCLIC_OPEN:
+ /* Open all (disable) */
+ gps->flag &= ~GP_STROKE_CYCLIC;
+ break;
+ case GP_STROKE_CYCLIC_TOGGLE:
+ /* Just toggle flag... */
+ gps->flag ^= GP_STROKE_CYCLIC;
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+/**
+ * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
+ * option to force opened/closed strokes instead of just toggle behavior.
+ */
+void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem cyclic_type[] = {
+ {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
+ {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
+ {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Set Cyclical State";
+ ot->idname = "GPENCIL_OT_stroke_cyclical_set";
+ ot->description = "Close or open the selected stroke adding an edge from last to first point";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_cyclical_set_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
+}
+
+/* ******************* Stroke join ************************** */
+
+/* Helper: flip stroke */
+static void gpencil_flip_stroke(bGPDstroke *gps)
+{
+ bGPDspoint pt, *point, *point2;
+ int end = gps->totpoints - 1;
+
+ for (int i = 0; i < gps->totpoints / 2; i++) {
+ /* save first point */
+ point = &gps->points[i];
+ pt.x = point->x;
+ pt.y = point->y;
+ pt.z = point->z;
+ pt.flag = point->flag;
+ pt.pressure = point->pressure;
+ pt.strength = point->strength;
+ pt.time = point->time;
+
+ /* replace first point with last point */
+ point2 = &gps->points[end];
+ point->x = point2->x;
+ point->y = point2->y;
+ point->z = point2->z;
+ point->flag = point2->flag;
+ point->pressure = point2->pressure;
+ point->strength = point2->strength;
+ point->time = point2->time;
+
+ /* replace last point with first saved before */
+ point = &gps->points[end];
+ point->x = pt.x;
+ point->y = pt.y;
+ point->z = pt.z;
+ point->flag = pt.flag;
+ point->pressure = pt.pressure;
+ point->strength = pt.strength;
+ point->time = pt.time;
+
+ end--;
+ }
+}
+
+/* Helper: copy point between strokes */
+static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
+ float pressure, float strength, float deltatime)
+{
+ bGPDspoint *newpoint;
+
+ gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
+ gps->totpoints++;
+
+ newpoint = &gps->points[gps->totpoints - 1];
+ newpoint->x = point->x * delta[0];
+ newpoint->y = point->y * delta[1];
+ newpoint->z = point->z * delta[2];
+ newpoint->flag = point->flag;
+ newpoint->pressure = pressure;
+ newpoint->strength = strength;
+ newpoint->time = point->time + deltatime;
+}
+
+/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
+static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
+{
+ bGPDspoint point, *pt;
+ int i;
+ float delta[3] = {1.0f, 1.0f, 1.0f};
+ float deltatime = 0.0f;
+
+ /* sanity checks */
+ if (ELEM(NULL, gps_a, gps_b))
+ return;
+
+ if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
+ return;
+
+ /* define start and end points of each stroke */
+ float sa[3], sb[3], ea[3], eb[3];
+ pt = &gps_a->points[0];
+ copy_v3_v3(sa, &pt->x);
+
+ pt = &gps_a->points[gps_a->totpoints - 1];
+ copy_v3_v3(ea, &pt->x);
+
+ pt = &gps_b->points[0];
+ copy_v3_v3(sb, &pt->x);
+
+ pt = &gps_b->points[gps_b->totpoints - 1];
+ copy_v3_v3(eb, &pt->x);
+
+ /* review if need flip stroke B */
+ float ea_sb = len_squared_v3v3(ea, sb);
+ float ea_eb = len_squared_v3v3(ea, eb);
+ /* flip if distance to end point is shorter */
+ if (ea_eb < ea_sb) {
+ gpencil_flip_stroke(gps_b);
+ }
+
+ /* don't visibly link the first and last points? */
+ if (leave_gaps) {
+ /* 1st: add one tail point to start invisible area */
+ point = gps_a->points[gps_a->totpoints - 1];
+ deltatime = point.time;
+ gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
+
+ /* 2nd: add one head point to finish invisible area */
+ point = gps_b->points[0];
+ gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
+ }
+
+ /* 3rd: add all points */
+ for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
+ /* check if still room in buffer */
+ if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
+ gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
+ }
+ }
+}
+
+static int gp_stroke_join_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDstroke *gps, *gpsn;
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+ bGPDframe *gpf_a = NULL;
+ bGPDstroke *stroke_a = NULL;
+ bGPDstroke *stroke_b = NULL;
+ bGPDstroke *new_stroke = NULL;
+
+ const int type = RNA_enum_get(op->ptr, "type");
+ const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ if (activegpl->flag & GP_LAYER_LOCKED)
+ return OPERATOR_CANCELLED;
+
+ BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
+
+
+ /* read all selected strokes */
+ bool first = false;
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+ {
+ bGPDframe *gpf = gpl->actframe;
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+ /* to join strokes, cyclic must be disabled */
+ gps->flag &= ~GP_STROKE_CYCLIC;
+ /* saves first frame and stroke */
+ if (!first) {
+ first = true;
+ gpf_a = gpf;
+ stroke_a = gps;
+ }
+ else {
+ stroke_b = gps;
+ /* create a new stroke if was not created before (only created if something to join) */
+ if (new_stroke == NULL) {
+ new_stroke = MEM_dupallocN(stroke_a);
+ new_stroke->points = MEM_dupallocN(stroke_a->points);
+ new_stroke->triangles = NULL;
+ new_stroke->tot_triangles = 0;
+ new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+ /* if new, set current color */
+ if (type == GP_STROKE_JOINCOPY) {
+ new_stroke->palcolor = palcolor;
+ strcpy(new_stroke->colorname, palcolor->info);
+ new_stroke->flag |= GP_STROKE_RECALC_COLOR;
+ }
+ }
+ /* join new_stroke and stroke B. New stroke will contain all the previous data */
+ gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
+
+ /* if join only, delete old strokes */
+ if (type == GP_STROKE_JOIN) {
+ if (stroke_a) {
+ BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
+ BLI_remlink(&gpf->strokes, stroke_a);
+ BKE_gpencil_free_stroke(stroke_a);
+ stroke_a = NULL;
+ }
+ if (stroke_b) {
+ BLI_remlink(&gpf->strokes, stroke_b);
+ BKE_gpencil_free_stroke(stroke_b);
+ stroke_b = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+ /* add new stroke if was not added before */
+ if (type == GP_STROKE_JOINCOPY) {
+ if (new_stroke) {
+ /* Add a new frame if needed */
+ if (activegpl->actframe == NULL)
+ activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
+
+ BLI_addtail(&activegpl->actframe->strokes, new_stroke);
+ }
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_join(wmOperatorType *ot)
+{
+ static EnumPropertyItem join_type[] = {
+ {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
+ {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Join Strokes";
+ ot->idname = "GPENCIL_OT_stroke_join";
+ ot->description = "Join selected strokes (optionally as new stroke)";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_join_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
+ RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
+}
+
+/* ******************* Stroke flip ************************** */
+
+static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* read all selected strokes */
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+ {
+ bGPDframe *gpf = gpl->actframe;
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+ /* flip stroke */
+ gpencil_flip_stroke(gps);
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Flip Stroke";
+ ot->idname = "GPENCIL_OT_stroke_flip";
+ ot->description = "Change direction of the points of the selected strokes";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_flip_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ************************************************ */
+/* Drawing Brushes Operators */
+
+/* ******************* Add New Brush ************************ */
+
+/* add new brush - wrapper around API */
+static int gp_brush_add_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ /* if there's no existing container */
+ if (ts == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
+ return OPERATOR_CANCELLED;
+ }
+ /* add new brush now */
+ BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Brush";
+ ot->idname = "GPENCIL_OT_brush_add";
+ ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil datablock";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_brush_add_exec;
+ ot->poll = gp_add_poll;
+}
+
+/* ******************* Remove Active Brush ************************* */
+
+static int gp_brush_remove_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+
+ /* sanity checks */
+ if (ELEM(NULL, ts, brush))
+ return OPERATOR_CANCELLED;
+
+ if (BLI_listbase_count(&ts->gp_brushes) < 2) {
+ BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush. Unable to delete brush");
+ return OPERATOR_CANCELLED;
+ }
+
+
+ /* make the brush before this the new active brush
+ * - use the one after if this is the first
+ * - if this is the only brush, this naturally becomes NULL
+ */
+ if (brush->prev)
+ BKE_gpencil_brush_setactive(ts, brush->prev);
+ else
+ BKE_gpencil_brush_setactive(ts, brush->next);
+
+ /* delete the brush now... */
+ BKE_gpencil_brush_delete(ts, brush);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove brush";
+ ot->idname = "GPENCIL_OT_brush_remove";
+ ot->description = "Remove active Grease Pencil drawing brush";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_brush_remove_exec;
+ ot->poll = gp_active_brush_poll;
+}
+
+/* ********************** Change Brush ***************************** */
+
+static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ /* call the menu, which will call this operator again, hence the canceled */
+ pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush");
+ UI_popup_menu_end(C, pup);
+
+ return OPERATOR_INTERFACE;
+}
+
+static int gp_brush_change_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = NULL;
+ int brush_num = RNA_enum_get(op->ptr, "brush");
+
+ /* Get brush or create new one */
+ if (brush_num == -1) {
+ /* Create brush */
+ brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
+ }
+ else {
+ /* Try to get brush */
+ brush = BLI_findlink(&ts->gp_brushes, brush_num);
+
+ if (brush == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* Set active brush */
+ BKE_gpencil_brush_setactive(ts, brush);
+
+ /* updates */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_change(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Brush";
+ ot->idname = "GPENCIL_OT_brush_change";
+ ot->description = "Change active Grease Pencil drawing brush";
+
+ /* callbacks */
+ ot->invoke = gp_brush_change_invoke;
+ ot->exec = gp_brush_change_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* gp brush to use (dynamic enum) */
+ ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", "");
+ RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf);
+}
+
+/* ******************* Move Brush Up/Down ************************** */
+
+enum {
+ GP_BRUSH_MOVE_UP = -1,
+ GP_BRUSH_MOVE_DOWN = 1
+};
+
+static int gp_brush_move_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+
+ int direction = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, ts, brush)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* up or down? */
+ if (direction == GP_BRUSH_MOVE_UP) {
+ /* up */
+ BLI_remlink(&ts->gp_brushes, brush);
+ BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush);
+ }
+ else if (direction == GP_BRUSH_MOVE_DOWN) {
+ /* down */
+ BLI_remlink(&ts->gp_brushes, brush);
+ BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL }
+ };
+
+ /* identifiers */
+ ot->name = "Move Brush";
+ ot->idname = "GPENCIL_OT_brush_move";
+ ot->description = "Move the active Grease Pencil drawing brush up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gp_brush_move_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", "");
+}
+
+/* ******************* Brush create presets ************************** */
+
+static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ BKE_gpencil_brush_init_presets(ts);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Create Preset Brushes";
+ ot->idname = "GPENCIL_OT_brush_presets_create";
+ ot->description = "Create a set of predefined Grease Pencil drawing brushes";
+
+ /* api callbacks */
+ ot->exec = gp_brush_presets_create_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+}
+
+/* ***************** Copy Brush ************************ */
+
+static int gp_brush_copy_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ /* if there's no existing container */
+ if (ts == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+ bGPDbrush *newbrush;
+
+ /* sanity checks */
+ if (ELEM(NULL, brush))
+ return OPERATOR_CANCELLED;
+
+ /* create a brush and duplicate data */
+ newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true);
+ newbrush->thickness = brush->thickness;
+ newbrush->draw_smoothfac = brush->draw_smoothfac;
+ newbrush->draw_smoothlvl = brush->draw_smoothlvl;
+ newbrush->sublevel = brush->sublevel;
+ newbrush->flag = brush->flag;
+ newbrush->draw_sensitivity = brush->draw_sensitivity;
+ newbrush->draw_strength = brush->draw_strength;
+ newbrush->draw_jitter = brush->draw_jitter;
+ newbrush->draw_angle = brush->draw_angle;
+ newbrush->draw_angle_factor = brush->draw_angle_factor;
+ newbrush->draw_random_press = brush->draw_random_press;
+ newbrush->draw_random_sub = brush->draw_random_sub;
+
+ /* free automatic curves created by default (replaced by copy) */
+ curvemapping_free(newbrush->cur_sensitivity);
+ curvemapping_free(newbrush->cur_strength);
+ curvemapping_free(newbrush->cur_jitter);
+
+ /* make a copy of curves */
+ newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity);
+ newbrush->cur_strength = curvemapping_copy(brush->cur_strength);
+ newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter);
+
+ BKE_gpencil_brush_setactive(ts, newbrush);
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Brush";
+ ot->idname = "GPENCIL_OT_brush_copy";
+ ot->description = "Copy current Grease Pencil drawing brush";
+
+ /* callbacks */
+ ot->exec = gp_brush_copy_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Select Brush ************************ */
+
+static int gp_brush_select_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ /* if there's no existing container */
+ if (ts == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere to go");
+ return OPERATOR_CANCELLED;
+ }
+
+ const int index = RNA_int_get(op->ptr, "index");
+ bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index);
+ /* sanity checks */
+ if (ELEM(NULL, brush)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_gpencil_brush_setactive(ts, brush);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Brush";
+ ot->idname = "GPENCIL_OT_brush_select";
+ ot->description = "Select a Grease Pencil drawing brush";
+
+ /* callbacks */
+ ot->exec = gp_brush_select_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX);
+}
+
+/* ************************************************ */
+/* Palette Operators */
+
+/* ******************* Add New Palette ************************ */
+
+/* add new palette - wrapper around API */
+static int gp_palette_add_exec(bContext *C, wmOperator *op)
+{
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
+
+ /* 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");
+ return OPERATOR_CANCELLED;
+ }
+ if (*gpd_ptr == NULL)
+ *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
+
+ /* add new palette now */
+ BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Palette";
+ ot->idname = "GPENCIL_OT_palette_add";
+ ot->description = "Add new Grease Pencil palette for the active Grease Pencil datablock";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palette_add_exec;
+ ot->poll = gp_add_poll;
+}
+
+/* ******************* Remove Active Palette ************************* */
+
+static int gp_palette_remove_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ if (BLI_listbase_count(&gpd->palettes) < 2) {
+ BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette. Unable to delete palette");
+ return OPERATOR_CANCELLED;
+ }
+
+
+ /* make the palette before this the new active palette
+ * - use the one after if this is the first
+ * - if this is the only palette, this naturally becomes NULL
+ */
+ if (palette->prev)
+ BKE_gpencil_palette_setactive(gpd, palette->prev);
+ else
+ BKE_gpencil_palette_setactive(gpd, palette->next);
+
+ /* delete the palette now... */
+ BKE_gpencil_palette_delete(gpd, palette);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove palette";
+ ot->idname = "GPENCIL_OT_palette_remove";
+ ot->description = "Remove active Grease Pencil palette";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palette_remove_exec;
+ ot->poll = gp_active_palette_poll;
+}
+
+/* ********************** Change Palette ***************************** */
+
+static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ /* call the menu, which will call this operator again, hence the canceled */
+ pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette");
+ UI_popup_menu_end(C, pup);
+
+ return OPERATOR_INTERFACE;
+}
+
+static int gp_palette_change_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ bGPDpalette *palette = NULL;
+ int palette_num = RNA_enum_get(op->ptr, "palette");
+
+ /* Get palette or create new one */
+ if (palette_num == -1) {
+ /* Create palette */
+ palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
+ }
+ else {
+ /* Try to get palette */
+ palette = BLI_findlink(&gpd->palettes, palette_num);
+
+ if (palette == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* Set active palette */
+ BKE_gpencil_palette_setactive(gpd, palette);
+
+ /* updates */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_change(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Palette";
+ ot->idname = "GPENCIL_OT_palette_change";
+ ot->description = "Change active Grease Pencil palette";
+
+ /* callbacks */
+ ot->invoke = gp_palette_change_invoke;
+ ot->exec = gp_palette_change_exec;
+ ot->poll = gp_active_palette_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* gp palette to use (dynamic enum) */
+ ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", "");
+ RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf);
+}
+
+/* ******************* Lock and hide any color non used in current layer ************************** */
+
+static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ palette = BKE_gpencil_palette_getactive(gpd);
+ if (ELEM(NULL, palette))
+ return OPERATOR_CANCELLED;
+
+ /* first lock and hide all colors */
+ for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag |= PC_COLOR_LOCKED;
+ palcolor->flag |= PC_COLOR_HIDE;
+ }
+
+ /* loop all selected strokes and unlock any color used in active layer */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+
+ /* unlock/unhide color if not unlocked before */
+ if (gps->palcolor != NULL) {
+ gps->palcolor->flag &= ~PC_COLOR_LOCKED;
+ gps->palcolor->flag &= ~PC_COLOR_HIDE;
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Disable Unused Layer Colors";
+ ot->idname = "GPENCIL_OT_palette_lock_layer";
+ ot->description = "Lock and hide any color not used in any layer";
+
+ /* api callbacks */
+ ot->exec = gp_palette_lock_layer_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ************************************************ */
+/* Palette Colors Operators */
+
+/* ******************* Add New Palette ************************ */
+
+/* add new palette - wrapper around API */
+static int gp_palettecolor_add_exec(bContext *C, wmOperator *op)
+{
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
+
+ /* 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");
+ return OPERATOR_CANCELLED;
+ }
+ if (*gpd_ptr == NULL)
+ *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil"));
+
+ /* verify palette */
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr);
+ if (palette == NULL)
+ palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
+
+ /* add new palette color now */
+ BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Palette Color";
+ ot->idname = "GPENCIL_OT_palettecolor_add";
+ ot->description = "Add new Grease Pencil palette color for the active Grease Pencil datablock";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_add_exec;
+ ot->poll = gp_add_poll;
+}
+
+/* ******************* Remove Active Palette color ************************* */
+
+static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, color))
+ return OPERATOR_CANCELLED;
+
+ /* make the palette color before this the new active color
+ * - use the one after if this is the first
+ * - if this is the only color, this naturally becomes NULL
+ */
+ if (color->prev)
+ BKE_gpencil_palettecolor_setactive(palette, color->prev);
+ else
+ BKE_gpencil_palettecolor_setactive(palette, color->next);
+
+ /* delete the strokes */
+ BKE_gpencil_palettecolor_delete_strokes(gpd, color->info);
+
+ /* delete the palette color now... */
+ BKE_gpencil_palettecolor_delete(palette, color);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove palette color";
+ ot->idname = "GPENCIL_OT_palettecolor_remove";
+ ot->description = "Remove active Grease Pencil palette color";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_remove_exec;
+ ot->poll = gp_active_palettecolor_poll;
+}
+
+/* ********************** Isolate palette color **************************** */
+
+static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette);
+ bGPDpalettecolor *palcolor;
+
+ int flags = PC_COLOR_LOCKED;
+ bool isolate = false;
+
+ if (RNA_boolean_get(op->ptr, "affect_visibility"))
+ flags |= PC_COLOR_HIDE;
+
+ if (ELEM(NULL, gpd, active_color)) {
+ BKE_report(op->reports, RPT_ERROR, "No active color to isolate");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Test whether to isolate or clear all flags */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ /* Skip if this is the active one */
+ if (palcolor == active_color)
+ continue;
+
+ /* If the flags aren't set, that means that the color is
+ * not alone, so we have some colors to isolate still
+ */
+ if ((palcolor->flag & flags) == 0) {
+ isolate = true;
+ break;
+ }
+ }
+
+ /* Set/Clear flags as appropriate */
+ if (isolate) {
+ /* Set flags on all "other" colors */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ if (palcolor == active_color)
+ continue;
+ else
+ palcolor->flag |= flags;
+ }
+ }
+ else {
+ /* Clear flags - Restore everything else */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~flags;
+ }
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Isolate Palette Color";
+ ot->idname = "GPENCIL_OT_palettecolor_isolate";
+ ot->description = "Toggle whether the active color is the only one that is editable and/or visible";
+
+ /* callbacks */
+ ot->exec = gp_isolate_palettecolor_exec;
+ ot->poll = gp_active_palettecolor_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling "
+ "the editability, also affect the visibility");
+}
+
+/* *********************** Hide Palette colors ******************************** */
+
+static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+ bool unselected = RNA_boolean_get(op->ptr, "unselected");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ if (unselected) {
+ bGPDpalettecolor *color;
+
+ /* hide unselected */
+ for (color = palette->colors.first; color; color = color->next) {
+ if (color != palcolor) {
+ color->flag |= PC_COLOR_HIDE;
+ }
+ }
+ }
+ else {
+ /* hide selected/active */
+ palcolor->flag |= PC_COLOR_HIDE;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hide Color(s)";
+ ot->idname = "GPENCIL_OT_palettecolor_hide";
+ ot->description = "Hide selected/unselected Grease Pencil colors";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_hide_exec;
+ ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors");
+}
+
+/* ********************** Show All Colors ***************************** */
+
+/* poll callback for showing colors */
+static int gp_palettecolor_reveal_poll(bContext *C)
+{
+ return ED_gpencil_data_get_active(C) != NULL;
+}
+
+static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ /* make all colors visible */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~PC_COLOR_HIDE;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Show All Colors";
+ ot->idname = "GPENCIL_OT_palettecolor_reveal";
+ ot->description = "Unhide all hidden Grease Pencil palette colors";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_reveal_exec;
+ ot->poll = gp_palettecolor_reveal_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Lock/Unlock All Palette colors ************************ */
+
+static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ /* make all layers non-editable */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag |= PC_COLOR_LOCKED;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Lock All Colors";
+ ot->idname = "GPENCIL_OT_palettecolor_lock_all";
+ ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_lock_all_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------- */
+
+static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ /* make all layers editable again*/
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~PC_COLOR_LOCKED;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Unlock All Colors";
+ ot->idname = "GPENCIL_OT_palettecolor_unlock_all";
+ ot->description = "Unlock all Grease Pencil colors so that they can be edited";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_unlock_all_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ******************* Move Color Up/Down ************************** */
+
+enum {
+ GP_COLOR_MOVE_UP = -1,
+ GP_COLOR_MOVE_DOWN = 1
+};
+
+static int gp_palettecolor_move_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+ int direction = RNA_enum_get(op->ptr, "direction");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ /* up or down? */
+ if (direction == GP_COLOR_MOVE_UP) {
+ /* up */
+ BLI_remlink(&palette->colors, palcolor);
+ BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor);
+ }
+ else if (direction == GP_COLOR_MOVE_DOWN) {
+ /* down */
+ BLI_remlink(&palette->colors, palcolor);
+ BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Move Palette color";
+ ot->idname = "GPENCIL_OT_palettecolor_move";
+ ot->description = "Move the active Grease Pencil palette color up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gp_palettecolor_move_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", "");
+}
+
+/* ***************** Select all strokes using Palette color ************************ */
+
+static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ /* read all strokes and select*/
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ /* verify something to do */
+ for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false)
+ continue;
+
+ /* select */
+ if (strcmp(palcolor->info, gps->colorname) == 0) {
+ bGPDspoint *pt;
+ int i;
+
+ gps->flag |= GP_STROKE_SELECT;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Color";
+ ot->idname = "GPENCIL_OT_palettecolor_select";
+ ot->description = "Select all Grease Pencil strokes using current color";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_select_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Copy Palette color ************************ */
+
+static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+ bGPDpalettecolor *newcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ /* create a new color and duplicate data */
+ newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true);
+ copy_v4_v4(newcolor->color, palcolor->color);
+ copy_v4_v4(newcolor->fill, palcolor->fill);
+ newcolor->flag = palcolor->flag;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Color";
+ ot->idname = "GPENCIL_OT_palettecolor_copy";
+ ot->description = "Copy current Grease Pencil palette color";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_copy_exec;
+ ot->poll = gp_active_palettecolor_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index ac49a51c716..b298769c6dc 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -125,10 +125,48 @@ static int gp_stroke_edit_poll(bContext *C)
return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
}
+/* ************ Stroke Hide selection Toggle ************** */
+
+static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ if (ts == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* Just toggle alpha... */
+ if (ts->gp_sculpt.alpha > 0.0f) {
+ ts->gp_sculpt.alpha = 0.0f;
+ }
+ else {
+ ts->gp_sculpt.alpha = 1.0f;
+ }
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hide Selection";
+ ot->idname = "GPENCIL_OT_selection_opacity_toggle";
+ ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
+
+ /* callbacks */
+ ot->exec = gpencil_hideselect_toggle_exec;
+ ot->poll = gp_stroke_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+}
+
/* ************** Duplicate Selected Strokes **************** */
/* Make copies of selected point segments in a selected stroke */
-static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
+static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
{
bGPDspoint *pt;
int i;
@@ -169,6 +207,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
/* make a stupid copy first of the entire stroke (to get the flags too) */
gpsd = MEM_dupallocN(gps);
+ strcpy(gpsd->tmp_layerinfo, layername); /* saves original layer name */
/* initialize triangle memory - will be calculated on next redraw */
gpsd->triangles = NULL;
@@ -216,8 +255,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
/* make copies of selected strokes, and deselect these once we're done */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false)
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
+ }
if (gps->flag & GP_STROKE_SELECT) {
if (gps->totpoints == 1) {
@@ -226,8 +266,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
/* make direct copies of the stroke and its points */
gpsd = MEM_dupallocN(gps);
+ strcpy(gpsd->tmp_layerinfo, gpl->info);
gpsd->points = MEM_dupallocN(gps->points);
-
+
/* triangle information - will be calculated on next redraw */
gpsd->flag |= GP_STROKE_RECALC_CACHES;
gpsd->triangles = NULL;
@@ -238,7 +279,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
}
else {
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &new_strokes);
+ gp_duplicate_points(gps, &new_strokes, gpl->info);
}
/* deselect original stroke, or else the originals get moved too
@@ -345,6 +386,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
/* make direct copies of the stroke and its points */
gpsd = MEM_dupallocN(gps);
+ strcpy(gpsd->tmp_layerinfo, gpl->info); /* saves original layer name */
gpsd->points = MEM_dupallocN(gps->points);
/* triangles cache - will be recalculated on next redraw */
@@ -358,7 +400,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
}
else {
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &gp_strokes_copypastebuf);
+ gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
}
}
}
@@ -395,13 +437,20 @@ static int gp_strokes_paste_poll(bContext *C)
return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
}
+enum {
+ GP_COPY_ONLY = -1,
+ GP_COPY_MERGE = 1
+};
+
static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
bGPDframe *gpf;
-
+
+ int type = RNA_enum_get(op->ptr, "type");
+
/* check for various error conditions */
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -413,9 +462,9 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
}
else if (gpl == NULL) {
/* no active layer - let's just create one */
- gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
- else if (gpencil_layer_is_editable(gpl) == false) {
+ else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
return OPERATOR_CANCELLED;
}
@@ -463,26 +512,34 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
* we are obliged to add a new frame if one
* doesn't exist already
*/
- gpf = gpencil_layer_getframe(gpl, CFRA, true);
- if (gpf) {
bGPDstroke *gps;
-
/* Copy each stroke into the layer */
for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
- bGPDstroke *new_stroke = MEM_dupallocN(gps);
-
- new_stroke->points = MEM_dupallocN(gps->points);
-
- new_stroke->flag |= GP_STROKE_RECALC_CACHES;
- new_stroke->triangles = NULL;
-
- new_stroke->next = new_stroke->prev = NULL;
- BLI_addtail(&gpf->strokes, new_stroke);
+ /* need to verify if layer exist nad frame */
+ if (type != GP_COPY_MERGE) {
+ gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info));
+ if (gpl == NULL) {
+ /* no layer - use active (only if layer deleted before paste) */
+ gpl = CTX_data_active_gpencil_layer(C);
+ }
+ }
+ gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
+ if (gpf) {
+ bGPDstroke *new_stroke = MEM_dupallocN(gps);
+ new_stroke->tmp_layerinfo[0] = '\0';
+
+ new_stroke->points = MEM_dupallocN(gps->points);
+
+ new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+ new_stroke->triangles = NULL;
+
+ new_stroke->next = new_stroke->prev = NULL;
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
}
}
- }
/* updates */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -492,10 +549,16 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_paste(wmOperatorType *ot)
{
+ static EnumPropertyItem copy_type[] = {
+ {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
+ {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
/* identifiers */
ot->name = "Paste Strokes";
ot->idname = "GPENCIL_OT_paste";
- ot->description = "Paste previously copied strokes into active layer";
+ ot->description = "Paste previously copied strokes or copy and merge in active layer";
/* callbacks */
ot->exec = gp_strokes_paste_exec;
@@ -503,6 +566,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
}
/* ******************* Move To Layer ****************************** */
@@ -532,7 +597,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
/* Get layer or create new one */
if (layer_num == -1) {
/* Create layer */
- target_layer = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
else {
/* Try to get layer */
@@ -577,7 +642,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
/* Paste them all in one go */
if (strokes.first) {
Scene *scene = CTX_data_scene(C);
- bGPDframe *gpf = gpencil_layer_getframe(target_layer, CFRA, true);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true);
BLI_movelisttolist(&gpf->strokes, &strokes);
BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
@@ -614,7 +679,7 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
static int gp_actframe_delete_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
/* only if there's an active layer with an active frame */
return (gpl && gpl->actframe);
@@ -625,8 +690,8 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
- bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
/* if there's no existing Grease-Pencil data there, add some */
if (gpd == NULL) {
@@ -639,7 +704,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
}
/* delete it... */
- gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_delframe(gpl, gpf);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -681,13 +746,13 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
{
/* try to get the "active" frame - but only if it actually occurs on this frame */
- bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
if (gpf == NULL)
continue;
/* delete it... */
- gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_delframe(gpl, gpf);
/* we successfully modified something */
success = true;
@@ -1069,7 +1134,7 @@ void GPENCIL_OT_delete(wmOperatorType *ot)
};
/* identifiers */
- ot->name = "Delete...";
+ ot->name = "Delete";
ot->idname = "GPENCIL_OT_delete";
ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
@@ -1124,25 +1189,65 @@ static int gp_snap_poll(bContext *C)
static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
{
RegionView3D *rv3d = CTX_wm_region_data(C);
- float gridf = rv3d->gridview;
+ const float gridf = rv3d->gridview;
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- // TOOD: if entire stroke is selected, offset entire stroke by same amount?
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* only if point is selected.. */
- if (pt->flag & GP_SPOINT_SELECT) {
- pt->x = gridf * floorf(0.5f + pt->x / gridf);
- pt->y = gridf * floorf(0.5f + pt->y / gridf);
- pt->z = gridf * floorf(0.5f + pt->z / gridf);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float diff_mat[4][4];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
+ bGPDspoint *pt;
+ int i;
+
+ // TOOD: if entire stroke is selected, offset entire stroke by same amount?
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+
+ /* only if point is selected.. */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (gpl->parent == NULL) {
+ pt->x = gridf * floorf(0.5f + pt->x / gridf);
+ pt->y = gridf * floorf(0.5f + pt->y / gridf);
+ pt->z = gridf * floorf(0.5f + pt->z / gridf);
+ }
+ else {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
+ fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
+ fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
+
+ /* return data */
+ copy_v3_v3(&pt->x, fpt);
+ gp_apply_parent_point(gpl, pt);
+ }
+
+ }
+ }
+
}
}
}
- CTX_DATA_END;
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -1169,41 +1274,68 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
-
+
const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d);
-
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0)
- continue;
-
- if (use_offset) {
- float offset[3];
-
- /* compute offset from first point of stroke to cursor */
- /* TODO: Allow using midpoint instead? */
- sub_v3_v3v3(offset, cursor_global, &gps->points->x);
-
- /* apply offset to all points in the stroke */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- add_v3_v3(&pt->x, offset);
+
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float diff_mat[4][4];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
}
- }
- else {
- /* affect each selected point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- copy_v3_v3(&pt->x, cursor_global);
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
+ bGPDspoint *pt;
+ int i;
+
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0)
+ continue;
+
+ if (use_offset) {
+ float offset[3];
+
+ /* compute offset from first point of stroke to cursor */
+ /* TODO: Allow using midpoint instead? */
+ sub_v3_v3v3(offset, cursor_global, &gps->points->x);
+
+ /* apply offset to all points in the stroke */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ add_v3_v3(&pt->x, offset);
+ }
+ }
+ else {
+ /* affect each selected point */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v3_v3(&pt->x, cursor_global);
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
+ }
+ }
}
+
+
}
}
}
- CTX_DATA_END;
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -1243,24 +1375,57 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
INIT_MINMAX(min, max);
/* calculate midpoints from selected points */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0)
- continue;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- add_v3_v3(centroid, &pt->x);
- minmax_v3v3_v3(min, max, &pt->x);
- count++;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float diff_mat[4][4];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
+ bGPDspoint *pt;
+ int i;
+
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0)
+ continue;
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (gpl->parent == NULL) {
+ add_v3_v3(centroid, &pt->x);
+ minmax_v3v3_v3(min, max, &pt->x);
+ }
+ else {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ add_v3_v3(centroid, fpt);
+ minmax_v3v3_v3(min, max, fpt);
+ }
+ count++;
+ }
+ }
+
}
}
}
- CTX_DATA_END;
if (v3d->around == V3D_AROUND_CENTER_MEAN && count) {
mul_v3_fl(centroid, 1.0f / (float)count);
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 53fb33eeb9b..f37fba4212d 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -101,6 +101,23 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct
int *r_x, int *r_y);
/**
+ * Convert point to parent space
+ *
+ * \param pt Original point
+ * \param diff_mat Matrix with the difference between original parent matrix
+ * \param[out] r_pt Pointer to new point after apply matrix
+ */
+void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt);
+/**
+ * Change points position relative to parent object
+ */
+void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps);
+/**
+ * Change point position relative to parent object
+ */
+void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt);
+
+/**
* Convert a screenspace point to a 3D Grease Pencil coordinate.
*
* For use with editing tools where it is easier to perform the operations in 2D,
@@ -116,6 +133,10 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float
int gp_add_poll(struct bContext *C);
int gp_active_layer_poll(struct bContext *C);
+int gp_active_brush_poll(struct bContext *C);
+int gp_active_palette_poll(struct bContext *C);
+int gp_active_palettecolor_poll(struct bContext *C);
+int gp_brush_crt_presets_poll(bContext *C);
/* Copy/Paste Buffer --------------------------------- */
/* gpencil_edit.c */
@@ -137,17 +158,47 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke
bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure);
/**
+* Apply smooth for strength to stroke point
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf);
+
+/**
+* Apply smooth for thickness to stroke point (use pressure)
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf);
+
+/**
* Subdivide a stroke once, by adding points at the midpoint between each pair of points
* \param gps Stroke data
* \param new_totpoints Total number of points (after subdividing)
*/
void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints);
+/**
+* Add randomness to stroke
+* \param gps Stroke data
+* \param brsuh Brush data
+*/
+void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush);
+
/* Layers Enums -------------------------------------- */
struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
+/* Enums of GP Brushes */
+EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free);
+
+/* Enums of GP palettes */
+EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free);
/* ***************************************************** */
/* Operator Defines */
@@ -155,7 +206,7 @@ struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *
void GPENCIL_OT_draw(struct wmOperatorType *ot);
-/* Paint Modes for operator*/
+/* Paint Modes for operator */
typedef enum eGPencil_PaintModes {
GP_PAINTMODE_DRAW = 0,
GP_PAINTMODE_ERASER,
@@ -166,6 +217,7 @@ typedef enum eGPencil_PaintModes {
/* stroke editing ----- */
void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot);
+void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_select(struct wmOperatorType *ot);
void GPENCIL_OT_select_all(struct wmOperatorType *ot);
@@ -216,12 +268,50 @@ void GPENCIL_OT_lock_all(struct wmOperatorType *ot);
void GPENCIL_OT_unlock_all(struct wmOperatorType *ot);
void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_merge(struct wmOperatorType *ot);
void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot);
void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
+enum {
+ GP_STROKE_JOIN = -1,
+ GP_STROKE_JOINCOPY = 1
+};
+
+void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_join(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot);
+
+void GPENCIL_OT_brush_add(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_change(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_move(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_copy(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_select(struct wmOperatorType *ot);
+
+void GPENCIL_OT_palette_add(struct wmOperatorType *ot);
+void GPENCIL_OT_palette_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_palette_change(struct wmOperatorType *ot);
+void GPENCIL_OT_palette_lock_layer(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_add(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_isolate(struct wmOperatorType *ot);
+
+void GPENCIL_OT_palettecolor_hide(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_reveal(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_lock_all(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_unlock_all(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_move(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_select(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_copy(struct wmOperatorType *ot);
+
/* undo stack ---------- */
void gpencil_undo_init(struct bGPdata *gpd);
@@ -273,4 +363,39 @@ typedef enum ACTCONT_TYPES {
ACTCONT_GPENCIL
} ACTCONT_TYPES;
+/**
+* Iterate over all editable strokes in the current context,
+* stopping on each usable layer + stroke pair (i.e. gpl and gps)
+* to perform some operations on the stroke.
+*
+* \param gpl The identifier to use for the layer of the stroke being processed.
+* Choose a suitable value to avoid name clashes.
+* \param gps The identifier to use for current stroke being processed.
+* Choose a suitable value to avoid name clashes.
+*/
+#define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \
+{ \
+ CTX_DATA_BEGIN(C, bGPDlayer*, gpl, editable_gpencil_layers) \
+ { \
+ if (gpl->actframe == NULL) \
+ continue; \
+ /* calculate difference matrix if parent object */ \
+ float diff_mat[4][4]; \
+ ED_gpencil_parent_location(gpl, diff_mat); \
+ /* loop over strokes */ \
+ for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { \
+ /* skip strokes that are invalid for current view */ \
+ if (ED_gpencil_stroke_can_use(C, gps) == false) \
+ continue; \
+ /* check if the color is editable */ \
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) \
+ continue; \
+ /* ... Do Stuff With Strokes ... */
+
+#define GP_EDITABLE_STROKES_END \
+ } \
+ } \
+ CTX_DATA_END; \
+} (void)0
+
#endif /* __GPENCIL_INTERN_H__ */
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 65ee1122b56..50f4e795d70 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -140,8 +140,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
* that the only data being edited is that of the Grease Pencil strokes
*/
- /* FKEY = Eraser Radius */
- kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
+ /* CTRL + FKEY = Eraser Radius */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius");
@@ -169,8 +169,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength");
- /* Ctrl-FKEY = Sculpt Brush Size */
- kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
+ /* FKEY = Sculpt Brush Size */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size");
@@ -240,6 +240,12 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "GPENCIL_OT_active_frames_delete_all", XKEY, KM_PRESS, KM_SHIFT, 0);
+ /* join strokes */
+ WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL, 0);
+
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "type", GP_STROKE_JOINCOPY);
+
/* copy + paste */
WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0);
@@ -266,14 +272,37 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0);
RNA_boolean_set(kmi->ptr, "unselected", true);
+
+ WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0);
/* Isolate Layer */
WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0);
/* Move to Layer */
WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0);
-
-
+
+ /* Select drawing brush using index */
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ONEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 0);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", TWOKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 1);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", THREEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 2);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FOURKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 3);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FIVEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 4);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SIXKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 5);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SEVENKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 6);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", EIGHTKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 7);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", NINEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 8);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ZEROKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 9);
+
/* Transform Tools */
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0);
@@ -318,7 +347,8 @@ void ED_operatortypes_gpencil(void)
/* Editing (Strokes) ------------ */
WM_operatortype_append(GPENCIL_OT_editmode_toggle);
-
+ WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle);
+
WM_operatortype_append(GPENCIL_OT_select);
WM_operatortype_append(GPENCIL_OT_select_all);
WM_operatortype_append(GPENCIL_OT_select_circle);
@@ -362,12 +392,44 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_lock_all);
WM_operatortype_append(GPENCIL_OT_unlock_all);
WM_operatortype_append(GPENCIL_OT_layer_isolate);
-
+ WM_operatortype_append(GPENCIL_OT_layer_merge);
+
WM_operatortype_append(GPENCIL_OT_active_frame_delete);
WM_operatortype_append(GPENCIL_OT_active_frames_delete_all);
WM_operatortype_append(GPENCIL_OT_convert);
+ WM_operatortype_append(GPENCIL_OT_stroke_arrange);
+ WM_operatortype_append(GPENCIL_OT_stroke_change_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_lock_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness);
+ WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set);
+ WM_operatortype_append(GPENCIL_OT_stroke_join);
+ WM_operatortype_append(GPENCIL_OT_stroke_flip);
+
+ WM_operatortype_append(GPENCIL_OT_palette_add);
+ WM_operatortype_append(GPENCIL_OT_palette_remove);
+ WM_operatortype_append(GPENCIL_OT_palette_change);
+ WM_operatortype_append(GPENCIL_OT_palette_lock_layer);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_add);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_remove);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_isolate);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_hide);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_reveal);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_lock_all);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_unlock_all);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_move);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_select);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_copy);
+
+ WM_operatortype_append(GPENCIL_OT_brush_add);
+ WM_operatortype_append(GPENCIL_OT_brush_remove);
+ WM_operatortype_append(GPENCIL_OT_brush_change);
+ WM_operatortype_append(GPENCIL_OT_brush_move);
+ WM_operatortype_append(GPENCIL_OT_brush_presets_create);
+ WM_operatortype_append(GPENCIL_OT_brush_copy);
+ WM_operatortype_append(GPENCIL_OT_brush_select);
+
/* Editing (Time) --------------- */
}
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index a570d586f50..e7e39a85792 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -39,21 +39,26 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLI_rand.h"
#include "BLT_translation.h"
#include "PIL_time.h"
+#include "BKE_main.h"
+#include "BKE_paint.h"
#include "BKE_gpencil.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
+#include "BKE_colortools.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_brush_types.h"
#include "DNA_windowmanager_types.h"
#include "UI_view2d.h"
@@ -151,6 +156,10 @@ typedef struct tGPsdata {
float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */
void *erasercursor; /* radial cursor data for drawing eraser */
+
+ bGPDpalettecolor *palettecolor; /* current palette color */
+ bGPDbrush *brush; /* current drawing brush */
+ short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */
} tGPsdata;
/* ------ */
@@ -333,10 +342,90 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3]
}
}
+/* apply jitter to stroke */
+static void gp_brush_jitter(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2])
+{
+ float pressure = pt->pressure;
+ float tmp_pressure = pt->pressure;
+ if (brush->draw_jitter > 0.0f) {
+ float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure);
+ tmp_pressure = curvef * brush->draw_sensitivity;
+ }
+ const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */
+ const float fac = BLI_frand() * exfactor * tmp_pressure;
+ /* Jitter is applied perpendicular to the mouse movement vector (2D space) */
+ float mvec[2], svec[2];
+ /* mouse movement in ints -> floats */
+ if (gpd->sbuffer_size > 1) {
+ mvec[0] = (float)(mval[0] - (pt - 1)->x);
+ mvec[1] = (float)(mval[1] - (pt - 1)->y);
+ normalize_v2(mvec);
+ }
+ else {
+ mvec[0] = 0.0f;
+ mvec[1] = 0.0f;
+ }
+ /* rotate mvec by 90 degrees... */
+ svec[0] = -mvec[1];
+ svec[1] = mvec[0];
+ /* scale the displacement by the random, and apply */
+ if (BLI_frand() > 0.5f) {
+ mul_v2_fl(svec, -fac);
+ }
+ else {
+ mul_v2_fl(svec, fac);
+ }
+
+ r_mval[0] = mval[0] + svec[0];
+ r_mval[1] = mval[1] + svec[1];
+
+}
+
+/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */
+static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2])
+{
+ float mvec[2];
+ float sen = brush->draw_angle_factor; /* sensitivity */;
+ float fac;
+ float mpressure;
+
+ float angle = brush->draw_angle; /* default angle of brush in radians */;
+ float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */
+
+ /* Apply to first point (only if there are 2 points because before no data to do it ) */
+ if (gpd->sbuffer_size == 1) {
+ mvec[0] = (float)(mval[0] - (pt - 1)->x);
+ mvec[1] = (float)(mval[1] - (pt - 1)->y);
+ normalize_v2(mvec);
+
+ /* uses > 1.0f to get a smooth transition in first point */
+ fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
+ (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac);
+
+ CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f);
+ }
+
+ /* apply from second point */
+ if (gpd->sbuffer_size >= 1) {
+ mvec[0] = (float)(mval[0] - (pt - 1)->x);
+ mvec[1] = (float)(mval[1] - (pt - 1)->y);
+ normalize_v2(mvec);
+
+ fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
+ /* interpolate with previous point for smoother transitions */
+ mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f);
+ pt->pressure = mpressure;
+
+ CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f);
+ }
+
+}
+
/* add current stroke-point to buffer (returns whether point was successfully added) */
static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, double curtime)
{
bGPdata *gpd = p->gpd;
+ bGPDbrush *brush = p->brush;
tGPspoint *pt;
/* check painting mode */
@@ -349,6 +438,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
/* store settings */
copy_v2_v2_int(&pt->x, mval);
pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */
+ pt->strength = 1.0f;
pt->time = (float)(curtime - p->inittime);
/* increment buffer size */
@@ -363,6 +453,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
/* store settings */
copy_v2_v2_int(&pt->x, mval);
pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */
+ pt->strength = 1.0f;
pt->time = (float)(curtime - p->inittime);
/* now the buffer has 2 points (and shouldn't be allowed to get any larger) */
@@ -381,8 +472,65 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size);
/* store settings */
- copy_v2_v2_int(&pt->x, mval);
- pt->pressure = pressure;
+ /* pressure */
+ if (brush->flag & GP_BRUSH_USE_PRESSURE) {
+ float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure);
+ pt->pressure = curvef * brush->draw_sensitivity;
+ }
+ else {
+ pt->pressure = 1.0f;
+ }
+ /* Apply jitter to position */
+ if (brush->draw_jitter > 0.0f) {
+ int r_mval[2];
+ gp_brush_jitter(gpd, brush, pt, mval, r_mval);
+ copy_v2_v2_int(&pt->x, r_mval);
+ }
+ else {
+ copy_v2_v2_int(&pt->x, mval);
+ }
+ /* apply randomness to pressure */
+ if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) {
+ float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure);
+ float tmp_pressure = curvef * brush->draw_sensitivity;
+ if (BLI_frand() > 0.5f) {
+ pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_frand();
+ }
+ else {
+ pt->pressure += tmp_pressure * brush->draw_random_press * BLI_frand();
+ }
+ CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f);
+ }
+
+ /* apply angle of stroke to brush size */
+ if (brush->draw_angle_factor > 0.0f) {
+ gp_brush_angle(gpd, brush, pt, mval);
+ }
+
+ /* color strength */
+ if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+ float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure);
+ float tmp_pressure = curvef * brush->draw_sensitivity;
+
+ pt->strength = tmp_pressure * brush->draw_strength;
+ }
+ else {
+ pt->strength = brush->draw_strength;
+ }
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+
+ /* apply randomness to color strength */
+ if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_STRENGTH)) {
+ if (BLI_frand() > 0.5f) {
+ pt->strength -= pt->strength * brush->draw_random_press * BLI_frand();
+ }
+ else {
+ pt->strength += pt->strength * brush->draw_random_press * BLI_frand();
+ }
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ }
+
+ /* point time */
pt->time = (float)(curtime - p->inittime);
/* increment counters */
@@ -395,12 +543,15 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
return GP_STROKEADD_NORMAL;
}
else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
+
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
/* get pointer to destination point */
pt = (tGPspoint *)(gpd->sbuffer);
/* store settings */
copy_v2_v2_int(&pt->x, mval);
pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */
+ pt->strength = 1.0f;
pt->time = (float)(curtime - p->inittime);
/* if there's stroke for this poly line session add (or replace last) point
@@ -433,10 +584,16 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pts);
+ }
/* copy pressure and time */
pts->pressure = pt->pressure;
+ pts->strength = pt->strength;
pts->time = pt->time;
+ /* force fill recalc */
+ gps->flag |= GP_STROKE_RECALC_CACHES;
}
/* increment counters */
@@ -534,6 +691,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
bGPDstroke *gps;
bGPDspoint *pt;
tGPspoint *ptc;
+ bGPDbrush *brush = p->brush;
+ ToolSettings *ts = p->scene->toolsettings;
int i, totelem;
/* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */
@@ -569,7 +728,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* copy appropriate settings for stroke */
gps->totpoints = totelem;
- gps->thickness = p->gpl->thickness;
+ gps->thickness = brush->thickness;
gps->flag = gpd->sbuffer_sflag;
gps->inittime = p->inittime;
@@ -577,7 +736,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gps->flag |= GP_STROKE_RECALC_CACHES;
/* allocate enough memory for a continuous array for storage points */
- int sublevel = gpl->sublevel;
+ int sublevel = brush->sublevel;
int new_totpoints = gps->totpoints;
for (i = 0; i < sublevel; i++) {
@@ -600,9 +759,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
pt++;
@@ -614,9 +778,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
+
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
}
}
@@ -626,9 +796,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
}
else {
@@ -703,6 +878,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
}
@@ -716,28 +893,49 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gp_subdivide_stroke(gps, totpoints);
}
}
-
+ /* apply randomness to stroke */
+ if (brush->draw_random_sub > 0.0f) {
+ gp_randomize_stroke(gps, brush);
+ }
+
/* smooth stroke after subdiv - only if there's something to do
* for each iteration, the factor is reduced to get a better smoothing without changing too much
* the original stroke
*/
- if (gpl->draw_smoothfac > 0.0f) {
+ if (brush->draw_smoothfac > 0.0f) {
float reduce = 0.0f;
- for (int r = 0; r < gpl->draw_smoothlvl; ++r) {
+ for (int r = 0; r < brush->draw_smoothlvl; ++r) {
for (i = 0; i < gps->totpoints; i++) {
/* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */
- gp_smooth_stroke(gps, i, gpl->draw_smoothfac - reduce, false);
+ gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false);
}
reduce += 0.25f; // reduce the factor
}
}
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent(gpl, gps);
+ }
+
if (depth_arr)
MEM_freeN(depth_arr);
}
+ /* Save palette color */
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+ gps->palcolor = palcolor;
+ strcpy(gps->colorname, palcolor->info);
- /* add stroke to frame */
- BLI_addtail(&p->gpf->strokes, gps);
+ /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head
+ * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist
+ * when drawing the background
+ */
+ if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode != GP_PAINTMODE_DRAW_POLY)) {
+ BLI_addhead(&p->gpf->strokes, gps);
+ }
+ else {
+ BLI_addtail(&p->gpf->strokes, gps);
+ }
gp_stroke_added_enable(p);
}
@@ -761,12 +959,21 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons
(p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH))
{
RegionView3D *rv3d = p->ar->regiondata;
+ bGPDlayer *gpl = p->gpl;
+
const int mval[2] = {x, y};
float mval_3d[3];
-
+ float fpt[3];
+
+ float diff_mat[4][4];
+ /* calculate difference matrix if parent object */
+ ED_gpencil_parent_location(gpl, diff_mat);
+
if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) {
const float depth_mval = view3d_point_depth(rv3d, mval_3d);
- const float depth_pt = view3d_point_depth(rv3d, &pt->x);
+
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ const float depth_pt = view3d_point_depth(rv3d, fpt);
if (depth_pt > depth_mval) {
return true;
@@ -804,7 +1011,13 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
int pc1[2] = {0};
int pc2[2] = {0};
int i;
-
+ float diff_mat[4][4];
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
if (gps->totpoints == 0) {
/* just free stroke */
if (gps->points)
@@ -816,8 +1029,14 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
else if (gps->totpoints == 1) {
/* only process if it hasn't been masked out... */
if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
- gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ }
+ else {
+ bGPDspoint pt_temp;
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ }
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
/* only check if point is inside */
@@ -826,7 +1045,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
// XXX: pressure sensitive eraser should apply here too?
MEM_freeN(gps->points);
if (gps->triangles)
- MEM_freeN(gps->triangles);
+ MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps);
}
}
@@ -836,7 +1055,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* Pressure threshold at which stroke should be culled: Calculated as pressure value
* below which we would have invisible strokes
*/
- const float cull_thresh = (gpl->thickness) ? 1.0f / ((float)gpl->thickness) : 1.0f;
+ const float cull_thresh = (gps->thickness) ? 1.0f / ((float)gps->thickness) : 1.0f;
/* Amount to decrease the pressure of each point with each stroke */
// TODO: Fetch from toolsettings, or compute based on thickness instead?
@@ -865,15 +1084,24 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* get points to work with */
pt1 = gps->points + i;
pt2 = gps->points + i + 1;
-
+
/* only process if it hasn't been masked out... */
if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT))
continue;
- /* get coordinates of point in screenspace */
- gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
- gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
+ gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
+ }
+ else {
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
+ }
+
/* Check that point segment of the boundbox of the eraser stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
@@ -955,7 +1183,10 @@ static void gp_stroke_doeraser(tGPsdata *p)
/* loop over strokes, checking segments for intersections */
for (gps = gpf->strokes.first; gps; gps = gpn) {
gpn = gps->next;
-
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
/* Not all strokes in the datablock may be valid in the current editor/context
* (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
*/
@@ -994,6 +1225,78 @@ static void gp_session_validatebuffer(tGPsdata *p)
p->inittime = 0.0;
}
+/* create a new palette color */
+static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette)
+{
+ bGPDpalettecolor *palcolor;
+
+ palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+
+ return palcolor;
+}
+
+/* initialize a drawing brush */
+static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p)
+{
+ bGPDbrush *brush;
+
+ /* if not exist, create a new one */
+ if (BLI_listbase_is_empty(&ts->gp_brushes)) {
+ /* create new brushes */
+ BKE_gpencil_brush_init_presets(ts);
+ brush = BKE_gpencil_brush_getactive(ts);
+ }
+ else {
+ /* Use the current */
+ brush = BKE_gpencil_brush_getactive(ts);
+ }
+ /* be sure curves are initializated */
+ curvemapping_initialize(brush->cur_sensitivity);
+ curvemapping_initialize(brush->cur_strength);
+ curvemapping_initialize(brush->cur_jitter);
+
+ /* asign to temp tGPsdata */
+ p->brush = brush;
+}
+
+
+/* initialize a paint palette brush and a default color if not exist */
+static void gp_init_palette(tGPsdata *p)
+{
+ bGPdata *gpd;
+ bGPDpalette *palette;
+ bGPDpalettecolor *palcolor;
+
+ gpd = p->gpd;
+
+ /* if not exist, create a new palette */
+ if (BLI_listbase_is_empty(&gpd->palettes)) {
+ /* create new palette */
+ palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
+ /* now create a default color */
+ palcolor = gp_create_new_color(palette);
+ }
+ else {
+ /* Use the current palette and color */
+ palette = BKE_gpencil_palette_getactive(gpd);
+ /* the palette needs one color */
+ if (BLI_listbase_is_empty(&palette->colors)) {
+ palcolor = gp_create_new_color(palette);
+ }
+ else {
+ palcolor = BKE_gpencil_palettecolor_getactive(palette);
+ }
+ /* in some situations can be null, so use first */
+ if (palcolor == NULL) {
+ BKE_gpencil_palettecolor_setactive(palette, palette->colors.first);
+ palcolor = palette->colors.first;
+ }
+ }
+
+ /* asign to temp tGPsdata */
+ p->palettecolor = palcolor;
+}
+
/* (re)init new painting data */
static bool gp_session_initdata(bContext *C, tGPsdata *p)
{
@@ -1145,7 +1448,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
else {
/* if no existing GPencil block exists, add one */
if (*gpd_ptr == NULL)
- *gpd_ptr = gpencil_data_addnew("GPencil");
+ *gpd_ptr = BKE_gpencil_data_addnew("GPencil");
p->gpd = *gpd_ptr;
}
@@ -1158,7 +1461,16 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
/* clear out buffer (stored in gp-data), in case something contaminated it */
gp_session_validatebuffer(p);
-
+ /* set brush and create a new one if null */
+ gp_init_drawing_brush(ts, p);
+ /* set palette info and create a new one if null */
+ gp_init_palette(p);
+ /* set palette colors */
+ bGPDpalettecolor *palcolor = p->palettecolor;
+ bGPdata *pdata = p->gpd;
+ copy_v4_v4(pdata->scolor, palcolor->color);
+ pdata->sflag = palcolor->flag;
+
return 1;
}
@@ -1177,7 +1489,7 @@ static tGPsdata *gp_session_initpaint(bContext *C)
* erase size won't get lost
*/
p->radius = U.gp_eraser;
-
+
/* return context data for running paint operator */
return p;
}
@@ -1211,9 +1523,9 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode)
ToolSettings *ts = scene->toolsettings;
/* get active layer (or add a new one if non-existent) */
- p->gpl = gpencil_layer_getactive(p->gpd);
+ p->gpl = BKE_gpencil_layer_getactive(p->gpd);
if (p->gpl == NULL) {
- p->gpl = gpencil_layer_addnew(p->gpd, "GP_Layer", true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true);
if (p->custom_color[3])
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -1241,7 +1553,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode)
/* Add a new frame if needed (and based off the active frame,
* as we need some existing strokes to erase)
*/
- gpl->actframe = gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY);
+ gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY);
/* XXX: we omit GP_FRAME_PAINT here for now,
* as it is only really useful for doing
@@ -1277,7 +1589,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode)
else
add_frame_mode = GP_GETFRAME_ADD_NEW;
- p->gpf = gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode);
+ p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode);
if (p->gpf == NULL) {
p->status = GP_STATUS_ERROR;
@@ -1495,7 +1807,6 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event)
/* ------------------------------- */
-
static void gpencil_draw_exit(bContext *C, wmOperator *op)
{
tGPsdata *p = op->customdata;
@@ -1513,7 +1824,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op)
/* turn off radial brush cursor */
gpencil_draw_toggle_eraser_cursor(C, p, false);
}
-
+
/* always store the new eraser size to be used again next time
* NOTE: Do this even when not in eraser mode, as eraser may
* have been toggled at some point.
@@ -1600,7 +1911,7 @@ static void gpencil_draw_status_indicators(tGPsdata *p)
break;
case GP_PAINTMODE_DRAW:
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | "
- "ESC/Enter to end (or click outside this area)"));
+ "E/ESC/Enter to end (or click outside this area)"));
break;
case GP_PAINTMODE_DRAW_POLY:
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | "
@@ -1691,6 +2002,31 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
*/
p->mval[0] = event->mval[0] + 1;
p->mval[1] = event->mval[1] + 1;
+
+ /* verify key status for straight lines */
+ if ((event->ctrl > 0) || (event->alt > 0)) {
+ if (p->straight[0] == 0) {
+ int dx = abs(p->mval[0] - p->mvalo[0]);
+ int dy = abs(p->mval[1] - p->mvalo[1]);
+ if ((dx > 0) || (dy > 0)) {
+ /* check mouse direction to replace the other coordinate with previous values */
+ if (dx >= dy) {
+ /* horizontal */
+ p->straight[0] = 1;
+ p->straight[1] = p->mval[1]; /* save y */
+ }
+ else {
+ /* vertical */
+ p->straight[0] = 2;
+ p->straight[1] = p->mval[0]; /* save x */
+ }
+ }
+ }
+ }
+ else {
+ p->straight[0] = 0;
+ }
+
p->curtime = PIL_check_seconds_timer();
/* handle pressure sensitivity (which is supplied by tablets) */
@@ -1725,7 +2061,9 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
p->mvalo[1] = p->mval[1];
p->opressure = p->pressure;
p->inittime = p->ocurtime = p->curtime;
-
+ p->straight[0] = 0;
+ p->straight[1] = 0;
+
/* special exception here for too high pressure values on first touch in
* windows for some tablets, then we just skip first touch...
*/
@@ -1733,6 +2071,18 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
return;
}
+ /* check if alt key is pressed and limit to straight lines */
+ if (p->straight[0] != 0) {
+ if (p->straight[0] == 1) {
+ /* horizontal */
+ p->mval[1] = p->straight[1]; /* replace y */
+ }
+ else {
+ /* vertical */
+ p->mval[0] = p->straight[1]; /* replace x */
+ }
+ }
+
/* fill in stroke data (not actually used directly by gpencil_draw_apply) */
RNA_collection_add(op->ptr, "stroke", &itemptr);
@@ -1855,21 +2205,21 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
if (p->paintmode == GP_PAINTMODE_ERASER) {
gpencil_draw_toggle_eraser_cursor(C, p, true);
}
-
/* set cursor
* NOTE: This may change later (i.e. intentionally via brush toggle,
* or unintentionally if the user scrolls outside the area)...
*/
gpencil_draw_cursor_set(p);
-
+
/* only start drawing immediately if we're allowed to do so... */
if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
/* hotkey invoked - start drawing */
/* printf("\tGP - set first spot\n"); */
p->status = GP_STATUS_PAINTING;
-
+
/* handle the initial drawing - i.e. for just doing a simple dot */
gpencil_draw_apply_event(op, event);
+ op->flag |= OP_IS_MODAL_CURSOR_REGION;
}
else {
/* toolbar invoked - don't start drawing yet... */
@@ -1937,6 +2287,28 @@ static void gpencil_stroke_end(wmOperator *op)
p->gpf = NULL;
}
+/* Move last stroke in the listbase to the head to be drawn below all previous strokes in the layer */
+static void gpencil_move_last_stroke_to_back(bContext *C)
+{
+ /* move last stroke (the polygon) to head of the listbase stroke to draw on back of all previous strokes */
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
+ return;
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps = gpf->strokes.last;
+ if (ELEM(NULL, gps)) {
+ return;
+ }
+
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps);
+}
+
/* events handling during interactive drawing part of operator */
static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
@@ -1976,6 +2348,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* is essential for ensuring that they can quickly return to that view
*/
}
+ else if ((ELEM(event->type, DKEY)) && (event->val == KM_RELEASE)) {
+ /* enable continuous if release D key in mid drawing */
+ p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON;
+ }
else {
estate = OPERATOR_RUNNING_MODAL;
}
@@ -1986,9 +2362,15 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* exit painting mode (and/or end current stroke)
* NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647]
*/
- if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) {
+ if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) {
/* exit() ends the current stroke before cleaning up */
/* printf("\t\tGP - end of paint op + end of stroke\n"); */
+ /* if drawing polygon and enable on back, must move stroke */
+ if ((p->scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) {
+ if (p->flags & GP_PAINTFLAG_STROKEADDED) {
+ gpencil_move_last_stroke_to_back(C);
+ }
+ }
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
@@ -2045,6 +2427,12 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
/* printf("\t\tGP - end of stroke + op\n"); */
+ /* if drawing polygon and enable on back, must move stroke */
+ if ((p->scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) {
+ if (p->flags & GP_PAINTFLAG_STROKEADDED) {
+ gpencil_move_last_stroke_to_back(C);
+ }
+ }
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
@@ -2125,6 +2513,12 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* NOTE: Don't eter this case if an error occurred while finding the
* region (as above)
*/
+ /* if drawing polygon and enable on back, must move stroke */
+ if ((p->scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) {
+ if (p->flags & GP_PAINTFLAG_STROKEADDED) {
+ gpencil_move_last_stroke_to_back(C);
+ }
+ }
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index b6482786b4f..4cb966c6378 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -43,6 +43,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
@@ -265,7 +266,7 @@ static void gp_select_same_layer(bContext *C)
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
{
- bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
bGPDstroke *gps;
bool found = false;
@@ -616,9 +617,10 @@ void GPENCIL_OT_select_less(wmOperatorType *ot)
/* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke()
* It would be great to de-duplicate the logic here sometime, but that can wait...
*/
-static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
- const int mx, const int my, const int radius,
- const bool select, rcti *rect)
+static bool gp_stroke_do_circle_sel(
+ bGPDstroke *gps, GP_SpaceConversion *gsc,
+ const int mx, const int my, const int radius,
+ const bool select, rcti *rect, const bool parented, float diff_mat[4][4])
{
bGPDspoint *pt1, *pt2;
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
@@ -626,7 +628,14 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
bool changed = false;
if (gps->totpoints == 1) {
- gp_point_to_xy(gsc, gps, gps->points, &x0, &y0);
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, gps->points, &x0, &y0);
+ }
+ else {
+ bGPDspoint pt_temp;
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0);
+ }
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
@@ -654,9 +663,18 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
/* get points to work with */
pt1 = gps->points + i;
pt2 = gps->points + i + 1;
-
- gp_point_to_xy(gsc, gps, pt1, &x0, &y0);
- gp_point_to_xy(gsc, gps, pt2, &x1, &y1);
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, pt1, &x0, &y0);
+ gp_point_to_xy(gsc, gps, pt2, &x1, &y1);
+ }
+ else {
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &x0, &y0);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &x1, &y1);
+ }
/* check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) ||
@@ -691,7 +709,7 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
}
/* Ensure that stroke selection is in sync with its points */
- gpencil_stroke_sync_selection(gps);
+ BKE_gpencil_stroke_sync_selection(gps);
}
return changed;
@@ -733,12 +751,14 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
/* find visible strokes, and select if hit */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
- changed |= gp_stroke_do_circle_sel(gps, &gsc, mx, my, radius, select, &rect);
+ changed |= gp_stroke_do_circle_sel(
+ gps, &gsc, mx, my, radius, select, &rect,
+ (gpl->parent != NULL), diff_mat);
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* updates */
if (changed) {
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
@@ -818,17 +838,25 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op)
WM_operator_properties_border_to_rcti(op, &rect);
/* select/deselect points */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
+
bGPDspoint *pt;
int i;
-
+
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int x0, y0;
-
+
/* convert point coords to screenspace */
- gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
+ }
+ else {
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0);
+ }
+
/* test if in selection rect */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) {
if (select) {
@@ -837,16 +865,16 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op)
else {
pt->flag &= ~GP_SPOINT_SELECT;
}
-
+
changed = true;
}
}
-
+
/* Ensure that stroke selection is in sync with its points */
- gpencil_stroke_sync_selection(gps);
+ BKE_gpencil_stroke_sync_selection(gps);
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* updates */
if (changed) {
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
@@ -920,20 +948,26 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
}
/* select/deselect points */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
bGPDspoint *pt;
int i;
-
+
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int x0, y0;
-
+
/* convert point coords to screenspace */
- gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
+ }
+ else {
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0);
+ }
/* test if in lasso boundbox + within the lasso noose */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) &&
- BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX))
+ BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX))
{
if (select) {
pt->flag |= GP_SPOINT_SELECT;
@@ -941,16 +975,16 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
else {
pt->flag &= ~GP_SPOINT_SELECT;
}
-
+
changed = true;
}
}
-
+
/* Ensure that stroke selection is in sync with its points */
- gpencil_stroke_sync_selection(gps);
+ BKE_gpencil_stroke_sync_selection(gps);
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* cleanup */
MEM_freeN((void *)mcords);
@@ -1020,35 +1054,42 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* First Pass: Find stroke point which gets hit */
/* XXX: maybe we should go from the top of the stack down instead... */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
bGPDspoint *pt;
int i;
-
+
/* firstly, check for hit-point */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int xy[2];
-
- gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]);
-
+
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]);
+ }
+ else {
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]);
+ }
+
/* do boundbox check first */
if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
const int pt_distance = len_manhattan_v2v2_int(mval, xy);
-
+
/* check if point is inside */
if (pt_distance <= radius_squared) {
/* only use this point if it is a better match than the current hit - T44685 */
if (pt_distance < hit_distance) {
hit_stroke = gps;
- hit_point = pt;
+ hit_point = pt;
hit_distance = pt_distance;
}
}
}
}
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* Abort if nothing hit... */
if (ELEM(NULL, hit_stroke, hit_point)) {
return OPERATOR_CANCELLED;
@@ -1111,7 +1152,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
hit_point->flag &= ~GP_SPOINT_SELECT;
/* ensure that stroke is selected correctly */
- gpencil_stroke_sync_selection(hit_stroke);
+ BKE_gpencil_stroke_sync_selection(hit_stroke);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index f9b479ca03d..793ed2a07d0 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -100,14 +100,14 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name)
bGPdata *gpd = *gpd_ptr;
bGPDlayer *gpl, *gpld;
- free_gpencil_layers(&gpd->layers);
+ BKE_gpencil_free_layers(&gpd->layers);
/* copy layers */
BLI_listbase_clear(&gpd->layers);
for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) {
/* make a copy of source layer and its data */
- gpld = gpencil_layer_duplicate(gpl);
+ gpld = BKE_gpencil_layer_duplicate(gpl);
BLI_addtail(&gpd->layers, gpld);
}
}
@@ -142,7 +142,7 @@ void gpencil_undo_push(bGPdata *gpd)
*/
undo_node->gpd->adt = NULL;
- BKE_gpencil_free(undo_node->gpd);
+ BKE_gpencil_free(undo_node->gpd, false);
MEM_freeN(undo_node->gpd);
BLI_freelinkN(&undo_nodes, undo_node);
@@ -153,7 +153,7 @@ void gpencil_undo_push(bGPdata *gpd)
/* create new undo node */
undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node");
- undo_node->gpd = gpencil_data_duplicate(G.main, gpd, true);
+ undo_node->gpd = BKE_gpencil_data_duplicate(G.main, gpd, true);
cur_node = undo_node;
@@ -170,7 +170,7 @@ void gpencil_undo_finish(void)
*/
undo_node->gpd->adt = NULL;
- BKE_gpencil_free(undo_node->gpd);
+ BKE_gpencil_free(undo_node->gpd, false);
MEM_freeN(undo_node->gpd);
undo_node = undo_node->next;
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index d62625baaa4..ed9a591dcbe 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -17,7 +17,7 @@
*
* The Original Code is Copyright (C) 2014, Blender Foundation
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -32,9 +32,13 @@
#include <stddef.h>
#include <math.h>
+#include "MEM_guardedalloc.h"
+
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+#include "BLI_rand.h"
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
@@ -46,6 +50,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_tracking.h"
+#include "BKE_action.h"
#include "WM_api.h"
@@ -220,7 +225,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra)
/* just check both for now... */
// XXX: this could get confusing (e.g. if only on the object, but other places don't show this)
if (scene->gpd) {
- bGPDlayer *gpl = gpencil_layer_getactive(scene->gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(scene->gpd);
if (gpl) {
if (gpl->actframe) {
// XXX: assumes that frame has been fetched already
@@ -234,7 +239,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra)
}
if (ob && ob->gpd) {
- bGPDlayer *gpl = gpencil_layer_getactive(ob->gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->gpd);
if (gpl) {
if (gpl->actframe) {
// XXX: assumes that frame has been fetched already
@@ -264,11 +269,39 @@ int gp_add_poll(bContext *C)
int gp_active_layer_poll(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
return (gpl != NULL);
}
+/* poll callback for checking if there is an active brush */
+int gp_active_brush_poll(bContext *C)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+
+ return (brush != NULL);
+}
+
+/* poll callback for checking if there is an active palette */
+int gp_active_palette_poll(bContext *C)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+
+ return (palette != NULL);
+}
+
+/* poll callback for checking if there is an active palette color */
+int gp_active_palettecolor_poll(bContext *C)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+ return (palcolor != NULL);
+}
+
/* ******************************************************** */
/* Dynamic Enums of GP Layers */
/* NOTE: These include an option to create a new layer and use that... */
@@ -412,6 +445,60 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
return ED_gpencil_stroke_can_use_direct(sa, gps);
}
+/* Check whether given stroke can be edited for the current color */
+bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps)
+{
+ /* check if the color is editable */
+ bGPDpalettecolor *palcolor = gps->palcolor;
+ if (palcolor != NULL) {
+ if (palcolor->flag & PC_COLOR_HIDE)
+ return false;
+ if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED))
+ return false;
+ }
+
+ return true;
+}
+
+/* Get palette color or create a new one */
+bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps)
+{
+ bGPDpalette *palette;
+ bGPDpalettecolor *palcolor;
+
+ if ((gps->palcolor != NULL) && ((gps->flag & GP_STROKE_RECALC_COLOR) == 0))
+ return gps->palcolor;
+
+ /* get palette */
+ palette = BKE_gpencil_palette_getactive(gpd);
+ if (palette == NULL) {
+ palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
+ }
+ /* get color */
+ palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname);
+ if (palcolor == NULL) {
+ if (gps->palcolor == NULL) {
+ palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+ /* set to a different color */
+ ARRAY_SET_ITEMS(palcolor->color, 1.0f, 0.0f, 1.0f, 0.9f);
+ }
+ else {
+ palcolor = BKE_gpencil_palettecolor_addnew(palette, gps->colorname, true);
+ /* set old color and attributes */
+ bGPDpalettecolor *gpscolor = gps->palcolor;
+ copy_v4_v4(palcolor->color, gpscolor->color);
+ copy_v4_v4(palcolor->fill, gpscolor->fill);
+ palcolor->flag = gpscolor->flag;
+ }
+ }
+
+ /* clear flag and set pointer */
+ gps->flag &= ~GP_STROKE_RECALC_COLOR;
+ gps->palcolor = palcolor;
+
+ return palcolor;
+}
+
/* ******************************************************** */
/* Space Conversion */
@@ -451,6 +538,50 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
}
}
+/* convert point to parent space */
+void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt)
+{
+ float fpt[3];
+
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ copy_v3_v3(&r_pt->x, fpt);
+}
+
+/* Change position relative to parent object */
+void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps)
+{
+ bGPDspoint *pt;
+ int i;
+
+ /* undo matrix */
+ float diff_mat[4][4];
+ float inverse_diff_mat[4][4];
+ float fpt[3];
+
+ ED_gpencil_parent_location(gpl, diff_mat);
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+
+ for (i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
+ copy_v3_v3(&pt->x, fpt);
+ }
+}
+
+/* Change point position relative to parent object */
+void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt)
+{
+ /* undo matrix */
+ float diff_mat[4][4];
+ float inverse_diff_mat[4][4];
+ float fpt[3];
+
+ ED_gpencil_parent_location(gpl, diff_mat);
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+
+ mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
+ copy_v3_v3(&pt->x, fpt);
+}
/* Convert Grease Pencil points to screen-space values
* WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn
@@ -591,25 +722,107 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure)
madd_v3_v3fl(sco, &pt1->x, average_fac);
madd_v3_v3fl(sco, &pt2->x, average_fac);
+#if 0
+ /* XXX: Disabled because get weird result */
/* do pressure too? */
if (affect_pressure) {
pressure += pt1->pressure * average_fac;
pressure += pt2->pressure * average_fac;
}
+#endif
}
}
/* Based on influence factor, blend between original and optimal smoothed coordinate */
interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
+#if 0
+ /* XXX: Disabled because get weird result */
if (affect_pressure) {
pt->pressure = pressure;
}
+#endif
return true;
}
/**
+* Apply smooth for strength to stroke point
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf)
+{
+ bGPDspoint *ptb = &gps->points[i];
+
+ /* Do nothing if not enough points */
+ if (gps->totpoints <= 2) {
+ return false;
+ }
+
+ /* Compute theoretical optimal value using distances */
+ bGPDspoint *pta, *ptc;
+ int before = i - 1;
+ int after = i + 1;
+
+ CLAMP_MIN(before, 0);
+ CLAMP_MAX(after, gps->totpoints - 1);
+
+ pta = &gps->points[before];
+ ptc = &gps->points[after];
+
+ /* the optimal value is the corresponding to the interpolation of the strength
+ * at the distance of point b
+ */
+ const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
+ const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength;
+
+ /* Based on influence factor, blend between original and optimal */
+ ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal;
+
+ return true;
+}
+
+/**
+* Apply smooth for thickness to stroke point (use pressure)
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf)
+{
+ bGPDspoint *ptb = &gps->points[i];
+
+ /* Do nothing if not enough points */
+ if (gps->totpoints <= 2) {
+ return false;
+ }
+
+ /* Compute theoretical optimal value using distances */
+ bGPDspoint *pta, *ptc;
+ int before = i - 1;
+ int after = i + 1;
+
+ CLAMP_MIN(before, 0);
+ CLAMP_MAX(after, gps->totpoints - 1);
+
+ pta = &gps->points[before];
+ ptc = &gps->points[after];
+
+ /* the optimal value is the corresponding to the interpolation of the pressure
+ * at the distance of point b
+ */
+ float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
+ float optimal = (1.0f - fac) * pta->pressure + fac * ptc->pressure;
+
+ /* Based on influence factor, blend between original and optimal */
+ ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal;
+
+ return true;
+}
+
+/**
* Subdivide a stroke once, by adding a point half way between each pair of existing points
* \param gps Stroke data
* \param new_totpoints Total number of points (after subdividing)
@@ -633,16 +846,99 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints)
interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f);
pt->pressure = interpf(prev->pressure, next->pressure, 0.5f);
- pt->time = interpf(prev->time, next->time, 0.5f);
+ pt->strength = interpf(prev->strength, next->strength, 0.5f);
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ pt->time = interpf(prev->time, next->time, 0.5f);
}
/* Update to new total number of points */
gps->totpoints = new_totpoints;
}
-/* ******************************************************** */
+/**
+ * Add randomness to stroke
+ * \param gps Stroke data
+ * \param brsuh Brush data
+ */
+void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush)
+{
+ bGPDspoint *pt1, *pt2, *pt3;
+ float v1[3];
+ float v2[3];
+ if (gps->totpoints < 3) {
+ return;
+ }
+
+ /* get two vectors using 3 points */
+ pt1 = &gps->points[0];
+ pt2 = &gps->points[1];
+ pt3 = &gps->points[(int)(gps->totpoints * 0.75)];
+
+ sub_v3_v3v3(v1, &pt2->x, &pt1->x);
+ sub_v3_v3v3(v2, &pt3->x, &pt2->x);
+ normalize_v3(v1);
+ normalize_v3(v2);
+
+ /* get normal vector to plane created by two vectors */
+ float normal[3];
+ cross_v3_v3v3(normal, v1, v2);
+ normalize_v3(normal);
+ /* get orthogonal vector to plane to rotate random effect */
+ float ortho[3];
+ cross_v3_v3v3(ortho, v1, normal);
+ normalize_v3(ortho);
+ /* Read all points and apply shift vector (first and last point not modified) */
+ for (int i = 1; i < gps->totpoints - 1; ++i) {
+ bGPDspoint *pt = &gps->points[i];
+ /* get vector with shift (apply a division because random is too sensitive */
+ const float fac = BLI_frand() * (brush->draw_random_sub / 10.0f);
+ float svec[3];
+ copy_v3_v3(svec, ortho);
+ if (BLI_frand() > 0.5f) {
+ mul_v3_fl(svec, -fac);
+ }
+ else {
+ mul_v3_fl(svec, fac);
+ }
+
+ /* apply shift */
+ add_v3_v3(&pt->x, svec);
+ }
+
+}
+/* calculate difference matrix */
+void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4])
+{
+ Object *ob = gpl->parent;
+ if (ob == NULL) {
+ unit_m4(diff_mat);
+ return;
+ }
+ else {
+ if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) {
+ mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse);
+ return;
+ }
+ else if (gpl->partype == PARBONE) {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr);
+ if (pchan) {
+ float tmp_mat[4][4];
+ mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat);
+ mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse);
+ }
+ else {
+ mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */
+ }
+ return;
+ }
+ else {
+ unit_m4(diff_mat); /* not defined type */
+ }
+ }
+}
+/* ******************************************************** */
bool ED_gpencil_stroke_minmax(
const bGPDstroke *gps, const bool use_select,
float r_min[3], float r_max[3])
@@ -659,3 +955,74 @@ bool ED_gpencil_stroke_minmax(
}
return changed;
}
+/* Dynamic Enums of GP Brushes */
+
+EnumPropertyItem *ED_gpencil_brushes_enum_itemf(
+ bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush;
+ EnumPropertyItem *item = NULL, item_tmp = { 0 };
+ int totitem = 0;
+ int i = 0;
+
+ if (ELEM(NULL, C, ts)) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ /* Existing brushes */
+ for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) {
+ item_tmp.identifier = brush->info;
+ item_tmp.name = brush->info;
+ item_tmp.value = i;
+
+ if (brush->flag & GP_BRUSH_ACTIVE)
+ item_tmp.icon = ICON_BRUSH_DATA;
+ else
+ item_tmp.icon = ICON_NONE;
+
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+/* Dynamic Enums of GP Palettes */
+
+EnumPropertyItem *ED_gpencil_palettes_enum_itemf(
+ bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ bGPDpalette *palette;
+ EnumPropertyItem *item = NULL, item_tmp = { 0 };
+ int totitem = 0;
+ int i = 0;
+
+ if (ELEM(NULL, C, gpd)) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ /* Existing palettes */
+ for (palette = gpd->palettes.first; palette; palette = palette->next, i++) {
+ item_tmp.identifier = palette->info;
+ item_tmp.name = palette->info;
+ item_tmp.value = i;
+
+ if (palette->flag & PL_PALETTE_ACTIVE)
+ item_tmp.icon = ICON_COLOR;
+ else
+ item_tmp.icon = ICON_NONE;
+
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+/* ******************************************************** */
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 0940f594482..bfd89e90fce 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -156,6 +156,7 @@ typedef enum eAnim_ChannelType {
ANIMTYPE_DSMAT,
ANIMTYPE_DSLAM,
ANIMTYPE_DSCAM,
+ ANIMTYPE_DSCACHEFILE,
ANIMTYPE_DSCUR,
ANIMTYPE_DSSKEY,
ANIMTYPE_DSWOR,
@@ -275,6 +276,7 @@ typedef enum eAnimFilter_Flags {
#define FILTER_MAT_OBJD(ma) (CHECK_TYPE_INLINE(ma, Material *), ((ma->flag & MA_DS_EXPAND)))
#define FILTER_LAM_OBJD(la) (CHECK_TYPE_INLINE(la, Lamp *), ((la->flag & LA_DS_EXPAND)))
#define FILTER_CAM_OBJD(ca) (CHECK_TYPE_INLINE(ca, Camera *), ((ca->flag & CAM_DS_EXPAND)))
+#define FILTER_CACHEFILE_OBJD(cf) (CHECK_TYPE_INLINE(cf, CacheFile *), ((cf->flag & CACHEFILE_DS_EXPAND)))
#define FILTER_CUR_OBJD(cu) (CHECK_TYPE_INLINE(cu, Curve *), ((cu->flag & CU_DS_EXPAND)))
#define FILTER_PART_OBJD(part) (CHECK_TYPE_INLINE(part, ParticleSettings *), ((part->flag & PART_DS_EXPAND)))
#define FILTER_MBALL_OBJD(mb) (CHECK_TYPE_INLINE(mb, MetaBall *), ((mb->flag2 & MB_DS_EXPAND)))
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index de5ab80a88f..d526b0841cc 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -41,6 +41,8 @@ struct bGPdata;
struct bGPDlayer;
struct bGPDframe;
struct bGPDstroke;
+struct bGPDpalette;
+struct bGPDpalettecolor;
struct bAnimContext;
struct KeyframeEditData;
struct PointerRNA;
@@ -57,6 +59,7 @@ struct wmKeyConfig;
typedef struct tGPspoint {
int x, y; /* x and y coordinates of cursor (in relative to area) */
float pressure; /* pressure of tablet at this point */
+ float strength; /* pressure of tablet at this point for alpha factor */
float time; /* Time relative to stroke start (used when converting to path) */
} tGPspoint;
@@ -86,6 +89,9 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr
bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps);
bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps);
+bool ED_gpencil_stroke_color_use(const struct bGPDlayer *gpl, const struct bGPDstroke *gps);
+
+struct bGPDpalettecolor *ED_gpencil_stroke_getcolor(struct bGPdata *gpd, struct bGPDstroke *gps);
bool ED_gpencil_stroke_minmax(
const struct bGPDstroke *gps, const bool use_select,
@@ -142,4 +148,10 @@ bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mod
int ED_gpencil_session_active(void);
int ED_undo_gpencil_step(struct bContext *C, int step, const char *name);
+/* ------------ Transformation Utilities ------------ */
+
+/* get difference matrix using parent */
+void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]);
+
+
#endif /* __ED_GPENCIL_H__ */
diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h
index b1f3f012e09..c478a8b17e5 100644
--- a/source/blender/editors/include/ED_keyframes_draw.h
+++ b/source/blender/editors/include/ED_keyframes_draw.h
@@ -34,6 +34,7 @@
struct bAnimContext;
struct AnimData;
+struct CacheFile;
struct FCurve;
struct bDopeSheet;
struct bAction;
@@ -141,6 +142,8 @@ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct D
void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* Object */
void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
+/* Cache File */
+void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* Scene */
void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* DopeSheet Summary */
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index d85b60dcc43..e016e014a1a 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -320,9 +320,9 @@ DEF_ICON(OUTLINER_OB_SPEAKER)
DEF_ICON(BLANK123)
DEF_ICON(BLANK124)
DEF_ICON(BLANK125)
- DEF_ICON(BLANK126)
- DEF_ICON(BLANK127)
#endif
+DEF_ICON(RESTRICT_COLOR_OFF)
+DEF_ICON(RESTRICT_COLOR_ON)
DEF_ICON(RESTRICT_VIEW_OFF)
DEF_ICON(RESTRICT_VIEW_ON)
DEF_ICON(RESTRICT_SELECT_OFF)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index a623f5cfb9c..1f053c806b0 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -382,7 +382,11 @@ typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1);
typedef struct uiPopupMenu uiPopupMenu;
-struct uiPopupMenu *UI_popup_menu_begin(struct bContext *C, const char *title, int icon) ATTR_NONNULL();
+uiPopupMenu *UI_popup_menu_begin(
+ struct bContext *C, const char *title, int icon) ATTR_NONNULL();
+uiPopupMenu *UI_popup_menu_begin_ex(
+ struct bContext *C, const char *title, const char *block_name,
+ int icon) ATTR_NONNULL();
void UI_popup_menu_end(struct bContext *C, struct uiPopupMenu *head);
struct uiLayout *UI_popup_menu_layout(uiPopupMenu *head);
@@ -941,6 +945,7 @@ void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C);
void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr);
void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name);
void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color);
+void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname);
/* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */
#define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 11703208b2a..79831cefde6 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -10156,6 +10156,10 @@ static void ui_popup_handler_remove(bContext *C, void *userdata)
{
uiPopupBlockHandle *menu = userdata;
+ if (menu->cancel_func) {
+ menu->cancel_func(C, menu->popup_arg);
+ }
+
/* free menu block if window is closed for some reason */
ui_popup_block_free(C, menu);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 4107414a240..22a450d2523 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -1541,6 +1541,8 @@ int UI_idcode_icon_get(const int idcode)
return ICON_BRUSH_DATA;
case ID_CA:
return ICON_CAMERA_DATA;
+ case ID_CF:
+ return ICON_FILE;
case ID_CU:
return ICON_CURVE_DATA;
case ID_GD:
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index f02aad1ff87..8336efa381b 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -548,6 +548,8 @@ struct uiPopupBlockHandle {
/* store data for refreshing popups */
struct uiPopupBlockCreate popup_create_vars;
+ /* true if we can re-create the popup using 'popup_create_vars' */
+ bool can_refresh;
struct wmTimer *scrolltimer;
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index 2caf52cf8dc..c621fcf493d 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -1701,7 +1701,9 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar)
ar->do_draw &= ~RGN_DRAW_REFRESH_UI;
for (block = ar->uiblocks.first; block; block = block_next) {
block_next = block->next;
- ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
+ if (block->handle->can_refresh) {
+ ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
+ }
}
}
@@ -1811,6 +1813,8 @@ uiBlock *ui_popup_block_refresh(
bContext *C, uiPopupBlockHandle *handle,
ARegion *butregion, uiBut *but)
{
+ BLI_assert(handle->can_refresh == true);
+
const int margin = UI_POPUP_MARGIN;
wmWindow *window = CTX_wm_window(C);
ARegion *ar = handle->region;
@@ -2001,6 +2005,8 @@ uiPopupBlockHandle *ui_popup_block_create(
handle->popup_create_vars.arg = arg;
handle->popup_create_vars.butregion = but ? butregion : NULL;
copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x);
+ /* caller may free vars used to create this popup, in that case this variable should be disabled. */
+ handle->can_refresh = true;
/* create area region */
ar = ui_region_temp_add(CTX_wm_screen(C));
@@ -2800,6 +2806,7 @@ uiPopupBlockHandle *ui_popup_menu_create(
WM_event_add_mousemove(C);
}
+ handle->can_refresh = false;
MEM_freeN(pup);
return handle;
@@ -2807,14 +2814,17 @@ uiPopupBlockHandle *ui_popup_menu_create(
/******************** Popup Menu API with begin and end ***********************/
-/* only return handler, and set optional title */
-uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
+/**
+ * Only return handler, and set optional title.
+ * \param block_name: Assigned to uiBlock.name (useful info for debugging).
+ */
+uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, const char *title, const char *block_name, int icon)
{
uiStyle *style = UI_style_get_dpi();
uiPopupMenu *pup = MEM_callocN(sizeof(uiPopupMenu), "popup menu");
uiBut *but;
- pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS_PULLDOWN);
+ pup->block = UI_block_begin(C, NULL, block_name, UI_EMBOSS_PULLDOWN);
pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP;
pup->block->puphash = ui_popup_menu_hash(title);
pup->layout = UI_block_layout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style);
@@ -2845,6 +2855,11 @@ uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
return pup;
}
+uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
+{
+ return UI_popup_menu_begin_ex(C, title, __func__, icon);
+}
+
/* set the whole structure to work */
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
{
@@ -2860,7 +2875,8 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
WM_event_add_mousemove(C);
-
+
+ menu->can_refresh = false;
MEM_freeN(pup);
}
@@ -2991,6 +3007,7 @@ void UI_pie_menu_end(bContext *C, uiPieMenu *pie)
menu, WM_HANDLER_ACCEPT_DBL_CLICK);
WM_event_add_mousemove(C);
+ menu->can_refresh = false;
MEM_freeN(pie);
}
@@ -3203,7 +3220,8 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports)
if (pup == NULL) {
char title[UI_MAX_DRAW_STR];
BLI_snprintf(title, sizeof(title), "%s: %s", IFACE_("Report"), report->typestr);
- pup = UI_popup_menu_begin(C, title, ICON_NONE);
+ /* popup_menu stuff does just what we need (but pass meaningful block name) */
+ pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE);
layout = UI_popup_menu_layout(pup);
}
else {
@@ -3332,7 +3350,7 @@ void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block)
UI_popup_handlers_remove(&win->modalhandlers, block->handle);
ui_popup_block_free(C, block->handle);
- /* In the case we have nested popups, closing one may need to redraw anorher, see: T48874 */
+ /* In the case we have nested popups, closing one may need to redraw another, see: T48874 */
for (ARegion *ar = win->screen->regionbase.first; ar; ar = ar->next) {
ED_region_tag_refresh_ui(ar);
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index aec4065adaf..241da5decc6 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -32,6 +32,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_cachefile_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -368,6 +369,7 @@ static const char *template_id_browse_tip(StructRNA *type)
case ID_MSK: return N_("Browse Mask to be linked");
case ID_PAL: return N_("Browse Palette Data to be linked");
case ID_PC: return N_("Browse Paint Curve Data to be linked");
+ case ID_CF: return N_("Browse Cache Files to be linked");
}
}
return N_("Browse ID data to be linked");
@@ -872,8 +874,8 @@ static uiLayout *draw_modifier(
/* mode enabling buttons */
UI_block_align_begin(block);
- /* Softbody not allowed in this situation, enforce! */
- if (((md->type != eModifierType_Softbody && md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) &&
+ /* Collision and Surface are always enabled, hide buttons! */
+ if (((md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) &&
(md->type != eModifierType_Surface) )
{
uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
@@ -1940,6 +1942,7 @@ enum {
UICURVE_FUNC_RESET_VIEW,
UICURVE_FUNC_HANDLE_VECTOR,
UICURVE_FUNC_HANDLE_AUTO,
+ UICURVE_FUNC_HANDLE_AUTO_ANIM,
UICURVE_FUNC_EXTEND_HOZ,
UICURVE_FUNC_EXTEND_EXP,
};
@@ -1960,13 +1963,16 @@ static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event)
cumap->curr = cumap->clipr;
break;
case UICURVE_FUNC_HANDLE_VECTOR: /* set vector */
- curvemap_sethandle(cuma, 1);
+ curvemap_handle_set(cuma, HD_VECT);
curvemapping_changed(cumap, false);
break;
case UICURVE_FUNC_HANDLE_AUTO: /* set auto */
- curvemap_sethandle(cuma, 0);
+ curvemap_handle_set(cuma, HD_AUTO);
curvemapping_changed(cumap, false);
break;
+ case UICURVE_FUNC_HANDLE_AUTO_ANIM: /* set auto-clamped */
+ curvemap_handle_set(cuma, HD_AUTO_ANIM);
+ curvemapping_changed(cumap, false);
case UICURVE_FUNC_EXTEND_HOZ: /* extend horiz */
cuma->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
curvemapping_changed(cumap, false);
@@ -2000,6 +2006,9 @@ static uiBlock *curvemap_tools_func(
uiDefIconTextBut(
block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Handle"),
0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO, "");
+ uiDefIconTextBut(
+ block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Clamped Handle"),
+ 0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO_ANIM, "");
}
if (show_extend) {
@@ -3841,3 +3850,73 @@ void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
UI_block_align_end(block);
}
+
+/********************************* Cache File *********************************/
+
+void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname)
+{
+ if (!ptr->data) {
+ return;
+ }
+
+ PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
+
+ if (!prop) {
+ printf("%s: property not found: %s.%s\n",
+ __func__, RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ if (RNA_property_type(prop) != PROP_POINTER) {
+ printf("%s: expected pointer property for %s.%s\n",
+ __func__, RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ PointerRNA fileptr = RNA_property_pointer_get(ptr, prop);
+ CacheFile *file = fileptr.data;
+
+ uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr);
+
+ uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL);
+
+ if (!file) {
+ return;
+ }
+
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiBlock *block = uiLayoutGetBlock(row);
+ uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, "");
+
+ row = uiLayoutRow(layout, false);
+ uiLayout *split = uiLayoutSplit(row, 0.0f, false);
+ row = uiLayoutRow(split, true);
+
+ uiItemR(row, &fileptr, "filepath", 0, "", ICON_NONE);
+ uiItemO(row, "", ICON_FILE_REFRESH, "cachefile.reload");
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &fileptr, "is_sequence", 0, "Is Sequence", ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &fileptr, "override_frame", 0, "Override Frame", ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetEnabled(row, RNA_boolean_get(&fileptr, "override_frame"));
+ uiItemR(row, &fileptr, "frame", 0, "Frame", ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE);
+
+ /* TODO: unused for now, so no need to expose. */
+#if 0
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &fileptr, "forward_axis", 0, "Forward Axis", ICON_NONE);
+
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &fileptr, "up_axis", 0, "Up Axis", ICON_NONE);
+#endif
+}
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index 828cb494eab..b3bbce939a5 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -28,6 +28,8 @@ set(INC
../../makesrna
../../windowmanager
../../collada
+ ../../alembic
+ ../../../../intern/guardedalloc
)
set(INC_SYS
@@ -35,9 +37,13 @@ set(INC_SYS
)
set(SRC
+ io_alembic.c
+ io_cache.c
io_collada.c
io_ops.c
+ io_alembic.h
+ io_cache.h
io_collada.h
io_ops.h
)
@@ -46,6 +52,14 @@ if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_ALEMBIC)
+
+ if(WITH_ALEMBIC_HDF5)
+ add_definitions(-DWITH_ALEMBIC_HDF5)
+ endif()
+endif()
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
new file mode 100644
index 00000000000..7a7c42e501b
--- /dev/null
+++ b/source/blender/editors/io/io_alembic.c
@@ -0,0 +1,458 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifdef WITH_ALEMBIC
+
+/* needed for directory lookup */
+#ifndef WIN32
+# include <dirent.h>
+#else
+# include "BLI_winstuff.h"
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "io_alembic.h"
+
+#include "ABC_alembic.h"
+
+static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ char filepath[FILE_MAX];
+ BLI_strncpy(filepath, G.main->name, sizeof(filepath));
+ BLI_replace_extension(filepath, sizeof(filepath), ".abc");
+ RNA_string_set(op->ptr, "filepath", filepath);
+ }
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+
+ UNUSED_VARS(event);
+}
+
+static int wm_alembic_export_exec(bContext *C, wmOperator *op)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
+
+ char filename[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filename);
+
+ const struct AlembicExportParams params = {
+ .frame_start = RNA_int_get(op->ptr, "start"),
+ .frame_end = RNA_int_get(op->ptr, "end"),
+
+ .frame_step_xform = 1.0 / (double)RNA_int_get(op->ptr, "xsamples"),
+ .frame_step_shape = 1.0 / (double)RNA_int_get(op->ptr, "gsamples"),
+
+ .shutter_open = RNA_float_get(op->ptr, "sh_open"),
+ .shutter_close = RNA_float_get(op->ptr, "sh_close"),
+
+ .selected_only = RNA_boolean_get(op->ptr, "selected"),
+ .uvs = RNA_boolean_get(op->ptr, "uvs"),
+ .normals = RNA_boolean_get(op->ptr, "normals"),
+ .vcolors = RNA_boolean_get(op->ptr, "vcolors"),
+ .apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"),
+ .flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"),
+ .visible_layers_only = RNA_boolean_get(op->ptr, "visible_layers_only"),
+ .renderable_only = RNA_boolean_get(op->ptr, "renderable_only"),
+ .face_sets = RNA_boolean_get(op->ptr, "face_sets"),
+ .use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"),
+ .compression_type = RNA_enum_get(op->ptr, "compression_type"),
+ .packuv = RNA_boolean_get(op->ptr, "packuv"),
+
+ .global_scale = RNA_float_get(op->ptr, "global_scale"),
+ };
+
+ ABC_export(CTX_data_scene(C), C, filename, &params);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
+{
+ uiLayout *box = uiLayoutBox(layout);
+ uiLayout *row;
+
+#ifdef WITH_ALEMBIC_HDF5
+ row = uiLayoutRow(box, false);
+ uiItemL(row, IFACE_("Archive Options:"), ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "compression_type", 0, NULL, ICON_NONE);
+#endif
+
+ box = uiLayoutBox(layout);
+ row = uiLayoutRow(box, false);
+ uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "global_scale", 0, NULL, ICON_NONE);
+
+ /* Scene Options */
+ box = uiLayoutBox(layout);
+ row = uiLayoutRow(box, false);
+ uiItemL(row, IFACE_("Scene Options:"), ICON_SCENE_DATA);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "start", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "end", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "xsamples", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "gsamples", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "sh_open", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "sh_close", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "renderable_only", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "visible_layers_only", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "flatten", 0, NULL, ICON_NONE);
+
+ /* Object Data */
+ box = uiLayoutBox(layout);
+ row = uiLayoutRow(box, false);
+ uiItemL(row, IFACE_("Object Options:"), ICON_OBJECT_DATA);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "uvs", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "packuv", 0, NULL, ICON_NONE);
+ uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "uvs"));
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "normals", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "vcolors", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "face_sets", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "subdiv_schema", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "apply_subdiv", 0, NULL, ICON_NONE);
+}
+
+static void wm_alembic_export_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ ui_alembic_export_settings(op->layout, &ptr);
+}
+
+void WM_OT_alembic_export(wmOperatorType *ot)
+{
+ ot->name = "Export Alembic Archive";
+ ot->idname = "WM_OT_alembic_export";
+
+ ot->invoke = wm_alembic_export_invoke;
+ ot->exec = wm_alembic_export_exec;
+ ot->poll = WM_operator_winactive;
+ ot->ui = wm_alembic_export_draw;
+
+ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
+ FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH,
+ FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+
+ RNA_def_int(ot->srna, "start", 1, INT_MIN, INT_MAX,
+ "Start Frame", "Start Frame", INT_MIN, INT_MAX);
+
+ RNA_def_int(ot->srna, "end", 1, INT_MIN, INT_MAX,
+ "End Frame", "End Frame", INT_MIN, INT_MAX);
+
+ RNA_def_int(ot->srna, "xsamples", 1, 1, 128,
+ "Transform Samples", "Number of times per frame transformations are sampled", 1, 128);
+
+ RNA_def_int(ot->srna, "gsamples", 1, 1, 128,
+ "Geometry Samples", "Number of times per frame object datas are sampled", 1, 128);
+
+ RNA_def_float(ot->srna, "sh_open", 0.0f, -1.0f, 1.0f,
+ "Shutter Open", "Time at which the shutter is open", -1.0f, 1.0f);
+
+ RNA_def_float(ot->srna, "sh_close", 1.0f, -1.0f, 1.0f,
+ "Shutter Close", "Time at which the shutter is closed", -1.0f, 1.0f);
+
+ RNA_def_boolean(ot->srna, "selected", 0,
+ "Selected Objects Only", "Export only selected objects");
+
+ RNA_def_boolean(ot->srna, "renderable_only", 1,
+ "Renderable Objects Only",
+ "Export only objects marked renderable in the outliner");
+
+ RNA_def_boolean(ot->srna, "visible_layers_only", 0,
+ "Visible Layers Only", "Export only objects in visible layers");
+
+ RNA_def_boolean(ot->srna, "flatten", 0,
+ "Flatten Hierarchy",
+ "Do not preserve objects' parent/children relationship");
+
+ RNA_def_boolean(ot->srna, "uvs", 1, "UVs", "Export UVs");
+
+ RNA_def_boolean(ot->srna, "packuv", 1, "Pack UV Islands",
+ "Export UVs with packed island");
+
+ RNA_def_boolean(ot->srna, "normals", 1, "Normals", "Export normals");
+
+ RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex colors", "Export vertex colors");
+
+ RNA_def_boolean(ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments");
+
+ RNA_def_boolean(ot->srna, "subdiv_schema", 0,
+ "Use Subdivision Schema",
+ "Export meshes using Alembic's subdivision schema");
+
+ RNA_def_boolean(ot->srna, "apply_subdiv", 0,
+ "Apply Subsurf", "Export subdivision surfaces as meshes");
+
+ RNA_def_enum(ot->srna, "compression_type", rna_enum_abc_compression_items,
+ ABC_ARCHIVE_OGAWA, "Compression", "");
+
+ RNA_def_float(ot->srna, "global_scale", 1.0f, 0.0001f, 1000.0f, "Scale",
+ "Value by which to enlarge or shrink the objects with respect to the world's origin",
+ 0.0001f, 1000.0f);
+}
+
+/* ************************************************************************** */
+
+/* TODO(kevin): check on de-duplicating all this with code in image_ops.c */
+
+typedef struct CacheFrame {
+ struct CacheFrame *next, *prev;
+ int framenr;
+} CacheFrame;
+
+static int cmp_frame(const void *a, const void *b)
+{
+ const CacheFrame *frame_a = a;
+ const CacheFrame *frame_b = b;
+
+ if (frame_a->framenr < frame_b->framenr) return -1;
+ if (frame_a->framenr > frame_b->framenr) return 1;
+ return 0;
+}
+
+static int get_sequence_len(char *filename, int *ofs)
+{
+ int frame;
+ int numdigit;
+
+ if (!BLI_path_frame_get(filename, &frame, &numdigit)) {
+ return 1;
+ }
+
+ char path[FILE_MAX];
+ BLI_split_dir_part(filename, path, FILE_MAX);
+
+ DIR *dir = opendir(path);
+
+ const char *ext = ".abc";
+ const char *basename = BLI_path_basename(filename);
+ const int len = strlen(basename) - (numdigit + strlen(ext));
+
+ ListBase frames;
+ BLI_listbase_clear(&frames);
+
+ struct dirent *fname;
+ while ((fname = readdir(dir)) != NULL) {
+ /* do we have the right extension? */
+ if (!strstr(fname->d_name, ext)) {
+ continue;
+ }
+
+ if (!STREQLEN(basename, fname->d_name, len)) {
+ continue;
+ }
+
+ CacheFrame *cache_frame = MEM_callocN(sizeof(CacheFrame), "abc_frame");
+
+ BLI_path_frame_get(fname->d_name, &cache_frame->framenr, &numdigit);
+
+ BLI_addtail(&frames, cache_frame);
+ }
+
+ closedir(dir);
+
+ BLI_listbase_sort(&frames, cmp_frame);
+
+ CacheFrame *cache_frame = frames.first;
+
+ if (cache_frame) {
+ int frame_curr = cache_frame->framenr;
+ (*ofs) = frame_curr;
+
+ while (cache_frame && (cache_frame->framenr == frame_curr)) {
+ ++frame_curr;
+ cache_frame = cache_frame->next;
+ }
+
+ BLI_freelistN(&frames);
+
+ return frame_curr - (*ofs);
+ }
+
+ return 1;
+}
+
+/* ************************************************************************** */
+
+static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
+{
+ uiLayout *box = uiLayoutBox(layout);
+ uiLayout *row = uiLayoutRow(box, false);
+ uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "scale", 0, NULL, ICON_NONE);
+
+ box = uiLayoutBox(layout);
+ row = uiLayoutRow(box, false);
+ uiItemL(row, IFACE_("Options:"), ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "set_frame_range", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "is_sequence", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
+}
+
+static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ ui_alembic_import_settings(op->layout, &ptr);
+}
+
+static int wm_alembic_import_exec(bContext *C, wmOperator *op)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
+
+ char filename[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filename);
+
+ const float scale = RNA_float_get(op->ptr, "scale");
+ const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence");
+ const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range");
+ const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
+
+ int offset = 0;
+ int sequence_len = 1;
+
+ if (is_sequence) {
+ sequence_len = get_sequence_len(filename, &offset);
+ }
+
+ ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes);
+
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_alembic_import(wmOperatorType *ot)
+{
+ ot->name = "Import Alembic Archive";
+ ot->idname = "WM_OT_alembic_import";
+
+ ot->invoke = WM_operator_filesel;
+ ot->exec = wm_alembic_import_exec;
+ ot->poll = WM_operator_winactive;
+ ot->ui = wm_alembic_import_draw;
+
+ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
+ FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH,
+ FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+
+ RNA_def_float(ot->srna, "scale", 1.0f, 0.0001f, 1000.0f, "Scale",
+ "Value by which to enlarge or shrink the objects with respect to the world's origin",
+ 0.0001f, 1000.0f);
+
+ RNA_def_boolean(ot->srna, "set_frame_range", true,
+ "Set Frame Range",
+ "If checked, update scene's start and end frame to match those of the Alembic archive");
+
+ RNA_def_boolean(ot->srna, "validate_meshes", 0,
+ "Validate Meshes", "Check imported mesh objects for invalid data (slow)");
+
+ RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence",
+ "Set to true if the cache is split into separate files");
+}
+
+#endif
diff --git a/source/blender/editors/io/io_alembic.h b/source/blender/editors/io/io_alembic.h
new file mode 100644
index 00000000000..5eefabef4be
--- /dev/null
+++ b/source/blender/editors/io/io_alembic.h
@@ -0,0 +1,37 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __IO_ALEMBIC_H__
+#define __IO_ALEMBIC_H__
+
+/** \file blender/editors/io/io_alembic.h
+ * \ingroup editor/io
+ */
+
+struct wmOperatorType;
+
+void WM_OT_alembic_export(struct wmOperatorType *ot);
+void WM_OT_alembic_import(struct wmOperatorType *ot);
+
+#endif /* __IO_ALEMBIC_H__ */
diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c
new file mode 100644
index 00000000000..d6e2c1ae204
--- /dev/null
+++ b/source/blender/editors/io/io_cache.c
@@ -0,0 +1,162 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "BKE_cachefile.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "io_cache.h"
+
+static void cachefile_init(bContext *C, wmOperator *op)
+{
+ PropertyPointerRNA *pprop;
+
+ op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
+ UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop);
+}
+
+static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ char filepath[FILE_MAX];
+ BLI_strncpy(filepath, G.main->name, sizeof(filepath));
+ BLI_replace_extension(filepath, sizeof(filepath), ".abc");
+ RNA_string_set(op->ptr, "filepath", filepath);
+ }
+
+ cachefile_init(C, op);
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+
+ UNUSED_VARS(event);
+}
+
+static void open_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ MEM_freeN(op->customdata);
+ op->customdata = NULL;
+}
+
+static int cachefile_open_exec(bContext *C, wmOperator *op)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
+
+ char filename[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filename);
+
+ Main *bmain = CTX_data_main(C);
+
+ CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename));
+ BLI_strncpy(cache_file->filepath, filename, FILE_MAX);
+ BKE_cachefile_reload(bmain, cache_file);
+
+ /* hook into UI */
+ PropertyPointerRNA *pprop = op->customdata;
+
+ if (pprop->prop) {
+ /* when creating new ID blocks, use is already 1, but RNA
+ * pointer se also increases user, so this compensates it */
+ id_us_min(&cache_file->id);
+
+ PointerRNA idptr;
+ RNA_id_pointer_create(&cache_file->id, &idptr);
+ RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
+ RNA_property_update(C, &pprop->ptr, pprop->prop);
+ }
+
+ MEM_freeN(op->customdata);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHEFILE_OT_open(wmOperatorType *ot)
+{
+ ot->name = "Open Cache File";
+ ot->idname = "CACHEFILE_OT_open";
+
+ ot->invoke = cachefile_open_invoke;
+ ot->exec = cachefile_open_exec;
+ ot->cancel = open_cancel;
+
+ WM_operator_properties_filesel(ot, FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
+ FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH,
+ FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+}
+
+/* ***************************** Reload Operator **************************** */
+
+static int cachefile_reload_exec(bContext *C, wmOperator *op)
+{
+ CacheFile *cache_file = CTX_data_edit_cachefile(C);
+
+ if (!cache_file) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Main *bmain = CTX_data_main(C);
+
+ BLI_listbase_clear(&cache_file->object_paths);
+ BKE_cachefile_reload(bmain, cache_file);
+
+ return OPERATOR_FINISHED;
+
+ UNUSED_VARS(op);
+}
+
+void CACHEFILE_OT_reload(wmOperatorType *ot)
+{
+ ot->name = "Refresh Archive";
+ ot->description = "Update objects paths list with new data from the archive";
+ ot->idname = "CACHEFILE_OT_reload";
+
+ /* api callbacks */
+ ot->exec = cachefile_reload_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/io/io_cache.h b/source/blender/editors/io/io_cache.h
new file mode 100644
index 00000000000..ea270c2aba1
--- /dev/null
+++ b/source/blender/editors/io/io_cache.h
@@ -0,0 +1,37 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __IO_CACHE_H__
+#define __IO_CACHE_H__
+
+/** \file blender/editors/io/io_cache.h
+ * \ingroup editor/io
+ */
+
+struct wmOperatorType;
+
+void CACHEFILE_OT_open(struct wmOperatorType *ot);
+void CACHEFILE_OT_reload(struct wmOperatorType *ot);
+
+#endif /* __IO_CACHE_H__ */
diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c
index a70a51a60be..d1e933517a9 100644
--- a/source/blender/editors/io/io_ops.c
+++ b/source/blender/editors/io/io_ops.c
@@ -30,11 +30,18 @@
#include "io_ops.h" /* own include */
+#include "WM_api.h"
+
#ifdef WITH_COLLADA
# include "io_collada.h"
-# include "WM_api.h"
#endif
+#ifdef WITH_ALEMBIC
+# include "io_alembic.h"
+#endif
+
+#include "io_cache.h"
+
void ED_operatortypes_io(void)
{
#ifdef WITH_COLLADA
@@ -42,4 +49,11 @@ void ED_operatortypes_io(void)
WM_operatortype_append(WM_OT_collada_export);
WM_operatortype_append(WM_OT_collada_import);
#endif
+#ifdef WITH_ALEMBIC
+ WM_operatortype_append(WM_OT_alembic_import);
+ WM_operatortype_append(WM_OT_alembic_export);
+#endif
+
+ WM_operatortype_append(CACHEFILE_OT_open);
+ WM_operatortype_append(CACHEFILE_OT_reload);
}
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index cc628210e20..a8b0c28599d 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -48,6 +48,7 @@
#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
#include "DNA_actuator_types.h"
+#include "DNA_gpencil_types.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@@ -1145,10 +1146,22 @@ static int object_delete_exec(bContext *C, wmOperator *op)
}
else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) {
BKE_reportf(op->reports, RPT_WARNING,
- "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user",
- base->object->id.name + 2, scene->id.name + 2);
+ "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user",
+ base->object->id.name + 2, scene->id.name + 2);
continue;
}
+ /* remove from Grease Pencil parent */
+ for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (gpl->parent != NULL) {
+ Object *ob = gpl->parent;
+ Object *curob = base->object;
+ if (ob == curob) {
+ gpl->parent = NULL;
+ }
+ }
+ }
+ }
/* deselect object -- it could be used in other scenes */
base->object->flag &= ~SELECT;
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index db8a4c1960f..59d78f13ccb 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -416,6 +416,13 @@ static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
con->flag |= CONSTRAINT_DISABLE;
}
+ else if (con->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
+ bTransformCacheConstraint *data = con->data;
+
+ if ((data->cache_file == NULL) || (data->object_path[0] == '\0')) {
+ con->flag |= CONSTRAINT_DISABLE;
+ }
+ }
/* Check targets for constraints */
if (check_targets && cti && cti->get_constraint_targets) {
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index b3edf1f5e0d..d5c7632765f 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -48,6 +48,7 @@
#include "DNA_world_types.h"
#include "DNA_object_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_gpencil_types.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
@@ -1381,7 +1382,7 @@ static int move_to_layer_exec(bContext *C, wmOperator *op)
/* upper byte is used for local view */
local = base->lay & 0xFF000000;
base->lay = lay + local;
- base->object->lay = lay;
+ base->object->lay = base->lay;
/* if (base->object->type == OB_LAMP) is_lamp = true; */
}
CTX_DATA_END;
@@ -1762,6 +1763,17 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in
else {
/* copy already clears */
}
+ /* remap gpencil parenting */
+
+ if (scene->gpd) {
+ bGPdata *gpd = scene->gpd;
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (gpl->parent == ob) {
+ gpl->parent = obn;
+ }
+ }
+ }
+
base->flag = obn->flag;
id_us_min(&ob->id);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 992b827113d..85c05ab0e5c 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -43,6 +43,7 @@
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
+#include "DNA_gpencil_types.h"
#include "BKE_camera.h"
#include "BKE_context.h"
@@ -414,6 +415,99 @@ static void screen_opengl_render_write(OGLRender *oglrender)
else printf("OpenGL Render failed to write '%s'\n", name);
}
+static void addAlphaOverFloat(float dest[4], const float source[4])
+{
+ /* d = s + (1-alpha_s)d*/
+ float mul;
+
+ mul = 1.0f - source[3];
+
+ dest[0] = (mul * dest[0]) + source[0];
+ dest[1] = (mul * dest[1]) + source[1];
+ dest[2] = (mul * dest[2]) + source[2];
+ dest[3] = (mul * dest[3]) + source[3];
+
+}
+
+/* add renderlayer and renderpass for each grease pencil layer for using in composition */
+static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, RenderView *rv)
+{
+ bGPdata *gpd = oglrender->scene->gpd;
+ Scene *scene = oglrender->scene;
+
+ /* sanity checks */
+ if (gpd == NULL) {
+ return;
+ }
+ if (scene == NULL) {
+ return;
+ }
+ if (BLI_listbase_is_empty(&gpd->layers)) {
+ return;
+ }
+
+ /* save old alpha mode */
+ short oldalphamode = scene->r.alphamode;
+ /* set alpha transparent for gp */
+ scene->r.alphamode = R_ALPHAPREMUL;
+
+ /* saves layer status */
+ short *oldsts = MEM_mallocN(BLI_listbase_count(&gpd->layers) * sizeof(short), "temp_gplayers_flag");
+ int i = 0;
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ oldsts[i] = gpl->flag;
+ ++i;
+ }
+ /* loop all layers to create separate render */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* dont draw layer if hidden */
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+ /* hide all layer except current */
+ for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) {
+ if (gpl != gph) {
+ gph->flag |= GP_LAYER_HIDE;
+ }
+ }
+
+ /* render this gp layer */
+ screen_opengl_render_doit(oglrender, rr);
+
+ /* add RendePass composite */
+ RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name);
+
+ /* copy image data from rectf */
+ float *src = RE_RenderViewGetById(rr, oglrender->view_id)->rectf;
+ float *dest = rp->rect;
+
+ float *pixSrc, *pixDest;
+ int x, y, rectx, recty;
+ rectx = rr->rectx;
+ recty = rr->recty;
+ for (y = 0; y < recty; y++) {
+ for (x = 0; x < rectx; x++) {
+ pixSrc = src + 4 * (rectx * y + x);
+ if (pixSrc[3] > 0.0) {
+ pixDest = dest + 4 * (rectx * y + x);
+ addAlphaOverFloat(pixDest, pixSrc);
+ }
+ }
+ }
+
+ /* back layer status */
+ i = 0;
+ for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) {
+ gph->flag = oldsts[i];
+ ++i;
+ }
+ }
+ /* free memory */
+ MEM_freeN(oldsts);
+
+ /* back default alpha mode */
+ scene->r.alphamode = oldalphamode;
+}
+
static void screen_opengl_render_apply(OGLRender *oglrender)
{
RenderResult *rr;
@@ -449,6 +543,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
BLI_assert(view_id < oglrender->views_len);
RE_SetActiveRenderView(oglrender->re, rv->name);
oglrender->view_id = view_id;
+ /* add grease pencil passes */
+ add_gpencil_renderpass(oglrender, rr, rv);
+ /* render composite */
screen_opengl_render_doit(oglrender, rr);
}
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 76d0143e898..b4f3426677a 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -1210,7 +1210,7 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r
/* setup job */
WM_jobs_customdata_set(wm_job, ip, icon_preview_free);
- WM_jobs_timer(wm_job, 0.1, NC_MATERIAL, NC_MATERIAL);
+ WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW);
WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index f61ad348501..34c51914027 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -85,7 +85,8 @@ const char *screen_context_dir[] = {
"sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */
"gpencil_data", "gpencil_data_owner", /* grease pencil data */
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
- "active_gpencil_layer", "active_gpencil_frame",
+ "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette",
+ "active_gpencil_palettecolor", "active_gpencil_brush",
"active_operator",
NULL};
@@ -116,7 +117,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
else if (CTX_data_equals(member, "visible_objects") || CTX_data_equals(member, "visible_bases")) {
const unsigned int lay = context_layers(sc, scene, sa);
- int visible_objects = CTX_data_equals(member, "visible_objects");
+ const bool visible_objects = CTX_data_equals(member, "visible_objects");
for (base = scene->base.first; base; base = base->next) {
if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & lay)) {
@@ -131,7 +132,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
else if (CTX_data_equals(member, "selectable_objects") || CTX_data_equals(member, "selectable_bases")) {
const unsigned int lay = context_layers(sc, scene, sa);
- int selectable_objects = CTX_data_equals(member, "selectable_objects");
+ const bool selectable_objects = CTX_data_equals(member, "selectable_objects");
for (base = scene->base.first; base; base = base->next) {
if (base->lay & lay) {
@@ -148,7 +149,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
else if (CTX_data_equals(member, "selected_objects") || CTX_data_equals(member, "selected_bases")) {
const unsigned int lay = context_layers(sc, scene, sa);
- int selected_objects = CTX_data_equals(member, "selected_objects");
+ const bool selected_objects = CTX_data_equals(member, "selected_objects");
for (base = scene->base.first; base; base = base->next) {
if ((base->flag & SELECT) && (base->lay & lay)) {
@@ -163,7 +164,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
else if (CTX_data_equals(member, "selected_editable_objects") || CTX_data_equals(member, "selected_editable_bases")) {
const unsigned int lay = context_layers(sc, scene, sa);
- int selected_editable_objects = CTX_data_equals(member, "selected_editable_objects");
+ const bool selected_editable_objects = CTX_data_equals(member, "selected_editable_objects");
for (base = scene->base.first; base; base = base->next) {
if ((base->flag & SELECT) && (base->lay & lay)) {
@@ -182,7 +183,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
else if (CTX_data_equals(member, "editable_objects") || CTX_data_equals(member, "editable_bases")) {
const unsigned int lay = context_layers(sc, scene, sa);
- int editable_objects = CTX_data_equals(member, "editable_objects");
+ const bool editable_objects = CTX_data_equals(member, "editable_objects");
/* Visible + Editable, but not necessarily selected */
for (base = scene->base.first; base; base = base->next) {
@@ -201,7 +202,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
else if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) {
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
EditBone *ebone, *flipbone = NULL;
- int editable_bones = CTX_data_equals(member, "editable_bones");
+ const bool editable_bones = CTX_data_equals(member, "editable_bones");
if (arm && arm->edbo) {
/* Attention: X-Axis Mirroring is also handled here... */
@@ -243,7 +244,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
else if (CTX_data_equals(member, "selected_bones") || CTX_data_equals(member, "selected_editable_bones")) {
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
EditBone *ebone, *flipbone = NULL;
- int selected_editable_bones = CTX_data_equals(member, "selected_editable_bones");
+ const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones");
if (arm && arm->edbo) {
/* Attention: X-Axis Mirroring is also handled here... */
@@ -466,7 +467,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
if (gpd) {
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
if (gpl) {
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl);
@@ -474,12 +475,50 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
}
}
+ else if (CTX_data_equals(member, "active_gpencil_palette")) {
+ /* XXX: see comment for gpencil_data case... */
+ bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
+
+ if (gpd) {
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+
+ if (palette) {
+ CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette);
+ return 1;
+ }
+ }
+ }
+ else if (CTX_data_equals(member, "active_gpencil_palettecolor")) {
+ /* XXX: see comment for gpencil_data case... */
+ bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
+
+ if (gpd) {
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+
+ if (palette) {
+ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+ if (palcolor) {
+ CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor);
+ return 1;
+ }
+ }
+ }
+ }
+ else if (CTX_data_equals(member, "active_gpencil_brush")) {
+ /* XXX: see comment for gpencil_data case... */
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(scene->toolsettings);
+
+ if (brush) {
+ CTX_data_pointer_set(result, NULL, &RNA_GPencilBrush, brush);
+ return 1;
+ }
+ }
else if (CTX_data_equals(member, "active_gpencil_frame")) {
/* XXX: see comment for gpencil_data case... */
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
if (gpd) {
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
if (gpl) {
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe);
@@ -533,6 +572,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
for (gps = gpf->strokes.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use_direct(sa, gps)) {
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps);
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index f0a6b56a7d2..ac39c6f995c 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1943,6 +1943,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s
sc = ED_screen_add(win, oldscreen->scene, newname);
sc->state = state;
sc->redraws_flag = oldscreen->redraws_flag;
+ sc->temp = oldscreen->temp;
/* timer */
sc->animtimer = oldscreen->animtimer;
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 080bd5b73c7..9474a46d716 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -1126,8 +1126,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
/* blend into canvas */
for (a = 0; a < tot; a++) {
ED_imapaint_dirty_region(s->image, s->canvas,
- region[a].destx, region[a].desty,
- region[a].width, region[a].height, true);
+ region[a].destx, region[a].desty,
+ region[a].width, region[a].height, true);
if (s->do_masking) {
/* masking, find original pixels tiles from undo buffer to composite over */
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 1305b76b5ad..fe0fb3f5035 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1239,7 +1239,7 @@ static float brush_strength(
/* Return a multiplier for brush strength on a particular vertex. */
static float tex_strength(SculptSession *ss, Brush *br,
- const float point[3],
+ const float brush_point[3],
const float len,
const short vno[3],
const float fno[3],
@@ -1251,6 +1251,9 @@ static float tex_strength(SculptSession *ss, Brush *br,
MTex *mtex = &br->mtex;
float avg = 1;
float rgba[4];
+ float point[3];
+
+ sub_v3_v3v3(point, brush_point, cache->plane_offset);
if (!mtex->tex) {
avg = 1;
diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c
index 063a477d2b6..bfc808f6d83 100644
--- a/source/blender/editors/space_action/action_buttons.c
+++ b/source/blender/editors/space_action/action_buttons.c
@@ -122,7 +122,7 @@ void ACTION_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "ACTION_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = action_properties_toggle_exec;
ot->poll = ED_operator_action_active;
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 55b087c40e7..f8db35e2311 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -750,7 +750,7 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode)
/* insert gp frames */
for (ale = anim_data.first; ale; ale = ale->next) {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
- gpencil_layer_getframe(gpl, CFRA, add_frame_mode);
+ BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode);
}
ANIM_animdata_update(ac, &anim_data);
diff --git a/source/blender/editors/space_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c
index d31d7f53b30..b042bbe8fea 100644
--- a/source/blender/editors/space_clip/clip_toolbar.c
+++ b/source/blender/editors/space_clip/clip_toolbar.c
@@ -106,7 +106,7 @@ void CLIP_OT_properties(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Properties";
- ot->description = "Toggle clip properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "CLIP_OT_properties";
/* api callbacks */
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index 415839ab761..396d71f0a20 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -818,7 +818,7 @@ static void clip_keymap(struct wmKeyConfig *keyconf)
keymap = WM_keymap_find(keyconf, "Clip Dopesheet Editor", SPACE_CLIP, 0);
- kmi = WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_select_channel", ACTIONMOUSE, KM_PRESS, 0, 0);
+ kmi = WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_select_channel", LEFTMOUSE, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "extend", true); /* toggle */
WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_view_all", HOMEKEY, KM_PRESS, 0, 0);
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 5e9eb1f9207..b6e4991bf52 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -920,6 +920,8 @@ static int filelist_geticon_ex(
return ICON_FILE_BLANK;
else if (typeflag & FILE_TYPE_COLLADA)
return ICON_FILE_BLANK;
+ else if (typeflag & FILE_TYPE_ALEMBIC)
+ return ICON_FILE_BLANK;
else if (typeflag & FILE_TYPE_TEXT)
return ICON_FILE_TEXT;
else if (typeflag & FILE_TYPE_BLENDERLIB) {
@@ -1952,6 +1954,9 @@ int ED_path_extension_type(const char *path)
else if (BLI_testextensie(path, ".dae")) {
return FILE_TYPE_COLLADA;
}
+ else if (BLI_testextensie(path, ".abc")) {
+ return FILE_TYPE_ALEMBIC;
+ }
else if (BLI_testextensie_array(path, imb_ext_image) ||
(G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt)))
{
@@ -2004,6 +2009,8 @@ int ED_file_extension_icon(const char *path)
return ICON_FILE_BLANK;
case FILE_TYPE_COLLADA:
return ICON_FILE_BLANK;
+ case FILE_TYPE_ALEMBIC:
+ return ICON_FILE_BLANK;
case FILE_TYPE_TEXT:
return ICON_FILE_TEXT;
default:
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index ab33d452d3c..1a558d40560 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -185,6 +185,8 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BTX : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_collada")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_COLLADA : 0;
+ if ((prop = RNA_struct_find_property(op->ptr, "filter_alembic")))
+ params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_ALEMBIC : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_glob"))) {
/* Protection against pyscripts not setting proper size limit... */
char *tmp = RNA_property_string_get_alloc(
@@ -213,7 +215,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA |
FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB |
FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO |
- FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO;
+ FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF;
if (U.uiflag & USER_HIDE_DOT) {
params->flag |= FILE_HIDE_DOT;
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index a9ab1502e16..516814b63b4 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -812,6 +812,11 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
}
col = uiLayoutColumn(pa->layout, true);
+
+ if (driver->type == DRIVER_TYPE_PYTHON) {
+ uiItemR(col, &driver_ptr, "use_self", 0, NULL, ICON_NONE);
+ }
+
/* debug setting */
uiItemR(col, &driver_ptr, "show_debug_info", 0, NULL, ICON_NONE);
@@ -1062,7 +1067,7 @@ void GRAPH_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "GRAPH_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = graph_properties_toggle_exec;
ot->poll = ED_operator_graphedit_active;
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 38a54ade367..b9d98dfe794 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -1330,7 +1330,7 @@ void IMAGE_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "IMAGE_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = image_properties_toggle_exec;
ot->poll = ED_operator_image_active;
diff --git a/source/blender/editors/space_logic/logic_buttons.c b/source/blender/editors/space_logic/logic_buttons.c
index 12c7ef3d3ec..e5eee21ed08 100644
--- a/source/blender/editors/space_logic/logic_buttons.c
+++ b/source/blender/editors/space_logic/logic_buttons.c
@@ -63,7 +63,7 @@ static int logic_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
void LOGIC_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "LOGIC_OT_properties";
ot->exec = logic_properties_toggle_exec;
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index cbdc476bee6..3243579f7d0 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -131,6 +131,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@@ -567,7 +568,7 @@ void NLA_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "NLA_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = nla_properties_toggle_exec;
ot->poll = ED_operator_nla_active;
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index 9e73e03a664..4f30c049d9d 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -170,6 +170,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
+ case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index d40240e8412..f3d2bf2662a 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -1337,7 +1337,7 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point
scn_ptr = RNA_pointer_get(ptr, "scene");
RNA_string_get(&scn_ptr, "name", scene_name);
-
+
WM_operator_properties_create_ptr(&op_ptr, ot);
RNA_string_set(&op_ptr, "layer", layer_name);
RNA_string_set(&op_ptr, "scene", scene_name);
diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c
index 52b0292b9a3..f0567924edd 100644
--- a/source/blender/editors/space_node/node_buttons.c
+++ b/source/blender/editors/space_node/node_buttons.c
@@ -222,7 +222,7 @@ static int node_properties_poll(bContext *C)
void NODE_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
- ot->description = "Toggles the properties panel display";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "NODE_OT_properties";
ot->exec = node_properties_toggle_exec;
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c
index cf6e2ac226e..26eeaa91dd0 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.c
@@ -794,22 +794,37 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
}
else if (fromselect) {
- bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock);
- bNodeSocket *output_sock;
-
- /* update the group node and interface node sockets,
- * so the new interface socket can be linked.
- */
- node_group_verify(ntree, gnode, (ID *)ngroup);
- node_group_output_verify(ngroup, output_node, (ID *)ngroup);
+ /* First check whether the source of this link is already connected to an output.
+ * If yes, reuse that output instead of duplicating it. */
+ bool connected = false;
+ bNodeLink *olink;
+ for (olink = ngroup->links.first; olink; olink = olink->next) {
+ if (olink->fromsock == link->fromsock && olink->tonode == output_node) {
+ bNodeSocket *output_sock = node_group_find_output_socket(gnode, olink->tosock->identifier);
+ link->fromnode = gnode;
+ link->fromsock = output_sock;
+ connected = true;
+ }
+ }
- /* create new internal link */
- output_sock = node_group_output_find_socket(output_node, iosock->identifier);
- nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock);
-
- /* redirect external link */
- link->fromnode = gnode;
- link->fromsock = node_group_find_output_socket(gnode, iosock->identifier);
+ if (!connected) {
+ bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock);
+ bNodeSocket *output_sock;
+
+ /* update the group node and interface node sockets,
+ * so the new interface socket can be linked.
+ */
+ node_group_verify(ntree, gnode, (ID *)ngroup);
+ node_group_output_verify(ngroup, output_node, (ID *)ngroup);
+
+ /* create new internal link */
+ output_sock = node_group_output_find_socket(output_node, iosock->identifier);
+ nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock);
+
+ /* redirect external link */
+ link->fromnode = gnode;
+ link->fromsock = node_group_find_output_socket(gnode, iosock->identifier);
+ }
}
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index ae34a118992..d4553b650c5 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -1002,7 +1002,7 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon)
}
-static void tselem_draw_gp_icon_uibut(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl)
+static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl)
{
/* restrict column clip - skip it for now... */
if (arg->x >= arg->xmax) {
@@ -1173,6 +1173,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
UI_icon_draw(x, y, ICON_MOD_TRIANGULATE); break;
case eModifierType_MeshCache:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
+ case eModifierType_MeshSequenceCache:
+ UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
case eModifierType_Wireframe:
UI_icon_draw(x, y, ICON_MOD_WIREFRAME); break;
case eModifierType_LaplacianDeform:
@@ -1233,9 +1235,12 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
else
UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type));
break;
+ /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */
+#if 0
case TSE_GP_LAYER:
tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata);
break;
+#endif
default:
UI_icon_draw(x, y, ICON_DOT); break;
}
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index d2666cd0b6d..ca037cb20cc 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -62,7 +62,7 @@ typedef struct TreeElement {
#define TREESTORE_ID_TYPE(_id) \
(ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \
ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \
- ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO)) /* Only in 'blendfile' mode ... :/ */
+ ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF)) /* Only in 'blendfile' mode ... :/ */
/* TreeElement->flag */
#define TE_ACTIVE 1
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index b22e6595caf..96bab3d5c1e 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -38,6 +38,7 @@
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_group_types.h"
#include "DNA_key_types.h"
@@ -746,6 +747,16 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor
outliner_add_element(soops, &te->subtree, ca, te, TSE_ANIM_DATA, 0);
break;
}
+ case ID_CF:
+ {
+ CacheFile *cache_file = (CacheFile *)id;
+
+ if (outliner_animdata_test(cache_file->adt)) {
+ outliner_add_element(soops, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0);
+ }
+
+ break;
+ }
case ID_LA:
{
Lamp *la = (Lamp *)id;
diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c
index 86d3fcbe1ac..b33a26db728 100644
--- a/source/blender/editors/space_sequencer/sequencer_buttons.c
+++ b/source/blender/editors/space_sequencer/sequencer_buttons.c
@@ -94,7 +94,7 @@ void SEQUENCER_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "SEQUENCER_OT_properties";
- ot->description = "Open sequencer properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = sequencer_properties_toggle_exec;
ot->poll = ED_operator_sequencer_active;
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index adb7cf4940c..e1768e4aedc 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -471,7 +471,7 @@ static void draw_seq_handle(View2D *v2d, Sequence *seq, const float handsize_cla
}
/* draw info text on a sequence strip */
-static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float y1, float y2, const unsigned char background_col[3])
+static void draw_seq_text(View2D *v2d, SpaceSeq *sseq, Sequence *seq, float x1, float x2, float y1, float y2, const unsigned char background_col[3])
{
rctf rect;
char str[32 + FILE_MAX];
@@ -540,7 +540,12 @@ static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float
name, seq->len);
}
else if (seq->type == SEQ_TYPE_SOUND_RAM) {
- if (seq->sound) {
+ /* If a waveform is drawn, we don't want to overlay it with text,
+ * as it would make both hard to read. */
+ if ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM)) {
+ str[0] = 0;
+ str_len = 0;
+ } else if (seq->sound) {
str_len = BLI_snprintf(str, sizeof(str), "%s: %s | %d",
name, seq->sound->name, seq->len);
}
@@ -870,7 +875,7 @@ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, Scene *scene, AReg
/* nice text here would require changing the view matrix for texture text */
if ((x2 - x1) / pixelx > 32) {
- draw_seq_text(v2d, seq, x1, x2, y1, y2, background_col);
+ draw_seq_text(v2d, sseq, seq, x1, x2, y1, y2, background_col);
}
}
diff --git a/source/blender/editors/space_text/text_header.c b/source/blender/editors/space_text/text_header.c
index 91665f1a598..e06a5b5474e 100644
--- a/source/blender/editors/space_text/text_header.c
+++ b/source/blender/editors/space_text/text_header.c
@@ -92,7 +92,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Properties";
- ot->description = "Toggle text properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "TEXT_OT_properties";
/* api callbacks */
diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c
index fde955c24a7..021c4a54b0a 100644
--- a/source/blender/editors/space_time/space_time.c
+++ b/source/blender/editors/space_time/space_time.c
@@ -32,7 +32,10 @@
#include <string.h>
#include <stdio.h>
+#include "DNA_cachefile_types.h"
+#include "DNA_constraint_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -42,7 +45,10 @@
#include "BLI_dlrbTree.h"
#include "BLI_utildefines.h"
+#include "BKE_constraint.h"
#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_pointcache.h"
@@ -320,6 +326,9 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel)
case ID_GD:
gpencil_to_keylist(&ads, (bGPdata *)id, &keys);
break;
+ case ID_CF:
+ cachefile_to_keylist(&ads, (CacheFile *)id, &keys, NULL);
+ break;
}
/* build linked-list for searching */
@@ -344,6 +353,56 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel)
BLI_dlrbTree_free(&keys);
}
+static void time_draw_caches_keyframes(Main *bmain, Scene *scene, View2D *v2d, bool onlysel)
+{
+ CacheFile *cache_file;
+
+ for (cache_file = bmain->cachefiles.first;
+ cache_file;
+ cache_file = cache_file->id.next)
+ {
+ cache_file->draw_flag &= ~CACHEFILE_KEYFRAME_DRAWN;
+ }
+
+ for (Base *base = scene->base.first; base; base = base->next) {
+ Object *ob = base->object;
+
+ ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache);
+
+ if (md) {
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
+
+ cache_file = mcmd->cache_file;
+
+ if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) {
+ continue;
+ }
+
+ cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN;
+
+ time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel);
+ }
+
+ for (bConstraint *con = ob->constraints.first; con; con = con->next) {
+ if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) {
+ continue;
+ }
+
+ bTransformCacheConstraint *data = con->data;
+
+ cache_file = data->cache_file;
+
+ if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) {
+ continue;
+ }
+
+ cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN;
+
+ time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel);
+ }
+ }
+}
+
/* draw keyframe lines for timeline */
static void time_draw_keyframes(const bContext *C, ARegion *ar)
{
@@ -354,7 +413,11 @@ static void time_draw_keyframes(const bContext *C, ARegion *ar)
/* set this for all keyframe lines once and for all */
glLineWidth(1.0);
-
+
+ /* draw cache files keyframes (if available) */
+ UI_ThemeColor(TH_TIME_KEYFRAME);
+ time_draw_caches_keyframes(CTX_data_main(C), scene, v2d, onlysel);
+
/* draw grease pencil keyframes (if available) */
UI_ThemeColor(TH_TIME_GP_KEYFRAME);
if (scene->gpd) {
@@ -585,6 +648,10 @@ static void time_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa),
break;
}
break;
+ case NC_GPENCIL:
+ if (wmn->data == ND_DATA)
+ ED_region_tag_redraw(ar);
+ break;
}
}
diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c
index f5289a0d245..74a50497164 100644
--- a/source/blender/editors/space_view3d/drawmesh.c
+++ b/source/blender/editors/space_view3d/drawmesh.c
@@ -435,12 +435,16 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O
else {
/* draw with lights in the scene otherwise */
solidtex = false;
- if (v3d->flag2 & V3D_SHADELESS_TEX)
+ if (v3d->flag2 & V3D_SHADELESS_TEX) {
Gtexdraw.is_lit = 0;
- else
- Gtexdraw.is_lit = GPU_scene_object_lights(scene, ob, v3d->lay, rv3d->viewmat, !rv3d->is_persp);
+ }
+ else {
+ Gtexdraw.is_lit = GPU_scene_object_lights(
+ scene, ob, v3d->localvd ? v3d->localvd->lay : v3d->lay,
+ rv3d->viewmat, !rv3d->is_persp);
+ }
}
-
+
rgba_float_to_uchar(obcol, ob->col);
if (solidtex || v3d->drawtype == OB_TEXTURE) is_tex = true;
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 4320699b8d7..47390578573 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -4663,7 +4663,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3
index3_nors_incr = true;
}
else {
- if (!render_only || (render_only && BKE_displist_has_faces(lb))) {
+ if (!render_only || BKE_displist_has_faces(lb)) {
return drawDispListwire(lb, ob->type);
}
}
diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c
index e93d840eddd..8dbc2788744 100644
--- a/source/blender/editors/space_view3d/drawvolume.c
+++ b/source/blender/editors/space_view3d/drawvolume.c
@@ -379,11 +379,13 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
/* setup buffer and draw */
- int gl_depth = 0, gl_blend = 0;
+ int gl_depth = 0, gl_blend = 0, gl_depth_write = 0;
glGetBooleanv(GL_BLEND, (GLboolean *)&gl_blend);
glGetBooleanv(GL_DEPTH_TEST, (GLboolean *)&gl_depth);
+ glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&gl_depth_write);
glEnable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
@@ -422,6 +424,8 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
GPU_shader_unbind();
+ glDepthMask(gl_depth_write);
+
if (!gl_blend) {
glDisable(GL_BLEND);
}
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 96dda65b81d..075b1faf502 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1414,7 +1414,9 @@ static void view3d_id_remap(ScrArea *sa, SpaceLink *slink, ID *old_id, ID *new_i
if ((ID *)v3d->camera == old_id) {
v3d->camera = (Object *)new_id;
if (!new_id) {
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ /* 3D view might be inactive, in that case needs to use slink->regionbase */
+ ListBase *regionbase = (slink == sa->spacedata.first) ? &sa->regionbase : &slink->regionbase;
+ for (ar = regionbase->first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = is_local ? ((RegionView3D *)ar->regiondata)->localvd : ar->regiondata;
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index c52d1238163..b9c8c98b62f 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1206,7 +1206,7 @@ static int view3d_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
void VIEW3D_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
- ot->description = "Toggles the properties panel display";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "VIEW3D_OT_properties";
ot->exec = view3d_properties_toggle_exec;
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index b117a5e68d8..215d90a8878 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -4518,7 +4518,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
/* note: having key shortcut here is bad practice,
* but for now keep because this displays when dragging an image over the 3D viewport */
ot->name = "Add Background Image (Ctrl for Empty Object)";
- ot->description = "Add a new background image";
+ ot->description = "Add a new background image (Ctrl for Empty Object)";
ot->idname = "VIEW3D_OT_background_image_add";
/* api callbacks */
@@ -4527,7 +4527,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
ot->poll = ED_operator_view3d_active;
/* flags */
- ot->flag = 0;
+ ot->flag = OPTYPE_UNDO;
/* properties */
RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign");
diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c
index f46608b7d5e..37b068e3e49 100644
--- a/source/blender/editors/space_view3d/view3d_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_ruler.c
@@ -302,18 +302,18 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
bool changed = false;
if (scene->gpd == NULL) {
- scene->gpd = gpencil_data_addnew("GPencil");
+ scene->gpd = BKE_gpencil_data_addnew("GPencil");
}
gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
if (gpl == NULL) {
- gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false);
+ gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false);
gpl->thickness = 1;
gpl->flag |= GP_LAYER_HIDE;
}
- gpf = gpencil_layer_getframe(gpl, CFRA, true);
- free_gpencil_strokes(gpf);
+ gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
+ BKE_gpencil_free_strokes(gpf);
for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) {
bGPDspoint *pt;
@@ -327,6 +327,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
for (j = 0; j < 3; j++) {
copy_v3_v3(&pt->x, ruler_item->co[j]);
pt->pressure = 1.0f;
+ pt->strength = 1.0f;
pt++;
}
}
@@ -336,6 +337,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
for (j = 0; j < 3; j += 2) {
copy_v3_v3(&pt->x, ruler_item->co[j]);
pt->pressure = 1.0f;
+ pt->strength = 1.0f;
pt++;
}
}
@@ -358,7 +360,7 @@ static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info)
gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
if (gpl) {
bGPDframe *gpf;
- gpf = gpencil_layer_getframe(gpl, CFRA, false);
+ gpf = BKE_gpencil_layer_getframe(gpl, CFRA, false);
if (gpf) {
bGPDstroke *gps;
for (gps = gpf->strokes.first; gps; gps = gps->next) {
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index f18493e4b34..ef6cff19181 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -51,6 +51,7 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_ghash.h"
+#include "BLI_stackdefines.h"
#include "BLI_memarena.h"
#include "BKE_nla.h"
@@ -3719,19 +3720,10 @@ static void initRotation(TransInfo *t)
copy_v3_v3(t->axis_orig, t->axis);
}
-static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around)
+static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], const float *center)
{
float vec[3], totmat[3][3], smat[3][3];
float eul[3], fmat[3][3], quat[4];
- const float *center;
-
- /* local constraint shouldn't alter center */
- if (transdata_check_local_center(t, around)) {
- center = td->center;
- }
- else {
- center = t->center;
- }
if (t->flag & T_POINTS) {
mul_m3_m3m3(totmat, mat, td->mtx);
@@ -3941,6 +3933,21 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short
}
}
+static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around)
+{
+ const float *center;
+
+ /* local constraint shouldn't alter center */
+ if (transdata_check_local_center(t, around)) {
+ center = td->center;
+ }
+ else {
+ center = t->center;
+ }
+
+ ElementRotation_ex(t, td, mat, center);
+}
+
static void applyRotationValue(TransInfo *t, float angle, float axis[3])
{
TransData *td = t->data;
@@ -3990,10 +3997,8 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
applySnapping(t, &final);
- if (applyNumInput(&t->num, &final)) {
- /* Clamp between -PI and PI */
- final = angle_wrap_rad(final);
- }
+ /* Used to clamp final result in [-PI, PI[ range, no idea why, inheritance from 2.4x area, see T48998. */
+ applyNumInput(&t->num, &final);
t->values[0] = final;
@@ -4341,12 +4346,16 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
if (td->flag & TD_SKIP)
continue;
+ float rotate_offset[3] = {0};
+ bool use_rotate_offset = false;
+
/* handle snapping rotation before doing the translation */
if (usingSnappingNormal(t)) {
+ float mat[3][3];
+
if (validSnappingNormal(t)) {
const float *original_normal;
- float mat[3][3];
-
+
/* In pose mode, we want to align normals with Y axis of bones... */
if (t->flag & T_POSE)
original_normal = td->axismtx[1];
@@ -4354,18 +4363,19 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
original_normal = td->axismtx[2];
rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal);
-
- ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS);
}
else {
- float mat[3][3];
-
unit_m3(mat);
-
- ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS);
+ }
+
+ ElementRotation_ex(t, td, mat, t->tsnap.snapTarget);
+
+ if (td->loc) {
+ use_rotate_offset = true;
+ sub_v3_v3v3(rotate_offset, td->loc, td->iloc);
}
}
-
+
if (t->con.applyVec) {
float pvec[3];
t->con.applyVec(t, td, vec, tvec, pvec);
@@ -4373,6 +4383,10 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
else {
copy_v3_v3(tvec, vec);
}
+
+ if (use_rotate_offset) {
+ add_v3_v3(tvec, rotate_offset);
+ }
mul_m3_v3(td->smtx, tvec);
mul_v3_fl(tvec, td->factor);
@@ -4381,7 +4395,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
if (td->loc)
add_v3_v3v3(td->loc, td->iloc, tvec);
-
+
constraintTransLim(t, td);
}
}
@@ -5893,7 +5907,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
*/
static void calcEdgeSlide_mval_range(
TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr,
- const float mval[2], const bool use_btree_disp, const bool use_calc_direction)
+ const float mval[2], const bool use_occlude_geometry, const bool use_calc_direction)
{
TransDataEdgeSlideVert *sv_array = sld->sv;
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
@@ -5902,7 +5916,7 @@ static void calcEdgeSlide_mval_range(
View3D *v3d = NULL;
RegionView3D *rv3d = NULL;
float projectMat[4][4];
- BMBVHTree *btree;
+ BMBVHTree *bmbvh;
/* only for use_calc_direction */
float (*loop_dir)[3] = NULL, *loop_maxdist = NULL;
@@ -5926,11 +5940,11 @@ static void calcEdgeSlide_mval_range(
ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat);
}
- if (use_btree_disp) {
- btree = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false);
+ if (use_occlude_geometry) {
+ bmbvh = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false);
}
else {
- btree = NULL;
+ bmbvh = NULL;
}
/* find mouse vectors, the global one, and one per loop in case we have
@@ -5965,7 +5979,7 @@ static void calcEdgeSlide_mval_range(
continue;
/* This test is only relevant if object is not wire-drawn! See [#32068]. */
- if (use_btree_disp && !BMBVH_EdgeVisible(btree, e_other, ar, v3d, t->obedit)) {
+ if (use_occlude_geometry && !BMBVH_EdgeVisible(bmbvh, e_other, ar, v3d, t->obedit)) {
continue;
}
@@ -6046,8 +6060,8 @@ static void calcEdgeSlide_mval_range(
sld->mval_end[0] = t->mval[0] + mval_end[0];
sld->mval_end[1] = t->mval[1] + mval_end[1];
- if (btree) {
- BKE_bmbvh_free(btree);
+ if (bmbvh) {
+ BKE_bmbvh_free(bmbvh);
}
}
@@ -6109,8 +6123,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
int *sv_table; /* BMVert -> sv_array index */
EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
- int numsel, i, j, loop_nr;
- bool use_btree_disp = false;
+ int numsel, i, loop_nr;
+ bool use_occlude_geometry = false;
View3D *v3d = NULL;
RegionView3D *rv3d = NULL;
@@ -6157,31 +6171,39 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
- j = 0;
- BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- BM_elem_flag_enable(v, BM_ELEM_TAG);
- sv_table[i] = j;
- j += 1;
- }
- else {
- BM_elem_flag_disable(v, BM_ELEM_TAG);
- sv_table[i] = -1;
+#define INDEX_UNSET -1
+#define INDEX_INVALID -2
+
+ {
+ int j = 0;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ sv_table[i] = INDEX_UNSET;
+ j += 1;
+ }
+ else {
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+ sv_table[i] = INDEX_INVALID;
+ }
+ BM_elem_index_set(v, i); /* set_inline */
}
- BM_elem_index_set(v, i); /* set_inline */
- }
- bm->elem_index_dirty &= ~BM_VERT;
+ bm->elem_index_dirty &= ~BM_VERT;
- if (!j) {
- MEM_freeN(sld);
- MEM_freeN(sv_table);
- return false;
+ if (!j) {
+ MEM_freeN(sld);
+ MEM_freeN(sv_table);
+ return false;
+ }
+ sv_tot = j;
}
- sv_tot = j;
sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array");
loop_nr = 0;
+ STACK_DECLARE(sv_array);
+ STACK_INIT(sv_array, sv_tot);
+
while (1) {
float vec_a[3], vec_b[3];
BMLoop *l_a, *l_b;
@@ -6274,6 +6296,11 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
l_a_prev = NULL;
l_b_prev = NULL;
+#define SV_FROM_VERT(v) ( \
+ (sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \
+ ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), STACK_PUSH_RET_PTR(sv_array)) : \
+ (&sv_array[sv_table[BM_elem_index_get(v)]]))
+
/*iterate over the loop*/
v_first = v;
do {
@@ -6285,8 +6312,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
/* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */
BLI_assert(v != NULL);
- BLI_assert(sv_table[BM_elem_index_get(v)] != -1);
- sv = &sv_array[sv_table[BM_elem_index_get(v)]];
+ BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
+ sv = SV_FROM_VERT(v);
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
sv->loop_nr = loop_nr;
@@ -6311,8 +6338,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
if (!e) {
BLI_assert(v != NULL);
- BLI_assert(sv_table[BM_elem_index_get(v)] != -1);
- sv = &sv_array[sv_table[BM_elem_index_get(v)]];
+
+ BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
+ sv = SV_FROM_VERT(v);
+
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
sv->loop_nr = loop_nr;
@@ -6401,6 +6430,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
} while ((e != v_first->e) && (l_a || l_b));
+#undef SV_FROM_VERT
+#undef INDEX_UNSET
+#undef INDEX_INVALID
+
loop_nr++;
#undef EDGESLIDE_VERT_IS_INNER
@@ -6408,6 +6441,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
/* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
+ BLI_assert(STACK_SIZE(sv_array) == sv_tot);
+
sld->sv = sv_array;
sld->totsv = sv_tot;
@@ -6415,10 +6450,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
if (t->spacetype == SPACE_VIEW3D) {
v3d = t->sa ? t->sa->spacedata.first : NULL;
rv3d = t->ar ? t->ar->regiondata : NULL;
- use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
+ use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
}
- calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, true);
+ calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, true);
/* create copies of faces for customdata projection */
bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
@@ -6456,7 +6491,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f
EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
int loop_nr;
- bool use_btree_disp = false;
+ bool use_occlude_geometry = false;
View3D *v3d = NULL;
RegionView3D *rv3d = NULL;
@@ -6503,6 +6538,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f
bm->elem_index_dirty &= ~BM_VERT;
if (!j) {
+ MEM_freeN(sld);
return false;
}
@@ -6617,10 +6653,10 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f
if (t->spacetype == SPACE_VIEW3D) {
v3d = t->sa ? t->sa->spacedata.first : NULL;
rv3d = t->ar ? t->ar->regiondata : NULL;
- use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
+ use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
}
- calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false);
+ calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, false);
/* create copies of faces for customdata projection */
bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 50168e78dda..a59f9dc43dd 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -87,7 +87,6 @@ typedef struct TransSnap {
float snapPoint[3]; /* snapping from this point */
float snapTarget[3]; /* to this point */
float snapNormal[3];
- float snapTangent[3];
char snapNodeBorder;
ListBase points;
TransSnapPoint *selectedPoint;
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index b7456facbdf..9c266890d6d 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -701,7 +701,6 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
bPoseChannel *pchan;
Bone *bone;
int mode = *out_mode;
- int hastranslation = 0;
int total = 0;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
@@ -729,7 +728,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
}
}
/* now count, and check if we have autoIK or have to switch from translate to rotate */
- hastranslation = 0;
+ bool has_translation = false, has_rotation = false;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
bone = pchan->bone;
@@ -740,20 +739,29 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
if (has_targetless_ik(pchan) == NULL) {
if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) {
if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM)
- hastranslation = 1;
+ has_translation = true;
}
- else if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC)
- hastranslation = 1;
+ else {
+ if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC)
+ has_translation = true;
+ }
+ if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT)
+ has_rotation = true;
}
else
- hastranslation = 1;
+ has_translation = true;
}
}
}
/* if there are no translatable bones, do rotation */
- if (mode == TFM_TRANSLATION && !hastranslation) {
- *out_mode = TFM_ROTATION;
+ if (mode == TFM_TRANSLATION && !has_translation) {
+ if (has_rotation) {
+ *out_mode = TFM_ROTATION;
+ }
+ else {
+ *out_mode = TFM_RESIZE;
+ }
}
return total;
@@ -3284,7 +3292,7 @@ static void posttrans_gpd_clean(bGPdata *gpd)
for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
gpfn = gpf->next;
if (gpfn && gpf->framenum == gpfn->framenum) {
- gpencil_layer_delframe(gpl, gpf);
+ BKE_gpencil_layer_delframe(gpl, gpf);
}
}
}
@@ -7679,7 +7687,11 @@ static void createTransGPencil(bContext *C, TransInfo *t)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
-
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
if (is_prop_edit) {
/* Proportional Editing... */
if (is_prop_edit_connected) {
@@ -7727,14 +7739,27 @@ static void createTransGPencil(bContext *C, TransInfo *t)
if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf = gpl->actframe;
bGPDstroke *gps;
+ float diff_mat[4][4];
+ float inverse_diff_mat[4][4];
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ /* undo matrix */
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+ }
- /* Make a new frame to work on if the layer's frame and the current scene frame don't match up
+ /* Make a new frame to work on if the layer's frame and the current scene frame don't match up
* - This is useful when animating as it saves that "uh-oh" moment when you realize you've
* spent too much time editing the wrong frame...
*/
// XXX: should this be allowed when framelock is enabled?
if (gpf->framenum != cfra) {
- gpf = gpencil_frame_addcopy(gpl, cfra);
+ gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
+ /* in some weird situations (framelock enabled) return NULL */
+ if (gpf == NULL) {
+ continue;
+ }
}
/* Loop over strokes, adding TransData for points as needed... */
@@ -7747,7 +7772,10 @@ static void createTransGPencil(bContext *C, TransInfo *t)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
-
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
/* What we need to include depends on proportional editing settings... */
if (is_prop_edit) {
if (is_prop_edit_connected) {
@@ -7816,9 +7844,18 @@ static void createTransGPencil(bContext *C, TransInfo *t)
/* screenspace */
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
- copy_m3_m4(td->smtx, t->persmat);
- copy_m3_m4(td->mtx, t->persinv);
- unit_m3(td->axismtx);
+ /* apply parent transformations */
+ if (gpl->parent == NULL) {
+ copy_m3_m4(td->smtx, t->persmat);
+ copy_m3_m4(td->mtx, t->persinv);
+ unit_m3(td->axismtx);
+ }
+ else {
+ /* apply matrix transformation relative to parent */
+ copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */
+ copy_m3_m4(td->mtx, diff_mat); /* display position */
+ copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */
+ }
}
else {
/* configure 2D dataspace points so that they don't play up... */
@@ -7827,9 +7864,18 @@ static void createTransGPencil(bContext *C, TransInfo *t)
// XXX: matrices may need to be different?
}
- copy_m3_m3(td->smtx, smtx);
- copy_m3_m3(td->mtx, mtx);
- unit_m3(td->axismtx); // XXX?
+ /* apply parent transformations */
+ if (gpl->parent == NULL) {
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+ unit_m3(td->axismtx); // XXX?
+ }
+ else {
+ /* apply matrix transformation relative to parent */
+ copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */
+ copy_m3_m4(td->mtx, diff_mat); /* display position */
+ copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */
+ }
}
/* Triangulation must be calculated again, so save the stroke for recalc function */
td->extra = gps;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 728b10f5e6f..f78a23be7b8 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -975,7 +975,9 @@ static void recalcData_gpencil_strokes(TransInfo *t)
TransData *td = t->data;
for (int i = 0; i < t->total; i++, td++) {
bGPDstroke *gps = td->extra;
- gps->flag |= GP_STROKE_RECALC_CACHES;
+ if (gps != NULL) {
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ }
}
}
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c
index 309ad22e31c..075f311db72 100644
--- a/source/blender/editors/transform/transform_manipulator.c
+++ b/source/blender/editors/transform/transform_manipulator.c
@@ -58,6 +58,7 @@
#include "BKE_pointcache.h"
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
+#include "BKE_gpencil.h"
#include "BIF_gl.h"
@@ -68,6 +69,7 @@
#include "ED_curve.h"
#include "ED_particle.h"
#include "ED_view3d.h"
+#include "ED_gpencil.h"
#include "UI_resources.h"
@@ -288,24 +290,49 @@ static int calc_manipulator_stats(const bContext *C)
zero_v3(scene->twcent);
if (is_gp_edit) {
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- /* we're only interested in selected points here... */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
-
- /* Change selection status of all points, then make the stroke match */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- calc_tw_center(scene, &pt->x);
- totsel++;
+ float diff_mat[4][4];
+ float fpt[3];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
+ for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+
+ /* we're only interested in selected points here... */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+
+ /* Change selection status of all points, then make the stroke match */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (gpl->parent == NULL) {
+ calc_tw_center(scene, &pt->x);
+ totsel++;
+ }
+ else {
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ calc_tw_center(scene, fpt);
+ totsel++;
+ }
+ }
+ }
}
}
}
}
- CTX_DATA_END;
-
+
+
/* selection center */
if (totsel) {
mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 0bb64315845..f8bb124e943 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -984,15 +984,6 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
}
if (found == true) {
- float tangent[3];
-
- sub_v2_v2v2(tangent, loc, t->tsnap.snapPoint);
- tangent[2] = 0.0f;
-
- if (!is_zero_v3(tangent)) {
- copy_v3_v3(t->tsnap.snapTangent, tangent);
- }
-
copy_v3_v3(t->tsnap.snapPoint, loc);
copy_v3_v3(t->tsnap.snapNormal, no);
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
index ea5a55731c3..1f5e2b63bfa 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
@@ -807,6 +807,7 @@ void BlenderFileLoader::insertShapeNode(ObjectInstanceRen *obi, int id)
// sets the id of the rep
rep->setId(Id(id, 0));
rep->setName(obi->ob->id.name + 2);
+ rep->setLibraryPath(obi->ob->id.lib ? obi->ob->id.lib->name : NULL);
const BBox<Vec3r> bbox = BBox<Vec3r>(Vec3r(ls.minBBox[0], ls.minBBox[1], ls.minBBox[2]),
Vec3r(ls.maxBBox[0], ls.maxBBox[1], ls.maxBBox[2]));
diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
index 253bf278478..f2f53159fcf 100644
--- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
+++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp
@@ -296,6 +296,19 @@ static PyObject *ViewShape_name_get(BPy_ViewShape *self, void *UNUSED(closure))
return PyUnicode_FromString(self->vs->getName());
}
+PyDoc_STRVAR(ViewShape_library_path_doc,
+"The library path of the ViewShape.\n"
+"\n"
+":type: str, or None if the ViewShape is not part of a library");
+
+static PyObject *ViewShape_library_path_get(BPy_ViewShape *self, void *UNUSED(closure))
+{
+ const char *name = self->vs->getLibraryPath();
+ if (!name)
+ Py_RETURN_NONE;
+ return PyUnicode_FromString(name);
+}
+
PyDoc_STRVAR(ViewShape_id_doc,
"The Id of this ViewShape.\n"
"\n"
@@ -313,6 +326,7 @@ static PyGetSetDef BPy_ViewShape_getseters[] = {
(char *)ViewShape_vertices_doc, NULL},
{(char *)"edges", (getter)ViewShape_edges_get, (setter)ViewShape_edges_set, (char *)ViewShape_edges_doc, NULL},
{(char *)"name", (getter)ViewShape_name_get, (setter)NULL, (char *)ViewShape_name_doc, NULL},
+ {(char *)"library_path", (getter)ViewShape_library_path_get, (setter)NULL, (char *)ViewShape_library_path_doc, NULL},
{(char *)"id", (getter)ViewShape_id_get, (setter)NULL, (char *)ViewShape_id_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
diff --git a/source/blender/freestyle/intern/scene_graph/Rep.h b/source/blender/freestyle/intern/scene_graph/Rep.h
index c5036cdb153..773eb2d3278 100644
--- a/source/blender/freestyle/intern/scene_graph/Rep.h
+++ b/source/blender/freestyle/intern/scene_graph/Rep.h
@@ -48,6 +48,8 @@ public:
inline Rep() : BaseObject()
{
_Id = 0;
+ _Name = 0;
+ _LibraryPath = 0;
_FrsMaterial = 0;
}
@@ -55,6 +57,7 @@ public:
{
_Id = iBrother._Id;
_Name = iBrother._Name;
+ _LibraryPath = iBrother._LibraryPath;
if (0 == iBrother._FrsMaterial)
_FrsMaterial = 0;
else
@@ -68,6 +71,7 @@ public:
std::swap(_BBox, ioOther._BBox);
std::swap(_Id, ioOther._Id);
std::swap(_Name, ioOther._Name);
+ std::swap(_LibraryPath, ioOther._LibraryPath);
std::swap(_FrsMaterial, ioOther._FrsMaterial);
}
@@ -76,6 +80,7 @@ public:
if (&iBrother != this) {
_Id = iBrother._Id;
_Name = iBrother._Name;
+ _LibraryPath = iBrother._LibraryPath;
if (0 == iBrother._FrsMaterial) {
_FrsMaterial = 0;
}
@@ -132,6 +137,11 @@ public:
return _Name;
}
+ inline const char *getLibraryPath() const
+ {
+ return _LibraryPath;
+ }
+
inline const FrsMaterial *frs_material() const
{
return _FrsMaterial;
@@ -153,7 +163,12 @@ public:
_Name = name;
}
- inline void setFrsMaterial(const FrsMaterial& iMaterial)
+ inline void setLibraryPath(const char *path)
+ {
+ _LibraryPath = path;
+ }
+
+ inline void setFrsMaterial(const FrsMaterial& iMaterial)
{
_FrsMaterial = new FrsMaterial(iMaterial);
}
@@ -162,6 +177,7 @@ private:
BBox<Vec3f> _BBox;
Id _Id;
const char *_Name;
+ const char *_LibraryPath;
FrsMaterial *_FrsMaterial;
};
diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.cpp b/source/blender/freestyle/intern/view_map/BoxGrid.cpp
index ae22a26ec9b..34fa2b14379 100644
--- a/source/blender/freestyle/intern/view_map/BoxGrid.cpp
+++ b/source/blender/freestyle/intern/view_map/BoxGrid.cpp
@@ -77,11 +77,11 @@ BoxGrid::Iterator::Iterator (BoxGrid& grid, Vec3r& center, real /*epsilon*/)
// Find target cell
_cell = grid.findCell(_target);
#if BOX_GRID_LOGGING
- if (G.debug & G_DEBUG_FREESTYLE) {
- cout << "Searching for occluders of edge centered at " << _target << " in cell [" <<
- 1_cell->boundary[0] << ", " << _cell->boundary[1] << ", " << _cell->boundary[2] <<
- ", " << _cell->boundary[3] << "] (" << _cell->faces.size() << " occluders)" << endl;
- }
+ if (G.debug & G_DEBUG_FREESTYLE) {
+ cout << "Searching for occluders of edge centered at " << _target << " in cell [" <<
+ 1_cell->boundary[0] << ", " << _cell->boundary[1] << ", " << _cell->boundary[2] <<
+ ", " << _cell->boundary[3] << "] (" << _cell->faces.size() << " occluders)" << endl;
+ }
#endif
// Set iterator
diff --git a/source/blender/freestyle/intern/view_map/Silhouette.h b/source/blender/freestyle/intern/view_map/Silhouette.h
index b9924e6ad95..9d373107bfa 100644
--- a/source/blender/freestyle/intern/view_map/Silhouette.h
+++ b/source/blender/freestyle/intern/view_map/Silhouette.h
@@ -1416,6 +1416,7 @@ private:
vector<FEdge*> _edgesList; // list of all edges
Id _Id;
const char *_Name;
+ const char *_LibraryPath;
BBox<Vec3r> _BBox;
vector<FrsMaterial> _FrsMaterials;
@@ -1436,6 +1437,7 @@ public:
_importance = 0.0f;
_ViewShape = NULL;
_Name = NULL;
+ _LibraryPath = NULL;
}
/*! Copy constructor */
@@ -1444,6 +1446,7 @@ public:
userdata = NULL;
_Id = iBrother._Id;
_Name = iBrother._Name;
+ _LibraryPath = iBrother._LibraryPath;
_BBox = iBrother.bbox();
_FrsMaterials = iBrother._FrsMaterials;
_importance = iBrother._importance;
@@ -1893,6 +1896,12 @@ public:
return _Name;
}
+ /*! Returns the library path of the Shape. */
+ inline const char *getLibraryPath() const
+ {
+ return _LibraryPath;
+ }
+
/* Modififers */
/*! Sets the Id of the shape.*/
inline void setId(Id id)
@@ -1906,6 +1915,12 @@ public:
_Name = name;
}
+ /*! Sets the library path of the shape.*/
+ inline void setLibraryPath(const char *path)
+ {
+ _LibraryPath = path;
+ }
+
/*! Sets the list of materials for the shape */
inline void setFrsMaterials(const vector<FrsMaterial>& iMaterials)
{
diff --git a/source/blender/freestyle/intern/view_map/ViewMap.h b/source/blender/freestyle/intern/view_map/ViewMap.h
index 74297e1dbfd..8b73c8aac3a 100644
--- a/source/blender/freestyle/intern/view_map/ViewMap.h
+++ b/source/blender/freestyle/intern/view_map/ViewMap.h
@@ -1565,12 +1565,18 @@ public:
return _SShape->getId();
}
- /*! Returns the ViewShape id. */
+ /*! Returns the ViewShape name. */
inline const char *getName() const
{
return _SShape->getName();
}
+ /*! Returns the ViewShape library path. */
+ inline const char *getLibraryPath() const
+ {
+ return _SShape->getLibraryPath();
+ }
+
/* modifiers */
/*! Sets the SShape on top of which the ViewShape is built. */
inline void setSShape(SShape *iSShape)
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index 9ca021475b2..77beb1d97d9 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -1204,6 +1204,7 @@ void ViewMapBuilder::computeInitialViewEdges(WingedEdge& we)
psShape = new SShape;
psShape->setId((*it)->GetId());
psShape->setName((*it)->getName());
+ psShape->setLibraryPath((*it)->getLibraryPath());
psShape->setFrsMaterials((*it)->frs_materials()); // FIXME
// create the view shape
diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.cpp b/source/blender/freestyle/intern/winged_edge/WEdge.cpp
index 99aa2d22239..7bec5ba1d6e 100644
--- a/source/blender/freestyle/intern/winged_edge/WEdge.cpp
+++ b/source/blender/freestyle/intern/winged_edge/WEdge.cpp
@@ -471,6 +471,7 @@ WShape::WShape(WShape& iBrother)
{
_Id = iBrother.GetId();
_Name = iBrother._Name;
+ _LibraryPath = iBrother._LibraryPath;
_FrsMaterials = iBrother._FrsMaterials;
#if 0
_meanEdgeSize = iBrother._meanEdgeSize;
diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.h b/source/blender/freestyle/intern/winged_edge/WEdge.h
index 8001342775b..14109fba843 100644
--- a/source/blender/freestyle/intern/winged_edge/WEdge.h
+++ b/source/blender/freestyle/intern/winged_edge/WEdge.h
@@ -1025,6 +1025,7 @@ protected:
vector<WFace *> _FaceList;
int _Id;
const char *_Name;
+ const char *_LibraryPath;
static unsigned _SceneCurrentId;
#if 0
Vec3f _min;
@@ -1043,6 +1044,8 @@ public:
#endif
_Id = _SceneCurrentId;
_SceneCurrentId++;
+ _Name = 0;
+ _LibraryPath = 0;
}
/*! copy constructor */
@@ -1127,6 +1130,11 @@ public:
return _Name;
}
+ inline const char *getLibraryPath() const
+ {
+ return _LibraryPath;
+ }
+
/*! modifiers */
static inline void setCurrentId(const unsigned id)
{
@@ -1176,6 +1184,11 @@ public:
_Name = name;
}
+ inline void setLibraryPath(const char *path)
+ {
+ _LibraryPath = path;
+ }
+
/*! designed to build a specialized WFace for use in MakeFace */
virtual WFace *instanciateFace() const
{
diff --git a/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp b/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp
index 78773a9680d..dfdeedef2e1 100644
--- a/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp
+++ b/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp
@@ -42,6 +42,7 @@ void WXEdgeBuilder::visitIndexedFaceSet(IndexedFaceSet& ifs)
}
shape->setId(ifs.getId().getFirst());
shape->setName(ifs.getName());
+ shape->setLibraryPath(ifs.getLibraryPath());
//ifs.setId(shape->GetId());
}
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index aec94f9f2cb..c40bc8eaf27 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -1732,3 +1732,9 @@ void GPU_pass_free(GPUPass *pass)
MEM_freeN(pass->vertexcode);
MEM_freeN(pass);
}
+
+void GPU_pass_free_nodes(ListBase *nodes)
+{
+ gpu_nodes_free(nodes);
+}
+
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index 417e46a83ee..7af17f9122d 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -183,6 +183,7 @@ void GPU_pass_update_uniforms(GPUPass *pass);
void GPU_pass_unbind(GPUPass *pass);
void GPU_pass_free(GPUPass *pass);
+void GPU_pass_free_nodes(ListBase *nodes);
void gpu_codegen_init(void);
void gpu_codegen_exit(void);
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 4775d2ed30a..ed9718f1a17 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -270,6 +270,9 @@ static int GPU_material_construct_end(GPUMaterial *material, const char *passnam
material->partangvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_ANG_VELOCITY));
return 1;
}
+ else {
+ GPU_pass_free_nodes(&material->nodes);
+ }
return 0;
}
diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c
index 827c52c9a5f..54f0003c086 100644
--- a/source/blender/gpu/intern/gpu_texture.c
+++ b/source/blender/gpu/intern/gpu_texture.c
@@ -55,7 +55,8 @@ struct GPUTexture {
int number; /* number for multitexture binding */
int refcount; /* reference count */
GLenum target; /* GL_TEXTURE_* */
- GLenum target_base; /* same as target, (but no multisample) */
+ GLenum target_base; /* same as target, (but no multisample)
+ * use it for unbinding */
GLuint bindcode; /* opengl identifier for texture */
int fromblender; /* we got the texture from Blender */
@@ -374,6 +375,9 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget
GLint bindcode = GPU_verify_image(ima, iuser, textarget, 0, 0, mipmap, is_data);
GPU_update_image_time(ima, time);
+ /* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP
+ * these values are correct for glDisable, so textarget can be safely used in
+ * GPU_texture_bind/GPU_texture_unbind through tex->target_base */
if (textarget == GL_TEXTURE_2D)
gputt = TEXTARGET_TEXTURE_2D;
else
@@ -390,7 +394,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget
tex->number = -1;
tex->refcount = 1;
tex->target = textarget;
- tex->target_base = GL_TEXTURE_2D;
+ tex->target_base = textarget;
tex->fromblender = 1;
ima->gputexture[gputt] = tex;
@@ -626,11 +630,11 @@ void GPU_texture_bind(GPUTexture *tex, int number)
GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number);
if (number != 0) glActiveTexture(arbnumber);
if (tex->bindcode != 0) {
- glBindTexture(tex->target, tex->bindcode);
+ glBindTexture(tex->target_base, tex->bindcode);
}
else
- GPU_invalid_tex_bind(tex->target);
- glEnable(tex->target);
+ GPU_invalid_tex_bind(tex->target_base);
+ glEnable(tex->target_base);
if (number != 0) glActiveTexture(GL_TEXTURE0);
tex->number = number;
@@ -652,8 +656,6 @@ void GPU_texture_unbind(GPUTexture *tex)
GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number);
if (tex->number != 0) glActiveTexture(arbnumber);
- glBindTexture(tex->target, 0);
- glDisable(tex->target);
glBindTexture(tex->target_base, 0);
glDisable(tex->target_base);
if (tex->number != 0) glActiveTexture(GL_TEXTURE0);
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index bb5d2b4cf29..5c1bfc229da 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -213,43 +213,50 @@ typedef struct PreviewImage {
* Written to #BHead.code (for file IO)
* and the first 2 bytes of #ID.name (for runtime checks, see #GS macro).
*/
-#define ID_SCE MAKE_ID2('S', 'C') /* Scene */
-#define ID_LI MAKE_ID2('L', 'I') /* Library */
-#define ID_OB MAKE_ID2('O', 'B') /* Object */
-#define ID_ME MAKE_ID2('M', 'E') /* Mesh */
-#define ID_CU MAKE_ID2('C', 'U') /* Curve */
-#define ID_MB MAKE_ID2('M', 'B') /* MetaBall */
-#define ID_MA MAKE_ID2('M', 'A') /* Material */
-#define ID_TE MAKE_ID2('T', 'E') /* Tex (Texture) */
-#define ID_IM MAKE_ID2('I', 'M') /* Image */
-#define ID_LT MAKE_ID2('L', 'T') /* Lattice */
-#define ID_LA MAKE_ID2('L', 'A') /* Lamp */
-#define ID_CA MAKE_ID2('C', 'A') /* Camera */
-#define ID_IP MAKE_ID2('I', 'P') /* Ipo (depreciated, replaced by FCurves) */
-#define ID_KE MAKE_ID2('K', 'E') /* Key (shape key) */
-#define ID_WO MAKE_ID2('W', 'O') /* World */
-#define ID_SCR MAKE_ID2('S', 'R') /* Screen */
-#define ID_SCRN MAKE_ID2('S', 'N') /* (depreciated?) */
-#define ID_VF MAKE_ID2('V', 'F') /* VFont (Vector Font) */
-#define ID_TXT MAKE_ID2('T', 'X') /* Text */
-#define ID_SPK MAKE_ID2('S', 'K') /* Speaker */
-#define ID_SO MAKE_ID2('S', 'O') /* Sound */
-#define ID_GR MAKE_ID2('G', 'R') /* Group */
-#define ID_ID MAKE_ID2('I', 'D') /* (internal use only) */
-#define ID_AR MAKE_ID2('A', 'R') /* bArmature */
-#define ID_AC MAKE_ID2('A', 'C') /* bAction */
-#define ID_NT MAKE_ID2('N', 'T') /* bNodeTree */
-#define ID_BR MAKE_ID2('B', 'R') /* Brush */
-#define ID_PA MAKE_ID2('P', 'A') /* ParticleSettings */
-#define ID_GD MAKE_ID2('G', 'D') /* bGPdata, (Grease Pencil) */
-#define ID_WM MAKE_ID2('W', 'M') /* WindowManager */
-#define ID_MC MAKE_ID2('M', 'C') /* MovieClip */
-#define ID_MSK MAKE_ID2('M', 'S') /* Mask */
-#define ID_LS MAKE_ID2('L', 'S') /* FreestyleLineStyle */
-#define ID_PAL MAKE_ID2('P', 'L') /* Palette */
-#define ID_PC MAKE_ID2('P', 'C') /* PaintCurve */
-
- /* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */
+typedef enum ID_Type {
+ ID_SCE = MAKE_ID2('S', 'C'), /* Scene */
+ ID_LI = MAKE_ID2('L', 'I'), /* Library */
+ ID_OB = MAKE_ID2('O', 'B'), /* Object */
+ ID_ME = MAKE_ID2('M', 'E'), /* Mesh */
+ ID_CU = MAKE_ID2('C', 'U'), /* Curve */
+ ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */
+ ID_MA = MAKE_ID2('M', 'A'), /* Material */
+ ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */
+ ID_IM = MAKE_ID2('I', 'M'), /* Image */
+ ID_LT = MAKE_ID2('L', 'T'), /* Lattice */
+ ID_LA = MAKE_ID2('L', 'A'), /* Lamp */
+ ID_CA = MAKE_ID2('C', 'A'), /* Camera */
+ ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */
+ ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */
+ ID_WO = MAKE_ID2('W', 'O'), /* World */
+ ID_SCR = MAKE_ID2('S', 'R'), /* Screen */
+ ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */
+ ID_TXT = MAKE_ID2('T', 'X'), /* Text */
+ ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */
+ ID_SO = MAKE_ID2('S', 'O'), /* Sound */
+ ID_GR = MAKE_ID2('G', 'R'), /* Group */
+ ID_AR = MAKE_ID2('A', 'R'), /* bArmature */
+ ID_AC = MAKE_ID2('A', 'C'), /* bAction */
+ ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */
+ ID_BR = MAKE_ID2('B', 'R'), /* Brush */
+ ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */
+ ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */
+ ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */
+ ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */
+ ID_MSK = MAKE_ID2('M', 'S'), /* Mask */
+ ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */
+ ID_PAL = MAKE_ID2('P', 'L'), /* Palette */
+ ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */
+ ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */
+} ID_Type;
+
+/* Only used as 'placeholder' in .blend files for directly linked datablocks. */
+#define ID_ID MAKE_ID2('I', 'D') /* (internal use only) */
+
+/* Deprecated. */
+#define ID_SCRN MAKE_ID2('S', 'N')
+
+/* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */
#define ID_SEQ MAKE_ID2('S', 'Q')
/* constraint */
#define ID_CO MAKE_ID2('C', 'O')
@@ -371,6 +378,47 @@ enum {
FILTER_ID_VF = (1 << 25),
FILTER_ID_WO = (1 << 26),
FILTER_ID_PA = (1 << 27),
+ FILTER_ID_CF = (1 << 28),
+};
+
+/* IMPORTANT: this enum matches the order currently use in set_lisbasepointers,
+ * keep them in sync! */
+enum {
+ INDEX_ID_LI = 0,
+ INDEX_ID_IP,
+ INDEX_ID_AC,
+ INDEX_ID_KE,
+ INDEX_ID_GD,
+ INDEX_ID_NT,
+ INDEX_ID_IM,
+ INDEX_ID_TE,
+ INDEX_ID_MA,
+ INDEX_ID_VF,
+ INDEX_ID_AR,
+ INDEX_ID_CF,
+ INDEX_ID_ME,
+ INDEX_ID_CU,
+ INDEX_ID_MB,
+ INDEX_ID_LT,
+ INDEX_ID_LA,
+ INDEX_ID_CA,
+ INDEX_ID_TXT,
+ INDEX_ID_SO,
+ INDEX_ID_GR,
+ INDEX_ID_PAL,
+ INDEX_ID_PC,
+ INDEX_ID_BR,
+ INDEX_ID_PA,
+ INDEX_ID_SPK,
+ INDEX_ID_WO,
+ INDEX_ID_MC,
+ INDEX_ID_SCR,
+ INDEX_ID_OB,
+ INDEX_ID_LS,
+ INDEX_ID_SCE,
+ INDEX_ID_WM,
+ INDEX_ID_MSK,
+ INDEX_ID_NULL,
};
#ifdef __cplusplus
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 9a19606d6c8..f3df9090d41 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -694,7 +694,9 @@ typedef enum eAnimEdit_Context {
/* dopesheet (default) */
SACTCONT_DOPESHEET = 3,
/* mask */
- SACTCONT_MASK = 4
+ SACTCONT_MASK = 4,
+ /* cache file */
+ SACTCONT_CACHEFILE = 5,
} eAnimEdit_Context;
/* SpaceAction AutoSnap Settings (also used by other Animation Editors) */
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 6bd7b3a4999..31fe8fe563e 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -450,7 +450,9 @@ typedef enum eDriver_Flags {
/* the names are cached so they don't need have python unicode versions created each time */
DRIVER_FLAG_RENAMEVAR = (1<<4),
/* intermediate values of driver should be shown in the UI for debugging purposes */
- DRIVER_FLAG_SHOWDEBUG = (1<<5)
+ DRIVER_FLAG_SHOWDEBUG = (1<<5),
+ /* include 'self' in the drivers namespace. */
+ DRIVER_FLAG_USE_SELF = (1<<6),
} eDriver_Flags;
/* F-Curves -------------------------------------- */
diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h
new file mode 100644
index 00000000000..1cda0233aa8
--- /dev/null
+++ b/source/blender/makesdna/DNA_cachefile_types.h
@@ -0,0 +1,84 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_cachefile_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_CACHEFILE_TYPES_H__
+#define __DNA_CACHEFILE_TYPES_H__
+
+#include "DNA_ID.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* CacheFile::flag */
+enum {
+ CACHEFILE_DS_EXPAND = (1 << 0),
+};
+
+/* CacheFile::draw_flag */
+enum {
+ CACHEFILE_KEYFRAME_DRAWN = (1 << 0),
+};
+
+typedef struct AlembicObjectPath {
+ struct AlembicObjectPath *next, *prev;
+
+ char path[1024]; /* 1024 = FILE_MAX, might use PATH_MAX in the future. */
+} AlembicObjectPath;
+
+typedef struct CacheFile {
+ ID id;
+ struct AnimData *adt;
+
+ struct AbcArchiveHandle *handle;
+
+ /* Paths of the objects inside of the Alembic archive referenced by this
+ * CacheFile. */
+ ListBase object_paths;
+
+ char filepath[1024]; /* 1024 = FILE_MAX */
+
+ char is_sequence;
+ char forward_axis;
+ char up_axis;
+ char override_frame;
+
+ float scale;
+ float frame; /* The frame/time to lookup in the cache file. */
+
+ short flag; /* Animation flag. */
+ short draw_flag;
+} CacheFile;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DNA_CACHEFILE_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h
index d385e303a7c..ee147da8dae 100644
--- a/source/blender/makesdna/DNA_cloth_types.h
+++ b/source/blender/makesdna/DNA_cloth_types.h
@@ -63,6 +63,7 @@ typedef struct ClothSimSettings {
float max_sewing; /* max sewing force */
float avg_spring_len; /* used for normalized springs */
float timescale; /* parameter how fast cloth runs */
+ float time_scale; /* multiplies cloth speed */
float maxgoal; /* see SB */
float eff_force_scale;/* Scaling of effector forces (see softbody_calc_forces).*/
float eff_wind_scale; /* Scaling of effector wind (see softbody_calc_forces). */
@@ -98,6 +99,7 @@ typedef struct ClothSimSettings {
short presets; /* used for presets on GUI */
short reset;
+ char pad0[4];
struct EffectorWeights *effector_weights;
} ClothSimSettings;
diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h
index 1d88b01cf62..f7ee1ff3915 100644
--- a/source/blender/makesdna/DNA_color_types.h
+++ b/source/blender/makesdna/DNA_color_types.h
@@ -49,7 +49,8 @@ typedef struct CurveMapPoint {
/* curvepoint->flag */
enum {
CUMA_SELECT = 1,
- CUMA_VECTOR = 2
+ CUMA_HANDLE_VECTOR = 2,
+ CUMA_HANDLE_AUTO_ANIM = 4,
};
typedef struct CurveMap {
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 5fcd374b21f..fc4e7de73f5 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -458,6 +458,12 @@ typedef struct bObjectSolverConstraint {
struct Object *camera;
} bObjectSolverConstraint;
+/* Transform matrix cache constraint */
+typedef struct bTransformCacheConstraint {
+ struct CacheFile *cache_file;
+ char object_path[1024]; /* FILE_MAX */
+} bTransformCacheConstraint;
+
/* ------------------------------------------ */
/* bConstraint->type
@@ -494,6 +500,7 @@ typedef enum eBConstraint_Types {
CONSTRAINT_TYPE_FOLLOWTRACK = 26, /* Follow Track Constraint */
CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */
CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */
+ CONSTRAINT_TYPE_TRANSFORM_CACHE = 29, /* Transform Cache Constraint */
/* NOTE: no constraints are allowed to be added after this */
NUM_CONSTRAINT_TYPES
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 41f53f9f51c..f1546053c5c 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation.
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -32,9 +32,10 @@
#include "DNA_listBase.h"
#include "DNA_ID.h"
+#include "DNA_brush_types.h"
struct AnimData;
-
+struct CurveMapping;
/* Grease-Pencil Annotations - 'Stroke Point'
* -> Coordinates may either be 2d or 3d depending on settings at the time
@@ -44,6 +45,7 @@ struct AnimData;
typedef struct bGPDspoint {
float x, y, z; /* co-ordinates of point (usually 2d, but can be 3d as well) */
float pressure; /* pressure of input device (from 0 to 1) at this point */
+ float strength; /* color strength (used for alpha factor) */
float time; /* seconds since start of stroke */
int flag; /* additional options (NOTE: can shrink this field down later if needed) */
} bGPDspoint;
@@ -65,24 +67,113 @@ typedef struct bGPDtriangle {
int v1, v2, v3; /* indices for tesselated triangle used for GP Fill */
} bGPDtriangle;
+/* GP brush (used for new strokes) */
+typedef struct bGPDbrush {
+ struct bGPDbrush *next, *prev;
+
+ char info[64]; /* Brush name. Must be unique. */
+ short thickness; /* thickness to apply to strokes */
+ short flag;
+ float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */
+ short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */
+ short sublevel; /* number of times to subdivide new strokes */
+
+ float draw_sensitivity; /* amount of sensivity to apply to newly created strokes */
+ float draw_strength; /* amount of alpha strength to apply to newly created strokes */
+ float draw_jitter; /* amount of jitter to apply to newly created strokes */
+ float draw_angle; /* angle when the brush has full thickness */
+ float draw_angle_factor; /* factor to apply when angle change (only 90 degrees) */
+ float draw_random_press; /* factor of randomness for sensitivity and strength */
+ float draw_random_sub; /* factor of randomness for subdivision */
+ struct CurveMapping *cur_sensitivity;
+ struct CurveMapping *cur_strength;
+ struct CurveMapping *cur_jitter;
+} bGPDbrush;
+
+/* bGPDbrush->flag */
+typedef enum eGPDbrush_Flag {
+ /* brush is active */
+ GP_BRUSH_ACTIVE = (1 << 0),
+ /* brush use pressure */
+ GP_BRUSH_USE_PRESSURE = (1 << 1),
+ /* brush use pressure for alpha factor */
+ GP_BRUSH_USE_STENGTH_PRESSURE = (1 << 2),
+ /* brush use pressure for alpha factor */
+ GP_BRUSH_USE_JITTER_PRESSURE = (1 << 3),
+ /* brush use random for pressure */
+ GP_BRUSH_USE_RANDOM_PRESSURE = (1 << 4),
+ /* brush use random for strength */
+ GP_BRUSH_USE_RANDOM_STRENGTH = (1 << 5)
+} eGPDbrush_Flag;
+
+/* color of palettes */
+typedef struct bGPDpalettecolor {
+ struct bGPDpalettecolor *next, *prev;
+ char info[64]; /* Color name. Must be unique. */
+ float color[4];
+ float fill[4]; /* color that should be used for drawing "fills" for strokes */
+ short flag; /* settings for palette color */
+ char pad[6]; /* padding for compiler alignment error */
+} bGPDpalettecolor;
+
+/* bGPDpalettecolor->flag */
+typedef enum eGPDpalettecolor_Flag {
+ /* color is active */
+ PC_COLOR_ACTIVE = (1 << 0),
+ /* don't display color */
+ PC_COLOR_HIDE = (1 << 1),
+ /* protected from further editing */
+ PC_COLOR_LOCKED = (1 << 2),
+ /* do onion skinning */
+ PC_COLOR_ONIONSKIN = (1 << 3),
+ /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */
+ PC_COLOR_VOLUMETRIC = (1 << 4),
+ /* Use High quality fill */
+ PC_COLOR_HQ_FILL = (1 << 5)
+} eGPDpalettecolor_Flag;
+
+/* palette of colors */
+typedef struct bGPDpalette {
+ struct bGPDpalette *next, *prev;
+
+ /* pointer to individual colours */
+ ListBase colors;
+ char info[64]; /* Palette name. Must be unique. */
+
+ short flag;
+ char pad[6]; /* padding for compiler alignment error */
+} bGPDpalette;
+
+/* bGPDpalette->flag */
+typedef enum eGPDpalette_Flag {
+ /* palette is active */
+ PL_PALETTE_ACTIVE = (1 << 0)
+} eGPDpalette_Flag;
+
/* Grease-Pencil Annotations - 'Stroke'
* -> A stroke represents a (simplified version) of the curve
* drawn by the user in one 'mousedown'->'mouseup' operation
*/
typedef struct bGPDstroke {
struct bGPDstroke *next, *prev;
+
bGPDspoint *points; /* array of data-points for stroke */
- void *pad; /* keep 4 pointers at the beginning, padding for 'inittime' is tricky 64/32bit */
- int totpoints; /* number of data-points in array */
-
- short thickness; /* thickness of stroke (currently not used) */
- short flag; /* various settings about this stroke */
-
bGPDtriangle *triangles;/* tesselated triangles for GP Fill */
+ int totpoints; /* number of data-points in array */
int tot_triangles; /* number of triangles in array */
- int pad1, *pad2;
+
+ short thickness; /* thickness of stroke */
+ short flag, pad[2]; /* various settings about this stroke */
double inittime; /* Init time of stroke */
+ /* The pointer to color is only used during drawing, but not saved
+ * colorname is the join with the palette, but when draw, the pointer is update if the value is NULL
+ * to speed up the drawing
+ */
+ char colorname[128]; /* color name */
+ bGPDpalettecolor *palcolor; /* current palette color */
+ /* temporary layer name only used during copy/paste to put the stroke in the original layer */
+ char tmp_layerinfo[128];
} bGPDstroke;
/* bGPDstroke->flag */
@@ -97,6 +188,10 @@ typedef enum eGPDstroke_Flag {
GP_STROKE_SELECT = (1 << 3),
/* Recalculate triangulation for high quality fill (when true, force a new recalc) */
GP_STROKE_RECALC_CACHES = (1 << 4),
+ /* Recalculate the color pointer using the name as index (true force a new recalc) */
+ GP_STROKE_RECALC_COLOR = (1 << 5),
+ /* Flag used to indicate that stroke is closed and draw edge between last and first point */
+ GP_STROKE_CYCLIC = (1 << 7),
/* only for use with stroke-buffer (while drawing eraser) */
GP_STROKE_ERASER = (1 << 15)
} eGPDstroke_Flag;
@@ -139,16 +234,18 @@ typedef struct bGPDlayer {
float gcolor_prev[3]; /* optional color for ghosts before the active frame */
float gcolor_next[3]; /* optional color for ghosts after the active frame */
- float color[4]; /* color that should be used to draw all the strokes in this layer */
- float fill[4]; /* color that should be used for drawing "fills" for strokes */
+ float color[4]; /* Color for strokes in layers (replaced by palettecolor). Only used for ruler (which uses GPencil internally) */
+ float fill[4]; /* Fill color for strokes in layers. Not used and replaced by palettecolor fill */
char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3")
* this is used for the name of the layer too and kept unique. */
- float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */
- short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */
- short sublevel; /* number of times to subdivide new strokes */
- short pad[4]; /* padding for compiler error */
+ struct Object *parent; /* parent object */
+ float inverse[4][4]; /* inverse matrix (only used if parented) */
+ char parsubstr[64]; /* String describing subobject info, MAX_ID_NAME-2 */
+ short partype, pad;
+ float tintcolor[4]; /* Color used to tint layer, alpha value is used as factor */
+ float opacity; /* Opacity of the layer */
} bGPDlayer;
/* bGPDlayer->flag */
@@ -176,7 +273,9 @@ typedef enum eGPDlayer_Flag {
/* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */
GP_LAYER_VOLUMETRIC = (1 << 10),
/* Use high quality fill (instead of buggy legacy OpenGL Fill) */
- GP_LAYER_HQ_FILL = (1 << 11)
+ GP_LAYER_HQ_FILL = (1 << 11),
+ /* Unlock color */
+ GP_LAYER_UNLOCK_COLOR = (1 << 12)
} eGPDlayer_Flag;
/* Grease-Pencil Annotations - 'DataBlock' */
@@ -187,7 +286,7 @@ typedef struct bGPdata {
/* saved Grease-Pencil data */
ListBase layers; /* bGPDlayers */
int flag; /* settings for this datablock */
-
+
/* not-saved stroke buffer data (only used during paint-session)
* - buffer must be initialized before use, but freed after
* whole paint operation is over
@@ -195,6 +294,13 @@ typedef struct bGPdata {
short sbuffer_size; /* number of elements currently in cache */
short sbuffer_sflag; /* flags for stroke that cache represents */
void *sbuffer; /* stroke buffer (can hold GP_STROKE_BUFFER_MAX) */
+ float scolor[4]; /* buffer color using palettes */
+ char pad[6]; /* padding for compiler alignment error */
+ short sflag; /* settings for palette color */
+
+ /* saved paletes and brushes */
+ ListBase palettes;
+ //ListBase brushes;
} bGPdata;
/* bGPdata->flag */
@@ -229,7 +335,9 @@ typedef enum eGPdata_Flag {
GP_DATA_STROKE_EDITMODE = (1 << 8),
/* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */
- GP_DATA_SHOW_ONIONSKINS = (1 << 9)
+ GP_DATA_SHOW_ONIONSKINS = (1 << 9),
+ /* Draw a green and red point to indicate start and end of the stroke */
+ GP_DATA_SHOW_DIRECTION = (1 << 10)
} eGPdata_Flag;
#endif /* __DNA_GPENCIL_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index bbc8edf4344..0424dc98a25 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -85,6 +85,7 @@ typedef enum ModifierType {
eModifierType_DataTransfer = 49,
eModifierType_NormalEdit = 50,
eModifierType_CorrectiveSmooth = 51,
+ eModifierType_MeshSequenceCache = 52,
NUM_MODIFIER_TYPES
} ModifierType;
@@ -1541,4 +1542,25 @@ enum {
MOD_NORMALEDIT_MIX_MUL = 3,
};
+typedef struct MeshSeqCacheModifierData {
+ ModifierData modifier;
+
+ struct CacheFile *cache_file;
+ char object_path[1024]; /* 1024 = FILE_MAX */
+
+ char read_flag;
+ char pad[7];
+} MeshSeqCacheModifierData;
+
+/* MeshSeqCacheModifierData.read_flag */
+enum {
+ MOD_MESHSEQ_READ_VERT = (1 << 0),
+ MOD_MESHSEQ_READ_POLY = (1 << 1),
+ MOD_MESHSEQ_READ_UV = (1 << 2),
+ MOD_MESHSEQ_READ_COLOR = (1 << 3),
+};
+
+#define MOD_MESHSEQ_READ_ALL \
+ (MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
+
#endif /* __DNA_MODIFIER_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_object_force.h b/source/blender/makesdna/DNA_object_force.h
index cccee82cb51..59acefeffe4 100644
--- a/source/blender/makesdna/DNA_object_force.h
+++ b/source/blender/makesdna/DNA_object_force.h
@@ -339,6 +339,8 @@ typedef struct SoftBody {
struct PointCache *pointcache;
struct ListBase ptcaches;
+ struct Group *collision_group;
+
struct EffectorWeights *effector_weights;
/* reverse esimated obmatrix .. no need to store in blend file .. how ever who cares */
float lcom[3];
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index 8a734ae7d9e..7c866f939ce 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -160,6 +160,7 @@ typedef struct ParticleSettings {
struct SPHFluidSettings *fluid;
struct EffectorWeights *effector_weights;
+ struct Group *collision_group;
int flag, rt;
short type, from, distr, texact;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c7578a19e0c..a4934cc1f24 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -62,6 +62,7 @@ struct AnimData;
struct Editing;
struct SceneStats;
struct bGPdata;
+struct bGPDbrush;
struct MovieClip;
struct ColorSpace;
@@ -1117,12 +1118,12 @@ typedef enum eGP_EditBrush_Types {
GP_EDITBRUSH_TYPE_SUBDIVIDE = 7,
GP_EDITBRUSH_TYPE_SIMPLIFY = 8,
GP_EDITBRUSH_TYPE_CLONE = 9,
-
+ GP_EDITBRUSH_TYPE_STRENGTH = 10,
+
/* !!! Update GP_EditBrush_Data brush[###]; below !!! */
TOT_GP_EDITBRUSH_TYPES
} eGP_EditBrush_Types;
-
/* Settings for a GPencil Stroke Sculpting Brush */
typedef struct GP_EditBrush_Data {
short size; /* radius of brush */
@@ -1148,17 +1149,26 @@ typedef enum eGP_EditBrush_Flag {
/* GPencil Stroke Sculpting Settings */
typedef struct GP_BrushEdit_Settings {
- GP_EditBrush_Data brush[10]; /* TOT_GP_EDITBRUSH_TYPES */
+ GP_EditBrush_Data brush[11]; /* TOT_GP_EDITBRUSH_TYPES */
void *paintcursor; /* runtime */
int brushtype; /* eGP_EditBrush_Types */
int flag; /* eGP_BrushEdit_SettingsFlag */
+ char pad[4];
+ float alpha; /* alpha factor for selection color */
} GP_BrushEdit_Settings;
/* GP_BrushEdit_Settings.flag */
typedef enum eGP_BrushEdit_SettingsFlag {
/* only affect selected points */
- GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0)
+ GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0),
+ /* apply brush to position */
+ GP_BRUSHEDIT_FLAG_APPLY_POSITION = (1 << 1),
+ /* apply brush to strength */
+ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2),
+ /* apply brush to thickness */
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3)
+
} eGP_BrushEdit_SettingsFlag;
/* *************************************************************** */
@@ -1378,6 +1388,9 @@ typedef struct ToolSettings {
/* Grease Pencil Sculpt */
struct GP_BrushEdit_Settings gp_sculpt;
+ /* Grease Pencil Drawing Brushes (bGPDbrush) */
+ ListBase gp_brushes;
+
/* Image Paint (8 byttse aligned please!) */
struct ImagePaintSettings imapaint;
@@ -2075,6 +2088,8 @@ typedef enum eGPencil_Flags {
GP_TOOL_FLAG_PAINTSESSIONS_ON = (1 << 0),
/* When creating new frames, the last frame gets used as the basis for the new one */
GP_TOOL_FLAG_RETAIN_LAST = (1 << 1),
+ /* Add the strokes below all strokes in the layer */
+ GP_TOOL_FLAG_PAINT_ONBACK = (1 << 2)
} eGPencil_Flags;
/* toolsettings->gpencil_src */
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index fbeabb351ac..e208ef39719 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -218,11 +218,18 @@ typedef struct ScrArea {
char temp, pad;
struct SpaceType *type; /* callbacks for this space type */
-
- ListBase spacedata; /* SpaceLink */
- ListBase regionbase; /* ARegion */
- ListBase handlers; /* wmEventHandler */
-
+
+ /* A list of space links (editors) that were open in this area before. When
+ * changing the editor type, we try to reuse old editor data from this list.
+ * The first item is the active/visible one.
+ */
+ ListBase spacedata; /* SpaceLink */
+ /* NOTE: This region list is the one from the active/visible editor (first item in
+ * spacedata list). Use SpaceLink.regionbase if it's inactive (but only then)!
+ */
+ ListBase regionbase; /* ARegion */
+ ListBase handlers; /* wmEventHandler */
+
ListBase actionzones; /* AZone */
} ScrArea;
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 4ce0f369ebd..41188c2412f 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -738,6 +738,7 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_COLLADA = (1 << 13),
FILE_TYPE_OPERATOR = (1 << 14), /* from filter_glob operator property */
FILE_TYPE_APPLICATIONBUNDLE = (1 << 15),
+ FILE_TYPE_ALEMBIC = (1 << 16),
FILE_TYPE_DIR = (1 << 30), /* An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_BLENDERLIB = (1 << 31),
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index b78299316e1..2cea8715a65 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -129,6 +129,7 @@ static const char *includefiles[] = {
"DNA_rigidbody_types.h",
"DNA_freestyle_types.h",
"DNA_linestyle_types.h",
+ "DNA_cachefile_types.h",
/* see comment above before editing! */
/* empty string to indicate end of includefiles */
@@ -1340,4 +1341,5 @@ int main(int argc, char **argv)
#include "DNA_rigidbody_types.h"
#include "DNA_freestyle_types.h"
#include "DNA_linestyle_types.h"
+#include "DNA_cachefile_types.h"
/* end of list */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index b50d0f85701..45b0225ac1f 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -95,6 +95,8 @@ extern StructRNA RNA_Brush;
extern StructRNA RNA_BrushTextureSlot;
extern StructRNA RNA_BuildModifier;
extern StructRNA RNA_MeshCacheModifier;
+extern StructRNA RNA_MeshSequenceCacheModifier;
+extern StructRNA RNA_CacheFile;
extern StructRNA RNA_Camera;
extern StructRNA RNA_CastModifier;
extern StructRNA RNA_ChildOfConstraint;
@@ -258,6 +260,9 @@ extern StructRNA RNA_FreestyleSettings;
extern StructRNA RNA_Function;
extern StructRNA RNA_GPencilFrame;
extern StructRNA RNA_GPencilLayer;
+extern StructRNA RNA_GPencilPalette;
+extern StructRNA RNA_GPencilPaletteColor;
+extern StructRNA RNA_GPencilBrush;
extern StructRNA RNA_GPencilStroke;
extern StructRNA RNA_GPencilStrokePoint;
extern StructRNA RNA_GPencilSculptSettings;
@@ -694,6 +699,11 @@ void RNA_main_pointer_create(struct Main *main, PointerRNA *r_ptr);
void RNA_id_pointer_create(struct ID *id, PointerRNA *r_ptr);
void RNA_pointer_create(struct ID *id, StructRNA *type, void *data, PointerRNA *r_ptr);
+bool RNA_path_resolved_create(
+ PointerRNA *ptr, struct PropertyRNA *prop,
+ const int prop_index,
+ PathResolvedRNA *r_anim_rna);
+
void RNA_blender_rna_pointer_create(PointerRNA *r_ptr);
void RNA_pointer_recast(PointerRNA *ptr, PointerRNA *r_ptr);
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index 7ae3d552916..1c9b3593d17 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -198,6 +198,8 @@ extern EnumPropertyItem rna_enum_dt_mix_mode_items[];
extern EnumPropertyItem rna_enum_dt_layers_select_src_items[];
extern EnumPropertyItem rna_enum_dt_layers_select_dst_items[];
+extern EnumPropertyItem rna_enum_abc_compression_items[];
+
/* API calls */
int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 1d5f46a1814..276531992f9 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -64,6 +64,16 @@ typedef struct PropertyPointerRNA {
struct PropertyRNA *prop;
} PropertyPointerRNA;
+/**
+ * Stored result of a RNA path lookup (as used by anim-system)
+ */
+typedef struct PathResolvedRNA {
+ struct PointerRNA ptr;
+ struct PropertyRNA *prop;
+ /* -1 for non-array access */
+ int prop_index;
+} PathResolvedRNA;
+
/* Property */
typedef enum PropertyType {
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 2dee1fe3504..1f14ee60cbc 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -39,6 +39,7 @@ set(DEFSRC
rna_blenvm.c
rna_boid.c
rna_brush.c
+ rna_cachefile.c
rna_camera.c
rna_cloth.c
rna_color.c
@@ -292,6 +293,13 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_ALEMBIC)
+ list(APPEND INC
+ ../../alembic
+ )
+ add_definitions(-DWITH_ALEMBIC)
+endif()
+
if(WITH_BULLET)
list(APPEND INC
../../../../intern/rigidbody
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b2355206071..2d68c58281a 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -3302,6 +3302,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_blenvm.c", NULL, RNA_def_blenvm},
{"rna_boid.c", NULL, RNA_def_boid},
{"rna_brush.c", NULL, RNA_def_brush},
+ {"rna_cachefile.c", NULL, RNA_def_cachefile},
{"rna_camera.c", "rna_camera_api.c", RNA_def_camera},
{"rna_cloth.c", NULL, RNA_def_cloth},
{"rna_color.c", NULL, RNA_def_color},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index b6e9844d97a..fdb93ac1a18 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -52,6 +52,7 @@ EnumPropertyItem rna_enum_id_type_items[] = {
{ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armature", ""},
{ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""},
{ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""},
+ {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""},
{ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""},
{ID_VF, "FONT", ICON_FONT_DATA, "Font", ""},
{ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""},
@@ -64,10 +65,10 @@ EnumPropertyItem rna_enum_id_type_items[] = {
{ID_LT, "LATTICE", ICON_LATTICE_DATA, "Lattice", ""},
{ID_MSK, "MASK", ICON_MOD_MASK, "Mask", ""},
{ID_MA, "MATERIAL", ICON_MATERIAL_DATA, "Material", ""},
- {ID_MB, "META", ICON_META_DATA, "MetaBall", ""},
+ {ID_MB, "META", ICON_META_DATA, "Metaball", ""},
{ID_ME, "MESH", ICON_MESH_DATA, "Mesh", ""},
- {ID_MC, "MOVIECLIP", ICON_CLIP, "MovieClip", ""},
- {ID_NT, "NODETREE", ICON_NODETREE, "NodeTree", ""},
+ {ID_MC, "MOVIECLIP", ICON_CLIP, "Movie Clip", ""},
+ {ID_NT, "NODETREE", ICON_NODETREE, "Node Tree", ""},
{ID_OB, "OBJECT", ICON_OBJECT_DATA, "Object", ""},
{ID_PC, "PAINTCURVE", ICON_CURVE_BEZCURVE, "Paint Curve", ""},
{ID_PAL, "PALETTE", ICON_COLOR, "Palette", ""},
@@ -139,6 +140,7 @@ short RNA_type_to_ID_code(StructRNA *type)
if (RNA_struct_is_a(type, &RNA_Action)) return ID_AC;
if (RNA_struct_is_a(type, &RNA_Armature)) return ID_AR;
if (RNA_struct_is_a(type, &RNA_Brush)) return ID_BR;
+ if (RNA_struct_is_a(type, &RNA_CacheFile)) return ID_CF;
if (RNA_struct_is_a(type, &RNA_Camera)) return ID_CA;
if (RNA_struct_is_a(type, &RNA_Curve)) return ID_CU;
if (RNA_struct_is_a(type, &RNA_GreasePencil)) return ID_GD;
@@ -179,6 +181,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_AR: return &RNA_Armature;
case ID_BR: return &RNA_Brush;
case ID_CA: return &RNA_Camera;
+ case ID_CF: return &RNA_CacheFile;
case ID_CU: return &RNA_Curve;
case ID_GD: return &RNA_GreasePencil;
case ID_GR: return &RNA_Group;
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 5a93e18a7dd..047e5ea17ab 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -6966,3 +6966,22 @@ bool RNA_struct_equals(PointerRNA *a, PointerRNA *b, eRNAEqualsMode mode)
return equals;
}
+
+bool RNA_path_resolved_create(
+ PointerRNA *ptr, struct PropertyRNA *prop,
+ const int prop_index,
+ PathResolvedRNA *r_anim_rna)
+{
+ int array_len = RNA_property_array_length(ptr, prop);
+
+ if ((array_len == 0) || (prop_index < array_len)) {
+ r_anim_rna->ptr = *ptr;
+ r_anim_rna->prop = prop;
+ r_anim_rna->prop_index = array_len ? prop_index : -1;
+
+ return true;
+ }
+ else {
+ return false;
+ }
+}
diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c
new file mode 100644
index 00000000000..7249ebd5feb
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_cachefile.c
@@ -0,0 +1,169 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "DNA_cachefile_types.h"
+#include "DNA_scene_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#ifdef RNA_RUNTIME
+
+#include "BKE_cachefile.h"
+#include "BKE_depsgraph.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#ifdef WITH_ALEMBIC
+# include "../../../alembic/ABC_alembic.h"
+#endif
+
+static void rna_CacheFile_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->data;
+
+ DAG_id_tag_update(&cache_file->id, 0);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
+
+ UNUSED_VARS(bmain, scene);
+}
+
+static void rna_CacheFile_update_handle(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ CacheFile *cache_file = ptr->data;
+
+ BKE_cachefile_reload(bmain, cache_file);
+
+ rna_CacheFile_update(bmain, scene, ptr);
+}
+
+static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->data;
+ rna_iterator_listbase_begin(iter, &cache_file->object_paths, NULL);
+}
+
+#else
+
+/* cachefile.object_paths */
+static void rna_def_alembic_object_path(BlenderRNA *brna)
+{
+ StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPath", NULL);
+ RNA_def_struct_sdna(srna, "AlembicObjectPath");
+ RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive");
+ RNA_def_struct_ui_icon(srna, ICON_NONE);
+
+ PropertyRNA *prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Path", "Object path");
+ RNA_def_struct_name_property(srna, prop);
+}
+
+/* cachefile.object_paths */
+static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ RNA_def_property_srna(cprop, "AlembicObjectPaths");
+ StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPaths", NULL);
+ RNA_def_struct_sdna(srna, "CacheFile");
+ RNA_def_struct_ui_text(srna, "Object Paths", "Collection of object paths");
+}
+
+static void rna_def_cachefile(BlenderRNA *brna)
+{
+ StructRNA *srna = RNA_def_struct(brna, "CacheFile", "ID");
+ RNA_def_struct_sdna(srna, "CacheFile");
+ RNA_def_struct_ui_text(srna, "CacheFile", "");
+ RNA_def_struct_ui_icon(srna, ICON_FILE);
+
+ PropertyRNA *prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_ui_text(prop, "File Path", "Path to external displacements file");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update_handle");
+
+ prop = RNA_def_property(srna, "is_sequence", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Sequence", "Whether the cache is separated in a series of files");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ /* ----------------- For Scene time ------------------- */
+
+ prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Override Frame",
+ "Whether to use a custom frame for looking up data in the cache file,"
+ " instead of using the current scene frame");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "frame");
+ RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
+ RNA_def_property_ui_text(prop, "Frame", "The time to use for looking up the data in the cache file,"
+ " or to determine which file to use in a file sequence");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ /* ----------------- Axis Conversion ----------------- */
+
+ prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "forward_axis");
+ RNA_def_property_enum_items(prop, rna_enum_object_axis_items);
+ RNA_def_property_ui_text(prop, "Forward", "");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ prop = RNA_def_property(srna, "up_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "up_axis");
+ RNA_def_property_enum_items(prop, rna_enum_object_axis_items);
+ RNA_def_property_ui_text(prop, "Up", "");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "scale");
+ RNA_def_property_range(prop, 0.0001f, 1000.0f);
+ RNA_def_property_ui_text(prop, "Scale", "Value by which to enlarge or shrink the object with respect to the world's origin"
+ " (only applicable through a Transform Cache constraint)");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ /* object paths */
+ prop = RNA_def_property(srna, "object_paths", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "object_paths", NULL);
+ RNA_def_property_collection_funcs(prop, "rna_CacheFile_object_paths_begin", "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end", "rna_iterator_listbase_get",
+ NULL, NULL, NULL, NULL);
+ RNA_def_property_struct_type(prop, "AlembicObjectPath");
+ RNA_def_property_srna(prop, "AlembicObjectPaths");
+ RNA_def_property_ui_text(prop, "Object Paths", "Paths of the objects inside the Alembic archive");
+ rna_def_cachefile_object_paths(brna, prop);
+
+ rna_def_animdata_common(srna);
+}
+
+void RNA_def_cachefile(BlenderRNA *brna)
+{
+ rna_def_cachefile(brna);
+ rna_def_alembic_object_path(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c
index c91b6487653..781e44c9ed6 100644
--- a/source/blender/makesrna/intern/rna_cloth.c
+++ b/source/blender/makesrna/intern/rna_cloth.c
@@ -436,11 +436,19 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "quality", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "stepsPerFrame");
- RNA_def_property_range(prop, 1, 80);
+ RNA_def_property_range(prop, 1, INT_MAX);
+ RNA_def_property_ui_range(prop, 1, 80, 1, -1);
RNA_def_property_ui_text(prop, "Quality",
"Quality of the simulation in steps per frame (higher is better quality but slower)");
RNA_def_property_update(prop, 0, "rna_cloth_update");
+ prop = RNA_def_property(srna, "time_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "time_scale");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 10, 3);
+ RNA_def_property_ui_text(prop, "Speed", "Cloth speed is multiplied by this value");
+ RNA_def_property_update(prop, 0, "rna_cloth_update");
+
prop = RNA_def_property(srna, "vertex_group_shrink", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, "rna_ClothSettings_shrink_vgroup_get", "rna_ClothSettings_shrink_vgroup_length",
"rna_ClothSettings_shrink_vgroup_set");
@@ -557,6 +565,12 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Rest Shape Key", "Shape key to use the rest spring lengths from");
RNA_def_property_update(prop, 0, "rna_cloth_update");
+ prop = RNA_def_property(srna, "use_dynamic_mesh", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH);
+ RNA_def_property_ui_text(prop, "Dynamic Base Mesh", "Make simulation respect deformations in the base mesh");
+ RNA_def_property_update(prop, 0, "rna_cloth_update");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
/* unused */
/* unused still */
@@ -656,7 +670,8 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "collision_quality", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "loop_count");
- RNA_def_property_range(prop, 1, 20);
+ RNA_def_property_range(prop, 1, SHRT_MAX);
+ RNA_def_property_ui_range(prop, 1, 20, 1, -1);
RNA_def_property_ui_text(prop, "Collision Quality",
"How many collision iterations should be done. (higher is better quality but slower)");
RNA_def_property_update(prop, 0, "rna_cloth_update");
@@ -681,7 +696,8 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "self_collision_quality", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "self_loop_count");
- RNA_def_property_range(prop, 1, 10);
+ RNA_def_property_range(prop, 1, SHRT_MAX);
+ RNA_def_property_ui_range(prop, 1, 10, 1, -1);
RNA_def_property_ui_text(prop, "Self Collision Quality",
"How many self collision iterations should be done "
"(higher is better quality but slower)");
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index 1910493b933..a66867ece03 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -690,7 +690,8 @@ static void rna_def_curvemappoint(BlenderRNA *brna)
PropertyRNA *prop;
static EnumPropertyItem prop_handle_type_items[] = {
{0, "AUTO", 0, "Auto Handle", ""},
- {CUMA_VECTOR, "VECTOR", 0, "Vector Handle", ""},
+ {CUMA_HANDLE_AUTO_ANIM, "AUTO_CLAMPED", 0, "Auto Clamped Handle", ""},
+ {CUMA_HANDLE_VECTOR, "VECTOR", 0, "Vector Handle", ""},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 98560bf3452..db3f76f3cfc 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -72,6 +72,8 @@ EnumPropertyItem rna_enum_constraint_type_items[] = {
"Compensate for scaling one axis by applying suitable scaling to the other two axes"},
{CONSTRAINT_TYPE_TRANSFORM, "TRANSFORM", ICON_CONSTRAINT_DATA, "Transformation",
"Use one transform property from target to control another (or same) property on owner"},
+ {CONSTRAINT_TYPE_TRANSFORM_CACHE, "TRANSFORM_CACHE", ICON_CONSTRAINT_DATA, "Transform Cache",
+ "Look up the transformation matrix from an external file"},
{0, "", 0, N_("Tracking"), ""},
{CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CONSTRAINT_DATA, "Clamp To",
"Restrict movements to lie along a curve by remapping location along curve's longest axis"},
@@ -214,6 +216,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
return &RNA_CameraSolverConstraint;
case CONSTRAINT_TYPE_OBJECTSOLVER:
return &RNA_ObjectSolverConstraint;
+ case CONSTRAINT_TYPE_TRANSFORM_CACHE:
+ return &RNA_TransformCacheConstraint;
default:
return &RNA_UnknownType;
}
@@ -2571,6 +2575,27 @@ static void rna_def_constraint_object_solver(BlenderRNA *brna)
"rna_Constraint_cameraObject_poll");
}
+static void rna_def_constraint_transform_cache(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "TransformCacheConstraint", "Constraint");
+ RNA_def_struct_ui_text(srna, "Transform Cache Constraint", "Look up transformation from an external file");
+ RNA_def_struct_sdna_from(srna, "bTransformCacheConstraint", "data");
+
+ prop = RNA_def_property(srna, "cache_file", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cache_file");
+ RNA_def_property_struct_type(prop, "CacheFile");
+ RNA_def_property_ui_text(prop, "Cache File", "");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_Constraint_dependency_update");
+
+ prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup the transform matrix");
+ RNA_def_property_update(prop, 0, "rna_Constraint_update");
+}
+
/* base struct for constraints */
void RNA_def_constraint(BlenderRNA *brna)
{
@@ -2687,6 +2712,7 @@ void RNA_def_constraint(BlenderRNA *brna)
rna_def_constraint_follow_track(brna);
rna_def_constraint_camera_solver(brna);
rna_def_constraint_object_solver(brna);
+ rna_def_constraint_transform_cache(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 5fb581eb74a..c521e93d33e 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -1600,7 +1600,13 @@ static void rna_def_channeldriver(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", DRIVER_FLAG_SHOWDEBUG);
RNA_def_property_ui_text(prop, "Show Debug Info",
"Show intermediate values for the driver calculations to allow debugging of drivers");
-
+
+ prop = RNA_def_property(srna, "use_self", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", DRIVER_FLAG_USE_SELF);
+ RNA_def_property_ui_text(prop, "Use Self",
+ "Include a 'self' variable in the name-space, "
+ "so drivers can easily reference the data being modified (object, bone, etc...)");
+
/* State Info (for Debugging) */
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", DRIVER_FLAG_INVALID);
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 52c04bec743..80f4d5d05b6 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -15,7 +15,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Contributor(s): Blender Foundation (2009), Joshua Leung
+ * Contributor(s): Blender Foundation (2009), Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -41,6 +41,17 @@
#include "rna_internal.h"
#include "WM_types.h"
+#include "DNA_object_types.h"
+#include "ED_gpencil.h"
+
+/* parent type */
+static EnumPropertyItem parent_type_items[] = {
+ {PAROBJECT, "OBJECT", 0, "Object", "The layer is parented to an object"},
+ {PARSKEL, "ARMATURE", 0, "Armature", ""},
+ {PARBONE, "BONE", 0, "Bone", "The layer is parented to a bone"},
+ {0, NULL, 0, NULL, NULL}
+};
+
#ifdef RNA_RUNTIME
@@ -49,8 +60,7 @@
#include "WM_api.h"
#include "BKE_gpencil.h"
-
-#include "DNA_object_types.h"
+#include "BKE_action.h"
static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
@@ -90,6 +100,16 @@ static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, Pointer
rna_GPencil_update(bmain, scene, ptr);
}
+static void rna_GPencil_stroke_colorname_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ bGPDstroke *gps = (bGPDstroke *)ptr->data;
+ gps->flag |= GP_STROKE_RECALC_COLOR;
+ gps->palcolor = NULL;
+
+ /* Now do standard updates... */
+ rna_GPencil_update(bmain, scene, ptr);
+}
+
static char *rna_GPencilLayer_path(PointerRNA *ptr)
{
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
@@ -123,38 +143,140 @@ static void rna_GPencilLayer_line_width_range(PointerRNA *ptr, int *min, int *ma
* it's relatively hard to test for that. So, for now, only volumetric strokes
* get to be larger...
*/
+
+ /* From GP v2 this value is used to increase or decrease the thickness of the stroke */
if (gpl->flag & GP_LAYER_VOLUMETRIC) {
- *min = 1;
+ *min = -300;
*max = 300;
- *softmin = 1;
+ *softmin = -100;
*softmax = 100;
}
else {
- *min = 1;
+ *min = -10;
*max = 10;
- *softmin = 1;
+ *softmin = -10;
*softmax = 10;
}
}
-static int rna_GPencilLayer_is_stroke_visible_get(PointerRNA *ptr)
+/* set parent */
+static void set_parent(bGPDlayer *gpl, Object *par, const int type, const char *substr)
+{
+ if (type == PAROBJECT) {
+ invert_m4_m4(gpl->inverse, par->obmat);
+ gpl->parent = par;
+ gpl->partype |= PAROBJECT;
+ gpl->parsubstr[0] = 0;
+ }
+ else if (type == PARSKEL) {
+ invert_m4_m4(gpl->inverse, par->obmat);
+ gpl->parent = par;
+ gpl->partype |= PARSKEL;
+ gpl->parsubstr[0] = 0;
+ }
+ else if (type == PARBONE) {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, substr);
+ if (pchan) {
+ float tmp_mat[4][4];
+ mul_m4_m4m4(tmp_mat, par->obmat, pchan->pose_mat);
+
+ invert_m4_m4(gpl->inverse, tmp_mat);
+ gpl->parent = par;
+ gpl->partype |= PARBONE;
+ BLI_strncpy(gpl->parsubstr, substr, sizeof(gpl->parsubstr));
+ }
+ }
+}
+
+/* set parent object and inverse matrix */
+static void rna_GPencilLayer_parent_set(PointerRNA *ptr, PointerRNA value)
{
- /* see drawgpencil.c -> gp_draw_data_layers() for more details
- * about this limit for showing/not showing
- */
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
- return (gpl->color[3] > GPENCIL_ALPHA_OPACITY_THRESH);
+ Object *par = (Object *)value.data;
+
+ if (par != NULL) {
+ set_parent(gpl, par, gpl->partype, gpl->parsubstr);
+ }
+ else {
+ /* keep strokes in the same place, so apply current transformation */
+ if (gpl->parent != NULL) {
+ bGPDspoint *pt;
+ int i;
+ float diff_mat[4][4];
+ /* calculate difference matrix */
+ ED_gpencil_parent_location(gpl, diff_mat);
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ mul_m4_v3(diff_mat, &pt->x);
+ }
+ }
+ }
+ }
+ /* clear parent */
+ gpl->parent = NULL;
+ }
}
-static int rna_GPencilLayer_is_fill_visible_get(PointerRNA *ptr)
+/* set parent type */
+static void rna_GPencilLayer_parent_type_set(PointerRNA *ptr, int value)
+{
+ bGPDlayer *gpl = (bGPDlayer *)ptr->data;
+ Object *par = gpl->parent;
+ gpl->partype = value;
+
+ if (par != NULL) {
+ set_parent(gpl, par, value, gpl->parsubstr);
+ }
+}
+
+/* set parent bone */
+static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value)
+{
+ bGPDlayer *gpl = (bGPDlayer *)ptr->data;
+
+ Object *par = gpl->parent;
+ gpl->partype = PARBONE;
+
+ if (par != NULL) {
+ set_parent(gpl, par, gpl->partype, value);
+ }
+}
+
+
+/* parent types enum */
+static EnumPropertyItem *rna_Object_parent_type_itemf(
+ bContext *UNUSED(C), PointerRNA *ptr,
+ PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ bGPDlayer *gpl = (bGPDlayer *)ptr->data;
+ EnumPropertyItem *item = NULL;
+ int totitem = 0;
+
+ RNA_enum_items_add_value(&item, &totitem, parent_type_items, PAROBJECT);
+
+ if (gpl->parent) {
+ Object *par = gpl->parent;
+
+ if (par->type == OB_ARMATURE) {
+ /* special hack: prevents this being overrided */
+ RNA_enum_items_add_value(&item, &totitem, &parent_type_items[1], PARSKEL);
+ RNA_enum_items_add_value(&item, &totitem, parent_type_items, PARBONE);
+ }
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+
+static int rna_GPencilLayer_is_parented_get(PointerRNA *ptr)
{
- /* see drawgpencil.c -> gp_draw_data_layers() for more details
- * about this limit for showing/not showing
- */
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
- return (gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH);
+ return (gpl->parent != NULL);
}
static PointerRNA rna_GPencil_active_layer_get(PointerRNA *ptr)
@@ -201,7 +323,7 @@ static void rna_GPencil_active_layer_set(PointerRNA *ptr, PointerRNA value)
static int rna_GPencil_active_layer_index_get(PointerRNA *ptr)
{
bGPdata *gpd = (bGPdata *)ptr->id.data;
- bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
return BLI_findindex(&gpd->layers, gpl);
}
@@ -211,7 +333,7 @@ static void rna_GPencil_active_layer_index_set(PointerRNA *ptr, int value)
bGPdata *gpd = (bGPdata *)ptr->id.data;
bGPDlayer *gpl = BLI_findlink(&gpd->layers, value);
- gpencil_layer_setactive(gpd, gpl);
+ BKE_gpencil_layer_setactive(gpd, gpl);
}
static void rna_GPencil_active_layer_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
@@ -244,7 +366,7 @@ static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const int value)
/* set new value */
if (value) {
/* enable on active layer (it's the one that's most likely to be of interest right now) */
- gpl = gpencil_layer_getactive(gpd);
+ gpl = BKE_gpencil_layer_getactive(gpd);
if (gpl) {
gpl->flag |= GP_LAYER_ONIONSKIN;
}
@@ -313,7 +435,7 @@ static void rna_GPencil_stroke_point_select_set(PointerRNA *ptr, const int value
pt->flag &= ~GP_SPOINT_SELECT;
/* Check if the stroke should be selected or not... */
- gpencil_stroke_sync_selection(gps);
+ BKE_gpencil_stroke_sync_selection(gps);
}
}
@@ -357,10 +479,12 @@ static void rna_GPencil_stroke_point_pop(bGPDstroke *stroke, ReportList *reports
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
}
-static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame)
+static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame, const char *colorname)
{
bGPDstroke *stroke = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
-
+ strcpy(stroke->colorname, colorname);
+ stroke->palcolor = NULL;
+ stroke->flag |= GP_STROKE_RECALC_COLOR;
BLI_addtail(&frame->strokes, stroke);
return stroke;
@@ -410,7 +534,7 @@ static bGPDframe *rna_GPencil_frame_new(bGPDlayer *layer, ReportList *reports, i
return NULL;
}
- frame = gpencil_frame_addnew(layer, frame_number);
+ frame = BKE_gpencil_frame_addnew(layer, frame_number);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
@@ -425,7 +549,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin
return;
}
- gpencil_layer_delframe(layer, frame);
+ BKE_gpencil_layer_delframe(layer, frame);
RNA_POINTER_INVALIDATE(frame_ptr);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
@@ -433,7 +557,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin
static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src)
{
- bGPDframe *frame = gpencil_frame_duplicate(src);
+ bGPDframe *frame = BKE_gpencil_frame_duplicate(src);
while (BKE_gpencil_layer_find_frame(layer, frame->framenum)) {
frame->framenum++;
@@ -448,7 +572,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src)
static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, int setactive)
{
- bGPDlayer *gl = gpencil_layer_addnew(gpd, name, setactive != 0);
+ bGPDlayer *gl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -463,7 +587,7 @@ static void rna_GPencil_layer_remove(bGPdata *gpd, ReportList *reports, PointerR
return;
}
- gpencil_layer_delete(gpd, layer);
+ BKE_gpencil_layer_delete(gpd, layer);
RNA_POINTER_INVALIDATE(layer_ptr);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -471,25 +595,258 @@ static void rna_GPencil_layer_remove(bGPdata *gpd, ReportList *reports, PointerR
static void rna_GPencil_frame_clear(bGPDframe *frame)
{
- free_gpencil_strokes(frame);
+ BKE_gpencil_free_strokes(frame);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
static void rna_GPencil_layer_clear(bGPDlayer *layer)
{
- free_gpencil_frames(layer);
+ BKE_gpencil_free_frames(layer);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
static void rna_GPencil_clear(bGPdata *gpd)
{
- free_gpencil_layers(&gpd->layers);
+ BKE_gpencil_free_layers(&gpd->layers);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
+/* Palettes */
+static bGPDpalette *rna_GPencil_palette_new(bGPdata *gpd, const char *name, int setactive)
+{
+ bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, name, setactive != 0);
+
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return palette;
+}
+
+static void rna_GPencil_palette_remove(bGPdata *gpd, ReportList *reports, PointerRNA *palette_ptr)
+{
+ bGPDpalette *palette = palette_ptr->data;
+ if (BLI_findindex(&gpd->palettes, palette) == -1) {
+ BKE_report(reports, RPT_ERROR, "Palette not found in grease pencil data");
+ return;
+ }
+
+ BKE_gpencil_palette_delete(gpd, palette);
+ RNA_POINTER_INVALIDATE(palette_ptr);
+
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static PointerRNA rna_GPencil_active_palette_get(PointerRNA *ptr)
+{
+ bGPdata *gpd = ptr->id.data;
+
+ if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */
+ bGPDpalette *palette;
+
+ for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ if (palette->flag & PL_PALETTE_ACTIVE) {
+ break;
+ }
+ }
+
+ if (palette) {
+ return rna_pointer_inherit_refine(ptr, &RNA_GPencilPalette, palette);
+ }
+ }
+
+ return rna_pointer_inherit_refine(ptr, NULL, NULL);
+}
+
+static void rna_GPencil_active_palette_set(PointerRNA *ptr, PointerRNA value)
+{
+ bGPdata *gpd = ptr->id.data;
+
+ if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */
+ bGPDpalette *palette;
+
+ for (palette = gpd->palettes.first; palette; palette = palette->next) {
+ if (palette == value.data) {
+ palette->flag |= PL_PALETTE_ACTIVE;
+ }
+ else {
+ palette->flag &= ~PL_PALETTE_ACTIVE;
+ }
+ }
+ /* force color recalc */
+ BKE_gpencil_palette_change_strokes(gpd);
+
+ WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
+ }
+}
+
+static int rna_GPencilPalette_index_get(PointerRNA *ptr)
+{
+ bGPdata *gpd = (bGPdata *)ptr->id.data;
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+
+ return BLI_findindex(&gpd->palettes, palette);
+}
+
+static void rna_GPencilPalette_index_set(PointerRNA *ptr, int value)
+{
+ bGPdata *gpd = (bGPdata *)ptr->id.data;
+ bGPDpalette *palette = BLI_findlink(&gpd->palettes, value);
+
+ BKE_gpencil_palette_setactive(gpd, palette);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static void rna_GPencilPalette_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
+{
+ bGPdata *gpd = (bGPdata *)ptr->id.data;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&gpd->palettes) - 1);
+
+ *softmin = *min;
+ *softmax = *max;
+}
+
+/* Palette colors */
+static bGPDpalettecolor *rna_GPencilPalette_color_new(bGPDpalette *palette)
+{
+ bGPDpalettecolor *color = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+
+ return color;
+}
+
+static void rna_GPencilPalette_color_remove(bGPDpalette *palette, ReportList *reports, PointerRNA *color_ptr)
+{
+ bGPDpalettecolor *color = color_ptr->data;
+
+ if (BLI_findindex(&palette->colors, color) == -1) {
+ BKE_reportf(reports, RPT_ERROR, "Palette '%s' does not contain color given", palette->info + 2);
+ return;
+ }
+
+ BKE_gpencil_palettecolor_delete(palette, color);
+ RNA_POINTER_INVALIDATE(color_ptr);
+
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static PointerRNA rna_GPencilPalette_active_color_get(PointerRNA *ptr)
+{
+ bGPDpalette *palette = (bGPDpalette *)ptr->data;
+ bGPDpalettecolor *color;
+
+ for (color = palette->colors.first; color; color = color->next) {
+ if (color->flag & PC_COLOR_ACTIVE) {
+ break;
+ }
+ }
+
+ if (color) {
+ return rna_pointer_inherit_refine(ptr, &RNA_GPencilPaletteColor, color);
+ }
+
+ return rna_pointer_inherit_refine(ptr, NULL, NULL);
+}
+
+static void rna_GPencilPalette_active_color_set(PointerRNA *ptr, PointerRNA value)
+{
+ bGPDpalette *palette = (bGPDpalette *)ptr->data;
+ bGPDpalettecolor *color = value.data;
+
+ BKE_gpencil_palettecolor_setactive(palette, color);
+}
+
+static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value)
+{
+ bGPdata *gpd = ptr->id.data;
+ bGPDpalette *palette = ptr->data;
+
+ /* copy the new name into the name slot */
+ BLI_strncpy_utf8(palette->info, value, sizeof(palette->info));
+
+ BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info),
+ sizeof(palette->info));
+}
+
+static char *rna_GPencilPalette_color_path(PointerRNA *ptr)
+{
+ bGPdata *gpd = ptr->id.data;
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = ptr->data;
+
+ char name_palette[sizeof(palette->info) * 2];
+ char name_color[sizeof(palcolor->info) * 2];
+
+ BLI_strescape(name_palette, palette->info, sizeof(name_palette));
+ BLI_strescape(name_color, palcolor->info, sizeof(name_color));
+
+ return BLI_sprintfN("palettes[\"%s\"].colors[\"%s\"]", name_palette, name_color);
+}
+
+static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value)
+{
+ bGPdata *gpd = ptr->id.data;
+ bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = ptr->data;
+
+ /* rename all strokes */
+ BKE_gpencil_palettecolor_changename(gpd, palcolor->info, value);
+
+ /* copy the new name into the name slot */
+ BLI_strncpy_utf8(palcolor->info, value, sizeof(palcolor->info));
+ BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info),
+ sizeof(palcolor->info));
+}
+
+static void rna_GPencilStrokeColor_info_set(PointerRNA *ptr, const char *value)
+{
+ bGPDstroke *gps = ptr->data;
+
+ /* copy the new name into the name slot */
+ BLI_strncpy_utf8(gps->colorname, value, sizeof(gps->colorname));
+}
+
+
+static int rna_GPencilPaletteColor_is_stroke_visible_get(PointerRNA *ptr)
+{
+ bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data;
+ return (pcolor->color[3] > GPENCIL_ALPHA_OPACITY_THRESH);
+}
+
+static int rna_GPencilPaletteColor_is_fill_visible_get(PointerRNA *ptr)
+{
+ bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data;
+ return (pcolor->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH);
+}
+
+static int rna_GPencilPaletteColor_index_get(PointerRNA *ptr)
+{
+ bGPDpalette *palette = (bGPDpalette *)ptr->data;
+ bGPDpalettecolor *pcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+ return BLI_findindex(&palette->colors, pcolor);
+}
+
+static void rna_GPencilPaletteColor_index_set(PointerRNA *ptr, int value)
+{
+ bGPDpalette *palette = (bGPDpalette *)ptr->data;
+ bGPDpalettecolor *pcolor = BLI_findlink(&palette->colors, value);
+ BKE_gpencil_palettecolor_setactive(palette, pcolor);
+}
+
+static void rna_GPencilPaletteColor_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
+{
+ bGPDpalette *palette = (bGPDpalette *)ptr->data;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&palette->colors) - 1);
+
+ *softmin = *min;
+ *softmax = *max;
+}
+
#else
static void rna_def_gpencil_stroke_point(BlenderRNA *brna)
@@ -513,6 +870,12 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Pressure", "Pressure of tablet at point when drawing it");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+ prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "strength");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Strength", "Color intensity (alpha factor)");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SPOINT_SELECT);
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_point_select_set");
@@ -542,6 +905,35 @@ static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cpr
RNA_def_int(func, "index", -1, INT_MIN, INT_MAX, "Index", "point index", INT_MIN, INT_MAX);
}
+/* This information is read only and it can be used by add-ons */
+static void rna_def_gpencil_triangle(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "GPencilTriangle", NULL);
+ RNA_def_struct_sdna(srna, "bGPDtriangle");
+ RNA_def_struct_ui_text(srna, "Triangle", "Triangulation data for HQ fill");
+
+ /* point v1 */
+ prop = RNA_def_property(srna, "v1", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "v1");
+ RNA_def_property_ui_text(prop, "v1", "First triangle vertice index");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ /* point v2 */
+ prop = RNA_def_property(srna, "v2", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "v2");
+ RNA_def_property_ui_text(prop, "v2", "Second triangle vertice index");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ /* point v3 */
+ prop = RNA_def_property(srna, "v3", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "v3");
+ RNA_def_property_ui_text(prop, "v3", "Third triangle vertice index");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+}
+
static void rna_def_gpencil_stroke(BlenderRNA *brna)
{
StructRNA *srna;
@@ -566,6 +958,19 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Stroke Points", "Stroke data points");
rna_def_gpencil_stroke_points_api(brna, prop);
+ /* Triangles */
+ prop = RNA_def_property(srna, "triangles", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "triangles", "tot_triangles");
+ RNA_def_property_struct_type(prop, "GPencilTriangle");
+ RNA_def_property_ui_text(prop, "Triangles", "Triangulation data for HQ fill");
+
+ /* Color */
+ prop = RNA_def_property(srna, "color", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "GPencilPaletteColor");
+ RNA_def_property_pointer_sdna(prop, NULL, "palcolor");
+ RNA_def_property_ui_text(prop, "Palette Color", "Color from palette used in Stroke");
+ RNA_def_property_update(prop, 0, "rna_GPencil_update");
+
/* Settings */
prop = RNA_def_property(srna, "draw_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
@@ -578,6 +983,27 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_select_set");
RNA_def_property_ui_text(prop, "Select", "Stroke is selected for viewport editing");
RNA_def_property_update(prop, 0, "rna_GPencil_update");
+
+ /* Color Name */
+ prop = RNA_def_property(srna, "colorname", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilStrokeColor_info_set");
+ RNA_def_property_ui_text(prop, "Color Name", "Palette color name");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_colorname_update");
+
+ /* Cyclic: Draw a line from end to start point */
+ prop = RNA_def_property(srna, "draw_cyclic", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_CYCLIC);
+ RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic drawing, closing the stroke");
+ RNA_def_property_update(prop, 0, "rna_GPencil_update");
+
+ /* Line Thickness */
+ prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "thickness");
+ RNA_def_property_range(prop, 1, 300);
+ RNA_def_property_ui_range(prop, 1, 10, 1, 0);
+ RNA_def_property_ui_text(prop, "Thickness", "Thickness of stroke (in pixels)");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
}
static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop)
@@ -594,6 +1020,7 @@ static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "new", "rna_GPencil_stroke_new");
RNA_def_function_ui_description(func, "Add a new grease pencil stroke");
+ parm = RNA_def_string(func, "colorname", 0, MAX_NAME, "Color", "Name of the color");
parm = RNA_def_pointer(func, "stroke", "GPencilStroke", "", "The newly created stroke");
RNA_def_function_return(func, parm);
@@ -721,45 +1148,33 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in a volumetric effect");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- /* Use High Quality Fill */
- prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HQ_FILL);
- RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality method to avoid glitches (slower fps during animation playback)");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
-
- /* Stroke Drawing Color */
- prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
- RNA_def_property_array(prop, 3);
- RNA_def_property_range(prop, 0.0f, 1.0f);
- RNA_def_property_ui_text(prop, "Color", "Color for all strokes in this layer");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
-
- prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "color[3]");
+ prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "opacity");
RNA_def_property_range(prop, 0.0, 1.0f);
RNA_def_property_ui_text(prop, "Opacity", "Layer Opacity");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- /* Fill Drawing Color */
- prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA);
- RNA_def_property_float_sdna(prop, NULL, "fill");
+ /* Tint Color */
+ prop = RNA_def_property(srna, "tint_color", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "tintcolor");
RNA_def_property_array(prop, 3);
RNA_def_property_range(prop, 0.0f, 1.0f);
- RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke");
+ RNA_def_property_ui_text(prop, "Tint Color", "Color for tinting stroke colors");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "fill[3]");
+ /* Tint factor */
+ prop = RNA_def_property(srna, "tint_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "tintcolor[3]");
RNA_def_property_range(prop, 0.0, 1.0f);
- RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke");
+ RNA_def_property_ui_text(prop, "Tint Factor", "Factor of tinting color");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- /* Line Thickness */
- prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL);
+ /* Line Thickness change */
+ prop = RNA_def_property(srna, "line_change", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "thickness");
//RNA_def_property_range(prop, 1, 10); /* 10 px limit comes from Windows OpenGL limits for natively-drawn strokes */
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_GPencilLayer_line_width_range");
- RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)");
+ RNA_def_property_ui_text(prop, "Thickness", "Thickness change to apply current strokes (in pixels)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
/* Onion-Skinning */
@@ -803,31 +1218,6 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- /* Smoothing factor for new strokes */
- prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac");
- RNA_def_property_range(prop, 0.0, 2.0f);
- RNA_def_property_ui_text(prop, "Smooth",
- "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
-
- /* Iterations of the Smoothing factor */
- prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE);
- RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl");
- RNA_def_property_range(prop, 1, 3);
- RNA_def_property_ui_text(prop, "Iterations",
- "Number of times to smooth newly created strokes "
- "(smoothing strength is halved on each successive round of smoothing)");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
-
- /* Subdivision level for new strokes */
- prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE);
- RNA_def_property_int_sdna(prop, NULL, "sublevel");
- RNA_def_property_range(prop, 0, 3);
- RNA_def_property_ui_text(prop, "Subdivision Steps",
- "Number of times to subdivide newly created strokes, for less jagged strokes");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
-
/* Flags */
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE);
@@ -847,6 +1237,15 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Frame Locked", "Lock current frame displayed by layer");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+ /* Unlock colors */
+ prop = RNA_def_property(srna, "unlock_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_UNLOCK_COLOR);
+ RNA_def_property_ui_icon(prop, ICON_RESTRICT_COLOR_OFF, 1);
+ RNA_def_property_ui_text(prop, "Unlock color", "Unprotect colors selected from further editing "
+ "and/or frame changes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+
/* expose as layers.active */
#if 0
prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
@@ -873,18 +1272,42 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "X Ray", "Make the layer draw in front of objects");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
-
- /* Read-only state props (for simpler UI code) */
- prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_stroke_visible_get", NULL);
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible");
-
- prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_fill_visible_get", NULL);
+ /* Parent object */
+ prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_GPencilLayer_parent_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_ui_text(prop, "Parent", "Parent Object");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* parent type */
+ prop = RNA_def_property(srna, "parent_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "partype");
+ RNA_def_property_enum_items(prop, parent_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, "rna_GPencilLayer_parent_type_set", "rna_Object_parent_type_itemf");
+ RNA_def_property_ui_text(prop, "Parent Type", "Type of parent relation");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* parent bone */
+ prop = RNA_def_property(srna, "parent_bone", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "parsubstr");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilLayer_parent_bone_set");
+ RNA_def_property_ui_text(prop, "Parent Bone", "Name of parent bone in case of a bone parenting relation");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* matrix */
+ prop = RNA_def_property(srna, "matrix_inverse", PROP_FLOAT, PROP_MATRIX);
+ RNA_def_property_float_sdna(prop, NULL, "inverse");
+ RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "MatrixInverse", "Parent inverse transformation matrix");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* read only parented flag */
+ prop = RNA_def_property(srna, "is_parented", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_parented_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible");
-
+ RNA_def_property_ui_text(prop, "Is Parented", "True when the layer parent object is set");
+
/* Layers API */
func = RNA_def_function(srna, "clear", "rna_GPencil_layer_clear");
RNA_def_function_ui_description(func, "Remove all the grease pencil layer data");
@@ -933,6 +1356,207 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer");
}
+static void rna_def_gpencil_palettecolor(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "GPencilPaletteColor", NULL);
+ RNA_def_struct_sdna(srna, "bGPDpalettecolor");
+ RNA_def_struct_ui_text(srna, "Grease Pencil Palette color", "Collection of related colors");
+ RNA_def_struct_path_func(srna, "rna_GPencilPalette_color_path");
+
+ /* Stroke Drawing Color */
+ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "color");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Color", "Color for strokes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "color[3]");
+ RNA_def_property_range(prop, 0.0, 1.0f);
+ RNA_def_property_ui_text(prop, "Opacity", "Color Opacity");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Name */
+ prop = RNA_def_property(srna, "info", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Info", "Color name");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPaletteColor_info_set");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Fill Drawing Color */
+ prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "fill");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Fill alpha */
+ prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fill[3]");
+ RNA_def_property_range(prop, 0.0, 1.0f);
+ RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Flags */
+ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HIDE);
+ RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, 1);
+ RNA_def_property_ui_text(prop, "Hide", "Set color Visibility");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_LOCKED);
+ RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1);
+ RNA_def_property_ui_text(prop, "Locked", "Protect color from further editing and/or frame changes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ prop = RNA_def_property(srna, "ghost", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_ONIONSKIN);
+ RNA_def_property_ui_icon(prop, ICON_GHOST_ENABLED, 0);
+ RNA_def_property_ui_text(prop, "Ghost", "Display the color in onion skinning");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Draw Style */
+ prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_VOLUMETRIC);
+ RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in "
+ "a volumetric effect");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Use High quality fill */
+ prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HQ_FILL);
+ RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality to avoid glitches "
+ "(slower fps during animation play)");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Read-only state props (for simpler UI code) */
+ prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_stroke_visible_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible");
+
+ prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_fill_visible_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible");
+}
+
+/* palette colors api */
+static void rna_def_gpencil_palettecolors_api(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "GPencilPaletteColors");
+ srna = RNA_def_struct(brna, "GPencilPaletteColors", NULL);
+ RNA_def_struct_sdna(srna, "bGPDpalette");
+ RNA_def_struct_ui_text(srna, "Palette colors", "Collection of palette colors");
+
+ func = RNA_def_function(srna, "new", "rna_GPencilPalette_color_new");
+ RNA_def_function_ui_description(func, "Add a new color to the palette");
+ parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The newly created color");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_GPencilPalette_color_remove");
+ RNA_def_function_ui_description(func, "Remove a color from the palette");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The color to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "GPencilPaletteColor");
+ RNA_def_property_pointer_funcs(prop, "rna_GPencilPalette_active_color_get", "rna_GPencilPalette_active_color_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Active Palette Color", "Current active color");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_GPencilPaletteColor_index_get",
+ "rna_GPencilPaletteColor_index_set",
+ "rna_GPencilPaletteColor_index_range");
+ RNA_def_property_ui_text(prop, "Active color Index", "Index of active palette color");
+}
+
+static void rna_def_gpencil_palette(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "GPencilPalette", NULL);
+ RNA_def_struct_sdna(srna, "bGPDpalette");
+ RNA_def_struct_ui_text(srna, "Grease Pencil Palette", "Collection of related palettes");
+ RNA_def_struct_ui_icon(srna, ICON_COLOR);
+
+ /* Name */
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "info");
+ RNA_def_property_ui_text(prop, "Name", "Palette name");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPalette_info_set");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Colors */
+ prop = RNA_def_property(srna, "colors", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "colors", NULL);
+ RNA_def_property_struct_type(prop, "GPencilPaletteColor");
+ RNA_def_property_ui_text(prop, "Colors", "Colors of the palette");
+ rna_def_gpencil_palettecolors_api(brna, prop);
+
+}
+
+static void rna_def_gpencil_palettes_api(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "GreasePencilPalettes");
+ srna = RNA_def_struct(brna, "GreasePencilPalettes", NULL);
+ RNA_def_struct_sdna(srna, "bGPdata");
+ RNA_def_struct_ui_text(srna, "Grease Pencil Palettes", "Collection of grease pencil palettes");
+
+ func = RNA_def_function(srna, "new", "rna_GPencil_palette_new");
+ RNA_def_function_ui_description(func, "Add a new grease pencil palette");
+ parm = RNA_def_string(func, "name", "GPencilPalette", MAX_NAME, "Name", "Name of the palette");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_boolean(func, "set_active", 0, "Set Active", "Activate the newly created palette");
+ parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The newly created palette");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_GPencil_palette_remove");
+ RNA_def_function_ui_description(func, "Remove a grease pencil palette");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The palette to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "GPencilPalette");
+ RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_palette_get", "rna_GPencil_active_palette_set",
+ NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Active Palette", "Current active palette");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_GPencilPalette_index_get",
+ "rna_GPencilPalette_index_set",
+ "rna_GPencilPalette_index_range");
+ RNA_def_property_ui_text(prop, "Active Palette Index", "Index of active palette");
+}
+
static void rna_def_gpencil_data(BlenderRNA *brna)
{
StructRNA *srna;
@@ -951,6 +1575,13 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Layers", "");
rna_def_gpencil_layers_api(brna, prop);
+ /* Palettes */
+ prop = RNA_def_property(srna, "palettes", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "palettes", NULL);
+ RNA_def_property_struct_type(prop, "GPencilPalette");
+ RNA_def_property_ui_text(prop, "Palettes", "");
+ rna_def_gpencil_palettes_api(brna, prop);
+
/* Animation Data */
rna_def_animdata_common(srna);
@@ -967,6 +1598,12 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
"Show ghosts of the frames before and after the current frame, toggle to enable on active layer or disable all");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+ prop = RNA_def_property(srna, "show_stroke_direction", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_DIRECTION);
+ RNA_def_property_ui_text(prop, "Show Direction", "Show stroke drawing direction with a bigger green dot (start) "
+ "and smaller red dot (end) points");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
/* API Functions */
func = RNA_def_function(srna, "clear", "rna_GPencil_clear");
RNA_def_function_ui_description(func, "Remove all the grease pencil data");
@@ -980,8 +1617,12 @@ void RNA_def_gpencil(BlenderRNA *brna)
rna_def_gpencil_layer(brna);
rna_def_gpencil_frame(brna);
+ rna_def_gpencil_triangle(brna);
rna_def_gpencil_stroke(brna);
rna_def_gpencil_stroke_point(brna);
+
+ rna_def_gpencil_palette(brna);
+ rna_def_gpencil_palettecolor(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 58746ee82e7..7dc3dacb36f 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -134,6 +134,7 @@ void RNA_def_actuator(struct BlenderRNA *brna);
void RNA_def_blenvm(struct BlenderRNA *brna);
void RNA_def_boid(struct BlenderRNA *brna);
void RNA_def_brush(struct BlenderRNA *brna);
+void RNA_def_cachefile(struct BlenderRNA *brna);
void RNA_def_camera(struct BlenderRNA *brna);
void RNA_def_cloth(struct BlenderRNA *brna);
void RNA_def_color(struct BlenderRNA *brna);
@@ -334,6 +335,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop);
+void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop);
/* ID Properties */
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index e0538d1c05b..3392d0d9b3a 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -281,6 +281,12 @@ static void rna_Main_linestyle_begin(CollectionPropertyIterator *iter, PointerRN
rna_iterator_listbase_begin(iter, &bmain->linestyle, NULL);
}
+static void rna_Main_cachefiles_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Main *bmain = (Main *)ptr->data;
+ rna_iterator_listbase_begin(iter, &bmain->cachefiles, NULL);
+}
+
static void rna_Main_version_get(PointerRNA *ptr, int *value)
{
Main *bmain = (Main *)ptr->data;
@@ -354,6 +360,7 @@ void RNA_def_main(BlenderRNA *brna)
{"movieclips", "MovieClip", "rna_Main_movieclips_begin", "Movie Clips", "Movie Clip datablocks", RNA_def_main_movieclips},
{"masks", "Mask", "rna_Main_masks_begin", "Masks", "Masks datablocks", RNA_def_main_masks},
{"linestyles", "FreestyleLineStyle", "rna_Main_linestyle_begin", "Line Styles", "Line Style datablocks", RNA_def_main_linestyles},
+ {"cache_files", "CacheFile", "rna_Main_cachefiles_begin", "Cache Files", "Cache Files datablocks", RNA_def_main_cachefiles},
{NULL, NULL, NULL, NULL, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 0083207efd0..7c627e72fd5 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -535,6 +535,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(gpencil, gpencil, ID_GD)
RNA_MAIN_ID_TAG_FUNCS_DEF(movieclips, movieclip, ID_MC)
RNA_MAIN_ID_TAG_FUNCS_DEF(masks, mask, ID_MSK)
RNA_MAIN_ID_TAG_FUNCS_DEF(linestyle, linestyle, ID_LS)
+RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF)
#undef RNA_MAIN_ID_TAG_FUNCS_DEF
@@ -1548,6 +1549,21 @@ void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Main_palettes_is_updated_get", NULL);
}
+void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ RNA_def_property_srna(cprop, "BlendDataCacheFiles");
+ StructRNA *srna = RNA_def_struct(brna, "BlendDataCacheFiles", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Cache Files", "Collection of cache files");
+
+ FunctionRNA *func = RNA_def_function(srna, "tag", "rna_Main_cachefiles_tag");
+ PropertyRNA *parm = RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ PropertyRNA *prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_Main_cachefiles_is_updated_get", NULL);
+}
void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -1564,7 +1580,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_property_flag(parm, PROP_REQUIRED);
- func = RNA_def_function(srna, "new", "gpencil_data_addnew");
+ func = RNA_def_function(srna, "new", "BKE_gpencil_data_addnew");
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_string(func, "name", "GreasePencil", 0, "", "New name for the data-block");
RNA_def_property_flag(parm, PROP_REQUIRED);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index a23ef6eaa82..0b55c19c374 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include "DNA_armature_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
@@ -65,6 +66,7 @@ EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{0, "", 0, N_("Modify"), ""},
{eModifierType_DataTransfer, "DATA_TRANSFER", ICON_MOD_DATA_TRANSFER, "Data Transfer", ""},
{eModifierType_MeshCache, "MESH_CACHE", ICON_MOD_MESHDEFORM, "Mesh Cache", ""},
+ {eModifierType_MeshSequenceCache, "MESH_SEQUENCE_CACHE", ICON_MOD_MESHDEFORM, "Mesh Sequence Cache", ""},
{eModifierType_NormalEdit, "NORMAL_EDIT", ICON_MOD_NORMALEDIT, "Normal Edit", ""},
{eModifierType_UVProject, "UV_PROJECT", ICON_MOD_UVPROJECT, "UV Project", ""},
{eModifierType_UVWarp, "UV_WARP", ICON_MOD_UVPROJECT, "UV Warp", ""},
@@ -281,6 +283,7 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
#include "DNA_curve_types.h"
#include "DNA_smoke_types.h"
+#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_library.h"
@@ -288,6 +291,10 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
#include "BKE_object.h"
#include "BKE_particle.h"
+#ifdef WITH_ALEMBIC
+# include "ABC_alembic.h"
+#endif
+
static void rna_UVProject_projectors_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
UVProjectModifierData *uvp = (UVProjectModifierData *)ptr->data;
@@ -399,6 +406,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
return &RNA_NormalEditModifier;
case eModifierType_CorrectiveSmooth:
return &RNA_CorrectiveSmoothModifier;
+ case eModifierType_MeshSequenceCache:
+ return &RNA_MeshSequenceCacheModifier;
/* Default */
case eModifierType_None:
case eModifierType_ShapeKey:
@@ -4216,6 +4225,42 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
+static void rna_def_modifier_meshseqcache(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MeshSequenceCacheModifier", "Modifier");
+ RNA_def_struct_ui_text(srna, "Cache Modifier", "Cache Mesh");
+ RNA_def_struct_sdna(srna, "MeshSeqCacheModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_MESHDEFORM); /* XXX, needs own icon */
+
+ prop = RNA_def_property(srna, "cache_file", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cache_file");
+ RNA_def_property_struct_type(prop, "CacheFile");
+ RNA_def_property_ui_text(prop, "Cache File", "");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+
+ prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup geometric data");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ static EnumPropertyItem read_flag_items[] = {
+ {MOD_MESHSEQ_READ_VERT, "VERT", 0, "Vertex", ""},
+ {MOD_MESHSEQ_READ_POLY, "POLY", 0, "Faces", ""},
+ {MOD_MESHSEQ_READ_UV, "UV", 0, "UV", ""},
+ {MOD_MESHSEQ_READ_COLOR, "COLOR", 0, "Color", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ prop = RNA_def_property(srna, "read_data", PROP_ENUM, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_enum_sdna(prop, NULL, "read_flag");
+ RNA_def_property_enum_items(prop, read_flag_items);
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+}
+
static void rna_def_modifier_laplaciandeform(BlenderRNA *brna)
{
StructRNA *srna;
@@ -4745,6 +4790,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_wireframe(brna);
rna_def_modifier_datatransfer(brna);
rna_def_modifier_normaledit(brna);
+ rna_def_modifier_meshseqcache(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 33148549c45..3b91ddad969 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2690,6 +2690,10 @@ static void rna_Node_image_layer_update(Main *bmain, Scene *scene, PointerRNA *p
BKE_image_signal(ima, iuser, IMA_SIGNAL_SRC_CHANGE);
rna_Node_update(bmain, scene, ptr);
+
+ if (scene->nodetree != NULL) {
+ ntreeCompositForceHidden(scene->nodetree);
+ }
}
static EnumPropertyItem *renderresult_layers_add_enum(RenderLayer *rl)
@@ -2823,6 +2827,14 @@ static EnumPropertyItem *rna_Node_scene_layer_itemf(bContext *UNUSED(C), Pointer
return item;
}
+static void rna_Node_scene_layer_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ rna_Node_update(bmain, scene, ptr);
+ if (scene->nodetree != NULL) {
+ ntreeCompositForceHidden(scene->nodetree);
+ }
+}
+
static EnumPropertyItem *rna_Node_channel_itemf(bContext *UNUSED(C), PointerRNA *ptr,
PropertyRNA *UNUSED(prop), bool *r_free)
{
@@ -4868,7 +4880,7 @@ static void def_cmp_render_layers(StructRNA *srna)
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Node_scene_layer_itemf");
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
RNA_def_property_ui_text(prop, "Layer", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_scene_layer_update");
}
static void rna_def_cmp_output_file_slot_file(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index a8d8407fe74..f05813043ca 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -1852,6 +1852,11 @@ static void rna_def_softbody(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Self Collision", "Enable naive vertex ball self collision");
RNA_def_property_update(prop, 0, "rna_softbody_update");
+ prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group");
+ RNA_def_property_update(prop, 0, "rna_softbody_update");
+
prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "effector_weights");
RNA_def_property_struct_type(prop, "EffectorWeights");
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index a52473b95f2..eabf41cb332 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -2741,6 +2741,10 @@ static void rna_def_particle_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Random Size", "Give the particle size a random variation");
RNA_def_property_update(prop, 0, "rna_Particle_reset");
+ prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
/* global physical properties */
prop = RNA_def_property(srna, "drag_factor", PROP_FLOAT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index ed90f146f4d..156c327f97c 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -35,6 +35,7 @@
#include "DNA_linestyle_types.h"
#include "DNA_userdef_types.h"
#include "DNA_world_types.h"
+#include "DNA_gpencil_types.h"
#include "IMB_imbuf_types.h"
@@ -437,6 +438,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = {
#include "BKE_sequencer.h"
#include "BKE_animsys.h"
#include "BKE_freestyle.h"
+#include "BKE_gpencil.h"
#include "ED_info.h"
#include "ED_node.h"
@@ -449,6 +451,108 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = {
#include "FRS_freestyle.h"
#endif
+/* Grease pencil Drawing Brushes */
+static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, int setactive)
+{
+ bGPDbrush *brush = BKE_gpencil_brush_addnew(ts, name, setactive != 0);
+
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return brush;
+}
+
+static void rna_GPencil_brush_remove(ToolSettings *ts, ReportList *reports, PointerRNA *brush_ptr)
+{
+ bGPDbrush *brush = brush_ptr->data;
+ if (BLI_findindex(&ts->gp_brushes, brush) == -1) {
+ BKE_report(reports, RPT_ERROR, "Brush not found in grease pencil data");
+ return;
+ }
+
+ BKE_gpencil_brush_delete(ts, brush);
+ RNA_POINTER_INVALIDATE(brush_ptr);
+
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static PointerRNA rna_GPencilBrushes_active_get(PointerRNA *ptr)
+{
+ ToolSettings *ts = (ToolSettings *) ptr->data;
+
+ bGPDbrush *brush;
+
+ for (brush = ts->gp_brushes.first; brush; brush = brush->next) {
+ if (brush->flag & GP_BRUSH_ACTIVE) {
+ break;
+ }
+ }
+
+ if (brush) {
+ return rna_pointer_inherit_refine(ptr, &RNA_GPencilBrush, brush);
+ }
+
+ return rna_pointer_inherit_refine(ptr, NULL, NULL);
+}
+
+static void rna_GPencilBrushes_active_set(PointerRNA *ptr, PointerRNA value)
+{
+ ToolSettings *ts = (ToolSettings *) ptr->data;
+
+ bGPDbrush *brush;
+
+ for (brush = ts->gp_brushes.first; brush; brush = brush->next) {
+ if (brush == value.data) {
+ brush->flag |= GP_BRUSH_ACTIVE;
+ }
+ else {
+ brush->flag &= ~GP_BRUSH_ACTIVE;
+ }
+ }
+ WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
+}
+
+static int rna_GPencilBrushes_index_get(PointerRNA *ptr)
+{
+ ToolSettings *ts = (ToolSettings *) ptr->data;
+ bGPDbrush *brush = BKE_gpencil_brush_getactive(ts);
+
+ return BLI_findindex(&ts->gp_brushes, brush);
+}
+
+static void rna_GPencilBrushes_index_set(PointerRNA *ptr, int value)
+{
+ ToolSettings *ts = (ToolSettings *) ptr->data;
+
+ bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, value);
+
+ BKE_gpencil_brush_setactive(ts, brush);
+ WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static void rna_GPencilBrushes_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
+{
+ ToolSettings *ts = (ToolSettings *) ptr->data;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&ts->gp_brushes) - 1);
+
+ *softmin = *min;
+ *softmax = *max;
+}
+
+static void rna_GPencilBrush_name_set(PointerRNA *ptr, const char *value)
+{
+ ToolSettings *ts = ((Scene *) ptr->id.data)->toolsettings;
+ bGPDbrush *brush = ptr->data;
+
+ /* copy the new name into the name slot */
+ BLI_strncpy_utf8(brush->info, value, sizeof(brush->info));
+
+ BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info));
+}
+
+/* ----------------- end of Grease pencil drawing brushes ------------*/
+
static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
{
ED_space_image_uv_sculpt_update(bmain->wm.first, scene);
@@ -1991,6 +2095,203 @@ static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr))
#else
+/* Grease Pencil Drawing Brushes */
+static void rna_def_gpencil_brush(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "GPencilBrush", NULL);
+ RNA_def_struct_sdna(srna, "bGPDbrush");
+ RNA_def_struct_ui_text(srna, "Grease Pencil Brush",
+ "Collection of brushes being used to control the line style of new strokes");
+ RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA);
+
+ /* Name */
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "info");
+ RNA_def_property_ui_text(prop, "Name", "Brush name");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilBrush_name_set");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Line Thickness */
+ prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "thickness");
+ RNA_def_property_range(prop, 1, 300);
+ RNA_def_property_ui_range(prop, 1, 10, 1, 0);
+ RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Sensitivity factor for new strokes */
+ prop = RNA_def_property(srna, "pen_sensitivity_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_sensitivity");
+ RNA_def_property_range(prop, 0.1f, 3.0f);
+ RNA_def_property_ui_text(prop, "Sensitivity", "Pressure sensitivity factor for new strokes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Strength factor for new strokes */
+ prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_strength");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Strength", "Color strength for new strokes (affect alpha factor of color)");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Jitter factor for new strokes */
+ prop = RNA_def_property(srna, "jitter", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_jitter");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Jitter", "Jitter factor for new strokes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Randomnes factor for sensitivity and strength */
+ prop = RNA_def_property(srna, "random_press", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_random_press");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Randomness", "Randomness factor for pressure and strength in new strokes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Randomnes factor for subdivision */
+ prop = RNA_def_property(srna, "random_subdiv", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_random_sub");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Subdivision", "Randomness factor for new strokes after subdivision");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Angle when brush is full size */
+ prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_angle");
+ RNA_def_property_range(prop, 0.0f, M_PI_2);
+ RNA_def_property_ui_text(prop, "Angle", "Angle of drawing when brush has full size");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Factor to change brush size depending of angle */
+ prop = RNA_def_property(srna, "angle_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_angle_factor");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Angle Factor", "Factor to apply when the brush rotate of its full size");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Smoothing factor for new strokes */
+ prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac");
+ RNA_def_property_range(prop, 0.0, 2.0f);
+ RNA_def_property_ui_text(prop, "Smooth", "Amount of smoothing to apply to newly created strokes, to "
+ "reduce jitter/noise");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Iterations of the Smoothing factor */
+ prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl");
+ RNA_def_property_range(prop, 1, 3);
+ RNA_def_property_ui_text(prop, "Iterations",
+ "Number of times to smooth newly created strokes "
+ "[+ reason/effect of using higher values of this property]");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Subdivision level for new strokes */
+ prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "sublevel");
+ RNA_def_property_range(prop, 0, 3);
+ RNA_def_property_ui_text(prop, "Subdivision Steps",
+ "Number of times to subdivide newly created strokes, for less jagged strokes");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Curves for pressure */
+ prop = RNA_def_property(srna, "curve_sensitivity", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cur_sensitivity");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Curve Sensitivity", "Curve used for the sensitivity");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ prop = RNA_def_property(srna, "curve_strength", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cur_strength");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Curve Strength", "Curve used for the strength");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ prop = RNA_def_property(srna, "curve_jitter", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cur_jitter");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Curve Jitter", "Curve used for the jitter effect");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ /* Flags */
+ prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure", "Use tablet pressure");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ prop = RNA_def_property(srna, "use_strength_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_STENGTH_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure Strength", "Use tablet pressure for color strength");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ prop = RNA_def_property(srna, "use_jitter_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_JITTER_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
+ RNA_def_property_ui_text(prop, "Use Pressure Jitter", "Use tablet pressure for jitter");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ prop = RNA_def_property(srna, "use_random_pressure", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_PRESSURE);
+ RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0);
+ RNA_def_property_ui_text(prop, "Random Pressure", "Use random value for pressure");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+ prop = RNA_def_property(srna, "use_random_strength", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_STRENGTH);
+ RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0);
+ RNA_def_property_ui_text(prop, "Random Strength", "Use random value for strength");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
+}
+
+/* Grease Pencil Drawing Brushes API */
+static void rna_def_gpencil_brushes(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "GreasePencilBrushes");
+ srna = RNA_def_struct(brna, "GreasePencilBrushes", NULL);
+ RNA_def_struct_sdna(srna, "ToolSettings");
+ RNA_def_struct_ui_text(srna, "Grease Pencil Brushes", "Collection of grease pencil brushes");
+
+ func = RNA_def_function(srna, "new", "rna_GPencil_brush_new");
+ RNA_def_function_ui_description(func, "Add a new grease pencil brush");
+ parm = RNA_def_string(func, "name", "GPencilBrush", MAX_NAME, "Name", "Name of the brush");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_boolean(func, "set_active", 0, "Set Active", "Set the newly created brush to the active brush");
+ parm = RNA_def_pointer(func, "palette", "GPencilBrush", "", "The newly created brush");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_GPencil_brush_remove");
+ RNA_def_function_ui_description(func, "Remove a grease pencil brush");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "brush", "GPencilBrush", "", "The brush to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "GPencilBrush");
+ RNA_def_property_pointer_funcs(prop, "rna_GPencilBrushes_active_get", "rna_GPencilBrushes_active_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Active Brush", "Current active brush");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_GPencilBrushes_index_get",
+ "rna_GPencilBrushes_index_set",
+ "rna_GPencilBrushes_index_range");
+ RNA_def_property_ui_text(prop, "Active brush Index", "Index of active brush");
+}
+
static void rna_def_transform_orientation(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2311,6 +2612,12 @@ static void rna_def_tool_settings(BlenderRNA *brna)
"are included as the basis for the new one");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ prop = RNA_def_property(srna, "use_gpencil_draw_onback", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_PAINT_ONBACK);
+ RNA_def_property_ui_text(prop, "Draw Strokes on Back",
+ "When draw new strokes, the new stroke is drawn below of all strokes in the layer");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
prop = RNA_def_property(srna, "grease_pencil_source", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_src");
RNA_def_property_enum_items(prop, gpencil_source_3d_items);
@@ -2322,7 +2629,14 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt");
RNA_def_property_struct_type(prop, "GPencilSculptSettings");
RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", "");
-
+
+ /* Grease Pencil - Drawing brushes */
+ prop = RNA_def_property(srna, "gpencil_brushes", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "gp_brushes", NULL);
+ RNA_def_property_struct_type(prop, "GPencilBrush");
+ RNA_def_property_ui_text(prop, "Grease Pencil Brushes", "Grease Pencil drawing brushes");
+ rna_def_gpencil_brushes(brna, prop);
+
/* Grease Pencil - 3D View Stroke Placement */
prop = RNA_def_property(srna, "gpencil_stroke_placement_view3d", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v3d_align");
@@ -6839,6 +7153,7 @@ void RNA_def_scene(BlenderRNA *brna)
/* *** Non-Animated *** */
RNA_define_animate_sdna(false);
rna_def_tool_settings(brna);
+ rna_def_gpencil_brush(brna);
rna_def_unified_paint_settings(brna);
rna_def_curve_paint_settings(brna);
rna_def_statvis(brna);
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 1d3537dc9a0..ed51f0cffb0 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -37,6 +37,7 @@
#include "BLI_path_util.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
@@ -44,6 +45,18 @@
#include "rna_internal.h" /* own include */
+#ifdef WITH_ALEMBIC
+# include "../../alembic/ABC_alembic.h"
+#endif
+
+EnumPropertyItem rna_enum_abc_compression_items[] = {
+#ifdef WITH_ALEMBIC
+ { ABC_ARCHIVE_OGAWA, "OGAWA", 0, "Ogawa", "" },
+ { ABC_ARCHIVE_HDF5, "HDF5", 0, "HDF5", "" },
+#endif
+ { 0, NULL, 0, NULL, NULL }
+};
+
#ifdef RNA_RUNTIME
#include "BKE_animsys.h"
@@ -173,6 +186,73 @@ static void rna_Scene_ray_cast(
}
}
+#ifdef WITH_ALEMBIC
+
+static void rna_Scene_alembic_export(
+ Scene *scene,
+ bContext *C,
+ const char *filepath,
+ int frame_start,
+ int frame_end,
+ int xform_samples,
+ int geom_samples,
+ float shutter_open,
+ float shutter_close,
+ int selected_only,
+ int uvs,
+ int normals,
+ int vcolors,
+ int apply_subdiv,
+ int flatten_hierarchy,
+ int visible_layers_only,
+ int renderable_only,
+ int face_sets,
+ int use_subdiv_schema,
+ int compression_type,
+ int packuv,
+ float scale)
+{
+/* We have to enable allow_threads, because we may change scene frame number
+ * during export. */
+#ifdef WITH_PYTHON
+ BPy_BEGIN_ALLOW_THREADS;
+#endif
+
+ const struct AlembicExportParams params = {
+ .frame_start = frame_start,
+ .frame_end = frame_end,
+
+ .frame_step_xform = 1.0 / (double)xform_samples,
+ .frame_step_shape = 1.0 / (double)geom_samples,
+
+ .shutter_open = shutter_open,
+ .shutter_close = shutter_close,
+
+ .selected_only = selected_only,
+ .uvs = uvs,
+ .normals = normals,
+ .vcolors = vcolors,
+ .apply_subdiv = apply_subdiv,
+ .flatten_hierarchy = flatten_hierarchy,
+ .visible_layers_only = visible_layers_only,
+ .renderable_only = renderable_only,
+ .face_sets = face_sets,
+ .use_subdiv_schema = use_subdiv_schema,
+ .compression_type = compression_type,
+ .packuv = packuv,
+
+ .global_scale = scale,
+ };
+
+ ABC_export(scene, C, filepath, &params);
+
+#ifdef WITH_PYTHON
+ BPy_END_ALLOW_THREADS;
+#endif
+}
+
+#endif
+
#ifdef WITH_COLLADA
/* don't remove this, as COLLADA exporting cannot be done through operators in render() callback. */
#include "../../collada/collada.h"
@@ -296,6 +376,37 @@ void RNA_api_scene(StructRNA *srna)
RNA_def_function_ui_description(func, "Export to collada file");
#endif
+
+#ifdef WITH_ALEMBIC
+ func = RNA_def_function(srna, "alembic_export", "rna_Scene_alembic_export");
+ RNA_def_function_ui_description(func, "Export to Alembic file");
+
+ parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Alembic file");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_property_subtype(parm, PROP_FILEPATH); /* allow non utf8 */
+
+ RNA_def_int(func, "frame_start", 1, INT_MIN, INT_MAX, "Start", "Start Frame", INT_MIN, INT_MAX);
+ RNA_def_int(func, "frame_end", 1, INT_MIN, INT_MAX, "End", "End Frame", INT_MIN, INT_MAX);
+ RNA_def_int(func, "xform_samples", 1, 1, 128, "Xform samples", "Transform samples per frame", 1, 128);
+ RNA_def_int(func, "geom_samples", 1, 1, 128, "Geom samples", "Geometry samples per frame", 1, 128);
+ RNA_def_float(func, "shutter_open", 0.0f, -1.0f, 1.0f, "Shutter open", "", -1.0f, 1.0f);
+ RNA_def_float(func, "shutter_close", 1.0f, -1.0f, 1.0f, "Shutter close", "", -1.0f, 1.0f);
+ RNA_def_boolean(func, "selected_only" , 0, "Selected only", "Export only selected objects");
+ RNA_def_boolean(func, "uvs" , 1, "UVs", "Export UVs");
+ RNA_def_boolean(func, "normals" , 1, "Normals", "Export cormals");
+ RNA_def_boolean(func, "vcolors" , 0, "Vertex colors", "Export vertex colors");
+ RNA_def_boolean(func, "apply_subdiv" , 1, "Subsurfs as meshes", "Export subdivision surfaces as meshes");
+ RNA_def_boolean(func, "flatten" , 0, "Flatten hierarchy", "Flatten hierarchy");
+ RNA_def_boolean(func, "visible_layers_only" , 0, "Visible layers only", "Export only objects in visible layers");
+ RNA_def_boolean(func, "renderable_only" , 0, "Renderable objects only", "Export only objects marked renderable in the outliner");
+ RNA_def_boolean(func, "face_sets" , 0, "Facesets", "Export face sets");
+ RNA_def_boolean(func, "subdiv_schema", 0, "Use Alembic subdivision Schema", "Use Alembic subdivision Schema");
+ RNA_def_enum(func, "compression_type", rna_enum_abc_compression_items, 0, "Compression", "");
+ RNA_def_boolean(func, "packuv" , 0, "Export with packed UV islands", "Export with packed UV islands");
+ RNA_def_float(func, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", "Value by which to enlarge or shrink the objects with respect to the world's origin", 0.0001f, 1000.0f);
+
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+#endif
}
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 90215bc883f..acde2e0957e 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -63,7 +63,8 @@ static EnumPropertyItem particle_edit_hair_brush_items[] = {
EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = {
{GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points"},
{GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes"},
- {GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle"},
+ { GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" },
+ { GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" },
{GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them"},
{GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush"},
{GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush"},
@@ -71,7 +72,7 @@ EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = {
//{GP_EDITBRUSH_TYPE_SUBDIVIDE, "SUBDIVIDE", 0, "Subdivide", "Increase point density for higher resolution strokes when zoomed in"},
//{GP_EDITBRUSH_TYPE_SIMPLIFY, "SIMPLIFY", 0, "Simplify", "Reduce density of stroke points"},
{GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard"},
- {0, NULL, 0, NULL, NULL}
+ { 0, NULL, 0, NULL, NULL }
};
EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
@@ -100,6 +101,11 @@ EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
#include "ED_particle.h"
+static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+{
+ WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
+}
+
static EnumPropertyItem particle_edit_disconnected_hair_brush_items[] = {
{PE_BRUSH_NONE, "NONE", 0, "None", "Don't use any brush"},
{PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb hairs"},
@@ -1016,6 +1022,27 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_VERTEXSEL, 0); // FIXME: this needs a custom icon
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ prop = RNA_def_property(srna, "affect_position", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_POSITION);
+ RNA_def_property_ui_text(prop, "Affect position", "The brush affects the position of the point");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ prop = RNA_def_property(srna, "affect_strength", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_STRENGTH);
+ RNA_def_property_ui_text(prop, "Affect strength", "The brush affects the color strength of the point");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ prop = RNA_def_property(srna, "affect_thickness", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_THICKNESS);
+ RNA_def_property_ui_text(prop, "Affect thickness", "The brush affects the thickness of the point");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ prop = RNA_def_property(srna, "selection_alpha", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "alpha");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Alpha", "Alpha value for selected vertices");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update");
+
/* brush */
srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL);
RNA_def_struct_sdna(srna, "GP_EditBrush_Data");
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index 9a31952b84b..dad5577dc12 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -76,6 +76,21 @@ static void rna_Smoke_resetCache(Main *UNUSED(bmain), Scene *UNUSED(scene), Poin
DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
}
+static void rna_Smoke_cachetype_set(struct PointerRNA *ptr, int value)
+{
+ SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data;
+ Object *ob = (Object *)ptr->id.data;
+
+ if (value != settings->cache_file_format) {
+ /* Clear old caches. */
+ PTCacheID id;
+ BKE_ptcache_id_from_smoke(&id, ob, settings->smd);
+ BKE_ptcache_id_clear(&id, PTCACHE_CLEAR_ALL, 0);
+
+ settings->cache_file_format = value;
+ }
+}
+
static void rna_Smoke_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
{
SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data;
@@ -701,6 +716,7 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "cache_file_format", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "cache_file_format");
RNA_def_property_enum_items(prop, cache_file_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, "rna_Smoke_cachetype_set", NULL);
RNA_def_property_ui_text(prop, "File Format", "Select the file format to be used for caching");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
}
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 4818fe0cd35..1c8d4a4c818 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -3355,6 +3355,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna)
{SACTCONT_SHAPEKEY, "SHAPEKEY", ICON_SHAPEKEY_DATA, "Shape Key Editor", "Edit keyframes in active object's Shape Keys action"},
{SACTCONT_GPENCIL, "GPENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Edit timings for all Grease Pencil sketches in file"},
{SACTCONT_MASK, "MASK", ICON_MOD_MASK, "Mask", "Edit timings for Mask Editor splines"},
+ {SACTCONT_CACHEFILE, "CACHEFILE", ICON_FILE, "Cache File", "Edit timings for Cache File data-blocks"},
{0, NULL, 0, NULL, NULL}
};
@@ -3799,6 +3800,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
{FILTER_ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armatures", "Show/hide Armature data-blocks"},
{FILTER_ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brushes", "Show/hide Brushes data-blocks"},
{FILTER_ID_CA, "CAMERA", ICON_CAMERA_DATA, "Cameras", "Show/hide Camera data-blocks"},
+ {FILTER_ID_CF, "CACHEFILE", ICON_FILE, "Cache Files", "Show/hide Cache File data-blocks"},
{FILTER_ID_CU, "CURVE", ICON_CURVE_DATA, "Curves", "Show/hide Curve data-blocks"},
{FILTER_ID_GD, "GREASE_PENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Show/hide Grease pencil data-blocks"},
{FILTER_ID_GR, "GROUP", ICON_GROUP, "Groups", "Show/hide Group data-blocks"},
@@ -3844,7 +3846,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
"IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"},
{FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO,
"ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"},
- {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF,
+ {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_CF,
"MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index 80777f57811..a751c414d83 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -912,6 +912,11 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_function_ui_description(func, "Node Socket Icon");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_float_array(func, "color", 4, node_socket_color_default, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f);
+
+ func = RNA_def_function(srna, "template_cache_file", "uiTemplateCacheFile");
+ RNA_def_function_ui_description(func, "Item(s). User interface for selecting cache files and their source paths");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ api_ui_item_rna_common(func);
}
#endif
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 5c17ab46a78..96257f36de2 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -74,6 +74,7 @@ set(SRC
intern/MOD_meshcache_pc2.c
intern/MOD_meshcache_util.c
intern/MOD_meshdeform.c
+ intern/MOD_meshsequencecache.c
intern/MOD_mirror.c
intern/MOD_multires.c
intern/MOD_none.c
@@ -113,6 +114,13 @@ set(SRC
intern/MOD_weightvg_util.h
)
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_ALEMBIC)
+ list(APPEND INC
+ ../alembic
+ )
+endif()
+
if(WITH_MOD_BOOLEAN)
add_definitions(-DWITH_MOD_BOOLEAN)
list(APPEND SRC
diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h
index a5d96759952..4c881445893 100644
--- a/source/blender/modifiers/MOD_modifiertypes.h
+++ b/source/blender/modifiers/MOD_modifiertypes.h
@@ -84,6 +84,7 @@ extern ModifierTypeInfo modifierType_Wireframe;
extern ModifierTypeInfo modifierType_DataTransfer;
extern ModifierTypeInfo modifierType_NormalEdit;
extern ModifierTypeInfo modifierType_CorrectiveSmooth;
+extern ModifierTypeInfo modifierType_MeshSequenceCache;
/* MOD_util.c */
void modifier_type_init(ModifierTypeInfo *types[]);
diff --git a/source/blender/modifiers/intern/MOD_fluidsim_util.c b/source/blender/modifiers/intern/MOD_fluidsim_util.c
index 77fbb482f8b..ffbbb1b0745 100644
--- a/source/blender/modifiers/intern/MOD_fluidsim_util.c
+++ b/source/blender/modifiers/intern/MOD_fluidsim_util.c
@@ -525,7 +525,7 @@ DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Scene *scene,
return dm;
/* sanity check */
- if (!fluidmd || (fluidmd && !fluidmd->fss))
+ if (!fluidmd || !fluidmd->fss)
return dm;
fss = fluidmd->fss;
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
new file mode 100644
index 00000000000..5a6a7904af5
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -0,0 +1,197 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/modifiers/intern/MOD_meshsequencecache.c
+ * \ingroup modifiers
+ */
+
+#include "DNA_cachefile_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_cachefile.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_library_query.h"
+#include "BKE_scene.h"
+
+#include "depsgraph_private.h"
+#include "DEG_depsgraph_build.h"
+
+#include "MOD_modifiertypes.h"
+
+#ifdef WITH_ALEMBIC
+# include "ABC_alembic.h"
+#endif
+
+static void initData(ModifierData *md)
+{
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
+
+ mcmd->cache_file = NULL;
+ mcmd->object_path[0] = '\0';
+ mcmd->read_flag = MOD_MESHSEQ_READ_ALL;
+}
+
+static void copyData(ModifierData *md, ModifierData *target)
+{
+#if 0
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
+#endif
+ MeshSeqCacheModifierData *tmcmd = (MeshSeqCacheModifierData *)target;
+
+ modifier_copyData_generic(md, target);
+
+ if (tmcmd->cache_file) {
+ id_us_plus(&tmcmd->cache_file->id);
+ }
+}
+
+static void freeData(ModifierData *md)
+{
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
+
+ if (mcmd->cache_file) {
+ id_us_min(&mcmd->cache_file->id);
+ }
+}
+
+static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams))
+{
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
+
+ /* leave it up to the modifier to check the file is valid on calculation */
+ return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0');
+}
+
+static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
+ DerivedMesh *dm,
+ ModifierApplyFlag flag)
+{
+#ifdef WITH_ALEMBIC
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
+
+ Scene *scene = md->scene;
+ const float frame = BKE_scene_frame_get(scene);
+ const float time = BKE_cachefile_time_offset(mcmd->cache_file, frame, FPS);
+
+ const char *err_str = NULL;
+
+ CacheFile *cache_file = mcmd->cache_file;
+
+ BKE_cachefile_ensure_handle(G.main, cache_file);
+
+ DerivedMesh *result = ABC_read_mesh(cache_file->handle,
+ ob,
+ dm,
+ mcmd->object_path,
+ time,
+ &err_str,
+ mcmd->read_flag);
+
+ if (err_str) {
+ modifier_setError(md, "%s", err_str);
+ }
+
+ return result ? result : dm;
+ UNUSED_VARS(flag);
+#else
+ return dm;
+ UNUSED_VARS(md, ob, flag);
+#endif
+}
+
+static bool dependsOnTime(ModifierData *md)
+{
+ UNUSED_VARS(md);
+ return true;
+}
+
+static void foreachIDLink(ModifierData *md, Object *ob,
+ IDWalkFunc walk, void *userData)
+{
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
+
+ walk(userData, ob, (ID **)&mcmd->cache_file, IDWALK_USER);
+}
+
+
+static void updateDepgraph(ModifierData *md, DagForest *forest,
+ struct Main *bmain,
+ struct Scene *scene,
+ Object *ob, DagNode *obNode)
+{
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
+
+ if (mcmd->cache_file != NULL) {
+ DagNode *curNode = dag_get_node(forest, mcmd->cache_file);
+
+ dag_add_relation(forest, curNode, obNode,
+ DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cache File Modifier");
+ }
+
+ UNUSED_VARS(bmain, scene, ob);
+}
+
+static void updateDepsgraph(ModifierData *md,
+ struct Main *bmain,
+ struct Scene *scene,
+ Object *ob,
+ struct DepsNodeHandle *node)
+{
+ MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md;
+
+ if (mcmd->cache_file != NULL) {
+ DEG_add_cache_relation(node, mcmd->cache_file, DEG_COMPONENT_CACHE, "Mesh Cache File");
+ }
+
+ UNUSED_VARS(bmain, scene, ob);
+}
+
+ModifierTypeInfo modifierType_MeshSequenceCache = {
+ /* name */ "Mesh Sequence Cache",
+ /* structName */ "MeshSeqCacheModifierData",
+ /* structSize */ sizeof(MeshSeqCacheModifierData),
+ /* type */ eModifierTypeType_Constructive,
+ /* flags */ eModifierTypeFlag_AcceptsMesh |
+ eModifierTypeFlag_AcceptsCVs,
+ /* copyData */ copyData,
+ /* deformVerts */ NULL,
+ /* deformMatrices */ NULL,
+ /* deformVertsEM */ NULL,
+ /* deformMatricesEM */ NULL,
+ /* applyModifier */ applyModifier,
+ /* applyModifierEM */ NULL,
+ /* initData */ initData,
+ /* requiredDataMask */ NULL,
+ /* freeData */ freeData,
+ /* isDisabled */ isDisabled,
+ /* updateDepgraph */ updateDepgraph,
+ /* updateDepsgraph */ updateDepsgraph,
+ /* dependsOnTime */ dependsOnTime,
+ /* dependsOnNormals */ NULL,
+ /* foreachObjectLink */ NULL,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ NULL,
+};
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index f9291fb077f..93414562ccf 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -286,5 +286,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(DataTransfer);
INIT_TYPE(NormalEdit);
INIT_TYPE(CorrectiveSmooth);
+ INIT_TYPE(MeshSequenceCache);
#undef INIT_TYPE
}
diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp
index 9d30c285dbf..ab65b73f0a6 100644
--- a/source/blender/physics/intern/BPH_mass_spring.cpp
+++ b/source/blender/physics/intern/BPH_mass_spring.cpp
@@ -376,7 +376,8 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s,
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
// current_position = xold + t * (newposition - xold)
- interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time);
+ /* divide by time_scale to prevent goal vertices' delta locations from being multiplied */
+ interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time / parms->time_scale);
sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1
scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring);
@@ -1004,6 +1005,8 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, EffectorCo
float v[3];
sub_v3_v3v3(v, verts[i].xconst, verts[i].xold);
// mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
+ /* divide by time_scale to prevent constrained velocities from being multiplied */
+ mul_v3_fl(v, 1.0f / clmd->sim_parms->time_scale);
BPH_mass_spring_set_velocity(id, i, v);
}
}
@@ -1070,7 +1073,8 @@ int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, EffectorCo
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
float x[3];
- interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt);
+ /* divide by time_scale to prevent pinned vertices' delta locations from being multiplied */
+ interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, (step + dt) / clmd->sim_parms->time_scale);
BPH_mass_spring_set_position(id, i, x);
}
}
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index 4006816e788..3148dab3c50 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -32,6 +32,7 @@
#ifndef __BPY_EXTERN_H__
#define __BPY_EXTERN_H__
+struct PathResolvedRNA;
struct Text; /* defined in DNA_text_types.h */
struct ID; /* DNA_ID.h */
struct Object; /* DNA_object_types.h */
@@ -85,7 +86,7 @@ void BPY_modules_load_user(struct bContext *C);
void BPY_app_handlers_reset(const short do_all);
void BPY_driver_reset(void);
-float BPY_driver_exec(struct ChannelDriver *driver, const float evaltime);
+float BPY_driver_exec(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime);
void BPY_DECREF(void *pyob_ptr); /* Py_DECREF() */
void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr);
diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c
index 551a66d1ed8..8f287918a4a 100644
--- a/source/blender/python/bmesh/bmesh_py_ops_call.c
+++ b/source/blender/python/bmesh/bmesh_py_ops_call.c
@@ -581,7 +581,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
switch (slot->slot_subtype.map) {
case BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
{
- item = PyDict_New();
+ item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0);
if (slot_hash) {
GHASH_ITER (hash_iter, slot_hash) {
BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
@@ -599,7 +599,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
}
case BMO_OP_SLOT_SUBTYPE_MAP_FLT:
{
- item = PyDict_New();
+ item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0);
if (slot_hash) {
GHASH_ITER (hash_iter, slot_hash) {
BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
@@ -617,7 +617,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
}
case BMO_OP_SLOT_SUBTYPE_MAP_INT:
{
- item = PyDict_New();
+ item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0);
if (slot_hash) {
GHASH_ITER (hash_iter, slot_hash) {
BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
@@ -635,7 +635,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
}
case BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
{
- item = PyDict_New();
+ item = _PyDict_NewPresized(slot_hash ? BLI_ghash_size(slot_hash) : 0);
if (slot_hash) {
GHASH_ITER (hash_iter, slot_hash) {
BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 11646f3f3df..2e15b7b1413 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -98,7 +98,7 @@ static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop)
static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop)
{
- PyObject *seq = PyList_New(prop->len), *wrap;
+ PyObject *seq = PyList_New(prop->len);
IDProperty *array = IDP_IDPArray(prop);
int i;
@@ -110,10 +110,13 @@ static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop)
}
for (i = 0; i < prop->len; i++) {
- wrap = BPy_IDGroup_WrapData(id, array++, prop);
+ PyObject *wrap = BPy_IDGroup_WrapData(id, array++, prop);
- if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */
+ /* BPy_IDGroup_MapDataToPy sets the error */
+ if (UNLIKELY(wrap == NULL)) {
+ Py_DECREF(seq);
return NULL;
+ }
PyList_SET_ITEM(seq, i, wrap);
}
@@ -378,8 +381,10 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group,
}
else if (PyUnicode_Check(ob)) {
#ifdef USE_STRING_COERCE
+ Py_ssize_t value_size;
PyObject *value_coerce = NULL;
- val.string.str = PyC_UnicodeAsByte(ob, &value_coerce);
+ val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce);
+ val.string.len = (int)value_size + 1;
val.string.subtype = IDP_STRING_SUB_UTF8;
prop = IDP_New(IDP_STRING, &val, name);
Py_XDECREF(value_coerce);
@@ -657,7 +662,7 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
}
case IDP_IDPARRAY:
{
- PyObject *seq = PyList_New(prop->len), *wrap;
+ PyObject *seq = PyList_New(prop->len);
IDProperty *array = IDP_IDPArray(prop);
int i;
@@ -669,10 +674,13 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
}
for (i = 0; i < prop->len; i++) {
- wrap = BPy_IDGroup_MapDataToPy(array++);
+ PyObject *wrap = BPy_IDGroup_MapDataToPy(array++);
- if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */
+ /* BPy_IDGroup_MapDataToPy sets the error */
+ if (UNLIKELY(wrap == NULL)) {
+ Py_DECREF(seq);
return NULL;
+ }
PyList_SET_ITEM(seq, i, wrap);
}
@@ -680,14 +688,17 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
}
case IDP_GROUP:
{
- PyObject *dict = PyDict_New(), *wrap;
+ PyObject *dict = _PyDict_NewPresized(prop->len);
IDProperty *loop;
for (loop = prop->data.group.first; loop; loop = loop->next) {
- wrap = BPy_IDGroup_MapDataToPy(loop);
+ PyObject *wrap = BPy_IDGroup_MapDataToPy(loop);
- if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */
+ /* BPy_IDGroup_MapDataToPy sets the error */
+ if (UNLIKELY(wrap == NULL)) {
+ Py_DECREF(dict);
return NULL;
+ }
PyDict_SetItemString(dict, loop->name, wrap);
Py_DECREF(wrap);
@@ -702,7 +713,17 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
return NULL;
}
-static PyObject *BPy_IDGroup_Pop(BPy_IDProperty *self, PyObject *value)
+PyDoc_STRVAR(BPy_IDGroup_pop_doc,
+".. method:: pop(key)\n"
+"\n"
+" Remove an item from the group, returning a Python representation.\n"
+"\n"
+" :raises KeyError: When the item doesn't exist.\n"
+"\n"
+" :arg key: Name of item to remove.\n"
+" :type key: string\n"
+);
+static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *value)
{
IDProperty *idprop;
PyObject *pyform;
@@ -735,7 +756,12 @@ static PyObject *BPy_IDGroup_Pop(BPy_IDProperty *self, PyObject *value)
return NULL;
}
-static PyObject *BPy_IDGroup_IterItems(BPy_IDProperty *self)
+PyDoc_STRVAR(BPy_IDGroup_iter_items_doc,
+".. method:: iteritems()\n"
+"\n"
+" Iterate through the items in the dict; behaves like dictionary method iteritems.\n"
+);
+static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
{
BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
iter->group = self;
@@ -829,18 +855,32 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
return seq;
}
-
-static PyObject *BPy_IDGroup_GetKeys(BPy_IDProperty *self)
+PyDoc_STRVAR(BPy_IDGroup_keys_doc,
+".. method:: keys()\n"
+"\n"
+" Return the keys associated with this group as a list of strings.\n"
+);
+static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
{
return BPy_Wrap_GetKeys(self->prop);
}
-static PyObject *BPy_IDGroup_GetValues(BPy_IDProperty *self)
+PyDoc_STRVAR(BPy_IDGroup_values_doc,
+".. method:: values()\n"
+"\n"
+" Return the values associated with this group.\n"
+);
+static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
{
return BPy_Wrap_GetValues(self->id, self->prop);
}
-static PyObject *BPy_IDGroup_GetItems(BPy_IDProperty *self)
+PyDoc_STRVAR(BPy_IDGroup_items_doc,
+".. method:: items()\n"
+"\n"
+" Return the items associated with this group.\n"
+);
+static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
{
return BPy_Wrap_GetItems(self->id, self->prop);
}
@@ -859,7 +899,15 @@ static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
return IDP_GetPropertyFromGroup(self->prop, name) ? 1 : 0;
}
-static PyObject *BPy_IDGroup_Update(BPy_IDProperty *self, PyObject *value)
+PyDoc_STRVAR(BPy_IDGroup_update_doc,
+".. method:: update(other)\n"
+"\n"
+" Update key, values.\n"
+"\n"
+" :arg other: Updates the values in the group with this.\n"
+" :type other: :class:`IDPropertyGroup` or dict\n"
+);
+static PyObject *BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value)
{
PyObject *pkey, *pval;
Py_ssize_t i = 0;
@@ -890,19 +938,33 @@ static PyObject *BPy_IDGroup_Update(BPy_IDProperty *self, PyObject *value)
Py_RETURN_NONE;
}
+PyDoc_STRVAR(BPy_IDGroup_to_dict_doc,
+".. method:: to_dict()\n"
+"\n"
+" Return a purely python version of the group.\n"
+);
static PyObject *BPy_IDGroup_to_dict(BPy_IDProperty *self)
{
return BPy_IDGroup_MapDataToPy(self->prop);
}
+PyDoc_STRVAR(BPy_IDGroup_clear_doc,
+".. method:: clear()\n"
+"\n"
+" Clear all members from this group.\n"
+);
static PyObject *BPy_IDGroup_clear(BPy_IDProperty *self)
{
IDP_ClearProperty(self->prop);
Py_RETURN_NONE;
}
-/* Matches python dict.get(key, [default]) */
-static PyObject *BPy_IDGroup_Get(BPy_IDProperty *self, PyObject *args)
+PyDoc_STRVAR(BPy_IDGroup_get_doc,
+".. method:: get(key, default=None)\n"
+"\n"
+" Return the value for key, if it exists, else default.\n"
+);
+static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
{
IDProperty *idprop;
const char *key;
@@ -923,24 +985,15 @@ static PyObject *BPy_IDGroup_Get(BPy_IDProperty *self, PyObject *args)
}
static struct PyMethodDef BPy_IDGroup_methods[] = {
- {"pop", (PyCFunction)BPy_IDGroup_Pop, METH_O,
- "pop an item from the group; raises KeyError if the item doesn't exist"},
- {"iteritems", (PyCFunction)BPy_IDGroup_IterItems, METH_NOARGS,
- "iterate through the items in the dict; behaves like dictionary method iteritems"},
- {"keys", (PyCFunction)BPy_IDGroup_GetKeys, METH_NOARGS,
- "get the keys associated with this group as a list of strings"},
- {"values", (PyCFunction)BPy_IDGroup_GetValues, METH_NOARGS,
- "get the values associated with this group"},
- {"items", (PyCFunction)BPy_IDGroup_GetItems, METH_NOARGS,
- "get the items associated with this group"},
- {"update", (PyCFunction)BPy_IDGroup_Update, METH_O,
- "updates the values in the group with the values of another or a dict"},
- {"get", (PyCFunction)BPy_IDGroup_Get, METH_VARARGS,
- "idprop.get(k[,d]) -> idprop[k] if k in idprop, else d. d defaults to None"},
- {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS,
- "return a purely python version of the group"},
- {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS,
- "clear all members from this group"},
+ {"pop", (PyCFunction)BPy_IDGroup_pop, METH_O, BPy_IDGroup_pop_doc},
+ {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
+ {"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
+ {"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
+ {"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
+ {"update", (PyCFunction)BPy_IDGroup_update, METH_O, BPy_IDGroup_update_doc},
+ {"get", (PyCFunction)BPy_IDGroup_get, METH_VARARGS, BPy_IDGroup_get_doc},
+ {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, BPy_IDGroup_to_dict_doc},
+ {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS, BPy_IDGroup_clear_doc},
{NULL, NULL, 0, NULL}
};
@@ -1049,7 +1102,10 @@ static PyObject *BPy_IDArray_repr(BPy_IDArray *self)
return PyUnicode_FromFormat("<bpy id property array [%d]>", self->prop->len);
}
-static PyObject *BPy_IDArray_GetType(BPy_IDArray *self)
+PyDoc_STRVAR(BPy_IDArray_get_typecode_doc,
+"The type of the data in the array {'f': float, 'd': double, 'i': int}."
+);
+static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self)
{
switch (self->prop->subtype) {
case IDP_FLOAT: return PyUnicode_FromString("f");
@@ -1066,18 +1122,22 @@ static PyObject *BPy_IDArray_GetType(BPy_IDArray *self)
static PyGetSetDef BPy_IDArray_getseters[] = {
/* matches pythons array.typecode */
- {(char *)"typecode", (getter)BPy_IDArray_GetType, (setter)NULL, (char *)"The type of the data in the array, is an int.", NULL},
+ {(char *)"typecode", (getter)BPy_IDArray_get_typecode, (setter)NULL, BPy_IDArray_get_typecode_doc, NULL},
{NULL, NULL, NULL, NULL, NULL},
};
+PyDoc_STRVAR(BPy_IDArray_to_list_doc,
+".. method:: to_list()\n"
+"\n"
+" Return the array as a list.\n"
+);
static PyObject *BPy_IDArray_to_list(BPy_IDArray *self)
{
return BPy_IDGroup_MapDataToPy(self->prop);
}
static PyMethodDef BPy_IDArray_methods[] = {
- {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS,
- "return the array as a list"},
+ {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, BPy_IDArray_to_list_doc},
{NULL, NULL, 0, NULL}
};
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 72dec55e50b..7b2d58a1268 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -540,6 +540,35 @@ PyObject *PyC_ExceptionBuffer_Simple(void)
}
/* string conversion, escape non-unicode chars, coerce must be set to NULL */
+const char *PyC_UnicodeAsByteAndSize(PyObject *py_str, Py_ssize_t *size, PyObject **coerce)
+{
+ const char *result;
+
+ result = _PyUnicode_AsStringAndSize(py_str, size);
+
+ if (result) {
+ /* 99% of the time this is enough but we better support non unicode
+ * chars since blender doesnt limit this */
+ return result;
+ }
+ else {
+ PyErr_Clear();
+
+ if (PyBytes_Check(py_str)) {
+ *size = PyBytes_GET_SIZE(py_str);
+ return PyBytes_AS_STRING(py_str);
+ }
+ else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) {
+ *size = PyBytes_GET_SIZE(*coerce);
+ return PyBytes_AS_STRING(*coerce);
+ }
+ else {
+ /* leave error raised from EncodeFS */
+ return NULL;
+ }
+ }
+}
+
const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce)
{
const char *result;
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index a06a717ecbc..04cfc8801eb 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -53,6 +53,7 @@ void PyC_List_Fill(PyObject *list, PyObject *value);
PyObject * PyC_UnicodeFromByte(const char *str);
PyObject * PyC_UnicodeFromByteAndSize(const char *str, Py_ssize_t size);
const char * PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce); /* coerce must be NULL */
+const char * PyC_UnicodeAsByteAndSize(PyObject *py_str, Py_ssize_t *size, PyObject **coerce);
/* name namespace function for bpy & bge */
PyObject * PyC_DefaultNameSpace(const char *filename);
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 2715d2c5992..038c1e7eb10 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -49,6 +49,7 @@ set(SRC
gpu_offscreen.c
bpy.c
bpy_app.c
+ bpy_app_alembic.c
bpy_app_build_options.c
bpy_app_ffmpeg.c
bpy_app_handlers.c
@@ -82,6 +83,7 @@ set(SRC
gpu.h
bpy.h
bpy_app.h
+ bpy_app_alembic.h
bpy_app_build_options.h
bpy_app_ffmpeg.h
bpy_app_handlers.h
@@ -264,6 +266,10 @@ if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_ALEMBIC)
+endif()
+
if(WITH_OPENCOLORIO)
add_definitions(-DWITH_OCIO)
endif()
@@ -275,6 +281,13 @@ if(WITH_OPENVDB)
)
endif()
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_ALEMBIC)
+ list(APPEND INC
+ ../../alembic
+ )
+endif()
+
if(WITH_OPENIMAGEIO)
add_definitions(-DWITH_OPENIMAGEIO)
list(APPEND INC
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 727d980b182..78cb537cccd 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -33,6 +33,7 @@
#include "bpy_app.h"
+#include "bpy_app_alembic.h"
#include "bpy_app_ffmpeg.h"
#include "bpy_app_ocio.h"
#include "bpy_app_oiio.h"
@@ -104,6 +105,7 @@ static PyStructSequence_Field app_info_fields[] = {
{(char *)"build_system", (char *)"Build system used"},
/* submodules */
+ {(char *)"alembic", (char *)"Alembic library information backend"},
{(char *)"ffmpeg", (char *)"FFmpeg library information backend"},
{(char *)"ocio", (char *)"OpenColorIO library information backend"},
{(char *)"oiio", (char *)"OpenImageIO library information backend"},
@@ -182,6 +184,7 @@ static PyObject *make_app_info(void)
SetBytesItem("Unknown");
#endif
+ SetObjItem(BPY_app_alembic_struct());
SetObjItem(BPY_app_ffmpeg_struct());
SetObjItem(BPY_app_ocio_struct());
SetObjItem(BPY_app_oiio_struct());
diff --git a/source/blender/python/intern/bpy_app_alembic.c b/source/blender/python/intern/bpy_app_alembic.c
new file mode 100644
index 00000000000..90e6a02b418
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_alembic.c
@@ -0,0 +1,113 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_app_alembic.c
+ * \ingroup pythonintern
+ */
+
+#include <Python.h>
+#include "BLI_utildefines.h"
+
+#include "bpy_app_alembic.h"
+
+#ifdef WITH_ALEMBIC
+# include "ABC_alembic.h"
+#endif
+
+static PyTypeObject BlenderAppABCType;
+
+static PyStructSequence_Field app_alembic_info_fields[] = {
+ {(char *)"supported", (char *)"Boolean, True when Blender is built with Alembic support"},
+ {(char *)"version", (char *)"The Alembic version as a tuple of 3 numbers"},
+ {(char *)"version_string", (char *)"The Alembic version formatted as a string"},
+ {NULL}
+};
+
+static PyStructSequence_Desc app_alembic_info_desc = {
+ (char *)"bpy.app.alembic", /* name */
+ (char *)"This module contains information about Alembic blender is linked against", /* doc */
+ app_alembic_info_fields, /* fields */
+ ARRAY_SIZE(app_alembic_info_fields) - 1
+};
+
+static PyObject *make_alembic_info(void)
+{
+ PyObject *alembic_info = PyStructSequence_New(&BlenderAppABCType);
+
+ if (alembic_info == NULL) {
+ return NULL;
+ }
+
+ int pos = 0;
+
+#ifndef WITH_ALEMBIC
+# define SetStrItem(str) \
+ PyStructSequence_SET_ITEM(alembic_info, pos++, PyUnicode_FromString(str))
+#endif
+
+#define SetObjItem(obj) \
+ PyStructSequence_SET_ITEM(alembic_info, pos++, obj)
+
+#ifdef WITH_ALEMBIC
+ const int curversion = ABC_get_version();
+ const int major = curversion / 10000;
+ const int minor = (curversion / 100) - (major * 100);
+ const int patch = curversion - ((curversion / 100 ) * 100);
+
+ SetObjItem(PyBool_FromLong(1));
+ SetObjItem(Py_BuildValue("(iii)", major, minor, patch));
+ SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d", major, minor, patch));
+#else
+ SetObjItem(PyBool_FromLong(0));
+ SetObjItem(Py_BuildValue("(iii)", 0, 0, 0));
+ SetStrItem("Unknown");
+#endif
+
+ if (PyErr_Occurred()) {
+ Py_CLEAR(alembic_info);
+ return NULL;
+ }
+
+#undef SetStrItem
+#undef SetObjItem
+
+ return alembic_info;
+}
+
+PyObject *BPY_app_alembic_struct(void)
+{
+ PyStructSequence_InitType(&BlenderAppABCType, &app_alembic_info_desc);
+
+ PyObject *ret = make_alembic_info();
+
+ /* prevent user from creating new instances */
+ BlenderAppABCType.tp_init = NULL;
+ BlenderAppABCType.tp_new = NULL;
+ BlenderAppABCType.tp_hash = (hashfunc)_Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */
+
+ return ret;
+}
diff --git a/source/blender/python/intern/bpy_app_alembic.h b/source/blender/python/intern/bpy_app_alembic.h
new file mode 100644
index 00000000000..8cc647a77df
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_alembic.h
@@ -0,0 +1,38 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_app_alembic.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_APP_ALEMBIC_H__
+#define __BPY_APP_ALEMBIC_H__
+
+PyObject *BPY_app_alembic_struct(void);
+
+#endif /* __BPY_APP_ALEMBIC_H__ */
+
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index 4c186aab100..a6b98567a9a 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -69,6 +69,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{(char *)"player", NULL},
{(char *)"openmp", NULL},
{(char *)"openvdb", NULL},
+ {(char *)"alembic", NULL},
{NULL}
};
@@ -303,6 +304,12 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
+#ifdef WITH_ALEMBIC
+ SetObjIncref(Py_True);
+#else
+ SetObjIncref(Py_False);
+#endif
+
#undef SetObjIncref
return builtopts_info;
diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c
index b90deafb377..6ba858f0228 100644
--- a/source/blender/python/intern/bpy_app_translations.c
+++ b/source/blender/python/intern/bpy_app_translations.c
@@ -675,7 +675,7 @@ static PyObject *app_translations_new(PyTypeObject *type, PyObject *UNUSED(args)
_translations->contexts = app_translations_contexts_make();
- py_ctxts = PyDict_New();
+ py_ctxts = _PyDict_NewPresized(ARRAY_SIZE(_contexts));
for (ctxt = _contexts; ctxt->c_id; ctxt++) {
PyObject *val = PyUnicode_FromString(ctxt->py_id);
PyDict_SetItemString(py_ctxts, ctxt->c_id, val);
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
index 65b6bd501ce..c8ce7b2f770 100644
--- a/source/blender/python/intern/bpy_driver.c
+++ b/source/blender/python/intern/bpy_driver.c
@@ -44,6 +44,8 @@
#include "bpy_rna_driver.h" /* for pyrna_driver_get_variable_value */
+#include "bpy_intern_string.h"
+
#include "bpy_driver.h"
extern void BPY_update_rna_module(void);
@@ -97,26 +99,48 @@ int bpy_pydriver_create_dict(void)
}
/* note, this function should do nothing most runs, only when changing frame */
-static PyObject *bpy_pydriver_InternStr__frame = NULL;
/* not thread safe but neither is python */
-static float bpy_pydriver_evaltime_prev = FLT_MAX;
-
-static void bpy_pydriver_update_dict(const float evaltime)
+static struct {
+ float evaltime;
+
+ /* borrowed reference to the 'self' in 'bpy_pydriver_Dict'
+ * keep for as long as the same self is used. */
+ PyObject *self;
+} g_pydriver_state_prev = {
+ .evaltime = FLT_MAX,
+ .self = NULL,
+};
+
+static void bpy_pydriver_namespace_update_frame(const float evaltime)
{
- if (bpy_pydriver_evaltime_prev != evaltime) {
+ if (g_pydriver_state_prev.evaltime != evaltime) {
+ PyObject *item = PyFloat_FromDouble(evaltime);
+ PyDict_SetItem(bpy_pydriver_Dict, bpy_intern_str_frame, item);
+ Py_DECREF(item);
- /* currently only update the frame */
- if (bpy_pydriver_InternStr__frame == NULL) {
- bpy_pydriver_InternStr__frame = PyUnicode_FromString("frame");
- }
+ g_pydriver_state_prev.evaltime = evaltime;
+ }
+}
- PyObject *item = PyFloat_FromDouble(evaltime);
- PyDict_SetItem(bpy_pydriver_Dict,
- bpy_pydriver_InternStr__frame,
- item);
+static void bpy_pydriver_namespace_update_self(struct PathResolvedRNA *anim_rna)
+{
+ if ((g_pydriver_state_prev.self == NULL) ||
+ (pyrna_driver_is_equal_anim_rna(anim_rna, g_pydriver_state_prev.self) == false))
+ {
+ PyObject *item = pyrna_driver_self_from_anim_rna(anim_rna);
+ PyDict_SetItem(bpy_pydriver_Dict, bpy_intern_str_self, item);
Py_DECREF(item);
- bpy_pydriver_evaltime_prev = evaltime;
+ g_pydriver_state_prev.self = item;
+ }
+}
+
+static void bpy_pydriver_namespace_clear_self(void)
+{
+ if (g_pydriver_state_prev.self) {
+ PyDict_DelItem(bpy_pydriver_Dict, bpy_intern_str_self);
+
+ g_pydriver_state_prev.self = NULL;
}
}
@@ -139,11 +163,10 @@ void BPY_driver_reset(void)
bpy_pydriver_Dict = NULL;
}
- if (bpy_pydriver_InternStr__frame) {
- Py_DECREF(bpy_pydriver_InternStr__frame);
- bpy_pydriver_InternStr__frame = NULL;
- bpy_pydriver_evaltime_prev = FLT_MAX;
- }
+ g_pydriver_state_prev.evaltime = FLT_MAX;
+
+ /* freed when clearing driver dict */
+ g_pydriver_state_prev.self = NULL;
if (use_gil)
PyGILState_Release(gilstate);
@@ -174,7 +197,7 @@ static void pydriver_error(ChannelDriver *driver)
* now release the GIL on python operator execution instead, using
* PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender.
*/
-float BPY_driver_exec(ChannelDriver *driver, const float evaltime)
+float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, const float evaltime)
{
PyObject *driver_vars = NULL;
PyObject *retval = NULL;
@@ -224,8 +247,14 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime)
}
/* update global namespace */
- bpy_pydriver_update_dict(evaltime);
+ bpy_pydriver_namespace_update_frame(evaltime);
+ if (driver->flag & DRIVER_FLAG_USE_SELF) {
+ bpy_pydriver_namespace_update_self(anim_rna);
+ }
+ else {
+ bpy_pydriver_namespace_clear_self();
+ }
if (driver->expr_comp == NULL)
driver->flag |= DRIVER_FLAG_RECOMPILE;
@@ -264,7 +293,7 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime)
}
/* add target values to a dict that will be used as '__locals__' dict */
- driver_vars = PyDict_New();
+ driver_vars = _PyDict_NewPresized(PyTuple_GET_SIZE(expr_vars));
for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) {
PyObject *driver_arg = NULL;
diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h
index 1fccec7e1b2..017a6fe89c5 100644
--- a/source/blender/python/intern/bpy_driver.h
+++ b/source/blender/python/intern/bpy_driver.h
@@ -28,12 +28,13 @@
#define __BPY_DRIVER_H__
struct ChannelDriver;
+struct PathResolvedRNA;
int bpy_pydriver_create_dict(void);
extern PyObject *bpy_pydriver_Dict;
/* externals */
-float BPY_driver_exec(struct ChannelDriver *driver, const float evaltime);
+float BPY_driver_exec(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime);
void BPY_driver_reset(void);
#endif /* __BPY_DRIVER_H__ */
diff --git a/source/blender/python/intern/bpy_intern_string.c b/source/blender/python/intern/bpy_intern_string.c
index fd32c91a480..ac0100fa75d 100644
--- a/source/blender/python/intern/bpy_intern_string.c
+++ b/source/blender/python/intern/bpy_intern_string.c
@@ -34,7 +34,7 @@
#include "BLI_utildefines.h"
-static PyObject *bpy_intern_str_arr[11];
+static PyObject *bpy_intern_str_arr[13];
PyObject *bpy_intern_str_register;
PyObject *bpy_intern_str_unregister;
@@ -43,6 +43,8 @@ PyObject *bpy_intern_str_bl_property;
PyObject *bpy_intern_str_bpy_types;
PyObject *bpy_intern_str_order;
PyObject *bpy_intern_str_attr;
+PyObject *bpy_intern_str_self;
+PyObject *bpy_intern_str_frame;
PyObject *bpy_intern_str___slots__;
PyObject *bpy_intern_str___name__;
PyObject *bpy_intern_str___doc__;
@@ -62,6 +64,8 @@ void bpy_intern_string_init(void)
BPY_INTERN_STR(bpy_intern_str_bpy_types, "bpy.types");
BPY_INTERN_STR(bpy_intern_str_order, "order");
BPY_INTERN_STR(bpy_intern_str_attr, "attr");
+ BPY_INTERN_STR(bpy_intern_str_self, "self");
+ BPY_INTERN_STR(bpy_intern_str_frame, "frame");
BPY_INTERN_STR(bpy_intern_str___slots__, "__slots__");
BPY_INTERN_STR(bpy_intern_str___name__, "__name__");
BPY_INTERN_STR(bpy_intern_str___doc__, "__doc__");
diff --git a/source/blender/python/intern/bpy_intern_string.h b/source/blender/python/intern/bpy_intern_string.h
index f4f4560dbfd..394e84d89bd 100644
--- a/source/blender/python/intern/bpy_intern_string.h
+++ b/source/blender/python/intern/bpy_intern_string.h
@@ -37,6 +37,8 @@ extern PyObject *bpy_intern_str_bl_property;
extern PyObject *bpy_intern_str_bpy_types;
extern PyObject *bpy_intern_str_order;
extern PyObject *bpy_intern_str_attr;
+extern PyObject *bpy_intern_str_self;
+extern PyObject *bpy_intern_str_frame;
extern PyObject *bpy_intern_str___slots__;
extern PyObject *bpy_intern_str___name__;
extern PyObject *bpy_intern_str___doc__;
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index 37a7e0e23dd..ec69abbb1df 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -210,7 +210,7 @@ static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject *
ret->flag = ((is_link ? FILE_LINK : 0) |
(is_rel ? FILE_RELPATH : 0));
- ret->dict = PyDict_New();
+ ret->dict = _PyDict_NewPresized(MAX_LIBARRAY);
return (PyObject *)ret;
}
@@ -240,7 +240,7 @@ static PyObject *bpy_lib_enter(BPy_Library *self, PyObject *UNUSED(args))
{
PyObject *ret;
BPy_Library *self_from;
- PyObject *from_dict = PyDict_New();
+ PyObject *from_dict = _PyDict_NewPresized(MAX_LIBARRAY);
ReportList reports;
BKE_reports_init(&reports, RPT_STORE);
diff --git a/source/blender/python/intern/bpy_rna_driver.c b/source/blender/python/intern/bpy_rna_driver.c
index 482508a8d85..b4c0de51c04 100644
--- a/source/blender/python/intern/bpy_rna_driver.c
+++ b/source/blender/python/intern/bpy_rna_driver.c
@@ -77,3 +77,24 @@ PyObject *pyrna_driver_get_variable_value(
return driver_arg;
}
+
+PyObject *pyrna_driver_self_from_anim_rna(PathResolvedRNA *anim_rna)
+{
+ return pyrna_struct_CreatePyObject(&anim_rna->ptr);
+}
+
+bool pyrna_driver_is_equal_anim_rna(const PathResolvedRNA *anim_rna, const PyObject *py_anim_rna)
+{
+ if (BPy_StructRNA_Check(py_anim_rna)) {
+ const PointerRNA *ptr_a = &anim_rna->ptr;
+ const PointerRNA *ptr_b = &(((const BPy_StructRNA *)py_anim_rna)->ptr);
+
+ if ((ptr_a->id.data == ptr_b->id.data) &&
+ (ptr_a->type == ptr_b->type) &&
+ (ptr_a->data == ptr_b->data))
+ {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/source/blender/python/intern/bpy_rna_driver.h b/source/blender/python/intern/bpy_rna_driver.h
index 8deac2e4384..3f4bf2b9df5 100644
--- a/source/blender/python/intern/bpy_rna_driver.h
+++ b/source/blender/python/intern/bpy_rna_driver.h
@@ -27,7 +27,11 @@
struct ChannelDriver;
struct DriverTarget;
+struct PathResolvedRNA;
PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar);
+PyObject *pyrna_driver_self_from_anim_rna(struct PathResolvedRNA *anim_rna);
+bool pyrna_driver_is_equal_anim_rna(const struct PathResolvedRNA *anim_rna, const PyObject *py_anim_rna);
+
#endif /* __BPY_RNA_DRIVER_H__ */
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index 104e3e47646..31189ba4dee 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -198,7 +198,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
- data_cb.user_map = PyDict_New();
+ data_cb.user_map = _PyDict_NewPresized(subset_len);
data_cb.is_subset = true;
for (; subset_len; subset_array++, subset_len--) {
PyObject *set = PySet_New(NULL);
diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c
index 7b6fa466fce..afc8a30a6b5 100644
--- a/source/blender/python/mathutils/mathutils_Vector.c
+++ b/source/blender/python/mathutils/mathutils_Vector.c
@@ -2324,6 +2324,20 @@ static int Vector_swizzle_set(VectorObject *self, PyObject *value, void *closure
return 0;
}
+/* XYZW -> 0123 */
+#define AXIS_FROM_CHAR(a) (((a) != 'W') ? ((a) - 'X') : 3)
+
+#define _VA_SWIZZLE_1(a) ( \
+ ((AXIS_FROM_CHAR(a) | SWIZZLE_VALID_AXIS)))
+#define _VA_SWIZZLE_2(a, b) (_VA_SWIZZLE_1(a) | \
+ ((AXIS_FROM_CHAR(b) | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS)))
+#define _VA_SWIZZLE_3(a, b, c) (_VA_SWIZZLE_2(a, b) | \
+ ((AXIS_FROM_CHAR(c) | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)))
+#define _VA_SWIZZLE_4(a, b, c, d) (_VA_SWIZZLE_3(a, b, c) | \
+ ((AXIS_FROM_CHAR(d) | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3)))
+
+#define SWIZZLE(...) SET_INT_IN_POINTER(VA_NARGS_CALL_OVERLOAD(_VA_SWIZZLE_, __VA_ARGS__))
+
/*****************************************************************************/
/* Python attributes get/set structure: */
/*****************************************************************************/
@@ -2340,385 +2354,409 @@ static PyGetSetDef Vector_getseters[] = {
{(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL},
/* autogenerated swizzle attrs, see python script below */
- {(char *)"xx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 36
- {(char *)"xxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 292
- {(char *)"xxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2340
- {(char *)"xxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2852
- {(char *)"xxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3364
- {(char *)"xxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3876
- {(char *)"xxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 356
- {(char *)"xxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2404
- {(char *)"xxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2916
- {(char *)"xxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3428
- {(char *)"xxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3940
- {(char *)"xxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 420
- {(char *)"xxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2468
- {(char *)"xxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2980
- {(char *)"xxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3492
- {(char *)"xxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4004
- {(char *)"xxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 484
- {(char *)"xxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2532
- {(char *)"xxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3044
- {(char *)"xxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3556
- {(char *)"xxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4068
- {(char *)"xy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 44
- {(char *)"xyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 300
- {(char *)"xyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2348
- {(char *)"xyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2860
- {(char *)"xyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3372
- {(char *)"xyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3884
- {(char *)"xyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 364
- {(char *)"xyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2412
- {(char *)"xyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2924
- {(char *)"xyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3436
- {(char *)"xyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3948
- {(char *)"xyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 428
- {(char *)"xyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2476
- {(char *)"xyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2988
- {(char *)"xyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3500
- {(char *)"xyzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4012
- {(char *)"xyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 492
- {(char *)"xywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2540
- {(char *)"xywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3052
- {(char *)"xywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3564
- {(char *)"xyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4076
- {(char *)"xz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 52
- {(char *)"xzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 308
- {(char *)"xzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2356
- {(char *)"xzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2868
- {(char *)"xzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3380
- {(char *)"xzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3892
- {(char *)"xzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 372
- {(char *)"xzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2420
- {(char *)"xzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2932
- {(char *)"xzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3444
- {(char *)"xzyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3956
- {(char *)"xzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 436
- {(char *)"xzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2484
- {(char *)"xzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2996
- {(char *)"xzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3508
- {(char *)"xzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4020
- {(char *)"xzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 500
- {(char *)"xzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2548
- {(char *)"xzwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3060
- {(char *)"xzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3572
- {(char *)"xzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4084
- {(char *)"xw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 60
- {(char *)"xwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 316
- {(char *)"xwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2364
- {(char *)"xwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2876
- {(char *)"xwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3388
- {(char *)"xwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3900
- {(char *)"xwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 380
- {(char *)"xwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2428
- {(char *)"xwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2940
- {(char *)"xwyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3452
- {(char *)"xwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3964
- {(char *)"xwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 444
- {(char *)"xwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2492
- {(char *)"xwzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3004
- {(char *)"xwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3516
- {(char *)"xwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4028
- {(char *)"xww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 508
- {(char *)"xwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2556
- {(char *)"xwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3068
- {(char *)"xwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3580
- {(char *)"xwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((0 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4092
- {(char *)"yx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 37
- {(char *)"yxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 293
- {(char *)"yxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2341
- {(char *)"yxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2853
- {(char *)"yxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3365
- {(char *)"yxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3877
- {(char *)"yxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 357
- {(char *)"yxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2405
- {(char *)"yxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2917
- {(char *)"yxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3429
- {(char *)"yxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3941
- {(char *)"yxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 421
- {(char *)"yxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2469
- {(char *)"yxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2981
- {(char *)"yxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3493
- {(char *)"yxzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4005
- {(char *)"yxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 485
- {(char *)"yxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2533
- {(char *)"yxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3045
- {(char *)"yxwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3557
- {(char *)"yxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4069
- {(char *)"yy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 45
- {(char *)"yyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 301
- {(char *)"yyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2349
- {(char *)"yyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2861
- {(char *)"yyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3373
- {(char *)"yyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3885
- {(char *)"yyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 365
- {(char *)"yyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2413
- {(char *)"yyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2925
- {(char *)"yyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3437
- {(char *)"yyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3949
- {(char *)"yyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 429
- {(char *)"yyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2477
- {(char *)"yyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2989
- {(char *)"yyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3501
- {(char *)"yyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4013
- {(char *)"yyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 493
- {(char *)"yywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2541
- {(char *)"yywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3053
- {(char *)"yywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3565
- {(char *)"yyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4077
- {(char *)"yz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 53
- {(char *)"yzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 309
- {(char *)"yzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2357
- {(char *)"yzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2869
- {(char *)"yzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3381
- {(char *)"yzxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3893
- {(char *)"yzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 373
- {(char *)"yzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2421
- {(char *)"yzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2933
- {(char *)"yzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3445
- {(char *)"yzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3957
- {(char *)"yzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 437
- {(char *)"yzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2485
- {(char *)"yzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2997
- {(char *)"yzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3509
- {(char *)"yzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4021
- {(char *)"yzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 501
- {(char *)"yzwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2549
- {(char *)"yzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3061
- {(char *)"yzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3573
- {(char *)"yzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4085
- {(char *)"yw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 61
- {(char *)"ywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 317
- {(char *)"ywxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2365
- {(char *)"ywxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2877
- {(char *)"ywxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3389
- {(char *)"ywxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3901
- {(char *)"ywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 381
- {(char *)"ywyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2429
- {(char *)"ywyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2941
- {(char *)"ywyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3453
- {(char *)"ywyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3965
- {(char *)"ywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 445
- {(char *)"ywzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2493
- {(char *)"ywzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3005
- {(char *)"ywzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3517
- {(char *)"ywzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4029
- {(char *)"yww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 509
- {(char *)"ywwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2557
- {(char *)"ywwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3069
- {(char *)"ywwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3581
- {(char *)"ywww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((1 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4093
- {(char *)"zx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 38
- {(char *)"zxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 294
- {(char *)"zxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2342
- {(char *)"zxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2854
- {(char *)"zxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3366
- {(char *)"zxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3878
- {(char *)"zxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 358
- {(char *)"zxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2406
- {(char *)"zxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2918
- {(char *)"zxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3430
- {(char *)"zxyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3942
- {(char *)"zxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 422
- {(char *)"zxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2470
- {(char *)"zxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2982
- {(char *)"zxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3494
- {(char *)"zxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4006
- {(char *)"zxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 486
- {(char *)"zxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2534
- {(char *)"zxwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3046
- {(char *)"zxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3558
- {(char *)"zxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4070
- {(char *)"zy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 46
- {(char *)"zyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 302
- {(char *)"zyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2350
- {(char *)"zyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2862
- {(char *)"zyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3374
- {(char *)"zyxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3886
- {(char *)"zyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 366
- {(char *)"zyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2414
- {(char *)"zyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2926
- {(char *)"zyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3438
- {(char *)"zyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3950
- {(char *)"zyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 430
- {(char *)"zyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2478
- {(char *)"zyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2990
- {(char *)"zyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3502
- {(char *)"zyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4014
- {(char *)"zyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 494
- {(char *)"zywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2542
- {(char *)"zywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3054
- {(char *)"zywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3566
- {(char *)"zyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4078
- {(char *)"zz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 54
- {(char *)"zzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 310
- {(char *)"zzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2358
- {(char *)"zzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2870
- {(char *)"zzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3382
- {(char *)"zzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3894
- {(char *)"zzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 374
- {(char *)"zzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2422
- {(char *)"zzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2934
- {(char *)"zzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3446
- {(char *)"zzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3958
- {(char *)"zzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 438
- {(char *)"zzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2486
- {(char *)"zzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2998
- {(char *)"zzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3510
- {(char *)"zzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4022
- {(char *)"zzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 502
- {(char *)"zzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2550
- {(char *)"zzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3062
- {(char *)"zzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3574
- {(char *)"zzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4086
- {(char *)"zw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 62
- {(char *)"zwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 318
- {(char *)"zwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2366
- {(char *)"zwxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2878
- {(char *)"zwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3390
- {(char *)"zwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3902
- {(char *)"zwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 382
- {(char *)"zwyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2430
- {(char *)"zwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2942
- {(char *)"zwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3454
- {(char *)"zwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3966
- {(char *)"zwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 446
- {(char *)"zwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2494
- {(char *)"zwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3006
- {(char *)"zwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3518
- {(char *)"zwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4030
- {(char *)"zww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 510
- {(char *)"zwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2558
- {(char *)"zwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3070
- {(char *)"zwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3582
- {(char *)"zwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((2 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4094
- {(char *)"wx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 39
- {(char *)"wxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 295
- {(char *)"wxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2343
- {(char *)"wxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2855
- {(char *)"wxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3367
- {(char *)"wxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3879
- {(char *)"wxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 359
- {(char *)"wxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2407
- {(char *)"wxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2919
- {(char *)"wxyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3431
- {(char *)"wxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3943
- {(char *)"wxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 423
- {(char *)"wxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2471
- {(char *)"wxzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2983
- {(char *)"wxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3495
- {(char *)"wxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4007
- {(char *)"wxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 487
- {(char *)"wxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2535
- {(char *)"wxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3047
- {(char *)"wxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3559
- {(char *)"wxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4071
- {(char *)"wy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 47
- {(char *)"wyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 303
- {(char *)"wyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2351
- {(char *)"wyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2863
- {(char *)"wyxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3375
- {(char *)"wyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3887
- {(char *)"wyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 367
- {(char *)"wyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2415
- {(char *)"wyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2927
- {(char *)"wyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3439
- {(char *)"wyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3951
- {(char *)"wyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 431
- {(char *)"wyzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2479
- {(char *)"wyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2991
- {(char *)"wyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3503
- {(char *)"wyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4015
- {(char *)"wyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 495
- {(char *)"wywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2543
- {(char *)"wywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3055
- {(char *)"wywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3567
- {(char *)"wyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4079
- {(char *)"wz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 55
- {(char *)"wzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 311
- {(char *)"wzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2359
- {(char *)"wzxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2871
- {(char *)"wzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3383
- {(char *)"wzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3895
- {(char *)"wzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 375
- {(char *)"wzyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2423
- {(char *)"wzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2935
- {(char *)"wzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3447
- {(char *)"wzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3959
- {(char *)"wzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 439
- {(char *)"wzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2487
- {(char *)"wzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2999
- {(char *)"wzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3511
- {(char *)"wzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4023
- {(char *)"wzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 503
- {(char *)"wzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2551
- {(char *)"wzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3063
- {(char *)"wzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3575
- {(char *)"wzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4087
- {(char *)"ww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS)))}, // 63
- {(char *)"wwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 319
- {(char *)"wwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2367
- {(char *)"wwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2879
- {(char *)"wwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3391
- {(char *)"wwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3903
- {(char *)"wwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 383
- {(char *)"wwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2431
- {(char *)"wwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2943
- {(char *)"wwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3455
- {(char *)"wwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3967
- {(char *)"wwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 447
- {(char *)"wwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2495
- {(char *)"wwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3007
- {(char *)"wwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3519
- {(char *)"wwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4031
- {(char *)"www", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2))))}, // 511
- {(char *)"wwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((0 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 2559
- {(char *)"wwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((1 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3071
- {(char *)"wwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((2 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 3583
- {(char *)"wwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SET_INT_IN_POINTER(((3 | SWIZZLE_VALID_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((3 | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) )}, // 4095
+ {(char *)"xx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X')},
+ {(char *)"xxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X')},
+ {(char *)"xxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'X')},
+ {(char *)"xxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'Y')},
+ {(char *)"xxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'Z')},
+ {(char *)"xxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'X', 'W')},
+ {(char *)"xxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y')},
+ {(char *)"xxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'X')},
+ {(char *)"xxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'Y')},
+ {(char *)"xxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'Z')},
+ {(char *)"xxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Y', 'W')},
+ {(char *)"xxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z')},
+ {(char *)"xxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'X')},
+ {(char *)"xxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'Y')},
+ {(char *)"xxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'Z')},
+ {(char *)"xxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'Z', 'W')},
+ {(char *)"xxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W')},
+ {(char *)"xxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'X')},
+ {(char *)"xxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'Y')},
+ {(char *)"xxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'Z')},
+ {(char *)"xxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'X', 'W', 'W')},
+ {(char *)"xy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y')},
+ {(char *)"xyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X')},
+ {(char *)"xyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'X')},
+ {(char *)"xyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'Y')},
+ {(char *)"xyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'Z')},
+ {(char *)"xyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'X', 'W')},
+ {(char *)"xyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y')},
+ {(char *)"xyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'X')},
+ {(char *)"xyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'Y')},
+ {(char *)"xyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'Z')},
+ {(char *)"xyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Y', 'W')},
+ {(char *)"xyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'Z')},
+ {(char *)"xyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Z', 'X')},
+ {(char *)"xyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Z', 'Y')},
+ {(char *)"xyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'Z', 'Z')},
+ {(char *)"xyzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'Z', 'W')},
+ {(char *)"xyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'W')},
+ {(char *)"xywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'W', 'X')},
+ {(char *)"xywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'W', 'Y')},
+ {(char *)"xywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Y', 'W', 'Z')},
+ {(char *)"xyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Y', 'W', 'W')},
+ {(char *)"xz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z')},
+ {(char *)"xzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X')},
+ {(char *)"xzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'X')},
+ {(char *)"xzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'Y')},
+ {(char *)"xzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'Z')},
+ {(char *)"xzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'X', 'W')},
+ {(char *)"xzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'Y')},
+ {(char *)"xzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Y', 'X')},
+ {(char *)"xzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Y', 'Y')},
+ {(char *)"xzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Y', 'Z')},
+ {(char *)"xzyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'Y', 'W')},
+ {(char *)"xzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z')},
+ {(char *)"xzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'X')},
+ {(char *)"xzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'Y')},
+ {(char *)"xzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'Z')},
+ {(char *)"xzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'Z', 'W')},
+ {(char *)"xzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'W')},
+ {(char *)"xzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'W', 'X')},
+ {(char *)"xzwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'Z', 'W', 'Y')},
+ {(char *)"xzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'W', 'Z')},
+ {(char *)"xzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'Z', 'W', 'W')},
+ {(char *)"xw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W')},
+ {(char *)"xwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X')},
+ {(char *)"xwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'X')},
+ {(char *)"xwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'Y')},
+ {(char *)"xwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'Z')},
+ {(char *)"xwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'X', 'W')},
+ {(char *)"xwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Y')},
+ {(char *)"xwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Y', 'X')},
+ {(char *)"xwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Y', 'Y')},
+ {(char *)"xwyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Y', 'Z')},
+ {(char *)"xwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Y', 'W')},
+ {(char *)"xwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Z')},
+ {(char *)"xwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Z', 'X')},
+ {(char *)"xwzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('X', 'W', 'Z', 'Y')},
+ {(char *)"xwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Z', 'Z')},
+ {(char *)"xwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'Z', 'W')},
+ {(char *)"xww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W')},
+ {(char *)"xwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'X')},
+ {(char *)"xwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'Y')},
+ {(char *)"xwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'Z')},
+ {(char *)"xwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('X', 'W', 'W', 'W')},
+ {(char *)"yx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X')},
+ {(char *)"yxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X')},
+ {(char *)"yxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'X')},
+ {(char *)"yxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'Y')},
+ {(char *)"yxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'Z')},
+ {(char *)"yxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'X', 'W')},
+ {(char *)"yxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y')},
+ {(char *)"yxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'X')},
+ {(char *)"yxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'Y')},
+ {(char *)"yxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'Z')},
+ {(char *)"yxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Y', 'W')},
+ {(char *)"yxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'Z')},
+ {(char *)"yxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Z', 'X')},
+ {(char *)"yxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Z', 'Y')},
+ {(char *)"yxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'Z', 'Z')},
+ {(char *)"yxzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'Z', 'W')},
+ {(char *)"yxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'W')},
+ {(char *)"yxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'W', 'X')},
+ {(char *)"yxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'W', 'Y')},
+ {(char *)"yxwz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'X', 'W', 'Z')},
+ {(char *)"yxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'X', 'W', 'W')},
+ {(char *)"yy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y')},
+ {(char *)"yyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X')},
+ {(char *)"yyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'X')},
+ {(char *)"yyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'Y')},
+ {(char *)"yyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'Z')},
+ {(char *)"yyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'X', 'W')},
+ {(char *)"yyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y')},
+ {(char *)"yyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'X')},
+ {(char *)"yyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'Y')},
+ {(char *)"yyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'Z')},
+ {(char *)"yyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Y', 'W')},
+ {(char *)"yyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z')},
+ {(char *)"yyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'X')},
+ {(char *)"yyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'Y')},
+ {(char *)"yyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'Z')},
+ {(char *)"yyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'Z', 'W')},
+ {(char *)"yyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W')},
+ {(char *)"yywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'X')},
+ {(char *)"yywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'Y')},
+ {(char *)"yywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'Z')},
+ {(char *)"yyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Y', 'W', 'W')},
+ {(char *)"yz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z')},
+ {(char *)"yzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'X')},
+ {(char *)"yzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'X', 'X')},
+ {(char *)"yzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'X', 'Y')},
+ {(char *)"yzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'X', 'Z')},
+ {(char *)"yzxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'X', 'W')},
+ {(char *)"yzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y')},
+ {(char *)"yzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'X')},
+ {(char *)"yzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'Y')},
+ {(char *)"yzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'Z')},
+ {(char *)"yzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Y', 'W')},
+ {(char *)"yzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z')},
+ {(char *)"yzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'X')},
+ {(char *)"yzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'Y')},
+ {(char *)"yzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'Z')},
+ {(char *)"yzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'Z', 'W')},
+ {(char *)"yzw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'W')},
+ {(char *)"yzwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'Z', 'W', 'X')},
+ {(char *)"yzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'W', 'Y')},
+ {(char *)"yzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'W', 'Z')},
+ {(char *)"yzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'Z', 'W', 'W')},
+ {(char *)"yw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W')},
+ {(char *)"ywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'X')},
+ {(char *)"ywxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'X', 'X')},
+ {(char *)"ywxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'X', 'Y')},
+ {(char *)"ywxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'X', 'Z')},
+ {(char *)"ywxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'X', 'W')},
+ {(char *)"ywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y')},
+ {(char *)"ywyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'X')},
+ {(char *)"ywyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'Y')},
+ {(char *)"ywyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'Z')},
+ {(char *)"ywyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Y', 'W')},
+ {(char *)"ywz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'Z')},
+ {(char *)"ywzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Y', 'W', 'Z', 'X')},
+ {(char *)"ywzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Z', 'Y')},
+ {(char *)"ywzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Z', 'Z')},
+ {(char *)"ywzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'Z', 'W')},
+ {(char *)"yww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W')},
+ {(char *)"ywwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'X')},
+ {(char *)"ywwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'Y')},
+ {(char *)"ywwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'Z')},
+ {(char *)"ywww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Y', 'W', 'W', 'W')},
+ {(char *)"zx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X')},
+ {(char *)"zxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X')},
+ {(char *)"zxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'X')},
+ {(char *)"zxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'Y')},
+ {(char *)"zxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'Z')},
+ {(char *)"zxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'X', 'W')},
+ {(char *)"zxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'Y')},
+ {(char *)"zxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Y', 'X')},
+ {(char *)"zxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Y', 'Y')},
+ {(char *)"zxyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Y', 'Z')},
+ {(char *)"zxyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'Y', 'W')},
+ {(char *)"zxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z')},
+ {(char *)"zxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'X')},
+ {(char *)"zxzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'Y')},
+ {(char *)"zxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'Z')},
+ {(char *)"zxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'Z', 'W')},
+ {(char *)"zxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'W')},
+ {(char *)"zxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'W', 'X')},
+ {(char *)"zxwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'X', 'W', 'Y')},
+ {(char *)"zxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'W', 'Z')},
+ {(char *)"zxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'X', 'W', 'W')},
+ {(char *)"zy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y')},
+ {(char *)"zyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'X')},
+ {(char *)"zyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'X', 'X')},
+ {(char *)"zyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'X', 'Y')},
+ {(char *)"zyxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'X', 'Z')},
+ {(char *)"zyxw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'X', 'W')},
+ {(char *)"zyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y')},
+ {(char *)"zyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'X')},
+ {(char *)"zyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'Y')},
+ {(char *)"zyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'Z')},
+ {(char *)"zyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Y', 'W')},
+ {(char *)"zyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z')},
+ {(char *)"zyzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'X')},
+ {(char *)"zyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'Y')},
+ {(char *)"zyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'Z')},
+ {(char *)"zyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'Z', 'W')},
+ {(char *)"zyw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'W')},
+ {(char *)"zywx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'Y', 'W', 'X')},
+ {(char *)"zywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'W', 'Y')},
+ {(char *)"zywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'W', 'Z')},
+ {(char *)"zyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Y', 'W', 'W')},
+ {(char *)"zz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z')},
+ {(char *)"zzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X')},
+ {(char *)"zzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'X')},
+ {(char *)"zzxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'Y')},
+ {(char *)"zzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'Z')},
+ {(char *)"zzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'X', 'W')},
+ {(char *)"zzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y')},
+ {(char *)"zzyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'X')},
+ {(char *)"zzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'Y')},
+ {(char *)"zzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'Z')},
+ {(char *)"zzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Y', 'W')},
+ {(char *)"zzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z')},
+ {(char *)"zzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'X')},
+ {(char *)"zzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'Y')},
+ {(char *)"zzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'Z')},
+ {(char *)"zzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'Z', 'W')},
+ {(char *)"zzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W')},
+ {(char *)"zzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'X')},
+ {(char *)"zzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'Y')},
+ {(char *)"zzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'Z')},
+ {(char *)"zzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'Z', 'W', 'W')},
+ {(char *)"zw", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W')},
+ {(char *)"zwx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'X')},
+ {(char *)"zwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'X', 'X')},
+ {(char *)"zwxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'X', 'Y')},
+ {(char *)"zwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'X', 'Z')},
+ {(char *)"zwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'X', 'W')},
+ {(char *)"zwy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'Y')},
+ {(char *)"zwyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('Z', 'W', 'Y', 'X')},
+ {(char *)"zwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Y', 'Y')},
+ {(char *)"zwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Y', 'Z')},
+ {(char *)"zwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Y', 'W')},
+ {(char *)"zwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z')},
+ {(char *)"zwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'X')},
+ {(char *)"zwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'Y')},
+ {(char *)"zwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'Z')},
+ {(char *)"zwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'Z', 'W')},
+ {(char *)"zww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W')},
+ {(char *)"zwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'X')},
+ {(char *)"zwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'Y')},
+ {(char *)"zwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'Z')},
+ {(char *)"zwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('Z', 'W', 'W', 'W')},
+ {(char *)"wx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X')},
+ {(char *)"wxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X')},
+ {(char *)"wxxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'X')},
+ {(char *)"wxxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'Y')},
+ {(char *)"wxxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'Z')},
+ {(char *)"wxxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'X', 'W')},
+ {(char *)"wxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Y')},
+ {(char *)"wxyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Y', 'X')},
+ {(char *)"wxyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Y', 'Y')},
+ {(char *)"wxyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Y', 'Z')},
+ {(char *)"wxyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Y', 'W')},
+ {(char *)"wxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Z')},
+ {(char *)"wxzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Z', 'X')},
+ {(char *)"wxzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'X', 'Z', 'Y')},
+ {(char *)"wxzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Z', 'Z')},
+ {(char *)"wxzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'Z', 'W')},
+ {(char *)"wxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W')},
+ {(char *)"wxwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'X')},
+ {(char *)"wxwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'Y')},
+ {(char *)"wxwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'Z')},
+ {(char *)"wxww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'X', 'W', 'W')},
+ {(char *)"wy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y')},
+ {(char *)"wyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'X')},
+ {(char *)"wyxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'X', 'X')},
+ {(char *)"wyxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'X', 'Y')},
+ {(char *)"wyxz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'X', 'Z')},
+ {(char *)"wyxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'X', 'W')},
+ {(char *)"wyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y')},
+ {(char *)"wyyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'X')},
+ {(char *)"wyyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'Y')},
+ {(char *)"wyyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'Z')},
+ {(char *)"wyyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Y', 'W')},
+ {(char *)"wyz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'Z')},
+ {(char *)"wyzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Y', 'Z', 'X')},
+ {(char *)"wyzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Z', 'Y')},
+ {(char *)"wyzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Z', 'Z')},
+ {(char *)"wyzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'Z', 'W')},
+ {(char *)"wyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W')},
+ {(char *)"wywx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'X')},
+ {(char *)"wywy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'Y')},
+ {(char *)"wywz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'Z')},
+ {(char *)"wyww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Y', 'W', 'W')},
+ {(char *)"wz", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z')},
+ {(char *)"wzx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'X')},
+ {(char *)"wzxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'X', 'X')},
+ {(char *)"wzxy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'X', 'Y')},
+ {(char *)"wzxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'X', 'Z')},
+ {(char *)"wzxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'X', 'W')},
+ {(char *)"wzy", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'Y')},
+ {(char *)"wzyx", (getter)Vector_swizzle_get, (setter)Vector_swizzle_set, NULL, SWIZZLE('W', 'Z', 'Y', 'X')},
+ {(char *)"wzyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Y', 'Y')},
+ {(char *)"wzyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Y', 'Z')},
+ {(char *)"wzyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Y', 'W')},
+ {(char *)"wzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z')},
+ {(char *)"wzzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'X')},
+ {(char *)"wzzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'Y')},
+ {(char *)"wzzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'Z')},
+ {(char *)"wzzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'Z', 'W')},
+ {(char *)"wzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W')},
+ {(char *)"wzwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'X')},
+ {(char *)"wzwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'Y')},
+ {(char *)"wzwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'Z')},
+ {(char *)"wzww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'Z', 'W', 'W')},
+ {(char *)"ww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W')},
+ {(char *)"wwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X')},
+ {(char *)"wwxx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'X')},
+ {(char *)"wwxy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'Y')},
+ {(char *)"wwxz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'Z')},
+ {(char *)"wwxw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'X', 'W')},
+ {(char *)"wwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y')},
+ {(char *)"wwyx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'X')},
+ {(char *)"wwyy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'Y')},
+ {(char *)"wwyz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'Z')},
+ {(char *)"wwyw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Y', 'W')},
+ {(char *)"wwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z')},
+ {(char *)"wwzx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'X')},
+ {(char *)"wwzy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'Y')},
+ {(char *)"wwzz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'Z')},
+ {(char *)"wwzw", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'Z', 'W')},
+ {(char *)"www", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W')},
+ {(char *)"wwwx", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'X')},
+ {(char *)"wwwy", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'Y')},
+ {(char *)"wwwz", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'Z')},
+ {(char *)"wwww", (getter)Vector_swizzle_get, (setter)NULL, NULL, SWIZZLE('W', 'W', 'W', 'W')},
+
+#undef AXIS_FROM_CHAR
+#undef SWIZZLE
+#undef _VA_SWIZZLE_1
+#undef _VA_SWIZZLE_2
+#undef _VA_SWIZZLE_3
+#undef _VA_SWIZZLE_4
+
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
-/* Python script used to make swizzle array */
-/*
-SWIZZLE_BITS_PER_AXIS = 3
-SWIZZLE_VALID_AXIS = 0x4
-
-axis_dict = {}
-axis_pos = {'x':0, 'y':1, 'z':2, 'w':3}
-axises = 'xyzw'
-while len(axises) >= 2:
-
- for axis_0 in axises:
- axis_0_pos = axis_pos[axis_0]
- for axis_1 in axises:
- axis_1_pos = axis_pos[axis_1]
- axis_dict[axis_0 + axis_1] = '((%s|SWIZZLE_VALID_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<SWIZZLE_BITS_PER_AXIS))' % (axis_0_pos, axis_1_pos)
- if len(axises)>2:
- for axis_2 in axises:
- axis_2_pos = axis_pos[axis_2]
- axis_dict[axis_0 + axis_1 + axis_2] = '((%s|SWIZZLE_VALID_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<SWIZZLE_BITS_PER_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<(SWIZZLE_BITS_PER_AXIS * 2)))' % (axis_0_pos, axis_1_pos, axis_2_pos)
- if len(axises)>3:
- for axis_3 in axises:
- axis_3_pos = axis_pos[axis_3]
- axis_dict[axis_0 + axis_1 + axis_2 + axis_3] = '((%s|SWIZZLE_VALID_AXIS) | ((%s|SWIZZLE_VALID_AXIS)<<SWIZZLE_BITS_PER_AXIS) | ((%s|SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | ((%s|SWIZZLE_VALID_AXIS)<<(SWIZZLE_BITS_PER_AXIS * 3))) ' % (axis_0_pos, axis_1_pos, axis_2_pos, axis_3_pos)
-
- axises = axises[:-1]
-
-
-items = axis_dict.items()
-items.sort(key = lambda a: a[0].replace('x', '0').replace('y', '1').replace('z', '2').replace('w', '3'))
-
-unique = set()
-for key, val in items:
- num = eval(val)
- set_str = 'Vector_setSwizzle' if (len(set(key)) == len(key)) else 'NULL'
- print '\t{"%s", %s(getter)Vector_getSwizzle, (setter)%s, NULL, SET_INT_IN_POINTER(%s)}, // %s' % (key, (' '*(4 - len(key))), set_str, axis_dict[key], num)
- unique.add(num)
-
-if len(unique) != len(items):
- print "ERROR"
-*/
+/**
+ * Python script used to make swizzle array:
+ *
+ * \code{.py}
+ * SWIZZLE_BITS_PER_AXIS = 3
+ * SWIZZLE_VALID_AXIS = 0x4
+ *
+ * axis_dict = {}
+ * axis_pos = {'x': 0, 'y': 1, 'z': 2, 'w': 3}
+ * axises = 'xyzw'
+ * while len(axises) >= 2:
+ * for axis_0 in axises:
+ * axis_0_pos = axis_pos[axis_0]
+ * for axis_1 in axises:
+ * axis_1_pos = axis_pos[axis_1]
+ * axis_dict[axis_0 + axis_1] = (
+ * '((%s | SWIZZLE_VALID_AXIS) | '
+ * '((%s | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS))' %
+ * (axis_0_pos, axis_1_pos))
+ * if len(axises) > 2:
+ * for axis_2 in axises:
+ * axis_2_pos = axis_pos[axis_2]
+ * axis_dict[axis_0 + axis_1 + axis_2] = (
+ * '((%s | SWIZZLE_VALID_AXIS) | '
+ * '((%s | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | '
+ * '((%s | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)))' %
+ * (axis_0_pos, axis_1_pos, axis_2_pos))
+ * if len(axises) > 3:
+ * for axis_3 in axises:
+ * axis_3_pos = axis_pos[axis_3]
+ * axis_dict[axis_0 + axis_1 + axis_2 + axis_3] = (
+ * '((%s | SWIZZLE_VALID_AXIS) | '
+ * '((%s | SWIZZLE_VALID_AXIS) << SWIZZLE_BITS_PER_AXIS) | '
+ * '((%s | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 2)) | '
+ * '((%s | SWIZZLE_VALID_AXIS) << (SWIZZLE_BITS_PER_AXIS * 3))) ' %
+ * (axis_0_pos, axis_1_pos, axis_2_pos, axis_3_pos))
+ *
+ * axises = axises[:-1]
+ *
+ *
+ * items = list(axis_dict.items())
+ * items.sort(key=lambda a: a[0].replace('x', '0').replace('y', '1').replace('z', '2').replace('w', '3'))
+ *
+ * unique = set()
+ * for key, val in items:
+ * num = eval(val)
+ * set_str = 'Vector_swizzle_set' if (len(set(key)) == len(key)) else 'NULL'
+ * key_args = ', '.join(["'%s'" % c for c in key.upper()])
+ * print('\t{(char *)"%s", %s(getter)Vector_swizzle_get, (setter)%s, NULL, SWIZZLE(%s)},' %
+ * (key, (' ' * (4 - len(key))), set_str, key_args))
+ * unique.add(num)
+ *
+ * if len(unique) != len(items):
+ * print("ERROR, duplicate values found")
+ * \endcode
+ */
/* ROW VECTOR Multiplication - Vector X Matrix
* [x][y][z] * [1][4][7]
diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h
index 39f62f9fc33..509ad6f3515 100644
--- a/source/blender/render/extern/include/RE_pipeline.h
+++ b/source/blender/render/extern/include/RE_pipeline.h
@@ -238,6 +238,9 @@ void RE_render_result_rect_from_ibuf(
struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name);
float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname);
+/* add passes for grease pencil */
+struct RenderPass *RE_create_gp_pass(struct RenderResult *rr, const char *layername, const char *viewname);
+
/* obligatory initialize call, disprect is optional */
void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd,
struct SceneRenderLayer *srl,
diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h
index 2619ac76d59..0c4f4e20325 100644
--- a/source/blender/render/intern/include/render_result.h
+++ b/source/blender/render/intern/include/render_result.h
@@ -83,6 +83,9 @@ void render_result_save_empty_result_tiles(struct Render *re);
void render_result_exr_file_begin(struct Render *re);
void render_result_exr_file_end(struct Render *re);
+/* render pass wrapper for gpencil */
+struct RenderPass *gp_add_pass(struct RenderResult *rr, struct RenderLayer *rl, int channels, int passtype, const char *viewname);
+
void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname);
void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath);
diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
index 02a49fc3c8f..724a809077e 100644
--- a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
+++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
@@ -42,6 +42,10 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#if __cplusplus >= 201103L
+using std::isfinite;
+#endif
+
static bool selected_node(RTBuilder::Object *node)
{
return node->selected;
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 1ee905c596c..ec629aa2863 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -2785,6 +2785,7 @@ static void do_render_all_options(Render *re)
/* ensure no images are in memory from previous animated sequences */
BKE_image_all_free_anim_ibufs(re->r.cfra);
+ BKE_sequencer_all_free_anim_ibufs(re->r.cfra);
if (RE_engine_render(re, 1)) {
/* in this case external render overrides all */
@@ -4018,3 +4019,31 @@ RenderPass *RE_pass_find_by_type(volatile RenderLayer *rl, int passtype, const c
}
return rp;
}
+
+/* create a renderlayer and renderpass for grease pencil layer */
+RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const char *viewname)
+{
+ RenderLayer *rl = BLI_findstring(&rr->layers, layername, offsetof(RenderLayer, name));
+ /* only create render layer if not exist */
+ if (!rl) {
+ rl = MEM_callocN(sizeof(RenderLayer), layername);
+ BLI_addtail(&rr->layers, rl);
+ BLI_strncpy(rl->name, layername, sizeof(rl->name));
+ rl->lay = 0;
+ rl->layflag = SCE_LAY_SOLID;
+ rl->passflag = SCE_PASS_COMBINED;
+ rl->rectx = rr->rectx;
+ rl->recty = rr->recty;
+ }
+
+ /* clear previous pass if exist or the new image will be over previous one*/
+ RenderPass *rp = RE_pass_find_by_type(rl, SCE_PASS_COMBINED, viewname);
+ if (rp) {
+ if (rp->rect) {
+ MEM_freeN(rp->rect);
+ }
+ BLI_freelinkN(&rl->passes, rp);
+ }
+ /* create a totally new pass */
+ return gp_add_pass(rr, rl, 4, SCE_PASS_COMBINED, viewname);
+}
diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c
index bddd84c45d7..6ea46af6f7e 100644
--- a/source/blender/render/intern/source/render_result.c
+++ b/source/blender/render/intern/source/render_result.c
@@ -540,6 +540,11 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int
return rpass;
}
+/* wrapper called from render_opengl */
+RenderPass *gp_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname)
+{
+ return render_layer_add_pass(rr, rl, channels, passtype, viewname);
+}
#ifdef WITH_CYCLES_DEBUG
const char *RE_debug_pass_name_get(int debug_type)
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 26a1c7038d8..69905fc296b 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -387,9 +387,7 @@ void WM_gestures_remove(struct bContext *C);
/* fileselecting support */
void WM_event_add_fileselect(struct bContext *C, struct wmOperator *op);
void WM_event_fileselect_event(struct wmWindowManager *wm, void *ophandle, int eventval);
-#ifndef NDEBUG
void WM_event_print(const struct wmEvent *event);
-#endif
void WM_operator_region_active_win_set(struct bContext *C);
@@ -443,6 +441,7 @@ enum {
WM_JOB_TYPE_SEQ_BUILD_PREVIEW,
WM_JOB_TYPE_POINTCACHE,
WM_JOB_TYPE_DPAINT_BAKE,
+ WM_JOB_TYPE_ALEMBIC,
/* add as needed, screencast, seq proxy build
* if having hard coded values is a problem */
};
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 962ed3c040d..3825db14e93 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -862,11 +862,8 @@ static int wm_automatic_draw_method(wmWindow *win)
* copy to texture is slow though and so we use overlap instead there. */
if (win->drawmethod == USER_DRAW_AUTOMATIC) {
- /* ATI opensource driver is known to be very slow at this */
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE))
- return USER_DRAW_OVERLAP;
/* Windows software driver darkens color on each redraw */
- else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_WIN, GPU_DRIVER_SOFTWARE))
+ if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_WIN, GPU_DRIVER_SOFTWARE))
return USER_DRAW_OVERLAP_FLIP;
else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_UNIX, GPU_DRIVER_SOFTWARE))
return USER_DRAW_OVERLAP;
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 4515ae92f66..01dc77127d2 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -81,9 +81,7 @@
#include "wm_event_system.h"
#include "wm_event_types.h"
-#ifndef NDEBUG
-# include "RNA_enum_types.h"
-#endif
+#include "RNA_enum_types.h"
static void wm_notifier_clear(wmNotifier *note);
static void update_tablet_data(wmWindow *win, wmEvent *event);
@@ -550,8 +548,6 @@ void WM_operator_region_active_win_set(bContext *C)
}
/* for debugging only, getting inspecting events manually is tedious */
-#ifndef NDEBUG
-
void WM_event_print(const wmEvent *event)
{
if (event) {
@@ -593,8 +589,6 @@ void WM_event_print(const wmEvent *event)
}
}
-#endif /* NDEBUG */
-
/**
* Show the report in the info header.
*/
@@ -1969,15 +1963,11 @@ static int wm_action_not_handled(int action)
static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers)
{
-#ifndef NDEBUG
const bool do_debug_handler = (G.debug & G_DEBUG_HANDLERS) &&
/* comment this out to flood the console! (if you really want to test) */
!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)
;
-# define PRINT if (do_debug_handler) printf
-#else
-# define PRINT(format, ...)
-#endif
+# define PRINT if (do_debug_handler) printf
wmWindowManager *wm = CTX_wm_manager(C);
wmEventHandler *handler, *nexthandler;
@@ -2382,20 +2372,16 @@ void wm_event_do_handlers(bContext *C)
while ( (event = win->queue.first) ) {
int action = WM_HANDLER_CONTINUE;
-#ifndef NDEBUG
if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
printf("\n%s: Handling event\n", __func__);
WM_event_print(event);
}
-#endif
/* take care of pie event filter */
if (wm_event_pie_filter(win, event)) {
-#ifndef NDEBUG
if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
printf("\n%s: event filtered due to pie button pressed\n", __func__);
}
-#endif
BLI_remlink(&win->queue, event);
wm_event_free(event);
continue;
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index e2ccb6fbbd7..3405537a09c 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -577,7 +577,7 @@ static void lib_relocate_do(
WMLinkAppendDataItem *item;
/* We remove it from current Main, and add it to items to link... */
- /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitely linked here... */
+ /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */
BLI_remlink(lbarray[lba_idx], id);
item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id);
BLI_BITMAP_SET_ALL(item->libraries, true, lapp_data->num_libraries);
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 7ec2aea73e1..18836f34c99 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -97,6 +97,8 @@ void WM_operator_properties_filesel(
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "filter_collada", (filter & FILE_TYPE_COLLADA) != 0, "Filter COLLADA files", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "filter_alembic", (filter & FILE_TYPE_ALEMBIC) != 0, "Filter Alembic files", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", "");
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 42f6585f152..425287993d9 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -69,6 +69,8 @@
#include "ED_screen.h"
#include "ED_fileselect.h"
+#include "UI_interface.h"
+
#include "PIL_time.h"
#include "GPU_draw.h"
@@ -1194,14 +1196,27 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
break;
}
case GHOST_kEventNativeResolutionChange:
+ {
// printf("change, pixel size %f\n", GHOST_GetNativePixelSize(win->ghostwin));
U.pixelsize = wm_window_pixelsize(win);
BKE_blender_userdef_refresh();
+
+ // close all popups since they are positioned with the pixel
+ // size baked in and it's difficult to correct them
+ wmWindow *oldWindow = CTX_wm_window(C);
+ CTX_wm_window_set(C, win);
+ UI_popup_handlers_remove_all(C, &win->modalhandlers);
+ CTX_wm_window_set(C, oldWindow);
+
+ wm_window_make_drawable(wm, win);
+ wm_draw_window_clear(win);
+
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
break;
+ }
case GHOST_kEventTrackpad:
{
GHOST_TEventTrackpadData *pd = data;
diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt
index d7d65629928..c5999a90765 100644
--- a/source/blenderplayer/CMakeLists.txt
+++ b/source/blenderplayer/CMakeLists.txt
@@ -236,6 +236,10 @@ endif()
list(APPEND BLENDER_SORTED_LIBS bf_intern_openvdb)
endif()
+ if(WITH_ALEMBIC)
+ list(APPEND BLENDER_SORTED_LIBS bf_alembic)
+ endif()
+
foreach(SORTLIB ${BLENDER_SORTED_LIBS})
set(REMLIB ${SORTLIB})
foreach(SEARCHLIB ${BLENDER_LINK_LIBS})
diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c
index 8de70d87f00..cdfd326d986 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -45,6 +45,7 @@ struct ARegion;
struct ARegionType;
struct BMEditMesh;
struct Base;
+struct bContext;
struct BoundBox;
struct Brush;
struct CSG_FaceIteratorDescriptor;
@@ -111,6 +112,7 @@ struct bConstraint;
struct bConstraintOb;
struct bConstraintTarget;
struct bContextDataResult;
+struct bGPDlayer;
struct bNode;
struct bNodeType;
struct bNodeSocket;
@@ -153,6 +155,7 @@ struct wmWindowManager;
#include "../blender/editors/include/ED_clip.h"
#include "../blender/editors/include/ED_curve.h"
#include "../blender/editors/include/ED_fileselect.h"
+#include "../blender/editors/include/ED_gpencil.h"
#include "../blender/editors/include/ED_image.h"
#include "../blender/editors/include/ED_info.h"
#include "../blender/editors/include/ED_keyframes_edit.h"
@@ -320,6 +323,19 @@ void WM_cursor_modal_restore(struct wmWindow *win) RET_NONE
void WM_cursor_time(struct wmWindow *win, int nr) RET_NONE
void WM_cursor_warp(struct wmWindow *win, int x, int y) RET_NONE
+struct wmJob *WM_jobs_get(struct wmWindowManager *wm, struct wmWindow *win, void *owner, const char *name, int flag, int job_type) RET_NULL
+void WM_jobs_customdata_set(struct wmJob *job, void *customdata, void (*free)(void *)) RET_NONE
+void WM_jobs_timer(struct wmJob *job, double timestep, unsigned int note, unsigned int endnote) RET_NONE
+
+void WM_jobs_callbacks(struct wmJob *job,
+ void (*startjob)(void *, short *, short *, float *),
+ void (*initjob)(void *),
+ void (*update)(void *),
+ void (*endjob)(void *)) RET_NONE
+
+void WM_jobs_start(struct wmWindowManager *wm, struct wmJob *job) RET_NONE
+void WM_report(ReportType type, const char *message) RET_NONE
+
void WM_ndof_deadzone_set(float deadzone) RET_NONE
void WM_uilisttype_init(void) RET_NONE
@@ -350,6 +366,7 @@ void *ED_region_draw_cb_activate(struct ARegionType *art, void(*draw)(const stru
void *ED_region_draw_cb_customdata(void *handle) RET_ZERO /* XXX This one looks wrong also */
void ED_region_draw_cb_exit(struct ARegionType *art, void *handle) RET_NONE
void ED_area_headerprint(struct ScrArea *sa, const char *str) RET_NONE
+void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]) RET_NONE
void UI_view2d_region_to_view(struct View2D *v2d, float x, float y, float *viewx, float *viewy) RET_NONE
bool UI_view2d_view_to_region_clip(struct View2D *v2d, float x, float y, int *regionx, int *regiony) RET_ZERO
void UI_view2d_view_to_region(struct View2D *v2d, float x, float y, int *regionx, int *region_y) RET_NONE
@@ -620,6 +637,7 @@ void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, co
void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE
void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE
void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE
+void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname) RET_NONE
/* rna render */
struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL
@@ -726,7 +744,7 @@ void BPY_text_free_code(struct Text *text) RET_NONE
void BPY_id_release(struct ID *id) RET_NONE
int BPY_context_member_get(struct bContext *C, const char *member, struct bContextDataResult *result) RET_ZERO
void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTarget *ct) RET_NONE
-float BPY_driver_exec(struct ChannelDriver *driver, const float evaltime) RET_ZERO /* might need this one! */
+float BPY_driver_exec(PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime) RET_ZERO /* might need this one! */
void BPY_DECREF(void *pyob_ptr) RET_NONE
void BPY_pyconstraint_exec(struct bPythonConstraint *con, struct bConstraintOb *cob, struct ListBase *targets) RET_NONE
void macro_wrapper(struct wmOperatorType *ot, void *userdata) RET_NONE
diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
index 9acd4e441b6..79e511b1cee 100644
--- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
+++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
@@ -61,6 +61,7 @@ extern "C"
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
+#include "DNA_genfile.h"
#include "BLO_readfile.h"
#include "BLO_runtime.h"
@@ -494,6 +495,8 @@ int main(
// freeing up GPU_Textures works correctly.
BLI_threadapi_init();
+ DNA_sdna_current_init();
+
RNA_init();
init_nodesystem();
diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp
index c0d99d16a4c..b3061087344 100644
--- a/source/gameengine/Ketsji/KX_Scene.cpp
+++ b/source/gameengine/Ketsji/KX_Scene.cpp
@@ -1665,7 +1665,7 @@ static void update_anim_thread_func(TaskPool *pool, void *taskdata, int UNUSED(t
// Only do deformers here if they are not parented to an armature, otherwise the armature will
// handle updating its children
- if (gameobj->GetDeformer() && (!parent || (parent && parent->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE)))
+ if (gameobj->GetDeformer() && (!parent || parent->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE))
gameobj->GetDeformer()->Update();
for (int j=0; j<children->GetCount(); ++j) {
diff --git a/tests/gtests/testing/mock_log.h b/tests/gtests/testing/mock_log.h
new file mode 100644
index 00000000000..2ec558fac87
--- /dev/null
+++ b/tests/gtests/testing/mock_log.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: Zhanyong Wan
+//
+// Defines the ScopedMockLog class (using Google C++ Mocking
+// Framework), which is convenient for testing code that uses LOG().
+//
+// NOTE(keir): This is a fork until Google Log exports the scoped mock log
+// class; see: http://code.google.com/p/google-glog/issues/detail?id=88
+
+#ifndef GOOGLE_CERES_INTERNAL_MOCK_LOG_H_
+#define GOOGLE_CERES_INTERNAL_MOCK_LOG_H_
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "glog/logging.h"
+
+// Needed to make the scoped mock log tests work without modification.
+namespace ceres {
+namespace internal {
+using google::WARNING;
+} // namespace internal
+} // namespace ceres
+
+namespace testing {
+
+// A ScopedMockLog object intercepts LOG() messages issued during its
+// lifespan. Using this together with Google C++ Mocking Framework,
+// it's very easy to test how a piece of code calls LOG(). The
+// typical usage:
+//
+// TEST(FooTest, LogsCorrectly) {
+// ScopedMockLog log;
+//
+// // We expect the WARNING "Something bad!" exactly twice.
+// EXPECT_CALL(log, Log(WARNING, _, "Something bad!"))
+// .Times(2);
+//
+// // We allow foo.cc to call LOG(INFO) any number of times.
+// EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _))
+// .Times(AnyNumber());
+//
+// Foo(); // Exercises the code under test.
+// }
+class ScopedMockLog : public google::LogSink {
+ public:
+ // When a ScopedMockLog object is constructed, it starts to
+ // intercept logs.
+ ScopedMockLog() { AddLogSink(this); }
+
+ // When the object is destructed, it stops intercepting logs.
+ virtual ~ScopedMockLog() { RemoveLogSink(this); }
+
+ // Implements the mock method:
+ //
+ // void Log(LogSeverity severity, const string& file_path,
+ // const string& message);
+ //
+ // The second argument to Send() is the full path of the source file
+ // in which the LOG() was issued.
+ //
+ // Note, that in a multi-threaded environment, all LOG() messages from a
+ // single thread will be handled in sequence, but that cannot be guaranteed
+ // for messages from different threads. In fact, if the same or multiple
+ // expectations are matched on two threads concurrently, their actions will
+ // be executed concurrently as well and may interleave.
+ MOCK_METHOD3(Log, void(google::LogSeverity severity,
+ const std::string& file_path,
+ const std::string& message));
+
+ private:
+ // Implements the send() virtual function in class LogSink.
+ // Whenever a LOG() statement is executed, this function will be
+ // invoked with information presented in the LOG().
+ //
+ // The method argument list is long and carries much information a
+ // test usually doesn't care about, so we trim the list before
+ // forwarding the call to Log(), which is much easier to use in
+ // tests.
+ //
+ // We still cannot call Log() directly, as it may invoke other LOG()
+ // messages, either due to Invoke, or due to an error logged in
+ // Google C++ Mocking Framework code, which would trigger a deadlock
+ // since a lock is held during send().
+ //
+ // Hence, we save the message for WaitTillSent() which will be called after
+ // the lock on send() is released, and we'll call Log() inside
+ // WaitTillSent(). Since while a single send() call may be running at a
+ // time, multiple WaitTillSent() calls (along with the one send() call) may
+ // be running simultaneously, we ensure thread-safety of the exchange between
+ // send() and WaitTillSent(), and that for each message, LOG(), send(),
+ // WaitTillSent() and Log() are executed in the same thread.
+ virtual void send(google::LogSeverity severity,
+ const char* full_filename,
+ const char* /*base_filename*/,
+ int /*line*/,
+ const tm* /*tm_time*/,
+ const char* message,
+ size_t message_len) {
+ // We are only interested in the log severity, full file name, and
+ // log message.
+ message_info_.severity = severity;
+ message_info_.file_path = full_filename;
+ message_info_.message = std::string(message, message_len);
+ }
+
+ // Implements the WaitTillSent() virtual function in class LogSink.
+ // It will be executed after send() and after the global logging lock is
+ // released, so calls within it (or rather within the Log() method called
+ // within) may also issue LOG() statements.
+ //
+ // LOG(), send(), WaitTillSent() and Log() will occur in the same thread for
+ // a given log message.
+ virtual void WaitTillSent() {
+ // First, and very importantly, we save a copy of the message being
+ // processed before calling Log(), since Log() may indirectly call send()
+ // and WaitTillSent() in the same thread again.
+ MessageInfo message_info = message_info_;
+ Log(message_info.severity, message_info.file_path, message_info.message);
+ }
+
+ // All relevant information about a logged message that needs to be passed
+ // from send() to WaitTillSent().
+ struct MessageInfo {
+ google::LogSeverity severity;
+ std::string file_path;
+ std::string message;
+ };
+ MessageInfo message_info_;
+};
+
+} // namespace testing
+
+#endif // GOOGLE_CERES_INTERNAL_MOCK_LOG_H_
diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py
index e1b5e2d1d23..fb9a4b348b7 100755
--- a/tests/python/cycles_render_tests.py
+++ b/tests/python/cycles_render_tests.py
@@ -17,6 +17,8 @@ def render_file(filepath):
"--factory-startup",
filepath,
"-E", "CYCLES",
+ # Run with OSL enabled
+ # "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True",
"-o", TEMP_FILE_MASK,
"-F", "PNG",
"-f", "1",
@@ -66,15 +68,16 @@ def verify_output(filepath):
)
try:
subprocess.check_output(command)
- if os.path.exists(failed_image):
- os.remove(failed_image)
- return True
+ failed = False
except subprocess.CalledProcessError as e:
- if e.returncode != 1:
- shutil.copy(TEMP_FILE, failed_image)
if VERBOSE:
print(e.output.decode("utf-8"))
- return e.returncode == 1
+ failed = e.returncode != 1
+ if failed:
+ shutil.copy(TEMP_FILE, failed_image)
+ elif os.path.exists(failed_image):
+ os.remove(failed_image)
+ return not failed
def run_test(filepath):
diff --git a/tests/python/pep8.py b/tests/python/pep8.py
index 61093fd7c27..0e6250f534b 100644
--- a/tests/python/pep8.py
+++ b/tests/python/pep8.py
@@ -113,7 +113,7 @@ def main():
print("%s:%d:0: empty class (), remove" % (f, i + 1))
del re, class_check
- print("\n\n\n# running pep8...")
+ print("\n\n\n# running flake8...")
# these are very picky and often hard to follow
# while keeping common script formatting.
@@ -128,6 +128,10 @@ def main():
# "imports not at top of file."
# prefer to load as needed (lazy load addons etc).
"E402",
+ # "do not compare types, use 'isinstance()'"
+ # times types are compared,
+ # I rather keep them specific
+ "E721",
)
for f, pep8_type in files:
@@ -138,7 +142,10 @@ def main():
else:
ignore_tmp = ignore
- os.system("pep8 --repeat --ignore=%s '%s'" % (",".join(ignore_tmp), f))
+ os.system("flake8 "
+ "--isolated "
+ "--ignore=%s '%s'" %
+ (",".join(ignore_tmp), f))
# frosted
print("\n\n\n# running frosted...")
@@ -152,6 +159,7 @@ def main():
"--disable="
"C0111," # missing doc string
"C0103," # invalid name
+ "C0413," # import should be placed at the top
"W0613," # unused argument, may add this back
# but happens a lot for 'context' for eg.
"W0232," # class has no __init__, Operator/Panel/Menu etc
@@ -165,7 +173,6 @@ def main():
"R0914," # Too many local variables
"R0915," # Too many statements
" "
- "--include-ids=y "
"--output-format=parseable "
"--reports=n "
"--max-line-length=1000"