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:
authorPascal Schoen <pascal_schoen@gmx.net>2016-08-16 16:22:32 +0300
committerPascal Schoen <pascal_schoen@gmx.net>2016-08-16 16:22:32 +0300
commit9eed34c7d980e1b998df457c4f76021162c80f78 (patch)
tree0c47e10e97c2088d59a52c3802c35f7e9eb7901f
parentef29aaee1af8074e0228c480d962700e97ea5b36 (diff)
parentae475e355488db27c4b9fcc33385080578403833 (diff)
Merge branch 'master' into cycles_disney_brdf
-rw-r--r--CMakeLists.txt71
-rwxr-xr-xbuild_files/build_environment/install_deps.sh193
-rw-r--r--build_files/cmake/Modules/FindAlembic.cmake70
-rw-r--r--build_files/cmake/Modules/FindHDF5.cmake69
-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
-rw-r--r--build_files/cmake/macros.cmake9
-rw-r--r--extern/curve_fit_nd/README.blender5
-rw-r--r--extern/curve_fit_nd/intern/curve_fit_cubic.c12
-rw-r--r--intern/cycles/CMakeLists.txt8
-rw-r--r--intern/cycles/app/CMakeLists.txt3
-rw-r--r--intern/cycles/app/cycles_standalone.cpp2
-rw-r--r--intern/cycles/app/cycles_xml.cpp14
-rw-r--r--intern/cycles/blender/addon/osl.py6
-rw-r--r--intern/cycles/blender/addon/properties.py14
-rw-r--r--intern/cycles/blender/addon/ui.py39
-rw-r--r--intern/cycles/blender/blender_mesh.cpp70
-rw-r--r--intern/cycles/blender/blender_object.cpp18
-rw-r--r--intern/cycles/blender/blender_shader.cpp13
-rw-r--r--intern/cycles/device/device.cpp6
-rw-r--r--intern/cycles/device/device.h10
-rw-r--r--intern/cycles/device/device_cuda.cpp11
-rw-r--r--intern/cycles/device/device_opencl.cpp1
-rw-r--r--intern/cycles/kernel/CMakeLists.txt2
-rw-r--r--intern/cycles/kernel/closure/bsdf.h2
-rw-r--r--intern/cycles/kernel/geom/geom.h3
-rw-r--r--intern/cycles/kernel/geom/geom_attribute.h39
-rw-r--r--intern/cycles/kernel/geom/geom_curve.h24
-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.h65
-rw-r--r--intern/cycles/kernel/geom/geom_subd_triangle.h203
-rw-r--r--intern/cycles/kernel/geom/geom_triangle.h38
-rw-r--r--intern/cycles/kernel/geom/geom_volume.h27
-rw-r--r--intern/cycles/kernel/kernel_compat_cpu.h1
-rw-r--r--intern/cycles/kernel/kernel_compat_cuda.h3
-rw-r--r--intern/cycles/kernel/kernel_compat_opencl.h1
-rw-r--r--intern/cycles/kernel/kernel_path.h8
-rw-r--r--intern/cycles/kernel/kernel_shader.h4
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h22
-rw-r--r--intern/cycles/kernel/kernel_textures.h2
-rw-r--r--intern/cycles/kernel/kernel_types.h60
-rw-r--r--intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h14
-rw-r--r--intern/cycles/kernel/osl/osl_globals.h3
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp8
-rw-r--r--intern/cycles/kernel/osl/osl_shader.cpp17
-rw-r--r--intern/cycles/kernel/osl/osl_shader.h2
-rw-r--r--intern/cycles/kernel/shaders/node_rgb_curves.osl1
-rw-r--r--intern/cycles/kernel/shaders/node_rgb_ramp.osl1
-rw-r--r--intern/cycles/kernel/shaders/node_vector_curves.osl1
-rw-r--r--intern/cycles/kernel/svm/svm_attribute.h101
-rw-r--r--intern/cycles/kernel/svm/svm_image.h23
-rw-r--r--intern/cycles/kernel/svm/svm_math_util.h10
-rw-r--r--intern/cycles/kernel/svm/svm_tex_coord.h29
-rw-r--r--intern/cycles/render/attribute.cpp40
-rw-r--r--intern/cycles/render/attribute.h5
-rw-r--r--intern/cycles/render/image.cpp100
-rw-r--r--intern/cycles/render/image.h6
-rw-r--r--intern/cycles/render/mesh.cpp160
-rw-r--r--intern/cycles/render/mesh.h21
-rw-r--r--intern/cycles/render/mesh_displace.cpp160
-rw-r--r--intern/cycles/render/mesh_subdivision.cpp453
-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.h2
-rw-r--r--intern/cycles/render/session.cpp5
-rw-r--r--intern/cycles/render/shader.cpp10
-rw-r--r--intern/cycles/render/shader.h11
-rw-r--r--intern/cycles/subd/CMakeLists.txt6
-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/test/CMakeLists.txt11
-rw-r--r--intern/cycles/util/CMakeLists.txt5
-rw-r--r--intern/cycles/util/util_debug.h2
-rw-r--r--intern/cycles/util/util_half.h12
-rw-r--r--intern/cycles/util/util_math.h6
-rw-r--r--intern/cycles/util/util_static_assert.h64
-rw-r--r--intern/cycles/util/util_texture.h46
-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_WindowWin32.cpp2
-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
-rw-r--r--release/scripts/freestyle/modules/parameter_editor.py15
-rw-r--r--release/scripts/modules/sys_info.py7
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py13
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py26
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py474
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py2
-rw-r--r--release/scripts/startup/bl_ui/space_clip.py120
-rw-r--r--release/scripts/startup/bl_ui/space_image.py21
-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.py16
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py13
-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_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_cachefile.h67
-rw-r--r--source/blender/blenkernel/BKE_collision.h4
-rw-r--r--source/blender/blenkernel/BKE_context.h9
-rw-r--r--source/blender/blenkernel/BKE_effect.h2
-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.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h5
-rw-r--r--source/blender/blenkernel/BKE_material.h10
-rw-r--r--source/blender/blenkernel/BKE_particle.h7
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h2
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h2
-rw-r--r--source/blender/blenkernel/BKE_tracking.h4
-rw-r--r--source/blender/blenkernel/CMakeLists.txt9
-rw-r--r--source/blender/blenkernel/depsgraph_private.h10
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c10
-rw-r--r--source/blender/blenkernel/intern/bpath.c7
-rw-r--r--source/blender/blenkernel/intern/cachefile.c173
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c2
-rw-r--r--source/blender/blenkernel/intern/cloth.c11
-rw-r--r--source/blender/blenkernel/intern/collision.c18
-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/depsgraph.c153
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c6
-rw-r--r--source/blender/blenkernel/intern/effect.c31
-rw-r--r--source/blender/blenkernel/intern/gpencil.c834
-rw-r--r--source/blender/blenkernel/intern/idcode.c53
-rw-r--r--source/blender/blenkernel/intern/image.c1
-rw-r--r--source/blender/blenkernel/intern/library.c119
-rw-r--r--source/blender/blenkernel/intern/library_query.c40
-rw-r--r--source/blender/blenkernel/intern/library_remap.c7
-rw-r--r--source/blender/blenkernel/intern/material.c30
-rw-r--r--source/blender/blenkernel/intern/movieclip.c6
-rw-r--r--source/blender/blenkernel/intern/particle.c7
-rw-r--r--source/blender/blenkernel/intern/particle_system.c34
-rw-r--r--source/blender/blenkernel/intern/pointcache.c17
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c6
-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.c13
-rw-r--r--source/blender/blenkernel/intern/softbody.c9
-rw-r--r--source/blender/blenkernel/intern/tracking.c29
-rw-r--r--source/blender/blenkernel/intern/tracking_stabilize.c1447
-rw-r--r--source/blender/blenlib/BLI_system.h5
-rw-r--r--source/blender/blenlib/BLI_utildefines.h2
-rw-r--r--source/blender/blenlib/intern/system.c1
-rw-r--r--source/blender/blenloader/CMakeLists.txt7
-rw-r--r--source/blender/blenloader/intern/readfile.c87
-rw-r--r--source/blender/blenloader/intern/versioning_270.c148
-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/compositor/nodes/COM_MovieClipNode.cpp2
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp2
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h11
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc23
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc95
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h5
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc69
-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/nodes/deg_node_component.cc7
-rw-r--r--source/blender/depsgraph/intern/nodes/deg_node_component.h4
-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/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.h1
-rw-r--r--source/blender/editors/interface/interface_icons.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c78
-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/mesh/editmesh_tools.c18
-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.c15
-rw-r--r--source/blender/editors/render/render_opengl.c97
-rw-r--r--source/blender/editors/screen/screen_context.c64
-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_intern.h5
-rw-r--r--source/blender/editors/space_clip/clip_toolbar.c2
-rw-r--r--source/blender/editors/space_clip/clip_utils.c12
-rw-r--r--source/blender/editors/space_clip/space_clip.c20
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c25
-rw-r--r--source/blender/editors/space_clip/tracking_ops_stabilize.c141
-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.c2
-rw-r--r--source/blender/editors/space_graph/space_graph.c2
-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_relationships.c15
-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.c65
-rw-r--r--source/blender/editors/space_view3d/drawvolume.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_ruler.c12
-rw-r--r--source/blender/editors/transform/transform_conversions.c60
-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/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/GPU_debug.h4
-rw-r--r--source/blender/gpu/intern/gpu_debug.c125
-rw-r--r--source/blender/gpu/intern/gpu_texture.c16
-rw-r--r--source/blender/makesdna/DNA_ID.h42
-rw-r--r--source/blender/makesdna/DNA_action_types.h4
-rw-r--r--source/blender/makesdna/DNA_cachefile_types.h84
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h7
-rw-r--r--source/blender/makesdna/DNA_genfile.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h143
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h22
-rw-r--r--source/blender/makesdna/DNA_scene_types.h23
-rw-r--r--source/blender/makesdna/DNA_space_types.h1
-rw-r--r--source/blender/makesdna/DNA_tracking_types.h27
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c5
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h6
-rw-r--r--source/blender/makesrna/RNA_enum_types.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt8
-rw-r--r--source/blender/makesrna/intern/makesrna.c3
-rw-r--r--source/blender/makesrna/intern/rna_ID.c16
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c169
-rw-r--r--source/blender/makesrna/intern/rna_cloth.c31
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c26
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c828
-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_object_force.c9
-rw-r--r--source/blender/makesrna/intern/rna_particle.c8
-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_space.c4
-rw-r--r--source/blender/makesrna/intern/rna_tracking.c157
-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_cloth.c30
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c32
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c197
-rw-r--r--source/blender/modifiers/intern/MOD_smoke.c207
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c33
-rw-r--r--source/blender/modifiers/intern/MOD_util.c1
-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/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.h1
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c2
-rw-r--r--source/blender/windowmanager/intern/wm_window.c28
-rw-r--r--source/blenderplayer/CMakeLists.txt4
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c18
-rw-r--r--source/gameengine/GamePlayer/ghost/GPG_ghost.cpp3
332 files changed, 22209 insertions, 2411 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..51928b82d8c 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.
@@ -328,7 +337,7 @@ OSL_FORCE_REBUILD=false
OSL_SKIP=false
# OpenSubdiv needs to be compiled for now
-OSD_VERSION="3.0.2"
+OSD_VERSION="3.0.5"
OSD_VERSION_MIN=$OSD_VERSION
OSD_FORCE_BUILD=false
OSD_FORCE_REBUILD=false
@@ -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" )
@@ -712,7 +739,7 @@ OSL_SOURCE_REPO=( "https://github.com/Nazg-Gul/OpenShadingLanguage.git" )
OSL_SOURCE_REPO_UID="7d40ff5fe8e47b030042afb92d0e955f5aa96f48"
OSL_SOURCE_REPO_BRANCH="blender-fixes"
-OSD_USE_REPO=true
+OSD_USE_REPO=false
# Script foo to make the version string compliant with the archive name:
# ${Varname//SearchForThisChar/ReplaceWithThisChar}
OSD_SOURCE=( "https://github.com/PixarAnimationStudios/OpenSubdiv/archive/v${OSD_VERSION//./_}.tar.gz" )
@@ -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
@@ -1873,7 +1907,7 @@ compile_OSD() {
fi
# To be changed each time we make edits that would modify the compiled result!
- osd_magic=1
+ osd_magic=2
_init_osd
# Clean install if needed!
@@ -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/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/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/macros.cmake b/build_files/cmake/macros.cmake
index f57a6952164..c4e1c56699e 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()
@@ -607,6 +615,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/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 9c8ebcd098b..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;
}
@@ -896,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/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/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index 73dbf16a3d3..8cd499b7ca6 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -88,6 +88,9 @@ macro(cycles_target_link_libraries target)
if(WITH_CYCLES_OSL)
target_link_libraries(${target} ${OSL_LIBRARIES} ${LLVM_LIBRARIES})
endif()
+ if(WITH_CYCLES_OPENSUBDIV)
+ target_link_libraries(${target} ${OPENSUBDIV_LIBRARIES})
+ endif()
target_link_libraries(
${target}
${OPENIMAGEIO_LIBRARIES}
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index 726e9a51744..e8168bc15ff 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -375,6 +375,8 @@ static void options_parse(int argc, const char **argv)
"--threads %d", &options.session_params.threads, "CPU Rendering Threads",
"--width %d", &options.width, "Window width in pixel",
"--height %d", &options.height, "Window height in pixel",
+ "--tile-width %d", &options.session_params.tile_size.x, "Tile width in pixels",
+ "--tile-height %d", &options.session_params.tile_size.y, "Tile height in pixels",
"--list-devices", &list, "List information about all available devices",
#ifdef WITH_CYCLES_LOGGING
"--debug", &debug, "Enable debug logging",
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index 3d3aca33881..a54022268bb 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -57,14 +57,12 @@ struct XMLReadState : public XMLReader {
Shader *shader; /* current shader */
string base; /* base path to current file*/
float dicing_rate; /* current dicing rate */
- Mesh::DisplacementMethod displacement_method;
XMLReadState()
: scene(NULL),
smooth(false),
shader(NULL),
- dicing_rate(0.0f),
- displacement_method(Mesh::DISPLACE_BUMP)
+ dicing_rate(0.0f)
{
tfm = transform_identity();
}
@@ -405,8 +403,6 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
int shader = 0;
bool smooth = state.smooth;
- mesh->displacement_method = state.displacement_method;
-
/* read vertices and polygons, RIB style */
vector<float3> P;
vector<float> UV;
@@ -653,14 +649,6 @@ static void xml_read_state(XMLReadState& state, pugi::xml_node node)
state.smooth = true;
else if(xml_equal_string(node, "interpolation", "flat"))
state.smooth = false;
-
- /* read displacement method */
- if(xml_equal_string(node, "displacement_method", "true"))
- state.displacement_method = Mesh::DISPLACE_TRUE;
- else if(xml_equal_string(node, "displacement_method", "bump"))
- state.displacement_method = Mesh::DISPLACE_BUMP;
- else if(xml_equal_string(node, "displacement_method", "both"))
- state.displacement_method = Mesh::DISPLACE_BOTH;
}
/* Scene */
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 81204eb8ae0..8e82eac2b59 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -775,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
@@ -952,13 +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',
- )
-
@classmethod
def unregister(cls):
del bpy.types.Mesh.cycles
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 42f7970769a..52872d2b83f 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -674,40 +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="")
-
class CyclesObject_PT_motion_blur(CyclesButtonsPanel, Panel):
bl_label = "Motion Blur"
bl_context = "object"
@@ -1219,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/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 74fd4cb44a0..c33bc4c263f 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -409,7 +409,8 @@ static void attr_create_uv_map(Scene *scene,
BL::Mesh& b_mesh,
const vector<int>& nverts,
const vector<int>& face_flags,
- bool subdivision)
+ bool subdivision,
+ bool subdivide_uvs)
{
if(subdivision) {
BL::Mesh::uv_layers_iterator l;
@@ -429,6 +430,10 @@ static void attr_create_uv_map(Scene *scene,
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();
@@ -592,7 +597,8 @@ static void create_mesh(Scene *scene,
Mesh *mesh,
BL::Mesh& b_mesh,
const vector<Shader*>& used_shaders,
- bool subdivision=false)
+ bool subdivision=false,
+ bool subdivide_uvs=true)
{
/* count vertices and faces */
int numverts = b_mesh.vertices.length();
@@ -638,6 +644,7 @@ static void create_mesh(Scene *scene,
/* create generated coordinates from undeformed coordinates */
if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
Attribute *attr = attributes.add(ATTR_STD_GENERATED);
+ attr->flags |= ATTR_SUBDIVIDED;
float3 loc, size;
mesh_texture_space(b_mesh, loc, size);
@@ -746,7 +753,7 @@ static void create_mesh(Scene *scene,
* The calculate functions will check whether they're needed or not.
*/
attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags, subdivision);
- attr_create_uv_map(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
@@ -770,9 +777,39 @@ static void create_subd_mesh(Scene *scene,
float dicing_rate,
int max_subdivisions)
{
- create_mesh(scene, mesh, b_mesh, used_shaders, true);
+ 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);
+
+ 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();
- SubdParams sdparams(mesh);
+ crease++;
+ }
+ }
+
+ /* set subd params */
+ if(!mesh->subd_params) {
+ mesh->subd_params = new SubdParams(mesh);
+ }
+ SubdParams& sdparams = *mesh->subd_params;
PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
@@ -782,10 +819,6 @@ static void create_subd_mesh(Scene *scene,
scene->camera->update();
sdparams.camera = scene->camera;
sdparams.objecttoworld = get_transform(b_ob.matrix_world());
-
- /* tesselate */
- DiagSplit dsplit(sdparams);
- mesh->tessellate(&dsplit);
}
/* Sync */
@@ -903,8 +936,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
@@ -936,7 +967,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
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")) {
+ if(enabled && mod.type() == BL::Modifier::type_SUBSURF && RNA_boolean_get(&cobj, "use_adaptive_subdivision")) {
BL::SubsurfModifier subsurf(mod);
if(subsurf.subdivision_type() == BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) {
@@ -974,21 +1005,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);
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 4886735a18f..f305e8e17cc 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -329,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);
@@ -395,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 7b8317a50a7..171b8241280 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) {
@@ -840,8 +848,10 @@ static ShaderNode *add_node(Scene *scene,
}
}
- if(node)
+ if(node) {
+ node->name = b_node.name();
graph->add(node);
+ }
return node;
}
@@ -1183,6 +1193,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/device/device.cpp b/intern/cycles/device/device.cpp
index df01215c91a..85e736ad635 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -56,8 +56,14 @@ std::ostream& operator <<(std::ostream &os,
<< string_from_bool(requested_features.use_camera_motion) << std::endl;
os << "Use Baking: "
<< string_from_bool(requested_features.use_baking) << std::endl;
+ os << "Use Subsurface: "
+ << string_from_bool(requested_features.use_subsurface) << std::endl;
os << "Use Volume: "
<< string_from_bool(requested_features.use_volume) << std::endl;
+ os << "Use Branched Integrator: "
+ << string_from_bool(requested_features.use_integrator_branched) << std::endl;
+ os << "Use Patch Evaluation: "
+ << string_from_bool(requested_features.use_patch_evaluation) << std::endl;
return os;
}
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index e11bb7f76af..77dc1fa9713 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -109,6 +109,9 @@ public:
/* Use branched integrator. */
bool use_integrator_branched;
+ /* Use OpenSubdiv patch evaluation */
+ bool use_patch_evaluation;
+
DeviceRequestedFeatures()
{
/* TODO(sergey): Find more meaningful defaults. */
@@ -123,6 +126,7 @@ public:
use_subsurface = false;
use_volume = false;
use_integrator_branched = false;
+ use_patch_evaluation = false;
}
bool modified(const DeviceRequestedFeatures& requested_features)
@@ -137,7 +141,8 @@ public:
use_baking == requested_features.use_baking &&
use_subsurface == requested_features.use_subsurface &&
use_volume == requested_features.use_volume &&
- use_integrator_branched == requested_features.use_integrator_branched);
+ use_integrator_branched == requested_features.use_integrator_branched &&
+ use_patch_evaluation == requested_features.use_patch_evaluation);
}
/* Convert the requested features structure to a build options,
@@ -175,6 +180,9 @@ public:
if(!use_integrator_branched) {
build_options += " -D__NO_BRANCHED_PATH__";
}
+ if(!use_patch_evaluation) {
+ build_options += " -D__NO_PATCH_EVAL__";
+ }
return build_options;
}
};
diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp
index 6a511ea7316..76e52498b42 100644
--- a/intern/cycles/device/device_cuda.cpp
+++ b/intern/cycles/device/device_cuda.cpp
@@ -297,7 +297,7 @@ public:
cuda_error_message("CUDA nvcc compiler version could not be parsed.");
return false;
}
- if(cuda_version < 60) {
+ if(cuda_version < 75) {
printf("Unsupported CUDA version %d.%d detected, "
"you need CUDA 7.5 or newer.\n",
major, minor);
@@ -576,6 +576,7 @@ public:
case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break;
case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break;
case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break;
+ case TYPE_HALF: format = CU_AD_FORMAT_HALF; break;
default: assert(0); return;
}
@@ -747,8 +748,12 @@ public:
}
/* Resize once */
- if(flat_slot >= bindless_mapping.size())
- bindless_mapping.resize(4096); /*TODO(dingto): Make this a variable */
+ if(flat_slot >= bindless_mapping.size()) {
+ /* Allocate some slots in advance, to reduce amount
+ * of re-allocations.
+ */
+ bindless_mapping.resize(flat_slot + 128);
+ }
/* Set Mapping and tag that we need to (re-)upload to device */
bindless_mapping.get_data()[flat_slot] = (uint)tex;
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 f4d154ca19e..1bb93c7f922 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -166,6 +166,7 @@ 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
@@ -179,6 +180,7 @@ set(SRC_UTIL_HEADERS
../util/util_half.h
../util/util_math.h
../util/util_math_fast.h
+ ../util/util_static_assert.h
../util/util_transform.h
../util/util_texture.h
../util/util_types.h
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index 633a16ca8e5..ac4f52818c9 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -166,7 +166,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg,
return label;
}
-#ifndef __KERNEL_CUDS__
+#ifndef __KERNEL_CUDA__
ccl_device
#else
ccl_device_inline
diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h
index 493afdc4f62..3605394f182 100644
--- a/intern/cycles/kernel/geom/geom.h
+++ b/intern/cycles/kernel/geom/geom.h
@@ -17,6 +17,9 @@
#include "geom_attribute.h"
#include "geom_object.h"
+#ifdef __PATCH_EVAL__
+# include "geom_patch.h"
+#endif
#include "geom_triangle.h"
#include "geom_subd_triangle.h"
#include "geom_triangle_intersect.h"
diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h
index 5d78cf8f9fc..8604d30ad34 100644
--- a/intern/cycles/kernel/geom/geom_attribute.h
+++ b/intern/cycles/kernel/geom/geom_attribute.h
@@ -43,12 +43,19 @@ ccl_device_inline uint attribute_primitive_type(KernelGlobals *kg, const ShaderD
}
}
+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;
@@ -57,31 +64,37 @@ ccl_device_inline int find_attribute(KernelGlobals *kg, const ShaderData *sd, ui
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_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 b16f0c9a99b..4384c2093e9 100644
--- a/intern/cycles/kernel/geom/geom_primitive.h
+++ b/intern/cycles/kernel/geom/geom_primitive.h
@@ -25,24 +25,23 @@ CCL_NAMESPACE_BEGIN
ccl_device_inline float primitive_attribute_float(KernelGlobals *kg,
const ShaderData *sd,
- AttributeElement elem,
- int offset,
+ const AttributeDescriptor desc,
float *dx, float *dy)
{
if(ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE) {
if(subd_triangle_patch(kg, sd) == ~0)
- return triangle_attribute_float(kg, sd, elem, offset, dx, dy);
+ return triangle_attribute_float(kg, sd, desc, dx, dy);
else
- return subd_triangle_attribute_float(kg, sd, elem, offset, dx, dy);
+ 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 {
@@ -54,25 +53,23 @@ ccl_device_inline float primitive_attribute_float(KernelGlobals *kg,
ccl_device_inline float3 primitive_attribute_float3(KernelGlobals *kg,
const ShaderData *sd,
- AttributeElement elem,
- int offset,
- float3 *dx,
- float3 *dy)
+ const AttributeDescriptor desc,
+ float3 *dx, float3 *dy)
{
if(ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE) {
if(subd_triangle_patch(kg, sd) == ~0)
- return triangle_attribute_float3(kg, sd, elem, offset, dx, dy);
+ return triangle_attribute_float3(kg, sd, desc, dx, dy);
else
- return subd_triangle_attribute_float3(kg, sd, elem, offset, dx, dy);
+ 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 {
@@ -86,13 +83,12 @@ ccl_device_inline float3 primitive_attribute_float3(KernelGlobals *kg,
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;
}
@@ -102,15 +98,14 @@ ccl_device_inline 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;
@@ -132,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))));
@@ -173,19 +167,18 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData *
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
index bf9be182345..647840dc696 100644
--- a/intern/cycles/kernel/geom/geom_subd_triangle.h
+++ b/intern/cycles/kernel/geom/geom_subd_triangle.h
@@ -97,36 +97,81 @@ ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch,
/* Reading attributes on various subdivision triangle elements */
-ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy)
+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(elem == ATTR_ELEMENT_FACE) {
+#ifdef __PATCH_EVAL__
+ 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
+#endif /* __PATCH_EVAL__ */
+ if(desc.element == ATTR_ELEMENT_FACE) {
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
- return kernel_tex_fetch(__attributes_float, offset + subd_triangle_patch_face(kg, patch));
+ return kernel_tex_fetch(__attributes_float, desc.offset + subd_triangle_patch_face(kg, patch));
}
- else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) {
+ 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 a, b, c;
+ uint4 v = subd_triangle_patch_indices(kg, patch);
- float f0 = kernel_tex_fetch(__attributes_float, offset + v.x);
- float f1 = kernel_tex_fetch(__attributes_float, offset + v.y);
- float f2 = kernel_tex_fetch(__attributes_float, offset + v.z);
- float f3 = kernel_tex_fetch(__attributes_float, offset + v.w);
+ 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;
}
- a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
- b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
- c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+ 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;
@@ -135,28 +180,26 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
}
- else if(elem == ATTR_ELEMENT_CORNER) {
- int corners[4];
- subd_triangle_patch_corners(kg, patch, corners);
-
+ else if(desc.element == ATTR_ELEMENT_CORNER) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
- float a, b, c;
+ int corners[4];
+ subd_triangle_patch_corners(kg, patch, corners);
- float f0 = kernel_tex_fetch(__attributes_float, corners[0] + offset);
- float f1 = kernel_tex_fetch(__attributes_float, corners[1] + offset);
- float f2 = kernel_tex_fetch(__attributes_float, corners[2] + offset);
- float f3 = kernel_tex_fetch(__attributes_float, corners[3] + offset);
+ 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;
}
- a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
- b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
- c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+ 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;
@@ -173,36 +216,87 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa
}
}
-ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy)
+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(elem == ATTR_ELEMENT_FACE) {
+#ifdef __PATCH_EVAL__
+ 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
+#endif /* __PATCH_EVAL__ */
+ 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 + subd_triangle_patch_face(kg, patch)));
+ return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + subd_triangle_patch_face(kg, patch)));
}
- else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) {
+ 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 a, b, c;
+ uint4 v = subd_triangle_patch_indices(kg, patch);
- float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.x));
- float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.y));
- float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.z));
- float3 f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.w));
+ 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;
}
- a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
- b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
- c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+ 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;
@@ -211,27 +305,26 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader
return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c;
}
- else if(elem == ATTR_ELEMENT_CORNER || elem == ATTR_ELEMENT_CORNER_BYTE) {
- int corners[4];
- subd_triangle_patch_corners(kg, patch, corners);
-
+ else if(desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
- float3 a, b, c;
+ int corners[4];
+ subd_triangle_patch_corners(kg, patch, corners);
+
float3 f0, f1, f2, f3;
- if(elem == ATTR_ELEMENT_CORNER) {
- f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[0] + offset));
- f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[1] + offset));
- f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[2] + offset));
- f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[3] + offset));
+ 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] + offset));
- f1 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[1] + offset));
- f2 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[2] + offset));
- f3 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[3] + offset));
+ 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) {
@@ -239,9 +332,9 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader
f3 = (f3+f0)*0.5f;
}
- a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
- b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
- c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
+ 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;
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_volume.h b/intern/cycles/kernel/geom/geom_volume.h
index 7c8182bc430..efe540a8518 100644
--- a/intern/cycles/kernel/geom/geom_volume.h
+++ b/intern/cycles/kernel/geom/geom_volume.h
@@ -50,36 +50,35 @@ ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg,
{
/* 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;
@@ -88,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_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h
index c882b477c35..3775934f293 100644
--- a/intern/cycles/kernel/kernel_compat_cpu.h
+++ b/intern/cycles/kernel/kernel_compat_cpu.h
@@ -495,6 +495,7 @@ typedef texture<uint> texture_uint;
typedef texture<int> texture_int;
typedef texture<uint4> texture_uint4;
typedef texture<uchar4> texture_uchar4;
+typedef texture<uchar> texture_uchar;
typedef texture_image<float> texture_image_float;
typedef texture_image<uchar> texture_image_uchar;
typedef texture_image<half> texture_image_half;
diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/kernel_compat_cuda.h
index a039b414006..9a96cb9f438 100644
--- a/intern/cycles/kernel/kernel_compat_cuda.h
+++ b/intern/cycles/kernel/kernel_compat_cuda.h
@@ -31,6 +31,7 @@
#endif
#include <cuda.h>
+#include <cuda_fp16.h>
#include <float.h>
/* Qualifier wrappers for different names on different devices */
@@ -47,6 +48,7 @@
#define ccl_may_alias
#define ccl_addr_space
#define ccl_restrict __restrict__
+#define ccl_align(n) __align__(n)
/* No assert supported for CUDA */
@@ -65,6 +67,7 @@ typedef texture<float, 1> texture_float;
typedef texture<uint, 1> texture_uint;
typedef texture<int, 1> texture_int;
typedef texture<uint4, 1> texture_uint4;
+typedef texture<uchar, 1> texture_uchar;
typedef texture<uchar4, 1> texture_uchar4;
typedef texture<float4, 2> texture_image_float4;
typedef texture<float4, 3> texture_image3d_float4;
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_path.h b/intern/cycles/kernel/kernel_path.h
index 1f08f3459e6..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_inline 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_shader.h b/intern/cycles/kernel/kernel_shader.h
index 98d321c9c16..079bea30bdd 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -149,7 +149,7 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg,
/* ShaderData setup from BSSRDF scatter */
#ifdef __SUBSURFACE__
-# ifndef __KERNEL_CUDS__
+# ifndef __KERNEL_CUDA__
ccl_device
# else
ccl_device_inline
@@ -539,7 +539,7 @@ ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg,
#endif
-#ifndef __KERNEL_CUDS__
+#ifndef __KERNEL_CUDA__
ccl_device
#else
ccl_device_inline
diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h
index f404666177a..e83bfc3f08a 100644
--- a/intern/cycles/kernel/kernel_subsurface.h
+++ b/intern/cycles/kernel/kernel_subsurface.h
@@ -85,11 +85,16 @@ ccl_device ShaderClosure *subsurface_scatter_pick_closure(KernelGlobals *kg, Sha
return NULL;
}
-ccl_device_inline 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
@@ -235,7 +240,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_inline 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 7d6fec02331..8d5bb75a428 100644
--- a/intern/cycles/kernel/kernel_textures.h
+++ b/intern/cycles/kernel/kernel_textures.h
@@ -188,6 +188,8 @@ KERNEL_TEX(uint, texture_uint, __bindless_mapping)
/* packed image (opencl) */
KERNEL_TEX(uchar4, texture_uchar4, __tex_image_byte4_packed)
KERNEL_TEX(float4, texture_float4, __tex_image_float4_packed)
+KERNEL_TEX(uchar, texture_uchar, __tex_image_byte_packed)
+KERNEL_TEX(float, texture_float, __tex_image_float_packed)
KERNEL_TEX(uint4, texture_uint4, __tex_image_packed_info)
#undef KERNEL_TEX
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 18b5c35c768..e29940672ca 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -19,6 +19,7 @@
#include "kernel_math.h"
#include "svm/svm_types.h"
+#include "util_static_assert.h"
#ifndef __KERNEL_GPU__
# define __KERNEL_CPU__
@@ -34,7 +35,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
@@ -147,6 +148,7 @@ CCL_NAMESPACE_BEGIN
#define __CAMERA_CLIPPING__
#define __INTERSECTION_REFINE__
#define __CLAMP_SAMPLE__
+#define __PATCH_EVAL__
#ifdef __KERNEL_SHADING__
# define __SVM__
@@ -196,6 +198,9 @@ CCL_NAMESPACE_BEGIN
#ifdef __NO_BRANCHED_PATH__
# undef __BRANCHED_PATH__
#endif
+#ifdef __NO_PATCH_EVAL__
+# undef __PATCH_EVAL__
+#endif
/* Random Numbers */
@@ -624,6 +629,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__
@@ -644,23 +661,18 @@ typedef enum AttributeStandard {
* ShaderClosure has a fixed size, and any extra space must be allocated
* with closure_alloc_extra().
*
- * float3 is 12 bytes on CUDA and 16 bytes on CPU/OpenCL, we set the data
- * size to ensure ShaderClosure is 80 bytes total everywhere. */
+ * 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. */
#define SHADER_CLOSURE_BASE \
float3 weight; \
ClosureType type; \
float sample_weight \
-typedef ccl_addr_space struct ShaderClosure {
+typedef ccl_addr_space struct ccl_align(16) ShaderClosure {
SHADER_CLOSURE_BASE;
- /* pad to 80 bytes, data types are aligned to own size */
-#ifdef __KERNEL_CUDA__
- float data[15];
-#else
- float data[14];
-#endif
+ float data[14]; /* pad to 80 bytes */
} ShaderClosure;
/* Shader Context
@@ -735,7 +747,7 @@ enum ShaderDataFlag {
# 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_soa_member(type, name) type soa_##name;
+# 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
@@ -743,7 +755,7 @@ enum ShaderDataFlag {
# 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_soa_member(type, name) type soa_##name;
+# 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
@@ -979,6 +991,7 @@ typedef struct KernelCamera {
int pad;
} KernelCamera;
+static_assert_align(KernelCamera, 16);
typedef struct KernelFilm {
float exposure;
@@ -1033,6 +1046,7 @@ typedef struct KernelFilm {
int pass_pad3;
#endif
} KernelFilm;
+static_assert_align(KernelFilm, 16);
typedef struct KernelBackground {
/* only shader index */
@@ -1046,6 +1060,7 @@ typedef struct KernelBackground {
float ao_distance;
float ao_pad1, ao_pad2;
} KernelBackground;
+static_assert_align(KernelBackground, 16);
typedef struct KernelIntegrator {
/* emission */
@@ -1113,8 +1128,10 @@ typedef struct KernelIntegrator {
float volume_step_size;
int volume_samples;
- int pad;
+ int pad1;
+ int pad2;
} KernelIntegrator;
+static_assert_align(KernelIntegrator, 16);
typedef struct KernelBVH {
/* root node */
@@ -1126,6 +1143,7 @@ typedef struct KernelBVH {
int use_qbvh;
int pad1, pad2;
} KernelBVH;
+static_assert_align(KernelBVH, 16);
typedef enum CurveFlag {
/* runtime flags */
@@ -1145,11 +1163,13 @@ typedef struct KernelCurves {
float minimum_width;
float maximum_width;
} KernelCurves;
+static_assert_align(KernelCurves, 16);
typedef struct KernelTables {
int beckmann_offset;
int pad1, pad2, pad3;
} KernelTables;
+static_assert_align(KernelTables, 16);
typedef struct KernelData {
KernelCamera cam;
@@ -1160,8 +1180,12 @@ typedef struct KernelData {
KernelCurves curve;
KernelTables tables;
} KernelData;
+static_assert_align(KernelData, 16);
#ifdef __KERNEL_DEBUG__
+/* NOTE: This is a runtime-only struct, alignment is not
+ * really important here.
+ */
typedef ccl_addr_space struct DebugData {
// Total number of BVH node traversal steps and primitives intersections
// for the camera rays.
@@ -1239,6 +1263,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 (1u << 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/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h
index 47383140170..af68907a5c2 100644
--- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h
+++ b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h
@@ -25,12 +25,12 @@ ccl_device float4 kernel_tex_image_interp_impl(KernelGlobals *kg, int tex, float
{
if(tex >= TEX_START_HALF_CPU)
return kg->texture_half_images[tex - TEX_START_HALF_CPU].interp(x, y);
- else if(tex >= TEX_START_HALF4_CPU)
- return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp(x, y);
else if(tex >= TEX_START_BYTE_CPU)
return kg->texture_byte_images[tex - TEX_START_BYTE_CPU].interp(x, y);
else if(tex >= TEX_START_FLOAT_CPU)
return kg->texture_float_images[tex - TEX_START_FLOAT_CPU].interp(x, y);
+ else if(tex >= TEX_START_HALF4_CPU)
+ return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp(x, y);
else if(tex >= TEX_START_BYTE4_CPU)
return kg->texture_byte4_images[tex - TEX_START_BYTE4_CPU].interp(x, y);
else
@@ -41,12 +41,12 @@ ccl_device float4 kernel_tex_image_interp_3d_impl(KernelGlobals *kg, int tex, fl
{
if(tex >= TEX_START_HALF_CPU)
return kg->texture_half_images[tex - TEX_START_HALF_CPU].interp_3d(x, y, z);
- else if(tex >= TEX_START_HALF4_CPU)
- return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp_3d(x, y, z);
else if(tex >= TEX_START_BYTE_CPU)
return kg->texture_byte_images[tex - TEX_START_BYTE_CPU].interp_3d(x, y, z);
else if(tex >= TEX_START_FLOAT_CPU)
return kg->texture_float_images[tex - TEX_START_FLOAT_CPU].interp_3d(x, y, z);
+ else if(tex >= TEX_START_HALF4_CPU)
+ return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp_3d(x, y, z);
else if(tex >= TEX_START_BYTE4_CPU)
return kg->texture_byte4_images[tex - TEX_START_BYTE4_CPU].interp_3d(x, y, z);
else
@@ -57,13 +57,13 @@ ccl_device float4 kernel_tex_image_interp_3d_impl(KernelGlobals *kg, int tex, fl
ccl_device float4 kernel_tex_image_interp_3d_ex_impl(KernelGlobals *kg, int tex, float x, float y, float z, int interpolation)
{
if(tex >= TEX_START_HALF_CPU)
- return kg->texture_half4_images[tex - TEX_START_HALF_CPU].interp_3d_ex(x, y, z, interpolation);
- else if(tex >= TEX_START_HALF4_CPU)
- return kg->texture_half_images[tex - TEX_START_HALF4_CPU].interp_3d_ex(x, y, z, interpolation);
+ return kg->texture_half_images[tex - TEX_START_HALF_CPU].interp_3d_ex(x, y, z, interpolation);
else if(tex >= TEX_START_BYTE_CPU)
return kg->texture_byte_images[tex - TEX_START_BYTE_CPU].interp_3d_ex(x, y, z, interpolation);
else if(tex >= TEX_START_FLOAT_CPU)
return kg->texture_float_images[tex - TEX_START_FLOAT_CPU].interp_3d_ex(x, y, z, interpolation);
+ else if(tex >= TEX_START_HALF4_CPU)
+ return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp_3d_ex(x, y, z, interpolation);
else if(tex >= TEX_START_BYTE4_CPU)
return kg->texture_byte4_images[tex - TEX_START_BYTE4_CPU].interp_3d_ex(x, y, z, interpolation);
else
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 caae24405f1..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 {
@@ -815,7 +815,7 @@ bool OSLRenderServices::get_attribute(ShaderData *sd, bool derivatives, ustring
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 784e468635c..43a9e2f13aa 100644
--- a/intern/cycles/kernel/osl/osl_shader.cpp
+++ b/intern/cycles/kernel/osl/osl_shader.cpp
@@ -340,7 +340,7 @@ void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderConte
/* 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;
@@ -354,16 +354,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 a185b8b8c05..ad06dd6929d 100644
--- a/intern/cycles/kernel/osl/osl_shader.h
+++ b/intern/cycles/kernel/osl/osl_shader.h
@@ -59,7 +59,7 @@ public:
static void eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderContext ctx);
/* 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_rgb_curves.osl b/intern/cycles/kernel/shaders/node_rgb_curves.osl
index c8e7e4f175b..984b7d47e8f 100644
--- a/intern/cycles/kernel/shaders/node_rgb_curves.osl
+++ b/intern/cycles/kernel/shaders/node_rgb_curves.osl
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "stdosl.h"
#include "node_ramp_util.h"
shader node_rgb_curves(
diff --git a/intern/cycles/kernel/shaders/node_rgb_ramp.osl b/intern/cycles/kernel/shaders/node_rgb_ramp.osl
index 24b8728b999..4e7d8fdcf65 100644
--- a/intern/cycles/kernel/shaders/node_rgb_ramp.osl
+++ b/intern/cycles/kernel/shaders/node_rgb_ramp.osl
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "stdosl.h"
#include "node_ramp_util.h"
shader node_rgb_ramp(
diff --git a/intern/cycles/kernel/shaders/node_vector_curves.osl b/intern/cycles/kernel/shaders/node_vector_curves.osl
index d92fa11d439..ff284c48e0a 100644
--- a/intern/cycles/kernel/shaders/node_vector_curves.osl
+++ b/intern/cycles/kernel/shaders/node_vector_curves.osl
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "stdosl.h"
#include "node_ramp_util.h"
shader node_vector_curves(
diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h
index bd6013e9205..de978a423b4 100644
--- a/intern/cycles/kernel/svm/svm_attribute.h
+++ b/intern/cycles/kernel/svm/svm_attribute.h
@@ -18,117 +18,101 @@ 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;
- 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)) {
- *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));
}
}
}
-#ifndef __KERNEL_CUDS__
+#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));
}
}
}
-#ifndef __KERNEL_CUDS__
+#ifndef __KERNEL_CUDA__
ccl_device
#else
ccl_device_noinline
@@ -138,35 +122,32 @@ void svm_node_attr_bump_dy(KernelGlobals *kg,
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_image.h b/intern/cycles/kernel/svm/svm_image.h
index b6b90dfff81..5d02be1fa2f 100644
--- a/intern/cycles/kernel/svm/svm_image.h
+++ b/intern/cycles/kernel/svm/svm_image.h
@@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN
/* Float4 textures on various devices. */
#if defined(__KERNEL_CPU__)
-# define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_CPU
+# define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_CPU
#elif defined(__KERNEL_CUDA__)
# if __CUDA_ARCH__ < 300
# define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_CUDA
@@ -36,13 +36,26 @@ CCL_NAMESPACE_BEGIN
ccl_device_inline float4 svm_image_texture_read(KernelGlobals *kg, int id, int offset)
{
- if(id >= TEX_NUM_FLOAT4_IMAGES) {
+ /* Float4 */
+ if(id < TEX_START_BYTE4_OPENCL) {
+ return kernel_tex_fetch(__tex_image_float4_packed, offset);
+ }
+ /* Byte4 */
+ else if(id < TEX_START_FLOAT_OPENCL) {
uchar4 r = kernel_tex_fetch(__tex_image_byte4_packed, offset);
float f = 1.0f/255.0f;
return make_float4(r.x*f, r.y*f, r.z*f, r.w*f);
}
+ /* Float */
+ else if(id < TEX_START_BYTE_OPENCL) {
+ float f = kernel_tex_fetch(__tex_image_float_packed, offset);
+ return make_float4(f, f, f, 1.0f);
+ }
+ /* Byte */
else {
- return kernel_tex_fetch(__tex_image_float4_packed, offset);
+ uchar r = kernel_tex_fetch(__tex_image_byte_packed, offset);
+ float f = r * (1.0f/255.0f);
+ return make_float4(f, f, f, 1.0f);
}
}
@@ -277,8 +290,10 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
}
# else
CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id);
- if(id < 2048) /* TODO(dingto): Make this a variable */
+ /* float4, byte4 and half4 */
+ if(id < TEX_START_FLOAT_CUDA_KEPLER)
r = kernel_tex_image_interp_float4(tex, x, y);
+ /* float, byte and half */
else {
float f = kernel_tex_image_interp_float(tex, x, y);
r = make_float4(f, f, f, 1.0);
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_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h
index b39d6a3e009..01dede3fff5 100644
--- a/intern/cycles/kernel/svm/svm_tex_coord.h
+++ b/intern/cycles/kernel/svm/svm_tex_coord.h
@@ -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/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index e8ff81fe08e..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 ||
@@ -61,6 +62,11 @@ void Attribute::resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only)
}
}
+void Attribute::resize(size_t num_elements)
+{
+ buffer.resize(num_elements * data_sizeof(), 0);
+}
+
void Attribute::add(const float& f)
{
char *data = (char*)&f;
@@ -130,6 +136,10 @@ size_t Attribute::data_sizeof() 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) {
@@ -517,16 +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_element = ATTR_ELEMENT_NONE;
- subd_offset = 0;
+ subd_desc.element = ATTR_ELEMENT_NONE;
+ subd_desc.offset = 0;
+ subd_desc.type = NODE_ATTR_FLOAT;
}
AttributeRequest::AttributeRequest(AttributeStandard std_)
@@ -535,16 +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_element = ATTR_ELEMENT_NONE;
- subd_offset = 0;
+ 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 e51bdf28d66..f4538c76369 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -54,11 +54,13 @@ public:
TypeDesc type;
vector<char> buffer;
AttributeElement element;
+ uint flags; /* enum AttributeFlag */
Attribute() {}
~Attribute();
void set(ustring name, TypeDesc type, AttributeElement element);
void resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only);
+ void resize(size_t num_elements);
size_t data_sizeof() const;
size_t element_size(Mesh *mesh, AttributePrimitive prim) const;
@@ -135,8 +137,7 @@ public:
/* temporary variables used by MeshManager */
TypeDesc triangle_type, curve_type, subd_type;
- AttributeElement triangle_element, curve_element, subd_element;
- int triangle_offset, curve_offset, subd_offset;
+ AttributeDescriptor triangle_desc, curve_desc, subd_desc;
explicit AttributeRequest(ustring name_);
explicit AttributeRequest(AttributeStandard std);
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 614620c14af..24543601ef9 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -52,15 +52,15 @@ ImageManager::ImageManager(const DeviceInfo& info)
{ \
tex_num_images[IMAGE_DATA_TYPE_FLOAT4] = TEX_NUM_FLOAT4_ ## ARCH; \
tex_num_images[IMAGE_DATA_TYPE_BYTE4] = TEX_NUM_BYTE4_ ## ARCH; \
+ tex_num_images[IMAGE_DATA_TYPE_HALF4] = TEX_NUM_HALF4_ ## ARCH; \
tex_num_images[IMAGE_DATA_TYPE_FLOAT] = TEX_NUM_FLOAT_ ## ARCH; \
tex_num_images[IMAGE_DATA_TYPE_BYTE] = TEX_NUM_BYTE_ ## ARCH; \
- tex_num_images[IMAGE_DATA_TYPE_HALF4] = TEX_NUM_HALF4_ ## ARCH; \
tex_num_images[IMAGE_DATA_TYPE_HALF] = TEX_NUM_HALF_ ## ARCH; \
tex_start_images[IMAGE_DATA_TYPE_FLOAT4] = TEX_START_FLOAT4_ ## ARCH; \
tex_start_images[IMAGE_DATA_TYPE_BYTE4] = TEX_START_BYTE4_ ## ARCH; \
+ tex_start_images[IMAGE_DATA_TYPE_HALF4] = TEX_START_HALF4_ ## ARCH; \
tex_start_images[IMAGE_DATA_TYPE_FLOAT] = TEX_START_FLOAT_ ## ARCH; \
tex_start_images[IMAGE_DATA_TYPE_BYTE] = TEX_START_BYTE_ ## ARCH; \
- tex_start_images[IMAGE_DATA_TYPE_HALF4] = TEX_START_HALF4_ ## ARCH; \
tex_start_images[IMAGE_DATA_TYPE_HALF] = TEX_START_HALF_ ## ARCH; \
}
@@ -82,15 +82,15 @@ ImageManager::ImageManager(const DeviceInfo& info)
/* Should not happen. */
tex_num_images[IMAGE_DATA_TYPE_FLOAT4] = 0;
tex_num_images[IMAGE_DATA_TYPE_BYTE4] = 0;
+ tex_num_images[IMAGE_DATA_TYPE_HALF4] = 0;
tex_num_images[IMAGE_DATA_TYPE_FLOAT] = 0;
tex_num_images[IMAGE_DATA_TYPE_BYTE] = 0;
- tex_num_images[IMAGE_DATA_TYPE_HALF4] = 0;
tex_num_images[IMAGE_DATA_TYPE_HALF] = 0;
tex_start_images[IMAGE_DATA_TYPE_FLOAT4] = 0;
tex_start_images[IMAGE_DATA_TYPE_BYTE4] = 0;
+ tex_start_images[IMAGE_DATA_TYPE_HALF4] = 0;
tex_start_images[IMAGE_DATA_TYPE_FLOAT] = 0;
tex_start_images[IMAGE_DATA_TYPE_BYTE] = 0;
- tex_start_images[IMAGE_DATA_TYPE_HALF4] = 0;
tex_start_images[IMAGE_DATA_TYPE_HALF] = 0;
assert(0);
}
@@ -216,7 +216,7 @@ ImageManager::ImageDataType ImageManager::get_image_metadata(const string& filen
}
/* We use a consecutive slot counting scheme on the devices, in order
- * float4, byte4, float, byte.
+ * float4, byte4, half4, float, byte, half.
* These functions convert the slot ids from ImageManager "images" ones
* to device ones and vice versa. */
int ImageManager::type_index_to_flattened_slot(int slot, ImageDataType type)
@@ -284,7 +284,7 @@ int ImageManager::add_image(const string& filename,
if(type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_FLOAT4)
is_float = true;
- /* No single channel and half textures on CUDA (Fermi) and OpenCL, use available slots */
+ /* No single channel and half textures on CUDA (Fermi) and no half on OpenCL, use available slots */
if((type == IMAGE_DATA_TYPE_FLOAT ||
type == IMAGE_DATA_TYPE_HALF4 ||
type == IMAGE_DATA_TYPE_HALF) &&
@@ -1105,10 +1105,11 @@ void ImageManager::device_pack_images(Device *device,
size_t size = 0, offset = 0;
ImageDataType type;
- int info_size = tex_num_images[IMAGE_DATA_TYPE_FLOAT4] + tex_num_images[IMAGE_DATA_TYPE_BYTE4];
+ int info_size = tex_num_images[IMAGE_DATA_TYPE_FLOAT4] + tex_num_images[IMAGE_DATA_TYPE_BYTE4]
+ + tex_num_images[IMAGE_DATA_TYPE_FLOAT] + tex_num_images[IMAGE_DATA_TYPE_BYTE];
uint4 *info = dscene->tex_image_packed_info.resize(info_size);
- /* Byte Textures*/
+ /* Byte4 Textures*/
type = IMAGE_DATA_TYPE_BYTE4;
for(size_t slot = 0; slot < images[type].size(); slot++) {
@@ -1119,7 +1120,7 @@ void ImageManager::device_pack_images(Device *device,
size += tex_img.size();
}
- uchar4 *pixels_byte = dscene->tex_image_byte4_packed.resize(size);
+ uchar4 *pixels_byte4 = dscene->tex_image_byte4_packed.resize(size);
for(size_t slot = 0; slot < images[type].size(); slot++) {
if(!images[type][slot])
@@ -1131,11 +1132,11 @@ void ImageManager::device_pack_images(Device *device,
info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options);
- memcpy(pixels_byte+offset, (void*)tex_img.data_pointer, tex_img.memory_size());
+ memcpy(pixels_byte4+offset, (void*)tex_img.data_pointer, tex_img.memory_size());
offset += tex_img.size();
}
- /* Float Textures*/
+ /* Float4 Textures*/
type = IMAGE_DATA_TYPE_FLOAT4;
size = 0, offset = 0;
@@ -1147,7 +1148,7 @@ void ImageManager::device_pack_images(Device *device,
size += tex_img.size();
}
- float4 *pixels_float = dscene->tex_image_float4_packed.resize(size);
+ float4 *pixels_float4 = dscene->tex_image_float4_packed.resize(size);
for(size_t slot = 0; slot < images[type].size(); slot++) {
if(!images[type][slot])
@@ -1160,6 +1161,63 @@ void ImageManager::device_pack_images(Device *device,
uint8_t options = pack_image_options(type, slot);
info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options);
+ memcpy(pixels_float4+offset, (void*)tex_img.data_pointer, tex_img.memory_size());
+ offset += tex_img.size();
+ }
+
+ /* Byte Textures*/
+ type = IMAGE_DATA_TYPE_BYTE;
+ size = 0, offset = 0;
+
+ for(size_t slot = 0; slot < images[type].size(); slot++) {
+ if(!images[type][slot])
+ continue;
+
+ device_vector<uchar>& tex_img = dscene->tex_byte_image[slot];
+ size += tex_img.size();
+ }
+
+ uchar *pixels_byte = dscene->tex_image_byte_packed.resize(size);
+
+ for(size_t slot = 0; slot < images[type].size(); slot++) {
+ if(!images[type][slot])
+ continue;
+
+ device_vector<uchar>& tex_img = dscene->tex_byte_image[slot];
+
+ uint8_t options = pack_image_options(type, slot);
+
+ info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options);
+
+ memcpy(pixels_byte+offset, (void*)tex_img.data_pointer, tex_img.memory_size());
+ offset += tex_img.size();
+ }
+
+ /* Float Textures*/
+ type = IMAGE_DATA_TYPE_FLOAT;
+ size = 0, offset = 0;
+
+ for(size_t slot = 0; slot < images[type].size(); slot++) {
+ if(!images[type][slot])
+ continue;
+
+ device_vector<float>& tex_img = dscene->tex_float_image[slot];
+ size += tex_img.size();
+ }
+
+ float *pixels_float = dscene->tex_image_float_packed.resize(size);
+
+ for(size_t slot = 0; slot < images[type].size(); slot++) {
+ if(!images[type][slot])
+ continue;
+
+ device_vector<float>& tex_img = dscene->tex_float_image[slot];
+
+ /* todo: support 3D textures, only CPU for now */
+
+ uint8_t options = pack_image_options(type, slot);
+ info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options);
+
memcpy(pixels_float+offset, (void*)tex_img.data_pointer, tex_img.memory_size());
offset += tex_img.size();
}
@@ -1178,6 +1236,20 @@ void ImageManager::device_pack_images(Device *device,
}
device->tex_alloc("__tex_image_float4_packed", dscene->tex_image_float4_packed);
}
+ if(dscene->tex_image_byte_packed.size()) {
+ if(dscene->tex_image_byte_packed.device_pointer) {
+ thread_scoped_lock device_lock(device_mutex);
+ device->tex_free(dscene->tex_image_byte_packed);
+ }
+ device->tex_alloc("__tex_image_byte_packed", dscene->tex_image_byte_packed);
+ }
+ if(dscene->tex_image_float_packed.size()) {
+ if(dscene->tex_image_float_packed.device_pointer) {
+ thread_scoped_lock device_lock(device_mutex);
+ device->tex_free(dscene->tex_image_float_packed);
+ }
+ device->tex_alloc("__tex_image_float_packed", dscene->tex_image_float_packed);
+ }
if(dscene->tex_image_packed_info.size()) {
if(dscene->tex_image_packed_info.device_pointer) {
thread_scoped_lock device_lock(device_mutex);
@@ -1208,10 +1280,14 @@ void ImageManager::device_free(Device *device, DeviceScene *dscene)
device->tex_free(dscene->tex_image_byte4_packed);
device->tex_free(dscene->tex_image_float4_packed);
+ device->tex_free(dscene->tex_image_byte_packed);
+ device->tex_free(dscene->tex_image_float_packed);
device->tex_free(dscene->tex_image_packed_info);
dscene->tex_image_byte4_packed.clear();
dscene->tex_image_float4_packed.clear();
+ dscene->tex_image_byte_packed.clear();
+ dscene->tex_image_float_packed.clear();
dscene->tex_image_packed_info.clear();
}
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 07998684b23..cca71a6bb93 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -39,9 +39,9 @@ public:
enum ImageDataType {
IMAGE_DATA_TYPE_FLOAT4 = 0,
IMAGE_DATA_TYPE_BYTE4 = 1,
- IMAGE_DATA_TYPE_FLOAT = 2,
- IMAGE_DATA_TYPE_BYTE = 3,
- IMAGE_DATA_TYPE_HALF4 = 4,
+ IMAGE_DATA_TYPE_HALF4 = 2,
+ IMAGE_DATA_TYPE_FLOAT = 3,
+ IMAGE_DATA_TYPE_BYTE = 4,
IMAGE_DATA_TYPE_HALF = 5,
IMAGE_DATA_NUM_TYPES
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 4cf0a785897..fcf4e69984d 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -30,6 +30,9 @@
#include "osl_globals.h"
+#include "subd_split.h"
+#include "subd_patch_table.h"
+
#include "util_foreach.h"
#include "util_logging.h"
#include "util_progress.h"
@@ -112,19 +115,12 @@ float3 Mesh::SubdFace::normal(const Mesh *mesh) const
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);
@@ -177,11 +173,16 @@ Mesh::Mesh()
num_ngons = 0;
subdivision_type = SUBDIVISION_NONE;
+ subd_params = NULL;
+
+ patch_table = NULL;
}
Mesh::~Mesh()
{
delete bvh;
+ delete patch_table;
+ delete subd_params;
}
void Mesh::resize_mesh(int numverts, int numtris)
@@ -274,6 +275,8 @@ void Mesh::clear()
num_subd_verts = 0;
+ subd_creases.clear();
+
attributes.clear();
curve_attributes.clear();
subd_attributes.clear();
@@ -283,6 +286,9 @@ void Mesh::clear()
transform_negative_scaled = false;
transform_normal = transform_identity();
geometry_flags = GEOMETRY_NONE;
+
+ delete patch_table;
+ patch_table = NULL;
}
int Mesh::split_vertex(int vertex)
@@ -705,7 +711,6 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
-
void Mesh::compute_bvh(DeviceScene *dscene,
SceneParams *params,
Progress *progress,
@@ -779,6 +784,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;
@@ -831,9 +847,10 @@ 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_PRIM_TRIANGLE][attr.name()] = osl_attr;
og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_CURVE][attr.name()] = osl_attr;
@@ -853,9 +870,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;
@@ -875,9 +891,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
}
}
- 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;
@@ -897,9 +912,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att
}
}
- if(req.subd_element != ATTR_ELEMENT_NONE) {
- osl_attr.elem = req.subd_element;
- osl_attr.offset = req.subd_offset;
+ 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;
@@ -971,8 +985,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;
@@ -980,14 +994,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;
@@ -995,14 +1011,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.curve_desc.flags << 8;
}
index++;
if(mesh->subd_faces.size()) {
attr_map[index].x = id;
- attr_map[index].y = req.subd_element;
- attr_map[index].z = as_uint(req.subd_offset);
+ 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;
@@ -1010,6 +1028,8 @@ 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.subd_desc.flags << 8;
}
index++;
@@ -1069,17 +1089,20 @@ static void update_attribute_element_offset(Mesh *mesh,
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, prim);
+ AttributeElement& element = desc.element;
+ int& offset = desc.offset;
+
if(mattr->element == ATTR_ELEMENT_VOXEL) {
/* store slot in offset value */
VoxelAttribute *voxel_data = mattr->data_voxel();
@@ -1128,7 +1151,11 @@ 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;
@@ -1153,8 +1180,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;
}
}
@@ -1243,8 +1270,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
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,
@@ -1253,8 +1279,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
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,
@@ -1263,8 +1288,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene,
subd_mattr,
ATTR_PRIM_SUBD,
req.subd_type,
- req.subd_offset,
- req.subd_element);
+ req.subd_desc);
if(progress.get_cancel()) return;
}
@@ -1327,6 +1351,12 @@ void MeshManager::mesh_calc_offset(Scene *scene)
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();
@@ -1358,6 +1388,12 @@ void MeshManager::device_update_mesh(Device *device,
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();
+ }
}
}
@@ -1440,6 +1476,11 @@ void MeshManager::device_update_mesh(Device *device,
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;
}
@@ -1621,12 +1662,48 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
}
}
+ /* Tessellate meshes that are using subdivision */
+ size_t total_tess_needed = 0;
+ foreach(Mesh *mesh, scene->meshes) {
+ if(mesh->need_update &&
+ mesh->subdivision_type != Mesh::SUBDIVISION_NONE &&
+ mesh->num_subd_verts == 0 &&
+ mesh->subd_params)
+ {
+ total_tess_needed++;
+ }
+ }
+
+ size_t i = 0;
+ foreach(Mesh *mesh, scene->meshes) {
+ if(mesh->need_update &&
+ mesh->subdivision_type != Mesh::SUBDIVISION_NONE &&
+ mesh->num_subd_verts == 0 &&
+ mesh->subd_params)
+ {
+ string msg = "Tessellating ";
+ if(mesh->name == "")
+ msg += string_printf("%u/%u", (uint)(i+1), (uint)total_tess_needed);
+ else
+ msg += string_printf("%s %u/%u", mesh->name.c_str(), (uint)(i+1), (uint)total_tess_needed);
+
+ progress.set_status("Updating Mesh", msg);
+
+ DiagSplit dsplit(*mesh->subd_params);
+ mesh->tessellate(&dsplit);
+
+ i++;
+
+ if(progress.get_cancel()) return;
+ }
+ }
+
/* Update images needed for true displacement. */
bool true_displacement_used = false;
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;
@@ -1652,6 +1729,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;
@@ -1677,7 +1758,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
}
/* Update bvh. */
- size_t i = 0, num_bvh = 0;
+ size_t num_bvh = 0;
foreach(Mesh *mesh, scene->meshes) {
if(mesh->need_update && mesh->need_build_bvh()) {
num_bvh++;
@@ -1686,6 +1767,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
TaskPool pool;
+ i = 0;
foreach(Mesh *mesh, scene->meshes) {
if(mesh->need_update) {
pool.push(function_bind(&Mesh::compute_bvh,
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index c9ae9aab888..a77e296ea4a 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -39,7 +39,9 @@ class Progress;
class Scene;
class SceneParams;
class AttributeRequest;
+struct SubdParams;
class DiagSplit;
+struct PackedPatchTable;
/* Mesh */
@@ -110,13 +112,9 @@ public:
int num_ptex_faces() const { return num_corners == 4 ? 1 : num_corners; }
};
- /* Displacement */
- enum DisplacementMethod {
- DISPLACE_BUMP = 0,
- DISPLACE_TRUE = 1,
- DISPLACE_BOTH = 2,
-
- DISPLACE_NUM_METHODS,
+ struct SubdEdgeCrease {
+ int v[2];
+ float crease;
};
enum SubdivisionType {
@@ -157,6 +155,10 @@ public:
array<int> subd_face_corners;
int num_ngons;
+ array<SubdEdgeCrease> subd_creases;
+
+ SubdParams *subd_params;
+
vector<Shader*> used_shaders;
AttributeSet attributes;
AttributeSet curve_attributes;
@@ -166,7 +168,8 @@ public:
bool transform_applied;
bool transform_negative_scaled;
Transform transform_normal;
- DisplacementMethod displacement_method;
+
+ PackedPatchTable *patch_table;
uint motion_steps;
bool use_motion_blur;
@@ -184,6 +187,7 @@ public:
size_t curvekey_offset;
size_t patch_offset;
+ size_t patch_table_offset;
size_t face_offset;
size_t corner_offset;
@@ -234,6 +238,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
index fe8e41e8d35..efb40efbb79 100644
--- a/intern/cycles/render/mesh_subdivision.cpp
+++ b/intern/cycles/render/mesh_subdivision.cpp
@@ -19,13 +19,302 @@
#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);
@@ -36,113 +325,158 @@ void Mesh::tessellate(DiagSplit *split)
if(face.is_quad()) {
/* quad */
- LinearQuadPatch patch;
- float3 *hull = patch.hull;
- float3 *normals = patch.normals;
+ QuadDice::SubPatch subpatch;
- patch.patch_index = face.ptex_offset;
- patch.shader = face.shader;
+ LinearQuadPatch quad_patch;
+#ifdef WITH_OPENSUBDIV
+ OsdPatch osd_patch(&osd_data);
- for(int i = 0; i < 4; i++) {
- hull[i] = verts[subd_face_corners[face.start_corner+i]];
+ 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;
- if(face.smooth) {
for(int i = 0; i < 4; i++) {
- normals[i] = vN[subd_face_corners[face.start_corner+i]];
+ hull[i] = verts[subd_face_corners[face.start_corner+i]];
}
- }
- else {
- float3 N = face.normal(this);
- for(int i = 0; i < 4; i++) {
- normals[i] = N;
+
+ 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;
}
- swap(hull[2], hull[3]);
- swap(normals[2], normals[3]);
+ 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.
*/
- QuadDice::SubPatch subpatch;
- subpatch.patch = &patch;
-
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(&patch, &subpatch);
+ 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(&patch, &subpatch);
+ 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(&patch, &subpatch);
+ 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(&patch, &subpatch);
+ split->split_quad(subpatch.patch, &subpatch);
}
else {
/* ngon */
- float3 center_vert = make_float3(0.0f, 0.0f, 0.0f);
- float3 center_normal = make_float3(0.0f, 0.0f, 0.0f);
+#ifdef WITH_OPENSUBDIV
+ if(subdivision_type == SUBDIVISION_CATMULL_CLARK) {
+ OsdPatch patch(&osd_data);
+
+ patch.shader = face.shader;
- 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++) {
+ 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;
+ 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.patch_index = face.ptex_offset + corner;
- patch.shader = face.shader;
+ 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[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;
+ 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;
+ 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;
+ 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);
+ 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;
@@ -218,6 +552,15 @@ void Mesh::tessellate(DiagSplit *split)
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/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 9e72f197cce..8fec171b6fb 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -123,6 +123,8 @@ public:
/* opencl images */
device_vector<uchar4> tex_image_byte4_packed;
device_vector<float4> tex_image_float4_packed;
+ device_vector<uchar> tex_image_byte_packed;
+ device_vector<float> tex_image_float_packed;
device_vector<uint4> tex_image_packed_info;
KernelData data;
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 1cd76ff2b39..9d8c9fed7af 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -635,6 +635,11 @@ DeviceRequestedFeatures Session::get_requested_device_features()
}
requested_features.use_object_motion |= object->use_motion | mesh->use_motion_blur;
requested_features.use_camera_motion |= mesh->use_motion_blur;
+#ifdef WITH_OPENSUBDIV
+ if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
+ requested_features.use_patch_evaluation = true;
+ }
+#endif
}
BakeManager *bake_manager = scene->bake_manager;
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/subd/CMakeLists.txt b/intern/cycles/subd/CMakeLists.txt
index db497013693..dafb807bdf3 100644
--- a/intern/cycles/subd/CMakeLists.txt
+++ b/intern/cycles/subd/CMakeLists.txt
@@ -16,18 +16,16 @@ set(SRC
subd_dice.cpp
subd_patch.cpp
subd_split.cpp
+ subd_patch_table.cpp
)
set(SRC_HEADERS
subd_dice.h
subd_patch.h
+ subd_patch_table.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_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/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt
index 80fe893826a..9af777fb9dd 100644
--- a/intern/cycles/test/CMakeLists.txt
+++ b/intern/cycles/test/CMakeLists.txt
@@ -26,6 +26,7 @@ set(ALL_CYCLES_LIBRARIES
cycles_device
cycles_bvh
cycles_graph
+ cycles_subd
cycles_util
${OPENIMAGEIO_LIBRARIES}
)
@@ -41,6 +42,16 @@ if(WITH_IMAGE_OPENJPEG AND NOT WITH_SYSTEM_OPENJPEG)
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}
)
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index e6140b3ed09..f5674bdc15c 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -25,10 +25,6 @@ set(SRC
util_windows.cpp
)
-if(NOT CYCLES_STANDALONE_REPOSITORY)
- add_definitions(-DWITH_GLEW_MX)
-endif()
-
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
list(APPEND SRC
util_view.cpp
@@ -71,6 +67,7 @@ set(SRC_HEADERS
util_ssef.h
util_ssei.h
util_stack_allocator.h
+ util_static_assert.h
util_stats.h
util_string.h
util_system.h
diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h
index 1787ff648ee..73fd228b5d9 100644
--- a/intern/cycles/util/util_debug.h
+++ b/intern/cycles/util/util_debug.h
@@ -20,6 +20,8 @@
#include <cassert>
#include <iostream>
+#include "util_static_assert.h"
+
CCL_NAMESPACE_BEGIN
/* Global storage for all sort of flags used to fine-tune behavior of particular
diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h
index ae85ab3a915..5db3384cda4 100644
--- a/intern/cycles/util/util_half.h
+++ b/intern/cycles/util/util_half.h
@@ -33,17 +33,21 @@ CCL_NAMESPACE_BEGIN
#else
+/* CUDA has its own half data type, no need to define then */
+#ifndef __KERNEL_CUDA__
typedef unsigned short half;
+#endif
+
struct half4 { half x, y, z, w; };
#ifdef __KERNEL_CUDA__
ccl_device_inline void float4_store_half(half *h, float4 f, float scale)
{
- h[0] = __float2half_rn(f.x * scale);
- h[1] = __float2half_rn(f.y * scale);
- h[2] = __float2half_rn(f.z * scale);
- h[3] = __float2half_rn(f.w * scale);
+ h[0] = __float2half(f.x * scale);
+ h[1] = __float2half(f.y * scale);
+ h[2] = __float2half(f.z * scale);
+ h[3] = __float2half(f.w * scale);
}
#else
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index 13aba0646d2..89a882d9b9d 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -572,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)
diff --git a/intern/cycles/util/util_static_assert.h b/intern/cycles/util/util_static_assert.h
new file mode 100644
index 00000000000..1b945705145
--- /dev/null
+++ b/intern/cycles/util/util_static_assert.h
@@ -0,0 +1,64 @@
+/*
+ * 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 __UTIL_STATIC_ASSERT_H__
+#define __UTIL_STATIC_ASSERT_H__
+
+CCL_NAMESPACE_BEGIN
+
+/* TODO(sergey): In theory CUDA might work with own static assert
+ * implementation since it's just pure C++.
+ */
+#ifndef __KERNEL_GPU__
+# if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
+/* C++11 has built-in static_assert() */
+# else /* C++11 or MSVC2015 */
+template <bool Test> class StaticAssertFailure;
+template <> class StaticAssertFailure<true> {};
+# define _static_assert_private_glue_impl(A, B) A ## B
+# define _static_assert_glue(A, B) _static_assert_private_glue_impl(A, B)
+# ifdef __COUNTER__
+# define static_assert(condition, message) \
+ enum {_static_assert_glue(q_static_assert_result, __COUNTER__) = sizeof(StaticAssertFailure<!!(condition)>)} // NOLINT
+# else /* __COUNTER__ */
+# define static_assert(condition, message) \
+ enum {_static_assert_glue(q_static_assert_result, __LINE__) = sizeof(StaticAssertFailure<!!(condition)>)} // NOLINT
+# endif /* __COUNTER__ */
+# endif /* C++11 or MSVC2015 */
+#else /* __KERNEL_GPU__ */
+# define static_assert(statement, message)
+#endif /* __KERNEL_GPU__ */
+
+/* TODO(sergey): For until C++11 is a bare minimum for us,
+ * we do a bit of a trickery to show meaningful message so
+ * it's more or less clear what's wrong when building without
+ * C++11.
+ *
+ * The thing here is: our non-C++11 implementation doesn't
+ * have a way to print any message after preprocessor
+ * substitution so we rely on the message which is passed to
+ * static_assert() since that's the only message visible when
+ * compilation fails.
+ *
+ * After C++11 bump it should be possible to glue structure
+ * name to the error message,
+ */
+# define static_assert_align(st, align) \
+ static_assert((sizeof(st) % (align) == 0), "Structure must be strictly aligned") // NOLINT
+
+CCL_NAMESPACE_END
+
+#endif /* __UTIL_STATIC_ASSERT_H__ */
diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h
index 2ef47283029..aff928ea2ee 100644
--- a/intern/cycles/util/util_texture.h
+++ b/intern/cycles/util/util_texture.h
@@ -24,58 +24,58 @@ CCL_NAMESPACE_BEGIN
/* CPU */
#define TEX_NUM_FLOAT4_CPU 1024
#define TEX_NUM_BYTE4_CPU 1024
+#define TEX_NUM_HALF4_CPU 1024
#define TEX_NUM_FLOAT_CPU 1024
#define TEX_NUM_BYTE_CPU 1024
-#define TEX_NUM_HALF4_CPU 1024
#define TEX_NUM_HALF_CPU 1024
#define TEX_START_FLOAT4_CPU 0
#define TEX_START_BYTE4_CPU TEX_NUM_FLOAT4_CPU
-#define TEX_START_FLOAT_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU)
-#define TEX_START_BYTE_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_FLOAT_CPU)
-#define TEX_START_HALF4_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_FLOAT_CPU + TEX_NUM_BYTE_CPU)
-#define TEX_START_HALF_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_FLOAT_CPU + TEX_NUM_BYTE_CPU + TEX_NUM_HALF4_CPU)
+#define TEX_START_HALF4_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU)
+#define TEX_START_FLOAT_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_HALF4_CPU)
+#define TEX_START_BYTE_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_HALF4_CPU + TEX_NUM_FLOAT_CPU)
+#define TEX_START_HALF_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_HALF4_CPU + TEX_NUM_FLOAT_CPU + TEX_NUM_BYTE_CPU)
/* CUDA (Geforce 4xx and 5xx) */
#define TEX_NUM_FLOAT4_CUDA 5
-#define TEX_NUM_BYTE4_CUDA 88
+#define TEX_NUM_BYTE4_CUDA 85
+#define TEX_NUM_HALF4_CUDA 0
#define TEX_NUM_FLOAT_CUDA 0
#define TEX_NUM_BYTE_CUDA 0
-#define TEX_NUM_HALF4_CUDA 0
#define TEX_NUM_HALF_CUDA 0
#define TEX_START_FLOAT4_CUDA 0
#define TEX_START_BYTE4_CUDA TEX_NUM_FLOAT4_CUDA
-#define TEX_START_FLOAT_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA)
-#define TEX_START_BYTE_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_FLOAT_CUDA)
-#define TEX_START_HALF4_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_FLOAT_CUDA + TEX_NUM_BYTE_CUDA)
-#define TEX_START_HALF_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_FLOAT_CUDA + TEX_NUM_BYTE_CUDA + TEX_NUM_HALF4_CUDA)
+#define TEX_START_HALF4_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA)
+#define TEX_START_FLOAT_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_HALF4_CUDA)
+#define TEX_START_BYTE_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_HALF4_CUDA + TEX_NUM_FLOAT_CUDA)
+#define TEX_START_HALF_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_HALF4_CUDA + TEX_NUM_FLOAT_CUDA + TEX_NUM_BYTE_CUDA)
/* CUDA (Kepler, Geforce 6xx and above) */
#define TEX_NUM_FLOAT4_CUDA_KEPLER 1024
#define TEX_NUM_BYTE4_CUDA_KEPLER 1024
+#define TEX_NUM_HALF4_CUDA_KEPLER 1024
#define TEX_NUM_FLOAT_CUDA_KEPLER 1024
#define TEX_NUM_BYTE_CUDA_KEPLER 1024
-#define TEX_NUM_HALF4_CUDA_KEPLER 0
-#define TEX_NUM_HALF_CUDA_KEPLER 0
+#define TEX_NUM_HALF_CUDA_KEPLER 1024
#define TEX_START_FLOAT4_CUDA_KEPLER 0
#define TEX_START_BYTE4_CUDA_KEPLER TEX_NUM_FLOAT4_CUDA_KEPLER
-#define TEX_START_FLOAT_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER)
-#define TEX_START_BYTE_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER)
-#define TEX_START_HALF4_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER + TEX_NUM_BYTE_CUDA_KEPLER)
-#define TEX_START_HALF_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER + TEX_NUM_BYTE_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER)
+#define TEX_START_HALF4_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER)
+#define TEX_START_FLOAT_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER)
+#define TEX_START_BYTE_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER)
+#define TEX_START_HALF_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER + TEX_NUM_BYTE_CUDA_KEPLER)
/* OpenCL */
#define TEX_NUM_FLOAT4_OPENCL 1024
#define TEX_NUM_BYTE4_OPENCL 1024
-#define TEX_NUM_FLOAT_OPENCL 0
-#define TEX_NUM_BYTE_OPENCL 0
#define TEX_NUM_HALF4_OPENCL 0
+#define TEX_NUM_FLOAT_OPENCL 1024
+#define TEX_NUM_BYTE_OPENCL 1024
#define TEX_NUM_HALF_OPENCL 0
#define TEX_START_FLOAT4_OPENCL 0
#define TEX_START_BYTE4_OPENCL TEX_NUM_FLOAT4_OPENCL
-#define TEX_START_FLOAT_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL)
-#define TEX_START_BYTE_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_FLOAT_OPENCL)
-#define TEX_START_HALF4_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_FLOAT_OPENCL + TEX_NUM_BYTE_OPENCL)
-#define TEX_START_HALF_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_FLOAT_OPENCL + TEX_NUM_BYTE_OPENCL + TEX_NUM_HALF4_OPENCL)
+#define TEX_START_HALF4_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL)
+#define TEX_START_FLOAT_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_HALF4_OPENCL)
+#define TEX_START_BYTE_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_HALF4_OPENCL + TEX_NUM_FLOAT_OPENCL)
+#define TEX_START_HALF_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_HALF4_OPENCL + TEX_NUM_FLOAT_OPENCL + TEX_NUM_BYTE_OPENCL)
/* Color to use when textures are not found. */
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_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 6a27d7aadf9..2aa950f8278 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -649,7 +649,7 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
3, 2,
#endif
- GHOST_OPENGL_WGL_CONTEXT_FLAGS,
+ (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
#else
# error
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/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/sys_info.py b/release/scripts/modules/sys_info.py
index b225325ba27..30b9cdfaf37 100644
--- a/release/scripts/modules/sys_info.py
+++ b/release/scripts/modules/sys_info.py
@@ -158,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")
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_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index 6f53ae6e118..50221d57b49 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -151,14 +151,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")
@@ -180,6 +181,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")
@@ -214,6 +218,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)
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 baa70ed08f5..90cf410036a 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -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()
@@ -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,6 +296,11 @@ 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()
@@ -220,18 +310,54 @@ class GreasePencilStrokeSculptPanel:
row = layout.row(align=True)
row.prop_enum(brush, "direction", 'ADD', text="Pinch")
row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate")
- elif tool == 'TWIST':
+ elif settings.tool == 'TWIST':
row = layout.row(align=True)
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 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, "name", 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,44 +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)
-
- # Options
- col = layout.column(align=True)
- col.active = not gpl.lock
- col.prop(gpl, "line_width", slider=True)
-
+ # Offsets + Parenting (where available)
split = layout.split(percentage=0.5)
split.active = not gpl.lock
- col = split.column(align=True)
- col.prop(gpl, "use_volumetric_strokes")
- col.prop(gpl, "show_points", text="Points")
+ # 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)
- col = split.column(align=True)
- col.prop(gpl, "use_hq_fill")
- col.prop(gpl, "show_x_ray")
+ # 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="")
+
+ # Parenting
+ if context.space_data.type == 'VIEW_3D':
+ col = split.column(align=True)
+ col.label(text="Parent:")
+ col.prop(gpl, "parent", text="")
+
+ 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()
@@ -633,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 c2580d4ac71..89ea9dff69b 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -716,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/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py
index 58bb956f653..a9db1266bc6 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):
@@ -619,6 +621,7 @@ class CLIP_PT_track(CLIP_PT_tracking_panel, Panel):
text="", toggle=True, icon='IMAGE_ALPHA')
layout.prop(act_track, "weight")
+ layout.prop(act_track, "weight_stab")
if act_track.has_bundle:
label_text = "Average Error: %.4f" % (act_track.average_error)
@@ -905,44 +908,81 @@ class CLIP_PT_stabilization(CLIP_PT_reconstruction_panel, Panel):
self.layout.prop(stab, "use_2d_stabilization", text="")
def draw(self, context):
- layout = self.layout
-
tracking = context.space_data.clip.tracking
stab = tracking.stabilization
+ layout = self.layout
layout.active = stab.use_2d_stabilization
- row = layout.row()
- row.template_list("UI_UL_list", "stabilization_tracks", stab, "tracks",
- stab, "active_track_index", rows=2)
+ layout.prop(stab, "anchor_frame")
- sub = row.column(align=True)
+ layout.prop(stab, "use_stabilize_rotation")
- sub.operator("clip.stabilize_2d_add", icon='ZOOMIN', text="")
- sub.operator("clip.stabilize_2d_remove", icon='ZOOMOUT', text="")
+ box = layout.box()
+ row = box.row(align=True)
+ row.prop(stab, "show_tracks_expanded", text="", emboss=False)
- sub.menu('CLIP_MT_stabilize_2d_specials', text="",
- icon='DOWNARROW_HLT')
+ if not stab.show_tracks_expanded:
+ row.label(text="Tracks For Stabilization")
+ else:
+ row.label(text="Tracks For Location")
+ row = box.row()
+ row.template_list("UI_UL_list", "stabilization_tracks", stab, "tracks",
+ stab, "active_track_index", rows=2)
- layout.prop(stab, "influence_location")
+ sub = row.column(align=True)
- layout.prop(stab, "use_autoscale")
- col = layout.column()
- col.active = stab.use_autoscale
- col.prop(stab, "scale_max")
- col.prop(stab, "influence_scale")
+ sub.operator("clip.stabilize_2d_add", icon='ZOOMIN', text="")
+ sub.operator("clip.stabilize_2d_remove", icon='ZOOMOUT', text="")
- layout.prop(stab, "use_stabilize_rotation")
- col = layout.column()
- col.active = stab.use_stabilize_rotation
+ sub.menu('CLIP_MT_stabilize_2d_specials', text="",
+ icon='DOWNARROW_HLT')
- row = col.row(align=True)
- row.prop_search(stab, "rotation_track", tracking, "tracks", text="")
- row.operator("clip.stabilize_2d_set_rotation", text="", icon='ZOOMIN')
+ row = box.row()
+ row.label(text="Tracks For Rotation / Scale")
+ row = box.row()
+ row.active = stab.use_stabilize_rotation
+ row.template_list("UI_UL_list", "stabilization_rotation_tracks", stab, "rotation_tracks",
+ stab, "active_rotation_track_index", rows=2)
+
+ sub = row.column(align=True)
+
+ sub.operator("clip.stabilize_2d_rotation_add", icon='ZOOMIN', text="")
+ sub.operator("clip.stabilize_2d_rotation_remove", icon='ZOOMOUT', text="")
+
+ sub.menu('CLIP_MT_stabilize_2d_rotation_specials', text="",
+ icon='DOWNARROW_HLT')
+
+ row = layout.row()
+ row.active = stab.use_stabilize_rotation
+ row.prop(stab, "use_stabilize_scale")
+ if stab.use_autoscale:
+ row = layout.row(align=True)
+ row.prop(stab, "use_autoscale")
+ row.prop(stab, "scale_max", text="Max")
+ else:
+ layout.prop(stab, "use_autoscale")
+
+ layout.separator()
+ layout.label(text="Expected Position")
+ layout.prop(stab, "target_pos", text="")
+ layout.prop(stab, "target_rot")
+ if stab.use_autoscale:
+ layout.label(text="Auto Scale Factor: %5.3f" % (1.0 / stab.target_zoom))
+ else:
+ layout.prop(stab, "target_zoom")
+ layout.separator()
+ row = layout.row()
+ row.active = 0 < len(stab.tracks.values())
+ row.prop(stab, "influence_location")
+
+ col = layout.column()
+ col.active = stab.use_stabilize_rotation and 0 < len(stab.rotation_tracks.values())
row = col.row()
- row.active = stab.rotation_track is not None
row.prop(stab, "influence_rotation")
+ row = col.row()
+ row.prop(stab, "influence_scale")
layout.prop(stab, "filter_type")
@@ -1126,6 +1166,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 +1191,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"
@@ -1413,7 +1472,7 @@ class CLIP_MT_track_color_specials(Menu):
class CLIP_MT_stabilize_2d_specials(Menu):
- bl_label = "Track Color Specials"
+ bl_label = "Translation Track Specials"
def draw(self, context):
layout = self.layout
@@ -1421,5 +1480,14 @@ class CLIP_MT_stabilize_2d_specials(Menu):
layout.operator("clip.stabilize_2d_select")
+class CLIP_MT_stabilize_2d_rotation_specials(Menu):
+ bl_label = "Rotation Track Specials"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("clip.stabilize_2d_rotation_select")
+
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 9f719bc793e..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_
@@ -1187,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'
@@ -1202,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 681fa8e39aa..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
@@ -139,7 +142,9 @@ class VIEW3D_HT_header(Header):
# XXX: icon
layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH')
- layout.prop(context.tool_settings.gpencil_sculpt, "use_select_mask")
+ 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):
@@ -3079,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 e9f4a45c2c3..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,
@@ -1965,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/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index e36f9e2b43e..6f2b78e0845 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
@@ -153,3 +154,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_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_collision.h b/source/blender/blenkernel/BKE_collision.h
index d5b4a584ec6..8fedcd4ab06 100644
--- a/source/blender/blenkernel/BKE_collision.h
+++ b/source/blender/blenkernel/BKE_collision.h
@@ -146,6 +146,10 @@ void collision_get_collider_velocity(float vel_old[3], float vel_new[3], struct
/////////////////////////////////////////////////
// used in effect.c
/////////////////////////////////////////////////
+
+/* explicit control over layer mask and dupli recursion */
+struct Object **get_collisionobjects_ext(struct Scene *scene, struct Object *self, struct Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli);
+
struct Object **get_collisionobjects(struct Scene *scene, struct Object *self, struct Group *group, unsigned int *numcollobj, unsigned int modifier_type);
typedef struct ColliderCache {
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_effect.h b/source/blender/blenkernel/BKE_effect.h
index f8fee444d91..b934ec7166d 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -110,7 +110,7 @@ typedef struct EffectorCache {
} EffectorCache;
void free_partdeflect(struct PartDeflect *pd);
-struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc);
+struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool for_simulation);
void pdEndEffectors(struct ListBase **effectors);
void pdPrecalculateEffectors(struct ListBase *effectors);
void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse);
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 e32bc2ffb20..e49019fcfae 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -94,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_material.h b/source/blender/blenkernel/BKE_material.h
index df739996c54..8ae5c2b3c45 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -49,7 +49,7 @@ void BKE_material_free(struct Material *ma);
void BKE_material_free_ex(struct Material *ma, bool do_id_user);
void test_object_materials(struct Object *ob, struct ID *id);
void test_all_objects_materials(struct Main *bmain, struct ID *id);
-void BKE_material_resize_object(struct Object *ob, const short totcol, bool do_id_user);
+void BKE_material_resize_object(struct Main *bmain, struct Object *ob, const short totcol, bool do_id_user);
void BKE_material_init(struct Material *ma);
void BKE_material_remap_object(struct Object *ob, const unsigned int *remap);
void BKE_material_remap_object_calc(struct Object *ob_dst, struct Object *ob_src, short *remap_src_to_dst);
@@ -90,10 +90,10 @@ void BKE_texpaint_slot_refresh_cache(struct Scene *scene, struct Material *ma);
void BKE_texpaint_slots_refresh_object(struct Scene *scene, struct Object *ob);
/* rna api */
-void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user);
-void BKE_material_append_id(struct ID *id, struct Material *ma);
-struct Material *BKE_material_pop_id(struct ID *id, int index, bool update_data); /* index is an int because of RNA */
-void BKE_material_clear_id(struct ID *id, bool update_data);
+void BKE_material_resize_id(struct Main *bmain, struct ID *id, short totcol, bool do_id_user);
+void BKE_material_append_id(struct Main *bmain, struct ID *id, struct Material *ma);
+struct Material *BKE_material_pop_id(struct Main *bmain, struct ID *id, int index, bool update_data); /* index is an int because of RNA */
+void BKE_material_clear_id(struct Main *bmain, struct ID *id, bool update_data);
/* rendering */
void init_render_material(struct Material *, int, float *);
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index 37831728e6f..b3e3968ca9b 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -63,6 +63,8 @@ struct BVHTreeRay;
struct BVHTreeRayHit;
struct EdgeHash;
+#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))
@@ -205,8 +207,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;
@@ -218,7 +219,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_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index 8238ea64242..02f6c435ee2 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -304,7 +304,7 @@ void BKE_ptcache_mem_pointers_incr(struct PTCacheMem *pm);
int BKE_ptcache_mem_pointers_seek(int point_index, struct PTCacheMem *pm);
/* Main cache reading call. */
-int BKE_ptcache_read(PTCacheID *pid, float cfra);
+int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old);
/* Main cache writing call. */
int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra);
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 1938bb08395..30873567297 100644
--- a/source/blender/blenkernel/BKE_tracking.h
+++ b/source/blender/blenkernel/BKE_tracking.h
@@ -277,9 +277,9 @@ void BKE_tracking_detect_harris(struct MovieTracking *tracking, struct ListBase
bool place_outside_layer);
/* **** 2D stabilization **** */
-void BKE_tracking_stabilization_data_get(struct MovieTracking *tracking, int framenr, int width, int height,
+void BKE_tracking_stabilization_data_get(struct MovieClip *clip, int framenr, int width, int height,
float translation[2], float *scale, float *angle);
-struct ImBuf *BKE_tracking_stabilize_frame(struct MovieTracking *tracking, int framenr, struct ImBuf *ibuf,
+struct ImBuf *BKE_tracking_stabilize_frame(struct MovieClip *clip, int framenr, struct ImBuf *ibuf,
float translation[2], float *scale, float *angle);
void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect, float translation[2],
float scale, float angle, float mat[4][4]);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index b7ff81d603b..157c4408d6a 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -81,6 +81,7 @@ set(SRC
intern/brush.c
intern/bullet.c
intern/bvhutils.c
+ intern/cachefile.c
intern/camera.c
intern/cdderivedmesh.c
intern/cloth.c
@@ -207,6 +208,7 @@ set(SRC
BKE_brush.h
BKE_bullet.h
BKE_bvhutils.h
+ BKE_cachefile.h
BKE_camera.h
BKE_ccg.h
BKE_cdderivedmesh.h
@@ -500,6 +502,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/depsgraph_private.h b/source/blender/blenkernel/depsgraph_private.h
index 7b3199efb41..69ca75836d9 100644
--- a/source/blender/blenkernel/depsgraph_private.h
+++ b/source/blender/blenkernel/depsgraph_private.h
@@ -34,6 +34,11 @@
#include "DNA_constraint_types.h"
#include "BKE_constraint.h"
+struct Scene;
+struct Group;
+struct EffectorWeights;
+struct ModifierData;
+
/* **** DAG relation types *** */
/* scene link to object */
@@ -152,6 +157,11 @@ DagNode *dag_get_node(DagForest *forest, void *fob);
DagNode *dag_get_sub_node(DagForest *forest, void *fob);
void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, const char *name);
+typedef bool (*DagCollobjFilterFunction)(struct Object *obj, struct ModifierData *md);
+
+void dag_add_collision_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name);
+void dag_add_forcefield_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name);
+
void graph_print_queue(DagNodeQueue *nqueue);
void graph_print_queue_dist(DagNodeQueue *nqueue);
void graph_print_adj_list(DagForest *dag);
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 477c7036762..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);
@@ -2873,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
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/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/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 681b93172b2..28ef3f6f248 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -81,8 +81,10 @@ void cloth_init(ClothModifierData *clmd )
clmd->sim_parms->gravity[1] = 0.0;
clmd->sim_parms->gravity[2] = -9.81;
clmd->sim_parms->structural = 15.0;
+ clmd->sim_parms->max_struct = 15.0;
clmd->sim_parms->shear = 15.0;
clmd->sim_parms->bending = 0.5;
+ clmd->sim_parms->max_bend = 0.5;
clmd->sim_parms->bending_damping = 0.5;
clmd->sim_parms->Cdis = 5.0;
clmd->sim_parms->Cvi = 1.0;
@@ -449,9 +451,12 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
}
/* try to read from cache */
- cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe);
+ bool can_simulate = (framenr == clmd->clothObject->last_frame+1) && !(cache->flag & PTCACHE_BAKED);
- if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) {
+ cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate);
+
+ if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED ||
+ (!can_simulate && cache_result == PTCACHE_READ_OLD)) {
BKE_cloth_solver_set_positions(clmd);
cloth_to_object (ob, clmd, vertexCos);
@@ -473,7 +478,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
return;
}
- if (framenr!=clmd->clothObject->last_frame+1)
+ if (!can_simulate)
return;
/* if on second frame, write cache for first frame */
diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c
index 8cac856b560..35a7aafdbde 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -503,12 +503,13 @@ static void add_collision_object(Object ***objs, unsigned int *numobj, unsigned
// return all collision objects in scene
// collision object will exclude self
-Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type)
+Object **get_collisionobjects_ext(Scene *scene, Object *self, Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli)
{
Base *base;
Object **objs;
GroupObject *go;
unsigned int numobj= 0, maxobj= 100;
+ int level = dupli ? 0 : 1;
objs= MEM_callocN(sizeof(Object *)*maxobj, "CollisionObjectsArray");
@@ -516,16 +517,14 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned
if (group) {
/* use specified group */
for (go= group->gobject.first; go; go= go->next)
- add_collision_object(&objs, &numobj, &maxobj, go->ob, self, 0, modifier_type);
+ add_collision_object(&objs, &numobj, &maxobj, go->ob, self, level, modifier_type);
}
else {
Scene *sce_iter;
/* add objects in same layer in scene */
for (SETLOOPER(scene, sce_iter, base)) {
- /* Need to check for active layers, too.
- Otherwise this check fails if the objects are not on the same layer - DG */
- if ((base->lay & self->lay) || (base->lay & scene->lay))
- add_collision_object(&objs, &numobj, &maxobj, base->object, self, 0, modifier_type);
+ if ( base->lay & layer )
+ add_collision_object(&objs, &numobj, &maxobj, base->object, self, level, modifier_type);
}
}
@@ -535,6 +534,13 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned
return objs;
}
+Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type)
+{
+ /* Need to check for active layers, too.
+ Otherwise this check fails if the objects are not on the same layer - DG */
+ return get_collisionobjects_ext(scene, self, group, self->lay | scene->lay, numcollobj, modifier_type, true);
+}
+
static void add_collider_cache_object(ListBase **objs, Object *ob, Object *self, int level)
{
CollisionModifierData *cmd= NULL;
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/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 8ea65bf92c1..5f8332dcf0c 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"
@@ -58,14 +59,18 @@
#include "DNA_windowmanager_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_rigidbody_types.h"
#include "BKE_anim.h"
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_collision.h"
#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"
@@ -448,49 +453,51 @@ static void dag_add_lamp_driver_relations(DagForest *dag, DagNode *node, Lamp *l
la->id.tag &= ~LIB_TAG_DOIT;
}
-static void check_and_create_collision_relation(DagForest *dag, Object *ob, DagNode *node, Object *ob1, int skip_forcefield, bool no_collision)
+static void create_collision_relation(DagForest *dag, DagNode *node, Object *ob1, const char *name)
{
- DagNode *node2;
- if (ob1->pd && (ob1->pd->deflect || ob1->pd->forcefield) && (ob1 != ob)) {
- if ((skip_forcefield && ob1->pd->forcefield == skip_forcefield) || (no_collision && ob1->pd->forcefield == 0))
- return;
- node2 = dag_get_node(dag, ob1);
- dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Collision");
+ DagNode *node2 = dag_get_node(dag, ob1);
+ dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, name);
+}
+
+void dag_add_collision_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name)
+{
+ unsigned int numcollobj;
+ Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli);
+
+ for (unsigned int i = 0; i < numcollobj; i++) {
+ Object *ob1 = collobjs[i];
+
+ if (!fn || fn(ob1, modifiers_findByType(ob1, modifier_type))) {
+ create_collision_relation(dag, node, ob1, name);
+ }
}
+
+ if (collobjs)
+ MEM_freeN(collobjs);
}
-static void dag_add_collision_field_relation(DagForest *dag, Scene *scene, Object *ob, DagNode *node, int skip_forcefield, bool no_collision)
+void dag_add_forcefield_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name)
{
- Base *base;
- ParticleSystem *particle_system;
+ ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
- for (particle_system = ob->particlesystem.first;
- particle_system;
- particle_system = particle_system->next)
- {
- EffectorWeights *effector_weights = particle_system->part->effector_weights;
- if (effector_weights->group) {
- GroupObject *group_object;
+ if (effectors) {
+ for (EffectorCache *eff = effectors->first; eff; eff = eff->next) {
+ if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) {
+ create_collision_relation(dag, node, eff->ob, name);
- for (group_object = effector_weights->group->gobject.first;
- group_object;
- group_object = group_object->next)
- {
- if ((group_object->ob->lay & ob->lay)) {
- check_and_create_collision_relation(dag, ob, node, group_object->ob, skip_forcefield, no_collision);
+ if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
+ create_collision_relation(dag, node, eff->pd->f_source, "Smoke Force Domain");
+ }
+
+ if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
+ /* Actual code uses get_collider_cache */
+ dag_add_collision_relations(dag, scene, ob, node, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption");
}
}
}
}
- /* would be nice to have a list of colliders here
- * so for now walk all objects in scene check 'same layer rule' */
- for (base = scene->base.first; base; base = base->next) {
- if ((base->lay & ob->lay)) {
- Object *ob1 = base->object;
- check_and_create_collision_relation(dag, ob, node, ob1, skip_forcefield, no_collision);
- }
- }
+ pdEndEffectors(&effectors);
}
static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Scene *scene, Object *ob, int mask)
@@ -641,23 +648,13 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
}
}
- /* softbody collision */
+ /* rigidbody force fields */
if ((ob->type == OB_MESH) || (ob->type == OB_CURVE) || (ob->type == OB_LATTICE)) {
- if (ob->particlesystem.first ||
- modifiers_isModifierEnabled(ob, eModifierType_Softbody) ||
- modifiers_isModifierEnabled(ob, eModifierType_Cloth) ||
- modifiers_isModifierEnabled(ob, eModifierType_DynamicPaint))
- {
- dag_add_collision_field_relation(dag, scene, ob, node, 0, false); /* TODO: use effectorweight->group */
- }
- else if (modifiers_isModifierEnabled(ob, eModifierType_Smoke)) {
- dag_add_collision_field_relation(dag, scene, ob, node, PFIELD_SMOKEFLOW, false);
- }
- else if (ob->rigidbody_object) {
- dag_add_collision_field_relation(dag, scene, ob, node, 0, true);
+ if (ob->rigidbody_object && scene->rigidbody_world) {
+ dag_add_forcefield_relations(dag, scene, ob, node, scene->rigidbody_world->effector_weights, true, 0, "Force Field");
}
}
-
+
/* object data drivers */
if (ob->data) {
AnimData *adt = BKE_animdata_from_id((ID *)ob->data);
@@ -761,8 +758,6 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
BoidRule *rule = NULL;
BoidState *state = NULL;
ParticleSettings *part = psys->part;
- ListBase *effectors = NULL;
- EffectorCache *eff;
if (part->adt) {
dag_add_driver_relation(part->adt, dag, node, 1);
@@ -801,18 +796,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
}
}
- effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
-
- if (effectors) {
- for (eff = effectors->first; eff; eff = eff->next) {
- if (eff->psys) {
- node2 = dag_get_node(dag, eff->ob);
- dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Field");
- }
- }
+ if (part->type != PART_HAIR) {
+ /* Actual code uses get_collider_cache */
+ dag_add_collision_relations(dag, scene, ob, node, part->collision_group, ob->lay, eModifierType_Collision, NULL, true, "Particle Collision");
}
- pdEndEffectors(&effectors);
+ dag_add_forcefield_relations(dag, scene, ob, node, part->effector_weights, part->type == PART_HAIR, 0, "Particle Force Field");
if (part->boids) {
for (state = part->boids->states.first; state; state = state->next) {
@@ -2173,7 +2162,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) {
@@ -2799,9 +2793,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)) {
@@ -2841,9 +2833,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;
}
@@ -2924,9 +2914,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);
@@ -3001,6 +2989,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)
@@ -3020,12 +3035,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/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index d35de6fc5d3..24425720bd2 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -2015,11 +2015,13 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene
}
/* try to read from cache */
- if (BKE_ptcache_read(&pid, (float)scene->r.cfra)) {
+ bool can_simulate = ((int)scene->r.cfra == current_frame) && !(cache->flag & PTCACHE_BAKED);
+
+ if (BKE_ptcache_read(&pid, (float)scene->r.cfra, can_simulate)) {
BKE_ptcache_validate(cache, (int)scene->r.cfra);
}
/* if read failed and we're on surface range do recalculate */
- else if ((int)scene->r.cfra == current_frame && !(cache->flag & PTCACHE_BAKED)) {
+ else if (can_simulate) {
/* calculate surface frame */
canvas->flags |= MOD_DPAINT_BAKING;
dynamicPaint_calculateFrame(surface, scene, ob, current_frame);
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 5090e46f1fc..7e6897a2858 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -155,15 +155,20 @@ static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSyste
eff->frame = -1;
return eff;
}
-static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src)
+static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src, bool for_simulation)
{
EffectorCache *eff = NULL;
- if ( ob == ob_src || weights->weight[ob->pd->forcefield] == 0.0f )
+ if ( ob == ob_src )
return;
- if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal )
- return;
+ if (for_simulation) {
+ if (weights->weight[ob->pd->forcefield] == 0.0f )
+ return;
+
+ if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal )
+ return;
+ }
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
@@ -175,7 +180,7 @@ static void add_object_to_effectors(ListBase **effectors, Scene *scene, Effector
BLI_addtail(*effectors, eff);
}
-static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src)
+static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src, bool for_simulation)
{
ParticleSettings *part= psys->part;
@@ -185,14 +190,14 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec
if ( psys == psys_src && (part->flag & PART_SELF_EFFECT) == 0)
return;
- if ( part->pd && part->pd->forcefield && weights->weight[part->pd->forcefield] != 0.0f) {
+ if ( part->pd && part->pd->forcefield && (!for_simulation || weights->weight[part->pd->forcefield] != 0.0f)) {
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd));
}
- if (part->pd2 && part->pd2->forcefield && weights->weight[part->pd2->forcefield] != 0.0f) {
+ if (part->pd2 && part->pd2->forcefield && (!for_simulation || weights->weight[part->pd2->forcefield] != 0.0f)) {
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
@@ -202,7 +207,7 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec
/* returns ListBase handle with objects taking part in the effecting */
ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src,
- EffectorWeights *weights, bool precalc)
+ EffectorWeights *weights, bool for_simulation)
{
Base *base;
unsigned int layer= ob_src->lay;
@@ -214,13 +219,13 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
for (go= weights->group->gobject.first; go; go= go->next) {
if ( (go->ob->lay & layer) ) {
if ( go->ob->pd && go->ob->pd->forcefield )
- add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src);
+ add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src, for_simulation);
if ( go->ob->particlesystem.first ) {
ParticleSystem *psys= go->ob->particlesystem.first;
for ( ; psys; psys=psys->next )
- add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src);
+ add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src, for_simulation);
}
}
}
@@ -229,19 +234,19 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
for (base = scene->base.first; base; base= base->next) {
if ( (base->lay & layer) ) {
if ( base->object->pd && base->object->pd->forcefield )
- add_object_to_effectors(&effectors, scene, weights, base->object, ob_src);
+ add_object_to_effectors(&effectors, scene, weights, base->object, ob_src, for_simulation);
if ( base->object->particlesystem.first ) {
ParticleSystem *psys= base->object->particlesystem.first;
for ( ; psys; psys=psys->next )
- add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src);
+ add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src, for_simulation);
}
}
}
}
- if (precalc)
+ if (for_simulation)
pdPrecalculateEffectors(effectors);
return effectors;
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/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/image.c b/source/blender/blenkernel/intern/image.c
index ea28dabb945..626d389ac2d 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));
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index d3f20859f06..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"
@@ -332,8 +335,10 @@ 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 ((ID_Type)GS(id->name)) {
case ID_SCE:
@@ -423,6 +428,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
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:
@@ -510,7 +518,7 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
if (!test) *newid = (ID *)BKE_particlesettings_copy(bmain, (ParticleSettings *)id);
return true;
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);
@@ -527,6 +535,9 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
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:
@@ -639,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;
}
@@ -740,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 *****************
@@ -913,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;
}
@@ -1039,6 +1052,9 @@ 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. */
@@ -1226,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;
@@ -1639,22 +1656,20 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged
for (a = set_listbasepointers(bmain, lbarray); a--; ) {
id = lbarray[a]->first;
- if (!id || !BKE_idcode_is_linkable(GS(id->name))) {
- /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), they are assumed to be handled
- * by real datablocks responsible of them. */
- continue;
- }
+ /* 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 d71e8012e10..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;
@@ -842,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));
@@ -884,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:
@@ -943,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;
}
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/material.c b/source/blender/blenkernel/intern/material.c
index 470108545b8..54945242fe4 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -58,6 +58,7 @@
#include "BKE_animsys.h"
#include "BKE_displist.h"
#include "BKE_global.h"
+#include "BKE_depsgraph.h"
#include "BKE_icons.h"
#include "BKE_image.h"
#include "BKE_library.h"
@@ -398,7 +399,7 @@ static void material_data_index_clear_id(ID *id)
}
}
-void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user)
+void BKE_material_resize_id(Main *bmain, ID *id, short totcol, bool do_id_user)
{
Material ***matar = give_matarar_id(id);
short *totcolp = give_totcolp_id(id);
@@ -424,9 +425,11 @@ void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user)
*matar = MEM_recallocN(*matar, sizeof(void *) * totcol);
}
*totcolp = totcol;
+
+ DAG_relations_tag_update(bmain);
}
-void BKE_material_append_id(ID *id, Material *ma)
+void BKE_material_append_id(Main *bmain, ID *id, Material *ma)
{
Material ***matar;
if ((matar = give_matarar_id(id))) {
@@ -439,11 +442,12 @@ void BKE_material_append_id(ID *id, Material *ma)
(*matar)[(*totcol)++] = ma;
id_us_plus((ID *)ma);
- test_all_objects_materials(G.main, id);
+ test_all_objects_materials(bmain, id);
+ DAG_relations_tag_update(bmain);
}
}
-Material *BKE_material_pop_id(ID *id, int index_i, bool update_data)
+Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i, bool update_data)
{
short index = (short)index_i;
Material *ret = NULL;
@@ -472,13 +476,15 @@ Material *BKE_material_pop_id(ID *id, int index_i, bool update_data)
/* decrease mat_nr index */
material_data_index_remove_id(id, index);
}
+
+ DAG_relations_tag_update(bmain);
}
}
return ret;
}
-void BKE_material_clear_id(struct ID *id, bool update_data)
+void BKE_material_clear_id(Main *bmain, ID *id, bool update_data)
{
Material ***matar;
if ((matar = give_matarar_id(id))) {
@@ -497,6 +503,8 @@ void BKE_material_clear_id(struct ID *id, bool update_data)
/* decrease mat_nr index */
material_data_index_clear_id(id);
}
+
+ DAG_relations_tag_update(bmain);
}
}
@@ -553,7 +561,7 @@ Material *give_node_material(Material *ma)
return NULL;
}
-void BKE_material_resize_object(Object *ob, const short totcol, bool do_id_user)
+void BKE_material_resize_object(Main *bmain, Object *ob, const short totcol, bool do_id_user)
{
Material **newmatar;
char *newmatbits;
@@ -590,6 +598,8 @@ void BKE_material_resize_object(Object *ob, const short totcol, bool do_id_user)
ob->totcol = totcol;
if (ob->totcol && ob->actcol == 0) ob->actcol = 1;
if (ob->actcol > ob->totcol) ob->actcol = ob->totcol;
+
+ DAG_relations_tag_update(bmain);
}
void test_object_materials(Object *ob, ID *id)
@@ -601,7 +611,7 @@ void test_object_materials(Object *ob, ID *id)
return;
}
- BKE_material_resize_object(ob, *totcol, false);
+ BKE_material_resize_object(G.main, ob, *totcol, false);
}
void test_all_objects_materials(Main *bmain, ID *id)
@@ -617,7 +627,7 @@ void test_all_objects_materials(Main *bmain, ID *id)
BKE_main_lock(bmain);
for (ob = bmain->object.first; ob; ob = ob->id.next) {
if (ob->data == id) {
- BKE_material_resize_object(ob, *totcol, false);
+ BKE_material_resize_object(bmain, ob, *totcol, false);
}
}
BKE_main_unlock(bmain);
@@ -1881,7 +1891,7 @@ static short mesh_getmaterialnumber(Mesh *me, Material *ma)
/* append material */
static short mesh_addmaterial(Mesh *me, Material *ma)
{
- BKE_material_append_id(&me->id, NULL);
+ BKE_material_append_id(G.main, &me->id, NULL);
me->mat[me->totcol - 1] = ma;
id_us_plus(&ma->id);
@@ -2020,7 +2030,7 @@ static void convert_tfacematerial(Main *main, Material *ma)
/* remove material from mesh */
for (a = 0; a < me->totcol; ) {
if (me->mat[a] == ma) {
- BKE_material_pop_id(&me->id, a, true);
+ BKE_material_pop_id(main, &me->id, a, true);
}
else {
a++;
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 0d362086134..482015d3b26 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -1033,7 +1033,7 @@ static ImBuf *get_stable_cached_frame(MovieClip *clip, MovieClipUser *user, ImBu
stableibuf = cache->stabilized.ibuf;
- BKE_tracking_stabilization_data_get(&clip->tracking, clip_framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle);
+ BKE_tracking_stabilization_data_get(clip, clip_framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle);
/* check for stabilization parameters */
if (tscale != cache->stabilized.scale ||
@@ -1057,7 +1057,7 @@ static ImBuf *put_stabilized_frame_to_cache(MovieClip *clip, MovieClipUser *user
float tloc[2], tscale, tangle;
int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, framenr);
- stableibuf = BKE_tracking_stabilize_frame(&clip->tracking, clip_framenr, ibuf, tloc, &tscale, &tangle);
+ stableibuf = BKE_tracking_stabilize_frame(clip, clip_framenr, ibuf, tloc, &tscale, &tangle);
copy_v2_v2(cache->stabilized.loc, tloc);
@@ -1270,8 +1270,6 @@ void BKE_movieclip_reload(MovieClip *clip)
/* clear cache */
free_buffers(clip);
- clip->tracking.stabilization.ok = false;
-
/* update clip source */
detect_clip_source(clip);
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 42b818b35a5..4fb302c71ea 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -4050,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);
@@ -4070,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 8e3e2f5d6d0..b4e951ce04a 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;
@@ -3919,7 +3925,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_
/* 2. try to read from the cache */
if (pid) {
- int cache_result = BKE_ptcache_read(pid, cache_cfra);
+ int cache_result = BKE_ptcache_read(pid, cache_cfra, true);
if (ELEM(cache_result, PTCACHE_READ_EXACT, PTCACHE_READ_INTERPOLATED)) {
cached_step(sim, cfra);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 69a98c06000..d08814e3373 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -2632,7 +2632,7 @@ static int ptcache_interpolate(PTCacheID *pid, float cfra, int cfra1, int cfra2)
}
/* reads cache from disk or memory */
/* possible to get old or interpolated result */
-int BKE_ptcache_read(PTCacheID *pid, float cfra)
+int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old)
{
int cfrai = (int)floor(cfra), cfra1=0, cfra2=0;
int ret = 0;
@@ -2658,10 +2658,17 @@ int BKE_ptcache_read(PTCacheID *pid, float cfra)
return 0;
/* don't read old cache if already simulated past cached frame */
- if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe)
- return 0;
- if (cfra1 && cfra1 == cfra2)
- return 0;
+ if (no_extrapolate_old) {
+ if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe)
+ return 0;
+ if (cfra1 && cfra1 == cfra2)
+ return 0;
+ }
+ else {
+ /* avoid calling interpolate between the same frame values */
+ if (cfra1 && cfra1 == cfra2)
+ cfra1 = 0;
+ }
if (cfra1) {
if (pid->file_type == PTCACHE_FILE_OPENVDB && pid->read_openvdb_stream) {
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 0f1f9b4bdf7..c3ae5736aa9 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -1560,14 +1560,16 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
/* try to read from cache */
// RB_TODO deal with interpolated, old and baked results
- if (BKE_ptcache_read(&pid, ctime)) {
+ bool can_simulate = (ctime == rbw->ltime + 1) && !(cache->flag & PTCACHE_BAKED);
+
+ if (BKE_ptcache_read(&pid, ctime, can_simulate)) {
BKE_ptcache_validate(cache, (int)ctime);
rbw->ltime = ctime;
return;
}
/* advance simulation, we can only step one frame forward */
- if (ctime == rbw->ltime + 1 && !(cache->flag & PTCACHE_BAKED)) {
+ if (can_simulate) {
/* write cache for first frame when on second frame */
if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
BKE_ptcache_write(&pid, startframe);
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 43569f9ded2..3fc72628c97 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -2737,19 +2737,18 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
return;
}
+ /* only calculate something when we advanced a single frame */
+ /* don't simulate if viewing start frame, but scene frame is not real start frame */
+ bool can_simulate = (framenr == (int)smd->time + 1) && (framenr == scene->r.cfra);
+
/* try to read from cache */
- if (BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) {
+ if (BKE_ptcache_read(&pid, (float)framenr, can_simulate) == PTCACHE_READ_EXACT) {
BKE_ptcache_validate(cache, framenr);
smd->time = framenr;
return;
}
- /* only calculate something when we advanced a single frame */
- if (framenr != (int)smd->time + 1)
- return;
-
- /* don't simulate if viewing start frame, but scene frame is not real start frame */
- if (framenr != scene->r.cfra)
+ if (!can_simulate)
return;
#ifdef DEBUG_TIME
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 901926dd8d4..03cf33083da 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -3719,9 +3719,12 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i
}
/* try to read from cache */
- cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe);
+ bool can_simulate = (framenr == sb->last_frame+1) && !(cache->flag & PTCACHE_BAKED);
- if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) {
+ cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate);
+
+ if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED ||
+ (!can_simulate && cache_result == PTCACHE_READ_OLD)) {
softbody_to_object(ob, vertexCos, numVerts, sb->local);
BKE_ptcache_validate(cache, framenr);
@@ -3742,7 +3745,7 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i
return;
}
- if (framenr!=sb->last_frame+1)
+ if (!can_simulate)
return;
/* if on second frame, write cache for first frame */
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index a56fc0f9abe..d5d3384bb48 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -241,13 +241,9 @@ static void tracking_reconstruction_copy(
/* Copy stabilization structure. */
static void tracking_stabilization_copy(
- MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src,
- GHash *tracks_mapping)
+ MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src)
{
*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. */
@@ -284,7 +280,7 @@ void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *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);
+ tracking_stabilization_copy(&tracking_dst->stabilization, &tracking_src->stabilization);
if (tracking_src->act_track) {
tracking_dst->act_track = BLI_ghash_lookup(tracks_mapping, tracking_src->act_track);
}
@@ -316,7 +312,7 @@ void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src)
}
/* Initialize motion tracking settings to default values,
- * used when new movie clip datablock is creating.
+ * used when new movie clip datablock is created.
*/
void BKE_tracking_settings_init(MovieTracking *tracking)
{
@@ -334,10 +330,22 @@ void BKE_tracking_settings_init(MovieTracking *tracking)
tracking->settings.object_distance = 1;
tracking->stabilization.scaleinf = 1.0f;
+ tracking->stabilization.anchor_frame = MINFRAME;
+ zero_v2(tracking->stabilization.target_pos);
+ tracking->stabilization.target_rot = 0.0f;
+ tracking->stabilization.scale = 1.0f;
+
+ tracking->stabilization.act_track = 0;
+ tracking->stabilization.act_rot_track = 0;
+ tracking->stabilization.tot_track = 0;
+ tracking->stabilization.tot_rot_track = 0;
+
+ tracking->stabilization.scaleinf = 1.0f;
tracking->stabilization.locinf = 1.0f;
tracking->stabilization.rotinf = 1.0f;
tracking->stabilization.maxscale = 2.0f;
tracking->stabilization.filter = TRACKING_FILTER_BILINEAR;
+ tracking->stabilization.flag |= TRACKING_SHOW_STAB_TRACKS;
BKE_tracking_object_add(tracking, "Camera");
}
@@ -552,6 +560,7 @@ MovieTrackingTrack *BKE_tracking_track_add(MovieTracking *tracking, ListBase *tr
track->flag = settings->default_flag;
track->algorithm_flag = settings->default_algorithm_flag;
track->weight = settings->default_weight;
+ track->weight_stab = settings->default_weight;
memset(&marker, 0, sizeof(marker));
marker.pos[0] = x;
@@ -590,6 +599,12 @@ MovieTrackingTrack *BKE_tracking_track_duplicate(MovieTrackingTrack *track)
new_track->markers = MEM_dupallocN(new_track->markers);
+ /* Orevent duplicate from being used for 2D stabilization.
+ * If necessary, it shall be added explicitly.
+ */
+ new_track->flag &= ~TRACK_USE_2D_STAB;
+ new_track->flag &= ~TRACK_USE_2D_STAB_ROT;
+
return new_track;
}
diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c
index eb224020977..1d571099723 100644
--- a/source/blender/blenkernel/intern/tracking_stabilize.c
+++ b/source/blender/blenkernel/intern/tracking_stabilize.c
@@ -21,6 +21,7 @@
* Contributor(s): Blender Foundation,
* Sergey Sharybin
* Keir Mierle
+ * Ichthyostega
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -28,292 +29,1305 @@
/** \file blender/blenkernel/intern/tracking_stabilize.c
* \ingroup bke
*
- * This file contains implementation of 2D frame stabilization.
+ * This file contains implementation of 2D image stabilization.
*/
#include <limits.h>
#include "DNA_movieclip_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_anim_types.h"
+#include "RNA_access.h"
#include "BLI_utildefines.h"
+#include "BLI_sort_utils.h"
+#include "BLI_math_vector.h"
#include "BLI_math.h"
#include "BKE_tracking.h"
+#include "BKE_movieclip.h"
+#include "BKE_fcurve.h"
+#include "BLI_ghash.h"
+#include "MEM_guardedalloc.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
-/* Calculate median point of markers of tracks marked as used for
- * 2D stabilization.
- *
- * NOTE: frame number should be in clip space, not scene space
+
+/* == Parameterization constants == */
+
+/* When measuring the scale changes relative to the rotation pivot point, it
+ * might happen accidentally that a probe point (tracking point), which doesn't
+ * actually move on a circular path, gets very close to the pivot point, causing
+ * the measured scale contribution to go toward infinity. We damp this undesired
+ * effect by adding a bias (floor) to the measured distances, which will
+ * dominate very small distances and thus cause the corresponding track's
+ * contribution to diminish.
+ * Measurements happen in normalized (0...1) coordinates within a frame.
+ */
+static float SCALE_ERROR_LIMIT_BIAS = 0.01f;
+
+/* When to consider a track as completely faded out.
+ * This is used in conjunction with the "disabled" flag of the track
+ * to determine start positions, end positions and gaps
*/
-static bool stabilization_median_point_get(MovieTracking *tracking, int framenr, float median[2])
+static float EPSILON_WEIGHT = 0.005f;
+
+
+
+/* == private working data == */
+
+/* Per track baseline for stabilization, defined at reference frame.
+ * A track's reference frame is chosen as close as possible to the (global)
+ * anchor_frame. Baseline holds the constant part of each track's contribution
+ * to the observed movement; it is calculated at initialization pass, using the
+ * measurement value at reference frame plus the average contribution to fill
+ * the gap between global anchor_frame and the reference frame for this track.
+ * This struct with private working data is associated to the local call context
+ * via `StabContext::private_track_data`
+ */
+typedef struct TrackStabilizationBase {
+ float stabilization_offset_base[2];
+
+ /* measured relative to translated pivot */
+ float stabilization_rotation_base[2][2];
+
+ /* measured relative to translated pivot */
+ float stabilization_scale_base;
+
+ bool is_init_for_stabilization;
+ FCurve *track_weight_curve;
+} TrackStabilizationBase;
+
+/* Tracks are reordered for initialization, starting as close as possible to
+ * anchor_frame
+ */
+typedef struct TrackInitOrder {
+ int sort_value;
+ int reference_frame;
+ MovieTrackingTrack *data;
+} TrackInitOrder;
+
+/* Per frame private working data, for accessing possibly animated values. */
+typedef struct StabContext {
+ MovieClip *clip;
+ MovieTracking *tracking;
+ MovieTrackingStabilization *stab;
+ GHash *private_track_data;
+ FCurve *locinf;
+ FCurve *rotinf;
+ FCurve *scaleinf;
+ FCurve *target_pos[2];
+ FCurve *target_rot;
+ FCurve *target_scale;
+ bool use_animation;
+} StabContext;
+
+
+static TrackStabilizationBase *access_stabilization_baseline_data(
+ StabContext *ctx,
+ MovieTrackingTrack *track)
{
- bool ok = false;
- float min[2], max[2];
- MovieTrackingTrack *track;
+ return BLI_ghash_lookup(ctx->private_track_data, track);
+}
- INIT_MINMAX2(min, max);
+static void attach_stabilization_baseline_data(
+ StabContext *ctx,
+ MovieTrackingTrack *track,
+ TrackStabilizationBase *private_data)
+{
+ return BLI_ghash_insert(ctx->private_track_data, track, private_data);
+}
- track = tracking->tracks.first;
- while (track) {
- if (track->flag & TRACK_USE_2D_STAB) {
- MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
+static void discard_stabilization_baseline_data(void *val)
+{
+ if (val != NULL) {
+ MEM_freeN(val);
+ }
+}
- minmax_v2v2_v2(min, max, marker->pos);
- ok = true;
- }
+/* == access animated values for given frame == */
+
+static FCurve *retrieve_stab_animation(MovieClip *clip,
+ const char *data_path,
+ int idx)
+{
+ return id_data_find_fcurve(&clip->id,
+ &clip->tracking.stabilization,
+ &RNA_MovieTrackingStabilization,
+ data_path,
+ idx,
+ NULL);
+}
+
+static FCurve *retrieve_track_weight_animation(MovieClip *clip,
+ MovieTrackingTrack *track)
+{
+ return id_data_find_fcurve(&clip->id,
+ track,
+ &RNA_MovieTrackingTrack,
+ "weight_stab",
+ 0,
+ NULL);
+}
- track = track->next;
+static float fetch_from_fcurve(FCurve *animationCurve,
+ int framenr,
+ StabContext *ctx,
+ float default_value)
+{
+ if (ctx && ctx->use_animation && animationCurve) {
+ int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(ctx->clip,
+ framenr);
+ return evaluate_fcurve(animationCurve, scene_framenr);
}
+ return default_value;
+}
- median[0] = (max[0] + min[0]) / 2.0f;
- median[1] = (max[1] + min[1]) / 2.0f;
- return ok;
+static float get_animated_locinf(StabContext *ctx, int framenr)
+{
+ return fetch_from_fcurve(ctx->locinf, framenr, ctx, ctx->stab->locinf);
}
-/* Calculate stabilization data (translation, scale and rotation) from
- * given median of first and current frame medians, tracking data and
- * frame number.
- *
- * NOTE: frame number should be in clip space, not scene space
+static float get_animated_rotinf(StabContext *ctx, int framenr)
+{
+ return fetch_from_fcurve(ctx->rotinf, framenr, ctx, ctx->stab->rotinf);
+}
+
+static float get_animated_scaleinf(StabContext *ctx, int framenr)
+{
+ return fetch_from_fcurve(ctx->scaleinf, framenr, ctx, ctx->stab->scaleinf);
+}
+
+static void get_animated_target_pos(StabContext *ctx,
+ int framenr,
+ float target_pos[2])
+{
+ target_pos[0] = fetch_from_fcurve(ctx->target_pos[0],
+ framenr,
+ ctx,
+ ctx->stab->target_pos[0]);
+ target_pos[1] = fetch_from_fcurve(ctx->target_pos[1],
+ framenr,
+ ctx,
+ ctx->stab->target_pos[1]);
+}
+
+static float get_animated_target_rot(StabContext *ctx, int framenr)
+{
+ return fetch_from_fcurve(ctx->target_rot,
+ framenr,
+ ctx,
+ ctx->stab->target_rot);
+}
+
+static float get_animated_target_scale(StabContext *ctx, int framenr)
+{
+ return fetch_from_fcurve(ctx->target_scale, framenr, ctx, ctx->stab->scale);
+}
+
+static float get_animated_weight(StabContext *ctx,
+ MovieTrackingTrack *track,
+ int framenr)
+{
+ TrackStabilizationBase *working_data =
+ access_stabilization_baseline_data(ctx, track);
+ if (working_data && working_data->track_weight_curve) {
+ int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(ctx->clip,
+ framenr);
+ return evaluate_fcurve(working_data->track_weight_curve, scene_framenr);
+ }
+ /* Use weight at global 'current frame' as fallback default. */
+ return track->weight_stab;
+}
+
+static void use_values_from_fcurves(StabContext *ctx, bool toggle)
+{
+ if (ctx != NULL) {
+ ctx->use_animation = toggle;
+ }
+}
+
+
+/* Prepare per call private working area.
+ * Used for access to possibly animated values: retrieve available F-curves.
*/
-static void stabilization_calculate_data(MovieTracking *tracking, int framenr, int width, int height,
- const float firstmedian[2], const float median[2],
- float translation[2], float *scale, float *angle)
+static StabContext *initialize_stabilization_working_context(MovieClip *clip)
{
- MovieTrackingStabilization *stab = &tracking->stabilization;
+ StabContext *ctx = MEM_callocN(sizeof(StabContext),
+ "2D stabilization animation runtime data");
+ ctx->clip = clip;
+ ctx->tracking = &clip->tracking;
+ ctx->stab = &clip->tracking.stabilization;
+ ctx->private_track_data = BLI_ghash_ptr_new(
+ "2D stabilization per track private working data");
+ ctx->locinf = retrieve_stab_animation(clip, "influence_location", 0);
+ ctx->rotinf = retrieve_stab_animation(clip, "influence_rotation", 0);
+ ctx->scaleinf = retrieve_stab_animation(clip, "influence_scale", 0);
+ ctx->target_pos[0] = retrieve_stab_animation(clip, "target_pos", 0);
+ ctx->target_pos[1] = retrieve_stab_animation(clip, "target_pos", 1);
+ ctx->target_rot = retrieve_stab_animation(clip, "target_rot", 0);
+ ctx->target_scale = retrieve_stab_animation(clip, "target_zoom", 0);
+ ctx->use_animation = true;
+ return ctx;
+}
- *scale = (stab->scale - 1.0f) * stab->scaleinf + 1.0f;
- *angle = 0.0f;
+/* Discard all private working data attached to this call context.
+ * NOTE: We allocate the record for the per track baseline contribution
+ * locally for each call context (i.e. call to
+ * BKE_tracking_stabilization_data_get()
+ * Thus it is correct to discard all allocations found within the
+ * corresponding _local_ GHash
+ */
+static void discard_stabilization_working_context(StabContext *ctx)
+{
+ if (ctx != NULL) {
+ BLI_ghash_free(ctx->private_track_data,
+ NULL,
+ discard_stabilization_baseline_data);
+ MEM_freeN(ctx);
+ }
+}
- translation[0] = (firstmedian[0] - median[0]) * width * (*scale);
- translation[1] = (firstmedian[1] - median[1]) * height * (*scale);
+static bool is_init_for_stabilization(StabContext *ctx,
+ MovieTrackingTrack *track)
+{
+ TrackStabilizationBase *working_data =
+ access_stabilization_baseline_data(ctx, track);
+ return (working_data != NULL && working_data->is_init_for_stabilization);
+}
+
+static bool is_usable_for_stabilization(StabContext *ctx,
+ MovieTrackingTrack *track)
+{
+ return (track->flag & TRACK_USE_2D_STAB) &&
+ is_init_for_stabilization(ctx, track);
+}
- mul_v2_fl(translation, stab->locinf);
+static bool is_effectively_disabled(StabContext *ctx,
+ MovieTrackingTrack *track,
+ MovieTrackingMarker *marker)
+{
+ return (marker->flag & MARKER_DISABLED) ||
+ (EPSILON_WEIGHT > get_animated_weight(ctx, track, marker->framenr));
+}
- if ((stab->flag & TRACKING_STABILIZE_ROTATION) && stab->rot_track && stab->rotinf) {
- MovieTrackingMarker *marker;
- float a[2], b[2];
- float x0 = (float)width / 2.0f, y0 = (float)height / 2.0f;
- float x = median[0] * width, y = median[1] * height;
- marker = BKE_tracking_marker_get(stab->rot_track, 1);
- sub_v2_v2v2(a, marker->pos, firstmedian);
- a[0] *= width;
- a[1] *= height;
+static int search_closest_marker_index(MovieTrackingTrack *track,
+ int ref_frame)
+{
+ MovieTrackingMarker *markers = track->markers;
+ int end = track->markersnr;
+ int i = track->last_marker;
+
+ i = MAX2(0, i);
+ i = MIN2(i, end - 1);
+ for ( ; i < end - 1 && markers[i].framenr <= ref_frame; ++i);
+ for ( ; 0 < i && markers[i].framenr > ref_frame; --i);
+
+ track->last_marker = i;
+ return i;
+}
+
+static void retrieve_next_higher_usable_frame(StabContext *ctx,
+ MovieTrackingTrack *track,
+ int i,
+ int ref_frame,
+ int *next_higher)
+{
+ MovieTrackingMarker *markers = track->markers;
+ int end = track->markersnr;
+ BLI_assert(0 <= i && i < end);
+
+ while (i < end &&
+ (markers[i].framenr < ref_frame ||
+ is_effectively_disabled(ctx, track, &markers[i])))
+ {
+ ++i;
+ }
+ if (i < end && markers[i].framenr < *next_higher) {
+ BLI_assert(markers[i].framenr >= ref_frame);
+ *next_higher = markers[i].framenr;
+ }
+}
+
+static void retrieve_next_lower_usable_frame(StabContext *ctx,
+ MovieTrackingTrack *track,
+ int i,
+ int ref_frame,
+ int *next_lower)
+{
+ MovieTrackingMarker *markers = track->markers;
+ BLI_assert(0 <= i && i < track->markersnr);
+ while (i >= 0 &&
+ (markers[i].framenr > ref_frame ||
+ is_effectively_disabled(ctx, track, &markers[i])))
+ {
+ --i;
+ }
+ if (0 <= i && markers[i].framenr > *next_lower) {
+ BLI_assert(markers[i].framenr <= ref_frame);
+ *next_lower = markers[i].framenr;
+ }
+}
+
+/* Find closest frames with usable stabilization data.
+ * A frame counts as _usable_ when there is at least one track marked for
+ * translation stabilization, which has an enabled tracking marker at this very
+ * frame. We search both for the next lower and next higher position, to allow
+ * the caller to interpolate gaps and to extrapolate at the ends of the
+ * definition range.
+ *
+ * NOTE: Regarding performance note that the individual tracks will cache the
+ * last search position.
+ */
+static void find_next_working_frames(StabContext *ctx,
+ int framenr,
+ int *next_lower,
+ int *next_higher)
+{
+ for (MovieTrackingTrack *track = ctx->tracking->tracks.first;
+ track != NULL;
+ track = track->next)
+ {
+ if (is_usable_for_stabilization(ctx, track)) {
+ int startpoint = search_closest_marker_index(track, framenr);
+ retrieve_next_higher_usable_frame(ctx,
+ track,
+ startpoint,
+ framenr,
+ next_higher);
+ retrieve_next_lower_usable_frame(ctx,
+ track,
+ startpoint,
+ framenr,
+ next_lower);
+ }
+ }
+}
+
- marker = BKE_tracking_marker_get(stab->rot_track, framenr);
- sub_v2_v2v2(b, marker->pos, median);
- b[0] *= width;
- b[1] *= height;
+/* Find active (enabled) marker closest to the reference frame. */
+static MovieTrackingMarker *get_closest_marker(StabContext *ctx,
+ MovieTrackingTrack *track,
+ int ref_frame)
+{
+ if (track->markersnr > 0) {
+ int next_lower = MINAFRAME;
+ int next_higher = MAXFRAME;
+ int i = search_closest_marker_index(track, ref_frame);
+ retrieve_next_higher_usable_frame(ctx, track, i, ref_frame, &next_higher);
+ retrieve_next_lower_usable_frame(ctx, track, i, ref_frame, &next_lower);
+
+ if ((next_higher - ref_frame) < (ref_frame - next_lower)) {
+ return BKE_tracking_marker_get_exact(track, next_higher);
+ }
+ else {
+ return BKE_tracking_marker_get_exact(track, next_lower);
+ }
+ }
+ return NULL;
+}
- *angle = -atan2f(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]);
- *angle *= stab->rotinf;
- /* convert to rotation around image center */
- translation[0] -= (x0 + (x - x0) * cosf(*angle) - (y - y0) * sinf(*angle) - x) * (*scale);
- translation[1] -= (y0 + (x - x0) * sinf(*angle) + (y - y0) * cosf(*angle) - y) * (*scale);
+/* Retrieve tracking data, if available and applicable for this frame.
+ * The returned weight value signals the validity; data recorded for this
+ * tracking marker on the exact requested frame is output with the full weight
+ * of this track, while gaps in the data sequence cause the weight to go to zero.
+ */
+static MovieTrackingMarker *get_tracking_data_point(
+ StabContext *ctx,
+ MovieTrackingTrack *track,
+ int framenr,
+ float *weight)
+{
+ MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
+ if (marker && marker->framenr == framenr && !(marker->flag & MARKER_DISABLED)) {
+ *weight = get_animated_weight(ctx, track, framenr);
+ return marker;
+ }
+ else {
+ /* no marker at this frame (=gap) or marker disabled */
+ *weight = 0.0f;
+ return NULL;
}
}
-/* Calculate factor of a scale, which will eliminate black areas
- * appearing on the frame caused by frame translation.
+/* Calculate the contribution of a single track at the time position (frame) of
+ * the given marker. Each track has a local reference frame, which is as close
+ * as possible to the global anchor_frame. Thus the translation contribution is
+ * comprised of the offset relative to the image position at that reference
+ * frame, plus a guess of the contribution for the time span between the
+ * anchor_frame and the local reference frame of this track. The constant part
+ * of this contribution is precomputed initially. At the anchor_frame, by
+ * definition the contribution of all tracks is zero, keeping the frame in place.
+ *
+ * track_ref is per track baseline contribution at reference frame; filled in at
+ * initialization
+ * marker is tracking data to use as contribution for current frame.
+ * result_offset is a total cumulated contribution of this track,
+ * relative to the stabilization anchor_frame,
+ * in normalized (0...1) coordinates.
+ */
+static void translation_contribution(TrackStabilizationBase *track_ref,
+ MovieTrackingMarker *marker,
+ float result_offset[2])
+{
+ add_v2_v2v2(result_offset,
+ track_ref->stabilization_offset_base,
+ marker->pos);
+}
+
+/* Similar to the ::translation_contribution(), the rotation contribution is
+ * comprised of the contribution by this individual track, and the averaged
+ * contribution from anchor_frame to the ref point of this track.
+ * - Contribution is in terms of angles, -pi < angle < +pi, and all averaging
+ * happens in this domain.
+ * - Yet the actual measurement happens as vector between pivot and the current
+ * tracking point
+ * - Currently we use the center of frame as approximation for the rotation pivot
+ * point.
+ * - Moreover, the pivot point has to be compensated for the already determined
+ * shift offset, in order to get the pure rotation around the pivot.
+ * To turn this into a _contribution_, the likewise corrected angle at the
+ * reference frame has to be subtracted, to get only the pure angle difference
+ * this tracking point has captured.
+ * - To get from vectors to angles, we have to go through an arcus tangens,
+ * which involves the issue of the definition range: the resulting angles will
+ * flip by 360deg when the measured vector passes from the 2nd to the third
+ * quadrant, thus messing up the average calculation. Since _any_ tracking
+ * point might be used, these problems are quite common in practice.
+ * - Thus we perform the subtraction of the reference and the addition of the
+ * baseline contribution in polar coordinates as simple addition of angles;
+ * since these parts are fixed, we can bake them into a rotation matrix.
+ * With this approach, the border of the arcus tangens definition range will
+ * be reached only, when the _whole_ contribution approaches +- 180deg,
+ * meaning we've already tilted the frame upside down. This situation is way
+ * less common and can be tolerated.
+ * - As an additional feature, when activated, also changes in image scale
+ * relative to the rotation center can be picked up. To handle those values
+ * in the same framework, we average the scales as logarithms.
+ *
+ * aspect is a total aspect ratio of the undistorted image (includes fame and
+ * pixel aspect).
+ */
+static void rotation_contribution(TrackStabilizationBase *track_ref,
+ MovieTrackingMarker *marker,
+ float aspect,
+ float target_pos[2],
+ float averaged_translation_contribution[2],
+ float *result_angle,
+ float *result_scale)
+{
+ float len;
+ float pos[2];
+ float pivot[2];
+ copy_v2_fl(pivot, 0.5f); /* Use center of frame as hard wired pivot. */
+ add_v2_v2(pivot, averaged_translation_contribution);
+ sub_v2_v2(pivot, target_pos);
+ sub_v2_v2v2(pos, marker->pos, pivot);
+
+ pos[0] *= aspect;
+ mul_m2v2(track_ref->stabilization_rotation_base, pos);
+
+ *result_angle = atan2f(pos[1],pos[0]);
+
+ len = len_v2(pos) + SCALE_ERROR_LIMIT_BIAS;
+ *result_scale = len * track_ref->stabilization_scale_base;
+ BLI_assert(0.0 < *result_scale);
+}
+
+
+/* Weighted average of the per track cumulated contributions at given frame.
+ * Returns truth if all desired calculations could be done and all averages are
+ * available.
+ *
+ * NOTE: Even if the result is not `true`, the returned translation and angle
+ * are always sensible and as good as can be. Especially in the
+ * initialization phase we might not be able to get any average (yet) or
+ * get only a translation value. Since initialization visits tracks in a
+ * specific order, starting from anchor_frame, the result is logically
+ * correct non the less. But under normal operation conditions,
+ * a result of `false` should disable the stabilization function
*/
-static float stabilization_calculate_autoscale_factor(MovieTracking *tracking, int width, int height)
+static bool average_track_contributions(StabContext *ctx,
+ int framenr,
+ float aspect,
+ float r_translation[2],
+ float *r_angle,
+ float *r_scale_step)
{
- float firstmedian[2];
+ bool ok;
+ float weight_sum;
+ MovieTrackingTrack *track;
+ MovieTracking *tracking = ctx->tracking;
MovieTrackingStabilization *stab = &tracking->stabilization;
- float aspect = tracking->camera.pixel_aspect;
-
- /* Early output if stabilization data is already up-to-date. */
- if (stab->ok)
- return stab->scale;
-
- /* See comment in BKE_tracking_stabilization_data_get about first frame. */
- if (stabilization_median_point_get(tracking, 1, firstmedian)) {
- int sfra = INT_MAX, efra = INT_MIN, cfra;
- float scale = 1.0f;
- MovieTrackingTrack *track;
-
- stab->scale = 1.0f;
-
- /* Calculate frame range of tracks used for stabilization. */
- track = tracking->tracks.first;
- while (track) {
- if (track->flag & TRACK_USE_2D_STAB ||
- ((stab->flag & TRACKING_STABILIZE_ROTATION) && track == stab->rot_track))
- {
- sfra = min_ii(sfra, track->markers[0].framenr);
- efra = max_ii(efra, track->markers[track->markersnr - 1].framenr);
- }
+ BLI_assert(stab->flag & TRACKING_2D_STABILIZATION);
- track = track->next;
+ zero_v2(r_translation);
+ *r_scale_step = 0.0f; /* logarithm */
+ *r_angle = 0.0f;
+
+ ok = false;
+ weight_sum = 0.0f;
+ for (track = tracking->tracks.first; track; track = track->next) {
+ if (!is_init_for_stabilization(ctx, track)) {
+ continue;
+ }
+ if (track->flag & TRACK_USE_2D_STAB) {
+ float weight = 0.0f;
+ MovieTrackingMarker *marker = get_tracking_data_point(ctx,
+ track,
+ framenr,
+ &weight);
+ if (marker) {
+ TrackStabilizationBase *stabilization_base =
+ access_stabilization_baseline_data(ctx, track);
+ BLI_assert(stabilization_base != NULL);
+ float offset[2];
+ weight_sum += weight;
+ translation_contribution(stabilization_base, marker, offset);
+ mul_v2_fl(offset, weight);
+ add_v2_v2(r_translation, offset);
+ ok |= (weight_sum > EPSILON_WEIGHT);
+ }
}
+ }
+ if (!ok) {
+ return false;
+ }
- /* For every frame we calculate scale factor needed to eliminate black
- * area and choose largest scale factor as final one.
- */
- for (cfra = sfra; cfra <= efra; cfra++) {
- float median[2];
- float translation[2], angle, tmp_scale;
- int i;
- float mat[4][4];
- float points[4][2] = {{0.0f, 0.0f}, {0.0f, height}, {width, height}, {width, 0.0f}};
- float si, co;
+ r_translation[0] /= weight_sum;
+ r_translation[1] /= weight_sum;
- stabilization_median_point_get(tracking, cfra, median);
+ if (!(stab->flag & TRACKING_STABILIZE_ROTATION)) {
+ return ok;
+ }
- stabilization_calculate_data(tracking, cfra, width, height, firstmedian, median, translation,
- &tmp_scale, &angle);
+ ok = false;
+ weight_sum = 0.0f;
+ for (track = tracking->tracks.first; track; track = track->next) {
+ if (!is_init_for_stabilization(ctx, track)) {
+ continue;
+ }
+ if (track->flag & TRACK_USE_2D_STAB_ROT) {
+ float weight = 0.0f;
+ MovieTrackingMarker *marker = get_tracking_data_point(ctx,
+ track,
+ framenr,
+ &weight);
+ if (marker) {
+ TrackStabilizationBase *stabilization_base =
+ access_stabilization_baseline_data(ctx, track);
+ BLI_assert(stabilization_base != NULL);
+ float rotation, scale;
+ float target_pos[2];
+ weight_sum += weight;
+ get_animated_target_pos(ctx, framenr, target_pos);
+ rotation_contribution(stabilization_base,
+ marker,
+ aspect,
+ target_pos,
+ r_translation,
+ &rotation,
+ &scale);
+ *r_angle += rotation * weight;
+ if (stab->flag & TRACKING_STABILIZE_SCALE) {
+ *r_scale_step += logf(scale) * weight;
+ }
+ else {
+ *r_scale_step = 0;
+ }
+ ok |= (weight_sum > EPSILON_WEIGHT);
+ }
+ }
+ }
+ if (ok) {
+ *r_scale_step /= weight_sum;
+ *r_angle /= weight_sum;
+ }
+ else {
+ /* We reach this point because translation could be calculated,
+ * but rotation/scale found no data to work on.
+ */
+ *r_scale_step = 0.0f;
+ *r_angle = 0.0f;
+ }
+ return true;
+}
- BKE_tracking_stabilization_data_to_mat4(width, height, aspect, translation, 1.0f, angle, mat);
- si = sinf(angle);
- co = cosf(angle);
+/* Linear interpolation of data retrieved at two measurement points.
+ * This function is used to fill gaps in the middle of the covered area,
+ * at frames without any usable tracks for stabilization.
+ *
+ * framenr is a position to interpolate for.
+ * frame_a is a valid measurement point below framenr
+ * frame_b is a valid measurement point above framenr
+ * Returns truth if both measurements could actually be retrieved.
+ * Otherwise output parameters remain unaltered
+ */
+static bool interpolate_averaged_track_contributions(StabContext *ctx,
+ int framenr,
+ int frame_a,
+ int frame_b,
+ float aspect,
+ float translation[2],
+ float *r_angle,
+ float *r_scale_step)
+{
+ float t, s;
+ float trans_a[2], trans_b[2];
+ float angle_a, angle_b;
+ float scale_a, scale_b;
+ bool success = false;
+
+ BLI_assert(frame_a <= frame_b);
+ BLI_assert(frame_a <= framenr);
+ BLI_assert(framenr <= frame_b);
+
+ t = ((float)framenr - frame_a) / (frame_b - frame_a);
+ s = 1.0f - t;
+
+ success = average_track_contributions(ctx, frame_a, aspect, trans_a, &angle_a, &scale_a);
+ if (!success) {
+ return false;
+ }
+ success = average_track_contributions(ctx, frame_b, aspect, trans_b, &angle_b, &scale_b);
+ if (!success) {
+ return false;
+ }
- for (i = 0; i < 4; i++) {
- int j;
- float a[3] = {0.0f, 0.0f, 0.0f}, b[3] = {0.0f, 0.0f, 0.0f};
+ interp_v2_v2v2(translation, trans_a, trans_b, t);
+ *r_scale_step = s * scale_a + t * scale_b;
+ *r_angle = s * angle_a + t * angle_b;
+ return true;
+}
- copy_v3_v3(a, points[i]);
- copy_v3_v3(b, points[(i + 1) % 4]);
- mul_m4_v3(mat, a);
- mul_m4_v3(mat, b);
+/* Reorder tracks starting with those providing a tracking data frame
+ * closest to the global anchor_frame. Tracks with a gap at anchor_frame or
+ * starting farer away from anchor_frame altogether will be visited later.
+ * This allows to build up baseline contributions incrementally.
+ *
+ * order is an array for sorting the tracks. Must be of suitable size to hold
+ * all tracks.
+ * Returns number of actually usable tracks, can be less than the overall number
+ * of tracks.
+ *
+ * NOTE: After returning, the order array holds entries up to the number of
+ * usable tracks, appropriately sorted starting with the closest tracks.
+ * Initialization includes disabled tracks, since they might be enabled
+ * through automation later.
+ */
+static int establish_track_initialization_order(StabContext *ctx,
+ TrackInitOrder *order)
+{
+ size_t tracknr = 0;
+ MovieTrackingTrack *track;
+ MovieTracking *tracking = ctx->tracking;
+ int anchor_frame = tracking->stabilization.anchor_frame;
- for (j = 0; j < 4; j++) {
- float point[3] = {points[j][0], points[j][1], 0.0f};
- float v1[3], v2[3];
+ for (track = tracking->tracks.first; track != NULL; track = track->next) {
+ MovieTrackingMarker *marker;
+ order[tracknr].data = track;
+ marker = get_closest_marker(ctx, track, anchor_frame);
+ if (marker != NULL &&
+ (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)))
+ {
+ order[tracknr].sort_value = abs(marker->framenr - anchor_frame);
+ order[tracknr].reference_frame = marker->framenr;
+ ++tracknr;
+ }
+ }
+ if (tracknr) {
+ qsort(order, tracknr, sizeof(TrackInitOrder), BLI_sortutil_cmp_int);
+ }
+ return tracknr;
+}
- sub_v3_v3v3(v1, b, a);
- sub_v3_v3v3(v2, point, a);
- if (cross_v2v2(v1, v2) >= 0.0f) {
- const float rotDx[4][2] = {{1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}};
- const float rotDy[4][2] = {{0.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}};
+/* Setup the constant part of this track's contribution to the determined frame
+ * movement. Tracks usually don't provide tracking data for every frame. Thus,
+ * for determining data at a given frame, we split up the contribution into a
+ * part covered by actual measurements on this track, and the initial gap
+ * between this track's reference frame and the global anchor_frame.
+ * The (missing) data for the gap can be substituted by the average offset
+ * observed by the other tracks covering the gap. This approximation doesn't
+ * introduce wrong data, but it records data with incorrect weight. A totally
+ * correct solution would require us to average the contribution per frame, and
+ * then integrate stepwise over all frames -- which of course would be way more
+ * expensive, especially for longer clips. To the contrary, our solution
+ * cumulates the total contribution per track and averages afterwards over all
+ * tracks; it can thus be calculated just based on the data of a single frame,
+ * plus the "baseline" for the reference frame, which is what we are computing
+ * here.
+ *
+ * Since we're averaging _contributions_, we have to calculate the _difference_
+ * of the measured position at current frame and the position at the reference
+ * frame. But the "reference" part of this difference is constant and can thus
+ * be packed together with the baseline contribution into a single precomputed
+ * vector per track.
+ *
+ * In case of the rotation contribution, the principle is the same, but we have
+ * to compensate for the already determined translation and measure the pure
+ * rotation, simply because this is how we model the offset: shift plus rotation
+ * around the shifted rotation center. To circumvent problems with the
+ * definition range of the arcus tangens function, we perform this baseline
+ * addition and reference angle subtraction in polar coordinates and bake this
+ * operation into a precomputed rotation matrix.
+ *
+ * track is a track to be initialized to initialize
+ * reference_frame is a local frame for this track, the closest pick to the
+ * global anchor_frame.
+ * aspect is a total aspect ratio of the undistorted image (includes fame and
+ * pixel aspect).
+ * target_pos is a possibly animated target position as set by the user for
+ * the reference_frame
+ * average_translation is a value observed by the _other_ tracks for the gap
+ * between reference_frame and anchor_frame. This
+ * average must not contain contributions of frames
+ * not yet initialized
+ * average_angle in a similar way, the rotation value observed by the
+ * _other_ tracks.
+ * average_scale_step is an image scale factor observed on average by the other
+ * tracks for this frame. This value is recorded and
+ * averaged as logarithm. The recorded scale changes
+ * are damped for very small contributions, to limit
+ * the effect of probe points approaching the pivot
+ * too closely.
+ *
+ * NOTE: when done, this track is marked as initialized
+ */
+static void initialize_track_for_stabilization(StabContext *ctx,
+ MovieTrackingTrack *track,
+ int reference_frame,
+ float aspect,
+ const float target_pos[2],
+ const float average_translation[2],
+ float average_angle,
+ float average_scale_step)
+{
+ float pos[2], angle, len;
+ float pivot[2];
+ TrackStabilizationBase *local_data =
+ access_stabilization_baseline_data(ctx, track);
+ MovieTrackingMarker *marker =
+ BKE_tracking_marker_get_exact(track, reference_frame);
+ /* Logic for initialization order ensures there *is* a marker on that
+ * very frame.
+ */
+ BLI_assert(marker != NULL);
+ BLI_assert(local_data != NULL);
- float dx = translation[0] * rotDx[j][0] + translation[1] * rotDx[j][1],
- dy = translation[0] * rotDy[j][0] + translation[1] * rotDy[j][1];
+ /* Per track baseline value for translation. */
+ sub_v2_v2v2(local_data->stabilization_offset_base,
+ average_translation,
+ marker->pos);
- float w, h, E, F, G, H, I, J, K, S;
+ /* Per track baseline value for rotation. */
+ copy_v2_fl(pivot, 0.5f); /* Use center of frame as hard wired pivot. */
+ add_v2_v2(pivot, average_translation);
+ sub_v2_v2(pivot, target_pos);
+ sub_v2_v2v2(pos, marker->pos, pivot);
- if (j % 2) {
- w = (float)height / 2.0f;
- h = (float)width / 2.0f;
- }
- else {
- w = (float)width / 2.0f;
- h = (float)height / 2.0f;
- }
+ pos[0] *= aspect;
+ angle = average_angle - atan2f(pos[1],pos[0]);
+ rotate_m2(local_data->stabilization_rotation_base, angle);
- E = -w * co + h * si;
- F = -h * co - w * si;
+ /* Per track baseline value for zoom. */
+ len = len_v2(pos) + SCALE_ERROR_LIMIT_BIAS;
+ local_data->stabilization_scale_base = expf(average_scale_step) / len;
- if ((i % 2) == (j % 2)) {
- G = -w * co - h * si;
- H = h * co - w * si;
- }
- else {
- G = w * co + h * si;
- H = -h * co + w * si;
- }
+ local_data->is_init_for_stabilization = true;
+}
- I = F - H;
- J = G - E;
- K = G * F - E * H;
- S = (-w * I - h * J) / (dx * I + dy * J + K);
+static void initialize_all_tracks(StabContext *ctx, float aspect)
+{
+ size_t i, track_cnt = 0;
+ MovieClip *clip = ctx->clip;
+ MovieTracking *tracking = ctx->tracking;
+ MovieTrackingTrack *track;
+ TrackInitOrder *order;
- scale = max_ff(scale, S);
- }
- }
- }
+ /* Attempt to start initialization at anchor_frame.
+ * By definition, offset contribution is zero there.
+ */
+ int reference_frame = tracking->stabilization.anchor_frame;
+ float average_angle=0, average_scale_step=0;
+ float average_translation[2];
+ float target_pos_at_ref_frame[2];
+ zero_v2(target_pos_at_ref_frame);
+ zero_v2(average_translation);
+
+ /* Initialize private working data. */
+ for (track = tracking->tracks.first; track != NULL; track = track->next) {
+ TrackStabilizationBase *local_data =
+ access_stabilization_baseline_data(ctx, track);
+ if (!local_data) {
+ local_data = MEM_callocN(sizeof(TrackStabilizationBase),
+ "2D stabilization per track baseline data");
+ attach_stabilization_baseline_data(ctx, track, local_data);
}
+ BLI_assert(local_data != NULL);
+ local_data->track_weight_curve = retrieve_track_weight_animation(clip,
+ track);
+ local_data->is_init_for_stabilization = false;
- stab->scale = scale;
+ ++track_cnt;
+ }
+ if (!track_cnt) {
+ return;
+ }
- if (stab->maxscale > 0.0f)
- stab->scale = min_ff(stab->scale, stab->maxscale);
+ order = MEM_mallocN(track_cnt * sizeof(TrackInitOrder),
+ "stabilization track order");
+ if (!order) {
+ return;
}
- else {
- stab->scale = 1.0f;
+
+ track_cnt = establish_track_initialization_order(ctx, order);
+ if (track_cnt == 0) {
+ goto cleanup;
}
- stab->ok = true;
+ for (i = 0; i < track_cnt; ++i) {
+ track = order[i].data;
+ if (reference_frame != order[i].reference_frame) {
+ reference_frame = order[i].reference_frame;
+ average_track_contributions(ctx,
+ reference_frame,
+ aspect,
+ average_translation,
+ &average_angle,
+ &average_scale_step);
+ get_animated_target_pos(ctx,
+ reference_frame,
+ target_pos_at_ref_frame);
+ }
+ initialize_track_for_stabilization(ctx,
+ track,
+ reference_frame,
+ aspect,
+ target_pos_at_ref_frame,
+ average_translation,
+ average_angle,
+ average_scale_step);
+ }
- return stab->scale;
+cleanup:
+ MEM_freeN(order);
}
-/* Get stabilization data (translation, scaling and angle) for a given frame.
+
+/* Retrieve the measurement of frame movement by averaging contributions of
+ * active tracks.
*
- * NOTE: frame number should be in clip space, not scene space
+ * translation is a measurement in normalized 0..1 coordinates.
+ * angle is a measurement in radians -pi..+pi counter clockwise relative to
+ * translation compensated frame center
+ * scale_step is a measurement of image scale changes, in logarithmic scale
+ * (zero means scale == 1)
+ * Returns calculation enabled and all data retrieved as expected for this frame.
+ *
+ * NOTE: when returning `false`, output parameters are reset to neutral values.
*/
-void BKE_tracking_stabilization_data_get(MovieTracking *tracking, int framenr, int width, int height,
- float translation[2], float *scale, float *angle)
+static bool stabilization_determine_offset_for_frame(StabContext *ctx,
+ int framenr,
+ float aspect,
+ float r_translation[2],
+ float *r_angle,
+ float *r_scale_step)
{
- float firstmedian[2], median[2];
- MovieTrackingStabilization *stab = &tracking->stabilization;
+ bool success = false;
/* Early output if stabilization is disabled. */
- if ((stab->flag & TRACKING_2D_STABILIZATION) == 0) {
- zero_v2(translation);
- *scale = 1.0f;
- *angle = 0.0f;
+ if ((ctx->stab->flag & TRACKING_2D_STABILIZATION) == 0) {
+ zero_v2(r_translation);
+ *r_scale_step = 0.0f;
+ *r_angle = 0.0f;
+ return false;
+ }
- return;
+ success = average_track_contributions(ctx,
+ framenr,
+ aspect,
+ r_translation,
+ r_angle,
+ r_scale_step);
+ if (!success) {
+ /* Try to hold extrapolated settings beyond the definition range
+ * and to interpolate in gaps without any usable tracking data
+ * to prevent sudden jump to image zero position.
+ */
+ int next_lower = MINAFRAME;
+ int next_higher = MAXFRAME;
+ use_values_from_fcurves(ctx, true);
+ find_next_working_frames(ctx, framenr, &next_lower, &next_higher);
+ if (next_lower >= MINFRAME && next_higher < MAXFRAME) {
+ success = interpolate_averaged_track_contributions(ctx,
+ framenr,
+ next_lower,
+ next_higher,
+ aspect,
+ r_translation,
+ r_angle,
+ r_scale_step);
+ }
+ else if (next_higher < MAXFRAME) {
+ /* Before start of stabilized range: extrapolate start point
+ * settings.
+ */
+ success = average_track_contributions(ctx,
+ next_higher,
+ aspect,
+ r_translation,
+ r_angle,
+ r_scale_step);
+ }
+ else if (next_lower >= MINFRAME) {
+ /* After end of stabilized range: extrapolate end point settings. */
+ success = average_track_contributions(ctx,
+ next_lower,
+ aspect,
+ r_translation,
+ r_angle,
+ r_scale_step);
+ }
+ use_values_from_fcurves(ctx, false);
}
+ return success;
+}
- /* Even if tracks does not start at frame 1, their position will
- * be estimated at this frame, which will give reasonable result
- * in most of cases.
- *
- * However, it's still better to replace this with real first
- * frame number at which tracks are appearing.
+/* Calculate stabilization data (translation, scale and rotation) from given raw
+ * measurements. Result is in absolute image dimensions (expanded image, square
+ * pixels), includes automatic or manual scaling and compensates for a target
+ * frame position, if given.
+ *
+ * size is a size of the expanded image, the width in pixels is size * aspect.
+ * aspect is a ratio (width / height) of the effective canvas (square pixels).
+ * do_compensate denotes whether to actually output values necessary to
+ * _compensate_ the determined frame movement.
+ * Otherwise, the effective target movement is returned.
+ */
+static void stabilization_calculate_data(StabContext *ctx,
+ int framenr,
+ int size,
+ float aspect,
+ bool do_compensate,
+ float scale_step,
+ float r_translation[2],
+ float *r_scale,
+ float *r_angle)
+{
+ float target_pos[2];
+ float scaleinf = get_animated_scaleinf(ctx, framenr);
+
+ *r_scale = (get_animated_target_scale(ctx,framenr) - 1.0f) * scaleinf + 1.0f;
+
+ if (ctx->stab->flag & TRACKING_STABILIZE_SCALE) {
+ *r_scale *= expf(scale_step * scaleinf); /* Averaged in log scale */
+ }
+
+ mul_v2_fl(r_translation, get_animated_locinf(ctx, framenr));
+ *r_angle *= get_animated_rotinf(ctx, framenr);
+
+ /* Compensate for a target frame position.
+ * This allows to follow tracking / panning shots in a semi manual fashion,
+ * when animating the settings for the target frame position.
*/
- if (stabilization_median_point_get(tracking, 1, firstmedian)) {
- stabilization_median_point_get(tracking, framenr, median);
+ get_animated_target_pos(ctx, framenr, target_pos);
+ sub_v2_v2(r_translation, target_pos);
+ *r_angle -= get_animated_target_rot(ctx,framenr);
- if ((stab->flag & TRACKING_AUTOSCALE) == 0)
- stab->scale = 1.0f;
+ /* Convert from relative to absolute coordinates, square pixels. */
+ r_translation[0] *= (float)size * aspect;
+ r_translation[1] *= (float)size;
+
+ /* Output measured data, or inverse of the measured values for
+ * compensation?
+ */
+ if (do_compensate) {
+ mul_v2_fl(r_translation, -1.0f);
+ *r_angle *= -1.0f;
+ if (*r_scale != 0.0f) {
+ *r_scale = 1.0f / *r_scale;
+ }
+ }
+}
- if (!stab->ok) {
- if (stab->flag & TRACKING_AUTOSCALE)
- stabilization_calculate_autoscale_factor(tracking, width, height);
- stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median,
- translation, scale, angle);
+/* Determine the inner part of the frame, which is always safe to use.
+ * When enlarging the image by the inverse of this factor, any black areas
+ * appearing due to frame translation and rotation will be removed.
+ *
+ * NOTE: When calling this function, basic initialization of tracks must be
+ * done already
+ */
+static void stabilization_determine_safe_image_area(StabContext *ctx,
+ int size,
+ float image_aspect)
+{
+ MovieTrackingStabilization *stab = ctx->stab;
+ float pixel_aspect = ctx->tracking->camera.pixel_aspect;
- stab->ok = true;
+ int sfra = INT_MAX, efra = INT_MIN, cfra;
+ float scale = 1.0f, scale_step = 0.0f;
+ MovieTrackingTrack *track;
+ stab->scale = 1.0f;
+
+ /* Calculate maximal frame range of tracks where stabilization is active. */
+ for (track = ctx->tracking->tracks.first; track; track = track->next) {
+ if ((track->flag & TRACK_USE_2D_STAB) ||
+ ((stab->flag & TRACKING_STABILIZE_ROTATION) &&
+ (track->flag & TRACK_USE_2D_STAB_ROT)))
+ {
+ int first_frame = track->markers[0].framenr;
+ int last_frame = track->markers[track->markersnr - 1].framenr;
+ sfra = min_ii(sfra, first_frame);
+ efra = max_ii(efra, last_frame);
}
- else {
- stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median,
- translation, scale, angle);
+ }
+
+ /* For every frame we calculate scale factor needed to eliminate black border area
+ * and choose largest scale factor as final one.
+ */
+ for (cfra = sfra; cfra <= efra; cfra++) {
+ float translation[2], angle, tmp_scale;
+ int i;
+ float mat[4][4];
+ float points[4][2] = {{0.0f, 0.0f},
+ {0.0f, size},
+ {image_aspect * size, size},
+ {image_aspect * size, 0.0f}};
+ float si, co;
+ bool do_compensate = true;
+
+ stabilization_determine_offset_for_frame(ctx,
+ cfra,
+ image_aspect,
+ translation,
+ &angle,
+ &scale_step);
+ stabilization_calculate_data(ctx,
+ cfra,
+ size,
+ image_aspect,
+ do_compensate,
+ scale_step,
+ translation,
+ &tmp_scale,
+ &angle);
+
+ BKE_tracking_stabilization_data_to_mat4(size * image_aspect,
+ size,
+ pixel_aspect,
+ translation,
+ 1.0f,
+ angle,
+ mat);
+
+ si = sinf(angle);
+ co = cosf(angle);
+
+ /* Investigate the transformed border lines for this frame;
+ * find out, where it cuts the original frame.
+ */
+ for (i = 0; i < 4; i++) {
+ int j;
+ float a[3] = {0.0f, 0.0f, 0.0f},
+ b[3] = {0.0f, 0.0f, 0.0f};
+
+ copy_v2_v2(a, points[i]);
+ copy_v2_v2(b, points[(i + 1) % 4]);
+ a[2] = b[2] = 0.0f;
+
+ mul_m4_v3(mat, a);
+ mul_m4_v3(mat, b);
+
+ for (j = 0; j < 4; j++) {
+ float point[3] = {points[j][0], points[j][1], 0.0f};
+ float v1[3], v2[3];
+
+ sub_v3_v3v3(v1, b, a);
+ sub_v3_v3v3(v2, point, a);
+
+ if (cross_v2v2(v1, v2) >= 0.0f) {
+ const float rot_dx[4][2] = {{1.0f, 0.0f},
+ {0.0f, -1.0f},
+ {-1.0f, 0.0f},
+ {0.0f, 1.0f}};
+ const float rot_dy[4][2] = {{0.0f, 1.0f},
+ {1.0f, 0.0f},
+ {0.0f, -1.0f},
+ {-1.0f, 0.0f}};
+
+ float dx = translation[0] * rot_dx[j][0] +
+ translation[1] * rot_dx[j][1],
+ dy = translation[0] * rot_dy[j][0] +
+ translation[1] * rot_dy[j][1];
+
+ float w, h, E, F, G, H, I, J, K, S;
+
+ if (j % 2) {
+ w = (float)size / 2.0f;
+ h = image_aspect*size / 2.0f;
+ }
+ else {
+ w = image_aspect*size / 2.0f;
+ h = (float)size / 2.0f;
+ }
+
+ E = -w * co + h * si;
+ F = -h * co - w * si;
+
+ if ((i % 2) == (j % 2)) {
+ G = -w * co - h * si;
+ H = h * co - w * si;
+ }
+ else {
+ G = w * co + h * si;
+ H = -h * co + w * si;
+ }
+
+ I = F - H;
+ J = G - E;
+ K = G * F - E * H;
+
+ S = (dx * I + dy * J + K) / (-w * I - h * J);
+
+ scale = min_ff(scale, S);
+ }
+ }
}
}
+
+ stab->scale = scale;
+
+ if (stab->maxscale > 0.0f) {
+ stab->scale = max_ff(stab->scale, 1.0f / stab->maxscale);
+ }
+}
+
+
+/* Prepare working data and determine reference point for each track.
+ *
+ * NOTE: These calculations _could_ be cached and reused for all frames of the
+ * same clip. However, since proper initialization depends on (weight)
+ * animation and setup of tracks, ensuring consistency of cached init data
+ * turns out to be tricky, hard to maintain and generally not worth the
+ * effort. Thus we'll re-initialize on every frame.
+ */
+static StabContext *init_stabilizer(MovieClip *clip, int width, int height)
+{
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingStabilization *stab = &tracking->stabilization;
+ float pixel_aspect = tracking->camera.pixel_aspect;
+ float aspect = (float)width * pixel_aspect / height;
+ int size = height;
+
+ StabContext *ctx = initialize_stabilization_working_context(clip);
+ BLI_assert(ctx != NULL);
+ initialize_all_tracks(ctx, aspect);
+ if (stab->flag & TRACKING_AUTOSCALE) {
+ stabilization_determine_safe_image_area(ctx, size, aspect);
+ }
+ /* By default, just use values for the global current frame. */
+ use_values_from_fcurves(ctx, false);
+ return ctx;
+}
+
+
+/* === public interface functions === */
+
+/* Get stabilization data (translation, scaling and angle) for a given frame.
+ * Returned data describes how to compensate the detected movement, but with any
+ * chosen scale factor already applied and any target frame position already
+ * compensated. In case stabilization fails or is disabled, neutral values are
+ * returned.
+ *
+ * framenr is a frame number, relative to the clip (not relative to the scene
+ * timeline)
+ * width is an effective width of the canvas (square pixels), used to scale the
+ * determined translation
+ *
+ * Outputs:
+ * - translation of the lateral shift, absolute canvas coordinates
+ * (square pixels).
+ * - scale of the scaling to apply
+ * - angle of the rotation angle, relative to the frame center
+ */
+/* TODO(sergey): Use r_ prefix for output parameters here. */
+void BKE_tracking_stabilization_data_get(MovieClip *clip,
+ int framenr,
+ int width,
+ int height,
+ float translation[2],
+ float *scale,
+ float *angle)
+{
+ StabContext *ctx = NULL;
+ MovieTracking *tracking = &clip->tracking;
+ bool enabled = (tracking->stabilization.flag & TRACKING_2D_STABILIZATION);
+ /* Might become a parameter of a stabilization compositor node. */
+ bool do_compensate = true;
+ float scale_step = 0.0f;
+ float pixel_aspect = tracking->camera.pixel_aspect;
+ float aspect = (float)width * pixel_aspect / height;
+ int size = height;
+
+ if (enabled) {
+ ctx = init_stabilizer(clip, width, height);
+ }
+
+ if (enabled &&
+ stabilization_determine_offset_for_frame(ctx,
+ framenr,
+ aspect,
+ translation,
+ angle,
+ &scale_step))
+ {
+ stabilization_calculate_data(ctx,
+ framenr,
+ size,
+ aspect,
+ do_compensate,
+ scale_step,
+ translation,
+ scale,
+ angle);
+ }
else {
zero_v2(translation);
*scale = 1.0f;
*angle = 0.0f;
}
+ discard_stabilization_working_context(ctx);
}
-/* Stabilize given image buffer using stabilization data for
- * a specified frame number.
+/* Stabilize given image buffer using stabilization data for a specified
+ * frame number.
*
- * NOTE: frame number should be in clip space, not scene space
+ * NOTE: frame number should be in clip space, not scene space.
*/
-ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf *ibuf,
- float translation[2], float *scale, float *angle)
+/* TODO(sergey): Use r_ prefix for output parameters here. */
+ImBuf *BKE_tracking_stabilize_frame(MovieClip *clip,
+ int framenr,
+ ImBuf *ibuf,
+ float translation[2],
+ float *scale,
+ float *angle)
{
float tloc[2], tscale, tangle;
+ MovieTracking *tracking = &clip->tracking;
MovieTrackingStabilization *stab = &tracking->stabilization;
ImBuf *tmpibuf;
int width = ibuf->x, height = ibuf->y;
- float aspect = tracking->camera.pixel_aspect;
+ float pixel_aspect = tracking->camera.pixel_aspect;
float mat[4][4];
int j, filter = tracking->stabilization.filter;
void (*interpolation)(struct ImBuf *, struct ImBuf *, float, float, int, int) = NULL;
@@ -349,8 +1363,12 @@ ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf
tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ibuf_flags);
/* Calculate stabilization matrix. */
- BKE_tracking_stabilization_data_get(tracking, framenr, width, height, tloc, &tscale, &tangle);
- BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, aspect, tloc, tscale, tangle, mat);
+ BKE_tracking_stabilization_data_get(clip, framenr, width, height, tloc, &tscale, &tangle);
+ BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, pixel_aspect, tloc, tscale, tangle, mat);
+
+ /* The following code visits each nominal target grid position
+ * and picks interpolated data "backwards" from source.
+ * thus we need the inverse of the transformation to apply. */
invert_m4(mat);
if (filter == TRACKING_FILTER_NEAREST)
@@ -397,48 +1415,61 @@ ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf
return tmpibuf;
}
-/* Get 4x4 transformation matrix which corresponds to
- * stabilization data and used for easy coordinate
- * transformation.
+
+/* Build a 4x4 transformation matrix based on the given 2D stabilization data.
+ * mat is a 4x4 matrix in homogeneous coordinates, adapted to the
+ * final image buffer size and compensated for pixel aspect ratio,
+ * ready for direct OpenGL drawing.
*
- * NOTE: The reason it is 4x4 matrix is because it's
- * used for OpenGL drawing directly.
+ * TODO(sergey): The signature of this function should be changed. we actually
+ * don't need the dimensions of the image buffer. Instead we
+ * should consider to provide the pivot point of the rotation as a
+ * further stabilization data parameter.
*/
-void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect,
+void BKE_tracking_stabilization_data_to_mat4(int buffer_width,
+ int buffer_height,
+ float pixel_aspect,
float translation[2], float scale, float angle,
float mat[4][4])
{
float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4],
- center_mat[4][4], inv_center_mat[4][4],
+ pivot_mat[4][4], inv_pivot_mat[4][4],
aspect_mat[4][4], inv_aspect_mat[4][4];
- float scale_vector[3] = {scale, scale, scale};
+ float scale_vector[3] = {scale, scale, 1.0f};
+
+ float pivot[2]; /* XXX this should be a parameter, it is part of the stabilization data */
+
+ /* Use the motion compensated image center as rotation center.
+ * This is not 100% correct, but reflects the way the rotation data was
+ * measured. Actually we'd need a way to find a good pivot, and use that
+ * both for averaging and for compensation.
+ */
+ /* TODO(sergey) pivot shouldn't be calculated here, rather received
+ * as a parameter.
+ */
+ pivot[0] = pixel_aspect * buffer_width / 2.0f - translation[0];
+ pivot[1] = (float)buffer_height / 2.0f - translation[1];
unit_m4(translation_mat);
unit_m4(rotation_mat);
unit_m4(scale_mat);
- unit_m4(center_mat);
unit_m4(aspect_mat);
+ unit_m4(pivot_mat);
+ unit_m4(inv_pivot_mat);
/* aspect ratio correction matrix */
- aspect_mat[0][0] = 1.0f / aspect;
+ aspect_mat[0][0] /= pixel_aspect;
invert_m4_m4(inv_aspect_mat, aspect_mat);
- /* image center as rotation center
- *
- * Rotation matrix is constructing in a way rotation happens around image center,
- * and it's matter of calculating translation in a way, that applying translation
- * after rotation would make it so rotation happens around median point of tracks
- * used for translation stabilization.
- */
- center_mat[3][0] = (float)width / 2.0f;
- center_mat[3][1] = (float)height / 2.0f;
- invert_m4_m4(inv_center_mat, center_mat);
+ add_v2_v2(pivot_mat[3], pivot);
+ sub_v2_v2(inv_pivot_mat[3], pivot);
size_to_mat4(scale_mat, scale_vector); /* scale matrix */
add_v2_v2(translation_mat[3], translation); /* translation matrix */
rotate_m4(rotation_mat, 'Z', angle); /* rotation matrix */
/* compose transformation matrix */
- mul_m4_series(mat, translation_mat, center_mat, aspect_mat, rotation_mat, inv_aspect_mat,
- scale_mat, inv_center_mat);
+ mul_m4_series(mat, aspect_mat, translation_mat,
+ pivot_mat, scale_mat, rotation_mat, inv_pivot_mat,
+ inv_aspect_mat);
}
diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h
index cb8cb6f5a0d..f51b9623803 100644
--- a/source/blender/blenlib/BLI_system.h
+++ b/source/blender/blenlib/BLI_system.h
@@ -21,15 +21,14 @@
#ifndef __BLI_SYSTEM_H__
#define __BLI_SYSTEM_H__
+#include <stdio.h>
+
/** \file BLI_system.h
* \ingroup bli
*/
int BLI_cpu_support_sse2(void);
-
-#if defined(NDEBUG) || !defined(__BLI_UTILDEFINES_H__)
void BLI_system_backtrace(FILE *fp);
-#endif
/* getpid */
#ifdef WIN32
diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h
index d504e503c68..746eb922c65 100644
--- a/source/blender/blenlib/BLI_utildefines.h
+++ b/source/blender/blenlib/BLI_utildefines.h
@@ -633,7 +633,7 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size);
* for aborting need to define WITH_ASSERT_ABORT
*/
#ifndef NDEBUG
-extern void BLI_system_backtrace(FILE *fp);
+# include "BLI_system.h"
# ifdef WITH_ASSERT_ABORT
# define _BLI_DUMMY_ABORT abort
# else
diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c
index 5d1bdd6d978..898075e651e 100644
--- a/source/blender/blenlib/intern/system.c
+++ b/source/blender/blenlib/intern/system.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include "BLI_utildefines.h"
#include "BLI_system.h"
#include "MEM_guardedalloc.h"
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 7eabcd18fb8..ded60af4af2 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 */
@@ -4742,7 +4774,7 @@ static void lib_link_object(FileData *fd, Main *main)
/* Only expand so as not to loose any object materials that might be set. */
if (totcol_data && (*totcol_data > ob->totcol)) {
/* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */
- BKE_material_resize_object(ob, *totcol_data, false);
+ BKE_material_resize_object(main, ob, *totcol_data, false);
}
}
@@ -5872,6 +5904,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) {
@@ -6154,7 +6202,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;
@@ -6162,11 +6211,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);
@@ -6179,9 +6236,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;
}
}
}
@@ -7418,7 +7478,7 @@ static void direct_link_movieclip(FileData *fd, MovieClip *clip)
clip->tracking_context = NULL;
clip->tracking.stats = NULL;
- clip->tracking.stabilization.ok = 0;
+ /* Needed for proper versioning, will be NULL for all newer files anyway. */
clip->tracking.stabilization.rot_track = newdataadr(fd, clip->tracking.stabilization.rot_track);
clip->tracking.dopesheet.ok = 0;
@@ -7884,6 +7944,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";
@@ -8135,6 +8196,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);
@@ -8328,6 +8392,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 */
@@ -9434,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);
@@ -9629,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 3e6b0d34ba6..d735f099dc0 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -63,6 +63,8 @@
#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "BKE_screen.h"
+#include "BKE_tracking.h"
+#include "BKE_gpencil.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
@@ -74,6 +76,26 @@
#include "MEM_guardedalloc.h"
+/**
+ * Setup rotation stabilization from ancient single track spec.
+ * Former Version of 2D stabilization used a single tracking marker to determine the rotation
+ * to be compensated. Now several tracks can contribute to rotation detection and this feature
+ * is enabled by the MovieTrackingTrack#flag on a per track base.
+ */
+static void migrate_single_rot_stabilization_track_settings(MovieTrackingStabilization *stab)
+{
+ if (stab->rot_track) {
+ if (!(stab->rot_track->flag & TRACK_USE_2D_STAB_ROT)) {
+ stab->tot_rot_track++;
+ stab->rot_track->flag |= TRACK_USE_2D_STAB_ROT;
+ }
+ }
+ stab->rot_track = NULL; /* this field is now ignored */
+
+ /* by default show the track lists expanded, to improve "discoverability" */
+ stab->flag |= TRACKING_SHOW_STAB_TRACKS;
+}
+
static void do_version_constraints_radians_degrees_270_1(ListBase *lb)
{
bConstraint *con;
@@ -1075,15 +1097,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 +1205,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);
@@ -1251,4 +1264,119 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+
+ 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 --------------- */
+ }
+
+ {
+ if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingTrack", "float", "weight_stab")) {
+ MovieClip *clip;
+ for (clip = main->movieclip.first; clip; clip = clip->id.next) {
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingObject *tracking_object;
+ for (tracking_object = tracking->objects.first;
+ tracking_object != NULL;
+ tracking_object = tracking_object->next)
+ {
+ ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
+ MovieTrackingTrack *track;
+ for (track = tracksbase->first;
+ track != NULL;
+ track = track->next)
+ {
+ track->weight_stab = track->weight;
+ }
+ }
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingStabilization", "int", "tot_rot_track")) {
+ MovieClip *clip;
+ for (clip = main->movieclip.first; clip != NULL; clip = clip->id.next) {
+ if (clip->tracking.stabilization.rot_track) {
+ migrate_single_rot_stabilization_track_settings(&clip->tracking.stabilization);
+ if (!clip->tracking.stabilization.scale) {
+ /* ensure init.
+ * Was previously used for autoscale only,
+ * now used always (as "target scale") */
+ clip->tracking.stabilization.scale = 1.0f;
+ }
+ }
+ }
+ }
+ }
}
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 ba783e08b39..b6a54715763 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"
@@ -2673,6 +2674,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);
@@ -2835,6 +2850,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) {
@@ -2861,6 +2877,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);
+ }
}
}
@@ -3862,6 +3883,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)
{
@@ -4059,6 +4095,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/compositor/nodes/COM_MovieClipNode.cpp b/source/blender/compositor/nodes/COM_MovieClipNode.cpp
index 933223dacac..b3f1b5a4458 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.cpp
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.cpp
@@ -91,7 +91,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, const Composit
if (stab->flag & TRACKING_2D_STABILIZATION) {
int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip, context.getFramenumber());
- BKE_tracking_stabilization_data_get(&movieClip->tracking, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle);
+ BKE_tracking_stabilization_data_get(movieClip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle);
}
}
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp
index 5ddf15f7684..41f7da7c49f 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp
@@ -47,7 +47,7 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4],
angle = 0.0f;
if (this->m_clip) {
int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber);
- BKE_tracking_stabilization_data_get(&this->m_clip->tracking, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle);
+ BKE_tracking_stabilization_data_get(this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle);
}
switch (this->m_attribute) {
case MCA_SCALE:
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 49b648c7dae..0945da439ef 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -42,6 +42,8 @@ struct Depsgraph;
struct Main;
struct Scene;
+struct Group;
+struct EffectorWeights;
#ifdef __cplusplus
extern "C" {
@@ -79,6 +81,7 @@ void DEG_scene_graph_free(struct Scene *scene);
*/
struct DepsNodeHandle;
+struct CacheFile;
struct Object;
typedef enum eDepsSceneComponentType {
@@ -100,15 +103,23 @@ typedef enum eDepsObjectComponentType {
DEG_OB_COMP_EVAL_PARTICLES, /* Particle Systems Component */
DEG_OB_COMP_SHADING, /* Material Shading Component */
+ DEG_OB_COMP_CACHE, /* Cache Component */
} eDepsObjectComponentType;
void DEG_add_scene_relation(struct DepsNodeHandle *node, struct Scene *scene, eDepsSceneComponentType component, const char *description);
void DEG_add_object_relation(struct DepsNodeHandle *node, struct Object *ob, eDepsObjectComponentType component, const char *description);
void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description);
+void DEG_add_object_cache_relation(struct DepsNodeHandle *handle, struct CacheFile *cache_file, eDepsObjectComponentType component, const char *description);
/* TODO(sergey): Remove once all geometry update is granular. */
void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short flag);
+/* Utility functions for physics modifiers */
+typedef bool (*DEG_CollobjFilterFunction)(struct Object *obj, struct ModifierData *md);
+
+void DEG_add_collision_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name);
+void DEG_add_forcefield_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name);
+
/* ************************************************ */
#ifdef __cplusplus
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index a397b48e19c..1812384440f 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"
@@ -339,6 +340,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(Scene *scene,
@@ -1259,4 +1268,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 6ee0b8406a1..f378f076804 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;
@@ -144,6 +145,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 42b8260c05a..2148a3501d8 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"
@@ -64,6 +65,7 @@ extern "C" {
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "DNA_world_types.h"
+#include "DNA_object_force.h"
#include "BKE_action.h"
#include "BKE_armature.h"
@@ -71,6 +73,7 @@ extern "C" {
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_effect.h"
+#include "BKE_collision.h"
#include "BKE_fcurve.h"
#include "BKE_group.h"
#include "BKE_key.h"
@@ -242,6 +245,69 @@ void DepsgraphRelationBuilder::add_operation_relation(
}
}
+void DepsgraphRelationBuilder::add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name)
+{
+ unsigned int numcollobj;
+ Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, eModifierType_Collision, dupli);
+
+ for (unsigned int i = 0; i < numcollobj; i++)
+ {
+ Object *ob1 = collobjs[i];
+
+ ComponentKey trf_key(&ob1->id, DEPSNODE_TYPE_TRANSFORM);
+ add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, name);
+
+ ComponentKey coll_key(&ob1->id, DEPSNODE_TYPE_GEOMETRY);
+ add_relation(coll_key, key, DEPSREL_TYPE_STANDARD, name);
+ }
+
+ if (collobjs)
+ MEM_freeN(collobjs);
+}
+
+void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name)
+{
+ ListBase *effectors = pdInitEffectors(scene, ob, psys, eff, false);
+
+ if (effectors) {
+ for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) {
+ if (eff->ob != ob) {
+ ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_TRANSFORM);
+ add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
+ }
+
+ if (eff->psys) {
+ if (eff->ob != ob) {
+ ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES);
+ add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
+
+ /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */
+ ComponentKey mod_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY);
+ add_relation(mod_key, key, DEPSREL_TYPE_STANDARD, name);
+ }
+ else if (eff->psys != psys) {
+ OperationKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PSYS_EVAL, eff->psys->name);
+ add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
+ }
+ }
+
+ if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
+ ComponentKey trf_key(&eff->pd->f_source->id, DEPSNODE_TYPE_TRANSFORM);
+ add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain");
+
+ ComponentKey eff_key(&eff->pd->f_source->id, DEPSNODE_TYPE_GEOMETRY);
+ add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain");
+ }
+
+ if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
+ add_collision_relations(key, scene, ob, NULL, eff->ob->lay, true, "Force Absorption");
+ }
+ }
+ }
+
+ pdEndEffectors(&effectors);
+}
+
/* **** Functions to build relations between entities **** */
void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene)
@@ -599,6 +665,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);
@@ -1124,20 +1202,13 @@ void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob)
}
#endif
- /* effectors */
- ListBase *effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
-
- if (effectors) {
- for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) {
- if (eff->psys) {
- // XXX: DAG_RL_DATA_DATA | DAG_RL_OB_DATA
- ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY); // xxx: particles instead?
- add_relation(eff_key, psys_key, DEPSREL_TYPE_STANDARD, "Particle Field");
- }
- }
+ /* collisions */
+ if (part->type != PART_HAIR) {
+ add_collision_relations(psys_key, scene, ob, part->collision_group, ob->lay, true, "Particle Collision");
}
- pdEndEffectors(&effectors);
+ /* effectors */
+ add_forcefield_relations(psys_key, scene, ob, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field");
/* boids */
if (part->boids) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index ce6d2c961fd..46e65d464a4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -63,6 +63,8 @@ struct bConstraint;
struct Scene;
struct Tex;
struct World;
+struct EffectorWeights;
+struct ParticleSystem;
struct PropertyRNA;
@@ -244,6 +246,9 @@ struct DepsgraphRelationBuilder
void build_compositor(Scene *scene);
void build_gpencil(ID *owner, bGPdata *gpd);
+ void add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name);
+ void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name);
+
template <typename KeyType>
OperationDepsNode *find_operation_node(const KeyType &key);
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
index 9088e3bf403..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
@@ -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_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index b1271c39851..7a3b19e82c6 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -33,13 +33,18 @@
#include "MEM_guardedalloc.h"
extern "C" {
+#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_object_force.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BKE_main.h"
+#include "BKE_collision.h"
+#include "BKE_effect.h"
+#include "BKE_modifier.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_debug.h"
@@ -89,6 +94,7 @@ static DEG::eDepsNode_Type deg_build_object_component_type(
case DEG_OB_COMP_BONE: return DEG::DEPSNODE_TYPE_BONE;
case DEG_OB_COMP_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES;
case DEG_OB_COMP_SHADING: return DEG::DEPSNODE_TYPE_SHADING;
+ case DEG_OB_COMP_CACHE: return DEG::DEPSNODE_TYPE_CACHE;
}
return DEG::DEPSNODE_TYPE_UNDEFINED;
}
@@ -126,6 +132,20 @@ void DEG_add_object_relation(DepsNodeHandle *handle,
description);
}
+void DEG_add_object_cache_relation(DepsNodeHandle *handle,
+ CacheFile *cache_file,
+ eDepsObjectComponentType component,
+ const char *description)
+{
+ DEG::eDepsNode_Type type = deg_build_object_component_type(component);
+ DEG::ComponentKey comp_key(&cache_file->id, type);
+ DEG::DepsNodeHandle *deg_handle = get_handle(handle);
+ deg_handle->builder->add_node_handle_relation(comp_key,
+ deg_handle,
+ DEG::DEPSREL_TYPE_CACHE,
+ description);
+}
+
void DEG_add_bone_relation(DepsNodeHandle *handle,
Object *ob,
const char *bone_name,
@@ -288,3 +308,52 @@ void DEG_scene_graph_free(Scene *scene)
scene->depsgraph = NULL;
}
}
+
+void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name)
+{
+ unsigned int numcollobj;
+ Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli);
+
+ for (unsigned int i = 0; i < numcollobj; i++) {
+ Object *ob1 = collobjs[i];
+
+ if (!fn || fn(ob1, modifiers_findByType(ob1, (ModifierType)modifier_type))) {
+ DEG_add_object_relation(handle, ob1, DEG_OB_COMP_TRANSFORM, name);
+ DEG_add_object_relation(handle, ob1, DEG_OB_COMP_GEOMETRY, name);
+ }
+ }
+
+ if (collobjs)
+ MEM_freeN(collobjs);
+}
+
+void DEG_add_forcefield_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name)
+{
+ ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
+
+ if (effectors) {
+ for (EffectorCache *eff = (EffectorCache*)effectors->first; eff; eff = eff->next) {
+ if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) {
+ DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_TRANSFORM, name);
+
+ if (eff->psys) {
+ DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_EVAL_PARTICLES, name);
+
+ /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */
+ DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_GEOMETRY, name);
+ }
+
+ if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
+ DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_TRANSFORM, "Smoke Force Domain");
+ DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_GEOMETRY, "Smoke Force Domain");
+ }
+
+ if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) {
+ DEG_add_collision_relations(handle, scene, ob, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption");
+ }
+ }
+ }
+ }
+
+ pdEndEffectors(&effectors);
+}
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 7516ccbfdc2..effd34a0eb9 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). */
@@ -330,6 +332,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/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc
index 5832c458896..01f33b6368b 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc
@@ -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 acccb1cdcd4..7dec8eaaa90 100644
--- a/source/blender/depsgraph/intern/nodes/deg_node_component.h
+++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h
@@ -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 88d96c531e0..5cd305f69f5 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)
{
@@ -2839,6 +2899,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);
@@ -2950,7 +3016,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/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 f77e795adca..1f053c806b0 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -945,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_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_templates.c b/source/blender/editors/interface/interface_templates.c
index 58cadf5587a..50dd219b53c 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"
@@ -325,7 +326,9 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
}
else {
if (id) {
+ Main *bmain = CTX_data_main(C);
id_single_user(C, id, &template->ptr, template->prop);
+ DAG_relations_tag_update(bmain);
}
}
}
@@ -368,6 +371,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 +876,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);
@@ -3848,3 +3852,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/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 1a14fad8650..999d5b278ee 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -3080,7 +3080,7 @@ static void bm_mesh_hflag_flush_vert(BMesh *bm, const char hflag)
* \note This could be used for split-by-material for non mesh types.
* \note This could take material data from another object or args.
*/
-static void mesh_separate_material_assign_mat_nr(Object *ob, const short mat_nr)
+static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
{
ID *obdata = ob->data;
@@ -3116,18 +3116,18 @@ static void mesh_separate_material_assign_mat_nr(Object *ob, const short mat_nr)
ma_obdata = NULL;
}
- BKE_material_clear_id(obdata, true);
- BKE_material_resize_object(ob, 1, true);
- BKE_material_resize_id(obdata, 1, true);
+ BKE_material_clear_id(bmain, obdata, true);
+ BKE_material_resize_object(bmain, ob, 1, true);
+ BKE_material_resize_id(bmain, obdata, 1, true);
ob->mat[0] = ma_ob;
ob->matbits[0] = matbit;
(*matarar)[0] = ma_obdata;
}
else {
- BKE_material_clear_id(obdata, true);
- BKE_material_resize_object(ob, 0, true);
- BKE_material_resize_id(obdata, 0, true);
+ BKE_material_clear_id(bmain, obdata, true);
+ BKE_material_resize_object(bmain, ob, 0, true);
+ BKE_material_resize_id(bmain, obdata, 0, true);
}
}
@@ -3162,7 +3162,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM
/* leave the current object with some materials */
if (tot == bm_old->totface) {
- mesh_separate_material_assign_mat_nr(base_old->object, mat_nr);
+ mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr);
/* since we're in editmode, must set faces here */
BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
@@ -3174,7 +3174,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM
/* Move selection into a separate object */
base_new = mesh_separate_tagged(bmain, scene, base_old, bm_old);
if (base_new) {
- mesh_separate_material_assign_mat_nr(base_new->object, mat_nr);
+ mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr);
}
result |= (base_new != NULL);
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..067a5ad2b49 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);
@@ -2094,6 +2106,7 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo
}
BKE_main_id_clear_newpoins(bmain);
+ DAG_relations_tag_update(bmain);
}
/******************************* Make Local ***********************************/
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/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/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_intern.h b/source/blender/editors/space_clip/clip_intern.h
index 2a5d959bb84..13dcd405ee6 100644
--- a/source/blender/editors/space_clip/clip_intern.h
+++ b/source/blender/editors/space_clip/clip_intern.h
@@ -185,7 +185,10 @@ void CLIP_OT_detect_features(struct wmOperatorType *ot);
void CLIP_OT_stabilize_2d_add(struct wmOperatorType *ot);
void CLIP_OT_stabilize_2d_remove(struct wmOperatorType *ot);
void CLIP_OT_stabilize_2d_select(struct wmOperatorType *ot);
-void CLIP_OT_stabilize_2d_set_rotation(struct wmOperatorType *ot);
+
+void CLIP_OT_stabilize_2d_rotation_add(struct wmOperatorType *ot);
+void CLIP_OT_stabilize_2d_rotation_remove(struct wmOperatorType *ot);
+void CLIP_OT_stabilize_2d_rotation_select(struct wmOperatorType *ot);
void CLIP_OT_clean_tracks(struct wmOperatorType *ot);
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/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index 5964e9a898b..547c2fba66f 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -175,21 +175,14 @@ void clip_graph_tracking_iterate(SpaceClip *sc, bool selected_only, bool include
void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track)
{
MovieTracking *tracking = &clip->tracking;
- MovieTrackingStabilization *stab = &tracking->stabilization;
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
- bool has_bundle = false, update_stab = false;
+ bool has_bundle = false;
char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2];
if (track == act_track)
tracking->act_track = NULL;
- if (track == stab->rot_track) {
- stab->rot_track = NULL;
-
- update_stab = true;
- }
-
/* handle reconstruction display in 3d viewport */
if (track->flag & TRACK_HAS_BUNDLE)
has_bundle = true;
@@ -207,8 +200,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track)
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
- if (update_stab) {
- tracking->stabilization.ok = false;
+ if (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)) {
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
}
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index 396d71f0a20..4f653bab682 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -225,18 +225,6 @@ static void clip_scopes_check_gpencil_change(ScrArea *sa)
}
}
-static void clip_stabilization_tag_refresh(ScrArea *sa)
-{
- SpaceClip *sc = (SpaceClip *) sa->spacedata.first;
- MovieClip *clip = ED_space_clip_get_clip(sc);
-
- if (clip) {
- MovieTrackingStabilization *stab = &clip->tracking.stabilization;
-
- stab->ok = false;
- }
-}
-
/* ******************** default callbacks for clip space ***************** */
static SpaceLink *clip_new(const bContext *C)
@@ -368,7 +356,6 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
case NA_REMOVED:
case NA_EDITED:
case NA_EVALUATED:
- clip_stabilization_tag_refresh(sa);
/* fall-through */
case NA_SELECTED:
@@ -412,7 +399,6 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
case NC_SPACE:
if (wmn->data == ND_SPACE_CLIP) {
clip_scopes_tag_refresh(sa);
- clip_stabilization_tag_refresh(sa);
ED_area_tag_redraw(sa);
}
break;
@@ -457,7 +443,7 @@ static void clip_operatortypes(void)
/* navigation */
WM_operatortype_append(CLIP_OT_frame_jump);
- /* foorage */
+ /* set optical center to frame center */
WM_operatortype_append(CLIP_OT_set_center_principal);
/* selection */
@@ -505,7 +491,9 @@ static void clip_operatortypes(void)
WM_operatortype_append(CLIP_OT_stabilize_2d_add);
WM_operatortype_append(CLIP_OT_stabilize_2d_remove);
WM_operatortype_append(CLIP_OT_stabilize_2d_select);
- WM_operatortype_append(CLIP_OT_stabilize_2d_set_rotation);
+ WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_add);
+ WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_remove);
+ WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_select);
/* clean-up */
WM_operatortype_append(CLIP_OT_clear_track_path);
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index 61bfa5b315b..d28cbe5fb1d 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -1509,8 +1509,10 @@ static int join_tracks_exec(bContext *C, wmOperator *op)
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTracking *tracking = &clip->tracking;
+ MovieTrackingStabilization *stab = &tracking->stabilization;
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
+ bool update_stabilization = false;
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
if (act_track == NULL) {
@@ -1528,8 +1530,23 @@ static int join_tracks_exec(bContext *C, wmOperator *op)
if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
BKE_tracking_tracks_join(tracking, act_track, track);
- if (tracking->stabilization.rot_track == track) {
- tracking->stabilization.rot_track = act_track;
+ if (track->flag & TRACK_USE_2D_STAB) {
+ update_stabilization = true;
+ if ((act_track->flag & TRACK_USE_2D_STAB) == 0) {
+ act_track->flag |= TRACK_USE_2D_STAB;
+ } else {
+ stab->tot_track--;
+ }
+ BLI_assert(0 <= stab->tot_track);
+ }
+ if (track->flag & TRACK_USE_2D_STAB_ROT) {
+ update_stabilization = true;
+ if ((act_track->flag & TRACK_USE_2D_STAB_ROT) == 0) {
+ act_track->flag |= TRACK_USE_2D_STAB_ROT;
+ } else {
+ stab->tot_rot_track--;
+ }
+ BLI_assert(0 <= stab->tot_rot_track);
}
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
@@ -1551,6 +1568,10 @@ static int join_tracks_exec(bContext *C, wmOperator *op)
}
}
+ if (update_stabilization) {
+ WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
+ }
+
GSetIterator gs_iter;
int framenr = ED_space_clip_get_clip_frame_number(sc);
GSET_ITER (gs_iter, point_tracks) {
diff --git a/source/blender/editors/space_clip/tracking_ops_stabilize.c b/source/blender/editors/space_clip/tracking_ops_stabilize.c
index 8d6173e1cea..35b1aead343 100644
--- a/source/blender/editors/space_clip/tracking_ops_stabilize.c
+++ b/source/blender/editors/space_clip/tracking_ops_stabilize.c
@@ -84,7 +84,6 @@ static int stabilize_2d_add_exec(bContext *C, wmOperator *UNUSED(op))
}
if (update) {
- stab->ok = 0;
DAG_id_tag_update(&clip->id, 0);
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
}
@@ -96,7 +95,7 @@ void CLIP_OT_stabilize_2d_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Stabilization Tracks";
- ot->description = "Add selected tracks to 2D stabilization tool";
+ ot->description = "Add selected tracks to 2D translation stabilization";
ot->idname = "CLIP_OT_stabilize_2d_add";
/* api callbacks */
@@ -139,7 +138,6 @@ static int stabilize_2d_remove_exec(bContext *C, wmOperator *UNUSED(op))
}
if (update) {
- stab->ok = 0;
DAG_id_tag_update(&clip->id, 0);
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
}
@@ -151,7 +149,7 @@ void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Stabilization Track";
- ot->description = "Remove selected track from stabilization";
+ ot->description = "Remove selected track from translation stabilization";
ot->idname = "CLIP_OT_stabilize_2d_remove";
/* api callbacks */
@@ -193,7 +191,7 @@ void CLIP_OT_stabilize_2d_select(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Stabilization Tracks";
- ot->description = "Select tracks which are used for stabilization";
+ ot->description = "Select tracks which are used for translation stabilization";
ot->idname = "CLIP_OT_stabilize_2d_select";
/* api callbacks */
@@ -204,20 +202,85 @@ void CLIP_OT_stabilize_2d_select(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/***************** set 2d stabilization rotation track operator ****************/
+/********************** add 2d stabilization tracks for rotation operator ****************/
-static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op))
+static int stabilize_2d_rotation_add_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTracking *tracking = &clip->tracking;
- MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
+ ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
+ MovieTrackingStabilization *stab = &tracking->stabilization;
+
+ bool update = false;
+ for (MovieTrackingTrack *track = tracksbase->first;
+ track != NULL;
+ track = track->next)
+ {
+ if (TRACK_VIEW_SELECTED(sc, track) &&
+ (track->flag & TRACK_USE_2D_STAB_ROT) == 0)
+ {
+ track->flag |= TRACK_USE_2D_STAB_ROT;
+ stab->tot_rot_track++;
+ update = true;
+ }
+ }
+
+ if (update) {
+ DAG_id_tag_update(&clip->id, 0);
+ WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_stabilize_2d_rotation_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Stabilization Rotation Tracks";
+ ot->description = "Add selected tracks to 2D rotation stabilization";
+ ot->idname = "CLIP_OT_stabilize_2d_rotation_add";
+
+ /* api callbacks */
+ ot->exec = stabilize_2d_rotation_add_exec;
+ ot->poll = stabilize_2d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/********************** remove 2d stabilization tracks for rotation operator *************/
+
+static int stabilize_2d_rotation_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ MovieClip *clip = ED_space_clip_get_clip(sc);
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingStabilization *stab = &tracking->stabilization;
+ ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
+ int a = 0;
+ bool update = false;
- if (act_track != NULL) {
- MovieTrackingStabilization *stab = &tracking->stabilization;
- stab->rot_track = act_track;
- stab->ok = 0;
+ for (MovieTrackingTrack *track = tracksbase->first;
+ track != NULL;
+ track = track->next)
+ {
+ if (track->flag & TRACK_USE_2D_STAB_ROT) {
+ if (a == stab->act_rot_track) {
+ track->flag &= ~TRACK_USE_2D_STAB_ROT;
+ stab->act_rot_track--;
+ stab->tot_rot_track--;
+ if (stab->act_rot_track < 0) {
+ stab->act_rot_track = 0;
+ }
+ update = true;
+ break;
+ }
+ a++;
+ }
+ }
+ if (update) {
DAG_id_tag_update(&clip->id, 0);
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
}
@@ -225,18 +288,60 @@ static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
-void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot)
+void CLIP_OT_stabilize_2d_rotation_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Stabilization Rotation Track";
+ ot->description = "Remove selected track from rotation stabilization";
+ ot->idname = "CLIP_OT_stabilize_2d_rotation_remove";
+
+ /* api callbacks */
+ ot->exec = stabilize_2d_rotation_remove_exec;
+ ot->poll = stabilize_2d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/********************** select 2d stabilization rotation tracks operator *****************/
+
+static int stabilize_2d_rotation_select_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ MovieClip *clip = ED_space_clip_get_clip(sc);
+ MovieTracking *tracking = &clip->tracking;
+ ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
+ bool update = false;
+
+ for (MovieTrackingTrack *track = tracksbase->first;
+ track != NULL;
+ track = track->next)
+ {
+ if (track->flag & TRACK_USE_2D_STAB_ROT) {
+ BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
+ update = true;
+ }
+ }
+
+ if (update) {
+ WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_stabilize_2d_rotation_select(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Set Rotation Track";
- ot->description = "Use active track to compensate rotation when "
- "doing 2D stabilization";
- ot->idname = "CLIP_OT_stabilize_2d_set_rotation";
+ ot->name = "Select Stabilization Rotation Tracks";
+ ot->description = "Select tracks which are used for rotation stabilization";
+ ot->idname = "CLIP_OT_stabilize_2d_rotation_select";
/* api callbacks */
- ot->exec = stabilize_2d_set_rotation_exec;
+ ot->exec = stabilize_2d_rotation_select_exec;
ot->poll = stabilize_2d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
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 4cbf04f9d42..516814b63b4 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -1067,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_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 2582ba4be8d..b35d1b2c777 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -687,7 +687,7 @@ static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID
return;
}
- if ((ID *)sgraph->ads->filter_grp == old_id) {
+ if (sgraph->ads && (ID *)sgraph->ads->filter_grp == old_id) {
sgraph->ads->filter_grp = (Group *)new_id;
}
}
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 ddbd07616bc..7b08b8368ba 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -1338,7 +1338,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_relationships.c b/source/blender/editors/space_node/node_relationships.c
index ea3869ef387..5f592431558 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -276,25 +276,16 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in
return NULL;
}
-static int snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
+static bool snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
{
bNodeTree *ntree = snode->edittree;
- bNodeLink *link;
/* then we can connect */
if (replace)
nodeRemSocketLinks(ntree, sock_to);
- link = nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
- /* validate the new link */
- ntreeUpdateTree(G.main, ntree);
- if (!(link->flag & NODE_LINK_VALID)) {
- nodeRemLink(ntree, link);
- return 0;
- }
-
- snode_update(snode, node_to);
- return 1;
+ nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
+ return true;
}
static void snode_autoconnect(SpaceNode *snode, const bool allow_multiple, const bool replace)
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 525d42a1965..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) {
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/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_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_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 1376b6bf4da..9c266890d6d 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -3292,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);
}
}
}
@@ -7687,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) {
@@ -7735,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... */
@@ -7755,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) {
@@ -7824,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... */
@@ -7835,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/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/GPU_debug.h b/source/blender/gpu/GPU_debug.h
index 2c1728bfff1..61b2bc591ce 100644
--- a/source/blender/gpu/GPU_debug.h
+++ b/source/blender/gpu/GPU_debug.h
@@ -34,8 +34,6 @@
#include "GPU_glew.h"
-#include "BLI_utildefines.h"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -62,7 +60,7 @@ void GPU_assert_no_gl_errors(const char *file, int line, const char *str);
/* inserts a debug marker message for the debug context messaging system */
-void GPU_string_marker(size_t size, const char *str);
+void GPU_string_marker(const char *str);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.c
index be9285727fe..d632e767ca9 100644
--- a/source/blender/gpu/intern/gpu_debug.c
+++ b/source/blender/gpu/intern/gpu_debug.c
@@ -29,7 +29,9 @@
* \ingroup gpu
*/
+#include "BLI_utildefines.h"
#include "BLI_sys_types.h"
+#include "BLI_system.h"
#include "BKE_global.h"
@@ -159,17 +161,73 @@ const char *gpuErrorString(GLenum err)
#endif
+static const char* source_name(GLenum source)
+{
+ switch (source) {
+ case GL_DEBUG_SOURCE_API: return "API";
+ case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "window system";
+ case GL_DEBUG_SOURCE_SHADER_COMPILER: return "shader compiler";
+ case GL_DEBUG_SOURCE_THIRD_PARTY: return "3rd party";
+ case GL_DEBUG_SOURCE_APPLICATION: return "application";
+ case GL_DEBUG_SOURCE_OTHER: return "other";
+ default: return "???";
+ }
+}
+
+static const char* message_type_name(GLenum message)
+{
+ switch (message) {
+ case GL_DEBUG_TYPE_ERROR: return "error";
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "deprecated behavior";
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "undefined behavior";
+ case GL_DEBUG_TYPE_PORTABILITY: return "portability";
+ case GL_DEBUG_TYPE_PERFORMANCE: return "performance";
+ case GL_DEBUG_TYPE_OTHER: return "other";
+ case GL_DEBUG_TYPE_MARKER: return "marker"; /* KHR has this, ARB does not */
+ default: return "???";
+ }
+}
+
+static const char* category_name_amd(GLenum category)
+{
+ switch (category) {
+ case GL_DEBUG_CATEGORY_API_ERROR_AMD: return "API error";
+ case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: return "window system";
+ case GL_DEBUG_CATEGORY_DEPRECATION_AMD: return "deprecated behavior";
+ case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: return "undefined behavior";
+ case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: return "performance";
+ case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: return "shader compiler";
+ case GL_DEBUG_CATEGORY_APPLICATION_AMD: return "application";
+ case GL_DEBUG_CATEGORY_OTHER_AMD: return "other";
+ default: return "???";
+ }
+}
+
+
static void APIENTRY gpu_debug_proc(
GLenum source, GLenum type, GLuint UNUSED(id),
- GLenum UNUSED(severity), GLsizei UNUSED(length),
+ GLenum severity, GLsizei UNUSED(length),
const GLchar *message, const GLvoid *UNUSED(userParm))
{
- if (source == GL_DEBUG_SOURCE_API && type == GL_DEBUG_TYPE_ERROR) {
- fprintf(stderr, "GL: %s\n", message);
- fflush(stderr);
+ if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) {
+ /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */
+ return;
}
- else if (G.debug_value == 20) {
- fprintf(stderr, "GL: %s\n", message);
+
+ bool backtrace = false;
+
+ switch (severity) {
+ case GL_DEBUG_SEVERITY_HIGH:
+ backtrace = true;
+ /* fall through */
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ case GL_DEBUG_SEVERITY_LOW:
+ case GL_DEBUG_SEVERITY_NOTIFICATION: /* KHR has this, ARB does not */
+ fprintf(stderr, "GL %s %s: %s\n", source_name(source), message_type_name(type), message);
+ }
+
+ if (backtrace) {
+ BLI_system_backtrace(stderr);
fflush(stderr);
}
}
@@ -177,11 +235,30 @@ static void APIENTRY gpu_debug_proc(
#ifndef GLEW_ES_ONLY
static void APIENTRY gpu_debug_proc_amd(
- GLuint UNUSED(id), GLenum UNUSED(category),
- GLenum UNUSED(severity), GLsizei UNUSED(length),
+ GLuint UNUSED(id), GLenum category,
+ GLenum severity, GLsizei UNUSED(length),
const GLchar *message, GLvoid *UNUSED(userParm))
{
- fprintf(stderr, "GL: %s\n", message);
+ if (category == GL_DEBUG_CATEGORY_DEPRECATION_AMD) {
+ /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */
+ return;
+ }
+
+ bool backtrace = false;
+
+ switch (severity) {
+ case GL_DEBUG_SEVERITY_HIGH:
+ backtrace = true;
+ /* fall through */
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ case GL_DEBUG_SEVERITY_LOW:
+ fprintf(stderr, "GL %s: %s\n", category_name_amd(category), message);
+ }
+
+ if (backtrace) {
+ BLI_system_backtrace(stderr);
+ fflush(stderr);
+ }
}
#endif
@@ -194,36 +271,44 @@ void gpu_debug_init(void)
#if !defined(WITH_GLEW_ES) && !defined(GLEW_ES_ONLY)
if (GLEW_VERSION_4_3) {
+ fprintf(stderr, "Using OpenGL 4.3 debug facilities\n");
glEnable(GL_DEBUG_OUTPUT);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext());
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(sizeof(success), success);
+ GPU_string_marker(success);
return;
}
#endif
if (GLEW_KHR_debug) {
#ifndef GLEW_ES_ONLY
+ fprintf(stderr, "Using KHR_debug extension\n");
+ glEnable(GL_DEBUG_OUTPUT);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext());
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(sizeof(success), success);
+ GPU_string_marker(success);
#endif
return;
}
#ifndef GLEW_ES_ONLY
if (GLEW_ARB_debug_output) {
+ fprintf(stderr, "Using ARB_debug_output extension\n");
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, mxGetCurrentContext());
glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(sizeof(success), success);
+ GPU_string_marker(success);
return;
}
if (GLEW_AMD_debug_output) {
+ fprintf(stderr, "Using AMD_debug_output extension\n");
glDebugMessageCallbackAMD(gpu_debug_proc_amd, mxGetCurrentContext());
glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
- GPU_string_marker(sizeof(success), success);
+ GPU_string_marker(success);
return;
}
@@ -271,14 +356,14 @@ void gpu_debug_exit(void)
return;
}
-void GPU_string_marker(size_t length, const char *buf)
+void GPU_string_marker(const char *buf)
{
#ifndef WITH_GLEW_ES
#ifndef GLEW_ES_ONLY
if (GLEW_VERSION_4_3) {
glDebugMessageInsert(
GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0,
- GL_DEBUG_SEVERITY_NOTIFICATION, length, buf);
+ GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf);
return;
}
@@ -289,7 +374,7 @@ void GPU_string_marker(size_t length, const char *buf)
#ifndef GLEW_ES_ONLY
glDebugMessageInsert(
GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0,
- GL_DEBUG_SEVERITY_NOTIFICATION, length, buf);
+ GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf);
#endif
return;
}
@@ -298,7 +383,7 @@ void GPU_string_marker(size_t length, const char *buf)
if (GLEW_ARB_debug_output) {
glDebugMessageInsertARB(
GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_OTHER_ARB, 0,
- GL_DEBUG_SEVERITY_LOW_ARB, length, buf);
+ GL_DEBUG_SEVERITY_LOW_ARB, -1, buf);
return;
}
@@ -306,19 +391,17 @@ void GPU_string_marker(size_t length, const char *buf)
if (GLEW_AMD_debug_output) {
glDebugMessageInsertAMD(
GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0,
- length, buf);
+ 0, buf);
return;
}
if (GLEW_GREMEDY_string_marker) {
- glStringMarkerGREMEDY(length, buf);
+ glStringMarkerGREMEDY(0, buf);
return;
}
#endif
-
- return;
}
void GPU_print_error_debug(const char *str)
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 07df94ee332..5c1bfc229da 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -247,6 +247,7 @@ typedef enum ID_Type {
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. */
@@ -377,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_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_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_genfile.h b/source/blender/makesdna/DNA_genfile.h
index a2981c0aa76..9e9ab974b01 100644
--- a/source/blender/makesdna/DNA_genfile.h
+++ b/source/blender/makesdna/DNA_genfile.h
@@ -100,6 +100,7 @@ void *DNA_struct_reconstruct(
int DNA_elem_array_size(const char *str);
int DNA_elem_offset(struct SDNA *sdna, const char *stype, const char *vartype, const char *name);
+bool DNA_struct_find(const struct SDNA *sdna, const char *stype);
bool DNA_struct_elem_find(const struct SDNA *sdna, const char *stype, const char *vartype, const char *name);
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 41f53f9f51c..773d203bdb3 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,12 @@ 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 palettes */
+ ListBase palettes;
} bGPdata;
/* bGPdata->flag */
@@ -229,7 +334,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_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_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/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h
index 9888b735b8b..42b72c1ff93 100644
--- a/source/blender/makesdna/DNA_tracking_types.h
+++ b/source/blender/makesdna/DNA_tracking_types.h
@@ -158,7 +158,10 @@ typedef struct MovieTrackingTrack {
* Used to prevent jumps of the camera when tracks are appearing or
* disappearing.
*/
- float weight, pad;
+ float weight;
+
+ /* track weight especially for 2D stabilization */
+ float weight_stab;
} MovieTrackingTrack;
typedef struct MovieTrackingPlaneMarker {
@@ -250,19 +253,24 @@ typedef struct MovieTrackingSettings {
typedef struct MovieTrackingStabilization {
int flag;
- int tot_track, act_track; /* total number and index of active track in list */
+ int tot_track, act_track; /* total number of translation tracks and index of active track in list */
+ int tot_rot_track, act_rot_track; /* total number of rotation tracks and index of active track in list */
/* 2d stabilization */
float maxscale; /* max auto-scale factor */
- MovieTrackingTrack *rot_track; /* track used to stabilize rotation */
+ MovieTrackingTrack *rot_track DNA_DEPRECATED; /* use TRACK_USE_2D_STAB_ROT on individual tracks instead */
+
+ int anchor_frame; /* reference point to anchor stabilization offset */
+ float target_pos[2]; /* expected target position of frame after raw stabilization, will be subtracted */
+ float target_rot; /* expected target rotation of frame after raw stabilization, will be compensated */
+ float scale; /* zoom factor known to be present on original footage. Also used for autoscale */
float locinf, scaleinf, rotinf; /* influence on location, scale and rotation */
int filter; /* filter used for pixel interpolation */
- /* some pre-computing run-time variables */
- int ok; /* are precomputed values and scaled buf relevant? */
- float scale; /* autoscale factor */
+ /* initialization and run-time data */
+ int ok DNA_DEPRECATED; /* Without effect now, we initialize on every frame. Formerly used for caching of init values */
} MovieTrackingStabilization;
typedef struct MovieTrackingReconstruction {
@@ -386,7 +394,8 @@ enum {
TRACK_USE_2D_STAB = (1 << 8),
TRACK_PREVIEW_GRAYSCALE = (1 << 9),
TRACK_DOPE_SEL = (1 << 10),
- TRACK_PREVIEW_ALPHA = (1 << 11)
+ TRACK_PREVIEW_ALPHA = (1 << 11),
+ TRACK_USE_2D_STAB_ROT = (1 << 12)
};
/* MovieTrackingTrack->motion_model */
@@ -452,7 +461,9 @@ enum {
enum {
TRACKING_2D_STABILIZATION = (1 << 0),
TRACKING_AUTOSCALE = (1 << 1),
- TRACKING_STABILIZE_ROTATION = (1 << 2)
+ TRACKING_STABILIZE_ROTATION = (1 << 2),
+ TRACKING_STABILIZE_SCALE = (1 << 3),
+ TRACKING_SHOW_STAB_TRACKS = (1 << 5)
};
/* MovieTrackingStrabilization->filter */
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index 6a41591e051..96085a79eff 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -1294,6 +1294,11 @@ int DNA_elem_offset(SDNA *sdna, const char *stype, const char *vartype, const ch
return (int)((intptr_t)cp);
}
+bool DNA_struct_find(const SDNA *sdna, const char *stype)
+{
+ return DNA_struct_find_nr(sdna, stype) != -1;
+}
+
bool DNA_struct_elem_find(const SDNA *sdna, const char *stype, const char *vartype, const char *name)
{
const int SDNAnr = DNA_struct_find_nr(sdna, stype);
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 e884d769afe..9cbe132282f 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;
@@ -257,6 +259,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;
@@ -417,6 +422,7 @@ extern StructRNA RNA_MovieClipSequence;
extern StructRNA RNA_MovieTracking;
extern StructRNA RNA_MovieTrackingObject;
extern StructRNA RNA_MovieTrackingTrack;
+extern StructRNA RNA_MovieTrackingStabilization;
extern StructRNA RNA_MulticamSequence;
extern StructRNA RNA_MultiresModifier;
extern StructRNA RNA_MusgraveTexture;
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/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 7bfac9d0605..44e99bd2995 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -38,6 +38,7 @@ set(DEFSRC
rna_armature.c
rna_boid.c
rna_brush.c
+ rna_cachefile.c
rna_camera.c
rna_cloth.c
rna_color.c
@@ -290,6 +291,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 f9a46409aea..569c1ee5f3f 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -50,7 +50,7 @@
#ifndef NDEBUG
void BLI_system_backtrace(FILE *fp)
{
- (void)fp;
+ (void)fp;
}
#endif
@@ -3301,6 +3301,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_armature.c", "rna_armature_api.c", RNA_def_armature},
{"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 8ad6713192a..ab124b361f1 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", ""},
@@ -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;
@@ -383,15 +386,15 @@ int rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assig
}
}
-static void rna_IDMaterials_append_id(ID *id, Material *ma)
+static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma)
{
- BKE_material_append_id(id, ma);
+ BKE_material_append_id(bmain, id, ma);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, id);
WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, id);
}
-static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i, int remove_material_slot)
+static Material *rna_IDMaterials_pop_id(ID *id, Main *bmain, ReportList *reports, int index_i, int remove_material_slot)
{
Material *ma;
short *totcol = give_totcolp_id(id);
@@ -405,7 +408,7 @@ static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i
return NULL;
}
- ma = BKE_material_pop_id(id, index_i, remove_material_slot);
+ ma = BKE_material_pop_id(bmain, id, index_i, remove_material_slot);
if (*totcol == totcol_orig) {
BKE_report(reports, RPT_ERROR, "No material to removed");
@@ -421,7 +424,7 @@ static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i
static void rna_IDMaterials_clear_id(ID *id, int remove_material_slot)
{
- BKE_material_clear_id(id, remove_material_slot);
+ BKE_material_clear_id(G.main, id, remove_material_slot);
DAG_id_tag_update(id, OB_RECALC_DATA);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, id);
@@ -817,12 +820,13 @@ static void rna_def_ID_materials(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "ID Materials", "Collection of materials");
func = RNA_def_function(srna, "append", "rna_IDMaterials_append_id");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
RNA_def_function_ui_description(func, "Add a new material to the data block");
parm = RNA_def_pointer(func, "material", "Material", "", "Material to add");
RNA_def_property_flag(parm, PROP_REQUIRED);
func = RNA_def_function(srna, "pop", "rna_IDMaterials_pop_id");
- RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN);
RNA_def_function_ui_description(func, "Remove a material from the data block");
parm = RNA_def_int(func, "index", -1, -MAXMAT, MAXMAT, "", "Index of material to remove", 0, MAXMAT);
RNA_def_boolean(func, "update_data", 0, "", "Update data by re-adjusting the material slots assigned");
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 781e44c9ed6..d8bcbc2cc72 100644
--- a/source/blender/makesrna/intern/rna_cloth.c
+++ b/source/blender/makesrna/intern/rna_cloth.c
@@ -56,6 +56,12 @@ static void rna_cloth_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
}
+static void rna_cloth_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ DAG_relations_tag_update(bmain);
+ rna_cloth_update(bmain, scene, ptr);
+}
+
static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Object *ob = (Object *)ptr->id.data;
@@ -68,6 +74,16 @@ static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene),
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
}
+static void rna_ClothSettings_bending_set(struct PointerRNA *ptr, float value)
+{
+ ClothSimSettings *settings = (ClothSimSettings *)ptr->data;
+
+ settings->bending = value;
+
+ /* check for max clipping */
+ if (value > settings->max_bend)
+ settings->max_bend = value;
+}
static void rna_ClothSettings_max_bend_set(struct PointerRNA *ptr, float value)
{
@@ -80,6 +96,17 @@ static void rna_ClothSettings_max_bend_set(struct PointerRNA *ptr, float value)
settings->max_bend = value;
}
+static void rna_ClothSettings_structural_set(struct PointerRNA *ptr, float value)
+{
+ ClothSimSettings *settings = (ClothSimSettings *)ptr->data;
+
+ settings->structural = value;
+
+ /* check for max clipping */
+ if (value > settings->max_struct)
+ settings->max_struct = value;
+}
+
static void rna_ClothSettings_max_struct_set(struct PointerRNA *ptr, float value)
{
ClothSimSettings *settings = (ClothSimSettings *)ptr->data;
@@ -493,6 +520,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "structural_stiffness", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "structural");
RNA_def_property_range(prop, 0.0f, 10000.0f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_ClothSettings_structural_set", NULL);
RNA_def_property_ui_text(prop, "Structural Stiffness", "Overall stiffness of structure");
RNA_def_property_update(prop, 0, "rna_cloth_update");
@@ -521,6 +549,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "bending_stiffness", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "bending");
RNA_def_property_range(prop, 0.0f, 10000.0f);
+ RNA_def_property_float_funcs(prop, NULL, "rna_ClothSettings_bending_set", NULL);
RNA_def_property_ui_text(prop, "Bending Stiffness",
"Wrinkle coefficient (higher = less smaller but more big wrinkles)");
RNA_def_property_update(prop, 0, "rna_cloth_update");
@@ -706,7 +735,7 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "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_cloth_update");
+ RNA_def_property_update(prop, 0, "rna_cloth_dependency_update");
prop = RNA_def_property(srna, "vertex_group_self_collisions", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, "rna_CollSettings_selfcol_vgroup_get", "rna_CollSettings_selfcol_vgroup_length",
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_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 52c04bec743..9476c964b40 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,14 @@ 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");
-
+ if (colorname) {
+ strcpy(stroke->colorname, colorname);
+ }
+ stroke->palcolor = NULL;
+ stroke->flag |= GP_STROKE_RECALC_COLOR;
BLI_addtail(&frame->strokes, stroke);
return stroke;
@@ -410,7 +536,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 +551,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 +559,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 +574,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 +589,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 +597,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 +872,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 +907,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 +960,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 +985,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 +1022,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 +1150,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 +1220,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 +1239,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 +1274,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 +1358,208 @@ 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, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "info");
+ RNA_def_property_ui_text(prop, "Name", "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 +1578,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 +1601,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 +1620,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 161e19f581c..364aa9ba939 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -133,6 +133,7 @@ void RNA_def_armature(struct BlenderRNA *brna);
void RNA_def_actuator(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);
@@ -332,6 +333,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_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index f05813043ca..1d89f7535c4 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -730,6 +730,11 @@ static void rna_softbody_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Point
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
}
+static void rna_softbody_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ DAG_relations_tag_update(bmain);
+ rna_softbody_update(bmain, scene, ptr);
+}
static EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), PointerRNA *ptr,
PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
@@ -1378,7 +1383,7 @@ static void rna_def_field(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_absorption", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_VISIBILITY);
RNA_def_property_ui_text(prop, "Absorption", "Force gets absorbed by collision objects");
- RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
+ RNA_def_property_update(prop, 0, "rna_FieldSettings_dependency_update");
prop = RNA_def_property(srna, "use_multiple_springs", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_MULTIPLE_SPRINGS);
@@ -1855,7 +1860,7 @@ static void rna_def_softbody(BlenderRNA *brna)
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");
+ RNA_def_property_update(prop, 0, "rna_softbody_dependency_update");
prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "effector_weights");
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index eabf41cb332..5e3fa4b467d 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -630,6 +630,12 @@ static void rna_Particle_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET);
}
+static void rna_Particle_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ DAG_relations_tag_update(bmain);
+ rna_Particle_reset(bmain, scene, ptr);
+}
+
static void rna_Particle_change_type(Main *bmain, Scene *scene, PointerRNA *ptr)
{
particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET | PSYS_RECALC_TYPE);
@@ -2744,7 +2750,7 @@ static void rna_def_particle_settings(BlenderRNA *brna)
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");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset_dependency");
/* 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_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_tracking.c b/source/blender/makesrna/intern/rna_tracking.c
index 2564bdb800f..0591c65d484 100644
--- a/source/blender/makesrna/intern/rna_tracking.c
+++ b/source/blender/makesrna/intern/rna_tracking.c
@@ -394,6 +394,16 @@ static int rna_track_2d_stabilization(CollectionPropertyIterator *UNUSED(iter),
return 0;
}
+static int rna_track_2d_stabilization_rotation(CollectionPropertyIterator *UNUSED(iter), void *data)
+{
+ MovieTrackingTrack *track = (MovieTrackingTrack *)data;
+
+ if ((track->flag & TRACK_USE_2D_STAB_ROT) == 0)
+ return 1;
+
+ return 0;
+}
+
static void rna_tracking_stabTracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
MovieClip *clip = (MovieClip *)ptr->id.data;
@@ -421,23 +431,36 @@ static void rna_tracking_stabTracks_active_index_range(PointerRNA *ptr, int *min
*max = max_ii(0, clip->tracking.stabilization.tot_track - 1);
}
-static void rna_tracking_resetIntrinsics(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+static void rna_tracking_stabRotTracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
MovieClip *clip = (MovieClip *)ptr->id.data;
- MovieTracking *tracking = &clip->tracking;
+ rna_iterator_listbase_begin(iter, &clip->tracking.tracks, rna_track_2d_stabilization_rotation);
+}
- if (tracking->camera.intrinsics) {
- BKE_tracking_distortion_free(tracking->camera.intrinsics);
- tracking->camera.intrinsics = NULL;
- }
+static int rna_tracking_stabRotTracks_active_index_get(PointerRNA *ptr)
+{
+ MovieClip *clip = (MovieClip *)ptr->id.data;
+ return clip->tracking.stabilization.act_rot_track;
}
-static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
+static void rna_tracking_stabRotTracks_active_index_set(PointerRNA *ptr, int value)
+{
+ MovieClip *clip = (MovieClip *)ptr->id.data;
+ clip->tracking.stabilization.act_rot_track = value;
+}
+
+static void rna_tracking_stabRotTracks_active_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
{
MovieClip *clip = (MovieClip *)ptr->id.data;
- MovieTrackingStabilization *stab = &clip->tracking.stabilization;
- stab->ok = 0;
+ *min = 0;
+ *max = max_ii(0, clip->tracking.stabilization.tot_rot_track - 1);
+}
+
+static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
+{
+ MovieClip *clip = (MovieClip *)ptr->id.data;
nodeUpdateID(scene->nodetree, &clip->id);
@@ -446,6 +469,17 @@ static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerR
DAG_id_tag_update(&clip->id, 0);
}
+static void rna_tracking_resetIntrinsics(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ MovieClip *clip = (MovieClip *)ptr->id.data;
+ MovieTracking *tracking = &clip->tracking;
+
+ if (tracking->camera.intrinsics) {
+ BKE_tracking_distortion_free(tracking->camera.intrinsics);
+ tracking->camera.intrinsics = NULL;
+ }
+}
+
static void rna_trackingObject_tracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
MovieTrackingObject *object = (MovieTrackingObject *)ptr->data;
@@ -1495,6 +1529,12 @@ static void rna_def_trackingTrack(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Weight", "Influence of this track on a final solution");
+ /* weight_stab */
+ prop = RNA_def_property(srna, "weight_stab", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "weight_stab");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Stab Weight", "Influence of this track on 2D stabilization");
+
/* offset */
prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 2);
@@ -1634,15 +1674,15 @@ static void rna_def_trackingStabilization(BlenderRNA *brna)
PropertyRNA *prop;
static EnumPropertyItem filter_items[] = {
- {TRACKING_FILTER_NEAREST, "NEAREST", 0, "Nearest", ""},
- {TRACKING_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", ""},
- {TRACKING_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", ""},
+ {TRACKING_FILTER_NEAREST, "NEAREST", 0, "Nearest", "No interpolation; use nearest neighbor pixel"},
+ {TRACKING_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", "Simple interpolation between adjacent pixels"},
+ {TRACKING_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", "High quality pixel interpolation"},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "MovieTrackingStabilization", NULL);
RNA_def_struct_path_func(srna, "rna_trackingStabilization_path");
- RNA_def_struct_ui_text(srna, "Movie tracking stabilization data", "Match-moving stabilization data for tracking");
+ RNA_def_struct_ui_text(srna, "Movie tracking stabilization data", "2D stabilization based on tracking markers");
/* 2d stabilization */
prop = RNA_def_property(srna, "use_2d_stabilization", PROP_BOOLEAN, PROP_NONE);
@@ -1651,22 +1691,29 @@ static void rna_def_trackingStabilization(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use 2D stabilization", "Use 2D stabilization for footage");
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
+ /* use_stabilize_rotation */
+ prop = RNA_def_property(srna, "use_stabilize_rotation", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_ROTATION);
+ RNA_def_property_ui_text(prop, "Stabilize Rotation", "Stabilize detected rotation around center of frame");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
+
+ /* use_stabilize_scale */
+ prop = RNA_def_property(srna, "use_stabilize_scale", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_SCALE);
+ RNA_def_property_ui_text(prop, "Stabilize Scale", "Compensate any scale changes relative to center of rotation");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
+
/* tracks */
prop = RNA_def_property(srna, "tracks", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_funcs(prop, "rna_tracking_stabTracks_begin", "rna_iterator_listbase_next",
"rna_iterator_listbase_end", "rna_iterator_listbase_get",
NULL, NULL, NULL, NULL);
RNA_def_property_struct_type(prop, "MovieTrackingTrack");
- RNA_def_property_ui_text(prop, "Tracks", "Collection of tracks used for stabilization");
+ RNA_def_property_ui_text(prop, "Translation Tracks", "Collection of tracks used for 2D stabilization (translation)");
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
- /* rotation track */
- prop = RNA_def_property(srna, "rotation_track", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "rot_track");
- RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_ui_text(prop, "Rotation Track", "Track used to compensate rotation");
- RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate");
-
/* active track index */
prop = RNA_def_property(srna, "active_track_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "act_track");
@@ -1674,7 +1721,57 @@ static void rna_def_trackingStabilization(BlenderRNA *brna)
RNA_def_property_int_funcs(prop, "rna_tracking_stabTracks_active_index_get",
"rna_tracking_stabTracks_active_index_set",
"rna_tracking_stabTracks_active_index_range");
- RNA_def_property_ui_text(prop, "Active Track Index", "Index of active track in stabilization tracks list");
+ RNA_def_property_ui_text(prop, "Active Track Index", "Index of active track in translation stabilization tracks list");
+
+ /* tracks used for rotation stabilization */
+ prop = RNA_def_property(srna, "rotation_tracks", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop, "rna_tracking_stabRotTracks_begin", "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end", "rna_iterator_listbase_get",
+ NULL, NULL, NULL, NULL);
+ RNA_def_property_struct_type(prop, "MovieTrackingTrack");
+ RNA_def_property_ui_text(prop, "Rotation Tracks", "Collection of tracks used for 2D stabilization (translation)");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
+
+ /* active rotation track index */
+ prop = RNA_def_property(srna, "active_rotation_track_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "act_rot_track");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_funcs(prop, "rna_tracking_stabRotTracks_active_index_get",
+ "rna_tracking_stabRotTracks_active_index_set",
+ "rna_tracking_stabRotTracks_active_index_range");
+ RNA_def_property_ui_text(prop, "Active Rotation Track Index", "Index of active track in rotation stabilization tracks list");
+
+ /* anchor frame */
+ prop = RNA_def_property(srna, "anchor_frame", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "anchor_frame");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, MINFRAME, MAXFRAME);
+ RNA_def_property_ui_text(prop, "Anchor Frame", "Reference point to anchor stabilization (other frames will be adjusted relative to this frame's position)");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
+
+ /* target position */
+ prop = RNA_def_property(srna, "target_pos", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3); /* increment in steps of 0.01 and show 3 digit after point */
+ RNA_def_property_float_sdna(prop, NULL, "target_pos");
+ RNA_def_property_ui_text(prop, "Expected Position", "Known relative offset of original shot, will be subtracted; e.g. for panning shot, can be animated");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
+
+ /* target rotation */
+ prop = RNA_def_property(srna, "target_rot", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "target_rot");
+ RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3);
+ RNA_def_property_ui_text(prop, "Expected Rotation", "Rotation present on original shot, will be compensated; e.g. for deliberate tilting");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
+
+ /* target scale */
+ prop = RNA_def_property(srna, "target_zoom", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "scale");
+ RNA_def_property_range(prop, FLT_EPSILON, 100.0f);
+ RNA_def_property_ui_range(prop, 0.1f, 10.0f, 1, 3); /* increment in steps of 0.01. Show 3 digit after point */
+ RNA_def_property_ui_text(prop, "Expected Zoom", "Explicitly scale resulting frame to compensate zoom of original shot");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
/* autoscale */
prop = RNA_def_property(srna, "use_autoscale", PROP_BOOLEAN, PROP_NONE);
@@ -1705,13 +1802,6 @@ static void rna_def_trackingStabilization(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Scale Influence", "Influence of stabilization algorithm on footage scale");
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
- /* use_stabilize_rotation */
- prop = RNA_def_property(srna, "use_stabilize_rotation", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_ROTATION);
- RNA_def_property_ui_text(prop, "Stabilize Rotation", "Stabilize horizon line on the shot");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
-
/* influence_rotation */
prop = RNA_def_property(srna, "influence_rotation", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "rotinf");
@@ -1723,8 +1813,15 @@ static void rna_def_trackingStabilization(BlenderRNA *brna)
prop = RNA_def_property(srna, "filter_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "filter");
RNA_def_property_enum_items(prop, filter_items);
- RNA_def_property_ui_text(prop, "Filter", "Method to use to filter stabilization");
+ RNA_def_property_ui_text(prop, "Interpolate", "Interpolation to use for sub-pixel shifts and rotations due to stabilization");
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate");
+
+ /* UI display : show participating tracks */
+ prop = RNA_def_property(srna, "show_tracks_expanded", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_SHOW_STAB_TRACKS);
+ RNA_def_property_ui_text(prop, "Show Tracks", "Show UI list of tracks participating in stabilization");
+ RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1);
}
static void rna_def_reconstructedCamera(BlenderRNA *brna)
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 0de7676e8f8..b8ebb375a48 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -73,6 +73,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
@@ -112,6 +113,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_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 6cc2f097be8..d15a6fcb1c8 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -123,19 +123,11 @@ static void updateDepgraph(ModifierData *md, DagForest *forest,
{
ClothModifierData *clmd = (ClothModifierData *) md;
- Base *base;
-
if (clmd) {
- for (base = scene->base.first; base; base = base->next) {
- Object *ob1 = base->object;
- if (ob1 != ob) {
- CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision);
- if (coll_clmd) {
- DagNode *curNode = dag_get_node(forest, ob1);
- dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cloth Collision");
- }
- }
- }
+ /* Actual code uses get_collisionobjects */
+ dag_add_collision_relations(forest, scene, ob, obNode, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision");
+
+ dag_add_forcefield_relations(forest, scene, ob, obNode, clmd->sim_parms->effector_weights, true, 0, "Cloth Field");
}
}
@@ -147,16 +139,10 @@ static void updateDepsgraph(ModifierData *md,
{
ClothModifierData *clmd = (ClothModifierData *)md;
if (clmd != NULL) {
- Base *base;
- for (base = scene->base.first; base; base = base->next) {
- Object *ob1 = base->object;
- if (ob1 != ob) {
- CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision);
- if (coll_clmd) {
- DEG_add_object_relation(node, ob1, DEG_OB_COMP_TRANSFORM, "Cloth Modifier");
- }
- }
- }
+ /* Actual code uses get_collisionobjects */
+ DEG_add_collision_relations(node, scene, ob, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision");
+
+ DEG_add_forcefield_relations(node, scene, ob, clmd->sim_parms->effector_weights, true, 0, "Cloth Field");
}
}
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index edf959f42c6..bde20e56748 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -114,6 +114,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
return dm;
}
+static bool is_brush_cb(Object *UNUSED(ob), ModifierData *pmd)
+{
+ return ((DynamicPaintModifierData*)pmd)->brush != NULL;
+}
+
static void updateDepgraph(ModifierData *md, DagForest *forest,
struct Main *UNUSED(bmain),
struct Scene *scene,
@@ -124,16 +129,13 @@ static void updateDepgraph(ModifierData *md, DagForest *forest,
/* add relation from canvases to all brush objects */
if (pmd && pmd->canvas) {
- Base *base = scene->base.first;
-
- for (; base; base = base->next) {
- DynamicPaintModifierData *pmd2 =
- (DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint);
-
- if (pmd2 && pmd2->brush && ob != base->object) {
- DagNode *brushNode = dag_get_node(forest, base->object);
- dag_add_relation(forest, brushNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Dynamic Paint Brush");
+ for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
+ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
+ dag_add_forcefield_relations(forest, scene, ob, obNode, surface->effector_weights, true, 0, "Dynamic Paint Field");
}
+
+ /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */
+ dag_add_collision_relations(forest, scene, ob, obNode, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush");
}
}
}
@@ -147,13 +149,13 @@ static void updateDepsgraph(ModifierData *md,
DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
/* Add relation from canvases to all brush objects. */
if (pmd->canvas != NULL) {
- Base *base = scene->base.first;
- for (; base; base = base->next) {
- DynamicPaintModifierData *pmd2 =
- (DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint);
- if (pmd2 && pmd2->brush && ob != base->object) {
- DEG_add_object_relation(node, base->object, DEG_OB_COMP_TRANSFORM, "Dynamic Paint Brush");
+ for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
+ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
+ DEG_add_forcefield_relations(node, scene, ob, surface->effector_weights, true, 0, "Dynamic Paint Field");
}
+
+ /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */
+ DEG_add_collision_relations(node, scene, ob, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush");
}
}
}
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
new file mode 100644
index 00000000000..355ac9563dd
--- /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_object_cache_relation(node, mcmd->cache_file, DEG_OB_COMP_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_smoke.c b/source/blender/modifiers/intern/MOD_smoke.c
index 237d4cc6718..f04d7432a8f 100644
--- a/source/blender/modifiers/intern/MOD_smoke.c
+++ b/source/blender/modifiers/intern/MOD_smoke.c
@@ -117,219 +117,48 @@ static bool dependsOnTime(ModifierData *UNUSED(md))
return true;
}
-static void update_depsgraph_flow_coll_object(DagForest *forest,
- DagNode *obNode,
- Object *object2)
+static bool is_flow_cb(Object *UNUSED(ob), ModifierData *md)
{
- SmokeModifierData *smd;
- if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
- return;
- }
- object2->id.tag &= ~LIB_TAG_DOIT;
- smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke);
- if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) ||
- ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll)))
- {
- DagNode *curNode = dag_get_node(forest, object2);
- dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow/Coll");
- }
- if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
- GroupObject *go;
- for (go = object2->dup_group->gobject.first;
- go != NULL;
- go = go->next)
- {
- if (go->ob == NULL) {
- continue;
- }
- update_depsgraph_flow_coll_object(forest, obNode, go->ob);
- }
- }
+ SmokeModifierData *smd = (SmokeModifierData *) md;
+ return (smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow;
}
-static void update_depsgraph_field_source_object(DagForest *forest,
- DagNode *obNode,
- Object *object,
- Object *object2)
+static bool is_coll_cb(Object *UNUSED(ob), ModifierData *md)
{
- if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
- return;
- }
- object2->id.tag &= ~LIB_TAG_DOIT;
- if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) {
- DagNode *node2 = dag_get_node(forest, object2);
- dag_add_relation(forest, obNode, node2, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Source Object");
- }
- if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
- GroupObject *go;
- for (go = object2->dup_group->gobject.first;
- go != NULL;
- go = go->next)
- {
- if (go->ob == NULL) {
- continue;
- }
- update_depsgraph_field_source_object(forest, obNode, object, go->ob);
- }
- }
+ SmokeModifierData *smd = (SmokeModifierData *) md;
+ return (smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll;
}
static void updateDepgraph(ModifierData *md, DagForest *forest,
- struct Main *bmain,
+ struct Main *UNUSED(bmain),
struct Scene *scene, struct Object *ob,
DagNode *obNode)
{
SmokeModifierData *smd = (SmokeModifierData *) md;
- Base *base;
if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) {
- if (smd->domain->fluid_group || smd->domain->coll_group) {
- GroupObject *go = NULL;
-
- if (smd->domain->fluid_group)
- for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) {
- if (go->ob) {
- SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
-
- /* check for initialized smoke object */
- if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) {
- DagNode *curNode = dag_get_node(forest, go->ob);
- dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow");
- }
- }
- }
-
- if (smd->domain->coll_group)
- for (go = smd->domain->coll_group->gobject.first; go; go = go->next) {
- if (go->ob) {
- SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
-
- /* check for initialized smoke object */
- if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) {
- DagNode *curNode = dag_get_node(forest, go->ob);
- dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Coll");
- }
- }
- }
- }
- else {
- BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
- base = scene->base.first;
- for (; base; base = base->next) {
- update_depsgraph_flow_coll_object(forest, obNode, base->object);
- }
- }
- /* add relation to all "smoke flow" force fields */
- base = scene->base.first;
- BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
- for (; base; base = base->next) {
- update_depsgraph_field_source_object(forest, obNode, ob, base->object);
- }
- }
-}
+ /* Actual code uses get_collisionobjects */
+ dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow");
+ dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll");
-static void update_depsgraph_flow_coll_object_new(struct DepsNodeHandle *node,
- Object *object2)
-{
- SmokeModifierData *smd;
- if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
- return;
- }
- object2->id.tag &= ~LIB_TAG_DOIT;
- smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke);
- if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) ||
- ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll)))
- {
- DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Smoke Flow/Coll");
- DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Smoke Flow/Coll");
- }
- if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
- GroupObject *go;
- for (go = object2->dup_group->gobject.first;
- go != NULL;
- go = go->next)
- {
- if (go->ob == NULL) {
- continue;
- }
- update_depsgraph_flow_coll_object_new(node, go->ob);
- }
- }
-}
-
-static void update_depsgraph_field_source_object_new(struct DepsNodeHandle *node,
- Object *object,
- Object *object2)
-{
- if ((object2->id.tag & LIB_TAG_DOIT) == 0) {
- return;
- }
- object2->id.tag &= ~LIB_TAG_DOIT;
- if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) {
- DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Field Source Object");
- DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Field Source Object");
- }
- if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) {
- GroupObject *go;
- for (go = object2->dup_group->gobject.first;
- go != NULL;
- go = go->next)
- {
- if (go->ob == NULL) {
- continue;
- }
- update_depsgraph_field_source_object_new(node, object, go->ob);
- }
+ dag_add_forcefield_relations(forest, scene, ob, obNode, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field");
}
}
static void updateDepsgraph(ModifierData *md,
- struct Main *bmain,
+ struct Main *UNUSED(bmain),
struct Scene *scene,
Object *ob,
struct DepsNodeHandle *node)
{
SmokeModifierData *smd = (SmokeModifierData *)md;
- Base *base;
+
if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) {
- if (smd->domain->fluid_group || smd->domain->coll_group) {
- GroupObject *go = NULL;
- if (smd->domain->fluid_group != NULL) {
- for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) {
- if (go->ob != NULL) {
- SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
- /* Check for initialized smoke object. */
- if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) {
- DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Flow");
- }
- }
- }
- }
- if (smd->domain->coll_group != NULL) {
- for (go = smd->domain->coll_group->gobject.first; go; go = go->next) {
- if (go->ob != NULL) {
- SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke);
- /* Check for initialized smoke object. */
- if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) {
- DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Coll");
- }
- }
- }
- }
- }
- else {
- BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
- base = scene->base.first;
- for (; base; base = base->next) {
- update_depsgraph_flow_coll_object_new(node, base->object);
- }
- }
- /* add relation to all "smoke flow" force fields */
- base = scene->base.first;
- BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true);
- for (; base; base = base->next) {
- update_depsgraph_field_source_object_new(node, ob, base->object);
- }
+ /* Actual code uses get_collisionobjects */
+ DEG_add_collision_relations(node, scene, ob, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow");
+ DEG_add_collision_relations(node, scene, ob, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll");
+
+ DEG_add_forcefield_relations(node, scene, ob, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field");
}
}
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 98a1412d0c6..17adc7f1520 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -35,6 +35,7 @@
#include <stdio.h>
#include "DNA_scene_types.h"
+#include "DNA_object_force.h"
#include "BLI_utildefines.h"
@@ -42,6 +43,9 @@
#include "BKE_particle.h"
#include "BKE_softbody.h"
+#include "depsgraph_private.h"
+#include "DEG_depsgraph_build.h"
+
#include "MOD_modifiertypes.h"
static void deformVerts(ModifierData *md, Object *ob,
@@ -58,6 +62,31 @@ static bool dependsOnTime(ModifierData *UNUSED(md))
return true;
}
+static void updateDepgraph(ModifierData *UNUSED(md), DagForest *forest,
+ struct Main *UNUSED(bmain),
+ Scene *scene, Object *ob, DagNode *obNode)
+{
+ if (ob->soft) {
+ /* Actual code uses ccd_build_deflector_hash */
+ dag_add_collision_relations(forest, scene, ob, obNode, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision");
+
+ dag_add_forcefield_relations(forest, scene, ob, obNode, ob->soft->effector_weights, true, 0, "Softbody Field");
+ }
+}
+
+static void updateDepsgraph(ModifierData *UNUSED(md),
+ struct Main *UNUSED(bmain),
+ struct Scene *scene,
+ Object *ob,
+ struct DepsNodeHandle *node)
+{
+ if (ob->soft) {
+ /* Actual code uses ccd_build_deflector_hash */
+ DEG_add_collision_relations(node, scene, ob, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision");
+
+ DEG_add_forcefield_relations(node, scene, ob, ob->soft->effector_weights, true, 0, "Softbody Field");
+ }
+}
ModifierTypeInfo modifierType_Softbody = {
/* name */ "Softbody",
@@ -80,8 +109,8 @@ ModifierTypeInfo modifierType_Softbody = {
/* requiredDataMask */ NULL,
/* freeData */ NULL,
/* isDisabled */ NULL,
- /* updateDepgraph */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepgraph */ updateDepgraph,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ dependsOnTime,
/* dependsOnNormals */ NULL,
/* foreachObjectLink */ 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/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/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 493fe82ff80..69905fc296b 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -441,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_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..2d43c47679d 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,30 @@ 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));
-
+ {
+ // only update if the actual pixel size changes
+ float prev_pixelsize = U.pixelsize;
U.pixelsize = wm_window_pixelsize(win);
- BKE_blender_userdef_refresh();
- WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
- WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
+
+ if (U.pixelsize != prev_pixelsize) {
+ 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 ca841954b16..2748de0e7dd 100644
--- a/source/blenderplayer/CMakeLists.txt
+++ b/source/blenderplayer/CMakeLists.txt
@@ -233,6 +233,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 6c0bbc8e01f..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
diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
index d033afacc08..edbbf93bf7a 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"
@@ -492,6 +493,8 @@ int main(
// freeing up GPU_Textures works correctly.
BLI_threadapi_init();
+ DNA_sdna_current_init();
+
RNA_init();
init_nodesystem();